clqms-fe1/AGENTS.md
mahdahar ae806911be feat(equipment,organization): add equipment API client and complete organization module structure
- Add equipment.js API client with full CRUD operations
- Add organization sub-routes: account, department, discipline, instrument, site, workstation
- Create EquipmentModal and DeleteConfirmModal components
- Update master-data navigation and sidebar
- Update tests, containers, counters, geography, locations, occupations, specialties, testmap, and valuesets pages
- Add COMPONENT_ORGANIZATION.md documentation
2026-02-24 16:53:04 +07:00

6.2 KiB

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

# 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

<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, footer } = $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

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

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

<Modal bind:open={showModal} title="Edit Item" size="lg">
  {#snippet children()}
    <form onsubmit={handleSubmit}>
      <input class="input input-bordered" bind:value={formData.name} />
    </form>
  {/snippet}
  {#snippet footer()}
    <button class="btn btn-ghost" onclick={() => showModal = false}>Cancel</button>
    <button class="btn btn-primary" onclick={handleSubmit}>Save</button>
  {/snippet}
</Modal>

Form 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;
  }
}

Error Handling

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

Project Structure

src/
  lib/
    api/          # API clients per feature
    stores/       # Svelte stores (auth, config, valuesets)
    components/   # Reusable components (Modal, DataTable, Sidebar)
    utils/        # Utilities (toast, helpers)
    types/        # TypeScript type definitions
  routes/         # SvelteKit routes
    (app)/        # Route groups (protected)
      dashboard/
      patients/
      master-data/
    login/        # Public routes

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, clqms_remember, auth_token

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: /apihttp://localhost:8000 (dev)
  • API Docs: https://clqms01-api.services-summit.my.id/swagger/