Skip to content

Phase 3: reusable component library for the logging redesign - #180

Merged
mapgie merged 4 commits into
mainfrom
claude/logging-redesign-phase-3
Aug 26, 2026
Merged

Phase 3: reusable component library for the logging redesign#180
mapgie merged 4 commits into
mainfrom
claude/logging-redesign-phase-3

Conversation

@mapgie

@mapgie mapgie commented Aug 25, 2026

Copy link
Copy Markdown
Owner

What this is

Phase 3 of the logging & category-system redesign (PLAN.md §6, build guide: phases/phase-3-component-library.md). Builds the stateless, parameterised Compose primitives every later redesign screen is assembled from, all in ui/components/, all preview-only: no existing screen is rewired, nothing is removed. No DB changes (schema stays at v24).

Note on the base: this was branched off the Phase 2 branch, but Phase 2 merged to main as #179 while this was in flight and its branch was deleted, so main (which now contains Phase 2) is the base; origin/main has been merged in and the diff is Phase 3 only.

The primitives

Component File Notes
SectionHeader SectionHeader.kt Uppercase 11sp/700/+0.11em label, right-aligned current value in the group's role colour
ListCard / ListRow / HairlineDivider ListCard.kt Hairline-outlined 18dp card of key/value rows (min 52dp, tabular values, chevron + Role.Button when clickable)
StepScale StepScale.kt Discrete rising tap-steps, no drag; exposes to TalkBack as ONE control ("Flow, Medium, 3 of 4") via clearAndSetSemantics, with per-step custom accessibility actions so it stays operable
ChipToggle / ChipRow ChipToggle.kt Tonal fill + leading check when selected, hairline when not; built on FilterChip (toggle semantics + min touch target)
ToneHero ToneHero.kt Tonal container that makes one metric the page; hero word in Comfortaa, applied explicitly
SegmentedToggle SegmentedToggle.kt M3 single-choice segmented buttons (Grouped/Ungrouped, Yes/No), optional role tint
RolePicker RolePicker.kt 6 in-theme role pills + fixed-colour track, factored from the Phase 1 inline picker; Role.RadioButton, 48dp targets, check/dot so selection is never colour-only
IconPicker IconPicker.kt 48dp tile grid over CategoryIcon; radio semantics + display-name descriptions
SwitchRow SwitchRow.kt Whole row is the target: toggleable with Role.Switch + explicit stateDescription; inner Switch has no click handler (no double focus)
Timeline / TimelineEntry Timeline.kt Timestamped multiple-per-day list; the time is always text (colour-blind safe); per-row overflow (edit/delete) + append row
PrimarySaveBar PrimarySaveBar.kt Sticky 52dp pill, radius 26, gradient fade of the background over scrolling content
MetricInput (stub) MetricInput.kt Facade switch over CategoryType returning each control; MetricConfig/MetricValue value types defined now, including YesNo/TimeOfDay variants ready for Phase 4

Support files: RoleTint.kt (roleContainerTint, tonal container fills for arbitrary role colours, lerp-toward-surface per the house ordinalShade pattern) and ComponentPreviews.kt (internal preview scaffolding through GoFloTheme, so previews exercise the real LocalExtendedRoles path).

Every primitive accents from a passed-in role: Color; none reads a hardcoded accent. Each has light, dark, and 200% font-scale previews. Selection changes are instant tonal swaps (reduce-motion safe by construction).

