Attainly/ai/frontend-context.md

5.3 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.vue for the root component.
  • src/main.ts for app creation, Pinia, the router, and global styles.
  • src/router/index.ts for routes and authentication guards.
  • src/stores/ for Pinia stores and API boundaries.
  • src/views/ and src/components/ for route and reusable UI.
  • @ as the src/ 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 in src/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.
  • 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 as unknown, 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.infer instead 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 through npm run test:e2e or just frontend-cypress-run.
  • 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 format and npm run lint before committing frontend changes, then review every rewrite.
  • Use the focused just frontend-* recipes for development feedback.
  • The shared just test-all command 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.