create-tigra 1.1.0 → 2.0.1

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 (243) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +80 -87
  3. package/bin/create-tigra.js +259 -308
  4. package/package.json +49 -41
  5. package/template/_claude/QUICK_REFERENCE.md +193 -0
  6. package/template/_claude/README.md +53 -0
  7. package/template/_claude/commands/create-client.md +881 -0
  8. package/template/_claude/commands/create-server.md +383 -0
  9. package/template/_claude/rules/client/01-project-structure.md +133 -0
  10. package/template/_claude/rules/client/02-components-and-types.md +146 -0
  11. package/template/_claude/rules/client/03-data-and-state.md +156 -0
  12. package/template/_claude/rules/client/04-design-system.md +185 -0
  13. package/template/_claude/rules/client/05-security.md +55 -0
  14. package/template/_claude/rules/client/06-ux-checklist.md +81 -0
  15. package/template/_claude/rules/client/core.md +42 -0
  16. package/template/_claude/rules/global/core.md +77 -0
  17. package/template/_claude/rules/server/core.md +50 -0
  18. package/template/_claude/rules/server/database.md +124 -0
  19. package/template/_claude/rules/server/project-conventions.md +150 -0
  20. package/template/_claude/rules/server/response-handling.md +144 -0
  21. package/template/client/.env.example +5 -0
  22. package/template/client/README.md +36 -0
  23. package/template/client/components.json +23 -0
  24. package/template/client/eslint.config.mjs +18 -0
  25. package/template/client/next.config.ts +34 -0
  26. package/template/client/package.json +44 -0
  27. package/template/client/postcss.config.mjs +7 -0
  28. package/template/client/src/app/(auth)/layout.tsx +18 -0
  29. package/template/client/src/app/(auth)/login/page.tsx +13 -0
  30. package/template/client/src/app/(auth)/register/page.tsx +13 -0
  31. package/template/client/src/app/(main)/dashboard/page.tsx +22 -0
  32. package/template/client/src/app/(main)/layout.tsx +11 -0
  33. package/template/client/src/app/error.tsx +27 -0
  34. package/template/client/src/app/favicon.ico +0 -0
  35. package/template/client/src/app/globals.css +145 -0
  36. package/template/client/src/app/layout.tsx +36 -0
  37. package/template/client/src/app/loading.tsx +11 -0
  38. package/template/client/src/app/not-found.tsx +23 -0
  39. package/template/client/src/app/page.tsx +45 -0
  40. package/template/client/src/app/providers.tsx +43 -0
  41. package/template/client/src/components/common/ConfirmDialog.tsx +56 -0
  42. package/template/client/src/components/common/EmptyState.tsx +31 -0
  43. package/template/client/src/components/common/LoadingSpinner.tsx +30 -0
  44. package/template/client/src/components/common/Pagination.tsx +55 -0
  45. package/template/client/src/components/layout/Footer.tsx +17 -0
  46. package/template/client/src/components/layout/Header.tsx +173 -0
  47. package/template/client/src/components/layout/MainLayout.tsx +18 -0
  48. package/template/client/src/components/ui/alert-dialog.tsx +196 -0
  49. package/template/client/src/components/ui/badge.tsx +48 -0
  50. package/template/client/src/components/ui/button.tsx +64 -0
  51. package/template/client/src/components/ui/card.tsx +92 -0
  52. package/template/client/src/components/ui/input.tsx +21 -0
  53. package/template/client/src/components/ui/label.tsx +24 -0
  54. package/template/client/src/components/ui/select.tsx +190 -0
  55. package/template/client/src/components/ui/skeleton.tsx +13 -0
  56. package/template/client/src/components/ui/table.tsx +116 -0
  57. package/template/client/src/features/auth/components/AuthInitializer.tsx +55 -0
  58. package/template/client/src/features/auth/components/LoginForm.tsx +107 -0
  59. package/template/client/src/features/auth/components/RegisterForm.tsx +178 -0
  60. package/template/client/src/features/auth/hooks/useAuth.ts +84 -0
  61. package/template/client/src/features/auth/services/auth.service.ts +52 -0
  62. package/template/client/src/features/auth/store/authSlice.ts +38 -0
  63. package/template/client/src/features/auth/types/auth.types.ts +32 -0
  64. package/template/client/src/hooks/useDebounce.ts +14 -0
  65. package/template/client/src/hooks/useLocalStorage.ts +55 -0
  66. package/template/client/src/hooks/useMediaQuery.ts +27 -0
  67. package/template/client/src/lib/api/api.types.ts +34 -0
  68. package/template/client/src/lib/api/axios.config.ts +98 -0
  69. package/template/client/src/lib/constants/api-endpoints.ts +18 -0
  70. package/template/client/src/lib/constants/app.constants.ts +12 -0
  71. package/template/client/src/lib/constants/routes.ts +9 -0
  72. package/template/client/src/lib/utils/error.ts +32 -0
  73. package/template/client/src/lib/utils/format.ts +37 -0
  74. package/template/client/src/lib/utils/security.ts +34 -0
  75. package/template/client/src/lib/utils.ts +6 -0
  76. package/template/client/src/middleware.ts +57 -0
  77. package/template/client/src/store/hooks.ts +7 -0
  78. package/template/client/src/store/index.ts +12 -0
  79. package/template/client/src/types/index.ts +3 -0
  80. package/template/client/tsconfig.json +34 -0
  81. package/template/gitignore +34 -0
  82. package/template/server/.dockerignore +66 -0
  83. package/template/server/.env.example +96 -69
  84. package/template/server/.env.production.example +90 -0
  85. package/template/server/Dockerfile +94 -0
  86. package/template/server/docker-compose.yml +82 -111
  87. package/template/server/docs/logging.md +62 -0
  88. package/template/server/eslint.config.mjs +17 -0
  89. package/template/server/package.json +68 -81
  90. package/template/server/phpmyadmin-config.php +26 -0
  91. package/template/server/postman_collection.json +666 -0
  92. package/template/server/prisma/schema.prisma +77 -93
  93. package/template/server/prisma/seed.ts +46 -142
  94. package/template/server/scripts/flush-redis.ts +41 -0
  95. package/template/server/src/app.ts +243 -71
  96. package/template/server/src/config/env.ts +67 -94
  97. package/template/server/src/libs/auth.ts +88 -0
  98. package/template/server/src/libs/cleanup.ts +35 -0
  99. package/template/server/src/libs/cookies.ts +46 -0
  100. package/template/server/src/libs/logger.ts +33 -60
  101. package/template/server/src/libs/monitoring.ts +205 -0
  102. package/template/server/src/libs/password.ts +38 -0
  103. package/template/server/src/libs/prisma.ts +68 -0
  104. package/template/server/src/libs/redis.ts +60 -79
  105. package/template/server/src/libs/requestLogger.ts +66 -0
  106. package/template/server/src/libs/storage/file-storage.service.ts +211 -0
  107. package/template/server/src/libs/storage/file-validator.ts +97 -0
  108. package/template/server/src/libs/storage/filename-sanitizer.ts +71 -0
  109. package/template/server/src/libs/storage/image-optimizer.service.ts +144 -0
  110. package/template/server/src/modules/auth/__tests__/auth.service.test.ts +365 -0
  111. package/template/server/src/modules/auth/auth.controller.ts +90 -141
  112. package/template/server/src/modules/auth/auth.repo.ts +120 -218
  113. package/template/server/src/modules/auth/auth.routes.ts +96 -83
  114. package/template/server/src/modules/auth/auth.schemas.ts +35 -137
  115. package/template/server/src/modules/auth/auth.service.ts +286 -329
  116. package/template/server/src/modules/auth/session.repo.ts +110 -0
  117. package/template/server/src/modules/users/users.controller.ts +120 -0
  118. package/template/server/src/modules/users/users.repo.ts +77 -0
  119. package/template/server/src/modules/users/users.routes.ts +89 -0
  120. package/template/server/src/modules/users/users.schemas.ts +21 -0
  121. package/template/server/src/modules/users/users.service.ts +169 -0
  122. package/template/server/src/server.ts +58 -139
  123. package/template/server/src/shared/errors/AppError.ts +21 -0
  124. package/template/server/src/shared/errors/errors.ts +43 -0
  125. package/template/server/src/shared/responses/paginatedResponse.ts +38 -0
  126. package/template/server/src/shared/responses/successResponse.ts +17 -0
  127. package/template/server/src/shared/schemas/pagination.schema.ts +12 -0
  128. package/template/server/src/shared/types/index.ts +26 -0
  129. package/template/server/src/test/setup.ts +74 -38
  130. package/template/server/tsconfig.json +27 -89
  131. package/template/server/uploads/avatars/.gitkeep +1 -0
  132. package/template/server/vitest.config.ts +43 -98
  133. package/template/.agent/rules/client/01-project-structure.md +0 -326
  134. package/template/.agent/rules/client/02-component-patterns.md +0 -249
  135. package/template/.agent/rules/client/03-typescript-rules.md +0 -226
  136. package/template/.agent/rules/client/04-state-management.md +0 -474
  137. package/template/.agent/rules/client/05-api-integration.md +0 -129
  138. package/template/.agent/rules/client/06-forms-validation.md +0 -129
  139. package/template/.agent/rules/client/07-common-patterns.md +0 -150
  140. package/template/.agent/rules/client/08-color-system.md +0 -93
  141. package/template/.agent/rules/client/09-security-rules.md +0 -97
  142. package/template/.agent/rules/client/10-testing-strategy.md +0 -370
  143. package/template/.agent/rules/global/ai-edit-safety.md +0 -38
  144. package/template/.agent/rules/server/01-db-and-migrations.md +0 -242
  145. package/template/.agent/rules/server/02-general-rules.md +0 -111
  146. package/template/.agent/rules/server/03-migrations.md +0 -20
  147. package/template/.agent/rules/server/04-pagination.md +0 -130
  148. package/template/.agent/rules/server/05-project-conventions.md +0 -71
  149. package/template/.agent/rules/server/06-response-handling.md +0 -173
  150. package/template/.agent/rules/server/07-testing-strategy.md +0 -506
  151. package/template/.agent/rules/server/08-observability.md +0 -180
  152. package/template/.agent/rules/server/10-background-jobs-v2.md +0 -185
  153. package/template/.agent/rules/server/11-rate-limiting-v2.md +0 -210
  154. package/template/.agent/rules/server/12-performance-optimization.md +0 -567
  155. package/template/.claude/rules/client-01-project-structure.md +0 -327
  156. package/template/.claude/rules/client-02-component-patterns.md +0 -250
  157. package/template/.claude/rules/client-03-typescript-rules.md +0 -227
  158. package/template/.claude/rules/client-04-state-management.md +0 -475
  159. package/template/.claude/rules/client-05-api-integration.md +0 -130
  160. package/template/.claude/rules/client-06-forms-validation.md +0 -130
  161. package/template/.claude/rules/client-07-common-patterns.md +0 -151
  162. package/template/.claude/rules/client-08-color-system.md +0 -94
  163. package/template/.claude/rules/client-09-security-rules.md +0 -98
  164. package/template/.claude/rules/client-10-testing-strategy.md +0 -371
  165. package/template/.claude/rules/global-ai-edit-safety.md +0 -39
  166. package/template/.claude/rules/server-01-db-and-migrations.md +0 -243
  167. package/template/.claude/rules/server-02-general-rules.md +0 -112
  168. package/template/.claude/rules/server-03-migrations.md +0 -21
  169. package/template/.claude/rules/server-04-pagination.md +0 -131
  170. package/template/.claude/rules/server-05-project-conventions.md +0 -72
  171. package/template/.claude/rules/server-06-response-handling.md +0 -174
  172. package/template/.claude/rules/server-07-testing-strategy.md +0 -507
  173. package/template/.claude/rules/server-08-observability.md +0 -181
  174. package/template/.claude/rules/server-10-background-jobs-v2.md +0 -186
  175. package/template/.claude/rules/server-11-rate-limiting-v2.md +0 -211
  176. package/template/.claude/rules/server-12-performance-optimization.md +0 -568
  177. package/template/.cursor/rules/client-01-project-structure.mdc +0 -327
  178. package/template/.cursor/rules/client-02-component-patterns.mdc +0 -250
  179. package/template/.cursor/rules/client-03-typescript-rules.mdc +0 -227
  180. package/template/.cursor/rules/client-04-state-management.mdc +0 -475
  181. package/template/.cursor/rules/client-05-api-integration.mdc +0 -130
  182. package/template/.cursor/rules/client-06-forms-validation.mdc +0 -130
  183. package/template/.cursor/rules/client-07-common-patterns.mdc +0 -151
  184. package/template/.cursor/rules/client-08-color-system.mdc +0 -94
  185. package/template/.cursor/rules/client-09-security-rules.mdc +0 -98
  186. package/template/.cursor/rules/client-10-testing-strategy.mdc +0 -371
  187. package/template/.cursor/rules/global-ai-edit-safety.mdc +0 -39
  188. package/template/.cursor/rules/server-01-db-and-migrations.mdc +0 -243
  189. package/template/.cursor/rules/server-02-general-rules.mdc +0 -112
  190. package/template/.cursor/rules/server-03-migrations.mdc +0 -21
  191. package/template/.cursor/rules/server-04-pagination.mdc +0 -131
  192. package/template/.cursor/rules/server-05-project-conventions.mdc +0 -72
  193. package/template/.cursor/rules/server-06-response-handling.mdc +0 -174
  194. package/template/.cursor/rules/server-07-testing-strategy.mdc +0 -507
  195. package/template/.cursor/rules/server-08-observability.mdc +0 -181
  196. package/template/.cursor/rules/server-09-api-documentation-v2.mdc +0 -169
  197. package/template/.cursor/rules/server-10-background-jobs-v2.mdc +0 -186
  198. package/template/.cursor/rules/server-11-rate-limiting-v2.mdc +0 -211
  199. package/template/.cursor/rules/server-12-performance-optimization.mdc +0 -568
  200. package/template/CLAUDE.md +0 -207
  201. package/template/server/.tsc-aliasrc.json +0 -13
  202. package/template/server/IMPORT_FIX_CHECKLIST.md +0 -98
  203. package/template/server/IMPORT_FIX_COMPLETE.md +0 -89
  204. package/template/server/README.md +0 -183
  205. package/template/server/REMAINING_IMPORT_FIXES.md +0 -150
  206. package/template/server/SECURITY.md +0 -190
  207. package/template/server/Tigra-API.postman_collection.json +0 -733
  208. package/template/server/biome.json +0 -42
  209. package/template/server/scripts/fix-all-imports.ps1 +0 -52
  210. package/template/server/scripts/fix-imports-reference.ps1 +0 -16
  211. package/template/server/scripts/fix-imports.mjs +0 -55
  212. package/template/server/scripts/setup-env.js +0 -50
  213. package/template/server/scripts/wait-for-db.js +0 -60
  214. package/template/server/src/hooks/request-timing.hook.ts +0 -26
  215. package/template/server/src/libs/auth/authenticate.middleware.ts +0 -22
  216. package/template/server/src/libs/auth/rbac.middleware.test.ts +0 -134
  217. package/template/server/src/libs/auth/rbac.middleware.ts +0 -147
  218. package/template/server/src/libs/db.ts +0 -76
  219. package/template/server/src/libs/error-handler.ts +0 -89
  220. package/template/server/src/libs/queue.ts +0 -79
  221. package/template/server/src/modules/admin/admin.controller.ts +0 -122
  222. package/template/server/src/modules/admin/admin.routes.ts +0 -62
  223. package/template/server/src/modules/admin/admin.schemas.ts +0 -35
  224. package/template/server/src/modules/admin/admin.service.ts +0 -167
  225. package/template/server/src/modules/auth/auth.integration.test.ts +0 -150
  226. package/template/server/src/modules/auth/auth.service.test.ts +0 -119
  227. package/template/server/src/modules/auth/auth.types.ts +0 -97
  228. package/template/server/src/modules/resources/resources.controller.ts +0 -218
  229. package/template/server/src/modules/resources/resources.repo.ts +0 -253
  230. package/template/server/src/modules/resources/resources.routes.ts +0 -116
  231. package/template/server/src/modules/resources/resources.schemas.ts +0 -146
  232. package/template/server/src/modules/resources/resources.service.ts +0 -218
  233. package/template/server/src/modules/resources/resources.types.ts +0 -73
  234. package/template/server/src/plugins/rate-limit.plugin.ts +0 -21
  235. package/template/server/src/plugins/security.plugin.ts +0 -21
  236. package/template/server/src/routes/health.routes.ts +0 -31
  237. package/template/server/src/types/fastify.d.ts +0 -36
  238. package/template/server/src/utils/errors.ts +0 -108
  239. package/template/server/src/utils/pagination.ts +0 -120
  240. package/template/server/src/utils/response.ts +0 -110
  241. package/template/server/src/workers/file.worker.ts +0 -106
  242. package/template/server/tsconfig.build.json +0 -30
  243. package/template/server/tsconfig.test.json +0 -22
