- 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
16 KiB
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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
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:
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
// /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)
- API endpoints for streak, word count, heatmap
- Basic stats page with counters
- Heatmap visualization
Phase 2: Visualizations (Week 2)
- Word count trend chart
- Event type distribution chart
- Time-of-day analysis
Phase 3: Gamification (Week 3)
- Achievement system
- Daily goals
- Streak UI improvements
Phase 4: Summaries (Week 4)
- Monthly/yearly summaries
- "This time last year" comparison
- 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:
- Streak tracking - strongest motivation driver
- Heatmap - visual engagement, GitHub-style
- Achievements - gamification layer
- Word count - progress visualization
All data stays local - no privacy concerns. Implementation can be phased with clear value at each step.