# 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 # Development pnpm run dev # Start development server pnpm run build # Production build (outputs to build/) pnpm run preview # Preview production build pnpm run prepare # Sync SvelteKit (runs on install) # Package management pnpm install # Install dependencies (use pnpm, not npm/yarn) # Testing (not configured yet - add when needed) # vitest run src/path/to/test.js # Run single test # vitest # Run tests in watch mode # npx playwright test # E2E tests ``` **No ESLint/Prettier configured yet.** When adding: configure in `vite.config.js` or separate config files. ## 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 with `@param` and `@returns` ### Imports Order 1. Svelte (`svelte`, `$app/*`) 2. `$lib/*` (stores, api, components, utils) 3. External libraries (`lucide-svelte`) 4. Relative imports (minimize, prefer `$lib`) ## Naming Conventions - **Components**: PascalCase (`LoginForm.svelte`, `PatientFormModal.svelte`) - **Files/Routes**: lowercase with hyphens (`+page.svelte`, `user-profile/`) - **Variables**: camelCase (`isLoading`, `userName`) - **Constants**: UPPER_SNAKE_CASE (`API_URL`, `STORAGE_KEY`) - **Stores**: camelCase, descriptive (`auth`, `config`, `userStore`) - **Event handlers**: prefix with `handle` (`handleSubmit`, `handleClick`) - **Form state**: `formLoading`, `formError`, `deleteConfirmOpen` ## Svelte 5 Component Structure ```svelte ``` ## TypeScript Types ```typescript // src/lib/types/ - Type definitions export type TestType = 'TEST' | 'PARAM' | 'CALC' | 'GROUP' | 'TITLE'; export interface TestSummary { TestSiteID: number; TestSiteCode: string; TestSiteName: string; TestType: TestType; } // Use JSDoc for type imports in JS files /** @typedef {import('$lib/types/test.types.js').TestSummary} TestSummary */ ``` ## 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) /** @param {Object} params - Query parameters */ 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, update } = writable(getInitialState()); return { subscribe, setData: (data) => { if (browser) localStorage.setItem('key', JSON.stringify(data)); set({ data }); } }; } ``` ## Component Patterns ### Modal with Snippets ```svelte {#snippet children()}
{/snippet} {#snippet footer()} {/snippet}
``` ### Form with Validation ```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); toastSuccess('Success'); } catch (err) { formError = err.message; } finally { formLoading = false; } } ``` ## Error Handling ```javascript try { const result = await api.fetchData(); toastSuccess('Operation successful'); } catch (err) { const message = err.message || 'An unexpected error occurred'; toastError(message); console.error('Operation failed:', err); } ``` ## Styling (DaisyUI + Tailwind) - **Components**: `btn`, `card`, `alert`, `input`, `navbar`, `modal`, `dropdown`, `menu` - **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`, `.compact-btn`, `.compact-card` ### Input with Icons (DaisyUI Pattern) **Correct way** - Use DaisyUI's built-in input with icon support: ```svelte
``` **Key points:** - Use `