@@ -1,474 +0,0 @@
1
- ---
2
- trigger: always_on
3
- ---
4
-
5
- > **SCOPE**: These rules apply specifically to the **client** directory.
6
-
7
- # State Management Patterns
8
-
9
- ## State Strategy
10
-
11
- ### Decision Matrix
12
-
13
- | State Type | Tool | Examples |
14
- |------------|------|----------|
15
- | **Server Data** | React Query | Resources, profiles, application data from API |
16
- | **Global Client** | Redux | Auth tokens, current user, theme settings |
17
- | **Local** | useState | Form inputs, modals, hover state, toggles |
18
- | **URL** | React Router | Filters, pagination, search query |
19
-
20
- ## React Query (Server State)
21
-
22
- ### Setup
23
-
24
- ```tsx
25
- // app/providers.tsx
26
- import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
27
-
28
- const queryClient = new QueryClient({
29
- defaultOptions: {
30
- queries: {
31
- staleTime: 5 * 60 * 1000,
32
- cacheTime: 10 * 60 * 1000,
33
- refetchOnWindowFocus: false,
34
- retry: 1,
35
- },
36
- },
37
- });
38
- ```
39
-
40
- ---
41
-
42
- ## Query Key Factory Pattern (MANDATORY)
43
-
44
- **Why?** Consistent query keys prevent cache bugs and enable powerful invalidation patterns.
45
-
46
- ### Factory Structure
47
-
48
- ```tsx
49
- // features/resources/lib/query-keys.ts
50
-
51
- export const resourceKeys = {
52
- // Base key
53
- all: ['resources'] as const,
54
-
55
- // List queries
56
- lists: () => [...resourceKeys.all, 'list'] as const,
57
- list: (filters: ResourceFilters) => [...resourceKeys.lists(), filters] as const,
58
-
59
- // Detail queries
60
- details: () => [...resourceKeys.all, 'detail'] as const,
61
- detail: (id: string) => [...resourceKeys.details(), id] as const,
62
-
63
- // User-specific queries
64
- myResources: () => [...resourceKeys.all, 'my'] as const,
65
- myResource: (filters: ResourceFilters) => [...resourceKeys.myResources(), filters] as const,
66
- };
67
-
68
- // Usage in hooks
69
- export const useResources = (filters: ResourceFilters) => {
70
- return useQuery({
71
- queryKey: resourceKeys.list(filters),
72
- queryFn: () => resourceService.getResources(filters),
73
- });
74
- };
75
-
76
- export const useResource = (id: string) => {
77
- return useQuery({
78
- queryKey: resourceKeys.detail(id),
79
- queryFn: () => resourceService.getResource(id),
80
- enabled: !!id, // Only fetch if ID exists
81
- });
82
- };
83
- ```
84
-
85
- ### Complex Example (Categories with Resources)
86
-
87
- ```tsx
88
- // features/categories/lib/query-keys.ts
89
- export const categoryKeys = {
90
- all: ['categories'] as const,
91
- lists: () => [...categoryKeys.all, 'list'] as const,
92
- list: (filters: CategoryFilters) => [...categoryKeys.lists(), filters] as const,
93
- details: () => [...categoryKeys.all, 'detail'] as const,
94
- detail: (id: string) => [...categoryKeys.details(), id] as const,
95
-
96
- // Nested resources
97
- resources: (categoryId: string) => [...categoryKeys.detail(categoryId), 'resources'] as const,
98
- };
99
- ```
100
-
101
- ### Invalidation Patterns
102
-
103
- ```tsx
104
- // After creating a resource
105
- useMutation({
106
- mutationFn: resourceService.createResource,
107
- onSuccess: () => {
108
- // Invalidate all resource lists
109
- queryClient.invalidateQueries({ queryKey: resourceKeys.lists() });
110
- },
111
- });
112
-
113
- // After updating a specific resource
114
- useMutation({
115
- mutationFn: ({ id, data }) => resourceService.updateResource(id, data),
116
- onSuccess: (_, variables) => {
117
- // Invalidate this specific resource
118
- queryClient.invalidateQueries({ queryKey: resourceKeys.detail(variables.id) });
119
- // Also invalidate lists (resource might move categories)
120
- queryClient.invalidateQueries({ queryKey: resourceKeys.lists() });
121
- },
122
- });
123
-
124
- // After deleting
125
- useMutation({
126
- mutationFn: resourceService.deleteResource,
127
- onSuccess: () => {
128
- // Remove all resource-related queries
129
- queryClient.invalidateQueries({ queryKey: resourceKeys.all });
130
- },
131
- });
132
- ```
133
-
134
- ---
135
-
136
- ## Query Pattern
137
-
138
- ```tsx
139
- // features/resources/hooks/useResources.ts
140
- import { useQuery } from '@tanstack/react-query';
141
- import { resourceService } from '../services/resource.service';
142
- import { resourceKeys } from '../lib/query-keys';
143
-
144
- export const useResources = (filters = {}, page = 1, limit = 10) => {
145
- return useQuery({
146
- queryKey: resourceKeys.list({ ...filters, page, limit }),
147
- queryFn: () => resourceService.getResources({ ...filters, page, limit }),
148
- staleTime: 5 * 60 * 1000,
149
- });
150
- };
151
- ```
152
-
153
- ## Mutation Pattern
154
-
155
- ```tsx
156
- // features/resources/hooks/useCreateResource.ts
157
- import { useMutation, useQueryClient } from '@tanstack/react-query';
158
- import { message } from 'antd'; // Ant Design message
159
- import { resourceKeys } from '../lib/query-keys';
160
-
161
- export const useCreateResource = () => {
162
- const queryClient = useQueryClient();
163
-
164
- return useMutation({
165
- mutationFn: resourceService.createResource,
166
- onSuccess: () => {
167
- queryClient.invalidateQueries({ queryKey: resourceKeys.lists() });
168
- message.success('Resource created!');
169
- },
170
- onError: (error) => {
171
- message.error(getErrorMessage(error));
172
- },
173
- });
174
- };
175
- ```
176
-
177
- ## Optimistic Updates
178
-
179
- ```tsx
180
- export const useToggleStatus = () => {
181
- const queryClient = useQueryClient();
182
-
183
- return useMutation({
184
- mutationFn: ({ id, status }) => resourceService.updateStatus(id, status),
185
- onMutate: async ({ id, status }) => {
186
- // Cancel outgoing queries
187
- await queryClient.cancelQueries({ queryKey: resourceKeys.detail(id) });
188
-
189
- // Snapshot previous value
190
- const previous = queryClient.getQueryData(resourceKeys.detail(id));
191
-
192
- // Optimistically update
193
- queryClient.setQueryData(resourceKeys.detail(id), (old: any) => ({
194
- ...old,
195
- status
196
- }));
197
-
198
- return { previous, id };
199
- },
200
- onError: (err, variables, context) => {
201
- // Rollback on error
202
- if (context?.previous) {
203
- queryClient.setQueryData(resourceKeys.detail(context.id), context.previous);
204
- }
205
- message.error('Failed to update status');
206
- },
207
- onSettled: (data, error, variables) => {
208
- // Refetch to ensure sync
209
- queryClient.invalidateQueries({ queryKey: resourceKeys.detail(variables.id) });
210
- },
211
- });
212
- };
213
- ```
214
-
215
- ---
216
-
217
- ## Redux (Global Client State)
218
-
219
- ### When to Use Redux
220
- - Authentication state (user, tokens)
221
- - Global UI state (theme, sidebar collapsed)
222
- - App-wide settings
223
-
224
- ### Slice Pattern
225
- Standard Redux Toolkit pattern for auth and global UI state.
226
-
227
- ```tsx
228
- // features/auth/store/authSlice.ts
229
- import { createSlice } from '@reduxjs/toolkit';
230
-
231
- interface AuthState {
232
- user: User | null;
233
- tokens: { accessToken: string; refreshToken: string } | null;
234
- isAuthenticated: boolean;
235
- }
236
-
237
- const initialState: AuthState = {
238
- user: null,
239
- tokens: null,
240
- isAuthenticated: false,
241
- };
242
-
243
- const authSlice = createSlice({
244
- name: 'auth',
245
- initialState,
246
- reducers: {
247
- setCredentials: (state, action) => {
248
- state.user = action.payload.user;
249
- state.tokens = action.payload.tokens;
250
- state.isAuthenticated = true;
251
- },
252
- logout: (state) => {
253
- state.user = null;
254
- state.tokens = null;
255
- state.isAuthenticated = false;
256
- },
257
- },
258
- });
259
-
260
- export const { setCredentials, logout } = authSlice.actions;
261
- export default authSlice.reducer;
262
- ```
263
-
264
- ---
265
-
266
- ## Local State
267
- Use `useState` for UI-only state (e.g., `isModalOpen`, form inputs before submission).
268
-
269
- ```tsx
270
- export const ResourceCard = ({ resource }) => {
271
- const [isHovered, setIsHovered] = useState(false);
272
- const [showDetails, setShowDetails] = useState(false);
273
-
274
- return (
275
- <div
276
- onMouseEnter={() => setIsHovered(true)}
277
- onMouseLeave={() => setIsHovered(false)}
278
- >
279
- {/* UI */}
280
- </div>
281
- );
282
- };
283
- ```
284
-
285
- ---
286
-
287
- ## URL State
288
- Use `useSearchParams` from `react-router-dom` for filters and pagination to ensure page refreshes and direct links work as expected.
289
-
290
- ```tsx
291
- import { useSearchParams } from 'react-router-dom';
292
-
293
- export const ResourcesPage = () => {
294
- const [searchParams, setSearchParams] = useSearchParams();
295
-
296
- const page = parseInt(searchParams.get('page') || '1');
297
- const category = searchParams.get('category') || '';
298
-
299
- const handleFilterChange = (newCategory: string) => {
300
- setSearchParams({ page: '1', category: newCategory });
301
- };
302
-
303
- const { data } = useResources({ category }, page);
304
-
305
- return (
306
- <div>
307
- <CategoryFilter value={category} onChange={handleFilterChange} />
308
- <ResourceList resources={data?.items} />
309
- <Pagination
310
- current={page}
311
- total={data?.pagination.totalPages}
312
- onChange={(newPage) => setSearchParams({ page: String(newPage), category })}
313
- />
314
- </div>
315
- );
316
- };
317
- ```
318
-
319
- ---
320
-
321
- ## Advanced Query Patterns
322
-
323
- ### Dependent Queries
324
- ```tsx
325
- export const useResourceWithOwner = (resourceId: string) => {
326
- // First fetch resource
327
- const { data: resource } = useQuery({
328
- queryKey: resourceKeys.detail(resourceId),
329
- queryFn: () => resourceService.getResource(resourceId),
330
- });
331
-
332
- // Then fetch owner (depends on resource)
333
- const { data: owner } = useQuery({
334
- queryKey: ['users', resource?.ownerId],
335
- queryFn: () => userService.getUser(resource!.ownerId),
336
- enabled: !!resource?.ownerId, // Only run when we have ownerId
337
- });
338
-
339
- return { resource, owner };
340
- };
341
- ```
342
-
343
- ### Parallel Queries
344
- ```tsx
345
- export const useDashboardData = () => {
346
- const resources = useQuery({
347
- queryKey: resourceKeys.lists(),
348
- queryFn: resourceService.getResources,
349
- });
350
-
351
- const categories = useQuery({
352
- queryKey: categoryKeys.lists(),
353
- queryFn: categoryService.getCategories,
354
- });
355
-
356
- const stats = useQuery({
357
- queryKey: ['stats'],
358
- queryFn: statsService.getStats,
359
- });
360
-
361
- return {
362
- isLoading: resources.isLoading || categories.isLoading || stats.isLoading,
363
- data: {
364
- resources: resources.data,
365
- categories: categories.data,
366
- stats: stats.data,
367
- },
368
- };
369
- };
370
- ```
371
-
372
- ### Infinite Queries (Load More)
373
- ```tsx
374
- export const useInfiniteResources = (filters: ResourceFilters) => {
375
- return useInfiniteQuery({
376
- queryKey: [...resourceKeys.lists(), 'infinite', filters],
377
- queryFn: ({ pageParam = 1 }) =>
378
- resourceService.getResources({ ...filters, page: pageParam }),
379
- getNextPageParam: (lastPage) =>
380
- lastPage.pagination.hasNextPage ? lastPage.pagination.page + 1 : undefined,
381
- });
382
- };
383
-
384
- // Usage in component
385
- const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteResources(filters);
386
-
387
- <Button onClick={() => fetchNextPage()} disabled={!hasNextPage} loading={isFetchingNextPage}>
388
- Load More
389
- </Button>
390
- ```
391
-
392
- ---
393
-
394
- ## Anti-Patterns
395
-
396
- ### ❌ DO NOT:
397
- ```tsx
398
- // Don't store server data in Redux
399
- const dispatch = useDispatch();
400
- const resources = await resourceService.getResources();
401
- dispatch(setResources(resources)); // BAD
402
-
403
- // Don't use string literals for query keys
404
- useQuery(['resources'], () => ...); // BAD - inconsistent
405
-
406
- // Don't forget to invalidate after mutations
407
- useMutation({
408
- mutationFn: createResource,
409
- // Missing onSuccess invalidation - cache will be stale!
410
- });
411
- ```
412
-
413
- ### ✅ DO:
414
- ```tsx
415
- // Use React Query for server data
416
- const { data: resources } = useQuery({
417
- queryKey: resourceKeys.lists(),
418
- queryFn: resourceService.getResources,
419
- });
420
-
421
- // Always use query key factories
422
- queryClient.invalidateQueries({ queryKey: resourceKeys.lists() });
423
-
424
- // Always invalidate after mutations
425
- useMutation({
426
- mutationFn: createResource,
427
- onSuccess: () => {
428
- queryClient.invalidateQueries({ queryKey: resourceKeys.lists() });
429
- },
430
- });
431
- ```
432
-
433
- ---
434
-
435
- ## Prefetching Data
436
-
437
- ```tsx
438
- export const ResourcesPage = () => {
439
- const queryClient = useQueryClient();
440
-
441
- // Prefetch next page on hover
442
- const handleResourceHover = (id: string) => {
443
- queryClient.prefetchQuery({
444
- queryKey: resourceKeys.detail(id),
445
- queryFn: () => resourceService.getResource(id),
446
- });
447
- };
448
-
449
- return (
450
- <div>
451
- {resources.map((resource) => (
452
- <ResourceCard
453
- key={resource.id}
454
- resource={resource}
455
- onMouseEnter={() => handleResourceHover(resource.id)}
456
- />
457
- ))}
458
- </div>
459
- );
460
- };
461
- ```
462
-
463
- ---
464
-
465
- ## Checklist
466
-
467
- - [ ] Query key factories created for all domains
468
- - [ ] React Query for all server data
469
- - [ ] Redux ONLY for auth and global UI state
470
- - [ ] URL state for filters/pagination
471
- - [ ] Proper invalidation after mutations
472
- - [ ] Optimistic updates for instant feedback
473
- - [ ] Error handling in mutations
474
- - [ ] Loading states handled in UI
@@ -1,129 +0,0 @@
1
- ---
2
- trigger: always_on
3
- ---
4
-
5
- > **SCOPE**: These rules apply specifically to the **client** directory.
6
-
7
- # API Integration & Error Handling
8
-
9
- ## Axios Configuration
10
- Standard pattern for interceptors and token management.
11
-
12
- ```tsx
13
- // lib/api/axios.config.ts
14
- import axios from 'axios';
15
- import { store } from '@/store';
16
- import { logout, updateTokens } from '@/features/auth/store/authSlice';
17
-
18
- export const apiClient = axios.create({
19
- baseURL: import.meta.env.VITE_API_BASE_URL,
20
- headers: { 'Content-Type': 'application/json' },
21
- timeout: 30000,
22
- });
23
-
24
- apiClient.interceptors.request.use((config) => {
25
- const token = store.getState().auth.tokens?.accessToken;
26
- if (token) config.headers.Authorization = `Bearer ${token}`;
27
- return config;
28
- });
29
- ```
30
-
31
- ## Service Pattern
32
- Use classes or plain objects to group API calls by domain.
33
-
34
- ```tsx
35
- // features/resources/services/resource.service.ts
36
- class ResourceService {
37
- async getResources(params = {}) {
38
- const response = await apiClient.get('/resources', { params });
39
- return response.data.data;
40
- }
41
- }
42
- export const resourceService = new ResourceService();
43
- ```
44
-
45
- ## Error Handling Utilities
46
-
47
- ```tsx
48
- // lib/utils/error.ts
49
- export const getErrorMessage = (error: unknown): string => {
50
- if (axios.isAxiosError(error)) {
51
- return error.response?.data?.error?.message || error.message;
52
- }
53
- return 'An unexpected error occurred';
54
- };
55
- ```
56
-
57
- ## UI Feedback (Ant Design)
58
-
59
- ### Error Components
60
- ```tsx
61
- // components/common/ErrorMessage.tsx
62
- import { Alert } from 'antd';
63
- import { getErrorMessage } from '@/lib/utils/error';
64
-
65
- export const ErrorMessage = ({ error }: { error: any }) => (
66
- <Alert
67
- message="Error"
68
- description={getErrorMessage(error)}
69
- type="error"
70
- showIcon
71
- />
72
- );
73
- ```
74
-
75
- ### Loading States
76
- ```tsx
77
- // components/common/LoadingSpinner.tsx
78
- import { Spin } from 'antd';
79
-
80
- export const LoadingSpinner = () => (
81
- <div className="spinner-container">
82
- <Spin size="large" />
83
- </div>
84
- );
85
- ```
86
-
87
- ### Skeleton (Ant Design)
88
- ```tsx
89
- // components/common/ResourceSkeleton.tsx
90
- import { Skeleton, Card } from 'antd';
91
-
92
- export const ResourceSkeleton = () => (
93
- <Card>
94
- <Skeleton active />
95
- </Card>
96
- );
97
- ```
98
-
99
- ## Notifications (Ant Design)
100
- Use `message` or `notification` from Ant Design.
101
-
102
- ```tsx
103
- import { message } from 'antd';
104
-
105
- // Success
106
- message.success('Action completed!');
107
-
108
- // Error
109
- message.error(getErrorMessage(error));
110
- ```
111
-
112
- ## File Upload Pattern
113
- ```tsx
114
- // features/resources/hooks/useUploadFiles.ts
115
- export const useUploadFiles = () => {
116
- return useMutation({
117
- mutationFn: async (files: File[]) => {
118
- const formData = new FormData();
119
- files.forEach((file) => formData.append('files', file));
120
- return apiClient.post('/upload', formData, {
121
- headers: { 'Content-Type': 'multipart/form-data' },
122
- });
123
- },
124
- });
125
- };
126
- ```
127
-
128
- ## Infinite Scroll Pattern
129
- Standard React Query `useInfiniteQuery` implementation with Ant Design's `Button` for "Load More" or a custom observer.