Skip to content

Commit 62a979c

Browse files
author
Jez
committed
Merge PR ifindev#18: Replace hardcoded database ID with environment variable
2 parents 0e7b938 + b5c79d7 commit 62a979c

File tree

4 files changed

+320
-1
lines changed

4 files changed

+320
-1
lines changed

.dev.vars.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ NEXTJS_ENV=development
44
# Drizzle Kit credentials for D1
55
# Get your Account ID from: https://dash.cloudflare.com/ (right sidebar)
66
# Get your API Token from: https://dash.cloudflare.com/profile/api-tokens
7+
# Get your Database ID from: wrangler d1 list (or create with: wrangler d1 create <name>)
78
CLOUDFLARE_ACCOUNT_ID=your-account-id-here
9+
CLOUDFLARE_D1_DATABASE_ID=your-database-id-here
810
CLOUDFLARE_D1_TOKEN=your-api-token-here
911
CLOUDFLARE_R2_URL=your-r2-url-here
1012
CLOUDFLARE_API_TOKEN=your-api-token-here

PROJECT_BRIEF.md

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
# Project Brief: Fullstack Next.js + Cloudflare CRM
2+
3+
**Created**: 2025-11-08
4+
**Status**: Ready for Planning
5+
**Purpose**: Learning exercise to understand Next.js 15 + Cloudflare Workers integration
6+
7+
---
8+
9+
## Vision
10+
11+
A lightweight CRM built on the fullstack-next-cloudflare template to learn modern fullstack patterns: Next.js App Router, Cloudflare D1/R2, Server Actions, and module-sliced architecture.
12+
13+
---
14+
15+
## Problem/Opportunity
16+
17+
**Learning objective**: Understand how to build production-grade features on Cloudflare's edge platform by implementing real-world CRM functionality.
18+
19+
**Why CRM as the learning vehicle**:
20+
- Multi-entity relationships (contacts ↔ deals)
21+
- CRUD operations with validation
22+
- Data modeling with foreign keys
23+
- UI patterns (lists, forms, boards)
24+
- Follows existing architecture (todos module)
25+
26+
---
27+
28+
## Target Audience
29+
30+
- **Primary user**: You (Jez) - exploring the stack
31+
- **Scale**: Single user for learning (no multi-tenancy needed)
32+
- **Context**: Educational project, not production SaaS
33+
- **Data**: Can use synthetic/test data
34+
35+
---
36+
37+
## Core Functionality (MVP)
38+
39+
### 1. Contacts Module
40+
**Essential**:
41+
- ✅ Create, read, update, delete contacts
42+
- ✅ Fields: firstName, lastName, email, phone, company, jobTitle, notes
43+
- ✅ Search/filter by name, email, company
44+
- ✅ Tag system (many-to-many: contacts ↔ tags)
45+
- ✅ User-specific tags with colors
46+
47+
**Deferred to Phase 2** (keep MVP lean):
48+
- ❌ Activity timeline (calls, meetings, notes)
49+
- ❌ Avatar uploads to R2
50+
- ❌ Email integration
51+
- ❌ Import/export
52+
53+
### 2. Deals/Pipeline Module
54+
**Essential**:
55+
- ✅ Create, read, update, delete deals
56+
- ✅ Fields: title, value, currency, stage, expectedCloseDate, description
57+
- ✅ Link deal to contact (simple 1:1 relationship)
58+
- ✅ Pipeline board view (simple columns by stage)
59+
- ✅ Fixed stages: Prospecting → Qualification → Proposal → Negotiation → Closed Won/Lost
60+
61+
**Deferred to Phase 2**:
62+
- ❌ Custom user-defined stages
63+
- ❌ Drag-and-drop stage changes
64+
- ❌ Deal probability/forecasting
65+
- ❌ Multiple contacts per deal
66+
67+
### 3. Dashboard Integration
68+
**Essential**:
69+
- ✅ Add navigation to /dashboard/contacts and /dashboard/deals
70+
- ✅ Simple metrics cards (total contacts, active deals, pipeline value)
71+
72+
**Deferred**:
73+
- ❌ Charts/graphs
74+
- ❌ Activity feed
75+
- ❌ Advanced analytics
76+
77+
---
78+
79+
## Tech Stack (Validated)
80+
81+
Uses existing template stack - no changes needed:
82+
83+
- **Frontend**: Next.js 15.4.6 (App Router) + React 19 + TypeScript
84+
- **UI**: Tailwind v4 + shadcn/ui + Lucide icons
85+
- **Backend**: Cloudflare Workers with Static Assets (@opennextjs/cloudflare)
86+
- **Database**: Cloudflare D1 (SQLite) with Drizzle ORM
87+
- **Storage**: Cloudflare R2 (not using for MVP - deferred avatars)
88+
- **Auth**: Better Auth (already configured)
89+
- **Forms**: React Hook Form + Zod validation
90+
- **Deployment**: Cloudflare Workers (via GitHub Actions)
91+
92+
**Why this stack works for learning**:
93+
- ✅ Modern patterns (Server Actions, RSC)
94+
- ✅ Edge-first architecture
95+
- ✅ Type-safe end-to-end (TypeScript + Drizzle + Zod)
96+
- ✅ Template already has auth, DB, migrations configured
97+
- ✅ Follows module-sliced pattern (easy to extend)
98+
99+
---
100+
101+
## Research Findings
102+
103+
### Existing Template Analysis
104+
105+
**What's already built** (from /home/jez/Documents/fullstack-next-cloudflare-demo):
106+
-**Auth module**: Better Auth with email/password + Google OAuth
107+
-**Todos module**: Complete CRUD example with categories, priorities, status
108+
-**Database setup**: D1 + Drizzle + migrations working
109+
-**R2 integration**: Image upload pattern (in todos for cover images)
110+
-**Module architecture**: `src/modules/[feature]/` with actions/, components/, schemas/
111+
-**UI components**: 13 shadcn/ui components configured
112+
-**Deployment**: GitHub Actions workflow ready
113+
114+
**Pattern to follow**:
115+
The `src/modules/todos/` structure is the perfect blueprint:
116+
```
117+
todos/
118+
├── actions/ # Server actions (create, get, update, delete)
119+
├── components/ # UI components (form, card, list)
120+
├── models/ # Enums and types
121+
└── schemas/ # Drizzle + Zod schemas
122+
```
123+
124+
We'll replicate this for `contacts/` and `deals/` modules.
125+
126+
### Technical Validation
127+
128+
**✅ D1 Relational Data**:
129+
- Drizzle ORM supports foreign keys and joins
130+
- Template already has `todos → categories` relationship
131+
- Contacts ↔ Deals will work the same way
132+
133+
**✅ Many-to-Many Tags**:
134+
- Need junction table: `contacts_to_tags`
135+
- Drizzle example in their docs: https://orm.drizzle.team/docs/rqb#many-to-many
136+
137+
**✅ Server Actions Performance**:
138+
- Template uses server actions for all mutations
139+
- Edge runtime = fast globally
140+
- No API route boilerplate needed
141+
142+
**Known Challenges**:
143+
1. **Junction table queries** - Drizzle syntax for many-to-many can be verbose
144+
- Mitigation: Study existing `todos.categoryId` pattern, extend to junction table
145+
2. **Pipeline board UI** - Kanban layout without drag-drop library
146+
- Mitigation: Simple CSS Grid columns, manual stage update dropdown (defer drag-drop to Phase 2)
147+
3. **Search implementation** - D1 doesn't have full-text search
148+
- Mitigation: Use SQL `LIKE` queries for MVP (good enough for learning)
149+
150+
---
151+
152+
## Scope Validation
153+
154+
### Why Build This?
155+
**Learning objectives met**:
156+
- ✅ Practice module-sliced architecture
157+
- ✅ Understand Drizzle ORM relationships (1:1, many-to-many)
158+
- ✅ Learn Server Actions data mutation patterns
159+
- ✅ Explore D1 migrations workflow
160+
- ✅ Build complex forms with validation
161+
- ✅ Create dashboard visualizations
162+
- ✅ Deploy to Cloudflare edge
163+
164+
**Why NOT use existing CRM**:
165+
- This is about learning the stack, not production use
166+
- Building from scratch teaches architectural patterns
167+
- Template provides 80% foundation (auth, DB, UI), we add 20% (domain logic)
168+
169+
### Why This Scope?
170+
**MVP is deliberately minimal** to focus on learning core patterns:
171+
- 2 main entities (contacts, deals) = practice relationships
172+
- Tags system = practice many-to-many
173+
- Pipeline board = practice UI state management
174+
- Dashboard metrics = practice aggregations
175+
176+
**Deferred features** prevent scope creep:
177+
- Activity logging (complex timeline UI)
178+
- Avatars (R2 already demonstrated in todos)
179+
- Custom stages (adds complexity)
180+
- Advanced analytics (not core learning)
181+
182+
**Time investment** = ~6-8 hours (~6-8 minutes with Claude Code)
183+
- Realistic for learning project
184+
- Can complete in 1-2 sessions
185+
- Leaves room for experimentation
186+
187+
### What Could Go Wrong?
188+
189+
**Risk 1: Overcomplicating relationships**
190+
- *What*: Trying to add too many foreign keys (deals → contacts → companies → industries...)
191+
- *Mitigation*: Stick to MVP scope (contacts ↔ tags, deals → contacts). No nested hierarchies.
192+
193+
**Risk 2: UI perfectionism**
194+
- *What*: Spending hours on drag-and-drop Kanban, animations, etc.
195+
- *Mitigation*: Use simple table/grid layouts. Focus on functionality, not polish.
196+
197+
**Risk 3: Scope creep during build**
198+
- *What*: "While I'm here, let me add email integration..."
199+
- *Mitigation*: Strict adherence to MVP checklist. Document ideas for Phase 2.
200+
201+
---
202+
203+
## Estimated Effort
204+
205+
**Total MVP**: ~6-8 hours (~6-8 minutes human time with Claude Code)
206+
207+
**Breakdown**:
208+
- Setup (clone, configure D1, run migrations): 30 min
209+
- Contacts module (schema, actions, UI, tags): 2.5 hours
210+
- Deals module (schema, actions, UI, board): 2 hours
211+
- Dashboard integration (nav, metrics): 1 hour
212+
- Testing & seed data: 1 hour
213+
- Documentation: 30 min
214+
215+
**Phase 2** (optional extensions):
216+
- Activity timeline: +2 hours
217+
- Avatar uploads: +1 hour
218+
- Drag-drop Kanban: +2 hours
219+
- Custom stages: +1.5 hours
220+
- Advanced search: +2 hours
221+
222+
---
223+
224+
## Success Criteria (MVP)
225+
226+
**Functional Requirements**:
227+
- [ ] Can create, edit, delete, search contacts
228+
- [ ] Can assign multiple tags to contacts
229+
- [ ] Can create tags with colors
230+
- [ ] Can create, edit, delete deals
231+
- [ ] Deals link to contacts (dropdown selector)
232+
- [ ] Pipeline board shows deals in columns by stage
233+
- [ ] Dashboard shows: total contacts, active deals, pipeline value
234+
- [ ] All data isolated to logged-in user
235+
- [ ] Forms have proper validation (Zod schemas)
236+
- [ ] UI responsive on mobile/desktop
237+
238+
**Technical Requirements**:
239+
- [ ] Follows module-sliced architecture (`src/modules/contacts/`, `src/modules/deals/`)
240+
- [ ] Uses Server Actions (not API routes)
241+
- [ ] Database migrations run successfully (local + production)
242+
- [ ] Type-safe end-to-end (TypeScript + Drizzle + Zod)
243+
- [ ] shadcn/ui components used consistently
244+
- [ ] Deploys to Cloudflare Workers without errors
245+
246+
**Learning Objectives**:
247+
- [ ] Understand how to structure multi-entity features
248+
- [ ] Practice Drizzle ORM relationships (foreign keys, joins, many-to-many)
249+
- [ ] Learn Server Actions patterns for CRUD
250+
- [ ] Experience D1 migrations workflow
251+
- [ ] Build complex forms with React Hook Form + Zod
252+
253+
---
254+
255+
## Next Steps
256+
257+
### If Proceeding (Recommended)
258+
259+
1. **Exit plan mode** and start implementation
260+
2. **Clone project** to `/home/jez/Documents/fullstack-next-cloudflare-crm`
261+
3. **Configure local Cloudflare**:
262+
- Create new D1 database: `npx wrangler d1 create fullstack-crm`
263+
- Update `wrangler.jsonc` with new database ID
264+
- Set up `.dev.vars` with Better Auth secrets
265+
4. **Implement in phases**:
266+
- Phase 1: Project setup + database schema
267+
- Phase 2: Contacts module
268+
- Phase 3: Deals module
269+
- Phase 4: Dashboard integration
270+
- Phase 5: Testing & documentation
271+
5. **Deploy when ready** (Cloudflare account setup)
272+
273+
### If Refining Scope
274+
275+
**Want simpler?**
276+
- Skip tags (just contacts + deals)
277+
- Skip pipeline board (simple table view)
278+
- Reduces to ~4 hours
279+
280+
**Want more ambitious?**
281+
- Add activity timeline
282+
- Add R2 avatar uploads
283+
- Add custom stages
284+
- Increases to ~10-12 hours
285+
286+
---
287+
288+
## Research References
289+
290+
- **Template repo**: https://github.com/jezweb/fullstack-next-cloudflare (forked from ifindev)
291+
- **Local codebase**: /home/jez/Documents/fullstack-next-cloudflare-demo
292+
- **Drizzle ORM relationships**: https://orm.drizzle.team/docs/rqb
293+
- **shadcn/ui components**: https://ui.shadcn.com/docs
294+
- **Cloudflare D1 docs**: via `mcp__cloudflare-docs__search_cloudflare_documentation`
295+
- **Relevant skills**: `~/.claude/skills/cloudflare-d1`, `~/.claude/skills/drizzle-orm-d1`
296+
297+
---
298+
299+
## Recommendation
300+
301+
**Proceed with MVP implementation**
302+
303+
**Rationale**:
304+
1. Scope is well-defined and realistic (6-8 hours)
305+
2. Template provides solid foundation (80% already built)
306+
3. Learning objectives are clear and achievable
307+
4. No technical blockers (all patterns exist in template)
308+
5. Can defer advanced features to Phase 2 without compromising learning
309+
310+
**This is an excellent learning project** - complex enough to teach real patterns, simple enough to complete without frustration.

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ Edit `.dev.vars` with your credentials:
149149
```bash
150150
# Cloudflare Configuration
151151
CLOUDFLARE_ACCOUNT_ID=your-account-id
152+
CLOUDFLARE_D1_DATABASE_ID=your-database-id
152153
CLOUDFLARE_D1_TOKEN=your-api-token
153154

154155
# Authentication Secrets
@@ -263,6 +264,10 @@ wrangler d1 create your-app-name
263264
# Output will show:
264265
# database_name = "your-app-name"
265266
# database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
267+
#
268+
# Copy the database_id - you'll need it for:
269+
# - wrangler.jsonc (d1_databases.database_id)
270+
# - .dev.vars (CLOUDFLARE_D1_DATABASE_ID)
266271
```
267272

