clqms-fe1/AGENTS.md
2026-02-08 17:39:53 +07:00

4.8 KiB

CLQMS Frontend - Agent Guidelines

Build Commands

# 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

<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

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)

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

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

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

// 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)