ginskill-init 2.7.0

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 (128) hide show
  1. package/.wrangler/cache/pages.json +4 -0
  2. package/.wrangler/cache/wrangler-account.json +6 -0
  3. package/DEVELOPMENT.md +510 -0
  4. package/README.md +104 -0
  5. package/agents/developer.md +56 -0
  6. package/agents/frontend-design.md +69 -0
  7. package/agents/mobile-reviewer.md +36 -0
  8. package/agents/review-code.md +49 -0
  9. package/agents/security-scanner.md +50 -0
  10. package/agents/tester.md +72 -0
  11. package/bin/cli.js +461 -0
  12. package/landing/ai-build-ai.png +0 -0
  13. package/landing/index.html +1495 -0
  14. package/landing/logo.png +0 -0
  15. package/package.json +37 -0
  16. package/skills/active-life-dev/SKILL.md +157 -0
  17. package/skills/active-life-dev/docs/auth.md +187 -0
  18. package/skills/active-life-dev/docs/customers.md +216 -0
  19. package/skills/active-life-dev/docs/integrations.md +209 -0
  20. package/skills/active-life-dev/docs/inventory.md +192 -0
  21. package/skills/active-life-dev/docs/modules.md +181 -0
  22. package/skills/active-life-dev/docs/orders.md +180 -0
  23. package/skills/active-life-dev/docs/patterns.md +319 -0
  24. package/skills/active-life-dev/docs/products.md +216 -0
  25. package/skills/active-life-dev/docs/schema.md +502 -0
  26. package/skills/active-life-dev/docs/setup.md +169 -0
  27. package/skills/active-life-dev/docs/vouchers.md +144 -0
  28. package/skills/ai-asset-generator/SKILL.md +247 -0
  29. package/skills/ai-asset-generator/docs/gen-image.md +274 -0
  30. package/skills/ai-asset-generator/docs/genvideo.md +341 -0
  31. package/skills/ai-asset-generator/docs/remove-background.md +19 -0
  32. package/skills/ai-asset-generator/lib/bg-remove.mjs +34 -0
  33. package/skills/ai-asset-generator/lib/env.mjs +48 -0
  34. package/skills/ai-asset-generator/lib/kie-client.mjs +100 -0
  35. package/skills/ai-build-ai/SKILL.md +127 -0
  36. package/skills/ai-build-ai/docs/agent-teams.md +293 -0
  37. package/skills/ai-build-ai/docs/checkpointing.md +161 -0
  38. package/skills/ai-build-ai/docs/create-agent.md +399 -0
  39. package/skills/ai-build-ai/docs/create-mcp.md +395 -0
  40. package/skills/ai-build-ai/docs/create-skill.md +299 -0
  41. package/skills/ai-build-ai/docs/headless-mode.md +614 -0
  42. package/skills/ai-build-ai/docs/hooks.md +578 -0
  43. package/skills/ai-build-ai/docs/memory-claude-md.md +375 -0
  44. package/skills/ai-build-ai/docs/output-styles.md +208 -0
  45. package/skills/ai-build-ai/docs/overview.md +162 -0
  46. package/skills/ai-build-ai/docs/permissions.md +391 -0
  47. package/skills/ai-build-ai/docs/plugins.md +396 -0
  48. package/skills/ai-build-ai/docs/sandbox.md +262 -0
  49. package/skills/ai-build-ai/docs/team-lead-workflow.md +648 -0
  50. package/skills/ant-design/SKILL.md +323 -0
  51. package/skills/ant-design/docs/components.md +160 -0
  52. package/skills/ant-design/docs/data-entry.md +406 -0
  53. package/skills/ant-design/docs/display.md +594 -0
  54. package/skills/ant-design/docs/feedback.md +451 -0
  55. package/skills/ant-design/docs/key-components.md +414 -0
  56. package/skills/ant-design/docs/navigation.md +310 -0
  57. package/skills/ant-design/docs/pro-components.md +543 -0
  58. package/skills/ant-design/docs/setup.md +213 -0
  59. package/skills/ant-design/docs/theme.md +265 -0
  60. package/skills/flutter-performance/SKILL.md +803 -0
  61. package/skills/flutter-performance/references/flutter-patterns.md +595 -0
  62. package/skills/icon-generator/SKILL.md +270 -0
  63. package/skills/mobile-app-review/SKILL.md +321 -0
  64. package/skills/mobile-app-review/references/apple-review.md +132 -0
  65. package/skills/mobile-app-review/references/google-play-review.md +203 -0
  66. package/skills/mongodb/SKILL.md +667 -0
  67. package/skills/mongodb/references/mongoose-patterns.md +368 -0
  68. package/skills/nestjs-architecture/SKILL.md +1086 -0
  69. package/skills/nestjs-architecture/references/advanced-patterns.md +590 -0
  70. package/skills/performance/SKILL.md +509 -0
  71. package/skills/react-fsd-architecture/SKILL.md +693 -0
  72. package/skills/react-fsd-architecture/references/fsd-patterns.md +747 -0
  73. package/skills/react-native-expo/SKILL.md +128 -0
  74. package/skills/react-native-expo/references/data-layer.md +252 -0
  75. package/skills/react-native-expo/references/design-system.md +252 -0
  76. package/skills/react-native-expo/references/navigation.md +199 -0
  77. package/skills/react-native-expo/references/performance.md +229 -0
  78. package/skills/react-native-expo/references/platform-services.md +179 -0
  79. package/skills/react-native-expo/references/state-management.md +209 -0
  80. package/skills/react-native-expo/references/ui-patterns.md +301 -0
  81. package/skills/react-query/SKILL.md +685 -0
  82. package/skills/react-query/references/query-patterns.md +365 -0
  83. package/skills/review-code/SKILL.md +374 -0
  84. package/skills/review-code/references/clean-code-principles.md +395 -0
  85. package/skills/review-code/references/frontend-patterns.md +136 -0
  86. package/skills/review-code/references/nestjs-patterns.md +184 -0
  87. package/skills/security-scanner/SKILL.md +366 -0
  88. package/skills/security-scanner/references/nestjs-security.md +260 -0
  89. package/skills/security-scanner/references/nextjs-security.md +201 -0
  90. package/skills/security-scanner/references/react-native-security.md +199 -0
  91. package/skills/traefik/SKILL.md +105 -0
  92. package/skills/traefik/docs/advanced-routing.md +186 -0
  93. package/skills/traefik/docs/auth-providers.md +137 -0
  94. package/skills/traefik/docs/cicd-devops.md +396 -0
  95. package/skills/traefik/docs/core-config.md +171 -0
  96. package/skills/traefik/docs/distributed-config.md +96 -0
  97. package/skills/traefik/docs/docker-compose.md +182 -0
  98. package/skills/traefik/docs/ha-performance.md +177 -0
  99. package/skills/traefik/docs/kubernetes.md +278 -0
  100. package/skills/traefik/docs/middleware.md +205 -0
  101. package/skills/traefik/docs/monitoring.md +357 -0
  102. package/skills/traefik/docs/security.md +391 -0
  103. package/skills/traefik/docs/tls-acme.md +155 -0
  104. package/skills/ui-ux-pro-max/SKILL.md +377 -0
  105. package/skills/ui-ux-pro-max/data/charts.csv +26 -0
  106. package/skills/ui-ux-pro-max/data/colors.csv +97 -0
  107. package/skills/ui-ux-pro-max/data/icons.csv +101 -0
  108. package/skills/ui-ux-pro-max/data/landing.csv +31 -0
  109. package/skills/ui-ux-pro-max/data/products.csv +97 -0
  110. package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  111. package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  112. package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  113. package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  114. package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  115. package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  116. package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  117. package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  118. package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  119. package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  120. package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  121. package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  122. package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  123. package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  124. package/skills/ui-ux-pro-max/data/styles.csv +68 -0
  125. package/skills/ui-ux-pro-max/data/typography.csv +58 -0
  126. package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  127. package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  128. package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: react-native-expo
