@famgia/omnify-ai-guides 2.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +105 -0
  2. package/dist/chunk-RCTEXK7C.js +549 -0
  3. package/dist/chunk-RCTEXK7C.js.map +1 -0
  4. package/dist/config/rules.yaml +524 -0
  5. package/dist/index.cjs +587 -0
  6. package/dist/index.cjs.map +1 -0
  7. package/dist/index.d.cts +55 -0
  8. package/dist/index.d.ts +55 -0
  9. package/dist/index.js +26 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/knowledge/agents/architect.md.stub +150 -0
  12. package/dist/knowledge/agents/developer.md.stub +190 -0
  13. package/dist/knowledge/agents/reviewer.md.stub +134 -0
  14. package/dist/knowledge/agents/tester.md.stub +196 -0
  15. package/dist/knowledge/checklists/backend.md.stub +112 -0
  16. package/dist/knowledge/checklists/react.md.stub +108 -0
  17. package/dist/knowledge/claude-rules/laravel-controllers.md.stub +57 -0
  18. package/dist/knowledge/claude-rules/laravel-migrations.md.stub +47 -0
  19. package/dist/knowledge/claude-rules/laravel-tests.md.stub +52 -0
  20. package/dist/knowledge/claude-rules/naming.md.stub +369 -0
  21. package/dist/knowledge/claude-rules/performance.md.stub +256 -0
  22. package/dist/knowledge/claude-rules/php-standards.md.stub +305 -0
  23. package/dist/knowledge/claude-rules/react-components.md.stub +67 -0
  24. package/dist/knowledge/claude-rules/schema-yaml.md.stub +83 -0
  25. package/dist/knowledge/claude-rules/security.md.stub +164 -0
  26. package/dist/knowledge/cursor-rules/antd-deprecations.mdc.stub +62 -0
  27. package/dist/knowledge/cursor-rules/basemodel-readonly.mdc.stub +66 -0
  28. package/dist/knowledge/cursor-rules/baserequest-readonly.mdc.stub +74 -0
  29. package/dist/knowledge/cursor-rules/baseresource-readonly.mdc.stub +78 -0
  30. package/dist/knowledge/cursor-rules/laravel-controller.mdc.stub +421 -0
  31. package/dist/knowledge/cursor-rules/laravel-request.mdc.stub +112 -0
  32. package/dist/knowledge/cursor-rules/laravel-resource.mdc.stub +73 -0
  33. package/dist/knowledge/cursor-rules/laravel-review.mdc.stub +69 -0
  34. package/dist/knowledge/cursor-rules/laravel-testing.mdc.stub +138 -0
  35. package/dist/knowledge/cursor-rules/laravel.mdc.stub +138 -0
  36. package/dist/knowledge/cursor-rules/migrations-workflow.mdc.stub +224 -0
  37. package/dist/knowledge/cursor-rules/model-editable.mdc.stub +120 -0
  38. package/dist/knowledge/cursor-rules/omnify-migrations.mdc.stub +109 -0
  39. package/dist/knowledge/cursor-rules/omnify-schema.mdc.stub +358 -0
  40. package/dist/knowledge/cursor-rules/omnify.mdc.stub +58 -0
  41. package/dist/knowledge/cursor-rules/react-design.mdc.stub +693 -0
  42. package/dist/knowledge/cursor-rules/react-form.mdc.stub +292 -0
  43. package/dist/knowledge/cursor-rules/react-services.mdc.stub +304 -0
  44. package/dist/knowledge/cursor-rules/react.mdc.stub +336 -0
  45. package/dist/knowledge/cursor-rules/request-editable.mdc.stub +111 -0
  46. package/dist/knowledge/cursor-rules/resource-editable.mdc.stub +125 -0
  47. package/dist/knowledge/cursor-rules/schema-create.mdc.stub +440 -0
  48. package/dist/knowledge/cursor-rules/validation-rules.mdc.stub +181 -0
  49. package/dist/knowledge/laravel/README.md.stub +59 -0
  50. package/dist/knowledge/laravel/architecture.md.stub +424 -0
  51. package/dist/knowledge/laravel/authentication.md.stub +588 -0
  52. package/dist/knowledge/laravel/controller.md.stub +484 -0
  53. package/dist/knowledge/laravel/datetime.md.stub +334 -0
  54. package/dist/knowledge/laravel/migrations-team.md.stub +376 -0
  55. package/dist/knowledge/laravel/openapi.md.stub +449 -0
  56. package/dist/knowledge/laravel/request.md.stub +450 -0
  57. package/dist/knowledge/laravel/resource.md.stub +516 -0
  58. package/dist/knowledge/laravel/service.md.stub +503 -0
  59. package/dist/knowledge/laravel/testing.md.stub +1504 -0
  60. package/dist/knowledge/omnify/antdesign-guide.md.stub +401 -0
  61. package/dist/knowledge/omnify/config-guide.md.stub +405 -0
  62. package/dist/knowledge/omnify/japan-guide.md.stub +186 -0
  63. package/dist/knowledge/omnify/laravel-guide.md.stub +61 -0
  64. package/dist/knowledge/omnify/partial-schema-guide.md.stub +353 -0
  65. package/dist/knowledge/omnify/react-form-guide.md.stub +225 -0
  66. package/dist/knowledge/omnify/schema-guide.md.stub +144 -0
  67. package/dist/knowledge/omnify/typescript-guide.md.stub +337 -0
  68. package/dist/knowledge/react/README.md.stub +221 -0
  69. package/dist/knowledge/react/antd-guide.md +528 -0
  70. package/dist/knowledge/react/antd-guide.md.stub +528 -0
  71. package/dist/knowledge/react/checklist.md.stub +108 -0
  72. package/dist/knowledge/react/datetime-guide.md.stub +137 -0
  73. package/dist/knowledge/react/design-philosophy.md.stub +363 -0
  74. package/dist/knowledge/react/i18n-guide.md.stub +211 -0
  75. package/dist/knowledge/react/laravel-integration.md.stub +181 -0
  76. package/dist/knowledge/react/service-pattern.md.stub +180 -0
  77. package/dist/knowledge/react/tanstack-query.md.stub +339 -0
  78. package/dist/knowledge/react/types-guide.md +669 -0
  79. package/dist/knowledge/react/types-guide.md.stub +669 -0
  80. package/dist/knowledge/workflows/bug-fix.md.stub +201 -0
  81. package/dist/knowledge/workflows/code-review.md.stub +164 -0
  82. package/dist/knowledge/workflows/new-feature.md.stub +327 -0
  83. package/dist/plugin-M95GyBll.d.cts +191 -0
  84. package/dist/plugin-M95GyBll.d.ts +191 -0
  85. package/dist/plugin.cjs +573 -0
  86. package/dist/plugin.cjs.map +1 -0
  87. package/dist/plugin.d.cts +2 -0
  88. package/dist/plugin.d.ts +2 -0
  89. package/dist/plugin.js +15 -0
  90. package/dist/plugin.js.map +1 -0
  91. package/package.json +53 -0
