163 lines
4.8 KiB
Markdown
163 lines
4.8 KiB
Markdown
# CLQMS Frontend - Agent Guidelines
|
|
|
|
## Build Commands
|
|
|
|
```bash
|
|
# Development
|
|
pnpm dev # Start dev server (http://localhost:5173)
|
|
# Production
|
|
pnpm build # Build for production (outputs to build/)
|
|
pnpm preview # Preview production build locally
|
|
# Type Checking
|
|
pnpm check # Run svelte-check TypeScript validation
|
|
pnpm check:watch # Run type checker in watch mode
|
|
# Package Management
|
|
pnpm install # Install dependencies
|
|
pnpm prepare # Sync SvelteKit (runs automatically)
|
|
```
|
|
|
|
## Code Style Guidelines
|
|
|
|
### TypeScript & Svelte
|
|
- Use TypeScript strict mode (configured in tsconfig.json)
|
|
- Prefer explicit types over `any`
|
|
- Use Svelte 5 runes: `$state`, `$derived`, `$effect`, `$props`
|
|
- Components use `.svelte` extension with `<script lang="ts">`
|
|
|
|
### Imports & Aliases
|
|
Use path aliases (defined in svelte.config.js):
|
|
- `$components/*` → `src/lib/components/*`
|
|
- `$stores/*` → `src/lib/stores/*`
|
|
- `$types/*` → `src/lib/types/*`
|
|
- `$server/*` → `src/lib/server/*`
|
|
- `$lib/*` → `src/lib/*` (built-in)
|
|
Order: External libs → SvelteKit → Internal aliases → Relative imports
|
|
|
|
### Naming Conventions
|
|
- Components: PascalCase (e.g., `Sidebar.svelte`, `LoginForm.svelte`)
|
|
- Files: kebab-case for non-components (e.g., `api-client.ts`)
|
|
- Variables/functions: camelCase
|
|
- Constants: UPPER_SNAKE_CASE
|
|
- Types/Interfaces: PascalCase with descriptive names
|
|
- Svelte stores: camelCase (e.g., `auth`, `userStore`)
|
|
|
|
### Component Structure
|
|
```svelte
|
|
<script lang="ts">
|
|
// 1. Imports
|
|
import { goto } from '$app/navigation';
|
|
import { auth } from '$stores/auth';
|
|
// 2. Props
|
|
let { title, data } = $props<{ title: string; data: SomeType }>();
|
|
// 3. State
|
|
let loading = $state(false);
|
|
// 4. Derived
|
|
let isValid = $derived(data && data.length > 0);
|
|
// 5. Effects
|
|
$effect(() => { if (isValid) console.log('Data loaded'); });
|
|
</script>
|
|
<svelte:head><title>{title}</title></svelte:head>
|
|
<!-- Template -->
|
|
<div class="card"><slot /></div>
|
|
```
|
|
|
|
### Styling (Tailwind + DaisyUI)
|
|
- Use Tailwind utility classes
|
|
- DaisyUI components: `btn`, `card`, `input`, `modal`, etc.
|
|
- Theme: Forest (medical green primary: #2d6a4f)
|
|
- Color utilities: `text-emerald-600`, `bg-emerald-100`
|
|
- Responsive: `sm:`, `md:`, `lg:` prefixes
|
|
- Spacing: 4px base unit (e.g., `p-4` = 16px)
|
|
|
|
### Error Handling
|
|
```typescript
|
|
import { api } from '$server/api';
|
|
async function fetchData() {
|
|
const { data, error, success } = await api.get<Patient[]>('/api/patients');
|
|
if (!success || error) {
|
|
console.error('Failed to fetch:', error?.message);
|
|
return;
|
|
}
|
|
// Use data
|
|
}
|
|
```
|
|
|
|
### Form Handling (Superforms + Zod)
|
|
```typescript
|
|
import { superForm } from 'sveltekit-superforms';
|
|
import { zodClient } from 'sveltekit-superforms/adapters';
|
|
import { z } from 'zod';
|
|
const schema = z.object({
|
|
username: z.string().min(1, 'Required'),
|
|
email: z.string().email('Invalid email')
|
|
});
|
|
const { form, errors, enhance } = superForm(data?.form, {
|
|
validators: zodClient(schema)
|
|
});
|
|
```
|
|
|
|
### State Management
|
|
```typescript
|
|
import { writable } from 'svelte/store';
|
|
interface AuthState {
|
|
user: User | null;
|
|
isAuthenticated: boolean;
|
|
}
|
|
function createAuthStore() {
|
|
const { subscribe, set, update } = writable<AuthState>({
|
|
user: null,
|
|
isAuthenticated: false
|
|
});
|
|
return {
|
|
subscribe,
|
|
login: (user: User) => update(s => ({ ...s, user, isAuthenticated: true })),
|
|
logout: () => set({ user: null, isAuthenticated: false })
|
|
};
|
|
}
|
|
export const auth = createAuthStore();
|
|
```
|
|
|
|
### Route Structure
|
|
```
|
|
src/routes/
|
|
├── +layout.svelte # Root layout
|
|
├── +page.svelte # Landing page (redirects)
|
|
├── (app)/ # Group: Protected routes
|
|
│ ├── +layout.svelte # App layout with sidebar
|
|
│ └── dashboard/
|
|
│ └── +page.svelte
|
|
└── (auth)/ # Group: Public routes
|
|
├── +layout.svelte # Auth layout (centered)
|
|
└── login/
|
|
└── +page.svelte
|
|
```
|
|
|
|
### API Client Usage
|
|
```typescript
|
|
import { api } from '$server/api';
|
|
// GET
|
|
const { data } = await api.get<User[]>('/api/users');
|
|
// POST
|
|
const { data } = await api.post<User>('/api/users', { name: 'John' });
|
|
// PATCH
|
|
const { data } = await api.patch<User>('/api/users/1', { name: 'Jane' });
|
|
// DELETE
|
|
const { data } = await api.delete('/api/users/1');
|
|
```
|
|
|
|
### Environment Variables
|
|
```typescript
|
|
// Use only PUBLIC_ prefix for client-side
|
|
import { PUBLIC_API_BASE_URL } from '$env/static/public';
|
|
```
|
|
|
|
### Git Workflow
|
|
- Commit messages: imperative mood (e.g., "Add patient form validation")
|
|
- Branch naming: `feature/`, `fix/`, `refactor/` prefixes
|
|
- Never commit secrets or `.env.local`
|
|
|
|
## Testing (To Be Configured)
|
|
Test framework not yet configured. When added:
|
|
- Unit tests: Vitest (planned per checklist)
|
|
- E2E tests: Playwright (planned per checklist)
|