clqms-fe1/AGENTS.md

226 lines
5.5 KiB
Markdown
Raw Normal View History

# 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
<script>
// 1. Imports
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
import { auth } from '$lib/stores/auth.js';
import { User } from 'lucide-svelte';
// 2. Props with $bindable
let { open = $bindable(false), title = '', children } = $props();
// 3. State
let loading = $state(false);
let formData = $state({ name: '' });
// 4. Derived
let isValid = $derived(formData.name.length > 0);
// 5. Effects
$effect(() => { /* side effects */ });
// 6. Handlers
function handleSubmit() { /* impl */ }
</script>
```
## 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
<!-- Modal with $bindable props -->
<Modal bind:open={showModal} title="Edit" size="lg">
{#snippet children()}
<form>...</form>
{/snippet}
{#snippet footer()}
<button class="btn btn-primary">Save</button>
{/snippet}
</Modal>
<!-- Dialog with backdrop -->
<dialog class="modal modal-open">
<div class="modal-box">
{@render children?.()}
</div>
<form method="dialog" class="modal-backdrop" onclick={handleBackdropClick}>
<button>close</button>
</form>
</dialog>
```
## 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/