Add sitter booking dashboard: confirm/decline requests + calendar reconciliation#21
Merged
Merged
Conversation
…ar reconciliation) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…aces Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Implements Task 1 schema change (Declined column on BookingRequests) and three repo-layer functions (listBookingsForTenant, updateBookingStatus, getBookingWithCustomer) that the sitter dashboard will build on. Includes full test coverage for booking lifecycle state transitions and tenant isolation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
BookingRow.Declined (added in ae009b3) is required, which flows through to CapacityRow (BookingRow & { CapacityKind }). Two pre-existing CapacityRow literals in availability.test.ts's rowsToCapacityEvents tests were missing it; npm test (vitest) doesn't full-typecheck so this wasn't caught until npm run typecheck was run explicitly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
GET /:slug/admin/bookings lists a tenant's bookings joined with customer contact info; POST /:slug/admin/bookings/:id/status transitions a booking's status and best-effort emails the customer, reporting whether they were notified.
The try/catch only wrapped the reconcileBookingsWithCalendar call; the KV get/put around it could still throw uncaught out of the GET /admin/bookings route on a KV outage. Wrap the whole function body so a KV failure is as best-effort as a Calendar failure.
The previous fix moved the KV put inside the try block, so a reconcileBookingsWithCalendar failure (the exact case the TTL cache exists to throttle) skipped the marker write entirely, letting every subsequent dashboard load during an outage re-attempt the full Calendar round-trip. Use try/catch/finally so the marker always gets written (guarded with its own .catch), and guard the initial KV get the same way so a KV read failure is treated as a cache miss instead of throwing.
Implements Task 5: adds the AdminBooking type (for displaying booking info in the admin dashboard) and the bookings namespace with list() and setStatus() methods to the adminApi object. These will be consumed by the admin booking dashboard component in Task 6. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Add .pb-chip-bad to admin.css (existed on main, wasn't ported over when the component was copied); cancelled bookings now render with the bad/red styling instead of falling back to the default chip. - Replace BookingsSection's local fixed .pb-savebar with an in-flow .pb-hint message. App.tsx already renders a global fixed savebar across all sections; a second one at the same position would overlap it when both are active.
listSyncedBookingIds returned ALL synced, non-cancelled bookings for the tenant with no date bound, while reconcileBookingsWithCalendar only queried Google Calendar for a fixed [today-1, today+180) window. Any booking outside that window (already completed, or booked far enough out) could never appear in the Calendar response and was wrongly flagged "missing" and auto-cancelled. Give listSyncedBookingIds the same window bounds reconcileBookingsWithCalendar computes for its own Calendar query, using the same overlap-check idiom as listCapacityRows, so only bookings we could actually have seen in the Calendar response are considered candidates for cancellation. Also locks down two spec non-goals with regression tests: cancelling a booking never calls out to Google Calendar (admin-bookings.test.ts), and reconciliation only checks bookingId presence, never diffs event start/end times (calendar-reconcile.test.ts). Test fixture dates switched from hardcoded 2030 values to dates computed relative to the real clock, since the new window bound is itself relative to real "today" and a hardcoded future date would eventually age out of it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
listCalendarEvents fetched up to 2500 events with no pagination. When a tenant's query window has more instances than that (plausible once recurring series are expanded via singleEvents=true), events past page 1 went missing from the response. reconcileBookingsWithCalendar infers "event deleted" from a booking id's absence from that list, so truncation could wrongly auto-cancel real, confirmed bookings. listCalendarEvents now throws if the Google API response includes a nextPageToken, instead of silently returning a partial list. Both existing callers (reconcileIfStale's try/catch, availability.ts's try/catch) already degrade safely on a thrown error, so this fails loud at the source rather than adding pagination. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
BOOKING_COLS_QUALIFIED existed only to disambiguate the Id column in two JOIN queries. BookingRequests.* does the same thing without maintaining a derived, table-prefixed copy of BOOKING_COLS. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
4 tasks
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
main(which diverged fromcustom-servicesbefore that work landed), adapted for this branch's per-tenantTenantServicesmodel.Notable review findings (all resolved)
.pb-chip-badCSS class, a locally-rendered savebar that could visually collide with the app-level one) were caught and fixed during task-level review.Test plan
npm run typechecknpm run lintnpm test(256/256 passing)npm run build🤖 Generated with Claude Code