3
+ description: |
4
+ **React Native Expo (Sty AI Mobile)**: Production patterns for the Sty AI React Native app — Expo SDK 54, Expo Router v5, React Query v5, Zustand v5, Reanimated v4, FlashList v2, MMKV, BottomSheet v5, react-hook-form + zod, and the full Sty AI design system.
5
+ - MANDATORY TRIGGERS: react native, expo, mobile app, screen, component, navigation, route, tab, stack, modal, animation, reanimated, gesture, flash list, flatlist, list performance, bottom sheet, form, validation, zod, state management, zustand, store, query, mutation, api call, fetch data, image, expo-image, styling, theme, color, typography, spacing, font, button, card, skeleton, shimmer, toast, dialog, upload, camera, photo, push notification, deep link, branch, auth, login, onboarding, keyboard, MMKV, storage, cache, i18n, localization, live activity, adapty, subscription, in-app purchase, analytics, airbridge, facebook sdk
6
+ - Use this skill whenever working on ANY file in the styai-mobile directory, creating new screens/components, debugging mobile issues, optimizing performance, or reviewing mobile code. Also trigger when discussing React Native architecture, Expo configuration, or mobile-specific patterns — even casual mentions like 'fix this screen' or 'add a button'.
7
+ ---
8
+
9
+ # React Native Expo — Sty AI Mobile
10
+
11
+ Production-grade skill for the Sty AI React Native mobile app (Expo SDK 54, New Architecture, Hermes).
12
+
13
+ ## Quick Start — Load Only What You Need
14
+
15
+ Before writing code, read the correct reference file based on the task:
16
+
17
+ | Task | Reference File |
18
+ |------|---------------|
19
+ | Screens, routes, navigation, tabs, modals | `references/navigation.md` |
20
+ | React Query hooks, API calls, mutations | `references/data-layer.md` |
21
+ | Zustand stores, MMKV, global state | `references/state-management.md` |
22
+ | Animations, gestures, Reanimated, lists | `references/performance.md` |
23
+ | Theme, colors, typography, spacing, scaling | `references/design-system.md` |
24
+ | Forms, validation, zod, bottom sheets | `references/ui-patterns.md` |
25
+ | Auth, notifications, deep links, i18n, analytics | `references/platform-services.md` |
26
+
27
+ Read ONLY the relevant reference file(s) for the current task. This keeps token usage minimal.
28
+
29
+ ## Project Identity
30
+
31
+ - **App**: Sty AI — AI-powered wardrobe management
32
+ - **Bundle**: `app.styai.android` (Android) / `dev.jerrypham.easycloset` (iOS)
33
+ - **Stack**: Expo SDK 54, React Native 0.81, React 19, TypeScript 5.9
34
+ - **Package Manager**: pnpm
35
+ - **Font**: Poppins (300-900 weights, platform-specific families)
36
+ - **Design**: Sophisticated minimal with rose/blush brand accent (#EC4899)
37
+
38
+ ## Architecture at a Glance
39
+
40
+ ```
41
+ styai-mobile/src/
42
+ app/ # Route files (thin delegators to screens)
43
+ screens/ # Actual screen logic (mirrors app/ structure)
44
+ models/ # Domain models: _services/, _types/, _ui/, _store/
45
+ features/ # Cross-cutting features (auth, credit, stylist, etc.)
46
+ shared/ # Shared infrastructure
47
+ components/ # Typography, Button, BottomSheet, shimmer, toast
48
+ hooks/ # use-auth-init, use-push-notifications, use-infinite-scroll
49
+ libs/ # api-client (Axios + auto-refresh), token-storage (Keychain)
50
+ stores/ # Zustand stores with slices pattern
51
+ theme/ # Design tokens: color, typography, spacing, scaling
52
+ services/ # Singletons: logger, airbridge, facebook, push
53
+ types/ # PaginatedResponse<T>, IdRes, TimeStampRes, ApiError
54
+ constant/ # ROUTES object, enums
55
+ utils/ # Helpers: compress-image, date, logger, share-app
56
+ components/ # (deprecated — use shared/components/)
57
+ ```
58
+
59
+ ## Critical Rules
60
+
61
+ 1. **Route files are thin delegators** — actual logic lives in `src/screens/`
62
+ 2. **Always use `Typography`** instead of raw `<Text>`
63
+ 3. **Always use `Theme.color.*`** semantic tokens, never raw hex values
64
+ 4. **Always use `useShallow`** when selecting multiple fields from Zustand
65
+ 5. **Query keys = API URL path strings** (e.g., `"/api/v1/item"`)
66
+ 6. **All API calls through `client`** from `@/shared/libs/api-client`
67
+ 7. **Use `expo-image`** (`Image` from `expo-image`), not React Native's `Image`
68
+ 8. **Use `FlashList`** for lists, never `FlatList`
69
+ 9. **Use `Spacing.*`** semantic tokens for all spacing values
70
+ 10. **Platform fonts differ**: iOS uses `Poppins-Medium`, Android uses `Poppins_500Medium` — always use `typefaceBase` or `withFontWeight()`, never hardcode font names
71
+
72
+ ## Path Aliases
73
+
74
+ ```
75
+ @/* → ./src/*
76
+ @auth/* → ./src/features/auth/*
77
+ @testimonials/* → ./src/assets/images/testimonials/*
78
+ assets/* → ./assets/*
79
+ ```
80
+
81
+ ## Commands
82
+
83
+ ```bash
84
+ pnpm start # Dev server (Metro)
85
+ pnpm ios # iOS device
86
+ pnpm android # Android emulator
87
+ pnpm type-check # TypeScript check
88
+ pnpm lint:fix # ESLint + auto-fix
89
+ pnpm test # Jest
90
+ pnpm prebuild # Regenerate native code
91
+ pnpm cng:development # Full dev env setup
92
+ ```
93
+
94
+ ## Key Dependencies
95
+
96
+ | Package | Version | Use |
97
+ |---------|---------|-----|
98
+ | `expo` | 54 | Framework (New Arch, Hermes) |
99
+ | `expo-router` | 6 | File-based routing (v5 API) |
100
+ | `@tanstack/react-query` | 5 | Server state |
101
+ | `zustand` | 5 | Client state |
102
+ | `react-native-mmkv` | 3.1 | Fast sync storage |
103
+ | `react-native-reanimated` | 4.1 | UI-thread animations |
104
+ | `@shopify/flash-list` | 2.0 | High-perf lists (New Arch only) |
105
+ | `@gorhom/bottom-sheet` | 5 | Bottom sheets |
106
+ | `react-hook-form` + `zod` | 7 + 3 | Forms + validation |
107
+ | `expo-image` | 3.0 | Cached images |
108
+ | `react-native-gesture-handler` | 2.28 | Native gestures |
109
+ | `@shopify/react-native-skia` | 2.2 | Canvas drawing |
110
+ | `react-native-keychain` | 10 | Secure JWT storage |
111
+ | `i18next` + `react-i18next` | 25 + 16 | i18n (9 languages) |
112
+ | `react-native-adapty` | 3.15 | Subscriptions |
113
+ | `airbridge-expo-sdk` | 4.8 | Analytics |
114
+ | `lottie-react-native` | 7.3 | Lottie animations |
115
+ | `date-fns` / `dayjs` | 4 / 1.11 | Date utils |
116
+ | `axios` | 1.11 | HTTP client (wrapped) |
117
+ | `react-native-svg` | 15.12 | SVG rendering |
118
+ | `ai` + `@ai-sdk/react` | 4 + 1 | AI SDK streaming |
119
+
120
+ ## New Code Placement
121
+
122
+ - **New model** → `src/models/<model-name>/` with `_services/`, `_types/`
123
+ - **New screen** → `src/screens/` + thin delegator in `src/app/`
124
+ - **New shared component** → `src/shared/components/`
125
+ - **New hook** → `src/shared/hooks/` or model's `_hooks/`
126
+ - **New store** → `src/shared/stores/` (Zustand) or feature's `_stores/`
127
+ - **New feature** → `src/features/<feature-name>/`
128
+ - **DO NOT** add to `src/components/` (deprecated)
@@ -0,0 +1,252 @@
1
+ # Data Layer — React Query v5 + API Client
2
+
3
+ ## API Client (`@/shared/libs/api-client`)
4
+
5
+ Custom Axios wrapper with:
6
+ - **Auto token injection** via request interceptor
7
+ - **401 auto-refresh** with single-flight pattern (max 2 retries, prevents race conditions)
8
+ - **FormData fallback** to native `fetch` (Axios has RN FormData issues)
9
+ - **Credit header parsing**: Response interceptor reads `x-credits-balance` and `x-credits-used` headers
10
+ - **Custom headers**: `User-Agent`, `X-Client-Type: mobile`, `Accept-Language`
11
+ - Base URL from `EXPO_PUBLIC_API_URL` env var
12
+ - Android emulator: `localhost` auto-replaced with `10.0.2.2`
13
+
14
+ ```typescript
15
+ import { client } from "@/shared/libs/api-client"
16
+
17
+ // Standard requests
18
+ const { data } = await client.get<ItemRes[]>("/api/v1/item")
19
+ const { data } = await client.post<ItemRes>("/api/v1/item", body)
20
+ const { data } = await client.patch<ItemRes>(`/api/v1/item/${id}`, body)
21
+ await client.delete(`/api/v1/item/${id}`)
22
+
23
+ // FormData (auto falls back to native fetch)
24
+ const formData = new FormData()
25
+ formData.append("file", { uri, type, name } as any)
26
+ const { data } = await client.post("/api/v1/upload", formData, {
27
+ headers: { "X-FormData-Upload": "true" },
28
+ })
29
+ ```
30
+
31
+ ## Service Pattern — One Hook Per File
32
+
33
+ Each query/mutation lives in its own file under `_services/`:
34
+
35
+ ```
36
+ src/models/item/_services/
37
+ use-get-items.ts # useInfiniteQuery
38
+ use-get-item-detail.ts # useQuery
39
+ use-create-item.ts # useMutation
40
+ use-update-item.ts # useMutation
41
+ use-delete-item.ts # useMutation
42
+ ```
43
+
44
+ ### Query Key Convention
45
+
46
+ Query keys are **always the API URL path string**:
47
+
48
+ ```typescript
49
+ export const ITEM_INFINITE_LIST_QUERY_KEY = "/api/v1/item"
50
+ export const ITEM_DETAIL_QUERY_KEY = "/api/v1/item/detail"
51
+ export const CATEGORIES_QUERY_KEY = "/api/v1/category"
52
+ ```
53
+
54
+ ### useQuery Pattern
55
+
56
+ ```typescript
57
+ import { useQuery } from "@tanstack/react-query"
58
+ import { client } from "@/shared/libs/api-client"
59
+
60
+ export const ITEM_DETAIL_QUERY_KEY = "/api/v1/item/detail"
61
+
62
+ export const useGetItemDetail = (id: string) => {
63
+ return useQuery({
64
+ queryKey: [ITEM_DETAIL_QUERY_KEY, id],
65
+ queryFn: async () => {
66
+ const { data } = await client.get<ItemDetailPopulated>(`/api/v1/item/${id}`)
67
+ return data
68
+ },
69
+ enabled: !!id,
70
+ })
71
+ }
72
+ ```
73
+
74
+ ### useInfiniteQuery Pattern
75
+
76
+ ```typescript
77
+ import { useInfiniteQuery } from "@tanstack/react-query"
78
+
79
+ export const ITEM_INFINITE_LIST_QUERY_KEY = "/api/v1/item"
80
+
81
+ export const useGetItemInfiniteList = (params?: ItemListParams, opts?: { enabled?: boolean }) => {
82
+ const limit = 20
83
+ const query = useInfiniteQuery({
84
+ queryKey: [ITEM_INFINITE_LIST_QUERY_KEY, limit, params],
85
+ initialPageParam: 0,
86
+ queryFn: async ({ pageParam }) => {
87
+ const { data } = await client.get<PaginatedResponse<ItemRes>>(ITEM_INFINITE_LIST_QUERY_KEY, {
88
+ params: { limit, skip: pageParam, paging_count: pageParam === 0, ...params },
89
+ })
90
+ return data
91
+ },
92
+ getNextPageParam: (lastPage, allPages) => {
93
+ if ((lastPage?.data?.length ?? 0) < limit) return undefined
94
+ return allPages.reduce((sum, p) => sum + (p?.data?.length ?? 0), 0)
95
+ },
96
+ enabled: opts?.enabled,
97
+ })
98
+
99
+ // Flatten pages
100
+ const items = query.data?.pages.flatMap((p) => p.data) ?? []
101
+ const firstMeta = query.data?.pages?.[0]?.meta
102
+
103
+ return { items, meta: firstMeta, ...query }
104
+ }
105
+ ```
106
+
107
+ Usage with FlashList + infinite scroll:
108
+ ```typescript
109
+ const { items, fetchNextPage, hasNextPage, isFetchingNextPage } = useGetItemInfiniteList()
110
+ const { onEndReached } = useInfiniteScroll({ fetchNextPage, hasNextPage, isFetchingNextPage })
111
+
112
+ <FlashList
113
+ data={items}
114
+ renderItem={renderItem}
115
+ estimatedItemSize={120}
116
+ onEndReached={onEndReached}
117
+ onEndReachedThreshold={0.5}
118
+ ListFooterComponent={isFetchingNextPage ? <ActivityIndicator /> : null}
119
+ />
120
+ ```
121
+
122
+ ### useMutation Pattern
123
+
124
+ ```typescript
125
+ import { useMutation, useQueryClient } from "@tanstack/react-query"
126
+ import { showErrorToast } from "@/shared/components/toast"
127
+ import { logEvent } from "@/shared/utils/log-event"
128
+
129
+ export const useUpdateItem = () => {
130
+ const queryClient = useQueryClient()
131
+
132
+ const { mutate, isPending, error } = useMutation({
133
+ mutationFn: async ({ _id, body }: { _id: string; body: UpdateItemBody }) => {
134
+ return (await client.patch<ItemRes>(`/api/v1/item/${_id}`, body)).data
135
+ },
136
+ onSuccess: () => {
137
+ queryClient.invalidateQueries({ queryKey: [ITEM_INFINITE_LIST_QUERY_KEY] })
138
+ queryClient.invalidateQueries({ queryKey: [ITEM_DETAIL_QUERY_KEY] })
139
+ logEvent("item_updated")
140
+ },
141
+ onError: () => {
142
+ showErrorToast("Update Failed", "Could not update the item. Please try again.")
143
+ },
144
+ })
145
+
146
+ return { updateItem: mutate, isUpdatingItem: isPending, updateItemError: error }
147
+ }
148
+ ```
149
+
150
+ ### Optimistic Updates
151
+
152
+ ```typescript
153
+ export const useToggleFavorite = () => {
154
+ const queryClient = useQueryClient()
155
+
156
+ return useMutation({
157
+ mutationFn: async (id: string) => (await client.patch(`/api/v1/item/${id}/favorite`)).data,
158
+ onMutate: async (id) => {
159
+ await queryClient.cancelQueries({ queryKey: [ITEM_DETAIL_QUERY_KEY, id] })
160
+ const previous = queryClient.getQueryData([ITEM_DETAIL_QUERY_KEY, id])
161
+ queryClient.setQueryData([ITEM_DETAIL_QUERY_KEY, id], (old: any) => ({
162
+ ...old,
163
+ isFavorite: !old.isFavorite,
164
+ }))
165
+ return { previous }
166
+ },
167
+ onError: (_err, id, context) => {
168
+ queryClient.setQueryData([ITEM_DETAIL_QUERY_KEY, id], context?.previous)
169
+ },
170
+ onSettled: (_data, _err, id) => {
171
+ queryClient.invalidateQueries({ queryKey: [ITEM_DETAIL_QUERY_KEY, id] })
172
+ },
173
+ })
174
+ }
175
+ ```
176
+
177
+ ### Async Service Pattern (for Zustand stores)
178
+
179
+ ```typescript
180
+ // Non-hook function for use inside Zustand slices
181
+ export const getStyBalance = async (): Promise<StyBalance> => {
182
+ const response = await client.get<StyBalance>(GET_STY_BALANCE_QUERY_KEY)
183
+ return response.data
184
+ }
185
+ ```
186
+
187
+ ## Type Patterns
188
+
189
+ Types extend shared bases from `@/shared/types/`:
190
+
191
+ ```typescript
192
+ import { IdRes, TimeStampRes } from "@/shared/types"
193
+
194
+ // Response type (composition pattern)
195
+ type ItemRes = {
196
+ name: string
197
+ image_url: string
198
+ category_id: string
199
+ blurhash?: string
200
+ } & IdRes & TimeStampRes
201
+ // IdRes = { _id: string }
202
+ // TimeStampRes = { created_at: string; updated_at: string }
203
+
204
+ // Populated type (nested objects expanded)
205
+ type ItemDetailPopulated = Omit<ItemRes, "styles" | "occasions"> & {
206
+ styles: FashionStyleRes[]
207
+ occasions: FashionOccasionRes[]
208
+ }
209
+
210
+ // Paginated response
211
+ type PaginatedResponse<T> = {
212
+ data: T[]
213
+ meta: PaginationMeta // page, take, item_count, page_count, has_next_page
214
+ }
215
+ ```
216
+
217
+ ## React Query Provider Setup
218
+
219
+ ```typescript
220
+ // src/shared/providers/react-query-provider.tsx
221
+ // - Sets online manager from expo-network (device connectivity)
222
+ // - Sets focus manager from AppState (refetch on app focus)
223
+ // - Exports queryClient singleton
224
+ ```
225
+
226
+ ## Token Storage (`@/shared/libs/token-storage`)
227
+
228
+ Secure JWT storage via `react-native-keychain`:
229
+
230
+ ```typescript
231
+ import { storeTokens, getAccessToken, clearTokens, hasValidTokens, validateTokenIntegrity } from "@/shared/libs/token-storage"
232
+
233
+ await storeTokens({ accessToken, refreshToken })
234
+ const token = await getAccessToken()
235
+ await clearTokens() // On sign-out
236
+ const isValid = await hasValidTokens()
237
+ ```
238
+
239
+ ## Best Practices
240
+
241
+ - **One query/mutation hook per file** — named `use-get-*.ts` or `use-create-*.ts`
242
+ - **Export `QUERY_KEY` constant** from every query file
243
+ - **Query key = API URL path** — always a string constant
244
+ - **Always `invalidateQueries`** in mutation `onSuccess`
245
+ - **Always `showErrorToast`** in mutation `onError`
246
+ - **Always `logEvent`** for analytics in mutation `onSuccess`
247
+ - **Use `enabled` option** to prevent queries without required params
248
+ - **Use `useInfiniteQuery`** for paginated lists, never manual page tracking
249
+ - **Flatten pages** in the hook: `data?.pages.flatMap((p) => p.data) ?? []`
250
+ - **Distinguish loading states**: `isLoading` (initial), `isFetching` (refetch), `isPending` (mutation)
251
+ - **Never import axios directly** — always use `client` from `api-client`
252
+ - **FormData uploads** need `"X-FormData-Upload": "true"` header
@@ -0,0 +1,252 @@
1
+ # Design System — Theme, Colors, Typography, Spacing
2
+
3
+ ## Theme Usage
4
+
5
+ Always import from `@/shared/theme`:
6
+
7
+ ```typescript
8
+ import { Theme, Spacing, scale, moderateScale, withFontWeight } from "@/shared/theme"
9
+
10
+ Theme.color.brand // #EC4899
11
+ Theme.color.text // #262626
12
+ Theme.typography.b1 // { fontSize: 16, lineHeight: 24, fontFamily: "Poppins-Regular" }
13
+ Spacing.lg // 16
14
+ ```
15
+
16
+ ## Color System
17
+
18
+ Sophisticated minimal with rose/blush brand accent. **Always use semantic tokens, never raw hex.**
19
+
20
+ ### Brand Colors (use sparingly)
21
+
22
+ | Token | Value | Use |
23
+ |-------|-------|-----|
24
+ | `brand` | `#EC4899` | Primary brand (buttons, links) |
25
+ | `brandLight` | `#FFE4E9` | Light accent backgrounds |
26
+ | `brandSubtle` | `#FFF1F3` | Super light tint |
27
+ | `brandDark` | `#BE185D` | Dark brand emphasis |
28
+ | `brandText` | `#DB2777` | Brand-colored text |
29
+
30
+ ### Backgrounds
31
+
32
+ | Token | Value | Use |
33
+ |-------|-------|-----|
34
+ | `background` | `#F6F6F9` | Screen background |
35
+ | `backgroundSubtle` | `#EDEDF0` | Secondary background |
36
+ | `backgroundElevated` | `#FFFFFF` | Cards, elevated surfaces |
37
+ | `backgroundInverse` | `#171717` | Dark backgrounds |
38
+
39
+ ### Text
40
+
41
+ | Token | Value | Use |
42
+ |-------|-------|-----|
43
+ | `text` | `#262626` | Primary text |
44
+ | `textSecondary` | `#737373` | Secondary/muted |
45
+ | `textTertiary` | `#A3A3A3` | Placeholder, disabled |
46
+ | `textInverse` | `#FFFFFF` | On dark backgrounds |
47
+ | `textBrand` | `#DB2777` | Brand-colored text |
48
+
49
+ ### Borders
50
+
51
+ | Token | Value | Use |
52
+ |-------|-------|-----|
53
+ | `border` | `#E5E5E5` | Default |
54
+ | `borderSubtle` | `#EDEDED` | Subtle |
55
+ | `borderStrong` | `#D4D4D4` | Emphasized |
56
+ | `borderBrand` | `#FDA4B8` | Brand accent |
57
+
58
+ ### Interactive States
59
+
60
+ `interactive` (brand), `interactiveHover`, `interactiveActive`, `interactiveSubtle` (bg), `interactiveMuted` (selected bg)
61
+
62
+ ### Status Colors
63
+
64
+ | Status | Main | Subtle | Muted | Text |
65
+ |--------|------|--------|-------|------|
66
+ | Success | `#22C55E` | `#F0FDF4` | `#DCFCE7` | `#15803D` |
67
+ | Warning | `#F59E0B` | `#FFFBEB` | `#FEF3C7` | `#B45309` |
68
+ | Error | `#EF4444` | `#FEF2F2` | `#FEE2E2` | `#B91C1C` |
69
+
70
+ ### Special Tokens
71
+
72
+ `overlay` (rgba 0.5), `scrim` (rgba 0.6 for modals), `skeleton` (#E5E5E5), `disabled` (#D4D4D4), `focus` (#F87198), `ripple` (rgba 0.08 for Android)
73
+
74
+ ## Typography
75
+
76
+ Font: **Poppins** (Google Fonts, weights 300-900)
77
+
78
+ ### Variants
79
+
80
+ | Variant | Size | Height | Weight | Use |
81
+ |---------|------|--------|--------|-----|
82
+ | `xxl` | 72 | 80 | ExtraBold | Hero numbers |
83
+ | `h1` | 22 | 33 | Bold | Page titles |
84
+ | `h2` | 20 | 30 | Medium | Section headings |
85
+ | `h3` | 18 | 27 | Medium | Sub-headings |
86
+ | `b1` | 16 | 24 | Regular | Body text |
87
+ | `b2` | 14 | 21 | Regular | Small body |
88
+ | `b3` | 12 | 18 | Regular | Captions, labels |
89
+
90
+ ### Usage
91
+
92
+ ```typescript
93
+ import { Typography } from "@/shared/components"
94
+
95
+ <Typography variant="h1">Page Title</Typography>
96
+ <Typography variant="b2" color={Theme.color.textSecondary}>Subtitle</Typography>
97
+ ```
98
+
99
+ ### Font Weight Override
100
+
101
+ **Always use `withFontWeight()`** — never set fontWeight directly (breaks Android):
102
+
103
+ ```typescript
104
+ import { withFontWeight } from "@/shared/theme"
105
+
106
+ // CORRECT: Maps to correct platform font family
107
+ const boldBody = withFontWeight(Theme.typography.b1, "700")
108
+
109
+ // WRONG: Breaks on Android (fontWeight without matching fontFamily)
110
+ const bad = { ...Theme.typography.b1, fontWeight: "700" }
111
+ ```
112
+
113
+ ### Platform Font Families
114
+
115
+ | Weight | iOS | Android |
116
+ |--------|-----|---------|
117
+ | 300 | `Poppins-Light` | `Poppins_300Light` |
118
+ | 400 | `Poppins-Regular` | `Poppins_400Regular` |
119
+ | 500 | `Poppins-Medium` | `Poppins_500Medium` |
120
+ | 600 | `Poppins-SemiBold` | `Poppins_600SemiBold` |
121
+ | 700 | `Poppins-Bold` | `Poppins_700Bold` |
122
+ | 800 | `Poppins-ExtraBold` | `Poppins_800ExtraBold` |
123
+
124
+ Typography component: `maxFontSizeMultiplier = 1.15` (prevents OS font scaling issues).
125
+
126
+ ## Spacing System
127
+
128
+ 4-point grid. **Always use `Spacing.*` tokens.**
129
+
130
+ ### Component Spacing
131
+
132
+ | Token | Value | Use |
133
+ |-------|-------|-----|
134
+ | `xs` | 4 | Tight elements |
135
+ | `sm` | 8 | Compact layouts |
136
+ | `md` | 12 | Standard gaps |
137
+ | `lg` | 16 | Comfortable gaps |
138
+ | `xl` | 20 | Emphasis |
139
+ | `xxl` | 24 | Section spacing |
140
+ | `xxxl` | 32 | Large sections |
141
+
142
+ ### Layout Spacing
143
+
144
+ | Token | Value | Use |
145
+ |-------|-------|-----|
146
+ | `screenHorizontal` | 16 | Screen padding L/R |
147
+ | `screenVertical` | 24 | Screen padding T/B |
148
+ | `sectionGap` | 24 | Between sections |
149
+ | `cardPadding` | 16 | Card internal padding |
150
+ | `cardInnerGap` | 12 | Gap inside cards |
151
+ | `cardRadius` | 16 | Standard card radius |
152
+ | `cardRadiusLarge` | 20 | Hero card radius |
153
+ | `touchTarget` | 44 | Min touch size (iOS HIG) |
154
+ | `listItemGap` | 12 | Between list items |
155
+
156
+ ## Responsive Scaling
157
+
158
+ Baseline: 375x812 (iPhone 13/14)
159
+
160
+ ```typescript
161
+ import { scale, verticalScale, moderateScale, DeviceSize } from "@/shared/theme"
162
+
163
+ scale(16) // Linear horizontal scaling
164
+ verticalScale(24) // Linear vertical scaling
165
+ moderateScale(16) // Dampened (factor=0.5, recommended default)
166
+ moderateScale(16, 0.3) // Less aggressive
167
+
168
+ // Device breakpoints
169
+ DeviceSize.isSmall // < 360px (older devices)
170
+ DeviceSize.isMedium // 360-399px (mid-range)
171
+ DeviceSize.isLarge // >= 400px (standard+)
172
+ ```
173
+
174
+ Use `moderateScale` for text/padding, `scale` for widths/icons, raw values for tokens/borders.
175
+
176
+ ## Shadow Pattern
177
+
178
+ ```typescript
179
+ const styles = StyleSheet.create({
180
+ card: {
181
+ backgroundColor: Theme.color.backgroundElevated,
182
+ borderRadius: Spacing.cardRadius,
183
+ padding: Spacing.cardPadding,
184
+ shadowColor: Theme.color.shadowColor,
185
+ shadowOffset: { width: 0, height: 2 },
186
+ shadowOpacity: 0.08,
187
+ shadowRadius: 8,
188
+ elevation: 3, // Android
189
+ },
190
+ })
191
+ ```
192
+
193
+ ## Standard Card Pattern
194
+
195
+ Consistent card style used across all sections (overview, lists, settings):
196
+
197
+ ```typescript
198
+ const styles = StyleSheet.create({
199
+ card: {
200
+ backgroundColor: Theme.color.backgroundElevated,
201
+ borderRadius: Spacing.cardRadius, // 16
202
+ padding: Spacing.cardPadding, // 16
203
+ borderWidth: 1,
204
+ borderColor: Theme.color.border,
205
+ },
206
+ })
207
+ ```
208
+
209
+ For hero/featured cards, use `brandSubtle` background with `brandLight` border:
210
+ ```typescript
211
+ heroCard: {
212
+ backgroundColor: Theme.color.brandSubtle,
213
+ borderRadius: Spacing.cardRadius,
214
+ padding: Spacing.cardPadding,
215
+ borderWidth: 1,
216
+ borderColor: Theme.color.brandLight,
217
+ }
218
+ ```
219
+
220
+ ## Progress Bar Pattern
221
+
222
+ Standard progress bar: 4px height, 2px radius, `backgroundMuted` track, `brand` fill.
223
+ Use status colors (error/warning) only for actual error/warning states.
224
+
225
+ ```typescript
226
+ progressTrack: {
227
+ height: 4,
228
+ borderRadius: 2,
229
+ backgroundColor: Theme.color.backgroundMuted,
230
+ overflow: "hidden",
231
+ },
232
+ progressFill: {
233
+ height: "100%",
234
+ borderRadius: 2,
235
+ backgroundColor: Theme.color.brand, // Default — use consistently
236
+ },
237
+ ```
238
+
239
+ ## Safe Area in Modals
240
+
241
+ For absolute-positioned elements inside modals (e.g., close button in fullscreen image viewer),
242
+ `SafeAreaView` doesn't reliably apply insets. Use `useSafeAreaInsets()` with explicit padding:
243
+
244
+ ```typescript
245
+ const insets = useSafeAreaInsets()
246
+ const topPadding = insets.top + (Platform.OS === "ios" ? 8 : 12)
247
+
248
+ <View style={{ paddingTop: topPadding }}>
249
+ <TouchableOpacity onPress={onClose}>...</TouchableOpacity>
250
+ </View>
251
+ ```
252
+ ```