# 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](https://github.com/Giftyaning/Mood-Tracker) (Gitea [#20](https://gitea.ginnoir.com/ginnoir/famapp/issues/20), epic [#19](https://gitea.ginnoir.com/ginnoir/famapp/issues/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 consistency - `user_id` — **per-user privacy**; all reads/writes filter `user_id = current user` - `recorded_at` timestamptz — required date+time (defaults to now on create) - `title` text nullable — optional - `body` text — HTML from shared rich-text editor (ADR 0004) - `moods` jsonb — array of catalog mood ids (multi-select) - `stress` smallint nullable — 1–10 - `pills_taken` boolean 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/entries` - `GET/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/mood` and `/journal/insights` only (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