Phase 4: MetricInput facade + Yes/No and Time input types - #181
Merged
Conversation
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>
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>
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 4 of the logging redesign (build guide): the
MetricInputfacade now renders every input type,LogCategoryScreenrenders through it instead of its inline per-typewhen, 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 ofMetricInput.ktand in PLAN.md §7/§8.What changed, by file:
ui/util/CategoryAppearance.kt:CategoryTypegainsYES_NO("yes_no", "Yes / No")andTIME("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 asStepScale; 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_freekeeps unit label, placeholder, decimal keyboard.incrementcounter never drops below 0 and shows the unit.yes_norenders a role-tintedSegmentedToggle;timerenders the newTimeField.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 renderingwhenis gone; every non-timed input renders via oneMetricInputcall. A small pure mapping (metricValueFor) converts screen state toMetricValue, and the timed-increment path stays screen-driven (per-tap immediate saves, no Save button), now rendering theTimelineprimitive with per-entry delete.ui/screens/log/LogCategoryViewModel.kt: additivesetSelectedValues. Yes/No and Time readings flow throughselectedValuesas a single-label set, so loading,setDatere-resolution, and the existing else-branch ofsave()persist them with no new save logic.ui/screens/log/LogPeriodScreen.kt+LogPeriodViewModel.kt:PinnedCategoryInputgains additiveyes_no/timebranches delegating toMetricInput; its four pre-existing branches are untouched, and the new additivesetPinnedSingleValuefeeds the existing selection-set save path (computePinnedValueselse-branch). No restructuring of the period screen.data/database/entities/TrackingCategory.kt:isNumericre-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 fromCategoryType.entries; the unit field is now shown only for the genuinely numeric types.logging-yesno-time-metricinput.json(minor).Parity walk (map 01 §2, branch by branch)
numeric_sliderMetricInputNUMERIC_SLIDER:StepScalefor whole-step ratings up to 10 steps, parity slider otherwise (samestepsformula, readout, scale labels, min/max labels, hint)%.1fstring; unset still savesnumericMinvia the untouched VM fallbacknumeric_freeMetricInputNUMERIC_FREE (unit label, placeholder, decimal keyboard)increment+trackAgainstTimeTimedIncrementTimeline(count card +Timeline); per-tap immediate save withHH:mmvia untouchedaddTimedIncrement, per-entry delete, no notes/Save"1"per entry +loggedAt)incrementuntimedMetricInputINCREMENT counter (floor 0, decrement disabled at 0)defaultchipsMetricInputDEFAULTChipRow; empty-catalog text and "Previously recorded (removed from options)" chips kept at screen level verbatimyes_no(new)MetricInputYES_NOSegmentedToggle"Yes"/"No"value labeltime(new)MetricInputTIMETimeField"HH:mm"value labelShared extras confirmed intact: notes 500-char cap, Save/Update, delete-with-confirm,
DateSelectorCardwhencanEditDate, edit-vs-new resolution, track-against-time checkbox, pop-back on save/delete.Acceptance criteria (guide)
mainfor the 5 existing ones (verified by inspection against the untouchedLogCategoryViewModel.save()/ repository paths; CI is the build check)LogCategoryScreenno longer contains acategoryTypewhenfor rendering (it callsMetricInput; the remainingwhens are pure state-to-MetricValuemapping and the guide-sanctioned timed-increment screen flow)a11y_check.pygreen (alsowcag_check.py: 405 pairs, all passed)Feature-preservation checklist (guide)
LogPeriodScreenstill works;PinnedCategoryInput's existing four branches untouched, new types additivesyncFlowToTrackingLog,syncSymptomsToTrackingLog,syncPinnedCategoryLogs) unaffected (no changes to those paths;computePinnedValuesuntouched)isNumericchange only reroutes the two new types to the label chart family; the four existing types keep their prior classification)Notes / deviations
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.StepScale, an untouched rating shows no selected step (previously the slider sat at the minimum) but still savesnumericMin, preserving stored-data parity.🤖 Generated with Claude Code
https://claude.ai/code/session_01PZJLynVBkgLtehJFXffnfg
Generated by Claude Code