## Summary
- **FR-T01/T02/T05**: Volunteers can create, edit, and delete their own future time off; admins can manage time off for any volunteer
- **FR-T03**: Conflict detection on create — warns about assigned shifts, auto-removes volunteer on confirm, notifies admins
- **FR-T04**: Admin delete restores volunteer to previously removed shifts with impact preview
- **FR-T06**: Hard block prevents assigning a volunteer to a shift if they have approved time off on that date (409 response)
## Changes
- `internal/db/schema.go` — new `time_off_removed_shifts` tracking table
- `internal/timeoff/` — full store + handler rewrite: `Storer` interface, Update/Delete/ConflictingShifts/RemoveFromShifts/RestoreShifts/HasApprovedTimeOff
- `internal/schedule/handler.go` — `TimeOffChecker` interface, FR-T06 conflict check in UpdateInstance
- `internal/volunteer/volunteer.go` — `ListAdminIDs` for admin notifications
- `internal/server/server.go` — new routes: `PUT /timeoff/{id}`, `DELETE /timeoff/{id}`, `GET /timeoff/{id}/shifts`
- `web/src/api.ts` — `ApiError` class, new API methods, `ConflictingShift`/`TimeOffConflictResponse` types
- `web/src/pages/TimeOff.tsx` — edit/delete buttons, conflict warning dialog, admin volunteer picker, shift restoration preview
## Test plan
- [x] 16 Go handler tests (internal/timeoff/handler_test.go)
- [x] 11 React tests (web/src/pages/TimeOff.test.tsx)
- [x] All existing tests still pass (54 React, all Go packages)
- [x] Manual: create time off overlapping an assigned shift, verify conflict warning, confirm, verify removal + admin notification
- [x] Manual: admin deletes time off, verify shift restoration preview, confirm, verify volunteer re-added
Closes #3
Add full time-off lifecycle: create/edit/delete with shift conflict
detection, auto-removal from conflicting shifts with admin notification,
shift restoration on admin delete, and hard block on assigning volunteers
with approved time off to shifts.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Scan datetime columns directly into time.Time instead of strings in the
timeoff store — the intermediate string parse silently failed with
parseTime=true, producing zero-value dates. Display dates with month
names and filter admin's own entry from the volunteer dropdown.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Changes
internal/db/schema.go— newtime_off_removed_shiftstracking tableinternal/timeoff/— full store + handler rewrite:Storerinterface, Update/Delete/ConflictingShifts/RemoveFromShifts/RestoreShifts/HasApprovedTimeOffinternal/schedule/handler.go—TimeOffCheckerinterface, FR-T06 conflict check in UpdateInstanceinternal/volunteer/volunteer.go—ListAdminIDsfor admin notificationsinternal/server/server.go— new routes:PUT /timeoff/{id},DELETE /timeoff/{id},GET /timeoff/{id}/shiftsweb/src/api.ts—ApiErrorclass, new API methods,ConflictingShift/TimeOffConflictResponsetypesweb/src/pages/TimeOff.tsx— edit/delete buttons, conflict warning dialog, admin volunteer picker, shift restoration previewTest plan
Closes #3
6427595c62to704f11cec3