Skip to content

Commit 4931a0d

Browse files
committed
refactor: linting
1 parent 6c8b6b0 commit 4931a0d

11 files changed

Lines changed: 2307 additions & 1770 deletions

File tree

frontend/AGENT.md

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,21 @@ This file provides guidance to AI agents when working with code in this reposito
1010

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

1820
### ❌ DON'T
1921

20-
1. **Don't edit `schema.ts` or `zod.ts`** — they are auto-generated
22+
1. **Don't edit `types.gen.ts` or `zod.gen.ts`** — they are auto-generated by `@hey-api/openapi-ts`
2123
2. **Don't make API calls directly from stores** — use the service layer
22-
3. **Don't define types like `type UserRead = components['schemas']['UserRead']`** — import from utils
23-
4. **Don't use `isLoading`** — always use `loading`
24-
5. **Don't manually manage pagination state** — use `usePagination` composable
24+
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
25+
4. **Don't import `{ schemas }` from `@/types/zod.gen`** — there is no `schemas` namespace; import the named `z<X>` exports directly
26+
5. **Don't use `isLoading`** — always use `loading`
27+
6. **Don't manually manage pagination state** — use `usePagination` composable
2528

2629
---
2730

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

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

48+
Configuration: [openapi-ts.config.ts](openapi-ts.config.ts).
49+
4450
---
4551

4652
## Architecture Patterns
4753

4854
### 1. Type Imports
4955

50-
**Always import from `@/types/utils`** — this is the single source of truth for entity types.
56+
**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.
5157

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

56-
// ❌ WRONG - Don't define local type aliases
62+
// ❌ WRONG - the components['schemas']['X'] pattern is from the old generator
5763
import type { components } from '@/types/types.gen';
58-
type UserRead = components['schemas']['UserRead']; // Never do this!
64+
type UserRead = components['schemas']['UserRead']; // `components` does not exist!
65+
66+
// ❌ WRONG - don't import generated types directly when utils.ts already re-exports them
67+
import type { UserRead } from '@/types/types.gen';
5968
```
6069

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

68-
If you need a type that's not exported, **add it to `utils.ts`** first.
77+
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.
6978

7079
---
7180

@@ -219,13 +228,19 @@ const emit = defineEmits<{
219228
#### Form Validation
220229
```typescript
221230
import { useForm } from 'vee-validate';
222-
import { toTypedSchema } from '@vee-validate/zod';
223-
import { schemas } from '@/types/zod.gen';
231+
import { toTypedSchema } from '@/utils/zodAdapter';
232+
import { zUserBase } from '@/types/zod.gen';
224233

225-
const formSchema = toTypedSchema(schemas.UserBase);
234+
const formSchema = toTypedSchema(zUserBase);
226235
const { handleSubmit, isSubmitting } = useForm({ validationSchema: formSchema });
227236
```
228237

238+
**Notes on validation:**
239+
- Generated Zod schemas are exported individually as `z<SchemaName>` (e.g. `zUserBase`, `zAssessmentBase`, `zBodyLoginApiV1AuthTokenPost`). There is **no** `schemas` namespace.
240+
- `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.
241+
- 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.
242+
- 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`.
243+
229244
---
230245

231246
### 6. File Organization
@@ -241,11 +256,13 @@ src/
241256
├── services/ # API communication layer
242257
├── stores/ # Pinia state management
243258
├── types/
244-
│ ├── schema.ts # 🔒 Auto-generated - DO NOT EDIT
245-
│ ├── zod.ts # 🔒 Auto-generated - DO NOT EDIT
246-
│ ├── utils.ts # ✅ Shared entity types (add new types here)
259+
│ ├── types.gen.ts # 🔒 Auto-generated by @hey-api/openapi-ts - DO NOT EDIT
260+
│ ├── zod.gen.ts # 🔒 Auto-generated by @hey-api/openapi-ts - DO NOT EDIT
261+
│ ├── utils.ts # ✅ Re-exports from types.gen + hand-written utility types
247262
│ └── components.ts # ✅ Component-specific types
248-
├── utils/ # Utility functions
263+
├── utils/
264+
│ ├── zodAdapter.ts # ✅ Local toTypedSchema (Zod v4 → vee-validate)
265+
│ └── … # other utility functions
249266
└── views/ # Route components
250267
```
251268

