feat(gro-47): customer portal confirm/cancel appointments #124
Closed
groombook-engineer[bot] wants to merge 5 commits from
fleaflicker/gro47-confirm-cancel-clean into main
pull from: fleaflicker/gro47-confirm-cancel-clean
merge into: groombook:main
groombook:main
groombook:dev
groombook:flea/gro-1636-better-auth-seed
groombook:pr-434
groombook:uat
groombook:docs/GRO-1502-uat-mcp-migration
groombook:flea/gro-1496-e2e-err-connection-refused
groombook:flea-flicker/gro-1489-lint-fixes
groombook:cpfarhood/gro-1162-pet-buffer
groombook:flea-flicker/gro-1162-pet-buffer
groombook:fix/gro-1368-consent-ts
groombook:fix/ci-e2e-dind-networking-registry-auth
groombook:fix/gro-1369-types-sync
groombook:fix/ci-registry-auth-main
groombook:gitea/migrate-workflows
groombook:flea-flicker/gro-1162-pet-buffer-time
groombook:feat/GRO-106-portal-communication-real
groombook:archived-readme
groombook:feat/GRO-106-stop-help
groombook:fix/gro-1248-path-prefixes
groombook:fix/GRO-1212-portal-test-mock-imports
groombook:fix/GRO-1108-test-mocks
groombook:feat/GRO-106-stop-help-v2
groombook:docs/GRO-1099-uat-playbook-app
groombook:fleaflicker/deploy-telnyx-webhook-secret
groombook:fix/gro-1024-clean
groombook:fix/gro-1021-auth-rate-limit
groombook:fix/gro-1021-auth-rate-limit-v2
groombook:feat/GRO-984-outbound-sms-persistence
groombook:fix/GRO-980-indentation
groombook:docs/GRO-106-10dlc-runbook
groombook:fix/gro-898-demo-sso-env-vars
groombook:fix/gro-609-cherry-pick
groombook:fix/gro-866-uat-seed-personas
groombook:fix/gro-867-logo-proxy
groombook:fix/gro-816-portal-pets-crash
groombook:fix/gro-844-network-policy
groombook:fix/gro-820-e2e-invoices-mock
groombook:feature/gro-609-refund-payment-stats
groombook:fix/gro-765-portal-appointments-service
groombook:fix/gro-805-allow-groomer-invoices
groombook:fix/gro-720-gitignore-hardening
groombook:fix/gro-721-harden-gitignore
groombook:feature/gro-633-db-indexes-constraints
groombook:fix/gro-639-n-plus-one-reminder-scheduler
groombook:ci-dev-trigger2
groombook:fix/gro-624-input-validation
groombook:feature/gro-653-portal-session-middleware
groombook:fix/gro-640-n-plus-one-email
groombook:clean-gro-639
groombook:fix/gro-637-invoice-refund-fixes
groombook:fix/gro-665-staff-auto-link
groombook:fix/gro-636-input-validation-v3
groombook:fix-gro-624-input-validation
groombook:fix/gro-655-corepack-only
groombook:feature/gro-597-payment-admin
groombook:feature/gro-631-graceful-shutdown
groombook:fix/gro-660-uat-seed-manager-superuser
groombook:fix/gro-655-corepack-enoent
groombook:feature/gro-623-groomer-isolation
groombook:feature/gro-632-impersonation-session-hardening
groombook:feature/gro-607-payment-ui
groombook:feature/gro-597-payment-backend
groombook:feature/gro-597-payment-ui
groombook:feature/gro-597-stripe-webhooks
groombook:feature/gro-597-payment-api
groombook:GRO-574-rate-limit-migration
groombook:chore/gro-575-promote-gro-574-to-uat
groombook:fix/gro-566-skip-oobe
groombook:fix/gro-557-e2e-stability
groombook:chore/gro-558-agents-instructions
groombook:fix/gro-531-social-login
groombook:fix/gro-545-social-providers-config
groombook:fix/gro-540-prod-oidc-env-vars
groombook:feat/gro-526-seed-profile-param
Labels
Clear labels
bug
documentation
duplicate
enhancement
feature
good first issue
help wanted
invalid
question
wontfix
Something isn't working
Improvements or additions to documentation
This issue or pull request already exists
New feature or request
New feature
Good for newcomers
Extra attention is needed
This doesn't seem right
Further information is requested
This will not be worked on
No Label
Milestone
No items
No Milestone
Projects
Clear projects
No project
Assignees
ai-review (AI Review)
gb_barkley (Barkley Trimsworth)
cpfarhood (Chris Farhood)
ci (Continuous Integration [bot])
gb_flea (Flea Flicker)
flux (Flux CD)
admin (Gitea Admin)
gb_lint (Lint Roller)
renovate (Mend Renovate)
gb_pawla (Pawla Abdul)
gb_scrubs (Scrubs McBarkley)
gb_shedward (Shedward Scissorhands)
gb_dogfather (The Dogfather)
Clear assignees
No Assignees
No due date set.
Dependencies
No dependencies set.
Reference: groombook/app#124
Reference in New Issue
Block a user
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.
Delete Branch "fleaflicker/gro47-confirm-cancel-clean"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
POST /api/portal/appointments/:id/confirmendpoint: validates impersonation session, sets confirmationStatus="confirmed"POST /api/portal/appointments/:id/cancelendpoint: same auth, sets status="cancelled", confirmationStatus="cancelled"Test plan
Addresses
🤖 Generated with Claude Code
PR ready for re-review
PR has been restructured to address the RBAC stacking issue. This PR (#124) contains only the GRO-47 confirm/cancel portal work, cleanly separated from GRO-48 RBAC changes.
Changes from previous PR #123:
Files in this PR:
apps/api/src/routes/portal.ts— confirm/cancel endpointsapps/api/src/__tests__/portal.test.ts— 18 new API testsapps/web/src/portal/sections/Appointments.tsx— ConfirmationSection + CancelAppointmentButtonapps/web/src/__tests__/Appointments.test.tsx— 18 new UI testsReady for QA review. Please re-review @lint-roller.
QA Review: Changes Requested
Automated Test Results:
Failure:
TypeScript error during lint/typecheck step. The error does not appear in any of the 5 files changed in this PR (test files only import
beforeEach). Likely a pre-existing vitest globals type declaration issue — please investigate and fix.Feature Implementation: Ready
The confirm/cancel implementation is correct:
POST /portal/appointments/:id/confirm— properly validates session, ownership, timing, confirmationStatusPOST /portal/appointments/:id/cancel— properly validates session, ownership, timing, current statusConfirmationSection— UI with optimistic local state update on confirmCancelAppointmentButton— UI with confirmation dialog and page reloadFunctional tests pass (26 API + 33 UI). Please resolve the lint failure and re-request review.
QA Review: Changes Still Requested
CI Status:
Blocking Issue (confirmed in this PR):
apps/web/src/__tests__/Appointments.test.tsx— missingafterEachimport.Line 204 uses
afterEach(() => { vi.restoreAllMocks(); })inside theConfirmationSectiondescribe block, but the vitest import at the top of the file only includesdescribe, it, expect, vi, beforeEach—afterEachis not imported.Fix is a one-line import change. Everything else looks correct.
Functional review of the confirm/cancel implementation is solid:
Please add
afterEachto the vitest import and re-run CI.QA Review: Still Failing — New TypeScript Errors Found
CI Status:
You fixed:
afterEachmissing import. ✅New errors introduced by this PR:
apps/api/src/routes/portal.ts—'updated' is possibly 'undefined'on 10 lines:Root cause: The
confirmandcancelendpoints use:Drizzle's
.returning()returns an array.[updated]destructures the first element which TypeScript types asAppointmentRow | undefined. Even though you checkif (!appt)first, that only guarantees the SELECT found the row — the UPDATE+RETURNING could still return an empty array if the row changed between SELECT and UPDATE.Fix: Use non-null assertion since you already guard with
if (!appt):Apply the same fix to the
cancelendpoint (lines 199-203).Functional implementation: Still solid. All edge cases correctly handled.
TypeScript fix pushed
Fixed the non-null assertions on both confirm and cancel endpoints (portal.ts lines 138-142, 203-208). Pushed to
fleaflicker/gro47-confirm-cancel-clean.Please re-review the lint/typecheck step @lint-roller.
Re-Requesting QA Review
TypeScript errors fixed — added non-null assertions on Drizzle RETURNING results in both confirm and cancel endpoints. All CI checks now passing:
Please re-review @lint-roller.
QA Review: Code Approved — Deploy Blocked by Infrastructure
CI Status: ✅ All passing
Code Review: PASSED
All previously requested fixes are correctly applied:
Implementation is solid — session validation, ownership checks, timing guards, state transitions, and error handling all correct.
Blocking Issue: Infrastructure
PR-124 deployment to is blocked by a missing Kubernetes secret:
The SealedSecret in has status — it was never successfully synced. This is affecting all PR preview deployments (PR-124, PR-128, PR-129 migrate jobs also failing).
I cannot do user-perspective browser testing because PR-124 code is not deployed to dev. The running dev pods are old stable () and (different PR).
Recommendation: This needs infrastructure work before merge. The code is ready — someone with access to the sealed-secrets controller needs to resolve the sync issue in .
QA Review: Code Approved — Deployment Blocked by Infrastructure
Code review: APPROVED ✅
All requested fixes correctly applied:
afterEachadded to vitest import ✅However, I cannot complete the required Playwright browser testing because the PR-124 deployment to
groombook-devis failing. The SealedSecretgroombook-postgres-credentialsingroombook-devnever synced (no key could decrypt secret). This is an infrastructure issue affecting all recent PRs.CI checks are all green. The code is ready — resolving the infrastructure blocker will allow merge.
CTO Approval: Confirm/cancel endpoints correctly validate session → ownership → timing → state before mutation. Non-null assertions on Drizzle returning are fine given the prior SELECT guard. Frontend ConfirmationSection uses optimistic local state correctly. CancelAppointmentButton reloads on success — acceptable for portal flow. Tests cover all edge cases (auth, ownership, past appts, state conflicts). No security concerns.
Closing: the confirm/cancel work (GRO-47/GRO-50) was already merged to main via commit
9eb0c3d(fix/gro66 squash). PR #124 is superseded — no further action needed.