@panuwatbas/spec-driven-dev 1.0.2
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/README.md +378 -0
- package/bin/cli.js +289 -0
- package/package.json +39 -0
- package/skill/README.md +346 -0
- package/skill/SKILL.md +195 -0
- package/skill/examples/example-design.md +287 -0
- package/skill/examples/example-requirements.md +151 -0
- package/skill/examples/example-tasks.md +189 -0
- package/skill/scripts/init-spec.bat +70 -0
- package/skill/scripts/init-spec.sh +159 -0
- package/skill/templates/design.template.md +248 -0
- package/skill/templates/requirements.template.md +116 -0
- package/skill/templates/tasks.template.md +127 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# Technical Design: Shopping Cart Management
|
|
2
|
+
|
|
3
|
+
> **Status:** 🟢 Approved
|
|
4
|
+
>
|
|
5
|
+
> **Requirements:** [requirements.md](./example-requirements.md)
|
|
6
|
+
>
|
|
7
|
+
> **Author:** AI Agent
|
|
8
|
+
>
|
|
9
|
+
> **Created:** 2025-07-24
|
|
10
|
+
>
|
|
11
|
+
> **Last Updated:** 2025-07-24
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Overview
|
|
16
|
+
|
|
17
|
+
Implement a client-side cart with server-side sync for authenticated users.
|
|
18
|
+
Guest users' carts are stored in `localStorage`; logged-in users' carts are
|
|
19
|
+
persisted in PostgreSQL and synced via a REST API. The cart UI is a React component
|
|
20
|
+
with optimistic updates for a snappy user experience.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 2. Architecture
|
|
25
|
+
|
|
26
|
+
### 2.1 System Diagram
|
|
27
|
+
|
|
28
|
+
```mermaid
|
|
29
|
+
graph TD
|
|
30
|
+
A[React Frontend] --> B[Cart Context / Zustand Store]
|
|
31
|
+
B --> C{User Logged In?}
|
|
32
|
+
C -->|Yes| D[Cart API - /api/cart]
|
|
33
|
+
C -->|No| E[localStorage]
|
|
34
|
+
D --> F[Cart Service]
|
|
35
|
+
F --> G[(PostgreSQL - cart_items)]
|
|
36
|
+
F --> H[Inventory Service]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 2.2 Architectural Pattern
|
|
40
|
+
|
|
41
|
+
**Client-State + Server-Sync Hybrid**
|
|
42
|
+
|
|
43
|
+
- Guest users: purely client-side (localStorage + Zustand)
|
|
44
|
+
- Logged-in users: optimistic client updates with background server sync
|
|
45
|
+
- On login: merge localStorage cart into server cart, then clear localStorage
|
|
46
|
+
|
|
47
|
+
### 2.3 Tech Stack
|
|
48
|
+
|
|
49
|
+
| Layer | Technology | Justification |
|
|
50
|
+
|-------|-----------|---------------|
|
|
51
|
+
| Frontend | React 18 + TypeScript | Existing project stack |
|
|
52
|
+
| State | Zustand | Lightweight, no boilerplate |
|
|
53
|
+
| Backend | Node.js + Express | Existing API server |
|
|
54
|
+
| Database | PostgreSQL | Existing database, relational model fits cart data |
|
|
55
|
+
| Cache | Redis | Session-level cart caching for performance |
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 3. Component Design
|
|
60
|
+
|
|
61
|
+
### 3.1 CartProvider (Frontend)
|
|
62
|
+
|
|
63
|
+
- **Responsibility:** Global cart state management and persistence logic
|
|
64
|
+
- **Location:** `src/contexts/CartContext.tsx`
|
|
65
|
+
- **Public Interface:**
|
|
66
|
+
```typescript
|
|
67
|
+
interface CartContextValue {
|
|
68
|
+
items: CartItem[];
|
|
69
|
+
totalItems: number;
|
|
70
|
+
totalPrice: number;
|
|
71
|
+
addItem: (product: Product, quantity?: number) => void;
|
|
72
|
+
updateQuantity: (productId: string, quantity: number) => void;
|
|
73
|
+
removeItem: (productId: string) => void;
|
|
74
|
+
clearCart: () => void;
|
|
75
|
+
isLoading: boolean;
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
- **State Management:** Zustand store hydrated from localStorage (guest) or API (authenticated)
|
|
79
|
+
|
|
80
|
+
### 3.2 CartAPI (Backend)
|
|
81
|
+
|
|
82
|
+
- **Responsibility:** CRUD operations for server-side cart persistence
|
|
83
|
+
- **Location:** `src/server/routes/cart.ts`
|
|
84
|
+
- **Public Interface:**
|
|
85
|
+
```typescript
|
|
86
|
+
// Express routes
|
|
87
|
+
GET /api/cart → Get user's cart
|
|
88
|
+
POST /api/cart/items → Add item to cart
|
|
89
|
+
PATCH /api/cart/items/:id → Update item quantity
|
|
90
|
+
DELETE /api/cart/items/:id → Remove item from cart
|
|
91
|
+
DELETE /api/cart → Clear entire cart
|
|
92
|
+
POST /api/cart/merge → Merge guest cart on login
|
|
93
|
+
```
|
|
94
|
+
- **Error Handling:** Returns structured JSON errors with field-level details
|
|
95
|
+
|
|
96
|
+
### 3.3 CartService (Backend)
|
|
97
|
+
|
|
98
|
+
- **Responsibility:** Business logic — stock validation, quantity capping, price calculation
|
|
99
|
+
- **Location:** `src/server/services/CartService.ts`
|
|
100
|
+
- **Dependencies:** InventoryService, ProductRepository
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 4. Data Models
|
|
105
|
+
|
|
106
|
+
### 4.1 CartItem (Frontend)
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
interface CartItem {
|
|
110
|
+
productId: string;
|
|
111
|
+
name: string;
|
|
112
|
+
price: number; // Unit price at time of adding
|
|
113
|
+
quantity: number;
|
|
114
|
+
maxQuantity: number; // Stock limit
|
|
115
|
+
imageUrl: string;
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 4.2 Database Schema
|
|
120
|
+
|
|
121
|
+
```sql
|
|
122
|
+
CREATE TABLE cart_items (
|
|
123
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
124
|
+
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
125
|
+
product_id UUID NOT NULL REFERENCES products(id),
|
|
126
|
+
quantity INTEGER NOT NULL CHECK (quantity > 0),
|
|
127
|
+
added_at TIMESTAMP DEFAULT NOW(),
|
|
128
|
+
updated_at TIMESTAMP DEFAULT NOW(),
|
|
129
|
+
UNIQUE(user_id, product_id)
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
CREATE INDEX idx_cart_items_user ON cart_items(user_id);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 5. API Contracts
|
|
138
|
+
|
|
139
|
+
### 5.1 Add Item to Cart
|
|
140
|
+
|
|
141
|
+
| Property | Value |
|
|
142
|
+
|----------|-------|
|
|
143
|
+
| Method | `POST` |
|
|
144
|
+
| Path | `/api/cart/items` |
|
|
145
|
+
| Auth | Required (Bearer token) |
|
|
146
|
+
|
|
147
|
+
**Request:**
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"productId": "uuid",
|
|
151
|
+
"quantity": 1
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Response (201):**
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"id": "uuid",
|
|
159
|
+
"productId": "uuid",
|
|
160
|
+
"name": "Blue T-Shirt",
|
|
161
|
+
"quantity": 1,
|
|
162
|
+
"price": 29.99,
|
|
163
|
+
"maxQuantity": 10
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Errors:**
|
|
168
|
+
|
|
169
|
+
| Status | Scenario |
|
|
170
|
+
|--------|----------|
|
|
171
|
+
| 400 | Invalid productId or quantity ≤ 0 |
|
|
172
|
+
| 404 | Product not found |
|
|
173
|
+
| 409 | Quantity exceeds stock (returns `maxQuantity`) |
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 6. Sequence Diagrams
|
|
178
|
+
|
|
179
|
+
### 6.1 Add to Cart (Authenticated User)
|
|
180
|
+
|
|
181
|
+
```mermaid
|
|
182
|
+
sequenceDiagram
|
|
183
|
+
actor User
|
|
184
|
+
participant UI as CartButton
|
|
185
|
+
participant Store as Zustand Store
|
|
186
|
+
participant API as Cart API
|
|
187
|
+
participant Svc as CartService
|
|
188
|
+
participant Inv as InventoryService
|
|
189
|
+
participant DB as PostgreSQL
|
|
190
|
+
|
|
191
|
+
User->>UI: Click "Add to Cart"
|
|
192
|
+
UI->>Store: addItem(product, 1)
|
|
193
|
+
Store->>Store: Optimistic update (add item)
|
|
194
|
+
Store->>API: POST /api/cart/items
|
|
195
|
+
API->>Svc: addToCart(userId, productId, qty)
|
|
196
|
+
Svc->>Inv: checkStock(productId)
|
|
197
|
+
Inv-->>Svc: stock = 10
|
|
198
|
+
Svc->>DB: INSERT INTO cart_items
|
|
199
|
+
DB-->>Svc: OK
|
|
200
|
+
Svc-->>API: CartItem
|
|
201
|
+
API-->>Store: 201 Created
|
|
202
|
+
Store->>UI: Re-render with new item
|
|
203
|
+
UI-->>User: Toast "Item added to cart"
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### 6.2 Cart Merge on Login
|
|
207
|
+
|
|
208
|
+
```mermaid
|
|
209
|
+
sequenceDiagram
|
|
210
|
+
actor User
|
|
211
|
+
participant UI as LoginForm
|
|
212
|
+
participant Auth as Auth Service
|
|
213
|
+
participant Store as Zustand Store
|
|
214
|
+
participant API as Cart API
|
|
215
|
+
|
|
216
|
+
User->>UI: Submit credentials
|
|
217
|
+
UI->>Auth: POST /api/auth/login
|
|
218
|
+
Auth-->>UI: Token + User
|
|
219
|
+
UI->>Store: Get localStorage cart items
|
|
220
|
+
Store->>API: POST /api/cart/merge {localItems}
|
|
221
|
+
API->>API: Merge logic (server wins on conflict)
|
|
222
|
+
API-->>Store: Merged cart
|
|
223
|
+
Store->>Store: Clear localStorage, set server cart
|
|
224
|
+
Store->>UI: Re-render
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## 7. Error Handling Strategy
|
|
230
|
+
|
|
231
|
+
| Error Type | Handling Approach |
|
|
232
|
+
|-----------|-------------------|
|
|
233
|
+
| Stock exceeded | Cap quantity, show warning with max available |
|
|
234
|
+
| Product removed | Remove from cart, show info notification |
|
|
235
|
+
| Network failure | Keep optimistic state, retry with exponential backoff (max 3) |
|
|
236
|
+
| Auth expired | Queue cart operations, redirect to login, replay after auth |
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## 8. Testing Strategy
|
|
241
|
+
|
|
242
|
+
| Level | Tool | What to Test |
|
|
243
|
+
|-------|------|-------------|
|
|
244
|
+
| Unit | Vitest | CartService logic, Zustand store actions |
|
|
245
|
+
| Integration | Supertest | API endpoints with test database |
|
|
246
|
+
| E2E | Playwright | Full add-to-cart flow, quantity updates, persistence |
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## 9. Security Considerations
|
|
251
|
+
|
|
252
|
+
- [x] Cart API requires authentication (except merge endpoint during login flow)
|
|
253
|
+
- [x] User can only access their own cart (enforced by user_id from JWT)
|
|
254
|
+
- [x] Quantity validated server-side against inventory
|
|
255
|
+
- [x] Price always fetched from server at checkout (not from client state)
|
|
256
|
+
- [x] Rate limiting on cart endpoints (60 req/min per user)
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 10. Constraints & Rules
|
|
261
|
+
|
|
262
|
+
### Forbidden Patterns
|
|
263
|
+
- ❌ Never trust client-side prices for order totals
|
|
264
|
+
- ❌ No direct database access from route handlers (use service layer)
|
|
265
|
+
- ❌ No blocking API calls in the render path
|
|
266
|
+
|
|
267
|
+
### Naming Conventions
|
|
268
|
+
- Files: `kebab-case.ts`
|
|
269
|
+
- Components: `PascalCase.tsx`
|
|
270
|
+
- API routes: `/api/kebab-case`
|
|
271
|
+
- DB tables: `snake_case`
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## 11. Open Questions
|
|
276
|
+
|
|
277
|
+
- [x] Conflict resolution on merge? → Server wins, quantities are summed
|
|
278
|
+
- [ ] Should we add a "recently removed" undo feature?
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## 12. Revision History
|
|
283
|
+
|
|
284
|
+
| Version | Date | Author | Changes |
|
|
285
|
+
|---------|------|--------|---------|
|
|
286
|
+
| 0.1 | 2025-07-24 | AI Agent | Initial design |
|
|
287
|
+
| 1.0 | 2025-07-24 | AI Agent | Approved after review |
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Feature: Shopping Cart Management
|
|
2
|
+
|
|
3
|
+
> **Status:** 🟢 Approved
|
|
4
|
+
>
|
|
5
|
+
> **Author:** AI Agent
|
|
6
|
+
>
|
|
7
|
+
> **Created:** 2025-07-24
|
|
8
|
+
>
|
|
9
|
+
> **Last Updated:** 2025-07-24
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
Allow users to add products to a shopping cart, modify quantities, remove items,
|
|
16
|
+
and see a real-time total. The cart persists across browser sessions so users
|
|
17
|
+
don't lose their selections.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## User Stories
|
|
22
|
+
|
|
23
|
+
### US-1: Add Product to Cart
|
|
24
|
+
|
|
25
|
+
**As a** online shopper,
|
|
26
|
+
**I want to** add products to my shopping cart,
|
|
27
|
+
**so that** I can collect items I intend to purchase.
|
|
28
|
+
|
|
29
|
+
#### Acceptance Criteria
|
|
30
|
+
|
|
31
|
+
##### AC-1.1: Successfully add a product
|
|
32
|
+
|
|
33
|
+
**Given** I am on a product detail page
|
|
34
|
+
**And** the product is in stock
|
|
35
|
+
**When** I click the "Add to Cart" button
|
|
36
|
+
**Then** the product is added to my cart with quantity 1
|
|
37
|
+
**And** the cart icon in the header shows the updated item count
|
|
38
|
+
**And** I see a confirmation toast notification "Item added to cart"
|
|
39
|
+
|
|
40
|
+
##### AC-1.2: Add a product that is already in the cart
|
|
41
|
+
|
|
42
|
+
**Given** I have "Blue T-Shirt" in my cart with quantity 1
|
|
43
|
+
**When** I navigate to the "Blue T-Shirt" product page
|
|
44
|
+
**And** I click the "Add to Cart" button
|
|
45
|
+
**Then** the quantity of "Blue T-Shirt" in my cart increases to 2
|
|
46
|
+
**And** no duplicate entry is created
|
|
47
|
+
|
|
48
|
+
##### AC-1.3: Attempt to add an out-of-stock product
|
|
49
|
+
|
|
50
|
+
**Given** I am on a product detail page
|
|
51
|
+
**And** the product is out of stock
|
|
52
|
+
**When** I view the page
|
|
53
|
+
**Then** the "Add to Cart" button is disabled
|
|
54
|
+
**And** I see a message "Currently out of stock"
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
### US-2: Update Cart Item Quantity
|
|
59
|
+
|
|
60
|
+
**As a** online shopper,
|
|
61
|
+
**I want to** change the quantity of items in my cart,
|
|
62
|
+
**so that** I can buy the right number of each product.
|
|
63
|
+
|
|
64
|
+
#### Acceptance Criteria
|
|
65
|
+
|
|
66
|
+
##### AC-2.1: Increase item quantity
|
|
67
|
+
|
|
68
|
+
**Given** I have "Blue T-Shirt" in my cart with quantity 1
|
|
69
|
+
**When** I increase the quantity to 3
|
|
70
|
+
**Then** the item quantity updates to 3
|
|
71
|
+
**And** the cart total updates to reflect 3 × the item price
|
|
72
|
+
|
|
73
|
+
##### AC-2.2: Decrease item quantity to zero removes the item
|
|
74
|
+
|
|
75
|
+
**Given** I have "Blue T-Shirt" in my cart with quantity 1
|
|
76
|
+
**When** I decrease the quantity to 0
|
|
77
|
+
**Then** the item is removed from my cart
|
|
78
|
+
**And** I see a confirmation "Item removed from cart"
|
|
79
|
+
|
|
80
|
+
##### AC-2.3: Quantity cannot exceed stock limit
|
|
81
|
+
|
|
82
|
+
**Given** I have "Blue T-Shirt" in my cart
|
|
83
|
+
**And** the available stock is 10
|
|
84
|
+
**When** I attempt to set the quantity to 15
|
|
85
|
+
**Then** the quantity is capped at 10
|
|
86
|
+
**And** I see a message "Maximum available quantity is 10"
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### US-3: Cart Persistence
|
|
91
|
+
|
|
92
|
+
**As a** online shopper,
|
|
93
|
+
**I want** my cart to persist across browser sessions,
|
|
94
|
+
**so that** I don't lose my selections when I close the browser.
|
|
95
|
+
|
|
96
|
+
#### Acceptance Criteria
|
|
97
|
+
|
|
98
|
+
##### AC-3.1: Cart survives page reload
|
|
99
|
+
|
|
100
|
+
**Given** I have 3 items in my cart
|
|
101
|
+
**When** I refresh the browser page
|
|
102
|
+
**Then** all 3 items are still in my cart with the correct quantities
|
|
103
|
+
|
|
104
|
+
##### AC-3.2: Cart syncs for logged-in users
|
|
105
|
+
|
|
106
|
+
**Given** I am logged in and have items in my cart on Device A
|
|
107
|
+
**When** I log in on Device B
|
|
108
|
+
**Then** I see the same cart contents on Device B
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Out of Scope
|
|
113
|
+
|
|
114
|
+
- **Checkout flow** — will be a separate feature spec
|
|
115
|
+
- **Wishlist / Save for Later** — planned for Phase 2
|
|
116
|
+
- **Cart sharing via URL** — not in current roadmap
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Dependencies
|
|
121
|
+
|
|
122
|
+
| Dependency | Type | Status |
|
|
123
|
+
|-----------|------|--------|
|
|
124
|
+
| Product Catalog API | API | Available |
|
|
125
|
+
| User Authentication | Feature | Available |
|
|
126
|
+
| Inventory Service | API | Available |
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Non-Functional Requirements
|
|
131
|
+
|
|
132
|
+
- **Performance:** Cart updates must reflect in < 200ms
|
|
133
|
+
- **Security:** Cart data for logged-in users must be tied to authenticated sessions
|
|
134
|
+
- **Accessibility:** All cart controls must be keyboard-navigable and screen-reader compatible
|
|
135
|
+
- **Browser Support:** Chrome, Firefox, Safari, Edge (latest 2 versions)
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Open Questions
|
|
140
|
+
|
|
141
|
+
- [x] Should guest users have persistent carts? → Yes, via localStorage
|
|
142
|
+
- [ ] What is the maximum number of unique items allowed in a cart?
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Revision History
|
|
147
|
+
|
|
148
|
+
| Version | Date | Author | Changes |
|
|
149
|
+
|---------|------|--------|---------|
|
|
150
|
+
| 0.1 | 2025-07-24 | AI Agent | Initial draft |
|
|
151
|
+
| 1.0 | 2025-07-24 | AI Agent | Approved after review |
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Implementation Tasks: Shopping Cart Management
|
|
2
|
+
|
|
3
|
+
> **Status:** 🔵 In Progress
|
|
4
|
+
>
|
|
5
|
+
> **Requirements:** [requirements.md](./example-requirements.md)
|
|
6
|
+
>
|
|
7
|
+
> **Design:** [design.md](./example-design.md)
|
|
8
|
+
>
|
|
9
|
+
> **Created:** 2025-07-24
|
|
10
|
+
>
|
|
11
|
+
> **Last Updated:** 2025-07-24
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Progress Overview
|
|
16
|
+
|
|
17
|
+
| Phase | Status | Tasks | Completed |
|
|
18
|
+
|-------|--------|-------|-----------|
|
|
19
|
+
| Phase 1: Database & Models | ✅ Done | 4/4 | 100% |
|
|
20
|
+
| Phase 2: Backend API | 🔵 In Progress | 2/5 | 40% |
|
|
21
|
+
| Phase 3: Frontend UI | ⬜ Not Started | 0/5 | 0% |
|
|
22
|
+
| Phase 4: Testing & Polish | ⬜ Not Started | 0/4 | 0% |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Phase 1: Database & Models
|
|
27
|
+
|
|
28
|
+
> **Goal:** Set up the cart data layer — database schema, models, and data access
|
|
29
|
+
>
|
|
30
|
+
> **Traces to:** US-1, US-3 (persistence) from requirements.md
|
|
31
|
+
|
|
32
|
+
- [x] **Task 1.1:** Create cart_items database migration
|
|
33
|
+
- File(s): `src/server/migrations/003_create_cart_items.sql`
|
|
34
|
+
- Details: Create table with UUID PK, user_id FK, product_id FK, quantity check, unique constraint, and index
|
|
35
|
+
|
|
36
|
+
- [x] **Task 1.2:** Create CartItem TypeScript model
|
|
37
|
+
- File(s): `src/shared/types/cart.ts`
|
|
38
|
+
- Details: Define `CartItem`, `AddToCartPayload`, `UpdateQuantityPayload` interfaces
|
|
39
|
+
|
|
40
|
+
- [x] **Task 1.3:** Create CartRepository data access layer
|
|
41
|
+
- File(s): `src/server/repositories/CartRepository.ts`
|
|
42
|
+
- Details: Implement `findByUserId`, `upsertItem`, `updateQuantity`, `removeItem`, `clearCart`
|
|
43
|
+
|
|
44
|
+
- [x] **Task 1.4 (Verify):** Run verification for Phase 1
|
|
45
|
+
- [x] Migration runs without errors
|
|
46
|
+
- [x] Repository methods tested against test database
|
|
47
|
+
- [x] TypeScript types compile without errors
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Phase 2: Backend API
|
|
52
|
+
|
|
53
|
+
> **Goal:** Build the cart service and REST API endpoints
|
|
54
|
+
>
|
|
55
|
+
> **Traces to:** US-1 (AC-1.1, AC-1.2, AC-1.3), US-2 (AC-2.1, AC-2.2, AC-2.3) from requirements.md
|
|
56
|
+
|
|
57
|
+
- [x] **Task 2.1:** Implement CartService business logic
|
|
58
|
+
- File(s): `src/server/services/CartService.ts`
|
|
59
|
+
- Details:
|
|
60
|
+
- [x] `addToCart()` — validate stock, handle existing item (increment qty)
|
|
61
|
+
- [x] `updateQuantity()` — cap at stock limit, remove if qty = 0
|
|
62
|
+
- [x] `removeItem()` — remove single item
|
|
63
|
+
- [x] `getCart()` — return full cart with computed totals
|
|
64
|
+
- [x] `mergeCart()` — merge guest items on login
|
|
65
|
+
|
|
66
|
+
- [x] **Task 2.2:** Create cart API routes
|
|
67
|
+
- File(s): `src/server/routes/cart.ts`
|
|
68
|
+
- Details:
|
|
69
|
+
- [x] `GET /api/cart` — get user's cart
|
|
70
|
+
- [x] `POST /api/cart/items` — add item
|
|
71
|
+
- [x] `PATCH /api/cart/items/:id` — update quantity
|
|
72
|
+
- [x] `DELETE /api/cart/items/:id` — remove item
|
|
73
|
+
- [x] `POST /api/cart/merge` — merge guest cart
|
|
74
|
+
|
|
75
|
+
- [/] **Task 2.3:** Add input validation middleware
|
|
76
|
+
- File(s): `src/server/middleware/validate-cart.ts`
|
|
77
|
+
- Details: Validate productId format, quantity range, request body structure
|
|
78
|
+
|
|
79
|
+
- [ ] **Task 2.4:** Add authentication guard to cart routes
|
|
80
|
+
- File(s): `src/server/middleware/auth.ts`
|
|
81
|
+
- Details: Ensure all cart endpoints require valid JWT (except during merge flow)
|
|
82
|
+
|
|
83
|
+
- [ ] **Task 2.5 (Verify):** Run verification for Phase 2
|
|
84
|
+
- [ ] All endpoints return correct status codes
|
|
85
|
+
- [ ] Stock limit enforcement works (AC-2.3)
|
|
86
|
+
- [ ] Duplicate item handling works (AC-1.2)
|
|
87
|
+
- [ ] Out-of-stock rejection works (AC-1.3)
|
|
88
|
+
- [ ] Integration tests pass
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Phase 3: Frontend UI
|
|
93
|
+
|
|
94
|
+
> **Goal:** Build the cart UI components with state management
|
|
95
|
+
>
|
|
96
|
+
> **Traces to:** US-1 (AC-1.1), US-2 (AC-2.1, AC-2.2), US-3 (AC-3.1) from requirements.md
|
|
97
|
+
|
|
98
|
+
- [ ] **Task 3.1:** Create Zustand cart store
|
|
99
|
+
- File(s): `src/store/useCartStore.ts`
|
|
100
|
+
- Details:
|
|
101
|
+
- [ ] Define store with `items`, `totalItems`, `totalPrice` computed
|
|
102
|
+
- [ ] Implement `addItem`, `updateQuantity`, `removeItem`, `clearCart` actions
|
|
103
|
+
- [ ] Add localStorage persistence middleware for guests
|
|
104
|
+
- [ ] Add API sync middleware for authenticated users
|
|
105
|
+
|
|
106
|
+
- [ ] **Task 3.2:** Build CartButton header component
|
|
107
|
+
- File(s): `src/components/CartButton.tsx`
|
|
108
|
+
- Details: Shows cart icon with item count badge, links to cart page
|
|
109
|
+
|
|
110
|
+
- [ ] **Task 3.3:** Build AddToCartButton product component
|
|
111
|
+
- File(s): `src/components/AddToCartButton.tsx`
|
|
112
|
+
- Details:
|
|
113
|
+
- [ ] Enabled state: shows "Add to Cart"
|
|
114
|
+
- [ ] Disabled state: shows "Out of Stock" (AC-1.3)
|
|
115
|
+
- [ ] Loading state during API call
|
|
116
|
+
- [ ] Toast notification on success (AC-1.1)
|
|
117
|
+
|
|
118
|
+
- [ ] **Task 3.4:** Build CartPage with item management
|
|
119
|
+
- File(s): `src/pages/CartPage.tsx`
|
|
120
|
+
- Details:
|
|
121
|
+
- [ ] List all cart items with image, name, price, quantity
|
|
122
|
+
- [ ] Quantity +/- controls with stock limit enforcement (AC-2.3)
|
|
123
|
+
- [ ] Remove item button
|
|
124
|
+
- [ ] Cart total display
|
|
125
|
+
- [ ] Empty cart state
|
|
126
|
+
|
|
127
|
+
- [ ] **Task 3.5 (Verify):** Run verification for Phase 3
|
|
128
|
+
- [ ] Cart updates reflect in < 200ms (NFR)
|
|
129
|
+
- [ ] Cart persists across page reload (AC-3.1)
|
|
130
|
+
- [ ] All controls keyboard-accessible (NFR)
|
|
131
|
+
- [ ] Responsive on mobile and desktop
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Phase 4: Testing & Polish
|
|
136
|
+
|
|
137
|
+
> **Goal:** Comprehensive testing and final quality pass
|
|
138
|
+
|
|
139
|
+
- [ ] **Task 4.1:** Write end-to-end tests
|
|
140
|
+
- File(s): `tests/e2e/cart.spec.ts`
|
|
141
|
+
- Details:
|
|
142
|
+
- [ ] Test: Add product to empty cart
|
|
143
|
+
- [ ] Test: Add duplicate product (quantity increment)
|
|
144
|
+
- [ ] Test: Update quantity and verify total
|
|
145
|
+
- [ ] Test: Remove item from cart
|
|
146
|
+
- [ ] Test: Cart persistence across reload
|
|
147
|
+
- [ ] Test: Stock limit enforcement
|
|
148
|
+
|
|
149
|
+
- [ ] **Task 4.2:** Write unit tests for CartService
|
|
150
|
+
- File(s): `tests/unit/CartService.test.ts`
|
|
151
|
+
- Details:
|
|
152
|
+
- [ ] Test stock validation logic
|
|
153
|
+
- [ ] Test quantity capping
|
|
154
|
+
- [ ] Test merge cart conflict resolution
|
|
155
|
+
- [ ] Test price calculation
|
|
156
|
+
|
|
157
|
+
- [ ] **Task 4.3:** Accessibility audit
|
|
158
|
+
- [ ] All interactive elements have ARIA labels
|
|
159
|
+
- [ ] Tab order is logical
|
|
160
|
+
- [ ] Screen reader announces cart updates
|
|
161
|
+
- [ ] Color contrast meets WCAG AA
|
|
162
|
+
|
|
163
|
+
- [ ] **Task 4.4 (Final Verify):** Complete acceptance criteria check
|
|
164
|
+
- [ ] AC-1.1 ✓ Add product to cart
|
|
165
|
+
- [ ] AC-1.2 ✓ Duplicate product handling
|
|
166
|
+
- [ ] AC-1.3 ✓ Out-of-stock behavior
|
|
167
|
+
- [ ] AC-2.1 ✓ Increase quantity
|
|
168
|
+
- [ ] AC-2.2 ✓ Decrease to zero removes item
|
|
169
|
+
- [ ] AC-2.3 ✓ Stock limit cap
|
|
170
|
+
- [ ] AC-3.1 ✓ Cart persists on reload
|
|
171
|
+
- [ ] AC-3.2 ✓ Cart syncs across devices
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Task Status Legend
|
|
176
|
+
|
|
177
|
+
| Symbol | Meaning |
|
|
178
|
+
|--------|---------|
|
|
179
|
+
| `- [ ]` | Not started |
|
|
180
|
+
| `- [/]` | In progress |
|
|
181
|
+
| `- [x]` | Completed |
|
|
182
|
+
| `- [!]` | Blocked / Needs attention |
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Notes
|
|
187
|
+
|
|
188
|
+
- 2025-07-24: Decided to use Zustand over React Context for cart state — smaller bundle, simpler API
|
|
189
|
+
- 2025-07-24: Merge strategy on login = sum quantities, cap at stock limit, server wins on price conflicts
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
REM ============================================================================
|
|
3
|
+
REM init-spec.bat — Initialize a new spec directory for a feature (Windows)
|
|
4
|
+
REM
|
|
5
|
+
REM Usage:
|
|
6
|
+
REM .specs\scripts\init-spec.bat <feature-name>
|
|
7
|
+
REM
|
|
8
|
+
REM Examples:
|
|
9
|
+
REM .specs\scripts\init-spec.bat user-authentication
|
|
10
|
+
REM ============================================================================
|
|
11
|
+
|
|
12
|
+
setlocal enabledelayedexpansion
|
|
13
|
+
|
|
14
|
+
set "SPECS_DIR=.specs"
|
|
15
|
+
set "FEATURE_NAME=%~1"
|
|
16
|
+
|
|
17
|
+
if "%FEATURE_NAME%"=="" (
|
|
18
|
+
echo ❌ Error: Feature name is required.
|
|
19
|
+
echo.
|
|
20
|
+
echo Usage: %~nx0 ^<feature-name^>
|
|
21
|
+
echo.
|
|
22
|
+
echo Example: %~nx0 user-authentication
|
|
23
|
+
exit /b 1
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
set "SPEC_PATH=%SPECS_DIR%\%FEATURE_NAME%"
|
|
27
|
+
|
|
28
|
+
if exist "%SPEC_PATH%" (
|
|
29
|
+
echo ⚠️ Spec directory already exists: %SPEC_PATH%
|
|
30
|
+
exit /b 1
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
mkdir "%SPEC_PATH%"
|
|
34
|
+
echo 📁 Created: %SPEC_PATH%\
|
|
35
|
+
|
|
36
|
+
REM --- Try to find and copy templates ---
|
|
37
|
+
set "SKILL_DIR="
|
|
38
|
+
for %%D in (
|
|
39
|
+
".gemini\skills\spec-driven-dev\templates"
|
|
40
|
+
"skills\spec-driven-dev\templates"
|
|
41
|
+
".cursor\skills\spec-driven-dev\templates"
|
|
42
|
+
) do (
|
|
43
|
+
if exist "%%~D" (
|
|
44
|
+
set "SKILL_DIR=%%~D"
|
|
45
|
+
goto :found_templates
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
:found_templates
|
|
50
|
+
if defined SKILL_DIR (
|
|
51
|
+
copy "%SKILL_DIR%\requirements.template.md" "%SPEC_PATH%\requirements.md" >nul
|
|
52
|
+
copy "%SKILL_DIR%\design.template.md" "%SPEC_PATH%\design.md" >nul
|
|
53
|
+
copy "%SKILL_DIR%\tasks.template.md" "%SPEC_PATH%\tasks.md" >nul
|
|
54
|
+
echo 📄 Copied templates from %SKILL_DIR%
|
|
55
|
+
) else (
|
|
56
|
+
echo # Feature: %FEATURE_NAME% > "%SPEC_PATH%\requirements.md"
|
|
57
|
+
echo # Technical Design: %FEATURE_NAME% > "%SPEC_PATH%\design.md"
|
|
58
|
+
echo # Implementation Tasks: %FEATURE_NAME% > "%SPEC_PATH%\tasks.md"
|
|
59
|
+
echo 📄 Created minimal spec files (templates not found^)
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
echo.
|
|
63
|
+
echo ✅ Spec initialized for: %FEATURE_NAME%
|
|
64
|
+
echo.
|
|
65
|
+
echo 📋 Next steps:
|
|
66
|
+
echo 1. Edit %SPEC_PATH%\requirements.md — Define WHAT to build
|
|
67
|
+
echo 2. Edit %SPEC_PATH%\design.md — Define HOW to build it
|
|
68
|
+
echo 3. Edit %SPEC_PATH%\tasks.md — Define the execution plan
|
|
69
|
+
|
|
70
|
+
endlocal
|