# 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`.