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,180 @@
1
+ # Active Life Backend - Order System
2
+
3
+ ## Order Lifecycle
4
+
5
+ ```
6
+ PENDING → PROCESSING → SHIPPED → DELIVERED
7
+ │ │ │
8
+ ▼ ▼ ▼
9
+ CANCELLED CANCELLED RETURN_PENDING → RETURN_RECEIVED → DELIVERED (restocked)
10
+
11
+
12
+ RETURN_REJECTED
13
+
14
+ REFUND_PENDING → REFUNDED
15
+ ```
16
+
17
+ ## Order Statuses (OrderStatus enum)
18
+
19
+ | Status | Description | Triggered By |
20
+ |--------|------------|-------------|
21
+ | `PENDING` | New order, awaiting processing | Customer/Staff creates order |
22
+ | `PROCESSING` | Staff processing, selecting inventory lots | Staff processes order |
23
+ | `SHIPPED` | Order shipped to customer | Staff marks as shipped |
24
+ | `DELIVERED` | Customer received order | Staff confirms delivery |
25
+ | `CANCELLED` | Order cancelled | Customer or staff cancels |
26
+ | `RETURN_PENDING` | Return requested | Customer requests return |
27
+ | `RETURN_RECEIVED` | Returned items received | Staff confirms receipt |
28
+ | `RETURN_REJECTED` | Return request rejected | Staff rejects return |
29
+ | `REFUND_PENDING` | Refund in progress | Staff initiates refund |
30
+ | `REFUNDED` | Refund completed | Staff completes refund |
31
+
32
+ ## Order Model
33
+
34
+ ```prisma
35
+ model Order {
36
+ id String @id @default(uuid())
37
+ orderCode String @unique // Human-readable code
38
+ clientId String
39
+ status OrderStatus @default(PENDING)
40
+ paymentMethod PaymentMethod // CASH or TRANSFER
41
+ totalAmount Float // Sum of items
42
+ discountAmount Float @default(0) // Voucher discount
43
+ finalAmount Float // totalAmount - discountAmount + shippingFee
44
+ shippingAddress String?
45
+ shippingFee Float @default(0)
46
+ note String?
47
+ voucherId String?
48
+ staffId String? // Staff who processed
49
+ }
50
+ ```
51
+
52
+ ## Order Flow
53
+
54
+ ### 1. Customer Creates Order
55
+ ```
56
+ POST /api/v1/order (with jwt-client auth)
57
+ Body: CreateOrderDto {
58
+ items: [{ productId, comboId, quantity }],
59
+ paymentMethod: "CASH" | "TRANSFER",
60
+ shippingAddress: string,
61
+ voucherCode?: string,
62
+ note?: string
63
+ }
64
+ ```
65
+ - Validates product/combo availability
66
+ - Applies voucher if provided
67
+ - Calculates totals
68
+ - Creates Order + OrderItems
69
+ - Creates LogOrder (CREATE)
70
+
71
+ ### 2. Staff Creates Order (on behalf)
72
+ ```
73
+ POST /api/v1/order/staff-create (staff auth)
74
+ Body: StaffCreateOrderDto {
75
+ clientId: string,
76
+ items: [...],
77
+ paymentMethod: ...,
78
+ ...
79
+ }
80
+ ```
81
+
82
+ ### 3. Process Order (assign inventory)
83
+ ```
84
+ PATCH /api/v1/order/:id/process (staff auth)
85
+ Body: ProcessOrderDto {
86
+ items: [{
87
+ orderItemId: string,
88
+ lots: [{
89
+ inventoryItemId: string, // ProductInventoryItem
90
+ quantity: number
91
+ }]
92
+ }]
93
+ }
94
+ ```
95
+ - Staff selects specific inventory lots (FIFO by expiry)
96
+ - Deducts inventory quantities
97
+ - Creates InventoryTransactions (EXPORT type)
98
+ - Status: PENDING → PROCESSING
99
+
100
+ ### 4. Ship Order
101
+ ```
102
+ PATCH /api/v1/order/:id/ship (staff auth)
103
+ ```
104
+ - Status: PROCESSING → SHIPPED
105
+ - Creates LogOrder (SHIP)
106
+
107
+ ### 5. Deliver Order
108
+ ```
109
+ PATCH /api/v1/order/:id/deliver (staff auth)
110
+ ```
111
+ - Status: SHIPPED → DELIVERED
112
+ - Creates LogOrder (DELIVER)
113
+ - Awards loyalty points to client
114
+
115
+ ### 6. Cancel Order
116
+ ```
117
+ PATCH /api/v1/order/:id/cancel (staff or client auth)
118
+ Body: CancelOrderDto { reason: string }
119
+ ```
120
+ - Status: PENDING/PROCESSING → CANCELLED
121
+ - If PROCESSING: reverses inventory deductions
122
+ - Creates LogOrder (CANCEL_CLIENT or CANCEL_STAFF)
123
+
124
+ ### 7. Return Request
125
+ ```
126
+ POST /api/v1/order/:id/return-request (client or staff)
127
+ Body: CreateReturnRequestDto { reason, items[] }
128
+ ```
129
+ - Status: DELIVERED → RETURN_PENDING
130
+ - Creates LogOrder (RETURN_CREATE)
131
+
132
+ ### 8. Complete Return
133
+ ```
134
+ PATCH /api/v1/order/:id/return-complete (staff)
135
+ Body: CompleteReturnRequestDto { action: "receive" | "reject" }
136
+ ```
137
+ - If receive: RETURN_PENDING → RETURN_RECEIVED, restock inventory
138
+ - If reject: RETURN_PENDING → RETURN_REJECTED
139
+
140
+ ### 9. Refund Request
141
+ ```
142
+ POST /api/v1/order/:id/refund-request (staff)
143
+ Body: CreateRefundRequestDto { amount, reason }
144
+ ```
145
+ - Status: → REFUND_PENDING
146
+
147
+ ### 10. Complete Refund
148
+ ```
149
+ PATCH /api/v1/order/:id/refund-complete (staff)
150
+ ```
151
+ - Status: REFUND_PENDING → REFUNDED
152
+ - Creates LogOrder (REFUND_COMPLETE)
153
+
154
+ ## Audit Trail (LogOrder)
155
+
156
+ Every status change creates a log entry:
157
+ ```prisma
158
+ model LogOrder {
159
+ id String @id @default(uuid())
160
+ orderId String
161
+ type LogOrderType // CREATE, PROCESS, SHIP, DELIVER, CANCEL_*, RETURN_*, REFUND_*
162
+ note String?
163
+ userId String? // Staff who made the change
164
+ createdAt DateTime @default(now())
165
+ }
166
+ ```
167
+
168
+ ## Payment Methods
169
+ - `CASH` — Cash on delivery
170
+ - `TRANSFER` — Bank transfer
171
+
172
+ ## Key Business Rules
173
+ 1. Only PENDING orders can be processed
174
+ 2. Only PROCESSING orders can be shipped
175
+ 3. Only SHIPPED orders can be delivered
176
+ 4. Only PENDING/PROCESSING orders can be cancelled
177
+ 5. Only DELIVERED orders can have return requests
178
+ 6. Cancellation of PROCESSING orders must reverse inventory
179
+ 7. Points are awarded only on DELIVERED status
180
+ 8. Order code is auto-generated and unique
@@ -0,0 +1,319 @@
1
+ # Active Life Backend - Coding Patterns & Conventions
2
+
3
+ ## Project Structure Convention
4
+
5
+ Every feature module follows this structure:
6
+ ```
7
+ src/<module-name>/
8
+ ├── <module-name>.module.ts
9
+ ├── <module-name>.controller.ts
10
+ ├── <module-name>.service.ts
11
+ └── dto/
12
+ ├── create-<module-name>.dto.ts
13
+ └── update-<module-name>.dto.ts
14
+ ```
15
+
16
+ ## Module Pattern
17
+
18
+ ```typescript
19
+ import { Module } from '@nestjs/common';
20
+ import { XxxController } from './xxx.controller';
21
+ import { XxxService } from './xxx.service';
22
+
23
+ @Module({
24
+ controllers: [XxxController],
25
+ providers: [XxxService],
26
+ exports: [XxxService], // Only if other modules need this service
27
+ })
28
+ export class XxxModule {}
29
+ ```
30
+
31
+ ## Controller Pattern
32
+
33
+ ```typescript
34
+ import { Controller, Get, Post, Body, Param, Query, Patch, Delete } from '@nestjs/common';
35
+ import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
36
+ import { Public } from 'src/decorators/customize';
37
+ import { ResponseMessage, User } from 'src/decorators/customize';
38
+ import { IUser } from 'src/interface/users.interface';
39
+
40
+ @Controller('xxx')
41
+ @ApiTags('xxx')
42
+ export class XxxController {
43
+ constructor(private readonly xxxService: XxxService) {}
44
+
45
+ @Post()
46
+ @ApiBearerAuth()
47
+ @ApiOperation({ summary: 'Create xxx' })
48
+ @ResponseMessage('Created successfully')
49
+ create(@Body() dto: CreateXxxDto, @User() user: IUser) {
50
+ return this.xxxService.create(dto, user);
51
+ }
52
+
53
+ @Get()
54
+ @Public()
55
+ @ApiOperation({ summary: 'Get all xxx' })
56
+ findAll(@Query() query: any) {
57
+ return this.xxxService.findAll(query);
58
+ }
59
+
60
+ @Get(':id')
61
+ @Public()
62
+ findOne(@Param('id') id: string) {
63
+ return this.xxxService.findOne(id);
64
+ }
65
+
66
+ @Patch(':id')
67
+ @ApiBearerAuth()
68
+ update(@Param('id') id: string, @Body() dto: UpdateXxxDto) {
69
+ return this.xxxService.update(id, dto);
70
+ }
71
+
72
+ @Delete(':id')
73
+ @ApiBearerAuth()
74
+ remove(@Param('id') id: string) {
75
+ return this.xxxService.remove(id);
76
+ }
77
+ }
78
+ ```
79
+
80
+ ## Service Pattern
81
+
82
+ ```typescript
83
+ import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common';
84
+ import { PrismaService } from 'src/prisma/prisma.service';
85
+
86
+ @Injectable()
87
+ export class XxxService {
88
+ constructor(private prismaService: PrismaService) {}
89
+
90
+ async create(dto: CreateXxxDto, user?: IUser) {
91
+ // Check for duplicates if needed
92
+ const existing = await this.prismaService.xxx.findUnique({
93
+ where: { name: dto.name },
94
+ });
95
+ if (existing) {
96
+ throw new BadRequestException('Already exists');
97
+ }
98
+
99
+ return this.prismaService.xxx.create({
100
+ data: {
101
+ ...dto,
102
+ slug: this.generateSlug(dto.name), // If applicable
103
+ },
104
+ });
105
+ }
106
+
107
+ async findAll(query: any) {
108
+ const { page = 1, limit = 10, search } = query;
109
+ const skip = (page - 1) * limit;
110
+
111
+ const where = search
112
+ ? { name: { contains: search, mode: 'insensitive' as const } }
113
+ : {};
114
+
115
+ const [data, total] = await Promise.all([
116
+ this.prismaService.xxx.findMany({
117
+ where,
118
+ skip,
119
+ take: +limit,
120
+ orderBy: { createdAt: 'desc' },
121
+ }),
122
+ this.prismaService.xxx.count({ where }),
123
+ ]);
124
+
125
+ return {
126
+ data,
127
+ meta: {
128
+ total,
129
+ page: +page,
130
+ limit: +limit,
131
+ totalPages: Math.ceil(total / +limit),
132
+ },
133
+ };
134
+ }
135
+
136
+ async findOne(id: string) {
137
+ const item = await this.prismaService.xxx.findUnique({
138
+ where: { id },
139
+ include: { /* relations */ },
140
+ });
141
+ if (!item) {
142
+ throw new NotFoundException('Not found');
143
+ }
144
+ return item;
145
+ }
146
+
147
+ async update(id: string, dto: UpdateXxxDto) {
148
+ await this.findOne(id); // Ensure exists
149
+ return this.prismaService.xxx.update({
150
+ where: { id },
151
+ data: dto,
152
+ });
153
+ }
154
+
155
+ async remove(id: string) {
156
+ await this.findOne(id); // Ensure exists
157
+ return this.prismaService.xxx.delete({ where: { id } });
158
+ }
159
+ }
160
+ ```
161
+
162
+ ## DTO Pattern
163
+
164
+ ```typescript
165
+ import { ApiProperty } from '@nestjs/swagger';
166
+ import { IsString, IsNotEmpty, IsOptional, IsNumber, IsEnum, IsArray } from 'class-validator';
167
+ import { Type } from 'class-transformer';
168
+
169
+ export class CreateXxxDto {
170
+ @ApiProperty({ example: 'Example name' })
171
+ @IsString()
172
+ @IsNotEmpty()
173
+ name: string;
174
+
175
+ @ApiProperty({ required: false })
176
+ @IsOptional()
177
+ @IsString()
178
+ description?: string;
179
+
180
+ @ApiProperty({ example: 100 })
181
+ @IsNumber()
182
+ @Type(() => Number) // For query params auto-conversion
183
+ price: number;
184
+
185
+ @ApiProperty({ enum: ProductStatus })
186
+ @IsEnum(ProductStatus)
187
+ status: ProductStatus;
188
+
189
+ @ApiProperty({ type: [String] })
190
+ @IsArray()
191
+ @IsString({ each: true })
192
+ images: string[];
193
+ }
194
+ ```
195
+
196
+ **Update DTO** — typically partial:
197
+ ```typescript
198
+ import { PartialType } from '@nestjs/mapped-types';
199
+ import { CreateXxxDto } from './create-xxx.dto';
200
+
201
+ export class UpdateXxxDto extends PartialType(CreateXxxDto) {}
202
+ ```
203
+
204
+ ## Response Format
205
+
206
+ All responses are wrapped by `TransformInterceptor`:
207
+ ```typescript
208
+ // Success response
209
+ {
210
+ "statusCode": 200,
211
+ "message": "Success", // From @ResponseMessage() or default
212
+ "data": { ... } // Actual data
213
+ }
214
+
215
+ // Error response (from exceptions)
216
+ {
217
+ "statusCode": 400,
218
+ "message": "Bad Request",
219
+ "error": "Detailed error message"
220
+ }
221
+ ```
222
+
223
+ ## Slug Generation
224
+
225
+ Vietnamese-aware slug generation (remove diacritics):
226
+ ```typescript
227
+ private generateSlug(name: string): string {
228
+ return name
229
+ .normalize('NFD')
230
+ .replace(/[\u0300-\u036f]/g, '') // Remove diacritics
231
+ .replace(/đ/g, 'd').replace(/Đ/g, 'D')
232
+ .toLowerCase()
233
+ .replace(/[^a-z0-9\s-]/g, '')
234
+ .replace(/\s+/g, '-')
235
+ .replace(/-+/g, '-')
236
+ .trim();
237
+ }
238
+ ```
239
+
240
+ ## Pagination Pattern
241
+
242
+ Services return paginated data with meta:
243
+ ```typescript
244
+ {
245
+ data: [...items],
246
+ meta: {
247
+ total: 100,
248
+ page: 1,
249
+ limit: 10,
250
+ totalPages: 10
251
+ }
252
+ }
253
+ ```
254
+
255
+ ## Error Handling
256
+
257
+ Use NestJS built-in exceptions:
258
+ ```typescript
259
+ throw new BadRequestException('Validation error message');
260
+ throw new NotFoundException('Resource not found');
261
+ throw new ForbiddenException('Access denied');
262
+ throw new UnauthorizedException('Invalid credentials');
263
+ ```
264
+
265
+ ## Naming Conventions
266
+
267
+ - **Files**: kebab-case (`create-product.dto.ts`)
268
+ - **Classes**: PascalCase (`CreateProductDto`)
269
+ - **Methods**: camelCase (`findAll`, `createOrder`)
270
+ - **Database tables**: PascalCase in Prisma schema (auto-mapped to snake_case)
271
+ - **API routes**: kebab-case (`/api/v1/inventory-product`)
272
+ - **Env vars**: SCREAMING_SNAKE_CASE (`JWT_CLIENT_ACCESS_TOKEN_SECRET`)
273
+
274
+ ## Import Conventions
275
+
276
+ ```typescript
277
+ // NestJS core
278
+ import { Injectable, Controller, Module } from '@nestjs/common';
279
+ // Swagger
280
+ import { ApiTags, ApiBearerAuth, ApiProperty } from '@nestjs/swagger';
281
+ // Validation
282
+ import { IsString, IsNotEmpty } from 'class-validator';
283
+ // Project internals (absolute paths from src/)
284
+ import { PrismaService } from 'src/prisma/prisma.service';
285
+ import { Public, User, ResponseMessage } from 'src/decorators/customize';
286
+ import { IUser } from 'src/interface/users.interface';
287
+ ```
288
+
289
+ ## Logging
290
+
291
+ The `LoggingInterceptor` automatically logs:
292
+ - HTTP method, URL, IP
293
+ - User info (if authenticated)
294
+ - Response status code
295
+ - Execution time (ms)
296
+ - Error details on failure
297
+
298
+ No need to add manual logging in services unless debugging specific flows.
299
+
300
+ ## Global Configuration (main.ts)
301
+
302
+ ```typescript
303
+ // Applied globally:
304
+ app.useGlobalGuards(new JwtAuthGuard(...)); // Auth on all routes
305
+ app.useGlobalPipes(new ValidationPipe({
306
+ whitelist: true, // Strip unknown properties
307
+ transform: true, // Auto-transform types
308
+ transformOptions: { enableImplicitConversion: true },
309
+ }));
310
+ app.useGlobalInterceptors(
311
+ new LoggingInterceptor(),
312
+ new TransformInterceptor(reflector),
313
+ );
314
+
315
+ // CORS: all origins allowed
316
+ // Rate limit: 10 requests / 60 seconds
317
+ // API versioning: URI-based (/api/v1/, /api/v2/)
318
+ // Swagger at /api
319
+ ```
@@ -0,0 +1,216 @@
1
+ # Active Life Backend - Products & Catalog
2
+
3
+ ## Product Architecture
4
+
5
+ ```
6
+ Category (hierarchical tree)
7
+ └──< CategoryBrand >── Brand
8
+ └──< ProductCategory >── StoreProduct
9
+ └──< StoreProductCombo (pricing/bundle)
10
+ └──< StoreProductComboItem ──> InventoryProduct
11
+ └──< StoreProductComboItem (gifts) ──> InventoryProduct
12
+ ```
13
+
14
+ ### Key Concept: Product vs Combo vs Inventory Product
15
+
16
+ - **StoreProduct** — What customers see (name, images, description, status)
17
+ - **StoreProductCombo** — A purchasable configuration with price (e.g., "Pack of 3", "Family Bundle")
18
+ - **StoreProductComboItem** — Links combo to warehouse inventory products with quantities
19
+ - **InventoryProduct** — Actual warehouse item (SKU, barcode, unit, brand)
20
+
21
+ **Example**:
22
+ ```
23
+ StoreProduct: "Whey Protein Gold Standard"
24
+ ├── Combo: "1kg Bag" — price: 500,000 VND
25
+ │ └── Item: InventoryProduct("WP-GS-1KG") × 1
26
+ ├── Combo: "2kg Bag + Shaker" — price: 900,000 VND
27
+ │ ├── Item: InventoryProduct("WP-GS-2KG") × 1
28
+ │ └── Gift: InventoryProduct("SHAKER-01") × 1
29
+ └── Combo: "5kg Bulk" — price: 2,000,000 VND
30
+ └── Item: InventoryProduct("WP-GS-1KG") × 5
31
+ ```
32
+
33
+ ## Categories
34
+
35
+ ### Hierarchical Structure (Parent-Child)
36
+ ```prisma
37
+ model Category {
38
+ id String @id @default(uuid())
39
+ name String
40
+ slug String @unique
41
+ parentId String?
42
+ parent Category? @relation("CategoryTree", fields: [parentId], references: [id])
43
+ children Category[] @relation("CategoryTree")
44
+ }
45
+ ```
46
+
47
+ **Example tree**:
48
+ ```
49
+ Supplements
50
+ ├── Protein
51
+ │ ├── Whey Protein
52
+ │ └── Casein Protein
53
+ ├── Pre-Workout
54
+ └── Vitamins
55
+ Equipment
56
+ ├── Weights
57
+ └── Accessories
58
+ ```
59
+
60
+ ### Recursive Category Queries
61
+ The service uses raw SQL for recursive queries to get full category trees:
62
+ ```sql
63
+ WITH RECURSIVE category_tree AS (
64
+ SELECT * FROM "Category" WHERE "parentId" IS NULL
65
+ UNION ALL
66
+ SELECT c.* FROM "Category" c
67
+ JOIN category_tree ct ON c."parentId" = ct.id
68
+ )
69
+ SELECT * FROM category_tree;
70
+ ```
71
+
72
+ ### Category-Brand Relationship
73
+ Many-to-many via `CategoryBrand` join table. A category can have multiple brands, and a brand can appear in multiple categories.
74
+
75
+ ## Products
76
+
77
+ ### StoreProduct Model
78
+ ```prisma
79
+ model StoreProduct {
80
+ id String @id @default(uuid())
81
+ name String
82
+ slug String @unique
83
+ description String?
84
+ images String[] // Array of image URLs
85
+ status ProductStatus @default(NORMAL) // HIDDEN, NORMAL, HIGH
86
+ }
87
+ ```
88
+
89
+ ### Product Status
90
+ - `HIDDEN` — Not visible to customers
91
+ - `NORMAL` — Standard visibility
92
+ - `HIGH` — Featured/promoted product
93
+
94
+ ### Creating a Product
95
+ ```
96
+ POST /api/v1/product
97
+ Body: {
98
+ name: "Whey Protein Gold Standard",
99
+ description: "Premium whey protein...",
100
+ images: ["url1", "url2"],
101
+ categoryIds: ["cat-uuid-1", "cat-uuid-2"],
102
+ status: "NORMAL"
103
+ }
104
+ ```
105
+ Auto-generates slug from name (Vietnamese diacritics removed).
106
+
107
+ ## Combos (Product Pricing)
108
+
109
+ ### StoreProductCombo Model
110
+ ```prisma
111
+ model StoreProductCombo {
112
+ id String @id @default(uuid())
113
+ name String
114
+ price Float
115
+ comparePrice Float? // "Was" price for showing discounts
116
+ productId String
117
+ items StoreProductComboItem[] // Regular items
118
+ giftItems StoreProductComboItem[] // Gift items
119
+ }
120
+ ```
121
+
122
+ ### Creating a Combo
123
+ ```
124
+ POST /api/v1/product/:id/combo
125
+ Body: {
126
+ name: "1kg Bag",
127
+ price: 500000,
128
+ comparePrice: 600000,
129
+ items: [
130
+ { inventoryProductId: "inv-uuid", quantity: 1 }
131
+ ]
132
+ }
133
+ ```
134
+
135
+ ### Adding Gift Items
136
+ ```
137
+ POST /api/v1/product/:id/combo/:comboId/gift
138
+ Body: {
139
+ items: [
140
+ { inventoryProductId: "shaker-uuid", quantity: 1 }
141
+ ]
142
+ }
143
+ ```
144
+
145
+ ### Promotion History
146
+ When combo price changes, a history record is created:
147
+ ```prisma
148
+ model StoreProductComboPromotionHistory {
149
+ comboId String
150
+ oldPrice Float
151
+ newPrice Float
152
+ reason String?
153
+ createdAt DateTime @default(now())
154
+ }
155
+ ```
156
+
157
+ ## Brands
158
+
159
+ ```prisma
160
+ model Brand {
161
+ id String @id @default(uuid())
162
+ name String
163
+ slug String @unique
164
+ image String?
165
+ description String?
166
+ }
167
+ ```
168
+
169
+ ## Key API Endpoints
170
+
171
+ ### Products
172
+ ```
173
+ GET /api/v1/product — List products (public, paginated)
174
+ GET /api/v1/product/:id — Get product detail (public)
175
+ GET /api/v1/product/slug/:slug — Get by slug (public)
176
+ POST /api/v1/product — Create product (staff)
177
+ PATCH /api/v1/product/:id — Update product (staff)
178
+ DELETE /api/v1/product/:id — Delete product (staff)
179
+ ```
180
+
181
+ ### Combos
182
+ ```
183
+ POST /api/v1/product/:id/combo — Add combo to product
184
+ PATCH /api/v1/product/:id/combo/:comboId — Update combo
185
+ DELETE /api/v1/product/:id/combo/:comboId — Delete combo
186
+ POST /api/v1/product/:id/combo/:comboId/gift — Add gift items
187
+ ```
188
+
189
+ ### Categories
190
+ ```
191
+ GET /api/v1/category — List categories (tree structure, public)
192
+ GET /api/v1/category/:id — Get category with products (public)
193
+ POST /api/v1/category — Create category (staff)
194
+ PATCH /api/v1/category/:id — Update category (staff)
195
+ DELETE /api/v1/category/:id — Delete category (staff)
196
+ ```
197
+
198
+ ### Brands
199
+ ```
200
+ GET /api/v1/brand — List brands (public)
201
+ POST /api/v1/brand — Create brand (staff)
202
+ PATCH /api/v1/brand/:id — Update brand (staff)
203
+ DELETE /api/v1/brand/:id — Delete brand (staff)
204
+ ```
205
+
206
+ ## Shopping Cart Integration
207
+
208
+ Cart items reference both product and combo:
209
+ ```prisma
210
+ model CartItem {
211
+ productId String
212
+ comboId String
213
+ quantity Int
214
+ }
215
+ ```
216
+ Customers add combos to cart, not raw products.