Skip to content

Flexible chore scheduling: calendar-event triggers, completion-relative recurrence, multi-user assignment - #185

Closed
adamecker wants to merge 6 commits into
jherforth:mainfrom
adamecker:pr/chore-scheduling
Closed

adamecker wants to merge 6 commits into
jherforth:mainfrom
adamecker:pr/chore-scheduling

Conversation

@adamecker

Copy link
Copy Markdown

Summary

Overhauls chore scheduling in the Admin Panel (schema 27):

  • Unified schedule editor: presets, custom crontab, one-time, and completion-relative modes in one form.
  • Calendar-event triggers: a schedule can fire based on a calendar event title keyword (calendar_match). The chore becomes due at the event start and stays visible all day.
  • Completion-relative recurrence: "once completed" and "until completed" modes with configurable intervals.
  • Multi-user assignment and per-schedule custom emoji.
  • The schedule list now shows an explicit calendar-match indicator (✓/✗ with a tooltip) so a keyword that stops matching — e.g. a typo like Columbia vs the event's Colombia — is visible at a glance.

Bug fixes included

  • All-day calendar events were matched against the wrong day on servers behind UTC (SQLite localtime shifted UTC-midnight events to the previous date). All-day events now compare date(start_time) directly.
  • PATCH /api/chore-schedules/:id silently dropped calendar_match edits; the field is now accepted and persisted (this also fixes the bulk-insert column/placeholder mismatch found during review).
  • The calendar keyword placeholder itself contained a misspelling ("Columbia - Practice") that seeded a bad keyword; replaced with neutral examples.
  • Restores the sortable admin tables from feat(client): enable sorting the chore admin tables #183, which the rewritten schedule UI had dropped: Title/Clams/Schedules on definitions; Chore/Assigned To/Next Occurrence/Clams on schedules. Calendar schedules sort as "today" when an event matches, otherwise last.

Testing

  • Server: 253 passed, 0 failed. Client: 328 passed (including 7 new tests for the extracted nextOccurrenceAt sort key).
  • All-day timezone fix reproduced before/after in America/Los_Angeles: old query found 0 events for the intended day, new query found 1.
  • Fresh-DB boot verified; migration lands at schema 27.

Notes

  • Live end-to-end verification against a real calendar feed has not been done in this environment.

adamecker added 6 commits September 26, 2026 00:26
Overhaul of the chore scheduling UI and model:

- Unified schedule form (ChoreScheduleFields): repeat mode, interval,
  one-time and event-driven schedules in a single editor
- Repeat-after-completion modes (until-completed / once-completed) with
  automatic child schedule generation
- Calendar-event-driven chores: a schedule can trigger when a calendar
  event title matches a keyword (calendar_match), with a matched-today
  indicator in the admin table
- Multi-user assignment: create the same schedule for several users at
  once; all-users option on the widget quick-add
- Custom emoji icons per chore (ChoreIconPicker, choreIcons registry)
- Consolidated schedule fields and clearer next-occurrence labels
  (choreScheduleUtils)
- One-time schedules with a future due date stay hidden until that date
- Schema 27 migration adds the calendar_match column
- Server: schedule CRUD validates and stores the new fields; bulk
  creation writes all columns including calendar_match; deleting a
  chore cascades to its schedules

Note: the schedule admin table is rebuilt here and does not yet include
the sortable headers from upstream jherforth#183; re-applying that sort on the
new table structure is left as a follow-up.
- Register the schema 27 migration (calendar_match column)
- Deleting a chore cascades to its schedules
- GET /api/chore-schedules filters future-dated one-time schedules and
  supports the new duration modes
- POST /api/chore-schedules validates the new fields (interval for
  once-completed, crontab parsing, parent schedule references) and stores
  calendar_match
- POST /api/chore-schedules/bulk creates per-user schedules including
  calendar_match (fixes a column/placeholder mismatch that made every
  bulk create fail)
- PATCH /api/chore-schedules/:id accepts the new fields including
  calendar_match
- getTodaysRegularChoresForUser hides future-dated one-time schedules
All-day events are cached as UTC midnight. The calendar-match query
converted start_time with 'localtime', which shifts all-day events to
the previous day on servers behind UTC, so event-driven chores appeared
a day early and were missing on the actual event day. Compare the UTC
date directly for all-day events; timed events keep the localtime
conversion.
The match pill already tinted green on a match, but the signal was too
subtle to diagnose a non-matching keyword (e.g. a typo like Columbia
vs Colombia). Show an explicit check/cross suffix and a tooltip
explaining what a miss means.
The calendar-match example read 'Columbia - Practice', and the typo
got copied into a real schedule keyword that then failed to match the
'Colombia ...' calendar events. Use neutral, correctly spelled examples.
…edule UI

The scheduling overhaul dropped the sortable column headers that
upstream PR jherforth#183 added to the chore definitions and schedule tables.
Re-port them onto the new tables: Title/Clams/Schedules sort on the
definitions table; Chore/Assigned To/Next Occurrence/Clams on the
schedule table. Pickers keep their own alphabetical order.

Next Occurrence sorts on the parsed instant via a new
nextOccurrenceAt() helper (label and key share one parse, as upstream
did). Calendar-driven schedules sort as 'today' when an event matches,
otherwise last with one-time and unparseable rows.
@adamecker

Copy link
Copy Markdown
Author

Closing for now — I'll re-open after testing the branch locally.

@adamecker adamecker closed this Sep 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant