diff --git a/CLAUDE.md b/CLAUDE.md index 1375132..e92d89b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,31 +13,9 @@ Default to Bun instead of Node.js everywhere: - `bunx ` not `npx` - Bun auto-loads `.env` — never use dotenv. -## Common Commands +## Commands -```bash -bun run dev # dev server with hot reload (bun --watch src/serve.ts) -bun run build # Vite production build -bun run start # production server (NODE_ENV=production) -bun run typecheck # tsc --noEmit -bun run lint # biome check src/ -bun run lint:fix # biome check --write src/ - -# Database -bun run db:migrate # prisma migrate dev -bun run db:seed # seed demo data -bun run db:generate # regenerate prisma client -bun run db:studio # Prisma Studio GUI -bun run db:push # push schema without migration - -# Tests -bun run test # all tests -bun run test:unit # tests/unit/ -bun run test:integration # tests/integration/ — no server needed -bun run test:e2e # tests/e2e/ — requires Lightpanda Docker -``` - -Run a single test file: `bun test tests/integration/auth.test.ts` +See @docs/COMMANDS.md ## Architecture @@ -50,3 +28,7 @@ See @docs/TESTING.md ## Dev Tools See @docs/DEV_TOOLS.md + +## Frontend Conventions + +See @docs/CONVENTIONS.md diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md new file mode 100644 index 0000000..04d5241 --- /dev/null +++ b/docs/COMMANDS.md @@ -0,0 +1,25 @@ +# Commands + +```bash +bun run dev # dev server with hot reload (bun --watch src/serve.ts) +bun run build # Vite production build +bun run start # production server (NODE_ENV=production) +bun run typecheck # tsc --noEmit +bun run lint # biome check src/ +bun run lint:fix # biome check --write src/ + +# Database +bun run db:migrate # prisma migrate dev +bun run db:seed # seed demo data +bun run db:generate # regenerate prisma client +bun run db:studio # Prisma Studio GUI +bun run db:push # push schema without migration + +# Tests +bun run test # all tests +bun run test:unit # tests/unit/ +bun run test:integration # tests/integration/ — no server needed +bun run test:e2e # tests/e2e/ — requires Lightpanda Docker +``` + +Run a single test file: `bun test tests/integration/auth.test.ts` diff --git a/docs/CONVENTIONS.md b/docs/CONVENTIONS.md new file mode 100644 index 0000000..86cce96 --- /dev/null +++ b/docs/CONVENTIONS.md @@ -0,0 +1,66 @@ +# Frontend Conventions + +## Data Fetching + +- **SWR** for read-only data in route components (tables, lists, charts). +- **TanStack Query** (`useQuery`, `useMutation`) for auth state — see `src/frontend/hooks/useAuth.ts`. +- Never mix both in the same component/page. +- Debounce search inputs: `useDebouncedValue(search, 400)` + `useEffect` that only triggers when length >= 3 or === 0. + +## API URL Builder + +All URLs go through `src/frontend/config/api.ts` → `API_URLS`. Add new entries there, never inline URLs in components. + +Desa+ endpoints are proxied via `/api/proxy/desa-plus` → `DESA_PLUS_PROXY` constant. The actual API source is at: +`/Users/wibu04/Documents/Projects/sistem-desa-mandiri/src/app/api/monitoring/[[...slug]]/route.ts` + +## Filters & Pagination Pattern + +Server-side filtering — always pass filter params to the API, never filter client-side on paginated data. + +State pattern for a filtered table page: +```ts +const [page, setPage] = useState(1) +const [search, setSearch] = useState('') // raw input +const [searchQuery, setSearchQuery] = useState('') // debounced, sent to API +const [debouncedSearch] = useDebouncedValue(search, 400) + +useEffect(() => { + if (debouncedSearch.length >= 3 || debouncedSearch.length === 0) { + setSearchQuery(debouncedSearch) + setPage(1) + } +}, [debouncedSearch]) + +useEffect(() => { setPage(1) }, [filterA, filterB]) // reset page on filter change +``` + +## Mantine Components + +- Dark theme forced (`#242424`). Never add light-mode conditionals. +- `radius="md"` on inputs, `radius="2xl"` on container `Paper`. +- `className="glass"` on `Paper` cards for the frosted glass effect. +- `size="sm"` on table inputs and selects. +- Icons from `react-icons/tb` only — no other icon libraries. +- `DatePickerInput` from `@mantine/dates` with `type="range"` returns `[string | null, string | null]`, not Date objects. + +## Route Files + +File-based routing via TanStack Router Vite plugin. Files in `src/frontend/routes/`: + +| Pattern | Route | +|---|---| +| `apps.$appId.tsx` | Layout wrapper for per-app pages | +| `apps.$appId.index.tsx` | Overview/dashboard for an app | +| `apps.$appId.users.index.tsx` | User management | +| `apps.$appId.logs.tsx` | Activity logs | +| `apps.$appId.villages.tsx` | Villages layout | +| `apps.$appId.villages.index.tsx` | Village list | +| `apps.$appId.villages.$villageId.tsx` | Village detail | + +`routeTree.gen.ts` is auto-generated — never edit it manually. + +## App Registration + +App configs (ID, menu items) live in `src/frontend/config/appMenus.ts`. Add new apps there to register them. +Currently active app: `desa-plus`.