6.3 KiB
0005 — Journal / mood tracking
Date: 2026-07-03 Status: accepted (2026-07-04)
Context
Journal module (task 86) needs per-user entries, mood multi-select, charts over time, insights, and /api/v1/journal endpoints. ADR research included Giftyaning/Mood-Tracker (Gitea #20, epic #19).
Build-vs-adopt: Mood-Tracker
Decision: do not adopt the repo.
| Factor | Mood-Tracker | famapp need |
|---|---|---|
| License | None | Must have clear reuse rights |
| Stack | Vite SPA, JS, localStorage | Next.js 15, TypeScript, Postgres, Authentik |
| Mood model | Single-select, 5 presets | Multi-select fixed catalog |
| Fields | Title + plain text | Optional title + TipTap HTML, stress, pills |
| API / auth | None | /api/v1/ + session + bearer token |
| Code shape | One 672-line App.jsx |
src/modules/journal/ manifest pattern |
Borrow (reimplement in TypeScript): mood→numeric scoring, day streak, week-over-week trend, insight card layout. Not copied source.
What we decided (2026-07-04)
| # | Topic | Choice |
|---|---|---|
| 1 | Charts | Build natively + Recharts for mood/stress trends and correlation views |
| 2 | Moods | Fixed catalog (~8–12 presets with emoji + color); multi-select |
| 3 | Entries | Multiple per day allowed |
| 4 | Stress | Optional 1–10 slider |
| 5 | Insights v1 | Core + correlations — streak, top mood, avg stress; stress↔mood and pills↔avg-mood simple stats |
| 6 | Index calendar | Month grid with dots on days that have entries; tap day → filtered list |
| 7 | Entry shape | Optional title + rich-text body (shared RichTextEditor) |
| 8 | Chart points | All entries as individual points; x-axis uses datetime (not date-only rollup) |
Decision
Module: src/modules/journal/
Standard module layout: schema.ts, manifest.tsx, server/, components/, app routes under /journal.
Data model
Table journal_entries:
household_id— household scope for DB consistencyuser_id— per-user privacy; all reads/writes filteruser_id = current userrecorded_attimestamptz — required date+time (defaults to now on create)titletext nullable — optionalbodytext — HTML from shared rich-text editor (ADR 0004)moodsjsonb — array of catalog mood ids (multi-select)stresssmallint nullable — 1–10pills_takenboolean nullable
No household-shared journal in v1. Wife cannot see Matt's entries and vice versa.
Mood catalog
Fixed in code (src/modules/journal/mood-catalog.ts): ids, label, emoji, color, numeric score for analytics. Users pick from catalog only (no custom tags in v1).
UI surfaces
| Route | Purpose |
|---|---|
/journal |
Recent 5–10 entries, browse all link, month calendar, links to mood tracker + insights |
/journal/new, /journal/[id] |
Create/edit entry (date/time required; rest optional) |
/journal/mood |
Recharts mood/stress over time (all entry points) |
/journal/insights |
Streak, top mood, correlations |
Rich text: RichTextEditor / RichTextContent from src/components/rich-text/. No share viewer in v1.
Insights v1
- Entry streak (consecutive days with ≥1 entry)
- Most common mood (selected period)
- Average stress (7d / 30d)
- Week-over-week mood trend (score-based)
- Correlations: simple aggregates — avg mood score on days pills taken vs not; stress vs mood score scatter or grouped averages
No ML / sentiment analysis in v1.
API
Additive on ADR 0006 foundation:
GET/POST /api/v1/journal/entriesGET/PATCH/DELETE /api/v1/journal/entries/:id
Bearer token and session auth. Responses scoped to token owner's user id for journal (household token acts as owner user for API mutations per existing pattern, but journal entries are always stored with the authenticated user's id).
Document in docs/api/openapi.yaml.
Testing
- Playwright:
tests/e2e/journal.spec.ts— create minimal entry → list → detail → mood tracker loads - Unit tests for mood scoring / streak helpers where non-trivial
Consequences
Positive
- Recharts gives proper time-series without maintaining custom chart code for long trends.
- Fixed mood catalog keeps insights comparable across entries.
- Per-user privacy matches personal journal expectations.
- Reuses rich-text stack from task 85.
Trade-offs
- All-entry chart points mean busy days show multiple dots; datetime x-axis required.
- Fixed catalog may miss nuance — body text carries freeform detail.
- Correlation stats are descriptive only (no causation); keep copy honest in UI.
- Recharts adds bundle weight on
/journal/moodand/journal/insightsonly (route-level code split).
Out of scope (ADR)
- Household-shared journals
- LLM agent tools for journal (task 88)
- Custom user-defined mood tags
- Share links for journal entries
References
- Task brief:
docs/tasks/86-journal-module.md - Rich text:
docs/decisions/0004-rich-text-editor.md - API:
docs/decisions/0006-api-llm-agent.md - Mood-Tracker research: https://github.com/Giftyaning/Mood-Tracker