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.
- package/.wrangler/cache/pages.json +4 -0
- package/.wrangler/cache/wrangler-account.json +6 -0
- package/DEVELOPMENT.md +510 -0
- package/README.md +104 -0
- package/agents/developer.md +56 -0
- package/agents/frontend-design.md +69 -0
- package/agents/mobile-reviewer.md +36 -0
- package/agents/review-code.md +49 -0
- package/agents/security-scanner.md +50 -0
- package/agents/tester.md +72 -0
- package/bin/cli.js +461 -0
- package/landing/ai-build-ai.png +0 -0
- package/landing/index.html +1495 -0
- package/landing/logo.png +0 -0
- package/package.json +37 -0
- package/skills/active-life-dev/SKILL.md +157 -0
- package/skills/active-life-dev/docs/auth.md +187 -0
- package/skills/active-life-dev/docs/customers.md +216 -0
- package/skills/active-life-dev/docs/integrations.md +209 -0
- package/skills/active-life-dev/docs/inventory.md +192 -0
- package/skills/active-life-dev/docs/modules.md +181 -0
- package/skills/active-life-dev/docs/orders.md +180 -0
- package/skills/active-life-dev/docs/patterns.md +319 -0
- package/skills/active-life-dev/docs/products.md +216 -0
- package/skills/active-life-dev/docs/schema.md +502 -0
- package/skills/active-life-dev/docs/setup.md +169 -0
- package/skills/active-life-dev/docs/vouchers.md +144 -0
- package/skills/ai-asset-generator/SKILL.md +247 -0
- package/skills/ai-asset-generator/docs/gen-image.md +274 -0
- package/skills/ai-asset-generator/docs/genvideo.md +341 -0
- package/skills/ai-asset-generator/docs/remove-background.md +19 -0
- package/skills/ai-asset-generator/lib/bg-remove.mjs +34 -0
- package/skills/ai-asset-generator/lib/env.mjs +48 -0
- package/skills/ai-asset-generator/lib/kie-client.mjs +100 -0
- package/skills/ai-build-ai/SKILL.md +127 -0
- package/skills/ai-build-ai/docs/agent-teams.md +293 -0
- package/skills/ai-build-ai/docs/checkpointing.md +161 -0
- package/skills/ai-build-ai/docs/create-agent.md +399 -0
- package/skills/ai-build-ai/docs/create-mcp.md +395 -0
- package/skills/ai-build-ai/docs/create-skill.md +299 -0
- package/skills/ai-build-ai/docs/headless-mode.md +614 -0
- package/skills/ai-build-ai/docs/hooks.md +578 -0
- package/skills/ai-build-ai/docs/memory-claude-md.md +375 -0
- package/skills/ai-build-ai/docs/output-styles.md +208 -0
- package/skills/ai-build-ai/docs/overview.md +162 -0
- package/skills/ai-build-ai/docs/permissions.md +391 -0
- package/skills/ai-build-ai/docs/plugins.md +396 -0
- package/skills/ai-build-ai/docs/sandbox.md +262 -0
- package/skills/ai-build-ai/docs/team-lead-workflow.md +648 -0
- package/skills/ant-design/SKILL.md +323 -0
- package/skills/ant-design/docs/components.md +160 -0
- package/skills/ant-design/docs/data-entry.md +406 -0
- package/skills/ant-design/docs/display.md +594 -0
- package/skills/ant-design/docs/feedback.md +451 -0
- package/skills/ant-design/docs/key-components.md +414 -0
- package/skills/ant-design/docs/navigation.md +310 -0
- package/skills/ant-design/docs/pro-components.md +543 -0
- package/skills/ant-design/docs/setup.md +213 -0
- package/skills/ant-design/docs/theme.md +265 -0
- package/skills/flutter-performance/SKILL.md +803 -0
- package/skills/flutter-performance/references/flutter-patterns.md +595 -0
- package/skills/icon-generator/SKILL.md +270 -0
- package/skills/mobile-app-review/SKILL.md +321 -0
- package/skills/mobile-app-review/references/apple-review.md +132 -0
- package/skills/mobile-app-review/references/google-play-review.md +203 -0
- package/skills/mongodb/SKILL.md +667 -0
- package/skills/mongodb/references/mongoose-patterns.md +368 -0
- package/skills/nestjs-architecture/SKILL.md +1086 -0
- package/skills/nestjs-architecture/references/advanced-patterns.md +590 -0
- package/skills/performance/SKILL.md +509 -0
- package/skills/react-fsd-architecture/SKILL.md +693 -0
- package/skills/react-fsd-architecture/references/fsd-patterns.md +747 -0
- package/skills/react-native-expo/SKILL.md +128 -0
- package/skills/react-native-expo/references/data-layer.md +252 -0
- package/skills/react-native-expo/references/design-system.md +252 -0
- package/skills/react-native-expo/references/navigation.md +199 -0
- package/skills/react-native-expo/references/performance.md +229 -0
- package/skills/react-native-expo/references/platform-services.md +179 -0
- package/skills/react-native-expo/references/state-management.md +209 -0
- package/skills/react-native-expo/references/ui-patterns.md +301 -0
- package/skills/react-query/SKILL.md +685 -0
- package/skills/react-query/references/query-patterns.md +365 -0
- package/skills/review-code/SKILL.md +374 -0
- package/skills/review-code/references/clean-code-principles.md +395 -0
- package/skills/review-code/references/frontend-patterns.md +136 -0
- package/skills/review-code/references/nestjs-patterns.md +184 -0
- package/skills/security-scanner/SKILL.md +366 -0
- package/skills/security-scanner/references/nestjs-security.md +260 -0
- package/skills/security-scanner/references/nextjs-security.md +201 -0
- package/skills/security-scanner/references/react-native-security.md +199 -0
- package/skills/traefik/SKILL.md +105 -0
- package/skills/traefik/docs/advanced-routing.md +186 -0
- package/skills/traefik/docs/auth-providers.md +137 -0
- package/skills/traefik/docs/cicd-devops.md +396 -0
- package/skills/traefik/docs/core-config.md +171 -0
- package/skills/traefik/docs/distributed-config.md +96 -0
- package/skills/traefik/docs/docker-compose.md +182 -0
- package/skills/traefik/docs/ha-performance.md +177 -0
- package/skills/traefik/docs/kubernetes.md +278 -0
- package/skills/traefik/docs/middleware.md +205 -0
- package/skills/traefik/docs/monitoring.md +357 -0
- package/skills/traefik/docs/security.md +391 -0
- package/skills/traefik/docs/tls-acme.md +155 -0
- package/skills/ui-ux-pro-max/SKILL.md +377 -0
- package/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
# Active Life Backend - Prisma Database Schema
|
|
2
|
+
|
|
3
|
+
## Location
|
|
4
|
+
`prisma/schema.prisma`
|
|
5
|
+
|
|
6
|
+
## Database
|
|
7
|
+
PostgreSQL via Supabase with `@prisma/adapter-pg`
|
|
8
|
+
|
|
9
|
+
## Enums
|
|
10
|
+
|
|
11
|
+
```prisma
|
|
12
|
+
enum RewardType { POINT, VOUCHER }
|
|
13
|
+
enum VoucherType { PERCENT, FIXED }
|
|
14
|
+
enum OrderStatus {
|
|
15
|
+
PENDING, PROCESSING, SHIPPED, DELIVERED, CANCELLED,
|
|
16
|
+
REFUNDED, RETURN_PENDING, RETURN_RECEIVED, RETURN_REJECTED, REFUND_PENDING
|
|
17
|
+
}
|
|
18
|
+
enum PaymentMethod { CASH, TRANSFER }
|
|
19
|
+
enum InventoryTransactionType { IMPORT, EXPORT }
|
|
20
|
+
enum LogOrderType {
|
|
21
|
+
CREATE, PROCESS, SHIP, DELIVER, CANCEL_CLIENT, CANCEL_STAFF,
|
|
22
|
+
RETURN, RETURN_CREATE, RETURN_RECEIVE, RETURN_COMPLETE,
|
|
23
|
+
REFUND, REFUND_CREATE, REFUND_COMPLETE
|
|
24
|
+
}
|
|
25
|
+
enum ProductStatus { HIDDEN, NORMAL, HIGH }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Models (36 total)
|
|
29
|
+
|
|
30
|
+
### Core: Role & User (Staff)
|
|
31
|
+
|
|
32
|
+
```prisma
|
|
33
|
+
model Role {
|
|
34
|
+
id String @id @default(uuid())
|
|
35
|
+
name String @unique // Admin, TeleSales, LiveChat, Agency
|
|
36
|
+
permissions String[]
|
|
37
|
+
users User[]
|
|
38
|
+
createdAt DateTime @default(now())
|
|
39
|
+
updatedAt DateTime @updatedAt
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
model User {
|
|
43
|
+
id String @id @default(uuid())
|
|
44
|
+
phone String @unique
|
|
45
|
+
email String? @unique
|
|
46
|
+
name String
|
|
47
|
+
password String
|
|
48
|
+
avatar String?
|
|
49
|
+
isBan Boolean @default(false)
|
|
50
|
+
roleId String
|
|
51
|
+
role Role @relation(fields: [roleId], references: [id])
|
|
52
|
+
extension EtelecomExtension? // 1-to-1 SIP extension
|
|
53
|
+
createdAt DateTime @default(now())
|
|
54
|
+
updatedAt DateTime @updatedAt
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Core: Client (Customer)
|
|
59
|
+
|
|
60
|
+
```prisma
|
|
61
|
+
model Client {
|
|
62
|
+
id String @id @default(uuid())
|
|
63
|
+
phone String @unique
|
|
64
|
+
email String?
|
|
65
|
+
name String
|
|
66
|
+
password String?
|
|
67
|
+
avatar String?
|
|
68
|
+
dob DateTime?
|
|
69
|
+
gender String?
|
|
70
|
+
address String?
|
|
71
|
+
province String?
|
|
72
|
+
district String?
|
|
73
|
+
ward String?
|
|
74
|
+
point Int @default(0)
|
|
75
|
+
isBan Boolean @default(false)
|
|
76
|
+
note String?
|
|
77
|
+
tags String[]
|
|
78
|
+
// CRM fields
|
|
79
|
+
customerSourceId String?
|
|
80
|
+
customerStatusId String?
|
|
81
|
+
customerDetailStatusId String?
|
|
82
|
+
livechatStaffId String?
|
|
83
|
+
telesalesStaffId String?
|
|
84
|
+
agencyStaffId String?
|
|
85
|
+
firstCallAt DateTime?
|
|
86
|
+
lastOrderAt DateTime?
|
|
87
|
+
// Relations
|
|
88
|
+
orders Order[]
|
|
89
|
+
cart Cart?
|
|
90
|
+
vouchers ClientVoucher[]
|
|
91
|
+
bank ClientBank?
|
|
92
|
+
spinTransactions SpinTransaction[]
|
|
93
|
+
redeemTransactions RedeemTransaction[]
|
|
94
|
+
createdAt DateTime @default(now())
|
|
95
|
+
updatedAt DateTime @updatedAt
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### CRM Lookup Tables
|
|
100
|
+
|
|
101
|
+
```prisma
|
|
102
|
+
model CustomerSource {
|
|
103
|
+
id String @id @default(uuid())
|
|
104
|
+
name String @unique
|
|
105
|
+
description String?
|
|
106
|
+
createdAt DateTime @default(now())
|
|
107
|
+
updatedAt DateTime @updatedAt
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
model CustomerStatus {
|
|
111
|
+
id String @id @default(uuid())
|
|
112
|
+
name String @unique
|
|
113
|
+
description String?
|
|
114
|
+
createdAt DateTime @default(now())
|
|
115
|
+
updatedAt DateTime @updatedAt
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
model CustomerDetailStatus {
|
|
119
|
+
id String @id @default(uuid())
|
|
120
|
+
name String @unique
|
|
121
|
+
description String?
|
|
122
|
+
createdAt DateTime @default(now())
|
|
123
|
+
updatedAt DateTime @updatedAt
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
model InteractionStatus { /* same shape */ }
|
|
127
|
+
model InteractionResult { /* same shape */ }
|
|
128
|
+
model AdsSource { /* same shape */ }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Product Catalog
|
|
132
|
+
|
|
133
|
+
```prisma
|
|
134
|
+
model Category {
|
|
135
|
+
id String @id @default(uuid())
|
|
136
|
+
name String
|
|
137
|
+
slug String @unique
|
|
138
|
+
image String?
|
|
139
|
+
description String?
|
|
140
|
+
parentId String?
|
|
141
|
+
parent Category? @relation("CategoryTree", fields: [parentId], references: [id])
|
|
142
|
+
children Category[] @relation("CategoryTree")
|
|
143
|
+
brands CategoryBrand[]
|
|
144
|
+
products ProductCategory[]
|
|
145
|
+
createdAt DateTime @default(now())
|
|
146
|
+
updatedAt DateTime @updatedAt
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
model Brand {
|
|
150
|
+
id String @id @default(uuid())
|
|
151
|
+
name String
|
|
152
|
+
slug String @unique
|
|
153
|
+
image String?
|
|
154
|
+
description String?
|
|
155
|
+
categories CategoryBrand[]
|
|
156
|
+
inventoryProducts InventoryProduct[]
|
|
157
|
+
createdAt DateTime @default(now())
|
|
158
|
+
updatedAt DateTime @updatedAt
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
model CategoryBrand {
|
|
162
|
+
id String @id @default(uuid())
|
|
163
|
+
categoryId String
|
|
164
|
+
brandId String
|
|
165
|
+
category Category @relation(fields: [categoryId], references: [id])
|
|
166
|
+
brand Brand @relation(fields: [brandId], references: [id])
|
|
167
|
+
@@unique([categoryId, brandId])
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
model Unit {
|
|
171
|
+
id String @id @default(uuid())
|
|
172
|
+
name String @unique
|
|
173
|
+
abbreviation String?
|
|
174
|
+
createdAt DateTime @default(now())
|
|
175
|
+
updatedAt DateTime @updatedAt
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Store Products & Combos
|
|
180
|
+
|
|
181
|
+
```prisma
|
|
182
|
+
model StoreProduct {
|
|
183
|
+
id String @id @default(uuid())
|
|
184
|
+
name String
|
|
185
|
+
slug String @unique
|
|
186
|
+
description String?
|
|
187
|
+
images String[]
|
|
188
|
+
status ProductStatus @default(NORMAL)
|
|
189
|
+
categories ProductCategory[]
|
|
190
|
+
combos StoreProductCombo[]
|
|
191
|
+
cartItems CartItem[]
|
|
192
|
+
orderItems OrderItem[]
|
|
193
|
+
createdAt DateTime @default(now())
|
|
194
|
+
updatedAt DateTime @updatedAt
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
model ProductCategory {
|
|
198
|
+
id String @id @default(uuid())
|
|
199
|
+
productId String
|
|
200
|
+
categoryId String
|
|
201
|
+
product StoreProduct @relation(fields: [productId], references: [id])
|
|
202
|
+
category Category @relation(fields: [categoryId], references: [id])
|
|
203
|
+
@@unique([productId, categoryId])
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
model StoreProductCombo {
|
|
207
|
+
id String @id @default(uuid())
|
|
208
|
+
name String
|
|
209
|
+
price Float
|
|
210
|
+
comparePrice Float?
|
|
211
|
+
productId String
|
|
212
|
+
product StoreProduct @relation(fields: [productId], references: [id])
|
|
213
|
+
items StoreProductComboItem[]
|
|
214
|
+
giftItems StoreProductComboItem[] @relation("GiftItems")
|
|
215
|
+
promotionHistory StoreProductComboPromotionHistory[]
|
|
216
|
+
cartItems CartItem[]
|
|
217
|
+
orderItems OrderItem[]
|
|
218
|
+
createdAt DateTime @default(now())
|
|
219
|
+
updatedAt DateTime @updatedAt
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
model StoreProductComboItem {
|
|
223
|
+
id String @id @default(uuid())
|
|
224
|
+
comboId String?
|
|
225
|
+
giftComboId String?
|
|
226
|
+
inventoryProductId String
|
|
227
|
+
quantity Int
|
|
228
|
+
combo StoreProductCombo? @relation(fields: [comboId], references: [id])
|
|
229
|
+
giftCombo StoreProductCombo? @relation("GiftItems", fields: [giftComboId], references: [id])
|
|
230
|
+
inventoryProduct InventoryProduct @relation(fields: [inventoryProductId], references: [id])
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
model StoreProductComboPromotionHistory {
|
|
234
|
+
id String @id @default(uuid())
|
|
235
|
+
comboId String
|
|
236
|
+
oldPrice Float
|
|
237
|
+
newPrice Float
|
|
238
|
+
reason String?
|
|
239
|
+
combo StoreProductCombo @relation(fields: [comboId], references: [id])
|
|
240
|
+
createdAt DateTime @default(now())
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Inventory System
|
|
245
|
+
|
|
246
|
+
```prisma
|
|
247
|
+
model InventoryProduct {
|
|
248
|
+
id String @id @default(uuid())
|
|
249
|
+
name String
|
|
250
|
+
sku String? @unique
|
|
251
|
+
barcode String?
|
|
252
|
+
image String?
|
|
253
|
+
unitId String?
|
|
254
|
+
brandId String?
|
|
255
|
+
unit Unit? @relation(fields: [unitId], references: [id])
|
|
256
|
+
brand Brand? @relation(fields: [brandId], references: [id])
|
|
257
|
+
comboItems StoreProductComboItem[]
|
|
258
|
+
items ProductInventoryItem[]
|
|
259
|
+
transactions InventoryTransaction[]
|
|
260
|
+
createdAt DateTime @default(now())
|
|
261
|
+
updatedAt DateTime @updatedAt
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
model ProductInventoryItem {
|
|
265
|
+
id String @id @default(uuid())
|
|
266
|
+
inventoryProductId String
|
|
267
|
+
quantity Int
|
|
268
|
+
remainingQuantity Int
|
|
269
|
+
expiryDate DateTime?
|
|
270
|
+
lotNumber String?
|
|
271
|
+
inventoryProduct InventoryProduct @relation(fields: [inventoryProductId], references: [id])
|
|
272
|
+
createdAt DateTime @default(now())
|
|
273
|
+
updatedAt DateTime @updatedAt
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
model InventorySession {
|
|
277
|
+
id String @id @default(uuid())
|
|
278
|
+
type InventoryTransactionType
|
|
279
|
+
note String?
|
|
280
|
+
userId String // Staff who created
|
|
281
|
+
transactions InventoryTransaction[]
|
|
282
|
+
createdAt DateTime @default(now())
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
model InventoryTransaction {
|
|
286
|
+
id String @id @default(uuid())
|
|
287
|
+
sessionId String
|
|
288
|
+
inventoryProductId String
|
|
289
|
+
quantity Int
|
|
290
|
+
expiryDate DateTime?
|
|
291
|
+
lotNumber String?
|
|
292
|
+
session InventorySession @relation(fields: [sessionId], references: [id])
|
|
293
|
+
inventoryProduct InventoryProduct @relation(fields: [inventoryProductId], references: [id])
|
|
294
|
+
orderItemId String? // Links to order item when exporting
|
|
295
|
+
createdAt DateTime @default(now())
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Shopping Cart
|
|
300
|
+
|
|
301
|
+
```prisma
|
|
302
|
+
model Cart {
|
|
303
|
+
id String @id @default(uuid())
|
|
304
|
+
clientId String @unique
|
|
305
|
+
client Client @relation(fields: [clientId], references: [id])
|
|
306
|
+
items CartItem[]
|
|
307
|
+
createdAt DateTime @default(now())
|
|
308
|
+
updatedAt DateTime @updatedAt
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
model CartItem {
|
|
312
|
+
id String @id @default(uuid())
|
|
313
|
+
cartId String
|
|
314
|
+
productId String
|
|
315
|
+
comboId String
|
|
316
|
+
quantity Int
|
|
317
|
+
cart Cart @relation(fields: [cartId], references: [id])
|
|
318
|
+
product StoreProduct @relation(fields: [productId], references: [id])
|
|
319
|
+
combo StoreProductCombo @relation(fields: [comboId], references: [id])
|
|
320
|
+
createdAt DateTime @default(now())
|
|
321
|
+
updatedAt DateTime @updatedAt
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Orders
|
|
326
|
+
|
|
327
|
+
```prisma
|
|
328
|
+
model Order {
|
|
329
|
+
id String @id @default(uuid())
|
|
330
|
+
orderCode String @unique
|
|
331
|
+
clientId String
|
|
332
|
+
status OrderStatus @default(PENDING)
|
|
333
|
+
paymentMethod PaymentMethod
|
|
334
|
+
totalAmount Float
|
|
335
|
+
discountAmount Float @default(0)
|
|
336
|
+
finalAmount Float
|
|
337
|
+
shippingAddress String?
|
|
338
|
+
shippingFee Float @default(0)
|
|
339
|
+
note String?
|
|
340
|
+
voucherId String?
|
|
341
|
+
staffId String? // Staff who processed
|
|
342
|
+
client Client @relation(fields: [clientId], references: [id])
|
|
343
|
+
items OrderItem[]
|
|
344
|
+
logs LogOrder[]
|
|
345
|
+
createdAt DateTime @default(now())
|
|
346
|
+
updatedAt DateTime @updatedAt
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
model OrderItem {
|
|
350
|
+
id String @id @default(uuid())
|
|
351
|
+
orderId String
|
|
352
|
+
productId String
|
|
353
|
+
comboId String
|
|
354
|
+
quantity Int
|
|
355
|
+
price Float
|
|
356
|
+
order Order @relation(fields: [orderId], references: [id])
|
|
357
|
+
product StoreProduct @relation(fields: [productId], references: [id])
|
|
358
|
+
combo StoreProductCombo @relation(fields: [comboId], references: [id])
|
|
359
|
+
createdAt DateTime @default(now())
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
model LogOrder {
|
|
363
|
+
id String @id @default(uuid())
|
|
364
|
+
orderId String
|
|
365
|
+
type LogOrderType
|
|
366
|
+
note String?
|
|
367
|
+
userId String? // Staff who made the change
|
|
368
|
+
order Order @relation(fields: [orderId], references: [id])
|
|
369
|
+
createdAt DateTime @default(now())
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Vouchers & Rewards
|
|
374
|
+
|
|
375
|
+
```prisma
|
|
376
|
+
model Voucher {
|
|
377
|
+
id String @id @default(uuid())
|
|
378
|
+
code String @unique
|
|
379
|
+
type VoucherType
|
|
380
|
+
value Float // Percentage or fixed amount
|
|
381
|
+
minOrder Float? // Minimum order to use
|
|
382
|
+
maxDiscount Float? // Max discount (for PERCENT type)
|
|
383
|
+
quantity Int // Total available
|
|
384
|
+
usedCount Int @default(0)
|
|
385
|
+
expiryDate DateTime?
|
|
386
|
+
isActive Boolean @default(true)
|
|
387
|
+
clients ClientVoucher[]
|
|
388
|
+
createdAt DateTime @default(now())
|
|
389
|
+
updatedAt DateTime @updatedAt
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
model ClientVoucher {
|
|
393
|
+
id String @id @default(uuid())
|
|
394
|
+
clientId String
|
|
395
|
+
voucherId String
|
|
396
|
+
isUsed Boolean @default(false)
|
|
397
|
+
usedAt DateTime?
|
|
398
|
+
client Client @relation(fields: [clientId], references: [id])
|
|
399
|
+
voucher Voucher @relation(fields: [voucherId], references: [id])
|
|
400
|
+
@@unique([clientId, voucherId])
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
model SpinReward {
|
|
404
|
+
id String @id @default(uuid())
|
|
405
|
+
name String
|
|
406
|
+
type RewardType
|
|
407
|
+
value Float // Points or voucher value
|
|
408
|
+
probability Float // 0-100 chance
|
|
409
|
+
isActive Boolean @default(true)
|
|
410
|
+
transactions SpinTransaction[]
|
|
411
|
+
createdAt DateTime @default(now())
|
|
412
|
+
updatedAt DateTime @updatedAt
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
model SpinTransaction {
|
|
416
|
+
id String @id @default(uuid())
|
|
417
|
+
clientId String
|
|
418
|
+
rewardId String
|
|
419
|
+
client Client @relation(fields: [clientId], references: [id])
|
|
420
|
+
reward SpinReward @relation(fields: [rewardId], references: [id])
|
|
421
|
+
createdAt DateTime @default(now())
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
model RedeemTransaction {
|
|
425
|
+
id String @id @default(uuid())
|
|
426
|
+
clientId String
|
|
427
|
+
points Int
|
|
428
|
+
description String?
|
|
429
|
+
client Client @relation(fields: [clientId], references: [id])
|
|
430
|
+
createdAt DateTime @default(now())
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### eTelecom Integration
|
|
435
|
+
|
|
436
|
+
```prisma
|
|
437
|
+
model EtelecomExtension {
|
|
438
|
+
id String @id @default(uuid())
|
|
439
|
+
extensionNumber String @unique
|
|
440
|
+
extensionPassword String
|
|
441
|
+
tenantId String?
|
|
442
|
+
tenantDomain String?
|
|
443
|
+
userId String? @unique // 1-to-1 with User
|
|
444
|
+
user User? @relation(fields: [userId], references: [id])
|
|
445
|
+
createdAt DateTime @default(now())
|
|
446
|
+
updatedAt DateTime @updatedAt
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
model OmiCall {
|
|
450
|
+
id String @id @default(uuid())
|
|
451
|
+
callId String @unique
|
|
452
|
+
direction String // inbound/outbound
|
|
453
|
+
fromNumber String
|
|
454
|
+
toNumber String
|
|
455
|
+
status String // answered, missed, busy
|
|
456
|
+
duration Int? // seconds
|
|
457
|
+
recordingUrl String?
|
|
458
|
+
responseData Json?
|
|
459
|
+
userId String? // Staff who handled
|
|
460
|
+
createdAt DateTime @default(now())
|
|
461
|
+
updatedAt DateTime @updatedAt
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Other
|
|
466
|
+
|
|
467
|
+
```prisma
|
|
468
|
+
model ClientBank {
|
|
469
|
+
id String @id @default(uuid())
|
|
470
|
+
clientId String @unique
|
|
471
|
+
bankName String
|
|
472
|
+
accountNumber String
|
|
473
|
+
accountHolder String
|
|
474
|
+
client Client @relation(fields: [clientId], references: [id])
|
|
475
|
+
createdAt DateTime @default(now())
|
|
476
|
+
updatedAt DateTime @updatedAt
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
## Key Relationships Diagram
|
|
481
|
+
|
|
482
|
+
```
|
|
483
|
+
Role ──< User ──? EtelecomExtension
|
|
484
|
+
└──< OmiCall
|
|
485
|
+
|
|
486
|
+
Client ──? Cart ──< CartItem ──> StoreProduct + StoreProductCombo
|
|
487
|
+
──< Order ──< OrderItem ──> StoreProduct + StoreProductCombo
|
|
488
|
+
│ └──< LogOrder
|
|
489
|
+
──< ClientVoucher ──> Voucher
|
|
490
|
+
──? ClientBank
|
|
491
|
+
──< SpinTransaction ──> SpinReward
|
|
492
|
+
──< RedeemTransaction
|
|
493
|
+
|
|
494
|
+
Category (self-ref tree) ──< CategoryBrand ──> Brand
|
|
495
|
+
──< ProductCategory ──> StoreProduct
|
|
496
|
+
|
|
497
|
+
StoreProduct ──< StoreProductCombo ──< StoreProductComboItem ──> InventoryProduct
|
|
498
|
+
──< StoreProductComboPromotionHistory
|
|
499
|
+
|
|
500
|
+
InventoryProduct ──< ProductInventoryItem (lots with expiry)
|
|
501
|
+
──< InventoryTransaction ──> InventorySession
|
|
502
|
+
```
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Active Life Backend - Setup & Configuration
|
|
2
|
+
|
|
3
|
+
## Project Location
|
|
4
|
+
```
|
|
5
|
+
/Users/nhiensoft/Workspace/ginstudio/active-life/be-store-active-life-global/
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
- Node.js (ES2022 compatible)
|
|
10
|
+
- Yarn package manager
|
|
11
|
+
- PostgreSQL (Supabase recommended)
|
|
12
|
+
- Redis (optional, for caching)
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
cd be-store-active-life-global
|
|
18
|
+
yarn install
|
|
19
|
+
yarn db:generate # Generate Prisma client
|
|
20
|
+
yarn dev # Start dev server with hot-reload
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Environment Variables
|
|
24
|
+
|
|
25
|
+
### Required (.env)
|
|
26
|
+
```env
|
|
27
|
+
DATABASE_URL="" # Supabase pooler connection (for runtime)
|
|
28
|
+
DIRECT_URL="" # Direct DB connection (for migrations)
|
|
29
|
+
PORT=3000 # Server port
|
|
30
|
+
JWT_CLIENT_ACCESS_TOKEN_SECRET="" # JWT signing secret
|
|
31
|
+
JWT_CLIENT_ACCESS_EXPIRE="15d" # Token expiry duration
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Supabase Connection Setup
|
|
35
|
+
|
|
36
|
+
**IMPORTANT**: Use the correct connection format for Supabase:
|
|
37
|
+
|
|
38
|
+
**Pooler URL (DATABASE_URL)** — for application runtime:
|
|
39
|
+
```
|
|
40
|
+
postgresql://postgres.[project-ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres?pgbouncer=true
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Direct URL (DIRECT_URL)** — for migrations only:
|
|
44
|
+
```
|
|
45
|
+
postgresql://postgres.[project-ref]:[password]@aws-0-[region].pooler.supabase.com:5432/postgres
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Common Error**: "Tenant or user not found" — Make sure username is `postgres.[project-ref]` NOT just `postgres`.
|
|
49
|
+
|
|
50
|
+
### Optional Environment Variables
|
|
51
|
+
```env
|
|
52
|
+
# eTelecom
|
|
53
|
+
ETELECOM_URL= # eTelecom API base URL
|
|
54
|
+
AT_ETELECOM= # eTelecom API token
|
|
55
|
+
|
|
56
|
+
# Email
|
|
57
|
+
MAIL_HOST= # SMTP host
|
|
58
|
+
MAIL_PORT= # SMTP port
|
|
59
|
+
MAIL_USER= # SMTP username
|
|
60
|
+
MAIL_PASS= # SMTP password
|
|
61
|
+
MAIL_FROM= # Default sender
|
|
62
|
+
|
|
63
|
+
# Firebase
|
|
64
|
+
FIREBASE_PROJECT_ID=
|
|
65
|
+
FIREBASE_PRIVATE_KEY=
|
|
66
|
+
FIREBASE_CLIENT_EMAIL=
|
|
67
|
+
|
|
68
|
+
# Redis
|
|
69
|
+
REDIS_URL= # Redis connection URL
|
|
70
|
+
|
|
71
|
+
# MongoDB (if used)
|
|
72
|
+
MONGODB_URI= # MongoDB connection string
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Database Commands
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Sync schema to database (keeps existing data)
|
|
79
|
+
yarn db:push
|
|
80
|
+
|
|
81
|
+
# Create and run a migration (production-ready)
|
|
82
|
+
yarn db:migrate
|
|
83
|
+
|
|
84
|
+
# Seed database with initial data
|
|
85
|
+
yarn db:seed
|
|
86
|
+
|
|
87
|
+
# Open Prisma Studio (GUI for database)
|
|
88
|
+
yarn db:studio
|
|
89
|
+
|
|
90
|
+
# Generate Prisma client (after schema changes)
|
|
91
|
+
yarn db:generate
|
|
92
|
+
|
|
93
|
+
# Reset database (DESTRUCTIVE - drops all data)
|
|
94
|
+
yarn db:reset
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Migration Workflow
|
|
98
|
+
1. Edit `prisma/schema.prisma`
|
|
99
|
+
2. Run `yarn db:push` for development (quick, keeps data)
|
|
100
|
+
3. Run `yarn db:migrate` for production (creates migration files)
|
|
101
|
+
4. Commit migration files to git
|
|
102
|
+
|
|
103
|
+
## All Scripts (package.json)
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
yarn dev # nest start --watch (hot-reload)
|
|
107
|
+
yarn build # nest build
|
|
108
|
+
yarn start # nest start
|
|
109
|
+
yarn start:prod # node dist/main
|
|
110
|
+
yarn start:debug # nest start --debug --watch
|
|
111
|
+
yarn lint # eslint --fix
|
|
112
|
+
yarn format # prettier --write
|
|
113
|
+
yarn test # jest
|
|
114
|
+
yarn test:watch # jest --watch
|
|
115
|
+
yarn test:cov # jest --coverage
|
|
116
|
+
yarn test:e2e # jest --config ./test/jest-e2e.json
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Configuration Files
|
|
120
|
+
|
|
121
|
+
### nest-cli.json
|
|
122
|
+
- Builder: TypeScript compiler
|
|
123
|
+
- Assets: `mail/templates` copied to dist
|
|
124
|
+
- Source root: `src/`
|
|
125
|
+
|
|
126
|
+
### tsconfig.json
|
|
127
|
+
- Target: ES2022
|
|
128
|
+
- Module: CommonJS
|
|
129
|
+
- Path alias: `@prisma/*` → `src/generated/prisma/*`
|
|
130
|
+
- Strict mode: Relaxed (noImplicitAny: false, strictNullChecks: false)
|
|
131
|
+
|
|
132
|
+
### .prettierrc
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"singleQuote": true,
|
|
136
|
+
"trailingComma": "all"
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### .eslintrc.js
|
|
141
|
+
- Parser: @typescript-eslint
|
|
142
|
+
- Relaxed rules for rapid development
|
|
143
|
+
|
|
144
|
+
## API Documentation
|
|
145
|
+
|
|
146
|
+
Swagger UI available at: `http://localhost:3000/api`
|
|
147
|
+
|
|
148
|
+
## API Versioning
|
|
149
|
+
|
|
150
|
+
URI-based versioning:
|
|
151
|
+
- v1: `http://localhost:3000/api/v1/...`
|
|
152
|
+
- v2: `http://localhost:3000/api/v2/...`
|
|
153
|
+
|
|
154
|
+
Default version: v1 and v2 (both active)
|
|
155
|
+
|
|
156
|
+
## CORS Configuration
|
|
157
|
+
|
|
158
|
+
All origins allowed (development mode):
|
|
159
|
+
```typescript
|
|
160
|
+
app.enableCors({
|
|
161
|
+
origin: true,
|
|
162
|
+
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
|
|
163
|
+
credentials: true,
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Rate Limiting
|
|
168
|
+
|
|
169
|
+
Global throttle: 10 requests per 60 seconds per IP.
|