@@ -263,9 +280,12 @@ src/
263280
}
264281
```
265282

266-
2. **Add types to `utils.ts`** if needed:
283+
2. **Add types to `utils.ts`** if needed — append the name to the `export type { … } from './types.gen'` block:
267284
```typescript
268-
export type NewType = components['schemas']['NewType'];
285+
export type {
286+
// …existing entries…
287+
NewType,
288+
} from './types.gen';
269289
```
270290

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

292312
```bash
293-
bun run gen:types # Regenerate schema.ts
294-
bun run gen:zod # Regenerate zod.ts
313+
bun run update:api # fetch openapi.json + regenerate types.gen.ts and zod.gen.ts
295314
```
296315

297-
Then add any new types to `utils.ts` as exports.
316+
Then add any new types to `utils.ts` as re-exports.
298317

299318
---
300319

frontend/openapi-ts.config.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import { defineConfig } from '@hey-api/openapi-ts';
22

33
export default defineConfig({
4-
input: './openapi.json',
5-
output: {
6-
path: './src/types',
7-
clean: false,
8-
entryFile: false,
9-
},
10-
plugins: [
11-
'@hey-api/typescript',
12-
{
13-
name: 'zod',
14-
required: true,
15-
}
16-
],
17-
});
4+
input: './openapi.json',
5+
output: {
6+
path: './src/types',
7+
clean: false,
8+
entryFile: false,
9+
},
10+
plugins: [
11+
'@hey-api/typescript',
12+
{
13+
name: 'zod',
14+
required: true,
15+
},
16+
],
17+
});

frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,4 @@
5757
"overrides": {
5858
"handlebars": "^4.7.9"
5959
}
60-
}
60+
}

frontend/src/components/assessment/ActivityGeneralInfo.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ import {
3333
import { useMitre } from '@/composables/useMitre';
3434
import { tagService } from '@/services/tagService';
3535
import type { ActivityGroupRead, ActivityRead, TagRead } from '@/types/utils';
36-
import { zActivityPriority, zActivityState, zActivitySeverity } from '@/types/zod.gen';
36+
import {
37+
zActivityPriority,
38+
zActivityState,
39+
zActivitySeverity,
40+
} from '@/types/zod.gen';
3741
3842
const props = defineProps<{
3943
assessmentId: string;

frontend/src/components/profile/ProfileResetPassword.vue

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ import { zUserPasswordUpdate } from '@/types/zod.gen';
2020
const authStore = useAuthStore();
2121
2222
const formSchema = toTypedSchema(
23-
zUserPasswordUpdate.extend({
24-
confirm_password: z.string(),
25-
}).refine((data) => data.new_password === data.confirm_password, {
26-
message: "Passwords don't match",
27-
path: ['confirm_password'],
28-
}),
23+
zUserPasswordUpdate
24+
.extend({
25+
confirm_password: z.string(),
26+
})
27+
.refine((data) => data.new_password === data.confirm_password, {
28+
message: "Passwords don't match",
29+
path: ['confirm_password'],
30+
}),
2931
);
3032
3133
const { handleSubmit, isSubmitting, resetForm } = useForm({

frontend/src/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { configure } from 'vee-validate';
21
import { createPinia } from 'pinia';
2+
import { configure } from 'vee-validate';
33
import { createApp } from 'vue';
44
import { z } from 'zod';
55
import router from './router';

frontend/src/stores/auth.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@ import { type User as OidcUser, UserManager } from 'oidc-client-ts';
22
import { defineStore } from 'pinia';
33
import { computed, ref } from 'vue';
44
import { api } from '@/services/api';
5-
import type { BodyLoginApiV1AuthTokenPost, MfaSetupResponse, Otp, Token, UserPasswordUpdate } from '@/types/types.gen';
5+
import type {
6+
BodyLoginApiV1AuthTokenPost,
7+
MfaSetupResponse,
8+
Otp,
9+
Token,
10+
UserPasswordUpdate,
11+
} from '@/types/types.gen';
612
import type { AclRole, ExternalAuthProvider, UserReadAcl } from '@/types/utils';
713

814
type LoginBody = BodyLoginApiV1AuthTokenPost;

0 commit comments

Comments
 (0)