# AGENTS.md - Coding Guidelines for CLQMS Frontend SvelteKit frontend for Clinical Laboratory Quality Management System. Uses Svelte 5 runes, TailwindCSS 4, DaisyUI, and Lucide icons. ## Commands ```bash pnpm run dev # Development server pnpm run build # Production build pnpm run preview # Preview production build pnpm run prepare # Sync SvelteKit ``` **No test framework yet.** When adding: use Vitest (`vitest run src/path/to/test.js`), Playwright for E2E. ## Code Style - **ES Modules**: `import`/`export` (type: "module") - **Semicolons**: Required - **Quotes**: Single quotes - **Indentation**: 2 spaces - **Trailing commas**: In multi-line objects/arrays - **JSDoc**: Document all exported functions ## Naming Conventions - **Components**: PascalCase (`LoginForm.svelte`) - **Files/Routes**: lowercase with hyphens (`+page.svelte`) - **Variables**: camelCase (`isLoading`, `userName`) - **Constants**: UPPER_SNAKE_CASE (`API_URL`) - **Stores**: camelCase (`auth`, `config`) - **Handlers**: prefix with `handle` (`handleSubmit`) - **Form state**: `formLoading`, `formError` ## Imports Order 1. Svelte (`svelte`, `$app/*`) 2. `$lib/*` (stores, api, components, utils) 3. External libraries (`lucide-svelte`) 4. Relative imports (minimize, prefer `$lib`) ## Svelte 5 Components ```svelte ``` ## API Patterns ```javascript // src/lib/api/client.js - Use these helpers import { get, post, put, patch, del } from '$lib/api/client.js'; // Feature endpoints (with JSDoc) export async function fetchItems(params = {}) { const query = new URLSearchParams(params).toString(); return get(query ? `/api/items?${query}` : '/api/items'); } export async function createItem(data) { return post('/api/items', data); } export async function updateItem(id, data) { return patch(`/api/items/${id}`, data); } export async function deleteItem(id) { return del(`/api/items/${id}`); } ``` ## Store Patterns ```javascript import { writable } from 'svelte/store'; import { browser } from '$app/environment'; function createStore() { const getInitialState = () => { if (!browser) return { data: null }; return JSON.parse(localStorage.getItem('key')); }; const { subscribe, set } = writable(getInitialState()); return { subscribe, setData: (data) => { if (browser) localStorage.setItem('key', JSON.stringify(data)); set(data); } }; } ``` ## Component Patterns ```svelte {#snippet children()}
...
{/snippet} {#snippet footer()} {/snippet}
``` ## Error Handling ```javascript try { const result = await api.fetchData(); success('Operation successful'); } catch (err) { const message = err.message || 'An unexpected error occurred'; error(message); console.error('Operation failed:', err); } ``` ## Form Patterns ```javascript let formLoading = $state(false); let formError = $state(''); let formData = $state({ username: '', password: '' }); function validateForm() { formError = ''; if (!formData.username.trim()) { formError = 'Username is required'; return false; } return true; } async function handleSubmit() { if (!validateForm()) return; formLoading = true; try { await api.submit(formData); success('Success'); } catch (err) { formError = err.message; } finally { formLoading = false; } } ``` ## Styling (DaisyUI + Tailwind) - **Components**: `btn`, `card`, `alert`, `input`, `navbar`, `modal`, `dropdown` - **Colors**: `primary` (emerald), `secondary` (dark blue), `accent` (royal blue) - **Sizes**: `btn-sm`, `input-sm`, `select-sm` for compact forms - **Custom**: `.compact-y`, `.compact-p`, `.compact-input` ## Authentication - Check `$auth.isAuthenticated` in layout `onMount` - Redirect to `/login` if unauthenticated using `goto('/login')` - API client auto-redirects on 401 ## LocalStorage - Only access in browser: check `browser` from `$app/environment` - Use descriptive keys: `clqms_username`, `auth_token` ## Project Structure ``` src/ lib/ api/ # API clients per feature stores/ # Svelte stores components/ # Reusable components utils/ # Utilities (toast, helpers) routes/ # SvelteKit routes (app)/ # Route groups (protected) login/ dashboard/ ``` ## Important Notes - Uses Svelte 5: `$props`, `$state`, `$derived`, `$effect`, `$bindable` - Static adapter configured for static export - Runtime config allows API URL changes without rebuild - API proxy: `/api` → `http://localhost:8000` (dev) - API Docs: https://clqms01-api.services-summit.my.id/swagger/