frontend · git:20260916.b334104 · 2026-09-16 · sha256 57c9a4ee7edea270
frontend git:20260916.b334104B
Immutable. This exact content is served forever at /api/v1/blob/57c9a4ee7edea270.
---
name: frontend
description: Vue frontend — components, Vitest, generated API client, apiCallWithLoading, DaisyUI. Read this first for any frontend work. Use when working on the Vue frontend.
paths:
- "frontend/**"
---
# Frontend
Vue 3 + TypeScript in `frontend/src/`. Tests in `frontend/tests/`. Import the backend client from `@generated/donut-backend-api/sdk.gen`.
Matching `frontend-component`, `frontend-testing`, `frontend-api`, and `frontend-storybook` skills attach via `paths` when those files are in context — do not fetch them up front. Lint: `linting_formating` skill.
## Commands
```bash
CURSOR_DEV=true nix develop -c pnpm frontend:test
```
Spec paths are relative to `frontend/`. Do not pass `--` before the path (Vitest skips file filtering). Prefer the full frontend unit suite; a single file only while iterating on that file.
## Components
- `<script setup lang="ts">`. PascalCase `.vue` files; tests `ComponentName.spec.ts`.
- DaisyUI uses the `daisy-` prefix (`daisy-btn`, `daisy-card`). Unprefixed Tailwind utilities. No Bootstrap. Scoped SCSS. Lucide via `@lucide/vue` (`currentColor`, `:size` or Tailwind `size-*`).
- Derived state: `computed`, not a `ref` that mirrors props/store. `ref` only for owned mutable state (input, toggles).
- Modals: `Modal` from `@/components/commons/Modal.vue`. In tests, query `document.querySelector("dialog")`.
- Routing: name and params (helpers such as `noteShowLocation`); href only via `noteShowHref` (compiled from the named location). ADR 0005.
## API
User actions: `apiCallWithLoading(() => Controller.method(...))` from `@/managedApi/clientSetup`. Wrapped `{ data, error, request, response }`. Check `!error` before using `data`. Silent refresh: call the SDK directly (no loading bar or error toast).
`{ blockUi: true }` for a whole-UI blocker. `{ blockUi: true, cancelable: true }` only for allowlisted read-only sites gated by `frontend/tests/managedApi/cancelableAllowlist.spec.ts`. Do not opt mutations into cancelable. Do not add component-local `LoadingModal` refs.
Loading UI that means unfinished work marks `data-app-busy` (`LoadingThinBar`, `ContentLoader`, `LoadingModal`). E2E: `waitUntilAppIsNotBusy()`. Validation 400: `toOpenApiError(error)`.
## Tests
Drive the mounted component. Mock only the backend HTTP API with `mockSdkService(Controller, "method", payload)` from `@tests/helpers`. Payloads: `donut-test-fixtures/makeMe`. No `getByRole` (slow). Prefer `render()` from Testing Library. Vitest browser mode; do not use jsdom.