* feat: multi-pet client group booking (closesgroombook/groombook#10) (GRO-27)
- Add appointment_groups table: links multiple appointments from one client visit
- Add group_id FK on appointments (nullable, backward-compatible)
- Add GET/POST/PATCH/DELETE /api/appointment-groups endpoints
- POST creates group record + one appointment per pet atomically (with conflict checks)
- DELETE soft-cancels all appointments in the group
- Add GroupBooking.tsx page at /group-bookings with:
- Dynamic pet-slot form (min 2 pets, each with their own groomer/service/end time)
- Auto-calculates end time from service duration
- Group card list showing all pets, groomers, and statuses side-by-side
- Client filter and cancel-all action
- Wire into nav and routing in App.tsx
- Export AppointmentGroup type; add groupId field to Appointment type
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix: remove eslint-disable for uninstalled react-hooks plugin; remove unused clientMap (GRO-27)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
---------
Co-authored-by: Groom Book CTO <cto@groombook.app>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Implements Phase 1 of groombook/groombook#4 — automated email reminders
for upcoming appointments, with booking confirmations sent immediately
on creation.
- **DB**: new `reminder_logs` table tracks sent reminders per appointment
(unique on appointmentId+type prevents duplicates); `clients` gains
`email_opt_out` boolean (migration 0004_reminder_logs)
- **Email service**: `apps/api/src/services/email.ts` — nodemailer SMTP
transport (disabled when SMTP_HOST is unset, so self-hosted installs
without email config are unaffected); confirmation and reminder email
templates included
- **Reminder scheduler**: `apps/api/src/services/reminders.ts` — node-cron
job runs every minute, checks for appointments in the upcoming reminder
windows (default: 24 h and 2 h), sends emails for opted-in clients,
and records sends in reminder_logs (idempotent via ON CONFLICT DO NOTHING)
- **Confirmation email**: sent fire-and-forget after successful appointment
creation (both single and recurring); never blocks the API response
- **Config**: SMTP_HOST, SMTP_PORT, SMTP_SECURE, SMTP_USER, SMTP_PASS,
SMTP_FROM, REMINDER_HOURS_EARLY, REMINDER_HOURS_LATE env vars documented
in .env.example; all optional — feature is silently disabled without them
- **Types**: Client.emailOptOut field added to shared types package
Co-authored-by: Groom Book CTO <cto@groombook.app>
Co-authored-by: Paperclip <noreply@paperclip.ing>
* feat: recurring appointments with cascading change propagation
Implements GitHub issue #9 — recurring appointment scheduling with
configurable frequency and cascade edit/cancel options.
Changes:
- DB: add `recurring_series` table (frequency_weeks) and series_id /
series_index columns on appointments (migration 0003)
- API POST /appointments: accepts optional `recurrence` object
(frequencyWeeks + count) that creates a full series in one transaction
- API PATCH /appointments/🆔 new `cascadeMode` field
(this_only | this_and_future | all) applies time-delta shifts and
field updates across the series
- API DELETE /appointments/🆔 new `?cascade=` query param cancels
this_only / this_and_future / all series members
- Frontend: booking form gains a "Recurring appointment" checkbox with
frequency and count pickers; calendar chips show a ↻ recurring label;
detail modal shows "Recurring series" badge and a cascade-delete radio
picker for series appointments
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix: resolve TypeScript errors in recurring appointments route
Guard against possibly-undefined results from Drizzle .returning()
destructuring — use indexed access + explicit null checks instead of
array destructuring for the recurring_series insert, and add an early
throw when the series or first appointment row is missing.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
---------
Co-authored-by: Groom Book CTO <cto@groombook.app>
Co-authored-by: Paperclip <noreply@paperclip.ing>
- Add invoice_status and payment_method enums to schema
- Add invoices table: appointmentId, clientId, subtotal/tax/tip/total cents,
status (draft/pending/paid/void), paymentMethod, paidAt, notes
- Add invoice_line_items table: invoiceId, description, qty, unitPrice, total
- Migration 0002_invoices.sql with FK constraints and journal entry
- POST /api/invoices — create invoice with line items
- POST /api/invoices/from-appointment/:id — one-click invoice from appointment,
pre-populated with service name and price; returns 409 if already invoiced
- GET /api/invoices — list with optional ?status/clientId/appointmentId filters
- GET /api/invoices/:id — invoice with line items
- PATCH /api/invoices/:id — update status, payment method, tip, notes; auto-sets
paidAt when marking paid; blocks edits on voided invoices
- Add Invoice/InvoiceLineItem types to @groombook/types
- InvoicesPage: list view with status filter, create from appointment modal,
detail modal with tip input, payment method selector, Mark as Paid/Void actions
- Add Invoices nav link in App.tsx
Co-authored-by: Groom Book CTO <cto@groombook.app>
Co-authored-by: Paperclip <noreply@paperclip.ing>
- Add health_alerts column to pets table (migration 0001)
- Update DB schema, shared types, and API Zod validation
- Show health alerts prominently (red badge) in pet cards
- Add health alerts field to pet form with allergy/condition placeholder
- Add delete button per pet with confirmation dialog
- Add delete client button with cascade warning
Closesgroombook/groombook#2
Co-authored-by: Groom Book CTO <cto@groombook.app>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Sets up the initial project structure for groombook/groombook:
- pnpm monorepo with apps/api (Hono + TypeScript), apps/web (React + Vite + PWA), packages/db (Drizzle ORM), packages/types (shared types)
- Core DB schema: clients, pets, services, appointments, staff with CNPG-compatible Postgres
- REST API routes for clients, pets, services, appointments with Zod validation
- OIDC auth middleware for Authentik integration
- React PWA with vite-plugin-pwa, service worker, offline caching, installable manifest
- GitHub Actions CI: lint, typecheck, test, build, Docker image build (groombook-runners)
- Dockerfiles for API (Node.js) and Web (nginx)
- docker-compose.yml for local development
Co-Authored-By: Paperclip <noreply@paperclip.ing>