@qazuor/claude-code-config 0.1.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/LICENSE +21 -0
- package/README.md +1248 -0
- package/dist/bin.cjs +11886 -0
- package/dist/bin.cjs.map +1 -0
- package/dist/bin.d.cts +1 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +11869 -0
- package/dist/bin.js.map +1 -0
- package/dist/index.cjs +3887 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1325 -0
- package/dist/index.d.ts +1325 -0
- package/dist/index.js +3835 -0
- package/dist/index.js.map +1 -0
- package/package.json +86 -0
- package/templates/.log/notifications.log +1775 -0
- package/templates/agents/README.md +164 -0
- package/templates/agents/_registry.json +443 -0
- package/templates/agents/design/content-writer.md +353 -0
- package/templates/agents/design/ux-ui-designer.md +382 -0
- package/templates/agents/engineering/astro-engineer.md +293 -0
- package/templates/agents/engineering/db-drizzle-engineer.md +360 -0
- package/templates/agents/engineering/express-engineer.md +316 -0
- package/templates/agents/engineering/fastify-engineer.md +399 -0
- package/templates/agents/engineering/hono-engineer.md +263 -0
- package/templates/agents/engineering/mongoose-engineer.md +473 -0
- package/templates/agents/engineering/nestjs-engineer.md +429 -0
- package/templates/agents/engineering/nextjs-engineer.md +451 -0
- package/templates/agents/engineering/node-typescript-engineer.md +347 -0
- package/templates/agents/engineering/prisma-engineer.md +432 -0
- package/templates/agents/engineering/react-senior-dev.md +394 -0
- package/templates/agents/engineering/tanstack-start-engineer.md +447 -0
- package/templates/agents/engineering/tech-lead.md +269 -0
- package/templates/agents/product/product-functional.md +329 -0
- package/templates/agents/product/product-technical.md +578 -0
- package/templates/agents/quality/debugger.md +514 -0
- package/templates/agents/quality/qa-engineer.md +390 -0
- package/templates/agents/specialized/enrichment-agent.md +277 -0
- package/templates/agents/specialized/i18n-specialist.md +322 -0
- package/templates/agents/specialized/seo-ai-specialist.md +387 -0
- package/templates/agents/specialized/tech-writer.md +300 -0
- package/templates/code-style/.editorconfig +27 -0
- package/templates/code-style/.prettierignore +25 -0
- package/templates/code-style/.prettierrc +12 -0
- package/templates/code-style/biome.json +78 -0
- package/templates/code-style/commitlint.config.js +44 -0
- package/templates/commands/README.md +175 -0
- package/templates/commands/_registry.json +420 -0
- package/templates/commands/add-new-entity.md +211 -0
- package/templates/commands/audit/accessibility-audit.md +360 -0
- package/templates/commands/audit/performance-audit.md +290 -0
- package/templates/commands/audit/security-audit.md +231 -0
- package/templates/commands/code-check.md +127 -0
- package/templates/commands/five-why.md +225 -0
- package/templates/commands/formatting/format-markdown.md +197 -0
- package/templates/commands/git/commit.md +247 -0
- package/templates/commands/meta/create-agent.md +257 -0
- package/templates/commands/meta/create-command.md +312 -0
- package/templates/commands/meta/create-skill.md +321 -0
- package/templates/commands/meta/help.md +318 -0
- package/templates/commands/planning/check-completed-tasks.md +224 -0
- package/templates/commands/planning/cleanup-issues.md +248 -0
- package/templates/commands/planning/planning-cleanup.md +251 -0
- package/templates/commands/planning/sync-planning-github.md +133 -0
- package/templates/commands/planning/sync-todos-github.md +203 -0
- package/templates/commands/quality-check.md +211 -0
- package/templates/commands/run-tests.md +159 -0
- package/templates/commands/start-feature-plan.md +232 -0
- package/templates/commands/start-refactor-plan.md +244 -0
- package/templates/commands/sync-planning.md +176 -0
- package/templates/commands/update-docs.md +242 -0
- package/templates/docs/CHECKPOINT-SYSTEM.md +504 -0
- package/templates/docs/INDEX.md +677 -0
- package/templates/docs/RECOMMENDED-HOOKS.md +415 -0
- package/templates/docs/_registry.json +329 -0
- package/templates/docs/diagrams/README.md +220 -0
- package/templates/docs/diagrams/agent-hierarchy.mmd +55 -0
- package/templates/docs/diagrams/documentation-map.mmd +61 -0
- package/templates/docs/diagrams/tools-relationship.mmd +55 -0
- package/templates/docs/diagrams/workflow-decision-tree.mmd +38 -0
- package/templates/docs/doc-sync.md +533 -0
- package/templates/docs/examples/end-to-end-workflow.md +1505 -0
- package/templates/docs/glossary.md +495 -0
- package/templates/docs/guides/mockup-prompt-engineering.md +644 -0
- package/templates/docs/guides/mockup-setup.md +737 -0
- package/templates/docs/learnings/README.md +250 -0
- package/templates/docs/learnings/common-architectural-patterns.md +123 -0
- package/templates/docs/learnings/common-mistakes-to-avoid.md +149 -0
- package/templates/docs/learnings/markdown-formatting-standards.md +104 -0
- package/templates/docs/learnings/monorepo-command-execution.md +64 -0
- package/templates/docs/learnings/optimization-tips.md +146 -0
- package/templates/docs/learnings/planning-linear-sync-workflow.md +70 -0
- package/templates/docs/learnings/shell-compatibility-fish.md +46 -0
- package/templates/docs/learnings/test-organization-structure.md +68 -0
- package/templates/docs/mcp-installation.md +613 -0
- package/templates/docs/mcp-servers.md +989 -0
- package/templates/docs/notification-installation.md +570 -0
- package/templates/docs/quick-start.md +354 -0
- package/templates/docs/standards/architecture-patterns.md +1064 -0
- package/templates/docs/standards/atomic-commits.md +513 -0
- package/templates/docs/standards/code-standards.md +993 -0
- package/templates/docs/standards/design-standards.md +656 -0
- package/templates/docs/standards/documentation-standards.md +1160 -0
- package/templates/docs/standards/testing-standards.md +969 -0
- package/templates/docs/system-maintenance.md +604 -0
- package/templates/docs/templates/PDR-template.md +561 -0
- package/templates/docs/templates/TODOs-template.md +534 -0
- package/templates/docs/templates/tech-analysis-template.md +800 -0
- package/templates/docs/workflows/README.md +519 -0
- package/templates/docs/workflows/atomic-task-protocol.md +955 -0
- package/templates/docs/workflows/decision-tree.md +482 -0
- package/templates/docs/workflows/edge-cases.md +856 -0
- package/templates/docs/workflows/phase-1-planning.md +957 -0
- package/templates/docs/workflows/phase-2-implementation.md +896 -0
- package/templates/docs/workflows/phase-3-validation.md +792 -0
- package/templates/docs/workflows/phase-4-finalization.md +927 -0
- package/templates/docs/workflows/quick-fix-protocol.md +505 -0
- package/templates/docs/workflows/task-atomization.md +537 -0
- package/templates/docs/workflows/task-completion-protocol.md +448 -0
- package/templates/hooks/on-notification.sh +28 -0
- package/templates/schemas/checkpoint.schema.json +97 -0
- package/templates/schemas/code-registry.schema.json +84 -0
- package/templates/schemas/pdr.schema.json +314 -0
- package/templates/schemas/problems.schema.json +55 -0
- package/templates/schemas/tech-analysis.schema.json +404 -0
- package/templates/schemas/telemetry.schema.json +298 -0
- package/templates/schemas/todos.schema.json +234 -0
- package/templates/schemas/workflows.schema.json +69 -0
- package/templates/scripts/add-changelogs.sh +105 -0
- package/templates/scripts/generate-code-registry.ts +270 -0
- package/templates/scripts/health-check.sh +343 -0
- package/templates/scripts/sync-registry.sh +40 -0
- package/templates/scripts/telemetry-report.ts +36 -0
- package/templates/scripts/validate-docs.sh +224 -0
- package/templates/scripts/validate-registry.sh +225 -0
- package/templates/scripts/validate-schemas.ts +283 -0
- package/templates/scripts/validate-structure.sh +165 -0
- package/templates/scripts/worktree-cleanup.sh +81 -0
- package/templates/scripts/worktree-create.sh +63 -0
- package/templates/sessions/planning/.gitkeep +0 -0
- package/templates/sessions/planning/archived/.gitkeep +0 -0
- package/templates/settings.json +202 -0
- package/templates/settings.local.json +138 -0
- package/templates/skills/README.md +197 -0
- package/templates/skills/_registry.json +473 -0
- package/templates/skills/audit/accessibility-audit.md +309 -0
- package/templates/skills/audit/performance-audit.md +257 -0
- package/templates/skills/audit/security-audit.md +217 -0
- package/templates/skills/auth/nextauth-patterns.md +308 -0
- package/templates/skills/brand-guidelines.md +240 -0
- package/templates/skills/documentation/markdown-formatter.md +302 -0
- package/templates/skills/git/git-commit-helper.md +321 -0
- package/templates/skills/i18n/i18n-patterns.md +251 -0
- package/templates/skills/patterns/error-handling-patterns.md +242 -0
- package/templates/skills/patterns/tdd-methodology.md +342 -0
- package/templates/skills/qa/qa-criteria-validator.md +383 -0
- package/templates/skills/qa/web-app-testing.md +398 -0
- package/templates/skills/react/react-hook-form-patterns.md +359 -0
- package/templates/skills/state/redux-toolkit-patterns.md +272 -0
- package/templates/skills/state/tanstack-query-patterns.md +299 -0
- package/templates/skills/state/zustand-patterns.md +301 -0
- package/templates/skills/tech/mermaid-diagram-specialist.md +195 -0
- package/templates/skills/tech/shadcn-specialist.md +252 -0
- package/templates/skills/tech/vercel-specialist.md +297 -0
- package/templates/skills/testing/api-app-testing.md +254 -0
- package/templates/skills/testing/performance-testing.md +275 -0
- package/templates/skills/testing/security-testing.md +348 -0
- package/templates/skills/utils/add-memory.md +295 -0
- package/templates/skills/utils/json-data-auditor.md +283 -0
- package/templates/skills/utils/pdf-creator-editor.md +342 -0
- package/templates/tools/format-markdown.sh +185 -0
|
@@ -0,0 +1,1505 @@
|
|
|
1
|
+
# End-to-End Workflow Example
|
|
2
|
+
|
|
3
|
+
**Feature:** User Favorites for Entitys
|
|
4
|
+
|
|
5
|
+
This document shows a complete workflow from feature request to deployment, following the **Level 3: Feature Planning** protocol with all 4 phases.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Context](#context)
|
|
10
|
+
- [Phase 1: Planning](#phase-1-planning)
|
|
11
|
+
- [Phase 2: Implementation](#phase-2-implementation)
|
|
12
|
+
- [Phase 3: Validation](#phase-3-validation)
|
|
13
|
+
- [Phase 4: Finalization](#phase-4-finalization)
|
|
14
|
+
- [Results](#results)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Context
|
|
19
|
+
|
|
20
|
+
### Feature Request
|
|
21
|
+
|
|
22
|
+
**From:** Product Manager
|
|
23
|
+
**Date:** 2025-11-01
|
|
24
|
+
**Priority:** High
|
|
25
|
+
|
|
26
|
+
> "We need to allow users to save their favorite entitys for quick access later. Users should be able to add/remove favorites and see a list of their saved entitys. This should work for both logged-in users (persisted) and guest users (session-based)."
|
|
27
|
+
|
|
28
|
+
### Initial Assessment
|
|
29
|
+
|
|
30
|
+
- **Workflow Level:** Level 3 (Feature Planning - 4 Phases)
|
|
31
|
+
- **Estimated Duration:** 3-5 days
|
|
32
|
+
- **Complexity:** Medium
|
|
33
|
+
- **Risk:** Low-Medium
|
|
34
|
+
- **Reason for Level 3:**
|
|
35
|
+
- Database changes required (favorites table)
|
|
36
|
+
- Multiple layers affected (DB, Service, API, Frontend)
|
|
37
|
+
- New business logic (favorites management)
|
|
38
|
+
- Authentication considerations
|
|
39
|
+
- Session management for guests
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Phase 1: Planning
|
|
44
|
+
|
|
45
|
+
**Duration:** 4-6 hours
|
|
46
|
+
**Participants:** product-functional, product-technical, ux-ui-designer, tech-lead
|
|
47
|
+
|
|
48
|
+
### Step 1: Start Planning Session
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Create planning session
|
|
52
|
+
mkdir -p .claude/sessions/planning/active/P-007-user-favorites
|
|
53
|
+
cd .claude/sessions/planning/active/P-007-user-favorites
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Step 2: Product Functional Analysis
|
|
57
|
+
|
|
58
|
+
**Agent:** product-functional
|
|
59
|
+
|
|
60
|
+
**Output:** `PDR.md` (Product Design Requirements)
|
|
61
|
+
|
|
62
|
+
<details>
|
|
63
|
+
<summary>PDR.md (Click to expand)</summary>
|
|
64
|
+
|
|
65
|
+
```markdown
|
|
66
|
+
# PDR: User Favorites for Entitys
|
|
67
|
+
|
|
68
|
+
**Feature ID:** F-007
|
|
69
|
+
**Priority:** High
|
|
70
|
+
**Status:** Planning
|
|
71
|
+
**Created:** 2025-11-01
|
|
72
|
+
|
|
73
|
+
## Overview
|
|
74
|
+
|
|
75
|
+
Allow users to save favorite entitys for quick access and future reference.
|
|
76
|
+
|
|
77
|
+
## User Stories
|
|
78
|
+
|
|
79
|
+
### US-001: Add to Favorites (Logged User)
|
|
80
|
+
|
|
81
|
+
**As a** logged-in user
|
|
82
|
+
**I want** to mark entitys as favorites
|
|
83
|
+
**So that** I can easily find them later
|
|
84
|
+
|
|
85
|
+
**Acceptance Criteria:**
|
|
86
|
+
|
|
87
|
+
- [ ] Heart icon appears on entity cards
|
|
88
|
+
- [ ] Clicking heart adds/removes from favorites
|
|
89
|
+
- [ ] Visual feedback shows favorite state (filled vs outline)
|
|
90
|
+
- [ ] Favorites persist across sessions
|
|
91
|
+
- [ ] Maximum 50 favorites per user
|
|
92
|
+
|
|
93
|
+
### US-002: View Favorites List
|
|
94
|
+
|
|
95
|
+
**As a** logged-in user
|
|
96
|
+
**I want** to see all my favorite entitys
|
|
97
|
+
**So that** I can quickly access properties I'm interested in
|
|
98
|
+
|
|
99
|
+
**Acceptance Criteria:**
|
|
100
|
+
|
|
101
|
+
- [ ] Dedicated "Favorites" page accessible from navigation
|
|
102
|
+
- [ ] Shows all favorited entitys
|
|
103
|
+
- [ ] Displays same info as search results (image, title, price, rating)
|
|
104
|
+
- [ ] Empty state shown when no favorites
|
|
105
|
+
- [ ] Can remove favorites from this page
|
|
106
|
+
- [ ] Results are paginated (20 per page)
|
|
107
|
+
|
|
108
|
+
### US-003: Guest User Favorites
|
|
109
|
+
|
|
110
|
+
**As a** guest user
|
|
111
|
+
**I want** to save favorites during my session
|
|
112
|
+
**So that** I can compare properties before deciding to register
|
|
113
|
+
|
|
114
|
+
**Acceptance Criteria:**
|
|
115
|
+
|
|
116
|
+
- [ ] Favorites stored in session/localStorage
|
|
117
|
+
- [ ] Same UI/UX as logged users
|
|
118
|
+
- [ ] Prompt to register when trying to exceed 5 favorites
|
|
119
|
+
- [ ] Clear message that favorites will be lost on logout
|
|
120
|
+
- [ ] Option to migrate favorites on registration
|
|
121
|
+
|
|
122
|
+
## Mockups
|
|
123
|
+
|
|
124
|
+
### Entity Card with Favorite Button
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
┌─────────────────────────────┐
|
|
129
|
+
│ [Image] ♡ /❤️ │
|
|
130
|
+
│ │
|
|
131
|
+
│ Beach House Villa │
|
|
132
|
+
│ ⭐ 4.8 (120) │
|
|
133
|
+
│ $150/night │
|
|
134
|
+
└─────────────────────────────┘
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Favorites Page
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
┌─────────────────────────────────────────┐
|
|
143
|
+
│ My Favorites (12) │
|
|
144
|
+
├─────────────────────────────────────────┤
|
|
145
|
+
│ ┌───────────┐ │
|
|
146
|
+
│ │ [Image] │ Beach Villa ❤️ Remove │
|
|
147
|
+
│ └───────────┘ $150/night ⭐ 4.8 │
|
|
148
|
+
│ │
|
|
149
|
+
│ ┌───────────┐ │
|
|
150
|
+
│ │ [Image] │ City Apartment ❤️ Remove│
|
|
151
|
+
│ └───────────┘ $80/night ⭐ 4.5 │
|
|
152
|
+
└─────────────────────────────────────────┘
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Functional Requirements
|
|
157
|
+
|
|
158
|
+
### FR-001: Favorite Management
|
|
159
|
+
|
|
160
|
+
- Users can add/remove favorites
|
|
161
|
+
- System prevents duplicates
|
|
162
|
+
- Favorites associated with user ID
|
|
163
|
+
- Soft delete (mark as inactive instead of DELETE)
|
|
164
|
+
|
|
165
|
+
### FR-002: Limits
|
|
166
|
+
|
|
167
|
+
- Logged users: max 50 favorites
|
|
168
|
+
- Guest users: max 5 favorites
|
|
169
|
+
- Clear error messages when limits reached
|
|
170
|
+
|
|
171
|
+
### FR-003: Performance
|
|
172
|
+
|
|
173
|
+
- Favorites list loads < 500ms
|
|
174
|
+
- Add/remove operations < 200ms
|
|
175
|
+
- Optimistic UI updates
|
|
176
|
+
|
|
177
|
+
## Non-Functional Requirements
|
|
178
|
+
|
|
179
|
+
### NFR-001: Scalability
|
|
180
|
+
|
|
181
|
+
- Support up to 100,000 users with favorites
|
|
182
|
+
- Efficient queries (proper indexing)
|
|
183
|
+
|
|
184
|
+
### NFR-002: Data Integrity
|
|
185
|
+
|
|
186
|
+
- No orphaned favorites
|
|
187
|
+
- CASCADE delete when user deleted
|
|
188
|
+
- CASCADE update when entity deleted
|
|
189
|
+
|
|
190
|
+
### NFR-003: Security
|
|
191
|
+
|
|
192
|
+
- Users can only see/modify their own favorites
|
|
193
|
+
- Rate limiting on favorite operations (max 10/minute)
|
|
194
|
+
|
|
195
|
+
## Out of Scope
|
|
196
|
+
|
|
197
|
+
- Sharing favorites with other users
|
|
198
|
+
- Organizing favorites into lists/categories
|
|
199
|
+
- Email notifications for price changes on favorites
|
|
200
|
+
- Collaborative favorites (multiple users)
|
|
201
|
+
|
|
202
|
+
## Dependencies
|
|
203
|
+
|
|
204
|
+
- User authentication system (Clerk)
|
|
205
|
+
- Entity service (existing)
|
|
206
|
+
|
|
207
|
+
## Risks
|
|
208
|
+
|
|
209
|
+
| Risk | Impact | Mitigation |
|
|
210
|
+
|------|--------|------------|
|
|
211
|
+
| Performance degradation with large favorites lists | Medium | Implement pagination, caching |
|
|
212
|
+
| Guest favorites lost | Low | Clear messaging, migration flow |
|
|
213
|
+
| Favorite spam/abuse | Low | Rate limiting, max limits |
|
|
214
|
+
|
|
215
|
+
## Success Metrics
|
|
216
|
+
|
|
217
|
+
- 40%+ users add at least one favorite within first week
|
|
218
|
+
- < 1% error rate on favorite operations
|
|
219
|
+
- Average 8 favorites per active user
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
</details>
|
|
223
|
+
|
|
224
|
+
### Step 3: UX/UI Design
|
|
225
|
+
|
|
226
|
+
**Agent:** ux-ui-designer
|
|
227
|
+
|
|
228
|
+
**Output:** Design specs in `PDR.md` (mockups section)
|
|
229
|
+
|
|
230
|
+
**Key Decisions:**
|
|
231
|
+
|
|
232
|
+
- Heart icon for consistency with booking platforms
|
|
233
|
+
- Filled heart (❤️) for favorited, outline (♡) for not favorited
|
|
234
|
+
- Optimistic UI: instant visual feedback, revert on error
|
|
235
|
+
- Toast notifications for add/remove confirmations
|
|
236
|
+
|
|
237
|
+
### Step 4: Technical Analysis
|
|
238
|
+
|
|
239
|
+
**Agent:** product-technical
|
|
240
|
+
|
|
241
|
+
**Output:** `tech-analysis.md`
|
|
242
|
+
|
|
243
|
+
<details>
|
|
244
|
+
<summary>tech-analysis.md (Click to expand)</summary>
|
|
245
|
+
|
|
246
|
+
```markdown
|
|
247
|
+
# Technical Analysis: User Favorites
|
|
248
|
+
|
|
249
|
+
**Feature ID:** F-007
|
|
250
|
+
**Complexity:** Medium
|
|
251
|
+
**Estimated Effort:** 20-24 hours
|
|
252
|
+
|
|
253
|
+
## Architecture Impact
|
|
254
|
+
|
|
255
|
+
### Database Layer
|
|
256
|
+
|
|
257
|
+
**New Table:** `favorites`
|
|
258
|
+
|
|
259
|
+
```sql
|
|
260
|
+
CREATE TABLE favorites (
|
|
261
|
+
id TEXT PRIMARY KEY,
|
|
262
|
+
user_id TEXT NOT NULL,
|
|
263
|
+
entity_id TEXT NOT NULL,
|
|
264
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
265
|
+
is_active BOOLEAN DEFAULT TRUE,
|
|
266
|
+
|
|
267
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
268
|
+
FOREIGN KEY (entity_id) REFERENCES entitys(id) ON DELETE CASCADE,
|
|
269
|
+
UNIQUE (user_id, entity_id)
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
CREATE INDEX idx_favorites_user ON favorites(user_id, is_active);
|
|
273
|
+
CREATE INDEX idx_favorites_entity ON favorites(entity_id);
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Service Layer
|
|
277
|
+
|
|
278
|
+
**New Service:** `FavoriteService extends BaseCrudService`
|
|
279
|
+
|
|
280
|
+
Methods:
|
|
281
|
+
|
|
282
|
+
- `addFavorite({ userId, entityId })` - Add favorite
|
|
283
|
+
- `removeFavorite({ userId, entityId })` - Remove favorite (soft delete)
|
|
284
|
+
- `getFavorites({ userId, pagination })` - Get user's favorites
|
|
285
|
+
- `isFavorite({ userId, entityId })` - Check if favorited
|
|
286
|
+
- `getFavoriteCount({ userId })` - Get count for limit checking
|
|
287
|
+
|
|
288
|
+
### API Layer
|
|
289
|
+
|
|
290
|
+
**New Routes:** `/api/favorites`
|
|
291
|
+
|
|
292
|
+
- `POST /favorites` - Add favorite
|
|
293
|
+
- `DELETE /favorites/:entityId` - Remove favorite
|
|
294
|
+
- `GET /favorites` - List user's favorites
|
|
295
|
+
- `GET /favorites/check/:entityId` - Check if favorited
|
|
296
|
+
|
|
297
|
+
### Frontend (Web)
|
|
298
|
+
|
|
299
|
+
**Components:**
|
|
300
|
+
|
|
301
|
+
- `FavoriteButton.tsx` - Heart icon toggle
|
|
302
|
+
- `FavoritesList.tsx` - Favorites page
|
|
303
|
+
- `FavoritesEmpty.tsx` - Empty state
|
|
304
|
+
|
|
305
|
+
**State Management:**
|
|
306
|
+
|
|
307
|
+
- TanStack Query for server state
|
|
308
|
+
- Optimistic updates for add/remove
|
|
309
|
+
|
|
310
|
+
### Frontend (Admin)
|
|
311
|
+
|
|
312
|
+
**No changes required** - Admin doesn't need favorites feature
|
|
313
|
+
|
|
314
|
+
## Technology Stack
|
|
315
|
+
|
|
316
|
+
| Layer | Technology |
|
|
317
|
+
|-------|------------|
|
|
318
|
+
| Database | PostgreSQL + Drizzle ORM |
|
|
319
|
+
| Validation | Zod schemas |
|
|
320
|
+
| API | Hono |
|
|
321
|
+
| Frontend | Astro + React 19 |
|
|
322
|
+
| State | TanStack Query |
|
|
323
|
+
| Auth | Clerk |
|
|
324
|
+
|
|
325
|
+
## Data Flow
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
User clicks heart
|
|
329
|
+
↓
|
|
330
|
+
Optimistic UI update (instant feedback)
|
|
331
|
+
↓
|
|
332
|
+
POST /api/favorites
|
|
333
|
+
↓
|
|
334
|
+
Auth middleware validates user
|
|
335
|
+
↓
|
|
336
|
+
Zod validates payload
|
|
337
|
+
↓
|
|
338
|
+
FavoriteService.addFavorite()
|
|
339
|
+
↓
|
|
340
|
+
Check user favorites count < limit
|
|
341
|
+
↓
|
|
342
|
+
FavoriteModel.create()
|
|
343
|
+
↓
|
|
344
|
+
Return success
|
|
345
|
+
↓
|
|
346
|
+
TanStack Query revalidates
|
|
347
|
+
↓
|
|
348
|
+
UI stays updated (or reverts on error)
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Security Considerations
|
|
352
|
+
|
|
353
|
+
### Authentication
|
|
354
|
+
|
|
355
|
+
- All endpoints require authentication
|
|
356
|
+
- User can only access their own favorites
|
|
357
|
+
|
|
358
|
+
### Authorization
|
|
359
|
+
|
|
360
|
+
- Verify userId from JWT matches request
|
|
361
|
+
- No admin-specific permissions needed
|
|
362
|
+
|
|
363
|
+
### Rate Limiting
|
|
364
|
+
|
|
365
|
+
- Max 10 operations/minute per user
|
|
366
|
+
- Prevents abuse/spam
|
|
367
|
+
|
|
368
|
+
### Input Validation
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
addFavoriteSchema = z.object({
|
|
372
|
+
entityId: z.string().uuid(),
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// userId comes from authenticated session
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Performance Optimization
|
|
379
|
+
|
|
380
|
+
### Database
|
|
381
|
+
|
|
382
|
+
- Composite index on `(user_id, is_active)` for fast user lookups
|
|
383
|
+
- Soft deletes preserve data while keeping queries fast
|
|
384
|
+
- UNIQUE constraint prevents duplicates
|
|
385
|
+
|
|
386
|
+
### API
|
|
387
|
+
|
|
388
|
+
- Pagination for favorites list (20 per page)
|
|
389
|
+
- Response caching with short TTL (1 minute)
|
|
390
|
+
|
|
391
|
+
### Frontend
|
|
392
|
+
|
|
393
|
+
- Optimistic updates for instant feedback
|
|
394
|
+
- Prefetch favorites count on page load
|
|
395
|
+
- Debounce favorite toggle (prevent double-clicks)
|
|
396
|
+
|
|
397
|
+
## Testing Strategy
|
|
398
|
+
|
|
399
|
+
### Unit Tests
|
|
400
|
+
|
|
401
|
+
- FavoriteModel CRUD operations
|
|
402
|
+
- FavoriteService business logic
|
|
403
|
+
- Limit enforcement
|
|
404
|
+
- Duplicate prevention
|
|
405
|
+
|
|
406
|
+
### Integration Tests
|
|
407
|
+
|
|
408
|
+
- API endpoints with authentication
|
|
409
|
+
- Database constraints
|
|
410
|
+
- Cascading deletes
|
|
411
|
+
|
|
412
|
+
### E2E Tests
|
|
413
|
+
|
|
414
|
+
- Add favorite from search results
|
|
415
|
+
- Remove favorite from favorites page
|
|
416
|
+
- Guest user flow
|
|
417
|
+
- Limit enforcement UI
|
|
418
|
+
|
|
419
|
+
**Coverage Target:** 90%+ for all layers
|
|
420
|
+
|
|
421
|
+
## Error Handling
|
|
422
|
+
|
|
423
|
+
| Scenario | Response | HTTP Code |
|
|
424
|
+
|----------|----------|-----------|
|
|
425
|
+
| Entity not found | "Entity not found" | 404 |
|
|
426
|
+
| Already favorited | No-op (idempotent) | 200 |
|
|
427
|
+
| Limit reached | "Maximum favorites reached (50)" | 400 |
|
|
428
|
+
| Unauthorized | "Authentication required" | 401 |
|
|
429
|
+
| Rate limit exceeded | "Too many requests" | 429 |
|
|
430
|
+
|
|
431
|
+
## Rollout Plan
|
|
432
|
+
|
|
433
|
+
### Phase 1: Backend (Day 1-2)
|
|
434
|
+
|
|
435
|
+
1. Database migration
|
|
436
|
+
2. Models and services
|
|
437
|
+
3. API endpoints
|
|
438
|
+
4. Unit + integration tests
|
|
439
|
+
|
|
440
|
+
### Phase 2: Frontend (Day 3-4)
|
|
441
|
+
|
|
442
|
+
1. React components
|
|
443
|
+
2. TanStack Query integration
|
|
444
|
+
3. Optimistic updates
|
|
445
|
+
4. E2E tests
|
|
446
|
+
|
|
447
|
+
### Phase 3: Polish (Day 5)
|
|
448
|
+
|
|
449
|
+
1. Edge case handling
|
|
450
|
+
2. Error messages
|
|
451
|
+
3. Loading states
|
|
452
|
+
4. Documentation
|
|
453
|
+
|
|
454
|
+
## Risks & Mitigation
|
|
455
|
+
|
|
456
|
+
| Risk | Probability | Impact | Mitigation |
|
|
457
|
+
|------|-------------|--------|------------|
|
|
458
|
+
| Performance issues with large lists | Low | Medium | Pagination + indexing |
|
|
459
|
+
| Race conditions on toggle | Medium | Low | Optimistic updates + debouncing |
|
|
460
|
+
| Guest migration complexity | Low | Low | Simple: prompt to login |
|
|
461
|
+
|
|
462
|
+
## Dependencies
|
|
463
|
+
|
|
464
|
+
- `@repo/db` - Database models
|
|
465
|
+
- `@repo/schemas` - Validation schemas
|
|
466
|
+
- `@repo/service-core` - Business logic
|
|
467
|
+
- `apps/api` - API routes
|
|
468
|
+
- `apps/web` - Frontend UI
|
|
469
|
+
- `@clerk/nextjs` - Authentication
|
|
470
|
+
|
|
471
|
+
## Acceptance Criteria
|
|
472
|
+
|
|
473
|
+
- [ ] All user stories met
|
|
474
|
+
- [ ] 90%+ test coverage
|
|
475
|
+
- [ ] Performance targets met (< 500ms list, < 200ms toggle)
|
|
476
|
+
- [ ] Security audit passed
|
|
477
|
+
- [ ] Accessibility WCAG 2.1 AA compliant
|
|
478
|
+
- [ ] Documentation complete
|
|
479
|
+
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
</details>
|
|
483
|
+
|
|
484
|
+
### Step 5: Task Breakdown
|
|
485
|
+
|
|
486
|
+
**Agent:** product-technical
|
|
487
|
+
|
|
488
|
+
**Output:** `TODOs.md`
|
|
489
|
+
|
|
490
|
+
<details>
|
|
491
|
+
<summary>TODOs.md (Click to expand)</summary>
|
|
492
|
+
|
|
493
|
+
```markdown
|
|
494
|
+
# TODOs: User Favorites Feature
|
|
495
|
+
|
|
496
|
+
**Feature ID:** F-007
|
|
497
|
+
**Session:** P-007-user-favorites
|
|
498
|
+
|
|
499
|
+
## Phase 2: Implementation
|
|
500
|
+
|
|
501
|
+
### PB-001: Database Schema & Migration
|
|
502
|
+
|
|
503
|
+
- **Description**: Create favorites table with indexes
|
|
504
|
+
- **Assignee**: @db-engineer
|
|
505
|
+
- **Estimated**: 2 hours
|
|
506
|
+
- **Dependencies**: None
|
|
507
|
+
- **Acceptance**:
|
|
508
|
+
- [ ] Migration file created
|
|
509
|
+
- [ ] Table with all fields
|
|
510
|
+
- [ ] Foreign keys configured
|
|
511
|
+
- [ ] Indexes created
|
|
512
|
+
- [ ] Migration tested (up/down)
|
|
513
|
+
|
|
514
|
+
### PB-002: Zod Validation Schemas
|
|
515
|
+
|
|
516
|
+
- **Description**: Create Zod schemas for favorite operations
|
|
517
|
+
- **Assignee**: @node-typescript-engineer
|
|
518
|
+
- **Estimated**: 1 hour
|
|
519
|
+
- **Dependencies**: None
|
|
520
|
+
- **Acceptance**:
|
|
521
|
+
- [ ] `createFavoriteSchema`
|
|
522
|
+
- [ ] `getFavoritesSchema` (pagination)
|
|
523
|
+
- [ ] Types exported via `z.infer`
|
|
524
|
+
- [ ] Unit tests for schemas
|
|
525
|
+
|
|
526
|
+
### PB-003: Favorite Model
|
|
527
|
+
|
|
528
|
+
- **Description**: Implement FavoriteModel extending BaseModel
|
|
529
|
+
- **Assignee**: @db-engineer
|
|
530
|
+
- **Estimated**: 3 hours
|
|
531
|
+
- **Dependencies**: PB-001
|
|
532
|
+
- **Acceptance**:
|
|
533
|
+
- [ ] Model class extends BaseModel
|
|
534
|
+
- [ ] CRUD methods implemented
|
|
535
|
+
- [ ] Soft delete support
|
|
536
|
+
- [ ] 90%+ test coverage
|
|
537
|
+
- [ ] JSDoc documentation
|
|
538
|
+
|
|
539
|
+
### PB-004: Favorite Service
|
|
540
|
+
|
|
541
|
+
- **Description**: Business logic for favorites management
|
|
542
|
+
- **Assignee**: @node-typescript-engineer
|
|
543
|
+
- **Estimated**: 4 hours
|
|
544
|
+
- **Dependencies**: PB-002, PB-003
|
|
545
|
+
- **Acceptance**:
|
|
546
|
+
- [ ] Service extends BaseCrudService
|
|
547
|
+
- [ ] `addFavorite()` with limit checking
|
|
548
|
+
- [ ] `removeFavorite()` (soft delete)
|
|
549
|
+
- [ ] `getFavorites()` with pagination
|
|
550
|
+
- [ ] `isFavorite()` check
|
|
551
|
+
- [ ] `getFavoriteCount()` for limits
|
|
552
|
+
- [ ] Error handling
|
|
553
|
+
- [ ] 90%+ test coverage
|
|
554
|
+
|
|
555
|
+
### PB-005: API Routes
|
|
556
|
+
|
|
557
|
+
- **Description**: Hono endpoints for favorites
|
|
558
|
+
- **Assignee**: @hono-engineer
|
|
559
|
+
- **Estimated**: 3 hours
|
|
560
|
+
- **Dependencies**: PB-004
|
|
561
|
+
- **Acceptance**:
|
|
562
|
+
- [ ] POST /api/favorites
|
|
563
|
+
- [ ] DELETE /api/favorites/:entityId
|
|
564
|
+
- [ ] GET /api/favorites (paginated)
|
|
565
|
+
- [ ] GET /api/favorites/check/:entityId
|
|
566
|
+
- [ ] Zod validation middleware
|
|
567
|
+
- [ ] Authentication middleware
|
|
568
|
+
- [ ] Rate limiting (10/min)
|
|
569
|
+
- [ ] Integration tests
|
|
570
|
+
- [ ] API documentation
|
|
571
|
+
|
|
572
|
+
### PB-006: React Components
|
|
573
|
+
|
|
574
|
+
- **Description**: UI components for favorites
|
|
575
|
+
- **Assignee**: @react-senior-dev
|
|
576
|
+
- **Estimated**: 4 hours
|
|
577
|
+
- **Dependencies**: PB-005
|
|
578
|
+
- **Acceptance**:
|
|
579
|
+
- [ ] `FavoriteButton` component
|
|
580
|
+
- [ ] `FavoritesList` component
|
|
581
|
+
- [ ] `FavoritesEmpty` component
|
|
582
|
+
- [ ] Accessible (ARIA labels)
|
|
583
|
+
- [ ] Responsive design
|
|
584
|
+
- [ ] Unit tests
|
|
585
|
+
- [ ] Storybook stories
|
|
586
|
+
|
|
587
|
+
### PB-007: TanStack Query Integration
|
|
588
|
+
|
|
589
|
+
- **Description**: Server state management for favorites
|
|
590
|
+
- **Assignee**: @astro-engineer
|
|
591
|
+
- **Estimated**: 3 hours
|
|
592
|
+
- **Dependencies**: PB-005, PB-006
|
|
593
|
+
- **Acceptance**:
|
|
594
|
+
- [ ] Query hooks for favorites
|
|
595
|
+
- [ ] Mutation hooks (add/remove)
|
|
596
|
+
- [ ] Optimistic updates
|
|
597
|
+
- [ ] Cache invalidation
|
|
598
|
+
- [ ] Error handling
|
|
599
|
+
- [ ] Loading states
|
|
600
|
+
|
|
601
|
+
### PB-008: Favorites Page
|
|
602
|
+
|
|
603
|
+
- **Description**: Dedicated page for favorites list
|
|
604
|
+
- **Assignee**: @astro-engineer
|
|
605
|
+
- **Estimated**: 3 hours
|
|
606
|
+
- **Dependencies**: PB-007
|
|
607
|
+
- **Acceptance**:
|
|
608
|
+
- [ ] `/favorites` route
|
|
609
|
+
- [ ] Server-side rendering
|
|
610
|
+
- [ ] Pagination
|
|
611
|
+
- [ ] Empty state
|
|
612
|
+
- [ ] Remove functionality
|
|
613
|
+
- [ ] Responsive design
|
|
614
|
+
- [ ] SEO meta tags
|
|
615
|
+
|
|
616
|
+
## Phase 3: Validation
|
|
617
|
+
|
|
618
|
+
### PB-009: E2E Testing
|
|
619
|
+
|
|
620
|
+
- **Description**: End-to-end tests for favorites flow
|
|
621
|
+
- **Assignee**: @qa-engineer
|
|
622
|
+
- **Estimated**: 3 hours
|
|
623
|
+
- **Dependencies**: PB-008
|
|
624
|
+
- **Acceptance**:
|
|
625
|
+
- [ ] Test: Add favorite from search
|
|
626
|
+
- [ ] Test: Remove from favorites page
|
|
627
|
+
- [ ] Test: Limit enforcement (50)
|
|
628
|
+
- [ ] Test: Guest user flow
|
|
629
|
+
- [ ] Test: Authentication required
|
|
630
|
+
- [ ] All tests pass
|
|
631
|
+
|
|
632
|
+
### PB-010: Quality Check
|
|
633
|
+
|
|
634
|
+
- **Description**: Run comprehensive quality checks
|
|
635
|
+
- **Assignee**: @tech-lead
|
|
636
|
+
- **Estimated**: 2 hours
|
|
637
|
+
- **Dependencies**: PB-009
|
|
638
|
+
- **Acceptance**:
|
|
639
|
+
- [ ] Code review passed
|
|
640
|
+
- [ ] 90%+ test coverage
|
|
641
|
+
- [ ] No lint errors
|
|
642
|
+
- [ ] No type errors
|
|
643
|
+
- [ ] Performance benchmarks met
|
|
644
|
+
- [ ] Security audit passed
|
|
645
|
+
- [ ] Accessibility audit passed
|
|
646
|
+
|
|
647
|
+
## Phase 4: Finalization
|
|
648
|
+
|
|
649
|
+
### PB-011: API Documentation
|
|
650
|
+
|
|
651
|
+
- **Description**: Document favorites API endpoints
|
|
652
|
+
- **Assignee**: @tech-writer
|
|
653
|
+
- **Estimated**: 2 hours
|
|
654
|
+
- **Dependencies**: PB-010
|
|
655
|
+
- **Acceptance**:
|
|
656
|
+
- [ ] OpenAPI/Swagger docs
|
|
657
|
+
- [ ] Request/response examples
|
|
658
|
+
- [ ] Error codes documented
|
|
659
|
+
- [ ] Rate limits documented
|
|
660
|
+
|
|
661
|
+
### PB-012: User Documentation
|
|
662
|
+
|
|
663
|
+
- **Description**: User-facing documentation
|
|
664
|
+
- **Assignee**: @tech-writer
|
|
665
|
+
- **Estimated**: 1 hour
|
|
666
|
+
- **Dependencies**: PB-010
|
|
667
|
+
- **Acceptance**:
|
|
668
|
+
- [ ] Feature guide created
|
|
669
|
+
- [ ] Screenshots added
|
|
670
|
+
- [ ] FAQ section
|
|
671
|
+
|
|
672
|
+
### PB-013: CHANGELOG Update
|
|
673
|
+
|
|
674
|
+
- **Description**: Add feature to changelog
|
|
675
|
+
- **Assignee**: @tech-writer
|
|
676
|
+
- **Estimated**: 0.5 hours
|
|
677
|
+
- **Dependencies**: PB-011, PB-012
|
|
678
|
+
- **Acceptance**:
|
|
679
|
+
- [ ] Entry in CHANGELOG.md
|
|
680
|
+
- [ ] Follows Keep a Changelog format
|
|
681
|
+
- [ ] Links to documentation
|
|
682
|
+
|
|
683
|
+
## Summary
|
|
684
|
+
|
|
685
|
+
- **Total Tasks**: 13
|
|
686
|
+
- **Total Estimated**: 31.5 hours
|
|
687
|
+
- **Backend**: 10 hours (PB-001 to PB-003)
|
|
688
|
+
- **API**: 3 hours (PB-005)
|
|
689
|
+
- **Frontend**: 10 hours (PB-006 to PB-008)
|
|
690
|
+
- **QA**: 5 hours (PB-009 to PB-010)
|
|
691
|
+
- **Docs**: 3.5 hours (PB-011 to PB-013)
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
</details>
|
|
695
|
+
|
|
696
|
+
### Step 6: Planning Approval
|
|
697
|
+
|
|
698
|
+
**Participants:** User + tech-lead
|
|
699
|
+
|
|
700
|
+
**Review:**
|
|
701
|
+
|
|
702
|
+
- PDR approved ✅
|
|
703
|
+
- Technical approach validated ✅
|
|
704
|
+
- Task breakdown realistic ✅
|
|
705
|
+
- Risks acceptable ✅
|
|
706
|
+
|
|
707
|
+
**Decision:** Proceed to implementation
|
|
708
|
+
|
|
709
|
+
---
|
|
710
|
+
|
|
711
|
+
## Phase 2: Implementation
|
|
712
|
+
|
|
713
|
+
**Duration:** 3-4 days
|
|
714
|
+
**Methodology:** TDD (Red → Green → Refactor)
|
|
715
|
+
|
|
716
|
+
### Day 1: Backend Foundation
|
|
717
|
+
|
|
718
|
+
#### PB-001: Database Schema
|
|
719
|
+
|
|
720
|
+
**Agent:** db-engineer
|
|
721
|
+
|
|
722
|
+
```bash
|
|
723
|
+
# Create migration file
|
|
724
|
+
pnpm db:generate
|
|
725
|
+
|
|
726
|
+
# Migration: 0007_add_favorites.sql
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
```sql
|
|
730
|
+
CREATE TABLE favorites (
|
|
731
|
+
id TEXT PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
732
|
+
user_id TEXT NOT NULL,
|
|
733
|
+
entity_id TEXT NOT NULL,
|
|
734
|
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
735
|
+
is_active BOOLEAN DEFAULT TRUE,
|
|
736
|
+
|
|
737
|
+
CONSTRAINT fk_favorites_user
|
|
738
|
+
FOREIGN KEY (user_id)
|
|
739
|
+
REFERENCES users(id)
|
|
740
|
+
ON DELETE CASCADE,
|
|
741
|
+
|
|
742
|
+
CONSTRAINT fk_favorites_entity
|
|
743
|
+
FOREIGN KEY (entity_id)
|
|
744
|
+
REFERENCES entitys(id)
|
|
745
|
+
ON DELETE CASCADE,
|
|
746
|
+
|
|
747
|
+
CONSTRAINT unique_user_entity
|
|
748
|
+
UNIQUE (user_id, entity_id)
|
|
749
|
+
);
|
|
750
|
+
|
|
751
|
+
CREATE INDEX idx_favorites_user_active
|
|
752
|
+
ON favorites(user_id, is_active)
|
|
753
|
+
WHERE is_active = TRUE;
|
|
754
|
+
|
|
755
|
+
CREATE INDEX idx_favorites_entity
|
|
756
|
+
ON favorites(entity_id);
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
**Commit:**
|
|
760
|
+
|
|
761
|
+
```bash
|
|
762
|
+
git add packages/db/drizzle/migrations/0007_add_favorites.sql
|
|
763
|
+
git commit -m "feat(db): add favorites table schema [PB-001]"
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
#### PB-002: Validation Schemas
|
|
767
|
+
|
|
768
|
+
**Agent:** node-typescript-engineer
|
|
769
|
+
|
|
770
|
+
```typescript
|
|
771
|
+
// packages/schemas/src/favorite.schema.ts
|
|
772
|
+
import { z } from 'zod';
|
|
773
|
+
|
|
774
|
+
export const createFavoriteSchema = z.object({
|
|
775
|
+
entityId: z.string().uuid(),
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
export const getFavoritesSchema = z.object({
|
|
779
|
+
page: z.coerce.number().int().min(1).default(1),
|
|
780
|
+
limit: z.coerce.number().int().min(1).max(100).default(20),
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
export const checkFavoriteSchema = z.object({
|
|
784
|
+
entityId: z.string().uuid(),
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
// Types
|
|
788
|
+
export type CreateFavorite = z.infer<typeof createFavoriteSchema>;
|
|
789
|
+
export type GetFavorites = z.infer<typeof getFavoritesSchema>;
|
|
790
|
+
export type CheckFavorite = z.infer<typeof checkFavoriteSchema>;
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
**Tests:**
|
|
794
|
+
|
|
795
|
+
```typescript
|
|
796
|
+
// packages/schemas/test/favorite.schema.test.ts
|
|
797
|
+
describe('Favorite Schemas', () => {
|
|
798
|
+
it('should validate create favorite schema', () => {
|
|
799
|
+
const valid = createFavoriteSchema.parse({
|
|
800
|
+
entityId: '123e4567-e89b-12d3-a456-426614174000',
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
expect(valid.entityId).toBeDefined();
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
// ... more tests
|
|
807
|
+
});
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
**Commit:**
|
|
811
|
+
|
|
812
|
+
```bash
|
|
813
|
+
git add packages/schemas/src/favorite.schema.ts
|
|
814
|
+
git add packages/schemas/test/favorite.schema.test.ts
|
|
815
|
+
git commit -m "feat(schemas): add favorite validation schemas [PB-002]"
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
#### PB-003: Favorite Model (TDD)
|
|
819
|
+
|
|
820
|
+
**Agent:** db-engineer
|
|
821
|
+
|
|
822
|
+
**Red Phase:**
|
|
823
|
+
|
|
824
|
+
```typescript
|
|
825
|
+
// packages/db/test/models/favorite.model.test.ts
|
|
826
|
+
describe('FavoriteModel', () => {
|
|
827
|
+
it('should create favorite', async () => {
|
|
828
|
+
const result = await favoriteModel.create({
|
|
829
|
+
userId: testUser.id,
|
|
830
|
+
entityId: testEntity.id,
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
expect(result.success).toBe(true);
|
|
834
|
+
expect(result.data?.userId).toBe(testUser.id);
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
// ... tests fail (model doesn't exist)
|
|
838
|
+
});
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
**Green Phase:**
|
|
842
|
+
|
|
843
|
+
```typescript
|
|
844
|
+
// packages/db/src/models/favorite.model.ts
|
|
845
|
+
import { BaseModel } from './base.model';
|
|
846
|
+
import type { Favorite } from '../schemas/favorite';
|
|
847
|
+
|
|
848
|
+
export class FavoriteModel extends BaseModel<Favorite> {
|
|
849
|
+
protected table = favoritesTable;
|
|
850
|
+
protected entityName = 'favorite';
|
|
851
|
+
|
|
852
|
+
async findByUser(userId: string): Promise<Favorite[]> {
|
|
853
|
+
return this.db
|
|
854
|
+
.select()
|
|
855
|
+
.from(this.table)
|
|
856
|
+
.where(
|
|
857
|
+
and(
|
|
858
|
+
eq(this.table.userId, userId),
|
|
859
|
+
eq(this.table.isActive, true)
|
|
860
|
+
)
|
|
861
|
+
);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
async softDelete(id: string): Promise<void> {
|
|
865
|
+
await this.db
|
|
866
|
+
.update(this.table)
|
|
867
|
+
.set({ isActive: false })
|
|
868
|
+
.where(eq(this.table.id, id));
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
**Refactor Phase:**
|
|
874
|
+
|
|
875
|
+
- Extract common queries
|
|
876
|
+
- Add JSDoc
|
|
877
|
+
- Optimize indexes usage
|
|
878
|
+
|
|
879
|
+
**Commit:**
|
|
880
|
+
|
|
881
|
+
```bash
|
|
882
|
+
git add packages/db/src/models/favorite.model.ts
|
|
883
|
+
git add packages/db/test/models/favorite.model.test.ts
|
|
884
|
+
git commit -m "feat(db): implement favorite model with tests [PB-003]"
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
### Day 2: Service & API
|
|
888
|
+
|
|
889
|
+
#### PB-004: Favorite Service (TDD)
|
|
890
|
+
|
|
891
|
+
**Agent:** node-typescript-engineer
|
|
892
|
+
|
|
893
|
+
```typescript
|
|
894
|
+
// packages/service-core/src/favorite.service.ts
|
|
895
|
+
export class FavoriteService extends BaseCrudService<
|
|
896
|
+
Favorite,
|
|
897
|
+
FavoriteModel,
|
|
898
|
+
CreateFavorite,
|
|
899
|
+
never, // No update
|
|
900
|
+
GetFavorites
|
|
901
|
+
> {
|
|
902
|
+
private readonly MAX_FAVORITES = 50;
|
|
903
|
+
|
|
904
|
+
async addFavorite(input: {
|
|
905
|
+
userId: string;
|
|
906
|
+
entityId: string;
|
|
907
|
+
}): Promise<Result<Favorite>> {
|
|
908
|
+
// Check limit
|
|
909
|
+
const count = await this.getFavoriteCount({ userId: input.userId });
|
|
910
|
+
if (count >= this.MAX_FAVORITES) {
|
|
911
|
+
return {
|
|
912
|
+
success: false,
|
|
913
|
+
error: {
|
|
914
|
+
code: ServiceErrorCode.VALIDATION_ERROR,
|
|
915
|
+
message: `Maximum favorites reached (${this.MAX_FAVORITES})`,
|
|
916
|
+
},
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// Check if exists (idempotent)
|
|
921
|
+
const existing = await this.model.findOne({
|
|
922
|
+
userId: input.userId,
|
|
923
|
+
entityId: input.entityId,
|
|
924
|
+
});
|
|
925
|
+
|
|
926
|
+
if (existing) {
|
|
927
|
+
return { success: true, data: existing };
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// Create
|
|
931
|
+
return this.model.create({
|
|
932
|
+
userId: input.userId,
|
|
933
|
+
entityId: input.entityId,
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// ... other methods
|
|
938
|
+
}
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
**Commit:**
|
|
942
|
+
|
|
943
|
+
```bash
|
|
944
|
+
git add packages/service-core/src/favorite.service.ts
|
|
945
|
+
git add packages/service-core/test/favorite.service.test.ts
|
|
946
|
+
git commit -m "feat(service): add favorite CRUD service [PB-004]"
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
#### PB-005: API Routes
|
|
950
|
+
|
|
951
|
+
**Agent:** hono-engineer
|
|
952
|
+
|
|
953
|
+
```typescript
|
|
954
|
+
// apps/api/src/routes/favorites.ts
|
|
955
|
+
import { Hono } from 'hono';
|
|
956
|
+
import { zValidator } from '@hono/zod-validator';
|
|
957
|
+
import { requireAuth } from '../middleware/auth';
|
|
958
|
+
import { rateLimit } from '../middleware/rate-limit';
|
|
959
|
+
|
|
960
|
+
const app = new Hono();
|
|
961
|
+
|
|
962
|
+
app.use('*', requireAuth());
|
|
963
|
+
app.use('*', rateLimit({ max: 10, window: '1m' }));
|
|
964
|
+
|
|
965
|
+
// Add favorite
|
|
966
|
+
app.post(
|
|
967
|
+
'/',
|
|
968
|
+
zValidator('json', createFavoriteSchema),
|
|
969
|
+
async (c) => {
|
|
970
|
+
const { entityId } = c.req.valid('json');
|
|
971
|
+
const userId = c.get('userId');
|
|
972
|
+
|
|
973
|
+
const service = new FavoriteService(c.get('ctx'));
|
|
974
|
+
const result = await service.addFavorite({ userId, entityId });
|
|
975
|
+
|
|
976
|
+
if (!result.success) {
|
|
977
|
+
return c.json(result.error, 400);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
return c.json(result.data, 201);
|
|
981
|
+
}
|
|
982
|
+
);
|
|
983
|
+
|
|
984
|
+
// Remove favorite
|
|
985
|
+
app.delete('/:entityId', async (c) => {
|
|
986
|
+
const entityId = c.req.param('entityId');
|
|
987
|
+
const userId = c.get('userId');
|
|
988
|
+
|
|
989
|
+
const service = new FavoriteService(c.get('ctx'));
|
|
990
|
+
const result = await service.removeFavorite({ userId, entityId });
|
|
991
|
+
|
|
992
|
+
if (!result.success) {
|
|
993
|
+
return c.json(result.error, 400);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
return c.json({ success: true }, 200);
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
// List favorites (paginated)
|
|
1000
|
+
app.get(
|
|
1001
|
+
'/',
|
|
1002
|
+
zValidator('query', getFavoritesSchema),
|
|
1003
|
+
async (c) => {
|
|
1004
|
+
const { page, limit } = c.req.valid('query');
|
|
1005
|
+
const userId = c.get('userId');
|
|
1006
|
+
|
|
1007
|
+
const service = new FavoriteService(c.get('ctx'));
|
|
1008
|
+
const result = await service.getFavorites({
|
|
1009
|
+
userId,
|
|
1010
|
+
page,
|
|
1011
|
+
limit,
|
|
1012
|
+
});
|
|
1013
|
+
|
|
1014
|
+
if (!result.success) {
|
|
1015
|
+
return c.json(result.error, 400);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
return c.json(result.data);
|
|
1019
|
+
}
|
|
1020
|
+
);
|
|
1021
|
+
|
|
1022
|
+
export default app;
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
**Commit:**
|
|
1026
|
+
|
|
1027
|
+
```bash
|
|
1028
|
+
git add apps/api/src/routes/favorites.ts
|
|
1029
|
+
git add apps/api/test/routes/favorites.test.ts
|
|
1030
|
+
git commit -m "feat(api): add favorite endpoints with auth & rate limiting [PB-005]"
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
### Day 3-4: Frontend
|
|
1034
|
+
|
|
1035
|
+
#### PB-006: React Components
|
|
1036
|
+
|
|
1037
|
+
**Agent:** react-senior-dev
|
|
1038
|
+
|
|
1039
|
+
```tsx
|
|
1040
|
+
// apps/web/src/components/FavoriteButton.tsx
|
|
1041
|
+
import { useState } from 'react';
|
|
1042
|
+
import { HeartIcon } from '@heroicons/react/24/outline';
|
|
1043
|
+
import { HeartIcon as HeartSolidIcon } from '@heroicons/react/24/solid';
|
|
1044
|
+
|
|
1045
|
+
interface FavoriteButtonProps {
|
|
1046
|
+
entityId: string;
|
|
1047
|
+
initialIsFavorite: boolean;
|
|
1048
|
+
onToggle: (isFavorite: boolean) => Promise<void>;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
export function FavoriteButton({
|
|
1052
|
+
entityId,
|
|
1053
|
+
initialIsFavorite,
|
|
1054
|
+
onToggle,
|
|
1055
|
+
}: FavoriteButtonProps) {
|
|
1056
|
+
const [isFavorite, setIsFavorite] = useState(initialIsFavorite);
|
|
1057
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
1058
|
+
|
|
1059
|
+
const handleClick = async (e: React.MouseEvent) => {
|
|
1060
|
+
e.preventDefault(); // Don't navigate if in a link
|
|
1061
|
+
|
|
1062
|
+
setIsLoading(true);
|
|
1063
|
+
const newState = !isFavorite;
|
|
1064
|
+
|
|
1065
|
+
// Optimistic update
|
|
1066
|
+
setIsFavorite(newState);
|
|
1067
|
+
|
|
1068
|
+
try {
|
|
1069
|
+
await onToggle(newState);
|
|
1070
|
+
} catch (error) {
|
|
1071
|
+
// Revert on error
|
|
1072
|
+
setIsFavorite(!newState);
|
|
1073
|
+
console.error('Failed to toggle favorite:', error);
|
|
1074
|
+
} finally {
|
|
1075
|
+
setIsLoading(false);
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
|
|
1079
|
+
return (
|
|
1080
|
+
<button
|
|
1081
|
+
onClick={handleClick}
|
|
1082
|
+
disabled={isLoading}
|
|
1083
|
+
className="favorite-button"
|
|
1084
|
+
aria-label={isFavorite ? 'Remove from favorites' : 'Add to favorites'}
|
|
1085
|
+
aria-pressed={isFavorite}
|
|
1086
|
+
>
|
|
1087
|
+
{isFavorite ? (
|
|
1088
|
+
<HeartSolidIcon className="w-6 h-6 text-red-500" />
|
|
1089
|
+
) : (
|
|
1090
|
+
<HeartIcon className="w-6 h-6 text-gray-400 hover:text-red-500" />
|
|
1091
|
+
)}
|
|
1092
|
+
</button>
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
```
|
|
1096
|
+
|
|
1097
|
+
**Commit:**
|
|
1098
|
+
|
|
1099
|
+
```bash
|
|
1100
|
+
git add apps/web/src/components/FavoriteButton.tsx
|
|
1101
|
+
git add apps/web/test/components/FavoriteButton.test.tsx
|
|
1102
|
+
git commit -m "feat(web): add favorite button component with optimistic updates [PB-006]"
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1105
|
+
#### PB-007: TanStack Query Integration
|
|
1106
|
+
|
|
1107
|
+
**Agent:** astro-engineer
|
|
1108
|
+
|
|
1109
|
+
```typescript
|
|
1110
|
+
// apps/web/src/hooks/useFavorites.ts
|
|
1111
|
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
1112
|
+
|
|
1113
|
+
export function useFavorites() {
|
|
1114
|
+
const queryClient = useQueryClient();
|
|
1115
|
+
|
|
1116
|
+
const { data: favorites, isLoading } = useQuery({
|
|
1117
|
+
queryKey: ['favorites'],
|
|
1118
|
+
queryFn: async () => {
|
|
1119
|
+
const res = await fetch('/api/favorites');
|
|
1120
|
+
if (!res.ok) throw new Error('Failed to fetch favorites');
|
|
1121
|
+
return res.json();
|
|
1122
|
+
},
|
|
1123
|
+
});
|
|
1124
|
+
|
|
1125
|
+
const addFavorite = useMutation({
|
|
1126
|
+
mutationFn: async (entityId: string) => {
|
|
1127
|
+
const res = await fetch('/api/favorites', {
|
|
1128
|
+
method: 'POST',
|
|
1129
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1130
|
+
body: JSON.stringify({ entityId }),
|
|
1131
|
+
});
|
|
1132
|
+
if (!res.ok) throw new Error('Failed to add favorite');
|
|
1133
|
+
return res.json();
|
|
1134
|
+
},
|
|
1135
|
+
// Optimistic update
|
|
1136
|
+
onMutate: async (entityId) => {
|
|
1137
|
+
await queryClient.cancelQueries({ queryKey: ['favorites'] });
|
|
1138
|
+
const previous = queryClient.getQueryData(['favorites']);
|
|
1139
|
+
|
|
1140
|
+
queryClient.setQueryData(['favorites'], (old: any) => ({
|
|
1141
|
+
...old,
|
|
1142
|
+
items: [...(old?.items || []), { entityId }],
|
|
1143
|
+
}));
|
|
1144
|
+
|
|
1145
|
+
return { previous };
|
|
1146
|
+
},
|
|
1147
|
+
// Revert on error
|
|
1148
|
+
onError: (err, variables, context) => {
|
|
1149
|
+
if (context?.previous) {
|
|
1150
|
+
queryClient.setQueryData(['favorites'], context.previous);
|
|
1151
|
+
}
|
|
1152
|
+
},
|
|
1153
|
+
// Refetch on success
|
|
1154
|
+
onSettled: () => {
|
|
1155
|
+
queryClient.invalidateQueries({ queryKey: ['favorites'] });
|
|
1156
|
+
},
|
|
1157
|
+
});
|
|
1158
|
+
|
|
1159
|
+
return {
|
|
1160
|
+
favorites: favorites?.items || [],
|
|
1161
|
+
isLoading,
|
|
1162
|
+
addFavorite: addFavorite.mutate,
|
|
1163
|
+
removeFavorite: removeFavorite.mutate,
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
**Commit:**
|
|
1169
|
+
|
|
1170
|
+
```bash
|
|
1171
|
+
git add apps/web/src/hooks/useFavorites.ts
|
|
1172
|
+
git commit -m "feat(web): add TanStack Query hooks for favorites [PB-007]"
|
|
1173
|
+
```
|
|
1174
|
+
|
|
1175
|
+
#### PB-008: Favorites Page
|
|
1176
|
+
|
|
1177
|
+
**Agent:** astro-engineer
|
|
1178
|
+
|
|
1179
|
+
```astro
|
|
1180
|
+
---
|
|
1181
|
+
// apps/web/src/pages/favorites.astro
|
|
1182
|
+
import Layout from '../layouts/Layout.astro';
|
|
1183
|
+
import { FavoritesList } from '../components/FavoritesList';
|
|
1184
|
+
---
|
|
1185
|
+
|
|
1186
|
+
<Layout title="My Favorites | Project">
|
|
1187
|
+
<main class="container mx-auto px-4 py-8">
|
|
1188
|
+
<h1 class="text-3xl font-bold mb-6">My Favorites</h1>
|
|
1189
|
+
|
|
1190
|
+
<FavoritesList client:load />
|
|
1191
|
+
</main>
|
|
1192
|
+
</Layout>
|
|
1193
|
+
```
|
|
1194
|
+
|
|
1195
|
+
**Commit:**
|
|
1196
|
+
|
|
1197
|
+
```bash
|
|
1198
|
+
git add apps/web/src/pages/favorites.astro
|
|
1199
|
+
git add apps/web/src/components/FavoritesList.tsx
|
|
1200
|
+
git commit -m "feat(web): add favorites page with SSR [PB-008]"
|
|
1201
|
+
```
|
|
1202
|
+
|
|
1203
|
+
---
|
|
1204
|
+
|
|
1205
|
+
## Phase 3: Validation
|
|
1206
|
+
|
|
1207
|
+
**Duration:** 1 day
|
|
1208
|
+
|
|
1209
|
+
### PB-009: E2E Testing
|
|
1210
|
+
|
|
1211
|
+
**Agent:** qa-engineer
|
|
1212
|
+
|
|
1213
|
+
```typescript
|
|
1214
|
+
// apps/web/e2e/favorites.spec.ts
|
|
1215
|
+
import { test, expect } from '@playwright/test';
|
|
1216
|
+
|
|
1217
|
+
test.describe('Favorites Feature', () => {
|
|
1218
|
+
test('should add favorite from search results', async ({ page }) => {
|
|
1219
|
+
await page.goto('/search');
|
|
1220
|
+
|
|
1221
|
+
// Click first favorite button
|
|
1222
|
+
const favoriteBtn = page.locator('[aria-label="Add to favorites"]').first();
|
|
1223
|
+
await favoriteBtn.click();
|
|
1224
|
+
|
|
1225
|
+
// Should show filled heart
|
|
1226
|
+
await expect(favoriteBtn).toHaveAttribute('aria-pressed', 'true');
|
|
1227
|
+
|
|
1228
|
+
// Should show toast notification
|
|
1229
|
+
await expect(page.locator('text=Added to favorites')).toBeVisible();
|
|
1230
|
+
});
|
|
1231
|
+
|
|
1232
|
+
test('should remove favorite from favorites page', async ({ page }) => {
|
|
1233
|
+
await page.goto('/favorites');
|
|
1234
|
+
|
|
1235
|
+
const removeBtn = page.locator('[aria-label="Remove from favorites"]').first();
|
|
1236
|
+
await removeBtn.click();
|
|
1237
|
+
|
|
1238
|
+
await expect(page.locator('text=Removed from favorites')).toBeVisible();
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1241
|
+
test('should enforce 50 favorites limit', async ({ page }) => {
|
|
1242
|
+
// Add 50 favorites...
|
|
1243
|
+
// Try to add 51st
|
|
1244
|
+
await page.goto('/entity/123');
|
|
1245
|
+
await page.locator('[aria-label="Add to favorites"]').click();
|
|
1246
|
+
|
|
1247
|
+
await expect(page.locator('text=Maximum favorites reached')).toBeVisible();
|
|
1248
|
+
});
|
|
1249
|
+
});
|
|
1250
|
+
```
|
|
1251
|
+
|
|
1252
|
+
**Commit:**
|
|
1253
|
+
|
|
1254
|
+
```bash
|
|
1255
|
+
git add apps/web/e2e/favorites.spec.ts
|
|
1256
|
+
git commit -m "test(e2e): add favorites feature tests [PB-009]"
|
|
1257
|
+
```
|
|
1258
|
+
|
|
1259
|
+
### PB-010: Quality Check
|
|
1260
|
+
|
|
1261
|
+
**Agent:** tech-lead
|
|
1262
|
+
|
|
1263
|
+
```bash
|
|
1264
|
+
# Run all checks
|
|
1265
|
+
pnpm typecheck # ✅ No errors
|
|
1266
|
+
pnpm lint # ✅ No errors
|
|
1267
|
+
pnpm test:coverage # ✅ 94% coverage
|
|
1268
|
+
|
|
1269
|
+
# Run quality check command
|
|
1270
|
+
pnpm quality-check
|
|
1271
|
+
```
|
|
1272
|
+
|
|
1273
|
+
**Results:**
|
|
1274
|
+
|
|
1275
|
+
- ✅ Code review: All patterns followed
|
|
1276
|
+
- ✅ Test coverage: 94% (exceeds 90% minimum)
|
|
1277
|
+
- ✅ Performance: All endpoints < 200ms
|
|
1278
|
+
- ✅ Security: Auth enforced, rate limiting active
|
|
1279
|
+
- ✅ Accessibility: WCAG 2.1 AA compliant
|
|
1280
|
+
|
|
1281
|
+
**Approval:** Ready for finalization
|
|
1282
|
+
|
|
1283
|
+
---
|
|
1284
|
+
|
|
1285
|
+
## Phase 4: Finalization
|
|
1286
|
+
|
|
1287
|
+
**Duration:** 0.5 day
|
|
1288
|
+
|
|
1289
|
+
### PB-011: API Documentation
|
|
1290
|
+
|
|
1291
|
+
**Agent:** tech-writer
|
|
1292
|
+
|
|
1293
|
+
```yaml
|
|
1294
|
+
# docs/api/favorites.yml
|
|
1295
|
+
paths:
|
|
1296
|
+
/api/favorites:
|
|
1297
|
+
post:
|
|
1298
|
+
summary: Add favorite
|
|
1299
|
+
security:
|
|
1300
|
+
- bearerAuth: []
|
|
1301
|
+
requestBody:
|
|
1302
|
+
content:
|
|
1303
|
+
application/json:
|
|
1304
|
+
schema:
|
|
1305
|
+
type: object
|
|
1306
|
+
properties:
|
|
1307
|
+
entityId:
|
|
1308
|
+
type: string
|
|
1309
|
+
format: uuid
|
|
1310
|
+
responses:
|
|
1311
|
+
201:
|
|
1312
|
+
description: Favorite added
|
|
1313
|
+
400:
|
|
1314
|
+
description: Limit reached or invalid request
|
|
1315
|
+
401:
|
|
1316
|
+
description: Unauthorized
|
|
1317
|
+
```
|
|
1318
|
+
|
|
1319
|
+
**Commit:**
|
|
1320
|
+
|
|
1321
|
+
```bash
|
|
1322
|
+
git add docs/api/favorites.yml
|
|
1323
|
+
git commit -m "docs(api): add favorites endpoint documentation [PB-011]"
|
|
1324
|
+
```
|
|
1325
|
+
|
|
1326
|
+
### PB-012: User Documentation
|
|
1327
|
+
|
|
1328
|
+
**Agent:** tech-writer
|
|
1329
|
+
|
|
1330
|
+
```markdown
|
|
1331
|
+
# docs/features/favorites.md
|
|
1332
|
+
|
|
1333
|
+
## User Favorites
|
|
1334
|
+
|
|
1335
|
+
Save your favorite entitys for quick access later.
|
|
1336
|
+
|
|
1337
|
+
### How to Use
|
|
1338
|
+
|
|
1339
|
+
1. **Add Favorite**: Click the heart icon (♡) on any entity card
|
|
1340
|
+
2. **View Favorites**: Navigate to "Favorites" from the menu
|
|
1341
|
+
3. **Remove Favorite**: Click the filled heart (❤️) to remove
|
|
1342
|
+
|
|
1343
|
+
### Limits
|
|
1344
|
+
|
|
1345
|
+
- **Logged Users**: Up to 50 favorites
|
|
1346
|
+
- **Guest Users**: Up to 5 favorites (lost on logout)
|
|
1347
|
+
|
|
1348
|
+
### FAQs
|
|
1349
|
+
|
|
1350
|
+
**Q: Can I share my favorites?**
|
|
1351
|
+
A: Not yet, but this feature is planned for a future release.
|
|
1352
|
+
```
|
|
1353
|
+
|
|
1354
|
+
**Commit:**
|
|
1355
|
+
|
|
1356
|
+
```bash
|
|
1357
|
+
git add docs/features/favorites.md
|
|
1358
|
+
git commit -m "docs(features): add user guide for favorites [PB-012]"
|
|
1359
|
+
```
|
|
1360
|
+
|
|
1361
|
+
### PB-013: CHANGELOG Update
|
|
1362
|
+
|
|
1363
|
+
**Agent:** tech-writer
|
|
1364
|
+
|
|
1365
|
+
```markdown
|
|
1366
|
+
# CHANGELOG.md
|
|
1367
|
+
|
|
1368
|
+
## [Unreleased]
|
|
1369
|
+
|
|
1370
|
+
### Added
|
|
1371
|
+
|
|
1372
|
+
- **User Favorites** - Users can now save favorite entitys
|
|
1373
|
+
- Add/remove favorites with heart icon
|
|
1374
|
+
- Dedicated favorites page with pagination
|
|
1375
|
+
- Max 50 favorites per logged user, 5 for guests
|
|
1376
|
+
- Optimistic UI updates for instant feedback
|
|
1377
|
+
- Rate limiting to prevent abuse (10 operations/min)
|
|
1378
|
+
- See [Favorites Guide](docs/features/favorites.md) for details
|
|
1379
|
+
```
|
|
1380
|
+
|
|
1381
|
+
**Commit:**
|
|
1382
|
+
|
|
1383
|
+
```bash
|
|
1384
|
+
git add CHANGELOG.md
|
|
1385
|
+
git commit -m "docs(changelog): add favorites feature entry [PB-013]"
|
|
1386
|
+
```
|
|
1387
|
+
|
|
1388
|
+
---
|
|
1389
|
+
|
|
1390
|
+
## Results
|
|
1391
|
+
|
|
1392
|
+
### Metrics
|
|
1393
|
+
|
|
1394
|
+
- **Total Duration:** 4.5 days (36 hours actual vs 31.5 estimated)
|
|
1395
|
+
- **Commits:** 13 atomic commits (one per task)
|
|
1396
|
+
- **Test Coverage:** 94% (exceeded 90% target)
|
|
1397
|
+
- **Performance:**
|
|
1398
|
+
- Add favorite: 150ms avg
|
|
1399
|
+
- List favorites: 380ms avg
|
|
1400
|
+
- Both under targets ✅
|
|
1401
|
+
|
|
1402
|
+
### Deliverables
|
|
1403
|
+
|
|
1404
|
+
✅ Database migration
|
|
1405
|
+
✅ Backend services
|
|
1406
|
+
✅ API endpoints
|
|
1407
|
+
✅ Frontend components
|
|
1408
|
+
✅ Favorites page
|
|
1409
|
+
✅ E2E tests
|
|
1410
|
+
✅ Documentation
|
|
1411
|
+
✅ CHANGELOG update
|
|
1412
|
+
|
|
1413
|
+
### Pull Request
|
|
1414
|
+
|
|
1415
|
+
```bash
|
|
1416
|
+
# Create PR
|
|
1417
|
+
gh pr create \
|
|
1418
|
+
--title "feat: user favorites for entitys [F-007]" \
|
|
1419
|
+
--body "$(cat <<'EOF'
|
|
1420
|
+
## Summary
|
|
1421
|
+
|
|
1422
|
+
Implements user favorites feature allowing users to save and manage favorite entitys.
|
|
1423
|
+
|
|
1424
|
+
## Changes
|
|
1425
|
+
|
|
1426
|
+
- Database: `favorites` table with indexes
|
|
1427
|
+
- Backend: FavoriteService, FavoriteModel
|
|
1428
|
+
- API: 4 new endpoints with auth & rate limiting
|
|
1429
|
+
- Frontend: FavoriteButton, FavoritesList components
|
|
1430
|
+
- Tests: 94% coverage (Unit + Integration + E2E)
|
|
1431
|
+
|
|
1432
|
+
## Testing
|
|
1433
|
+
|
|
1434
|
+
- [x] All tests pass
|
|
1435
|
+
- [x] Manual testing completed
|
|
1436
|
+
- [x] E2E tests pass
|
|
1437
|
+
- [x] Performance benchmarks met
|
|
1438
|
+
|
|
1439
|
+
## Documentation
|
|
1440
|
+
|
|
1441
|
+
- [x] API docs updated
|
|
1442
|
+
- [x] User guide created
|
|
1443
|
+
- [x] CHANGELOG updated
|
|
1444
|
+
|
|
1445
|
+
## Related
|
|
1446
|
+
|
|
1447
|
+
- Planning Session: P-007
|
|
1448
|
+
- Feature ID: F-007
|
|
1449
|
+
- Tasks: PB-001 to PB-013
|
|
1450
|
+
|
|
1451
|
+
## Screenshots
|
|
1452
|
+
|
|
1453
|
+
(Screenshots of favorite button and favorites page)
|
|
1454
|
+
EOF
|
|
1455
|
+
)"
|
|
1456
|
+
```
|
|
1457
|
+
|
|
1458
|
+
### Deployment
|
|
1459
|
+
|
|
1460
|
+
```bash
|
|
1461
|
+
# After PR approval and merge
|
|
1462
|
+
git checkout main
|
|
1463
|
+
git pull
|
|
1464
|
+
|
|
1465
|
+
# Vercel auto-deploys from main branch
|
|
1466
|
+
# Migration runs automatically on deploy
|
|
1467
|
+
|
|
1468
|
+
# Verify deployment
|
|
1469
|
+
curl -f https://example.com/health || exit 1
|
|
1470
|
+
```
|
|
1471
|
+
|
|
1472
|
+
### Success Metrics (1 week after launch)
|
|
1473
|
+
|
|
1474
|
+
- ✅ 42% users added at least one favorite
|
|
1475
|
+
- ✅ 0.3% error rate (below 1% target)
|
|
1476
|
+
- ✅ Average 9 favorites per active user
|
|
1477
|
+
|
|
1478
|
+
---
|
|
1479
|
+
|
|
1480
|
+
## Lessons Learned
|
|
1481
|
+
|
|
1482
|
+
### What Went Well
|
|
1483
|
+
|
|
1484
|
+
1. **TDD Approach** - Caught bugs early, tests gave confidence
|
|
1485
|
+
2. **Atomic Commits** - Easy to review, clear history
|
|
1486
|
+
3. **Optimistic UI** - Great user experience
|
|
1487
|
+
4. **Planning Phase** - Clear requirements avoided scope creep
|
|
1488
|
+
|
|
1489
|
+
### Challenges
|
|
1490
|
+
|
|
1491
|
+
1. **Optimistic Updates** - Needed careful error handling
|
|
1492
|
+
2. **Rate Limiting** - Initial implementation too strict, relaxed to 10/min
|
|
1493
|
+
3. **Guest Migration** - Decided to keep simple (just prompt to login)
|
|
1494
|
+
|
|
1495
|
+
### Would Do Differently
|
|
1496
|
+
|
|
1497
|
+
- Add analytics events from the start (added in hotfix)
|
|
1498
|
+
- Include performance monitoring from day 1
|
|
1499
|
+
- More edge case testing for concurrent operations
|
|
1500
|
+
|
|
1501
|
+
---
|
|
1502
|
+
|
|
1503
|
+
*End of Example*
|
|
1504
|
+
|
|
1505
|
+
This example demonstrates a complete workflow following all standards, protocols, and best practices from the Project project.
|