5.8 KiB
Frontend context
Read ai/shared.md first. This file covers frontend-specific rules.
Project context
Stack: Vue 3.5, TypeScript 6, Vite 8, Vue Router 5, Pinia 4, Zod 4, Cypress 15, npm.
Location: frontend/website/.
The frontend is a standalone application. Keep its source, dependencies, development server, and production build independent from the backend unless the user explicitly requests integration.
The application currently has route-level views, reusable authentication components, a Pinia authentication store, Zod schemas, API URL handling, and Cypress end-to-end specs. The main entry points are:
src/App.vuefor the root component.src/main.tsfor app creation, Pinia, the router, and global styles.src/router/index.tsfor routes and authentication guards.src/stores/for Pinia stores and API boundaries.src/views/andsrc/components/for route and reusable UI.@as thesrc/alias in Vite and TypeScript.
Package management and commands
Use npm and keep package-lock.json committed. Run commands from
frontend/website/.
Install dependencies in a fresh checkout or worktree:
direnv exec "$(git rev-parse --show-toplevel)" npm install
npm run format and npm run lint rewrite files. Their format:check and
lint:check counterparts are non-mutating completion checks. npm run build
runs type checking and the production build; generated dist/ output is
ignored.
process-compose starts the frontend as part of the complete development
stack. The frontend is directly accessible on VITE_PORT; Caddy serves the
backend and does not proxy the frontend.
Vue conventions
- Use the Composition API and
<script setup lang="ts">. - Use PascalCase component and view filenames.
- Put reusable components under
src/components/. - Put route-level views under
src/views/and register them insrc/router/index.ts. - Keep page, component, composable, and store responsibilities distinct.
- Prefer small components with explicit props and emitted events.
- Use setup-style Pinia stores named
useXxxStore. - Keep application-wide resets and base styles in
src/styles/main.css. Keep component and view styles scoped and match established Attainly visual patterns. - Display user-authored set level kinds verbatim. Do not capitalize, replace
separators, pluralize, or apply
text-transformto a kind value; pluralize surrounding interface words such as "assignment" instead. - Inspect similar files before introducing a component, composable, store, route, or data-access pattern.
TypeScript and runtime validation
- Preserve strict TypeScript and
noUncheckedIndexedAccess. - Do not use
any. Model unknown external values asunknown, then parse or narrow them. - Zod schemas are the source of truth for runtime payloads. Define a schema
and derive its TypeScript type with
z.inferinstead of maintaining a parallel hand-written interface. - Parse every backend response at the store or API boundary before placing it in application state.
- Keep read and write schemas separate when their shapes differ.
- Validate user-submitted forms with a Zod object schema when the form has meaningful validation rules. Surface field errors from the schema rather than maintaining parallel regular expressions and error logic.
- Keep request and response schemas and types near the API or store boundary that owns them.
- Keep the
@alias aligned across Vite, TypeScript, and future test tooling.
State and API access
- Use Pinia for shared client state. Keep component-local state in components.
- Keep requests, response parsing, and response transformation at an API or store boundary, not in presentation components.
- Represent loading, empty, success, validation-error, and unexpected-error states explicitly.
- Do not cast unchecked JSON to an application interface.
- Send cookie-backed API requests with the established credentials behavior.
Testing
- Cypress is configured under
cypress/and runs throughnpm run test:e2eorjust frontend-cypress-run. - Every Cypress spec must appear in exactly one
frontend_specs_*group in the rootjustfile. The completion gate runs those groups concurrently, and itscypress-spec-coveragejob rejects ungrouped, duplicate, or missing specs. Keepfrontend-cypress-runfor whole-suite iteration. - Prefer the cheapest test seam that proves the behavior. Cypress covers routing, browser forms, authentication flows, request wiring, and responsive behavior.
- Mock backend calls in frontend-focused Cypress tests. Use the worktree backend only for a deliberately end-to-end integration scenario.
- Assert both the request contract and the rendered response behavior when a spec intercepts an API call.
- Keep mock payloads synchronized with exported store types and Zod schemas. Add typed builders when payloads repeat across specs, and parse builder output through the exported schema when practical.
- Authentication requests are mockable like every other frontend boundary. Backend persistence, cookie creation, middleware, and mail behavior belong in PHPUnit tests.
- No Vitest unit or component suite is configured. Do not claim that coverage. If new pure logic or component behavior cannot be proved economically with Cypress, establish the smallest appropriate Vitest setup test-first.
Frontend workflow
- Run the focused Cypress spec while developing when browser behavior changes.
- Run
npm run formatandnpm run lintbefore committing frontend changes, then review every rewrite. - Use the focused
just frontend-*recipes for development feedback. - The shared
just test-allcommand is the required completion gate. Focused frontend checks never replace it. - Do not claim a green gate when a command fails. Report baseline or environmental failures precisely.