diff --git a/.npmrc b/.npmrc index b8932b1..a8b79ea 100644 --- a/.npmrc +++ b/.npmrc @@ -2,3 +2,5 @@ public-hoist-pattern[]=@astrojs/check public-hoist-pattern[]=eslint-plugin-astro public-hoist-pattern[]=astro-eslint-parser public-hoist-pattern[]=@typescript-eslint/* +public-hoist-pattern[]=metro +public-hoist-pattern[]=metro-* diff --git a/.vscode/settings.json b/.vscode/settings.json index 00018be..5cd7ca8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "cSpell.words": [ - "midwifes" + "midwifes", + "Pressable" ], "eslint.useFlatConfig": true, "eslint.workingDirectories": [ diff --git a/AGENTS.md b/AGENTS.md index bedfa17..8783220 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,6 +53,15 @@ The visual design language for the mobile app. Covers: --- +### [`documentation/CONVENTIONS.md`](documentation/CONVENTIONS.md) +Coding conventions for the repository. Covers: +- File naming: PascalCase for components, camelCase for hooks, kebab-case for lib/util files +- JSDoc: when to write one, what to include (params, returns, examples), and line length + +**Update when:** establishing or changing any coding convention that should be applied consistently across the codebase. + +--- + ## Mockups Visual mockups live in `documentation/mockups/` and are for reference only — do not modify them unless producing new design artifacts. diff --git a/apps/mobile/.gitignore b/apps/mobile/.gitignore index d914c32..e3a0f18 100644 --- a/apps/mobile/.gitignore +++ b/apps/mobile/.gitignore @@ -39,3 +39,6 @@ yarn-error.* # generated native folders /ios /android + +# test coverage +coverage/ diff --git a/apps/mobile/App.tsx b/apps/mobile/App.tsx index c6131fa..97ae9b9 100644 --- a/apps/mobile/App.tsx +++ b/apps/mobile/App.tsx @@ -1,20 +1,43 @@ import { StatusBar } from "expo-status-bar"; -import { StyleSheet, Text, View } from "react-native"; +import { View } from "react-native"; +// import { useMigrations } from "drizzle-orm/expo-sqlite/migrator"; +// import { db } from "./src/db"; +// import migrations from "./drizzle/migrations"; +import { ClientsTest } from "./src/components/ClientsTest"; +import { useAppFonts } from "./src/hooks/useAppFonts"; +import { ThemeProvider, useTheme } from "./src/lib/theme-context"; +import { makeStyles } from "./src/lib/make-styles"; -export default function App() { +const useStyles = makeStyles((theme) => ({ + container: { + flex: 1, + backgroundColor: theme.background, + paddingTop: 30, + }, +})); + +function AppContent() { + const styles = useStyles(); + const theme = useTheme(); return ( - Open up App.tsx to start working on your app! - + + ); } -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: "#fff", - alignItems: "center", - justifyContent: "center", - }, -}); +export default function App() { + const fontsLoaded = useAppFonts(); + // const { success, error } = useMigrations(db, migrations); + + if (!fontsLoaded) return null; + // if (error) throw error; + // if (!success) return null; + + return ( + + + + ); +} diff --git a/apps/mobile/babel.config.js b/apps/mobile/babel.config.js new file mode 100644 index 0000000..3f8f89c --- /dev/null +++ b/apps/mobile/babel.config.js @@ -0,0 +1,4 @@ +module.exports = { + presets: ["babel-preset-expo"], + plugins: [["inline-import", { extensions: [".sql"] }]], +}; diff --git a/apps/mobile/drizzle.config.ts b/apps/mobile/drizzle.config.ts new file mode 100644 index 0000000..279bc7c --- /dev/null +++ b/apps/mobile/drizzle.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + schema: "./src/db/schema.ts", + out: "./drizzle", + dialect: "sqlite", + driver: "expo", +}); diff --git a/apps/mobile/metro.config.js b/apps/mobile/metro.config.js new file mode 100644 index 0000000..f713c61 --- /dev/null +++ b/apps/mobile/metro.config.js @@ -0,0 +1,7 @@ +const { getDefaultConfig } = require("expo/metro-config"); + +const config = getDefaultConfig(__dirname); + +config.resolver.assetExts.push("sql"); + +module.exports = config; diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 1d926ef..debaa18 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -9,9 +9,16 @@ "ios": "expo start --ios", "web": "expo start --web", "check:lint": "eslint --max-warnings 0", - "check:types": "tsc --noEmit" + "check:types": "tsc --noEmit", + "check:format": "prettier --check .", + "check": "pnpm check:format && pnpm run check:lint && pnpm run check:types", + "test": "jest", + "test:watch": "jest --watch", + "db:generate": "drizzle-kit generate" }, "dependencies": { + "@expo-google-fonts/inter": "^0.4.2", + "@expo-google-fonts/newsreader": "^0.4.1", "@midwifes-notebook/server": "workspace:*", "drizzle-orm": "^0.45.1", "expo": "~55.0.6", @@ -20,17 +27,23 @@ "expo-splash-screen": "^55.0.10", "expo-sqlite": "^55.0.10", "expo-status-bar": "~55.0.4", - "nativewind": "^4.2.2", "react": "19.2.0", "react-native": "0.83.2" }, "devDependencies": { + "@testing-library/react-native": "^13.3.3", + "@types/jest": "29.5.14", "@types/react": "~19.2.2", + "babel-plugin-inline-import": "^3.0.0", "drizzle-kit": "^0.31.9", "eslint": "^9.39.4", "eslint-config-expo": "^55.0.0", - "tailwindcss": "^4.2.1", + "jest": "~29.7.0", + "jest-expo": "~55.0.18", "typescript": "~5.9.2" }, + "jest": { + "preset": "jest-expo" + }, "private": true } diff --git a/apps/mobile/src/components/ClientsTest.tsx b/apps/mobile/src/components/ClientsTest.tsx new file mode 100644 index 0000000..9833fd4 --- /dev/null +++ b/apps/mobile/src/components/ClientsTest.tsx @@ -0,0 +1,71 @@ +import { useState } from "react"; +import { FlatList, StyleSheet, View } from "react-native"; +import { useLiveQuery } from "drizzle-orm/expo-sqlite"; + +import { db } from "../db"; +import { clients } from "../db/schema"; +import { useToggleTheme } from "../lib/theme-context"; + +import { Text } from "./ui/Text"; +import { Button } from "./ui/Button"; + +const NAMES = [ + "Alice", + "Betty", + "Clara", + "Diana", + "Elena", + "Fiona", + "Grace", + "Hannah", + "Iris", + "Julia", +]; + +function randomName() { + return ( + NAMES[Math.floor(Math.random() * NAMES.length)] + + " " + + Math.floor(Math.random() * 9000 + 1000) + ); +} + +export function ClientsTest() { + const { data } = useLiveQuery(db.select().from(clients)); + const [inserting, setInserting] = useState(false); + const toggleTheme = useToggleTheme(); + + async function insertClient() { + setInserting(true); + await db.insert(clients).values({ name: randomName() }); + setInserting(false); + } + + return ( + +