268273
**Create R2 Bucket:**
@@ -329,6 +334,7 @@ openssl rand -base64 32
329334
```bash
330335
# .dev.vars for local development
331336
CLOUDFLARE_ACCOUNT_ID=your-account-id
337+
CLOUDFLARE_D1_DATABASE_ID=your-database-id
332338
CLOUDFLARE_D1_TOKEN=your-api-token
333339
BETTER_AUTH_SECRET=your-generated-secret
334340
GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
@@ -403,6 +409,7 @@ Go to your GitHub repository → Settings → Secrets and add:
403409

404410
- `CLOUDFLARE_API_TOKEN` - Your API token from Step 2
405411
- `CLOUDFLARE_ACCOUNT_ID` - Your account ID
412+
- `CLOUDFLARE_D1_DATABASE_ID` - Your D1 database ID (from `wrangler d1 create` output)
406413
- `BETTER_AUTH_SECRET` - Your auth secret
407414
- `GOOGLE_CLIENT_ID` - Your Google client ID
408415
- `GOOGLE_CLIENT_SECRET` - Your Google client secret

drizzle.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default defineConfig({
1313
driver: "d1-http",
1414
dbCredentials: {
1515
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
16-
databaseId: "757a32d1-5779-4f09-bcf3-b268013395d4",
16+
databaseId: process.env.CLOUDFLARE_D1_DATABASE_ID!,
1717
token: process.env.CLOUDFLARE_D1_TOKEN!,
1818
},
1919
});

0 commit comments

Comments
 (0)