From a3e741c23c4e1f0de8f1f19d377eb0e47488f46e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 5 Jun 2026 15:27:48 +0200 Subject: [PATCH] feat(mood): revise factor + tag catalog (migration 0126) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tighten the operator-finalised mood catalog to three RATED factors and a trimmed default BINARY tag set. - Seed `factor_sadness` (RATED, 1..5, inverse — higher = worse) under the `factors` category, with its label in all six locales and a CloudRain glyph. The RATED set is now work / sleep quality / sadness. - Retire `factor_conflict`, `factor_family`, `factor_social`, `factor_stress`: flip each to BINARY and deactivate. The catalog rows stay so historical entries still resolve their labels (the insights read joins by id without an active filter); they just leave the RATED set and the default picker. - Trim the default BINARY tag set roughly in half: deactivate the sleep-tag category (now covered by the sleep-quality slider), the mood level restatements, the nutrition long-tail, and low-signal day-type tags. Kept rows are the high-signal, correlatable tags. Nothing is deleted; inactive rows still resolve labels for existing entries. Reference-data only — no schema change, idempotent upsert + closed-key updates, no per-user data touched. Co-Authored-By: Claude Opus 4.8 (1M context) --- messages/de.json | 3 +- messages/en.json | 3 +- messages/es.json | 3 +- messages/fr.json | 3 +- messages/it.json | 3 +- messages/pl.json | 3 +- .../migration.sql | 72 +++++++++++++++++++ src/components/mood/mood-tag-icons.ts | 2 + 8 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 prisma/migrations/0126_v1129_mood_catalog_revision/migration.sql diff --git a/messages/de.json b/messages/de.json index ef739e2c7..8cf0ff449 100644 --- a/messages/de.json +++ b/messages/de.json @@ -577,7 +577,8 @@ "factorSleepQuality": "Schlafqualität", "factorStress": "Stress", "factorConflict": "Konflikt", - "factorFamily": "Familie" + "factorFamily": "Familie", + "factorSadness": "Traurigkeit" } }, "medications": { diff --git a/messages/en.json b/messages/en.json index 0113f3ab5..84f091789 100644 --- a/messages/en.json +++ b/messages/en.json @@ -577,7 +577,8 @@ "factorSleepQuality": "sleep quality", "factorStress": "stress", "factorConflict": "conflict", - "factorFamily": "family" + "factorFamily": "family", + "factorSadness": "sadness" } }, "medications": { diff --git a/messages/es.json b/messages/es.json index 85772d59c..fac9fef9d 100644 --- a/messages/es.json +++ b/messages/es.json @@ -577,7 +577,8 @@ "factorSleepQuality": "calidad del sueño", "factorStress": "estrés", "factorConflict": "conflicto", - "factorFamily": "familia" + "factorFamily": "familia", + "factorSadness": "tristeza" } }, "medications": { diff --git a/messages/fr.json b/messages/fr.json index 76a80ca9e..d976ba187 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -577,7 +577,8 @@ "factorSleepQuality": "qualité du sommeil", "factorStress": "stress", "factorConflict": "conflit", - "factorFamily": "famille" + "factorFamily": "famille", + "factorSadness": "tristesse" } }, "medications": { diff --git a/messages/it.json b/messages/it.json index 75d2f312a..78e3b61fa 100644 --- a/messages/it.json +++ b/messages/it.json @@ -577,7 +577,8 @@ "factorSleepQuality": "qualità del sonno", "factorStress": "stress", "factorConflict": "conflitto", - "factorFamily": "famiglia" + "factorFamily": "famiglia", + "factorSadness": "tristezza" } }, "medications": { diff --git a/messages/pl.json b/messages/pl.json index 4ce4a7ddc..02f28c961 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -577,7 +577,8 @@ "factorSleepQuality": "jakość snu", "factorStress": "stres", "factorConflict": "konflikt", - "factorFamily": "rodzina" + "factorFamily": "rodzina", + "factorSadness": "smutek" } }, "medications": { diff --git a/prisma/migrations/0126_v1129_mood_catalog_revision/migration.sql b/prisma/migrations/0126_v1129_mood_catalog_revision/migration.sql new file mode 100644 index 000000000..7e3c7a277 --- /dev/null +++ b/prisma/migrations/0126_v1129_mood_catalog_revision/migration.sql @@ -0,0 +1,72 @@ +-- v1.12.9 — mood-catalog revision. +-- +-- Tightens the operator-finalised mood factor + tag catalog seeded by +-- 0118 / 0119 / 0120. Three changes, all reference-data only — no schema +-- change, no per-user data touched, no destructive delete: +-- +-- 1. Seed a new RATED factor `factor_sadness` (scale 1..5, inverse — +-- higher = a worse day), under the existing `factors` category. +-- 2. Retire the four RATED factors the operator dropped +-- (`factor_conflict`, `factor_family`, `factor_social`, +-- `factor_stress`): flip each to `kind = 'BINARY'` AND set +-- `is_active = false`. They leave the RATED set and the default +-- picker, but the catalog rows stay so any historical +-- `mood_entry_tag_links` row still resolves its label (the insights +-- read joins the catalog row by id and does NOT filter `is_active`). +-- The result: exactly THREE RATED factors remain +-- (`factor_work`, `factor_sleep_quality`, `factor_sadness`). +-- 3. Trim the default BINARY tag set roughly in half: mark the +-- long-tail / slider-redundant tags `is_active = false`. The picker +-- (`GET /api/mood/tags` filters `is_active = true`) stops offering +-- them; existing entries that reference a now-inactive tag still +-- resolve its label because the read joins by id without the active +-- filter. Nothing is deleted. +-- +-- Idempotent: the seed uses a deterministic id + ON CONFLICT upsert by +-- `key` (mirrors 0118 / 0119 / 0120); the retire / trim updates key off a +-- closed `key` list, so a re-run is a no-op. + +-- ─── 1. New RATED factor: sadness (inverse, 1..5) ──────────────────── +INSERT INTO "mood_tags" + ("id", "category_id", "key", "label_key", "icon", "sort_order", "is_active", "kind", "scale_min", "scale_max", "inverse") +VALUES ('mt_factor_sadness', 'mtc_factors', 'factor_sadness', 'mood.tag.factorSadness', 'CloudRain', 2, true, 'RATED', 1, 5, true) +ON CONFLICT ("key") DO UPDATE SET + "category_id" = EXCLUDED."category_id", "label_key" = EXCLUDED."label_key", "icon" = EXCLUDED."icon", + "sort_order" = EXCLUDED."sort_order", "is_active" = EXCLUDED."is_active", "kind" = EXCLUDED."kind", + "scale_min" = EXCLUDED."scale_min", "scale_max" = EXCLUDED."scale_max", "inverse" = EXCLUDED."inverse"; + +-- Re-seat the two surviving RATED factors at the top of the `factors` +-- category so the final order reads work → sleep quality → sadness. +UPDATE "mood_tags" SET "sort_order" = 0 WHERE "key" = 'factor_work'; +UPDATE "mood_tags" SET "sort_order" = 1 WHERE "key" = 'factor_sleep_quality'; + +-- ─── 2. Retire the four dropped RATED factors (BINARY + inactive) ───── +-- Flip kind so they leave the RATED set; deactivate so they leave the +-- default picker. Historical RATED links keep their `rating` value and +-- still resolve their label (read joins the catalog row by id, no active +-- filter). Not deleted — references stay valid. +UPDATE "mood_tags" +SET "kind" = 'BINARY', "is_active" = false +WHERE "key" IN ('factor_conflict', 'factor_family', 'factor_social', 'factor_stress'); + +-- ─── 3. Trim the default BINARY tag set (~half) ────────────────────── +-- Deactivate the long-tail / slider-redundant binary tags. Kept set is +-- the high-signal, plausibly-correlatable tags; dropped set is sleep +-- tags now covered by the Schlafqualität slider, mood-level restatements +-- (the 1..5 face already carries them), and low-signal duplicates. Rows +-- stay so historical entries still resolve their labels. +UPDATE "mood_tags" +SET "is_active" = false +WHERE "key" IN ( + -- feelings: drop tags that just restate the 5-face mood level / overlap + -- the surviving factor_sadness; keep a tight high-signal subset. + 'content', 'unsure', 'bored', 'tense', 'stressed', 'sad', + -- sleep: the Schlafqualität (factor_sleep_quality) slider supersedes + -- the whole sleep-tag category. + 'slept_well', 'slept_ok', 'slept_poorly', 'early_night', + -- health: drop the nutrition long-tail (kept: ate_well, alcohol, + -- worked_out, walked, hydrated). + 'fast_food', 'no_sweets', 'big_meal', + -- work: drop low-signal day-type tags (kept: productive, day_off). + 'overtime', 'travel', 'sick_day' +); diff --git a/src/components/mood/mood-tag-icons.ts b/src/components/mood/mood-tag-icons.ts index cf22b4508..0d6516ff9 100644 --- a/src/components/mood/mood-tag-icons.ts +++ b/src/components/mood/mood-tag-icons.ts @@ -10,6 +10,7 @@ import { CheckCircle, Clock, CloudMoon, + CloudRain, CloudSun, Dumbbell, Film, @@ -66,6 +67,7 @@ const MOOD_TAG_ICONS: Record = { CheckCircle, Clock, CloudMoon, + CloudRain, CloudSun, Dumbbell, Film,