fix(events): repair day-offs miscategorized as freeform#274
Merged
Conversation
The 2026-07-20 EventKind → template migration series routed `off`-kind events to the day-off template only when that template already existed per congregation. In unseeded congregations the join was a no-op, the events kept templateId=NULL, and 20260720200000_seed_system_templates then defaulted every orphan to freeform — erasing the classification. Adds a data-repair migration that moves freeform events with no parts, no service parts, and a span >= 20 hours back onto the day-off template. No shape createFreeformEvent produces matches (start and end share a single calendar date), and every shape createDayOff produces does (endDate >= startDate + 1 day). Idempotent. Symptom: legacy day-offs surfaced on /programs and were absent from /programs/days-off.
Addresses PR #274 review: - Comment now credits both migrations' freeform fallbacks (drop_event_kind step 3 and seed_system_templates step 3) and refers to handleFreeformMode as the caller that combines date + times. - Reframes "idempotent" as a one-shot repair — the migration table blocks re-execution, but a manual re-run against a DB with new long-freeform events would misclassify them. - Adds a browser-only caveat on the >= 1 day floor (createDayOff only rejects startDate > endDate; the min attribute is HTML5). - Notes the in-flight-import assumption. Adds a second DO block that flips EventPart.hasConflict and EventServicePart.hasConflict from false to true wherever an assignment's event overlaps a day-off owned by the same member. While the day-offs lived on the freeform template, refreshConflictFlags filtered them out via template.key = 'day-off', so those overlaps never flipped the flag. Re-linking (step 1) does not retrigger the refresh, so without this backfill the phantom greens would persist until the member's next absence create/delete. Runs against every day-off (not only the ones re-linked) so any pre-existing drift also gets repaired. Only false -> true, never clears a legitimately-set true. Verified against local fixtures: 5-day orphan freeform re-links to day-off, 2-hour freeform stays put, overlapping assignment for the day-off creator flips to hasConflict=true, idempotent on second run.
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
app/database/migrations/20260721000000_repair_orphaned_day_offs— data-repair migration that re-links freeform events back onto theday-offtemplate when they carry every day-off signal.20260720100000_drop_event_kindstep 2 required theday-offtemplate to already exist per congregation to routeoff-kind events. In unseeded congregations the JOIN was a no-op, events kepttemplateId=NULL, and20260720200000_seed_system_templatesstep 3 defaulted every orphan tofreeform— permanently erasing the classification (kindId was dropped one step earlier).EventPart, noEventServicePart, andendDate - startDate >= interval '20 hours'.createFreeformEventcombines a singledatewithstartTime/endTimeso real freeform events fit inside a calendar day;createDayOfffrom/me/days-offenforcesendDate >= startDate + 1 dayvia the form'smin. Idempotent — a second run finds nothing to update.Symptom fixed: legacy day-offs no longer surface on
/programs, and/programs/days-offshows them again.Test plan
day-off; a 2-hour freeform stayed putNOTICEDay-off recovery: re-linked N freeform event(s) …/programs/days-offand are absent from/programs