Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 47 additions & 28 deletions frontend/AGENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,21 @@ This file provides guidance to AI agents when working with code in this reposito

1. **Use `loading` not `isLoading`** for all loading state in stores
2. **Import types from `@/types/utils`** — never define local type aliases
3. **Delegate API calls from stores to services** — stores manage state only
4. **Use `<script setup lang="ts">`** for all Vue components
5. **Use composables** (`usePagination`, `useModalWithData`) for common patterns
6. **Run `bun run build`** to verify TypeScript after any changes
3. **Import Zod schemas as `z<SchemaName>`** from `@/types/zod.gen` (e.g. `zUserBase`, `zAssessmentBase`)
4. **Use `toTypedSchema` from `@/utils/zodAdapter`** — never from `@vee-validate/zod` (it doesn't support Zod v4)
5. **Delegate API calls from stores to services** — stores manage state only
6. **Use `<script setup lang="ts">`** for all Vue components
7. **Use composables** (`usePagination`, `useModalWithData`) for common patterns
8. **Run `bun run build`** to verify TypeScript after any changes

### ❌ DON'T

1. **Don't edit `schema.ts` or `zod.ts`** — they are auto-generated
1. **Don't edit `types.gen.ts` or `zod.gen.ts`** — they are auto-generated by `@hey-api/openapi-ts`
2. **Don't make API calls directly from stores** — use the service layer
3. **Don't define types like `type UserRead = components['schemas']['UserRead']`** — import from utils
4. **Don't use `isLoading`** — always use `loading`
5. **Don't manually manage pagination state** — use `usePagination` composable
3. **Don't define types like `type UserRead = components['schemas']['UserRead']`** — that pattern is from the old `openapi-typescript` generator; import from `@/types/utils` instead
4. **Don't import `{ schemas }` from `@/types/zod.gen`** — there is no `schemas` namespace; import the named `z<X>` exports directly
5. **Don't use `isLoading`** — always use `loading`
6. **Don't manually manage pagination state** — use `usePagination` composable

---

Expand All @@ -36,26 +39,32 @@ bun dev # Start dev server
bun run build # Type-check and build for production
bun run preview # Preview production build

# Type generation (requires backend on localhost:8000)
bun run gen:types # Generate TypeScript types from OpenAPI
bun run gen:zod # Generate Zod schemas from OpenAPI
# API code generation (requires backend on localhost:8000)
bun run fetch:openapi # Download openapi.json from the running backend
bun run gen:api # Run @hey-api/openapi-ts → types.gen.ts + zod.gen.ts
bun run update:api # Both of the above in sequence
```

Configuration: [openapi-ts.config.ts](openapi-ts.config.ts).

---

## Architecture Patterns

### 1. Type Imports

**Always import from `@/types/utils`** — this is the single source of truth for entity types.
**Always import from `@/types/utils`** — this is the single source of truth for entity types. `utils.ts` re-exports named types from `types.gen.ts` and adds hand-written utility types.

```typescript
// ✅ CORRECT
import type { UserRead, AssessmentRead, PaginationParams } from '@/types/utils';

// ❌ WRONG - Don't define local type aliases
import type { components } from '@/types/schema';
type UserRead = components['schemas']['UserRead']; // Never do this!
// ❌ WRONG - the components['schemas']['X'] pattern is from the old generator
import type { components } from '@/types/types.gen';
type UserRead = components['schemas']['UserRead']; // `components` does not exist!

// ❌ WRONG - don't import generated types directly when utils.ts already re-exports them
import type { UserRead } from '@/types/types.gen';
```

**Available types in `utils.ts`:**
Expand All @@ -65,7 +74,7 @@ type UserRead = components['schemas']['UserRead']; // Never do this!
- Enums: `ActivityPriority`, `ActivityState`, `ActivitySeverity`, `UserRole`, `AclRole`
- Utilities: `PaginatedResponse<T>`, `PaginationParams`, `PaginationState`, `MessageResponse`

If you need a type that's not exported, **add it to `utils.ts`** first.
If you need a type that's not re-exported, **add it to `utils.ts`** first by adding the name to the existing `export type { … } from './types.gen'` block.

---

Expand Down Expand Up @@ -219,13 +228,19 @@ const emit = defineEmits<{
#### Form Validation
```typescript
import { useForm } from 'vee-validate';
import { toTypedSchema } from '@vee-validate/zod';
import { schemas } from '@/types/zod';
import { toTypedSchema } from '@/utils/zodAdapter';
import { zUserBase } from '@/types/zod.gen';

const formSchema = toTypedSchema(schemas.UserBase);
const formSchema = toTypedSchema(zUserBase);
const { handleSubmit, isSubmitting } = useForm({ validationSchema: formSchema });
```

**Notes on validation:**
- Generated Zod schemas are exported individually as `z<SchemaName>` (e.g. `zUserBase`, `zAssessmentBase`, `zBodyLoginApiV1AuthTokenPost`). There is **no** `schemas` namespace.
- `toTypedSchema` lives in [src/utils/zodAdapter.ts](src/utils/zodAdapter.ts) — a small Zod v4 → vee-validate `TypedSchema` adapter. The published `@vee-validate/zod` only supports Zod v3.
- Validation runs **only on submit** (configured globally in `main.ts` via `configure({ validateOnBlur: false, validateOnChange: false, validateOnInput: false, validateOnModelUpdate: false })`). After a failed submit, vee-validate auto-revalidates each field as the user edits it, so errors clear live once the user has been told there's a problem.
- Required-field errors render as `"Required"` (not Zod v4's default `"Invalid input: expected string, received undefined"`) — see the `z.config({ customError })` block in `main.ts`.

---

### 6. File Organization
Expand All @@ -241,11 +256,13 @@ src/
├── services/ # API communication layer
├── stores/ # Pinia state management
├── types/
│ ├── schema.ts # 🔒 Auto-generated - DO NOT EDIT
│ ├── zod.ts # 🔒 Auto-generated - DO NOT EDIT
│ ├── utils.ts # ✅ Shared entity types (add new types here)
│ ├── types.gen.ts # 🔒 Auto-generated by @hey-api/openapi-ts - DO NOT EDIT
│ ├── zod.gen.ts # 🔒 Auto-generated by @hey-api/openapi-ts - DO NOT EDIT
│ ├── utils.ts # ✅ Re-exports from types.gen + hand-written utility types
│ └── components.ts # ✅ Component-specific types
├── utils/ # Utility functions
├── utils/
│ ├── zodAdapter.ts # ✅ Local toTypedSchema (Zod v4 → vee-validate)
│ └── … # other utility functions
└── views/ # Route components
```

Expand All @@ -263,9 +280,12 @@ src/
}
```

2. **Add types to `utils.ts`** if needed:
2. **Add types to `utils.ts`** if needed — append the name to the `export type { … } from './types.gen'` block:
```typescript
export type NewType = components['schemas']['NewType'];
export type {
// …existing entries…
NewType,
} from './types.gen';
```

3. **Update store** to use the service:
Expand All @@ -290,11 +310,10 @@ src/
### After Backend API Changes

```bash
bun run gen:types # Regenerate schema.ts
bun run gen:zod # Regenerate zod.ts
bun run update:api # fetch openapi.json + regenerate types.gen.ts and zod.gen.ts
```

Then add any new types to `utils.ts` as exports.
Then add any new types to `utils.ts` as re-exports.

---

Expand Down
4 changes: 2 additions & 2 deletions frontend/biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"includes": [
"**",
"!!**/dist",
"!!src/types/schema.ts",
"!!src/types/zod.ts",
"!!src/types/types.gen.ts",
"!!src/types/zod.gen.ts",
"!!src/components/ui",
"!!openapi.json",
"!!src/style.css"
Expand Down
Loading
Loading