# AGENTS.md - Coding Guidelines for CLQMS Frontend ## Project Overview SvelteKit frontend for Clinical Laboratory Quality Management System (CLQMS). Uses Svelte 5 runes, TailwindCSS 4, DaisyUI, and Lucide icons. ## Build Commands ```bash # Development server pnpm run dev # Production build pnpm run build # Preview production build pnpm run preview # Sync SvelteKit (runs automatically on install) pnpm run prepare ``` ## Testing **No test framework configured yet.** When adding tests: - Use Vitest for unit tests (recommended with SvelteKit) - Use Playwright for E2E tests - Run single test: `vitest run src/path/to/test.js` - Run tests in watch mode: `vitest` ## Code Style Guidelines ### JavaScript/TypeScript - **ES Modules**: Always use `import`/`export` (type: "module" in package.json) - **Semicolons**: Use semicolons consistently - **Quotes**: Use single quotes for strings - **Indentation**: 2 spaces - **Trailing commas**: Use in multi-line objects/arrays - **JSDoc**: Document all exported functions with JSDoc comments ### Svelte Components ```svelte ``` ### 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`, `userStore`) - **Event handlers**: prefix with `handle` (`handleSubmit`, `handleClick`) - **Form state**: `formLoading`, `formError`, `deleteConfirmOpen` ### Imports Order 1. Svelte imports (`svelte`, `$app/*`) 2. $lib aliases (`$lib/stores/*`, `$lib/api/*`, `$lib/components/*`, `$lib/utils/*`) 3. External libraries (`lucide-svelte`) 4. Relative imports (minimize these, prefer `$lib`) ### Project Structure ``` src/ lib/ api/ # API client and endpoints (per feature) stores/ # Svelte stores (auth, config, valuesets) components/ # Reusable components (DataTable, Modal, Sidebar) utils/ # Utilities (toast, helpers) assets/ # Static assets routes/ # SvelteKit routes (app)/ # Route groups (protected) login/ dashboard/ ``` ### API Client Patterns ```javascript // src/lib/api/client.js - Base client handles auth, 401 redirects import { apiClient, get, post, put, patch, del } from '$lib/api/client.js'; // src/lib/api/feature.js - Feature-specific 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(data) { return patch('/api/items', data); } export async function deleteItem(id) { return del(`/api/items/${id}`); } ``` ### Store Patterns ```javascript // Use writable for stores with localStorage persistence import { writable } from 'svelte/store'; import { browser } from '$app/environment'; function createStore() { const getInitialState = () => { if (!browser) return { data: null }; return { data: 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 ```svelte let { open = $bindable(false), selected = $bindable(null) } = $props(); {@render children?.()} {@render footer()} ``` ### 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 with Tailwind & DaisyUI - DaisyUI components: `btn`, `card`, `alert`, `input`, `navbar`, `modal`, `dropdown`, `menu` - Color scheme: `primary` (emerald), `secondary` (dark blue), `accent` (royal blue) - Custom compact classes: `.compact-y`, `.compact-p`, `.compact-input`, `.compact-btn`, `.compact-card` - Size modifiers: `.btn-sm`, `.input-sm`, `.select-sm` for compact forms ### Authentication Patterns - Auth state in `$lib/stores/auth.js` using writable store - Check `$auth.isAuthenticated` in layout `onMount` - Redirect to `/login` if unauthenticated using `goto('/login')` - API client auto-redirects on 401 responses ### Runtime Config Pattern ```javascript // Use runtime config for API URL that can be changed at runtime import { config } from '$lib/stores/config.js'; function getApiUrl() { return config.getApiUrl() || import.meta.env.VITE_API_URL || ''; } ``` ### LocalStorage - Only access in browser: check `browser` from `$app/environment` - Use descriptive keys: `clqms_username`, `clqms_remember`, `auth_token` ### Form Handling Patterns ```javascript // Form state with validation 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; } } ``` ### Toast/Notification System ```javascript import { success, error, info, warning } from '$lib/utils/toast.js'; // Use toast notifications for user feedback success('Item created successfully'); error('Failed to create item'); info('New message received'); warning('Action requires confirmation'); ``` ## Proxy Configuration API requests to `/api` are proxied to `http://localhost:8000` in dev. See `vite.config.js`. ## API Documentation API Reference (Swagger UI): https://clqms01-api.services-summit.my.id/swagger/ ## Important Notes - No ESLint or Prettier configured yet - add if needed - No test framework configured yet - Uses Svelte 5 runes: `$props`, `$state`, `$derived`, `$effect`, `$bindable` - SvelteKit file-based routing with `+page.svelte`, `+layout.svelte` - Static adapter configured for static export - Runtime config allows API URL changes without rebuild