- Updated TimeSelector.vue styles for smaller dimensions and font sizes. - Added new API proxy for '/events' in vite.config.ts. - Created bug specifications for various UI issues and fixes in bugs-1.0.5-001.md and bugs-1.0.5-002.md. - Introduced TimePickerPopover.vue for a new time selection interface in the chore scheduler. - Refactored ScheduleModal.vue to replace checkbox rows with a chip-based design for selecting specific days. - Enhanced chore scheduling logic to ensure proper handling of time extensions and UI updates.
8.0 KiB
Feature: Daily chore scheduler refactor phase 1
Overview
Parent Feature: .github/feat-calenar-chore/feat-calendar-chore.md
Goal: UI refactor of the 'Specific Days' portion of the chore scheduler so that it is not so complicated.
User Story:
- As a parent, I will be able to select an assigned chore and configure it to occur on 'Specific Days' as before, except I will be presented with a much easier to use interface.
Rules:
- Follow instructions from .github/copilot-instructions.md
Design:
- Keep the UI for 'Every X Days' the same for now, this will change in phase 2
- Remove days of the week, time selectors, and checkboxes
- Follow at 'Default Time' pattern with optional time for expiry
Architecture:
- The Design Logic The interface shifts from a list of tasks to a set of rules.
- The "Base" State: You see 7 day chips (Su, Mo, Tu, We, Th, Fr, Sa) and one "Default Deadline" box.
- The "Active" State: Days you click become "Active."
- The "Silent" State: Any day not clicked is ignored by the system.
- The Architecture Think of the system as having two layers of memory:
- The Global Layer: This holds the "Master Time" (e.g., 8:00 AM).
- The Exception Layer: This is an empty list that only fills up if you explicitly say a day is "special."
- The Merge Logic: When the system saves, it looks at each selected day. It asks: "Does this day have a special time in the Exception Layer? No? Okay, then use the Master Time."
- The "When/Then" Flow Here is exactly how the interaction feels for the user:
-
Step A: Establishing the Routine When you click Monday, Wednesday, and Friday... Then those days highlight, and the "Default Deadline" box becomes active. When you set that box to 8:00 AM... Then the system internally marks all three days as "8:00 AM."
-
Step B: Adding a Day () When you suddenly decide to add Sunday... Then Sunday highlights and automatically adopts the 8:00 AM deadline.
-
Step C: Breaking the Routine When you click "Set different time" and choose Sunday... Then a new, specific time box appears just for Sunday. When you change Sunday to 11:00 AM... Then the system "unhooks" Sunday from the Master Time. Sunday is now an Exception.
-
Step D: Changing the Master Time When you later change the "Default Deadline" from 8:00 AM to 9:00 AM... Then Monday, Wednesday, and Friday all update to 9:00 AM automatically. But Sunday stays at 11:00 AM because it is locked as an exception.
Instead of treating all 7 days as individual, equal data points, we treat them as a Group that follows a Rule.
- The Group: The days you selected (e.g., Mon, Wed, Fri, Sun).
- The Rule: The "Default Deadline" that applies to the whole group (e.g., 8:00 AM).
- The Exception: A specific day that breaks the rule (e.g., "Actually, make Sunday 11:00 AM").
Time Selector Design: We might need to create a new time selector or just add an additional time selector component
- The "Columnar" Picker This popover is split into three distinct columns: Hours, Minutes, and AM/PM. When you click the time box... Then a small panel opens with three narrow, scrollable columns. The Logic: The "Minutes" column only contains four options: :00, :15, :30, :45. The Flow: The user's eye scans horizontally. "8" → "30" → "PM".
The "High-Level" Combined Flow: Selection: User clicks Monday. Trigger: User clicks the "Deadline" box. The Picker: The Columnar Picker pops up. The Snap: The user clicks "8" in the first column and "00" in the second. The Result: The box now displays "08:00 AM."
The "Auto-Apply" Flow When the user clicks a value (e.g., "AM" or "PM"), the selection is registered immediately. When the user clicks anywhere outside the popover, it closes automatically. Then the main UI updates to show the new time.
Data Model Changes
Backend Models
ChoreSchedule gains two new fields (persisted in TinyDB, returned in API responses):
default_hour: int = 8— the master deadline hour formode='days'default_minute: int = 0— the master deadline minute formode='days'
from_dict defaults both to 8 / 0 for backwards compatibility with existing DB records.
Frontend Models
ChoreSchedule interface gains two optional fields (optional for backwards compat with old API responses):
default_hour?: numberdefault_minute?: number
Frontend Design
TimePickerPopover.vue— new shared component atfrontend/vue-app/src/components/shared/TimePickerPopover.vueScheduleModal.vue— "Specific Days" section fully replaced; "Every X Days" section unchanged
Backend Implementation
No backend implementation required for phase 1.
Backend Tests
- No backend changes required in phase 1
Frontend Implementation
- Created
frontend/vue-app/src/components/shared/TimePickerPopover.vue- Props:
modelValue: { hour: number, minute: number }, emitsupdate:modelValue - Displays formatted time as a clickable button (e.g.
08:00 AM) - Opens a columnar popover with three columns: Hour (1–12), Minute (:00/:15/:30/:45), AM/PM
- Clicking any column value updates the model immediately
- Closes on outside click via
mousedowndocument listener - Fully scoped styles using CSS variables from
colors.css
- Props:
- Refactored
ScheduleModal.vue— "Specific Days" section- Replaced 7 checkbox rows + per-row
TimeSelectorwith chip-based design - Day chips row: 7 short chips (Su Mo Tu We Th Fr Sa) — click to toggle active/inactive
- Default Deadline row: shown when ≥1 day selected; single
TimePickerPopoversets the master time for all non-exception days - Selected day list: one row per active day (sorted Sun→Sat); each row shows:
- Day name
- If no exception: italic "Default (HH:MM AM/PM)" label + "Set different time" link
- If exception set: a
TimePickerPopoverfor that day's override + "Reset to default" link
- State:
selectedDays: Set<number>,defaultTime: TimeValue,exceptions: Map<number, TimeValue> - Load logic: first
day_configentry setsdefaultTime; entries differing from it populateexceptions - Save logic: iterates
selectedDays, applies exception time or falls back todefaultTime→DayConfig[] - "Every X Days" mode left unchanged
- Validation: unchanged (
selectedDays.size > 0)
- Replaced 7 checkbox rows + per-row
Frontend Tests
TimePickerPopover.vue: renders formatted time, opens/closes popover, selecting hour/minute/period emits correct value, closes on outside clickScheduleModal.vue(Specific Days): chip toggles add/remove from selected set; removing a day also removes its exception; setting a different time creates an exception; resetting removes exception; changing default time does not override exceptions; save payload shape matchesDayConfig[]with correct times; loading an existing mixed-time schedule restores chips, defaultTime, and exceptions correctly
Future Considerations
Acceptance Criteria (Definition of Done)
Backend
- No backend changes required — existing
DayConfig { day, hour, minute }model fully supports the new UI
Frontend
- "Specific Days" mode shows 7 day chips instead of checkboxes
- Selecting chips shows a single "Default Deadline" time picker
- Selected day list shows each active day with either its default label or an exception time picker
- "Set different time" link creates a per-day exception that overrides the default
- "Reset to default" link removes the exception and the day reverts to the master time
- Changing the Default Deadline updates all non-exception days (by using
defaultTimeat save time) - "Every X Days" mode is unchanged
- Existing schedules load correctly (first entry = default, differing times = exceptions)
- Save payload is valid
DayConfig[]consumed by the existing API unchanged - New
TimePickerPopovercomponent: columnar Hour/Minute/AMPM picker, closes on outside click - Frontend component tests written and passing for
TimePickerPopoverand the refactoredScheduleModal