Skip to content

Phase 4: MetricInput facade + Yes/No and Time input types - #181

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

Phase 4: MetricInput facade + Yes/No and Time input types#181
mapgie merged 8 commits into
mainfrom
claude/logging-redesign-phase-4

Conversation

@mapgie

@mapgie mapgie commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Summary

Phase 4 of the logging redesign (build guide): the MetricInput facade now renders every input type, LogCategoryScreen renders through it instead of its inline per-type when, and two new category input types ship: Yes/No and Time.

Storage (PLAN.md §8 decision #3, resolved by the owner): Yes/No and Time store their readings as plain value-label strings in tracking_log_values: "Yes"/"No" for yes_no, 24-hour "HH:mm" for time. No new DB columns, no migration; DB stays at v24. Documented at the top of MetricInput.kt and in PLAN.md §7/§8.

What changed, by file:

  • ui/util/CategoryAppearance.kt: CategoryType gains YES_NO("yes_no", "Yes / No") and TIME("time", "Time"). Persisted keys are stable strings; display names show in the New Category type chips.
  • ui/components/MetricInput.kt: full facade. Whole-step rating scales (2 to 10 steps, no decimals) render as StepScale; decimal or wider ranges keep a slider with full parity (whole-number stepped behaviour, large readout, scale label, min/max labels, unset-value hint). numeric_free keeps unit label, placeholder, decimal keyboard. increment counter never drops below 0 and shows the unit. yes_no renders a role-tinted SegmentedToggle; time renders the new TimeField. usesStepScale() is exposed so screens can mirror the variant split.
  • ui/components/TimeField.kt (new): tap-to-pick time field opening a Material 24h time picker; Button role semantics, hoisted state.
  • ui/screens/log/LogCategoryScreen.kt: the per-type rendering when is gone; every non-timed input renders via one MetricInput call. A small pure mapping (metricValueFor) converts screen state to MetricValue, and the timed-increment path stays screen-driven (per-tap immediate saves, no Save button), now rendering the Timeline primitive with per-entry delete.
  • ui/screens/log/LogCategoryViewModel.kt: additive setSelectedValues. Yes/No and Time readings flow through selectedValues as a single-label set, so loading, setDate re-resolution, and the existing else-branch of save() persist them with no new save logic.
  • ui/screens/log/LogPeriodScreen.kt + LogPeriodViewModel.kt: PinnedCategoryInput gains additive yes_no/time branches delegating to MetricInput; its four pre-existing branches are untouched, and the new additive setPinnedSingleValue feeds the existing selection-set save path (computePinnedValues else-branch). No restructuring of the period screen.
  • data/database/entities/TrackingCategory.kt: isNumeric re-defined from "anything but default" to an explicit list of the numeric types, so yes_no/time chart as label categories in Stats (Yes/No pie/trends work for free) instead of being fed into numeric chart math.
  • ui/screens/categories/ManageCategoriesScreen.kt: the New Category dialog picks up the two new types automatically from CategoryType.entries; the unit field is now shown only for the genuinely numeric types.
  • Docs: PLAN.md §7 Phase 4 row (Done, DB 24 unchanged, deviations) and §8 Fix Kotlin compile errors blocking CI build #3 marked resolved; subsystem map 01 drift note; LESSONS.md entry; changelog fragment logging-yesno-time-metricinput.json (minor).

Parity walk (map 01 §2, branch by branch)

# Path Where it lives now Stored value
1 numeric_slider MetricInput NUMERIC_SLIDER: StepScale for whole-step ratings up to 10 steps, parity slider otherwise (same steps formula, readout, scale labels, min/max labels, hint) unchanged: int or %.1f string; unset still saves numericMin via the untouched VM fallback
2 numeric_free MetricInput NUMERIC_FREE (unit label, placeholder, decimal keyboard) unchanged trimmed text; empty still blocks save (VM rule untouched)
3 increment + trackAgainstTime screen-level TimedIncrementTimeline (count card + Timeline); per-tap immediate save with HH:mm via untouched addTimedIncrement, per-entry delete, no notes/Save unchanged ("1" per entry + loggedAt)
4 increment untimed MetricInput INCREMENT counter (floor 0, decrement disabled at 0) unchanged int string; count <= 0 still blocks save (VM rule untouched)
5 default chips MetricInput DEFAULT ChipRow; empty-catalog text and "Previously recorded (removed from options)" chips kept at screen level verbatim unchanged label set
6 yes_no (new) MetricInput YES_NO SegmentedToggle "Yes" / "No" value label
7 time (new) MetricInput TIME TimeField "HH:mm" value label

Shared extras confirmed intact: notes 500-char cap, Save/Update, delete-with-confirm, DateSelectorCard when canEditDate, edit-vs-new resolution, track-against-time checkbox, pop-back on save/delete.

Acceptance criteria (guide)

  • All 7 paths log + edit correctly; stored values identical to main for the 5 existing ones (verified by inspection against the untouched LogCategoryViewModel.save() / repository paths; CI is the build check)
  • Yes/No and Time categories can be created via the current create flow and logged
  • LogCategoryScreen no longer contains a categoryType when for rendering (it calls MetricInput; the remaining whens are pure state-to-MetricValue mapping and the guide-sanctioned timed-increment screen flow)
  • a11y_check.py green (also wcag_check.py: 405 pairs, all passed)
  • Builds green in CI

Feature-preservation checklist (guide)

  • Every bullet in the parity procedure passes (table above)
  • LogPeriodScreen still works; PinnedCategoryInput's existing four branches untouched, new types additive
  • Period fan-out (syncFlowToTrackingLog, syncSymptomsToTrackingLog, syncPinnedCategoryLogs) unaffected (no changes to those paths; computePinnedValues untouched)
  • Stats/History still read the same value labels (isNumeric change only reroutes the two new types to the label chart family; the four existing types keep their prior classification)

Notes / deviations

  • Role colour on the log screens stays colorScheme.primary (the pre-existing accent) rather than the category's own role token; wiring category roles into the log flow is Phase 5's unified screen.
  • With StepScale, an untouched rating shows no selected step (previously the slider sat at the minimum) but still saves numericMin, preserving stored-data parity.
  • The timed-increment entry rows previously showed a bare en dash for a missing timestamp; they now show "No time" (also removes an en dash from user-facing text).
  • Editing a yes_no/time category from Manage Categories still opens the default value-catalog editor (harmless: their inputs ignore the catalog; the create/edit flow is redesigned in Phase 7).

🤖 Generated with Claude Code

https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg


Generated by Claude Code

claude added 7 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>
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>
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>
…meField

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>
Base automatically changed from claude/logging-redesign-phase-3 to main August 26, 2026 01:09
@mapgie
mapgie marked this pull request as ready for review August 26, 2026 01:09
…gn-phase-4

# Conflicts:
#	app/src/main/java/com/mapgie/goflo/ui/components/MetricInput.kt
#	docs/design/logging-redesign/PLAN.md
@mapgie
mapgie merged commit e4cfd52 into main Aug 26, 2026
4 checks passed
@mapgie
mapgie deleted the claude/logging-redesign-phase-4 branch August 26, 2026 08:17
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