feat: v0.1.0 - geolocation capture, calendar, search, Starlight docs site

- Automatic browser geolocation capture on event creation
- Reverse geocoding via Nominatim API for place names
- Full-text search with SQLite FTS5
- Calendar view for browsing past entries
- DateNavigator component for day navigation
- SearchModal with Ctrl+K shortcut
- QuickAddWidget with Ctrl+J shortcut
- Starlight documentation site with GitHub Pages deployment
- Multiple AI provider support (Groq, OpenAI, Anthropic, Ollama, LM Studio)
- Multi-user registration support

BREAKING: Events now include latitude/longitude/placeName fields
This commit is contained in:
lotherk
2026-03-27 02:27:55 +00:00
parent deaf496a7d
commit 0bdd71a4ed
67 changed files with 15201 additions and 355 deletions

539
todo/stats.md Normal file
View File

@@ -0,0 +1,539 @@
# Statistics Feature Specification
## Overview
Add a comprehensive statistics dashboard to track journaling habits and provide insights. All analytics are computed locally from user data - no external services required.
---
## 1. Streak Tracking
### Feature Description
Track consecutive days of journaling activity to motivate users to maintain their habit.
### Metrics to Track
- **Current Streak**: Consecutive days with at least one event
- **Longest Streak**: All-time record of consecutive days
- **Streak Start Date**: When current streak began
- **Days Until Milestone**: Days until 7/14/30/60/90 day milestones
- **Streak Risk**: Days with no events that would break streak
### Visualization Approaches
- Flame icon with day count (🔥 12 days)
- Progress bar to next milestone
- Calendar mini-view showing streak days
- Warning indicator when streak at risk
### Dashboard Layout
```
┌─────────────────────────────────────┐
│ 🔥 12 Day Streak 🏆 45 days │
│ ████████░░░░░░░░ 4 days to 16 │
└─────────────────────────────────────┘
```
### Database Queries
```typescript
// Current streak calculation
const events = await prisma.event.findMany({
where: { userId },
select: { date: true },
orderBy: { date: 'desc' }
});
// Group by date, find consecutive days
```
### Implementation Complexity
- **Backend**: Medium - requires date range queries with grouping
- **Frontend**: Low - simple counter display
- **Priority**: HIGH - strong motivation driver
---
## 2. Word Count Statistics
### Feature Description
Track total words written in events and journal entries over time.
### Metrics to Track
- **Total Words**: All-time word count
- **Daily Average**: Average words per day (active days)
- **Today/This Week/This Month**: Rolling word counts
- **Longest Entry**: Day with most words
- **Word Count Distribution**: Histogram by day
- **Writing Sessions**: Days with 100+/500+/1000+ words
### Visualization Approaches
- Line chart showing word count over time (30/90/365 days)
- Comparison bar chart: this week vs last week
- Mini sparkline showing recent trend
- Percentile badges (e.g., "Top 10% writers")
### Dashboard Layout
```
┌─────────────────────────────────────┐
│ 📝 12,450 words total │
│ Avg: 156/day • This week: 1,234 │
│ ┌────────────────────────────────┐ │
│ │ ▂▃▅▇▅▃▂▄▅▇█▄▃▂▅▇ │ │
│ └────────────────────────────────┘ │
└─────────────────────────────────────┘
```
### Database Queries
```typescript
// Word count aggregation
await prisma.$queryRaw`
SELECT date, SUM(LENGTH(content) - LENGTH(REPLACE(content, ' ', '')) + 1) as word_count
FROM Event
WHERE userId = ${userId}
GROUP BY date
`;
```
### Implementation Complexity
- **Backend**: Low - simple aggregation queries
- **Frontend**: Low - chart library needed
- **Priority**: MEDIUM
---
## 3. Entry Frequency Heatmap
### Feature Description
GitHub-style contribution heatmap showing activity intensity by day.
### Metrics to Track
- Days with events (boolean per day)
- Event count per day (intensity levels)
- Active weeks/months count
- Most productive day of week
- Most productive month
### Visualization Approaches
- GitHub-style grid (52 weeks × 7 days)
- Color scale: empty → light → dark (slate-800 to purple-600)
- Tooltip on hover showing date + count
- Day-of-week labels on left
- Month labels on top
### Dashboard Layout
```
┌─────────────────────────────────────┐
│ Activity Heatmap │
│ Jan Feb Mar Apr May Jun │
│ Mon ░░░████▓▓░░░░░░░ │
│ Tue ░▓▓████▓▓░░░░░░░ │
│ Wed ░░░████▓▓░░░░░░░ │
│ Thu ░░░░░░▓▓░░░░░░░░ │
│ Fri ░░░░░░░░░░░░░░░░░ │
│ │
│ Less ░▒▓█ More │
└─────────────────────────────────────┘
```
### Database Queries
```typescript
// Heatmap data
await prisma.event.groupBy({
by: ['date'],
where: { userId },
_count: { id }
});
```
### Implementation Complexity
- **Backend**: Low - groupBy query
- **Frontend**: Medium - requires heatmap component (use react-heatmap-grid or custom SVG)
- **Priority**: HIGH - visual engagement
---
## 4. Event Type Distribution
### Feature Description
Breakdown of events by type (text, photo, voice, health, etc.).
### Metrics to Track
- Count per event type
- Percentage distribution
- Type trends over time (increasing/decreasing)
- Media attachments count
### Visualization Approaches
- Donut/pie chart with legend
- Horizontal bar chart (easier to read)
- Stacked area chart over time
- Type badges with counts
### Dashboard Layout
```
┌─────────────────────────────────────┐
│ Event Types │
│ ┌──────────┐ │
│ │ text │ ████████ 45% │
│ │ photo │ █████░░░ 28% │
│ │ voice │ ███░░░░░ 15% │
│ │ health │ █░░░░░░░ 8% │
│ │ event │ ██░░░░░░ 4% │
│ └──────────┘ │
└─────────────────────────────────────┘
```
### Database Queries
```typescript
// Type distribution
await prisma.event.groupBy({
by: ['type'],
where: { userId },
_count: { id }
});
```
### Implementation Complexity
- **Backend**: Low - simple groupBy
- **Frontend**: Low - chart library
- **Priority**: MEDIUM - nice-to-have insight
---
## 5. Time-of-Day Patterns
### Feature Description
Analyze when users typically log events (morning person vs night owl).
### Metrics to Track
- Events by hour of day (0-23)
- Events by time block (Morning 5-11, Afternoon 12-17, Evening 18-22, Night 23-4)
- Most active hour
- Average time of first event
- Average time of last event
### Visualization Approaches
- 24-hour radial/bar chart
- Time block pie chart
- Timeline showing first-last event range
- "Your prime time" indicator
### Dashboard Layout
```
┌─────────────────────────────────────┐
│ Writing Time Patterns │
│ ▔▔▔▔▔▔▔ │
│ ▔▔ ▔▔▔ │
│ ▔▔ ● ▔▔ │
│ ▔ ▔▔ ▔ │
│ Morning Afternoon Evening │
│ (6-12) (12-6) (6-12) │
│ │
│ ☀️ Peak: 9:00 AM │
└─────────────────────────────────────┘
```
### Database Queries
```typescript
// Hour extraction from createdAt
await prisma.$queryRaw`
SELECT strftime('%H', createdAt) as hour, COUNT(*) as count
FROM Event
WHERE userId = ${userId}
GROUP BY hour
`;
```
### Implementation Complexity
- **Backend**: Low - SQL date functions
- **Frontend**: Medium - radial chart
- **Priority**: LOW - interesting but not core
---
## 6. Monthly/Yearly Summaries
### Feature Description
Aggregate statistics for specific time periods with comparison to previous periods.
### Metrics to Track
- Monthly: events, journals, words, active days
- Yearly: same metrics
- Month-over-month change (%)
- Year-over-year change (%)
- Best month ever
- Days with 100% journal generation
### Visualization Approaches
- Monthly bar chart comparison
- Year-in-review cards
- Progress ring showing yearly goals
- "This time last year" comparison
### Dashboard Layout
```
┌─────────────────────────────────────┐
│ 2024 Year in Review │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ 342 │ │ 45 │ │ 15,600 │ │
│ │Events │ │ Journals│ │ Words │ │
│ └────────┘ └────────┘ └────────┘ │
│ │
│ Best month: November (52 events) │
│ +12% vs 2023 │
└─────────────────────────────────────┘
```
### Database Queries
```typescript
// Monthly aggregation
await prisma.event.groupBy({
by: ['date'],
where: {
userId,
date: { gte: '2024-01-01', lte: '2024-12-31' }
},
_count: { id }
});
```
### Implementation Complexity
- **Backend**: Low - grouped queries with date filters
- **Frontend**: Low-Medium - cards and charts
- **Priority**: MEDIUM
---
## 7. Progress & Motivation Features
### Feature Description
Gamification elements to encourage consistent journaling.
### Metrics & Features
- **Achievement Badges**:
- "First Entry" (1 event)
- "Week Warrior" (7 day streak)
- "Month Master" (30 day streak)
- "Century" (100 events)
- "Word Smith" (10,000 words)
- "Photojournalist" (50 photos)
- "Consistent" (journal every day for a month)
- **Goals**: Set daily/weekly targets
- **Streak Protection**: 1 "freeze" per month to preserve streak
- **Weekly Report**: Auto-generated summary every Sunday
- **Milestone Celebrations**: Confetti at 7/14/30/60/90 days
### Visualization Approaches
- Badge grid (earned + locked)
- Progress rings for goals
- Streak flame animation
- Weekly summary card
### Dashboard Layout
```
┌─────────────────────────────────────┐
│ Achievements │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │🔥7 │ │🌟30│ │📝1K│ │📸10│ │
│ └────┘ └────┘ └────┘ └────┘ │
│ ┌────┐ ┌────┐ ┌────┐ │
│ │🔒 │ │🔒 │ │🔒 │ (locked) │
│ └────┘ └────┘ └────┘ │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Daily Goal: 5 events │
│ ████████░░░░░░░░░ 3/5 │
└─────────────────────────────────────┘
```
### Database Queries
```typescript
// Achievement checks - multiple queries
const eventCount = await prisma.event.count({ where: { userId } });
const streak = await calculateStreak(userId);
```
### Implementation Complexity
- **Backend**: Medium - multiple queries, achievement logic
- **Frontend**: Medium - badge components, progress rings
- **Priority**: MEDIUM - strong engagement driver
---
## 8. Privacy-Preserving Analytics
### Feature Description
Ensure all statistics computation keeps data on-device.
### Approach
- **All aggregations in SQLite**: Use Prisma raw queries on server
- **No external analytics services**: No GA, Mixpanel, etc.
- **Opt-in sharing**: Optional anonymous stats (streak length, event count) for community features
- **Local-first**: Dashboard computes from local API responses
- **Data export**: Users can export all their data
### Implementation
```typescript
// All stats computed server-side via API
// Frontend receives aggregated numbers, not raw data
GET /api/v1/stats/streak
GET /api/v1/stats/words
GET /api/v1/stats/heatmap
```
### Privacy Considerations
- No tracking cookies
- No cross-user analytics
- API key = user identity (no additional tracking)
- Full data deletion on user request
---
## API Changes
### New Endpoints
```typescript
// Statistics aggregation
GET /api/v1/stats/streak { currentStreak, longestStreak, streakStart }
GET /api/v1/stats/words { total, dailyAvg, thisWeek, thisMonth, trend }
GET /api/v1/stats/heatmap [{ date, count }]
GET /api/v1/stats/types [{ type, count, percentage }]
GET /api/v1/stats/time-of-day [{ hour, count }]
GET /api/v1/stats/summary { monthly: [], yearly: [] }
GET /api/v1/stats/achievements { earned: [], available: [], progress: {} }
GET /api/v1/stats/month/:year { events, journals, words, activeDays }
```
### Response Types
```typescript
interface StreakStats {
currentStreak: number;
longestStreak: number;
streakStartDate: string;
daysToMilestone: number;
}
interface WordStats {
total: number;
dailyAverage: number;
thisWeek: number;
thisMonth: number;
trend: 'up' | 'down' | 'stable';
longestEntry: { date: string; count: number };
}
interface HeatmapData {
date: string;
count: number;
level: 0 | 1 | 2 | 3 | 4;
}
interface Achievement {
id: string;
name: string;
description: string;
icon: string;
earned: boolean;
earnedAt?: string;
progress?: number;
target: number;
}
```
---
## Database Schema Additions
### Optional: Cached Stats Table
For expensive queries, cache results:
```prisma
model UserStats {
userId String @id
currentStreak Int @default(0)
longestStreak Int @default(0)
totalWords Int @default(0)
totalEvents Int @default(0)
lastUpdated DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
```
**Update Strategy**: Recalculate on event create/delete or daily cron.
---
## Frontend Implementation
### New Page: /stats
```tsx
// /frontend/src/pages/Stats.tsx
import { useState, useEffect } from 'react';
import { api } from '../lib/api';
export default function Stats() {
const [streak, setStreak] = useState<StreakStats | null>(null);
const [words, setWords] = useState<WordStats | null>(null);
const [heatmap, setHeatmap] = useState<HeatmapData[]>([]);
// ...
}
```
### Chart Library Recommendation
- **Recharts**: Good for line/bar charts (used with React commonly)
- **react-heatmap-grid**: For activity heatmap
- **react-circular-progressbar**: For goal progress rings
- Build custom SVG for simple visualizations
---
## Implementation Roadmap
### Phase 1: Core Stats (Week 1)
1. API endpoints for streak, word count, heatmap
2. Basic stats page with counters
3. Heatmap visualization
### Phase 2: Visualizations (Week 2)
1. Word count trend chart
2. Event type distribution chart
3. Time-of-day analysis
### Phase 3: Gamification (Week 3)
1. Achievement system
2. Daily goals
3. Streak UI improvements
### Phase 4: Summaries (Week 4)
1. Monthly/yearly summaries
2. "This time last year" comparison
3. Weekly report
---
## Priority Recommendations
| Feature | Priority | Impact | Effort |
|---------|----------|--------|--------|
| Streak Tracking | HIGH | High | Low |
| Heatmap | HIGH | High | Medium |
| Word Count | MEDIUM | Medium | Low |
| Event Types | MEDIUM | Low | Low |
| Time Patterns | LOW | Low | Medium |
| Monthly/Yearly | MEDIUM | Medium | Low |
| Achievements | MEDIUM | High | Medium |
| Goals | MEDIUM | Medium | Low |
---
## Summary
Statistics feature transforms DearDiary from a passive journal into an active self-improvement tool. Key priorities:
1. **Streak tracking** - strongest motivation driver
2. **Heatmap** - visual engagement, GitHub-style
3. **Achievements** - gamification layer
4. **Word count** - progress visualization
All data stays local - no privacy concerns. Implementation can be phased with clear value at each step.