@@ -0,0 +1,669 @@
1
+ # TypeScript Types Guide
2
+
3
+ > This guide defines where and how to define types in this project.
4
+
5
+ ## Type Categories
6
+
7
+ | Category | Location | Generated | Example |
8
+ | ------------------- | ----------------- | --------- | --------------------------------------- |
9
+ | **Model** | `@omnify/schemas` | ✅ Omnify | `User`, `Post` |
10
+ | **Create/Update** | `@omnify/schemas` | ✅ Omnify | `UserCreate`, `UserUpdate` |
11
+ | **Common** | `@omnify/schemas` | ✅ Omnify | `DateTimeString`, `LocaleMap` |
12
+ | **Zod Schemas** | `@omnify/schemas` | ✅ Omnify | `userSchemas`, `userCreateSchema` |
13
+ | **i18n** | `@omnify/schemas` | ✅ Omnify | `getUserLabel()`, `getUserFieldLabel()` |
14
+ | **Enum** | `@omnify/enum` | ✅ Omnify | `PostStatus`, `MediaType` |
15
+ | **API Params** | Service file | ❌ Manual | `UserListParams` |
16
+ | **API Response** | `@/lib/api.ts` | ❌ Manual | `PaginatedResponse<T>` |
17
+ | **Component Props** | Component file | ❌ Manual | `UserTableProps` |
18
+
19
+ ---
20
+
21
+ ## 1. Model Types (Omnify)
22
+
23
+ **Location**: `{{TYPESCRIPT_BASE}}/omnify/schemas/`
24
+
25
+ **Source**: Auto-generated from Omnify schema YAML files
26
+
27
+ ```typescript
28
+ // ✅ Import from @omnify/schemas
29
+ import type { User, UserCreate, UserUpdate } from "@omnify/schemas";
30
+ import type { DateTimeString, LocaleMap } from "@omnify/schemas";
31
+ import { userSchemas, userCreateSchema, userUpdateSchema } from "@omnify/schemas";
32
+ import { getUserLabel, getUserFieldLabel, getUserFieldPlaceholder } from "@omnify/schemas";
33
+
34
+ // ❌ DON'T define model types manually
35
+ interface User { ... } // WRONG - already generated
36
+ ```
37
+
38
+ ### Structure
39
+
40
+ ```
41
+ {{TYPESCRIPT_BASE}}/omnify/
42
+ ├── schemas/ # Model types and schemas
43
+ │ ├── index.ts ❌ DO NOT EDIT (re-exports)
44
+ │ ├── i18n.ts ❌ DO NOT EDIT (i18n config)
45
+ │ └── User.ts ✅ CAN EDIT (extension file)
46
+ └── enum/ # Enum types
47
+ └── PostStatus.ts ❌ DO NOT EDIT
48
+ ```
49
+
50
+ **Note**: Base types come from `@omnify-base/schemas/` (in node_modules), enums from `@omnify-base/enum/`
51
+
52
+ ### Generated Types Per Model
53
+
54
+ ```typescript
55
+ // Auto-generated in base/User.ts:
56
+ interface User {
57
+ id: number;
58
+ name: string;
59
+ email: string;
60
+ created_at?: DateTimeString; // Uses DateTimeString
61
+ updated_at?: DateTimeString;
62
+ }
63
+
64
+ type UserCreate = Omit<User, 'id' | 'created_at' | 'updated_at'>;
65
+ type UserUpdate = Partial<UserCreate>;
66
+ ```
67
+
68
+ ### Extending Model Types
69
+
70
+ ```typescript
71
+ // src/types/model/User.ts (safe to edit)
72
+ import type { User as UserBase } from "./base/User";
73
+
74
+ export interface User extends UserBase {
75
+ // Frontend-only computed properties
76
+ fullName?: string;
77
+
78
+ // UI state
79
+ isSelected?: boolean;
80
+ }
81
+ ```
82
+
83
+ ---
84
+
85
+ ## 2. Enum Types (CRITICAL)
86
+
87
+ **Location**: `@omnify/enum/` or re-exported from `@omnify/schemas`
88
+
89
+ **ALWAYS use generated Enums - NEVER inline union types!**
90
+
91
+ ### Generated Enum Structure
92
+
93
+ ```typescript
94
+ // @omnify/enum/PostStatus.ts (auto-generated)
95
+
96
+ // 1. Enum type
97
+ export enum PostStatus {
98
+ Draft = "draft",
99
+ Published = "published",
100
+ Archived = "archived",
101
+ }
102
+
103
+ // 2. Values array (for iteration)
104
+ export const PostStatusValues = [
105
+ PostStatus.Draft,
106
+ PostStatus.Published,
107
+ PostStatus.Archived,
108
+ ] as const;
109
+
110
+ // 3. Type guard
111
+ export function isPostStatus(value: unknown): value is PostStatus {
112
+ return PostStatusValues.includes(value as PostStatus);
113
+ }
114
+
115
+ // 4. i18n label getter
116
+ export function getPostStatusLabel(value: PostStatus, locale: string): string {
117
+ // Returns localized label
118
+ }
119
+ ```
120
+
121
+ ### ❌ FORBIDDEN Patterns
122
+
123
+ ```typescript
124
+ // ❌ Complex type extraction - FORBIDDEN!
125
+ const status = value as NonNullable<ListParams["filter"]>["status"];
126
+
127
+ // ❌ Hardcoded union type - NOT DRY!
128
+ const status: "draft" | "published" | "archived" = "draft";
129
+
130
+ // ❌ String type - NO TYPE SAFETY!
131
+ const [status, setStatus] = useState<string>("");
132
+
133
+ // ❌ Hardcoded string comparisons
134
+ if (status === "draft") { ... }
135
+ ```
136
+
137
+ ### ✅ REQUIRED Patterns
138
+
139
+ ```typescript
140
+ // ✅ Import from generated enum file
141
+ import {
142
+ PostStatus,
143
+ PostStatusValues,
144
+ isPostStatus,
145
+ getPostStatusLabel
146
+ } from "@omnify/enum/PostStatus";
147
+
148
+ // ✅ Type assertions
149
+ const status = unknownValue as PostStatus;
150
+
151
+ // ✅ State with Enum
152
+ const [status, setStatus] = useState<PostStatus | "">("");
153
+ const [status, setStatus] = useState<PostStatus>(PostStatus.Pending);
154
+
155
+ // ✅ Enum comparisons
156
+ if (status === PostStatus.Pending) { ... }
157
+
158
+ // ✅ Iterate with Values array
159
+ const options = PostStatusValues.map(value => ({
160
+ value,
161
+ label: getPostStatusLabel(value, locale)
162
+ }));
163
+
164
+ // ✅ Type guard
165
+ if (isPostStatus(value)) {
166
+ // value is PostStatus
167
+ }
168
+ ```
169
+
170
+ ### Select/Filter Options Pattern
171
+
172
+ ```typescript
173
+ import {
174
+ PostStatus,
175
+ PostStatusValues,
176
+ getPostStatusLabel
177
+ } from "@omnify/enum/PostStatus";
178
+ import { useLocale } from "next-intl";
179
+
180
+ function StatusFilter() {
181
+ const locale = useLocale();
182
+
183
+ // ✅ Build options from generated enum
184
+ const statusOptions = PostStatusValues.map(value => ({
185
+ value,
186
+ label: getPostStatusLabel(value, locale)
187
+ }));
188
+
189
+ // ✅ State with Enum type
190
+ const [status, setStatus] = useState<PostStatus | "all">("all");
191
+
192
+ return (
193
+ <Select
194
+ value={status}
195
+ onChange={setStatus}
196
+ options={[
197
+ { value: "all", label: t("common.all") },
198
+ ...statusOptions
199
+ ]}
200
+ />
201
+ );
202
+ }
203
+ ```
204
+
205
+ ### Filter Params with Enum
206
+
207
+ ```typescript
208
+ import { PostStatus } from "@omnify/enum/PostStatus";
209
+
210
+ interface AttendanceListParams {
211
+ filter?: {
212
+ approval_status?: PostStatus; // ✅ Use Enum type
213
+ };
214
+ }
215
+
216
+ // ✅ Type assertion with Enum
217
+ const handleStatusChange = (value: string) => {
218
+ setFilters(prev => ({
219
+ ...prev,
220
+ filter: {
221
+ ...prev.filter,
222
+ approval_status: value as PostStatus // ✅ Not inline union type!
223
+ }
224
+ }));
225
+ };
226
+ ```
227
+
228
+ ---
229
+
230
+ ## 3. Using Generated Types
231
+
232
+ ### Create/Update Types
233
+
234
+ ```typescript
235
+ // ✅ Use Omnify-generated types
236
+ import type { User, UserCreate, UserUpdate } from "@omnify/schemas";
237
+
238
+ const userService = {
239
+ create: (input: UserCreate) => api.post("/api/users", input),
240
+ update: (id: number, input: UserUpdate) => api.put(`/api/users/${id}`, input),
241
+ };
242
+ ```
243
+
244
+ ### Validation Rules with Ant Design Form.Item
245
+
246
+ ```typescript
247
+ import { userSchemas, getUserFieldLabel } from "@omnify/schemas/User";
248
+ import { zodRule } from "@/lib/form-validation";
249
+ import { useLocale } from "next-intl";
250
+
251
+ function UserForm() {
252
+ const locale = useLocale();
253
+ const label = (key: string) => getUserFieldLabel(key, locale);
254
+
255
+ return (
256
+ <Form>
257
+ {/* Name */}
258
+ <Form.Item
259
+ name="name"
260
+ label={label("name")}
261
+ rules={[zodRule(userSchemas.name, label("name"))]}
262
+ >
263
+ <Input />
264
+ </Form.Item>
265
+
266
+ {/* Email */}
267
+ <Form.Item
268
+ name="email"
269
+ label={label("email")}
270
+ rules={[zodRule(userSchemas.email, label("email"))]}
271
+ >
272
+ <Input />
273
+ </Form.Item>
274
+ </Form>
275
+ );
276
+ }
277
+ ```
278
+
279
+ **Key Points:**
280
+ - Import `{model}Schemas` for Zod validation schemas
281
+ - Import `zodRule` from `@/lib/form-validation`
282
+ - Use `zodRule(schema, displayName)` in Form.Item rules
283
+ - Comment `{/* Field Name */}` before each Form.Item for clarity
284
+
285
+ ### DateTimeString
286
+
287
+ ```typescript
288
+ import type { DateTimeString } from "@omnify/schemas";
289
+ import { formatDateTime } from "@/lib/dayjs";
290
+
291
+ interface Event {
292
+ scheduled_at: DateTimeString; // ISO 8601 UTC string
293
+ }
294
+
295
+ // Display
296
+ formatDateTime(event.scheduled_at); // "2024/01/15 19:30"
297
+ ```
298
+
299
+ ---
300
+
301
+ ## 3. API Params Types (Manual)
302
+
303
+ **Location**: Service file (colocated)
304
+
305
+ **Only define query params (not in Omnify):**
306
+
307
+ ```typescript
308
+ // services/users.ts
309
+ import type { User, UserCreate, UserUpdate } from "@omnify/schemas";
310
+
311
+ /** Query params for listing users (GET /api/users) */
312
+ export interface UserListParams {
313
+ search?: string;
314
+ role?: string;
315
+ page?: number;
316
+ per_page?: number;
317
+ sort_by?: keyof User;
318
+ sort_order?: "asc" | "desc";
319
+ }
320
+
321
+ export const userService = {
322
+ list: (params?: UserListParams) => ...,
323
+ create: (input: UserCreate) => ..., // ← Use Omnify type
324
+ update: (id: number, input: UserUpdate) => ..., // ← Use Omnify type
325
+ };
326
+ ```
327
+
328
+ ---
329
+
330
+ ## 4. API Response Types
331
+
332
+ **Location**: `src/lib/api.ts`
333
+
334
+ **Naming**: `{Name}Response`, `Paginated{Name}`
335
+
336
+ ```typescript
337
+ // lib/api.ts
338
+
339
+ /** Laravel paginated response */
340
+ export interface PaginatedResponse<T> {
341
+ data: T[];
342
+ links: {
343
+ first: string | null;
344
+ last: string | null;
345
+ prev: string | null;
346
+ next: string | null;
347
+ };
348
+ meta: {
349
+ current_page: number;
350
+ from: number | null;
351
+ last_page: number;
352
+ per_page: number;
353
+ to: number | null;
354
+ total: number;
355
+ };
356
+ }
357
+
358
+ /** Laravel single resource response */
359
+ export interface ResourceResponse<T> {
360
+ data: T;
361
+ }
362
+
363
+ /** Laravel validation error (422) */
364
+ export interface ValidationError {
365
+ message: string;
366
+ errors: Record<string, string[]>;
367
+ }
368
+ ```
369
+
370
+ ### Usage in Service
371
+
372
+ ```typescript
373
+ import api, { PaginatedResponse } from "@/lib/api";
374
+ import type { User } from "@omnify/schemas";
375
+
376
+ export const userService = {
377
+ list: async (params?: UserListParams): Promise<PaginatedResponse<User>> => {
378
+ const { data } = await api.get("/api/users", { params });
379
+ return data;
380
+ },
381
+ };
382
+ ```
383
+
384
+ ---
385
+
386
+ ## 4. Component Props Types
387
+
388
+ **Location**: Same file as component (inline)
389
+
390
+ **Naming**: `{Component}Props`
391
+
392
+ ```typescript
393
+ // components/tables/UserTable.tsx
394
+
395
+ import type { User } from "@omnify/schemas";
396
+ import type { PaginatedResponse } from "@/lib/api";
397
+
398
+ // ─────────────────────────────────────────────────────────────────
399
+ // Props - Define at top of file
400
+ // ─────────────────────────────────────────────────────────────────
401
+
402
+ interface UserTableProps {
403
+ users: User[];
404
+ loading?: boolean;
405
+ pagination?: PaginatedResponse<User>["meta"];
406
+ onPageChange?: (page: number) => void;
407
+ onEdit?: (user: User) => void;
408
+ onDelete?: (user: User) => void;
409
+ }
410
+
411
+ // ─────────────────────────────────────────────────────────────────
412
+ // Component
413
+ // ─────────────────────────────────────────────────────────────────
414
+
415
+ export function UserTable({
416
+ users,
417
+ loading = false,
418
+ pagination,
419
+ onPageChange,
420
+ onEdit,
421
+ onDelete,
422
+ }: UserTableProps) {
423
+ return <Table ... />;
424
+ }
425
+ ```
426
+
427
+ ### When to Export Props
428
+
429
+ ```typescript
430
+ // ✅ Export if other components need it
431
+ export interface UserTableProps { ... }
432
+
433
+ // ✅ Don't export if only used internally
434
+ interface UserTableProps { ... }
435
+ ```
436
+
437
+ ---
438
+
439
+ ## 5. Hook Types
440
+
441
+ **Location**: Hook file (inline or inferred)
442
+
443
+ **Approach**: Let TypeScript infer return types when possible
444
+
445
+ ```typescript
446
+ // hooks/useUsers.ts
447
+
448
+ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
449
+ import { userService, UserCreateInput } from "@/services/users";
450
+ import { queryKeys } from "@/lib/queryKeys";
451
+
452
+ export function useUsers(params?: UserListParams) {
453
+ // Return type is inferred from userService.list
454
+ return useQuery({
455
+ queryKey: queryKeys.users.list(params),
456
+ queryFn: () => userService.list(params),
457
+ });
458
+ }
459
+
460
+ export function useCreateUser() {
461
+ const queryClient = useQueryClient();
462
+
463
+ // Return type is inferred from useMutation
464
+ return useMutation({
465
+ mutationFn: (input: UserCreateInput) => userService.create(input),
466
+ onSuccess: () => {
467
+ queryClient.invalidateQueries({ queryKey: queryKeys.users.all });
468
+ },
469
+ });
470
+ }
471
+ ```
472
+
473
+ ### When to Define Return Type
474
+
475
+ ```typescript
476
+ // ✅ Let TypeScript infer (simpler, less maintenance)
477
+ export function useUsers(params?: UserListParams) {
478
+ return useQuery({ ... });
479
+ }
480
+
481
+ // ✅ Define explicitly if complex or for documentation
482
+ export function useAuth(): {
483
+ user: User | undefined;
484
+ isLoading: boolean;
485
+ login: (input: LoginInput) => Promise<void>;
486
+ logout: () => Promise<void>;
487
+ } {
488
+ ...
489
+ }
490
+ ```
491
+
492
+ ---
493
+
494
+ ## 6. Shared/Utility Types
495
+
496
+ **Location**: `src/types/index.ts` (only if used across many files)
497
+
498
+ ```typescript
499
+ // types/index.ts
500
+
501
+ /** Common ID type */
502
+ export type ID = number;
503
+
504
+ /** Nullable type helper */
505
+ export type Nullable<T> = T | null;
506
+
507
+ /** Make specific keys optional */
508
+ export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
509
+
510
+ /** Extract array element type */
511
+ export type ArrayElement<T> = T extends (infer U)[] ? U : never;
512
+ ```
513
+
514
+ ### When to Use Shared Types
515
+
516
+ ```typescript
517
+ // ✅ Use shared types for truly common patterns
518
+ import type { ID, Nullable } from "@/types";
519
+
520
+ interface Post {
521
+ id: ID;
522
+ author_id: ID;
523
+ published_at: Nullable<string>;
524
+ }
525
+
526
+ // ❌ Don't over-abstract
527
+ // Bad: Creating shared types for every little thing
528
+ export type UserName = string; // Just use string
529
+ export type UserId = number; // Just use number
530
+ ```
531
+
532
+ ---
533
+
534
+ ## Type Definition Checklist
535
+
536
+ ### Before Creating a Type
537
+
538
+ 1. **Is it a Model?** → Use `@omnify/schemas` (Omnify)
539
+ 2. **Is it API input?** → Define in service file
540
+ 3. **Is it API response?** → Use/extend types in `lib/api.ts`
541
+ 4. **Is it component props?** → Define in component file
542
+ 5. **Is it used in 3+ places?** → Consider `types/index.ts`
543
+
544
+ ### Type Naming Conventions
545
+
546
+ | Type | Pattern | Example |
547
+ | ------------ | -------------------- | ------------------- |
548
+ | Model | PascalCase | `User`, `Post` |
549
+ | Create Input | `{Model}CreateInput` | `UserCreateInput` |
550
+ | Update Input | `{Model}UpdateInput` | `UserUpdateInput` |
551
+ | List Params | `{Model}ListParams` | `UserListParams` |
552
+ | Props | `{Component}Props` | `UserTableProps` |
553
+ | Response | `{Name}Response` | `PaginatedResponse` |
554
+
555
+ ---
556
+
557
+ ## Complete Example
558
+
559
+ ```typescript
560
+ // ═══════════════════════════════════════════════════════════════════
561
+ // types/model/User.ts (Omnify extension)
562
+ // ═══════════════════════════════════════════════════════════════════
563
+ import type { User as UserBase } from "./base/User";
564
+
565
+ export interface User extends UserBase {
566
+ // Add frontend-only properties if needed
567
+ }
568
+
569
+ // ═══════════════════════════════════════════════════════════════════
570
+ // services/users.ts
571
+ // ═══════════════════════════════════════════════════════════════════
572
+ import api, { PaginatedResponse } from "@/lib/api";
573
+ import type { User } from "@omnify/schemas";
574
+
575
+ export interface UserCreateInput {
576
+ name: string;
577
+ email: string;
578
+ password: string;
579
+ }
580
+
581
+ export interface UserUpdateInput {
582
+ name?: string;
583
+ email?: string;
584
+ }
585
+
586
+ export interface UserListParams {
587
+ search?: string;
588
+ page?: number;
589
+ }
590
+
591
+ export const userService = {
592
+ list: async (params?: UserListParams): Promise<PaginatedResponse<User>> => {
593
+ const { data } = await api.get("/api/users", { params });
594
+ return data;
595
+ },
596
+ get: async (id: number): Promise<User> => {
597
+ const { data } = await api.get(`/api/users/${id}`);
598
+ return data.data ?? data;
599
+ },
600
+ create: async (input: UserCreateInput): Promise<User> => {
601
+ const { data } = await api.post("/api/users", input);
602
+ return data.data ?? data;
603
+ },
604
+ update: async (id: number, input: UserUpdateInput): Promise<User> => {
605
+ const { data } = await api.put(`/api/users/${id}`, input);
606
+ return data.data ?? data;
607
+ },
608
+ delete: async (id: number): Promise<void> => {
609
+ await api.delete(`/api/users/${id}`);
610
+ },
611
+ };
612
+
613
+ // ═══════════════════════════════════════════════════════════════════
614
+ // components/tables/UserTable.tsx
615
+ // ═══════════════════════════════════════════════════════════════════
616
+ import type { User } from "@omnify/schemas";
617
+
618
+ interface UserTableProps {
619
+ users: User[];
620
+ loading?: boolean;
621
+ onEdit?: (user: User) => void;
622
+ }
623
+
624
+ export function UserTable({ users, loading, onEdit }: UserTableProps) {
625
+ return <Table dataSource={users} loading={loading} ... />;
626
+ }
627
+
628
+ // ═══════════════════════════════════════════════════════════════════
629
+ // app/(dashboard)/users/page.tsx
630
+ // ═══════════════════════════════════════════════════════════════════
631
+ "use client";
632
+
633
+ import { useQuery } from "@tanstack/react-query";
634
+ import { userService, UserListParams } from "@/services/users";
635
+ import { UserTable } from "@/components/tables/UserTable";
636
+ import { queryKeys } from "@/lib/queryKeys";
637
+
638
+ export default function UsersPage() {
639
+ const [params, setParams] = useState<UserListParams>({ page: 1 });
640
+
641
+ const { data, isLoading } = useQuery({
642
+ queryKey: queryKeys.users.list(params),
643
+ queryFn: () => userService.list(params),
644
+ });
645
+
646
+ return (
647
+ <UserTable
648
+ users={data?.data ?? []}
649
+ loading={isLoading}
650
+ onEdit={(user) => router.push(`/users/${user.id}/edit`)}
651
+ />
652
+ );
653
+ }
654
+ ```
655
+
656
+ ---
657
+
658
+ ## Summary
659
+
660
+ | Type | Location | Why |
661
+ | -------- | -------------------- | ------------------------- |
662
+ | Model | `@omnify/schemas` | Synced with DB via Omnify |
663
+ | Input | Service file | Colocated with API logic |
664
+ | Response | `lib/api.ts` | Shared Laravel patterns |
665
+ | Props | Component file | Colocated with component |
666
+ | Hook | Hook file (inferred) | TypeScript handles it |
667
+ | Utility | `types/index.ts` | Only if widely used |
668
+
669
+ **Philosophy**: Keep types close to their usage. Don't over-organize.