Skip to content

Commit 6ba7265

Browse files
committed
feat: implement multiple daily notes per day with customDate field
- Add customDate field to Page interface and database schema - Create migration script for converting legacy YYYY-MM-DD titles to customDate field - Update daily notes carousel to show day containers with multiple note pills - Replace DayCard with DayContainer component using global card styling - Use PillLink components for consistent note pill styling - Add CustomDateField component visible in both edit and view modes - Update page creation logic to use customDate field for daily notes - Enhance database queries to support both customDate and legacy title formats - Add API endpoint for updating custom dates - Maintain backward compatibility with existing daily notes Key features: - Multiple notes per day in timeline carousel - Custom date picker for any page - Consistent UI using global card and pill styles - Safe migration with dry-run mode - Backward compatibility with legacy format
1 parent 8769dff commit 6ba7265

15 files changed

Lines changed: 1215 additions & 138 deletions

File tree

DAILY_NOTES_MIGRATION_SUMMARY.md

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# Daily Notes Migration to Multiple Notes Per Day
2+
3+
## Overview
4+
5+
Successfully implemented the migration from single daily notes (YYYY-MM-DD titles) to multiple daily notes per day using a `customDate` field. This allows users to have multiple notes for each day while maintaining the timeline carousel functionality.
6+
7+
## ✅ Completed Tasks
8+
9+
### 1. Database Schema Updates
10+
- ✅ Added `customDate` field to Page interface in `app/types/database.ts`
11+
- ✅ Updated `CreatePageData` interface in `app/firebase/database/core.ts`
12+
- ✅ Updated API route interfaces in `app/api/pages/route.ts`
13+
14+
### 2. Migration Script
15+
- ✅ Created comprehensive migration script: `scripts/migrate-daily-notes.js`
16+
- ✅ Features:
17+
- Dry run mode for safe testing
18+
- Batch processing to avoid overwhelming Firestore
19+
- Detailed logging and progress tracking
20+
- Idempotent (safe to run multiple times)
21+
- Validates date formats and skips invalid dates
22+
- ✅ Updated documentation in `scripts/README.md`
23+
24+
### 3. Page Creation Logic Updates
25+
- ✅ Modified `app/new/page.tsx` to create daily notes with:
26+
- Title: "Daily note" (instead of YYYY-MM-DD)
27+
- `customDate` field: YYYY-MM-DD value
28+
- ✅ Updated UI to show "Daily note" as title while preserving date functionality
29+
- ✅ Locked title editing for daily notes to maintain consistency
30+
31+
### 4. Custom Date Field UI
32+
- ✅ Enhanced `PageStats` component with custom date display
33+
- ✅ Added clickable calendar picker for page owners
34+
- ✅ Created API endpoint: `/api/pages/[id]/custom-date/route.js`
35+
- ✅ Integrated with page footer to show custom dates
36+
37+
### 5. Daily Notes Carousel Enhancement
38+
- ✅ Updated carousel to support multiple notes per day
39+
- ✅ Modified data structures to handle arrays of notes per date
40+
- ✅ Enhanced `DayCard` component with note count indicators
41+
- ✅ Updated click handling for multiple notes (navigates to first note)
42+
- ✅ Backward compatibility with legacy YYYY-MM-DD title format
43+
44+
### 6. Database Query Optimization
45+
- ✅ Updated `app/utils/dailyNoteNavigation.ts` functions:
46+
- `findPreviousExistingDailyNote()` - now checks customDate first
47+
- `findNextExistingDailyNote()` - now checks customDate first
48+
- `checkDailyNoteExists()` - supports both formats
49+
- `getDailyNotePageId()` - supports both formats
50+
- ✅ Maintains backward compatibility with legacy title-based notes
51+
52+
## 🚀 Migration Instructions
53+
54+
### Step 1: Backup Database
55+
```bash
56+
# Ensure you have a recent backup of your Firestore database
57+
```
58+
59+
### Step 2: Run Migration Script (Dry Run)
60+
```bash
61+
# Test the migration without making changes
62+
node scripts/migrate-daily-notes.js --dry-run
63+
```
64+
65+
### Step 3: Run Migration Script (Live)
66+
```bash
67+
# Perform the actual migration
68+
node scripts/migrate-daily-notes.js
69+
```
70+
71+
### Step 4: Create Firestore Index (Optional but Recommended)
72+
For better performance, create a composite index on:
73+
- Collection: `pages`
74+
- Fields: `userId` (Ascending), `customDate` (Ascending), `deleted` (Ascending)
75+
76+
### Step 5: Deploy Application
77+
Deploy the updated application code to your environment.
78+
79+
## 🎯 Key Features
80+
81+
### Multiple Notes Per Day
82+
- Users can now create multiple notes for the same date
83+
- Each note has title "Daily note" with a `customDate` field
84+
- Carousel shows count indicators for days with multiple notes
85+
86+
### Custom Date Picker
87+
- Page owners can change the custom date for any page
88+
- Accessible through the page stats section
89+
- Clean modal interface with date validation
90+
91+
### Backward Compatibility
92+
- Existing YYYY-MM-DD titled notes continue to work
93+
- Migration script converts them to the new format
94+
- All queries support both old and new formats during transition
95+
96+
### Enhanced UI
97+
- Day cards show note counts for multiple notes
98+
- Improved visual indicators (checkmarks vs. count badges)
99+
- Smooth navigation between notes
100+
101+
## 🔧 Technical Details
102+
103+
### Data Structure Changes
104+
```typescript
105+
// Before
106+
interface Page {
107+
title: string; // "2024-01-15"
108+
// ...
109+
}
110+
111+
// After
112+
interface Page {
113+
title: string; // "Daily note"
114+
customDate?: string; // "2024-01-15"
115+
// ...
116+
}
117+
```
118+
119+
### API Endpoints
120+
- `GET /api/pages/[id]/custom-date` - Get custom date for a page
121+
- `PATCH /api/pages/[id]/custom-date` - Update custom date for a page
122+
123+
### Migration Script Features
124+
- Validates YYYY-MM-DD format dates
125+
- Skips already migrated pages (idempotent)
126+
- Configurable batch sizes
127+
- Comprehensive error handling and logging
128+
- Dry run mode for safe testing
129+
130+
## 🧪 Testing
131+
132+
### Test Migration Script
133+
```bash
134+
# Test with dry run
135+
node scripts/migrate-daily-notes.js --dry-run --batch-size=10
136+
137+
# Test with small batch
138+
node scripts/migrate-daily-notes.js --batch-size=5
139+
```
140+
141+
### Test Daily Notes Functionality
142+
1. Create a new daily note from the carousel
143+
2. Verify it has title "Daily note" and correct customDate
144+
3. Create multiple notes for the same day
145+
4. Verify carousel shows count indicator
146+
5. Test custom date picker functionality
147+
6. Test navigation between multiple notes
148+
149+
## 📋 Next Steps
150+
151+
1. **Run Migration**: Execute the migration script on your production database
152+
2. **Monitor Performance**: Watch for any query performance issues
153+
3. **Create Indexes**: Add recommended Firestore indexes for optimal performance
154+
4. **User Communication**: Inform users about the new multiple notes per day feature
155+
5. **Future Enhancement**: Consider adding a note selection modal for days with many notes
156+
157+
## 🔍 Troubleshooting
158+
159+
### Migration Issues
160+
- Check Firebase service account permissions
161+
- Verify `firebase-service-account.json` exists
162+
- Review migration script logs for specific errors
163+
164+
### Performance Issues
165+
- Create recommended Firestore indexes
166+
- Monitor query performance in Firebase console
167+
- Consider reducing batch sizes if timeouts occur
168+
169+
### UI Issues
170+
- Clear browser cache after deployment
171+
- Verify custom date field appears in page stats
172+
- Test calendar picker functionality
173+
174+
## 📚 Files Modified
175+
176+
### Core Files
177+
- `app/types/database.ts` - Added customDate field
178+
- `app/new/page.tsx` - Updated page creation logic
179+
- `app/components/pages/PageStats.js` - Added custom date UI
180+
- `app/components/daily-notes/DayCard.tsx` - Added count indicators
181+
- `app/components/daily-notes/DailyNotesCarousel.tsx` - Multiple notes support
182+
- `app/utils/dailyNoteNavigation.ts` - Updated queries
183+
184+
### New Files
185+
- `scripts/migrate-daily-notes.js` - Migration script
186+
- `app/api/pages/[id]/custom-date/route.js` - Custom date API
187+
188+
### Documentation
189+
- `scripts/README.md` - Updated with migration instructions
190+
- `DAILY_NOTES_MIGRATION_SUMMARY.md` - This summary document
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import { NextResponse } from 'next/server';
2+
import { getUserIdFromRequest } from '../../../auth-helper';
3+
4+
/**
5+
* Update custom date for a page
6+
* PATCH /api/pages/[id]/custom-date
7+
*/
8+
export async function PATCH(request, { params }) {
9+
try {
10+
const { id } = params;
11+
12+
if (!id) {
13+
return NextResponse.json(
14+
{ error: 'Page ID is required' },
15+
{ status: 400 }
16+
);
17+
}
18+
19+
// Get the current user ID for authorization
20+
const userId = await getUserIdFromRequest(request);
21+
if (!userId) {
22+
return NextResponse.json(
23+
{ error: 'Authentication required' },
24+
{ status: 401 }
25+
);
26+
}
27+
28+
// Parse request body
29+
const { customDate } = await request.json();
30+
31+
// Validate custom date format (YYYY-MM-DD)
32+
if (customDate && !/^\d{4}-\d{2}-\d{2}$/.test(customDate)) {
33+
return NextResponse.json(
34+
{ error: 'Invalid date format. Use YYYY-MM-DD format.' },
35+
{ status: 400 }
36+
);
37+
}
38+
39+
// Validate that the date is a valid date
40+
if (customDate) {
41+
try {
42+
const [year, month, day] = customDate.split('-').map(Number);
43+
const date = new Date(year, month - 1, day);
44+
if (isNaN(date.getTime()) ||
45+
date.getFullYear() !== year ||
46+
date.getMonth() !== month - 1 ||
47+
date.getDate() !== day) {
48+
return NextResponse.json(
49+
{ error: 'Invalid date value' },
50+
{ status: 400 }
51+
);
52+
}
53+
} catch (error) {
54+
return NextResponse.json(
55+
{ error: 'Invalid date value' },
56+
{ status: 400 }
57+
);
58+
}
59+
}
60+
61+
console.log(`API: Updating custom date for page ${id} to ${customDate}`);
62+
63+
// Import Firebase modules
64+
const { doc, getDoc, updateDoc } = await import('firebase/firestore');
65+
const { db } = await import('../../../../firebase/database');
66+
67+
// Get the page document to check ownership
68+
const pageRef = doc(db, 'pages', id);
69+
const pageDoc = await getDoc(pageRef);
70+
71+
if (!pageDoc.exists()) {
72+
return NextResponse.json(
73+
{ error: 'Page not found' },
74+
{ status: 404 }
75+
);
76+
}
77+
78+
const pageData = pageDoc.data();
79+
80+
// Check if page is deleted
81+
if (pageData.deleted === true) {
82+
return NextResponse.json(
83+
{ error: 'Page not found' },
84+
{ status: 404 }
85+
);
86+
}
87+
88+
// Check if user owns the page
89+
if (pageData.userId !== userId) {
90+
return NextResponse.json(
91+
{ error: 'You can only update the custom date of your own pages' },
92+
{ status: 403 }
93+
);
94+
}
95+
96+
// Update the page with new custom date
97+
const updateData = {
98+
customDate: customDate || null, // Allow clearing the custom date
99+
lastModified: new Date().toISOString()
100+
};
101+
102+
await updateDoc(pageRef, updateData);
103+
104+
console.log(`API: Successfully updated custom date for page ${id}`);
105+
106+
return NextResponse.json({
107+
success: true,
108+
customDate: customDate,
109+
message: customDate ? 'Custom date updated successfully' : 'Custom date cleared successfully'
110+
});
111+
112+
} catch (error) {
113+
console.error('Error updating custom date:', error);
114+
return NextResponse.json(
115+
{ error: error.message || 'An error occurred while updating the custom date' },
116+
{ status: 500 }
117+
);
118+
}
119+
}
120+
121+
/**
122+
* Get custom date for a page
123+
* GET /api/pages/[id]/custom-date
124+
*/
125+
export async function GET(request, { params }) {
126+
try {
127+
const { id } = params;
128+
129+
if (!id) {
130+
return NextResponse.json(
131+
{ error: 'Page ID is required' },
132+
{ status: 400 }
133+
);
134+
}
135+
136+
console.log(`API: Getting custom date for page ${id}`);
137+
138+
// Import Firebase modules
139+
const { doc, getDoc } = await import('firebase/firestore');
140+
const { db } = await import('../../../../firebase/database');
141+
142+
// Get the page document
143+
const pageRef = doc(db, 'pages', id);
144+
const pageDoc = await getDoc(pageRef);
145+
146+
if (!pageDoc.exists()) {
147+
return NextResponse.json(
148+
{ error: 'Page not found' },
149+
{ status: 404 }
150+
);
151+
}
152+
153+
const pageData = pageDoc.data();
154+
155+
// Check if page is deleted
156+
if (pageData.deleted === true) {
157+
return NextResponse.json(
158+
{ error: 'Page not found' },
159+
{ status: 404 }
160+
);
161+
}
162+
163+
return NextResponse.json({
164+
customDate: pageData.customDate || null
165+
});
166+
167+
} catch (error) {
168+
console.error('Error getting custom date:', error);
169+
return NextResponse.json(
170+
{ error: error.message || 'An error occurred while getting the custom date' },
171+
{ status: 500 }
172+
);
173+
}
174+
}

app/api/pages/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ interface PageData {
1919
createdAt?: string;
2020
deleted?: boolean;
2121
deletedAt?: string;
22+
customDate?: string; // YYYY-MM-DD format for daily notes and date-based pages
2223
}
2324

2425
interface PageQuery {

0 commit comments

Comments
 (0)