Deviations from the build guide

  • RolePicker is standalone; ManageCategoriesScreen is untouched. The guide asks to factor the picker out of the Phase 1 inline version but also requires "no screen consumes them yet (previews only)" and "existing screens compile unchanged". The preservation rules win: the new component mirrors the Phase 1 picker's visuals (with 48dp fixed-swatch targets and an extraFixedSlot for the custom-colour swatch) and the screen keeps its copy until Phase 6/7 rewires it.
  • Tonal fills for arbitrary roles are derived. M3 ColorScheme has no containers for quaternary/quinary/senary or fixed hexes, so chip/hero/segment fills use roleContainerTint (25% lerp toward surface) with onSurface content, which keeps surface-level text contrast in both light and dark.
  • ToneHero word is onSurface on the tint rather than the raw role (the mock's deep onPrimaryContainer word is closest to onSurface); the role carries through the container.
  • StepScale gained a name parameter (needed for the single-control announcement) plus per-step customActions.

Also adds a LESSONS.md entry: a role: Color parameter shadows the role semantics property inside semantics {} blocks; qualify with this.role.

Acceptance checklist (guide)

  • Every primitive has an @Preview (light + dark) rendering correctly
  • Previews at 200% font scale reflow without clipping (StepScale captions and ChipRow wrap; weight-based caption cells, FlowRow wrapping)
  • a11y_check.py green; StepScale exposes as one control; SwitchRow announces state
  • No primitive reads a hardcoded accent; accent always comes from role
  • Motion: selection is instant; reduce-motion honoured
  • Builds in CI

Feature-preservation checklist (guide)

  • Nothing existing is removed or rewired; SelectableChip untouched
  • Existing screens compile unchanged (no existing file modified except PLAN.md §7 and LESSONS.md)

a11y_check.py, wcag_check.py, and check_changelog_fragment.py (against origin/main) all pass. Changelog fragment: changelog/unreleased/logging-redesign-phase-3.json (patch, internal note per the guide).

🤖 Generated with Claude Code

https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg


Generated by Claude Code

claude added 4 commits August 25, 2026 00:50
Adds the redesign's only genuinely new table: a category group that owns
a colour role and a default input type. Fully additive; no UI changes.

- Group entity (table `groups`) + GroupDao; TrackingCategory gains a
  nullable groupId (no FK: deleting a group unfiles members instead of
  cascading). MIGRATION_23_24 creates the table and adds the column;
  existing rows keep groupId NULL and their colorToken, so nothing
  changes visually after migrating.
- Group CRUD, reorder, delete-with-unfile, and assign/unassign methods
  on TrackingRepository (new nullable groupDao constructor param, wired
  in GoFloApplication).
- Colour inheritance per PLAN.md section 5: new "inherit" colorToken
  sentinel resolved by TrackingCategory.effectiveColorToken(groups) to
  the group's colorRole, or neutral surfaceVariant when groupless.
  Deliberately not a CategoryColor entry so the Phase 1 role picker
  does not offer it. Existing tokens are untouched (no grey wipe;
  confirmed with owner against the handover's neutral-by-default).
- Migration test (Migration23To24Test): runs the real MIGRATION_23_24
  against a real SQLite v23 schema via sqlite-jdbc (test-only dep),
  routing execSQL through a reflection proxy, and asserts the exact
  schema Room expects plus data preservation. MigrationTestHelper is
  unusable here (no instrumented tests, exportSchema = false).
- PLAN.md section 7 progress log updated; subsystem map 02 updated for
  v24; changelog fragment (minor); LESSONS.md entry on JVM-side Room
  migration testing.

Verified: migration SQL executed against a seeded v23 schema in SQLite
(PRAGMA output matches the test's expected schema exactly);
a11y_check.py and wcag_check.py both clean.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg

Co-authored-by: Claude <noreply@anthropic.com>
Adds the ~12 stateless, parameterised Compose primitives every later
redesign screen is assembled from, in ui/components/, previews only.
No existing screen is rewired; nothing is removed.

- SectionHeader: uppercase 11sp section label with right-aligned value
- ListCard / ListRow / HairlineDivider: hairline-outlined card of rows
- StepScale: discrete rising tap-steps; exposes to TalkBack as a single
  control ("Flow, Medium, 3 of 4") with per-step custom actions
- ChipToggle / ChipRow: tonal-fill + check selection chips (FilterChip)
- ToneHero: tonal hero container, Comfortaa word applied explicitly
- SegmentedToggle: single-choice segmented buttons, optional role tint
- RolePicker: 6 in-theme role pills + fixed-colour track, standalone
  (visuals mirrored from the Phase 1 inline picker; ManageCategories
  keeps its copy until a later phase rewires it)
- IconPicker: 48dp icon tile grid with radio semantics + display names
- SwitchRow: full-row toggle, Role.Switch + stateDescription, inner
  Switch has no click handler
- Timeline / TimelineEntry: timestamped multiple-per-day list with
  per-row overflow and an append row
- PrimarySaveBar: sticky 52dp pill with gradient fade
- MetricInput facade stub + MetricConfig/MetricValue value types
  (incl. YesNo/TimeOfDay variants ready for Phase 4)

Every primitive accents from a passed-in role Color (tonal container
fills derived via roleContainerTint, lerp toward surface); each carries
light, dark, and 200% font-scale previews through GoFloTheme so the
extended-roles CompositionLocal path is exercised. a11y_check.py and
wcag_check.py both clean. PLAN.md progress log updated; DB stays at 24.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>
CI failed compileDebugKotlin on PR #180: role/selected/contentDescription/
customActions were assigned inside semantics{} blocks as this.role etc.
without importing the corresponding androidx.compose.ui.semantics extension
properties (importing the Role class does not cover the lowercase role
property). Adds the missing imports to IconPicker, ListCard, RolePicker,
StepScale, and Timeline, and extends the LESSONS.md shadowing entry with
the import requirement. Verified with a semantics-vs-imports sweep over
every main-source Kotlin file plus a11y_check.py.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg

Co-authored-by: Claude <noreply@anthropic.com>
@mapgie
mapgie marked this pull request as ready for review August 26, 2026 01:09
@mapgie
mapgie merged commit bc34faa into main Aug 26, 2026
4 checks passed
@mapgie
mapgie deleted the claude/logging-redesign-phase-3 branch August 26, 2026 01:09
mapgie added a commit that referenced this pull request Aug 26, 2026
* Phase 2: group data model (migration 23 to 24) with colour inheritance

Adds the redesign's only genuinely new table: a category group that owns
a colour role and a default input type. Fully additive; no UI changes.

- Group entity (table `groups`) + GroupDao; TrackingCategory gains a
  nullable groupId (no FK: deleting a group unfiles members instead of
  cascading). MIGRATION_23_24 creates the table and adds the column;
  existing rows keep groupId NULL and their colorToken, so nothing
  changes visually after migrating.
- Group CRUD, reorder, delete-with-unfile, and assign/unassign methods
  on TrackingRepository (new nullable groupDao constructor param, wired
  in GoFloApplication).
- Colour inheritance per PLAN.md section 5: new "inherit" colorToken
  sentinel resolved by TrackingCategory.effectiveColorToken(groups) to
  the group's colorRole, or neutral surfaceVariant when groupless.
  Deliberately not a CategoryColor entry so the Phase 1 role picker
  does not offer it. Existing tokens are untouched (no grey wipe;
  confirmed with owner against the handover's neutral-by-default).
- Migration test (Migration23To24Test): runs the real MIGRATION_23_24
  against a real SQLite v23 schema via sqlite-jdbc (test-only dep),
  routing execSQL through a reflection proxy, and asserts the exact
  schema Room expects plus data preservation. MigrationTestHelper is
  unusable here (no instrumented tests, exportSchema = false).
- PLAN.md section 7 progress log updated; subsystem map 02 updated for
  v24; changelog fragment (minor); LESSONS.md entry on JVM-side Room
  migration testing.

Verified: migration SQL executed against a seeded v23 schema in SQLite
(PRAGMA output matches the test's expected schema exactly);
a11y_check.py and wcag_check.py both clean.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg

Co-authored-by: Claude <noreply@anthropic.com>

* Phase 3: reusable component library for the logging redesign

Adds the ~12 stateless, parameterised Compose primitives every later
redesign screen is assembled from, in ui/components/, previews only.
No existing screen is rewired; nothing is removed.

- SectionHeader: uppercase 11sp section label with right-aligned value
- ListCard / ListRow / HairlineDivider: hairline-outlined card of rows
- StepScale: discrete rising tap-steps; exposes to TalkBack as a single
  control ("Flow, Medium, 3 of 4") with per-step custom actions
- ChipToggle / ChipRow: tonal-fill + check selection chips (FilterChip)
- ToneHero: tonal hero container, Comfortaa word applied explicitly
- SegmentedToggle: single-choice segmented buttons, optional role tint
- RolePicker: 6 in-theme role pills + fixed-colour track, standalone
  (visuals mirrored from the Phase 1 inline picker; ManageCategories
  keeps its copy until a later phase rewires it)
- IconPicker: 48dp icon tile grid with radio semantics + display names
- SwitchRow: full-row toggle, Role.Switch + stateDescription, inner
  Switch has no click handler
- Timeline / TimelineEntry: timestamped multiple-per-day list with
  per-row overflow and an append row
- PrimarySaveBar: sticky 52dp pill with gradient fade
- MetricInput facade stub + MetricConfig/MetricValue value types
  (incl. YesNo/TimeOfDay variants ready for Phase 4)

Every primitive accents from a passed-in role Color (tonal container
fills derived via roleContainerTint, lerp toward surface); each carries
light, dark, and 200% font-scale previews through GoFloTheme so the
extended-roles CompositionLocal path is exercised. a11y_check.py and
wcag_check.py both clean. PLAN.md progress log updated; DB stays at 24.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Phase 4: MetricInput facade + Yes/No and Time input types

Completes the MetricInput facade so it renders every category input type,
and refactors LogCategoryScreen to render through it with behaviour parity:

- CategoryType gains YES_NO ("yes_no") and TIME ("time"). Both store their
  readings as plain value-label strings ("Yes"/"No"; 24-hour "HH:mm") in
  tracking_log_values per the owner's resolution of PLAN.md §8 decision #3:
  no new columns, no migration (DB stays at v24).
- MetricInput: whole-step rating scales (up to 10 steps) render as StepScale;
  decimal or wider ranges keep the parity slider including whole-number
  stepped behaviour, large readout, scale labels, min/max labels, and the
  unset-value hint. numeric_free keeps unit label + placeholder; increment
  keeps the never-below-zero counter (decrement disabled at 0); yes_no
  renders a role-tinted SegmentedToggle; time renders the new TimeField
  primitive (Material time picker, 24h).
- LogCategoryScreen: the inline per-type when is gone; every non-timed input
  renders via MetricInput. Timed increment stays screen-driven (per-tap
  immediate saves, no Save button) and now renders the Timeline primitive.
  Notes 500-char cap, save/update, delete, date selection, edit-existing,
  empty-catalog text, and "previously recorded (removed)" chips unchanged.
- LogCategoryViewModel: additive setSelectedValues; yes_no/time flow through
  selectedValues, so the existing else-branch save path persists them and
  the numeric_free empty-blocks-save / increment <=0-blocks-save rules are
  untouched.
- LogPeriodScreen: PinnedCategoryInput gains additive yes_no/time branches
  delegating to MetricInput (pre-existing branches untouched); additive
  LogPeriodViewModel.setPinnedSingleValue feeds the existing selection-set
  save path.
- TrackingCategory.isNumeric now enumerates the numeric types explicitly so
  yes_no/time chart as label categories in Stats instead of being fed into
  numeric chart math.
- Create flow: the two new types appear automatically in the New Category
  type chips; the unit field is now limited to the genuinely numeric types.
- Docs: PLAN.md §7 row + §8 #3 resolution, subsystem map 01 drift note,
  LESSONS.md entry on negation-defined classifications, changelog fragment
  (minor).

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Fix unresolved semantics property references in the component library

CI failed compileDebugKotlin on PR #180: role/selected/contentDescription/
customActions were assigned inside semantics{} blocks as this.role etc.
without importing the corresponding androidx.compose.ui.semantics extension
properties (importing the Role class does not cover the lowercase role
property). Adds the missing imports to IconPicker, ListCard, RolePicker,
StepScale, and Timeline, and extends the LESSONS.md shadowing entry with
the import requirement. Verified with a semantics-vs-imports sweep over
every main-source Kotlin file plus a11y_check.py.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg

Co-authored-by: Claude <noreply@anthropic.com>

* Merge phase-3 semantics-import fix; fix the same missing import in TimeField

Brings in the phase-3 fix for unresolved semantics extension properties and
applies the same fix to TimeField.kt (Phase 4 file, missing the lowercase
androidx.compose.ui.semantics.role import), found by sweeping every
main-source file with the same checker.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg

Co-authored-by: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
mapgie added a commit that referenced this pull request Aug 26, 2026
…of the day (#182)

* Phase 2: group data model (migration 23 to 24) with colour inheritance

Adds the redesign's only genuinely new table: a category group that owns
a colour role and a default input type. Fully additive; no UI changes.

- Group entity (table `groups`) + GroupDao; TrackingCategory gains a
  nullable groupId (no FK: deleting a group unfiles members instead of
  cascading). MIGRATION_23_24 creates the table and adds the column;
  existing rows keep groupId NULL and their colorToken, so nothing
  changes visually after migrating.
- Group CRUD, reorder, delete-with-unfile, and assign/unassign methods
  on TrackingRepository (new nullable groupDao constructor param, wired
  in GoFloApplication).
- Colour inheritance per PLAN.md section 5: new "inherit" colorToken
  sentinel resolved by TrackingCategory.effectiveColorToken(groups) to
  the group's colorRole, or neutral surfaceVariant when groupless.
  Deliberately not a CategoryColor entry so the Phase 1 role picker
  does not offer it. Existing tokens are untouched (no grey wipe;
  confirmed with owner against the handover's neutral-by-default).
- Migration test (Migration23To24Test): runs the real MIGRATION_23_24
  against a real SQLite v23 schema via sqlite-jdbc (test-only dep),
  routing execSQL through a reflection proxy, and asserts the exact
  schema Room expects plus data preservation. MigrationTestHelper is
  unusable here (no instrumented tests, exportSchema = false).
- PLAN.md section 7 progress log updated; subsystem map 02 updated for
  v24; changelog fragment (minor); LESSONS.md entry on JVM-side Room
  migration testing.

Verified: migration SQL executed against a seeded v23 schema in SQLite
(PRAGMA output matches the test's expected schema exactly);
a11y_check.py and wcag_check.py both clean.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg

Co-authored-by: Claude <noreply@anthropic.com>

* Phase 3: reusable component library for the logging redesign

Adds the ~12 stateless, parameterised Compose primitives every later
redesign screen is assembled from, in ui/components/, previews only.
No existing screen is rewired; nothing is removed.

- SectionHeader: uppercase 11sp section label with right-aligned value
- ListCard / ListRow / HairlineDivider: hairline-outlined card of rows
- StepScale: discrete rising tap-steps; exposes to TalkBack as a single
  control ("Flow, Medium, 3 of 4") with per-step custom actions
- ChipToggle / ChipRow: tonal-fill + check selection chips (FilterChip)
- ToneHero: tonal hero container, Comfortaa word applied explicitly
- SegmentedToggle: single-choice segmented buttons, optional role tint
- RolePicker: 6 in-theme role pills + fixed-colour track, standalone
  (visuals mirrored from the Phase 1 inline picker; ManageCategories
  keeps its copy until a later phase rewires it)
- IconPicker: 48dp icon tile grid with radio semantics + display names
- SwitchRow: full-row toggle, Role.Switch + stateDescription, inner
  Switch has no click handler
- Timeline / TimelineEntry: timestamped multiple-per-day list with
  per-row overflow and an append row
- PrimarySaveBar: sticky 52dp pill with gradient fade
- MetricInput facade stub + MetricConfig/MetricValue value types
  (incl. YesNo/TimeOfDay variants ready for Phase 4)

Every primitive accents from a passed-in role Color (tonal container
fills derived via roleContainerTint, lerp toward surface); each carries
light, dark, and 200% font-scale previews through GoFloTheme so the
extended-roles CompositionLocal path is exercised. a11y_check.py and
wcag_check.py both clean. PLAN.md progress log updated; DB stays at 24.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Phase 4: MetricInput facade + Yes/No and Time input types

Completes the MetricInput facade so it renders every category input type,
and refactors LogCategoryScreen to render through it with behaviour parity:

- CategoryType gains YES_NO ("yes_no") and TIME ("time"). Both store their
  readings as plain value-label strings ("Yes"/"No"; 24-hour "HH:mm") in
  tracking_log_values per the owner's resolution of PLAN.md §8 decision #3:
  no new columns, no migration (DB stays at v24).
- MetricInput: whole-step rating scales (up to 10 steps) render as StepScale;
  decimal or wider ranges keep the parity slider including whole-number
  stepped behaviour, large readout, scale labels, min/max labels, and the
  unset-value hint. numeric_free keeps unit label + placeholder; increment
  keeps the never-below-zero counter (decrement disabled at 0); yes_no
  renders a role-tinted SegmentedToggle; time renders the new TimeField
  primitive (Material time picker, 24h).
- LogCategoryScreen: the inline per-type when is gone; every non-timed input
  renders via MetricInput. Timed increment stays screen-driven (per-tap
  immediate saves, no Save button) and now renders the Timeline primitive.
  Notes 500-char cap, save/update, delete, date selection, edit-existing,
  empty-catalog text, and "previously recorded (removed)" chips unchanged.
- LogCategoryViewModel: additive setSelectedValues; yes_no/time flow through
  selectedValues, so the existing else-branch save path persists them and
  the numeric_free empty-blocks-save / increment <=0-blocks-save rules are
  untouched.
- LogPeriodScreen: PinnedCategoryInput gains additive yes_no/time branches
  delegating to MetricInput (pre-existing branches untouched); additive
  LogPeriodViewModel.setPinnedSingleValue feeds the existing selection-set
  save path.
- TrackingCategory.isNumeric now enumerates the numeric types explicitly so
  yes_no/time chart as label categories in Stats instead of being fed into
  numeric chart math.
- Create flow: the two new types appear automatically in the New Category
  type chips; the unit field is now limited to the genuinely numeric types.
- Docs: PLAN.md §7 row + §8 #3 resolution, subsystem map 01 drift note,
  LESSONS.md entry on negation-defined classifications, changelog fragment
  (minor).

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Extract shared period-day logic into PeriodDaySync

The flow slider mapping (1 Spotting / 2 Light / 4 Heavy / else Medium),
the flow and symptom fan-out into the tracking system, the pinned-category
value rules, and the episode day-number helper move from private members of
LogPeriodViewModel into an internal PeriodDaySync object, so the unified day
screen (next commit) shares the exact same code instead of a copy that could
drift. LogPeriodViewModel delegates to it; behaviour is unchanged.

Also widens LogCategoryScreen's metricConfigFor and TimedIncrementTimeline
from private to internal for the same reuse.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Add unified LogScreen(date): one screen logs a day, period is a state

New LogScreen + LogViewModel compose the Phase 3/4 primitives into one
per-day surface behind the additive route log_day?date={date}:

- Off-period: the first tracked category leads as a ToneHero, flow is not
  rendered, and the footer is a quiet Period-started-today row (which flips
  the screen into its on-period arrangement before saving).
- On-period: the Flow group (StepScale, stepped slider, or chips per the
  category's mode, with the 1 Spotting / 2 Light / 4 Heavy / else Medium
  mapping) slots in at the top with the period dates card, pinned
  showInLogPeriod categories render in the flow context, and the footer is a
  filled status row with End/Undo.
- Between the two states everything is identical: symptoms chips with the
  inline Add dialog, tracked metrics organised by group (two or more members
  render as one ListCard of rows with the active row's input beneath; a group
  of one renders as its own open section), per-entry notes with the 500-char
  cap, previously-recorded chips, track-against-time, per-entry delete, and
  timed-increment timelines with per-tap saves.
- The title and every metric header open a switch sheet organised by group
  and tinted by role: from the title it switches day or jumps to a category;
  from a metric header it re-files the entered value under another category
  without losing it.
- Saving reuses the period screen's exact sequence via PeriodDaySync
  (episode continuation and boundary edits, episode meta, flow/symptom/pinned
  fan-out, widget and prediction-reminder refresh); off-period saves write
  only categories the user touched, using the category screen's per-type
  rules. Remove-day, delete-entire-period, disable-period-logging, and the
  unsaved-changes guard are all ported.

The LogPeriod and LogCategory routes stay registered and every existing
entry point still uses them; the only new entry is an opt-in preview row in
the calendar day sheet. Removal is Phase 8.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Phase 5 bookkeeping: progress log, map drift note, changelog, lesson

Marks Phase 5 done in PLAN.md with its deviations, records the new screen
and the PeriodDaySync extraction in subsystem map 01, adds the minor-bump
changelog fragment, and adds a lesson on translating single-entry save
blocking into per-entry skip rules on a batch save surface.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Fix unresolved semantics property references in the component library

CI failed compileDebugKotlin on PR #180: role/selected/contentDescription/
customActions were assigned inside semantics{} blocks as this.role etc.
without importing the corresponding androidx.compose.ui.semantics extension
properties (importing the Role class does not cover the lowercase role
property). Adds the missing imports to IconPicker, ListCard, RolePicker,
StepScale, and Timeline, and extends the LESSONS.md shadowing entry with
the import requirement. Verified with a semantics-vs-imports sweep over
every main-source Kotlin file plus a11y_check.py.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg

Co-authored-by: Claude <noreply@anthropic.com>

* Merge phase-3 semantics-import fix; fix the same missing import in TimeField

Brings in the phase-3 fix for unresolved semantics extension properties and
applies the same fix to TimeField.kt (Phase 4 file, missing the lowercase
androidx.compose.ui.semantics.role import), found by sweeping every
main-source file with the same checker.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg

Co-authored-by: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
mapgie added a commit that referenced this pull request Aug 26, 2026
* Phase 2: group data model (migration 23 to 24) with colour inheritance

Adds the redesign's only genuinely new table: a category group that owns
a colour role and a default input type. Fully additive; no UI changes.

- Group entity (table `groups`) + GroupDao; TrackingCategory gains a
  nullable groupId (no FK: deleting a group unfiles members instead of
  cascading). MIGRATION_23_24 creates the table and adds the column;
  existing rows keep groupId NULL and their colorToken, so nothing
  changes visually after migrating.
- Group CRUD, reorder, delete-with-unfile, and assign/unassign methods
  on TrackingRepository (new nullable groupDao constructor param, wired
  in GoFloApplication).
- Colour inheritance per PLAN.md section 5: new "inherit" colorToken
  sentinel resolved by TrackingCategory.effectiveColorToken(groups) to
  the group's colorRole, or neutral surfaceVariant when groupless.
  Deliberately not a CategoryColor entry so the Phase 1 role picker
  does not offer it. Existing tokens are untouched (no grey wipe;
  confirmed with owner against the handover's neutral-by-default).
- Migration test (Migration23To24Test): runs the real MIGRATION_23_24
  against a real SQLite v23 schema via sqlite-jdbc (test-only dep),
  routing execSQL through a reflection proxy, and asserts the exact
  schema Room expects plus data preservation. MigrationTestHelper is
  unusable here (no instrumented tests, exportSchema = false).
- PLAN.md section 7 progress log updated; subsystem map 02 updated for
  v24; changelog fragment (minor); LESSONS.md entry on JVM-side Room
  migration testing.

Verified: migration SQL executed against a seeded v23 schema in SQLite
(PRAGMA output matches the test's expected schema exactly);
a11y_check.py and wcag_check.py both clean.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg

Co-authored-by: Claude <noreply@anthropic.com>

* Phase 3: reusable component library for the logging redesign

Adds the ~12 stateless, parameterised Compose primitives every later
redesign screen is assembled from, in ui/components/, previews only.
No existing screen is rewired; nothing is removed.

- SectionHeader: uppercase 11sp section label with right-aligned value
- ListCard / ListRow / HairlineDivider: hairline-outlined card of rows
- StepScale: discrete rising tap-steps; exposes to TalkBack as a single
  control ("Flow, Medium, 3 of 4") with per-step custom actions
- ChipToggle / ChipRow: tonal-fill + check selection chips (FilterChip)
- ToneHero: tonal hero container, Comfortaa word applied explicitly
- SegmentedToggle: single-choice segmented buttons, optional role tint
- RolePicker: 6 in-theme role pills + fixed-colour track, standalone
  (visuals mirrored from the Phase 1 inline picker; ManageCategories
  keeps its copy until a later phase rewires it)
- IconPicker: 48dp icon tile grid with radio semantics + display names
- SwitchRow: full-row toggle, Role.Switch + stateDescription, inner
  Switch has no click handler
- Timeline / TimelineEntry: timestamped multiple-per-day list with
  per-row overflow and an append row
- PrimarySaveBar: sticky 52dp pill with gradient fade
- MetricInput facade stub + MetricConfig/MetricValue value types
  (incl. YesNo/TimeOfDay variants ready for Phase 4)

Every primitive accents from a passed-in role Color (tonal container
fills derived via roleContainerTint, lerp toward surface); each carries
light, dark, and 200% font-scale previews through GoFloTheme so the
extended-roles CompositionLocal path is exercised. a11y_check.py and
wcag_check.py both clean. PLAN.md progress log updated; DB stays at 24.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Phase 4: MetricInput facade + Yes/No and Time input types

Completes the MetricInput facade so it renders every category input type,
and refactors LogCategoryScreen to render through it with behaviour parity:

- CategoryType gains YES_NO ("yes_no") and TIME ("time"). Both store their
  readings as plain value-label strings ("Yes"/"No"; 24-hour "HH:mm") in
  tracking_log_values per the owner's resolution of PLAN.md §8 decision #3:
  no new columns, no migration (DB stays at v24).
- MetricInput: whole-step rating scales (up to 10 steps) render as StepScale;
  decimal or wider ranges keep the parity slider including whole-number
  stepped behaviour, large readout, scale labels, min/max labels, and the
  unset-value hint. numeric_free keeps unit label + placeholder; increment
  keeps the never-below-zero counter (decrement disabled at 0); yes_no
  renders a role-tinted SegmentedToggle; time renders the new TimeField
  primitive (Material time picker, 24h).
- LogCategoryScreen: the inline per-type when is gone; every non-timed input
  renders via MetricInput. Timed increment stays screen-driven (per-tap
  immediate saves, no Save button) and now renders the Timeline primitive.
  Notes 500-char cap, save/update, delete, date selection, edit-existing,
  empty-catalog text, and "previously recorded (removed)" chips unchanged.
- LogCategoryViewModel: additive setSelectedValues; yes_no/time flow through
  selectedValues, so the existing else-branch save path persists them and
  the numeric_free empty-blocks-save / increment <=0-blocks-save rules are
  untouched.
- LogPeriodScreen: PinnedCategoryInput gains additive yes_no/time branches
  delegating to MetricInput (pre-existing branches untouched); additive
  LogPeriodViewModel.setPinnedSingleValue feeds the existing selection-set
  save path.
- TrackingCategory.isNumeric now enumerates the numeric types explicitly so
  yes_no/time chart as label categories in Stats instead of being fed into
  numeric chart math.
- Create flow: the two new types appear automatically in the New Category
  type chips; the unit field is now limited to the genuinely numeric types.
- Docs: PLAN.md §7 row + §8 #3 resolution, subsystem map 01 drift note,
  LESSONS.md entry on negation-defined classifications, changelog fragment
  (minor).

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Extract shared period-day logic into PeriodDaySync

The flow slider mapping (1 Spotting / 2 Light / 4 Heavy / else Medium),
the flow and symptom fan-out into the tracking system, the pinned-category
value rules, and the episode day-number helper move from private members of
LogPeriodViewModel into an internal PeriodDaySync object, so the unified day
screen (next commit) shares the exact same code instead of a copy that could
drift. LogPeriodViewModel delegates to it; behaviour is unchanged.

Also widens LogCategoryScreen's metricConfigFor and TimedIncrementTimeline
from private to internal for the same reuse.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Add unified LogScreen(date): one screen logs a day, period is a state

New LogScreen + LogViewModel compose the Phase 3/4 primitives into one
per-day surface behind the additive route log_day?date={date}:

- Off-period: the first tracked category leads as a ToneHero, flow is not
  rendered, and the footer is a quiet Period-started-today row (which flips
  the screen into its on-period arrangement before saving).
- On-period: the Flow group (StepScale, stepped slider, or chips per the
  category's mode, with the 1 Spotting / 2 Light / 4 Heavy / else Medium
  mapping) slots in at the top with the period dates card, pinned
  showInLogPeriod categories render in the flow context, and the footer is a
  filled status row with End/Undo.
- Between the two states everything is identical: symptoms chips with the
  inline Add dialog, tracked metrics organised by group (two or more members
  render as one ListCard of rows with the active row's input beneath; a group
  of one renders as its own open section), per-entry notes with the 500-char
  cap, previously-recorded chips, track-against-time, per-entry delete, and
  timed-increment timelines with per-tap saves.
- The title and every metric header open a switch sheet organised by group
  and tinted by role: from the title it switches day or jumps to a category;
  from a metric header it re-files the entered value under another category
  without losing it.
- Saving reuses the period screen's exact sequence via PeriodDaySync
  (episode continuation and boundary edits, episode meta, flow/symptom/pinned
  fan-out, widget and prediction-reminder refresh); off-period saves write
  only categories the user touched, using the category screen's per-type
  rules. Remove-day, delete-entire-period, disable-period-logging, and the
  unsaved-changes guard are all ported.

The LogPeriod and LogCategory routes stay registered and every existing
entry point still uses them; the only new entry is an opt-in preview row in
the calendar day sheet. Removal is Phase 8.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Phase 5 bookkeeping: progress log, map drift note, changelog, lesson

Marks Phase 5 done in PLAN.md with its deviations, records the new screen
and the PeriodDaySync extraction in subsystem map 01, adds the minor-bump
changelog fragment, and adds a lesson on translating single-entry save
blocking into per-entry skip rules on a batch save surface.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Fix unresolved semantics property references in the component library

CI failed compileDebugKotlin on PR #180: role/selected/contentDescription/
customActions were assigned inside semantics{} blocks as this.role etc.
without importing the corresponding androidx.compose.ui.semantics extension
properties (importing the Role class does not cover the lowercase role
property). Adds the missing imports to IconPicker, ListCard, RolePicker,
StepScale, and Timeline, and extends the LESSONS.md shadowing entry with
the import requirement. Verified with a semantics-vs-imports sweep over
every main-source Kotlin file plus a11y_check.py.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg

Co-authored-by: Claude <noreply@anthropic.com>

* Merge phase-3 semantics-import fix; fix the same missing import in TimeField

Brings in the phase-3 fix for unresolved semantics extension properties and
applies the same fix to TimeField.kt (Phase 4 file, missing the lowercase
androidx.compose.ui.semantics.role import), found by sweeping every
main-source file with the same checker.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg

Co-authored-by: Claude <noreply@anthropic.com>

* Phase 6: redesign What You Track home with first-class groups

ManageCategoriesScreen becomes the Grouped/Ungrouped management home from
the row-4 mock: a SegmentedToggle swaps between role-tinted group cards
(colour dot + name + Edit, member rows resolved via effectiveColorToken,
inline "Add category to this group") and the neutral ungrouped list, each
row carrying an "Add to group" affordance.

- Add-to-group sheet: pick a group, jump to pre-filled group creation, or
  remove from the current group; a "Use the group's colour" switch sets the
  category's token to the "inherit" sentinel so it adopts and follows the
  group's role. Past entries are always kept.
- Group-centric add-member sheet lists ungrouped categories first, then
  members of other groups labelled with where they live, plus a New
  category row that pre-selects the group's default input type and files
  the category on creation.
- Group editor dialog: name, in-theme role via RolePicker (new additive
  showFixedSection flag hides the hex track: a group's role is never a
  hex), default input type, member unfiling, move up/down reorder, and
  delete with a confirmation explaining members are kept.
- ManageCategoriesViewModel gains the groups Flow and group CRUD /
  moveGroup / assign (with adopt-colour) / unassign on the Phase 2
  repository API. No DB changes (schema stays v24).
- Preserved unchanged: archive/unarchive and delete swipes with system
  protection, global drag reorder via the toolbar reorder mode, category
  creation dialog, ManageCategoryValues navigation, tracking modes and
  quick-log surfaces, archived section.
- Help dialog documents groups and the two views; PLAN.md §7 and map 02
  updated; changelog fragment (minor); LESSONS.md entry on snapshotting
  Compose state into locals before async completion callbacks.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
mapgie added a commit that referenced this pull request Aug 26, 2026
…s on edit (#184)

* Phase 2: group data model (migration 23 to 24) with colour inheritance

Adds the redesign's only genuinely new table: a category group that owns
a colour role and a default input type. Fully additive; no UI changes.

- Group entity (table `groups`) + GroupDao; TrackingCategory gains a
  nullable groupId (no FK: deleting a group unfiles members instead of
  cascading). MIGRATION_23_24 creates the table and adds the column;
  existing rows keep groupId NULL and their colorToken, so nothing
  changes visually after migrating.
- Group CRUD, reorder, delete-with-unfile, and assign/unassign methods
  on TrackingRepository (new nullable groupDao constructor param, wired
  in GoFloApplication).
- Colour inheritance per PLAN.md section 5: new "inherit" colorToken
  sentinel resolved by TrackingCategory.effectiveColorToken(groups) to
  the group's colorRole, or neutral surfaceVariant when groupless.
  Deliberately not a CategoryColor entry so the Phase 1 role picker
  does not offer it. Existing tokens are untouched (no grey wipe;
  confirmed with owner against the handover's neutral-by-default).
- Migration test (Migration23To24Test): runs the real MIGRATION_23_24
  against a real SQLite v23 schema via sqlite-jdbc (test-only dep),
  routing execSQL through a reflection proxy, and asserts the exact
  schema Room expects plus data preservation. MigrationTestHelper is
  unusable here (no instrumented tests, exportSchema = false).
- PLAN.md section 7 progress log updated; subsystem map 02 updated for
  v24; changelog fragment (minor); LESSONS.md entry on JVM-side Room
  migration testing.

Verified: migration SQL executed against a seeded v23 schema in SQLite
(PRAGMA output matches the test's expected schema exactly);
a11y_check.py and wcag_check.py both clean.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg

Co-authored-by: Claude <noreply@anthropic.com>

* Phase 3: reusable component library for the logging redesign

Adds the ~12 stateless, parameterised Compose primitives every later
redesign screen is assembled from, in ui/components/, previews only.
No existing screen is rewired; nothing is removed.

- SectionHeader: uppercase 11sp section label with right-aligned value
- ListCard / ListRow / HairlineDivider: hairline-outlined card of rows
- StepScale: discrete rising tap-steps; exposes to TalkBack as a single
  control ("Flow, Medium, 3 of 4") with per-step custom actions
- ChipToggle / ChipRow: tonal-fill + check selection chips (FilterChip)
- ToneHero: tonal hero container, Comfortaa word applied explicitly
- SegmentedToggle: single-choice segmented buttons, optional role tint
- RolePicker: 6 in-theme role pills + fixed-colour track, standalone
  (visuals mirrored from the Phase 1 inline picker; ManageCategories
  keeps its copy until a later phase rewires it)
- IconPicker: 48dp icon tile grid with radio semantics + display names
- SwitchRow: full-row toggle, Role.Switch + stateDescription, inner
  Switch has no click handler
- Timeline / TimelineEntry: timestamped multiple-per-day list with
  per-row overflow and an append row
- PrimarySaveBar: sticky 52dp pill with gradient fade
- MetricInput facade stub + MetricConfig/MetricValue value types
  (incl. YesNo/TimeOfDay variants ready for Phase 4)

Every primitive accents from a passed-in role Color (tonal container
fills derived via roleContainerTint, lerp toward surface); each carries
light, dark, and 200% font-scale previews through GoFloTheme so the
extended-roles CompositionLocal path is exercised. a11y_check.py and
wcag_check.py both clean. PLAN.md progress log updated; DB stays at 24.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Phase 4: MetricInput facade + Yes/No and Time input types

Completes the MetricInput facade so it renders every category input type,
and refactors LogCategoryScreen to render through it with behaviour parity:

- CategoryType gains YES_NO ("yes_no") and TIME ("time"). Both store their
  readings as plain value-label strings ("Yes"/"No"; 24-hour "HH:mm") in
  tracking_log_values per the owner's resolution of PLAN.md §8 decision #3:
  no new columns, no migration (DB stays at v24).
- MetricInput: whole-step rating scales (up to 10 steps) render as StepScale;
  decimal or wider ranges keep the parity slider including whole-number
  stepped behaviour, large readout, scale labels, min/max labels, and the
  unset-value hint. numeric_free keeps unit label + placeholder; increment
  keeps the never-below-zero counter (decrement disabled at 0); yes_no
  renders a role-tinted SegmentedToggle; time renders the new TimeField
  primitive (Material time picker, 24h).
- LogCategoryScreen: the inline per-type when is gone; every non-timed input
  renders via MetricInput. Timed increment stays screen-driven (per-tap
  immediate saves, no Save button) and now renders the Timeline primitive.
  Notes 500-char cap, save/update, delete, date selection, edit-existing,
  empty-catalog text, and "previously recorded (removed)" chips unchanged.
- LogCategoryViewModel: additive setSelectedValues; yes_no/time flow through
  selectedValues, so the existing else-branch save path persists them and
  the numeric_free empty-blocks-save / increment <=0-blocks-save rules are
  untouched.
- LogPeriodScreen: PinnedCategoryInput gains additive yes_no/time branches
  delegating to MetricInput (pre-existing branches untouched); additive
  LogPeriodViewModel.setPinnedSingleValue feeds the existing selection-set
  save path.
- TrackingCategory.isNumeric now enumerates the numeric types explicitly so
  yes_no/time chart as label categories in Stats instead of being fed into
  numeric chart math.
- Create flow: the two new types appear automatically in the New Category
  type chips; the unit field is now limited to the genuinely numeric types.
- Docs: PLAN.md §7 row + §8 #3 resolution, subsystem map 01 drift note,
  LESSONS.md entry on negation-defined classifications, changelog fragment
  (minor).

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Extract shared period-day logic into PeriodDaySync

The flow slider mapping (1 Spotting / 2 Light / 4 Heavy / else Medium),
the flow and symptom fan-out into the tracking system, the pinned-category
value rules, and the episode day-number helper move from private members of
LogPeriodViewModel into an internal PeriodDaySync object, so the unified day
screen (next commit) shares the exact same code instead of a copy that could
drift. LogPeriodViewModel delegates to it; behaviour is unchanged.

Also widens LogCategoryScreen's metricConfigFor and TimedIncrementTimeline
from private to internal for the same reuse.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Add unified LogScreen(date): one screen logs a day, period is a state

New LogScreen + LogViewModel compose the Phase 3/4 primitives into one
per-day surface behind the additive route log_day?date={date}:

- Off-period: the first tracked category leads as a ToneHero, flow is not
  rendered, and the footer is a quiet Period-started-today row (which flips
  the screen into its on-period arrangement before saving).
- On-period: the Flow group (StepScale, stepped slider, or chips per the
  category's mode, with the 1 Spotting / 2 Light / 4 Heavy / else Medium
  mapping) slots in at the top with the period dates card, pinned
  showInLogPeriod categories render in the flow context, and the footer is a
  filled status row with End/Undo.
- Between the two states everything is identical: symptoms chips with the
  inline Add dialog, tracked metrics organised by group (two or more members
  render as one ListCard of rows with the active row's input beneath; a group
  of one renders as its own open section), per-entry notes with the 500-char
  cap, previously-recorded chips, track-against-time, per-entry delete, and
  timed-increment timelines with per-tap saves.
- The title and every metric header open a switch sheet organised by group
  and tinted by role: from the title it switches day or jumps to a category;
  from a metric header it re-files the entered value under another category
  without losing it.
- Saving reuses the period screen's exact sequence via PeriodDaySync
  (episode continuation and boundary edits, episode meta, flow/symptom/pinned
  fan-out, widget and prediction-reminder refresh); off-period saves write
  only categories the user touched, using the category screen's per-type
  rules. Remove-day, delete-entire-period, disable-period-logging, and the
  unsaved-changes guard are all ported.

The LogPeriod and LogCategory routes stay registered and every existing
entry point still uses them; the only new entry is an opt-in preview row in
the calendar day sheet. Removal is Phase 8.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Phase 5 bookkeeping: progress log, map drift note, changelog, lesson

Marks Phase 5 done in PLAN.md with its deviations, records the new screen
and the PeriodDaySync extraction in subsystem map 01, adds the minor-bump
changelog fragment, and adds a lesson on translating single-entry save
blocking into per-entry skip rules on a batch save surface.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Fix unresolved semantics property references in the component library

CI failed compileDebugKotlin on PR #180: role/selected/contentDescription/
customActions were assigned inside semantics{} blocks as this.role etc.
without importing the corresponding androidx.compose.ui.semantics extension
properties (importing the Role class does not cover the lowercase role
property). Adds the missing imports to IconPicker, ListCard, RolePicker,
StepScale, and Timeline, and extends the LESSONS.md shadowing entry with
the import requirement. Verified with a semantics-vs-imports sweep over
every main-source Kotlin file plus a11y_check.py.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg

Co-authored-by: Claude <noreply@anthropic.com>

* Merge phase-3 semantics-import fix; fix the same missing import in TimeField

Brings in the phase-3 fix for unresolved semantics extension properties and
applies the same fix to TimeField.kt (Phase 4 file, missing the lowercase
androidx.compose.ui.semantics.role import), found by sweeping every
main-source file with the same checker.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg

Co-authored-by: Claude <noreply@anthropic.com>

* Phase 6: redesign What You Track home with first-class groups

ManageCategoriesScreen becomes the Grouped/Ungrouped management home from
the row-4 mock: a SegmentedToggle swaps between role-tinted group cards
(colour dot + name + Edit, member rows resolved via effectiveColorToken,
inline "Add category to this group") and the neutral ungrouped list, each
row carrying an "Add to group" affordance.

- Add-to-group sheet: pick a group, jump to pre-filled group creation, or
  remove from the current group; a "Use the group's colour" switch sets the
  category's token to the "inherit" sentinel so it adopts and follows the
  group's role. Past entries are always kept.
- Group-centric add-member sheet lists ungrouped categories first, then
  members of other groups labelled with where they live, plus a New
  category row that pre-selects the group's default input type and files
  the category on creation.
- Group editor dialog: name, in-theme role via RolePicker (new additive
  showFixedSection flag hides the hex track: a group's role is never a
  hex), default input type, member unfiling, move up/down reorder, and
  delete with a confirmation explaining members are kept.
- ManageCategoriesViewModel gains the groups Flow and group CRUD /
  moveGroup / assign (with adopt-colour) / unassign on the Phase 2
  repository API. No DB changes (schema stays v24).
- Preserved unchanged: archive/unarchive and delete swipes with system
  protection, global drag reorder via the toolbar reorder mode, category
  creation dialog, ManageCategoryValues navigation, tracking modes and
  quick-log surfaces, archived section.
- Help dialog documents groups and the two views; PLAN.md §7 and map 02
  updated; changelog fragment (minor); LESSONS.md entry on snapshotting
  Compose state into locals before async completion callbacks.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

* Phase 7: 2-step category create/edit flow, scale settings step, alarms on edit

New CategoryEditScreen + CategoryEditViewModel (route category_edit?categoryId&groupId)
replace AddCategoryDialog's role in navigation:

- Create step 1: name, IconPicker, RolePicker (6 roles + fixed hex + custom HSV
  slot), input-type chips (all 6 types), unit for the numeric family, and
  allow-multiple / log-with-period / track-against-time switch rows. Creating
  from inside a group pre-selects the group's default input type, files the
  category on save, and offers a "Use the group's colour" switch that writes
  the "inherit" sentinel (consistent with the Phase 6 adopt-colour sheets).
- Create step 2 (numeric_slider only): min/max range, per-step word labels,
  allow-decimals; both steps share one route with in-screen step state so the
  half-built form never crosses navigation.
- Edit: the same surface prefilled, plus a Reminders section wired to the
  existing CustomAlarm/EditAlarm system (rows open EditAlarm, switches toggle
  scheduling via ReminderScheduler), a "Scale settings" row into step 2, and a
  danger-zone delete-with-history row (system categories protected).
- Owner decision (PLAN.md section 8 item 2): categoryType is "fixed once
  logged" - editable until the category has a tracking log, then locked with a
  one-line explanation. Checked live via additive TrackingLogDao.
  countLogsForCategory / TrackingRepository.hasLogs and re-guarded at save.
- ManageCategoryValues keeps values/settings/alarms unchanged; its rename and
  palette dialog actions are superseded by one Edit action opening the new
  flow. Superseded dialogs stay in place for Phase 8 removal.

DB stays at v24. Changelog fragment (minor), PLAN.md section 7/8 updated,
subsystem map 02 drift-noted, LESSONS.md entry added. a11y_check, wcag_check,
and the semantics-import sweep are clean.

Claude-Session: https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Co-authored-by: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants