This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
groombook-engineer[bot] d0b4baf5aa feat: customer-facing appointment notes (GRO-106) (#109)
* feat: add customer-facing appointment notes (GRO-106)

- Migration 0014: add customer_notes column to appointments
- Schema update: add customerNotes field to appointments table
- Factory update: include customerNotes in buildAppointment
- Portal route: PATCH /api/portal/appointments/:id/notes
  - Ownership validation via impersonation session
  - Future-only validation (no edits after start)
  - 500 character limit
- Register portal router in index.ts

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* Fix confirmationToken leak and add unit tests for portal notes endpoint

- Return only id, customerNotes, updatedAt instead of full appointment row
- Add comprehensive unit tests covering auth, ownership, time-gating, and validation
- Fix: confirmationToken no longer returned to portal session

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* feat: add customer notes UI to portal and staff views (GRO-178)

- Add customerNotes field to Appointment type
- Add read-only customer notes display in staff appointment detail modal
- Add customer notes textarea with save, char counter (500 max), and disabled state
- Wire up PATCH /api/portal/appointments/:id/notes in portal UI
- Update mockData with customerNotes field

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix: address QA review feedback - null check and portal route auth

- Add null check after db.update().returning() in portal notes endpoint
- Move portal router registration before auth middleware so clients can access it
- Remove unused ENDED_SESSION variable from test file

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(portal): address QA review - isUpcoming time parsing and session header

- Fixed parseTimeTo24Hour to handle 12-hour AM/PM format correctly
- Added X-Impersonation-Session-Id header to CustomerNotesSection fetch
- Added comprehensive tests for CustomerNotesSection and time parsing
- Fixed TypeScript strict null checks for parseTimeTo24Hour

Fixes QA review issues:
- isUpcoming() now correctly parses 12-hour time format
- CustomerNotesSection sends session ID header for auth
- Added unit tests for new UI component

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix: thread sessionId as prop instead of sessionStorage

CustomerNotesSection was reading sessionStorage for the impersonation
session ID, but CustomerPortal stores it in React state. Pass sessionId
as a prop through AppointmentsSection and AppointmentCard instead.

Also update tests to pass sessionId prop and add test for null sessionId
case.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

---------

Co-authored-by: Scrubs McBarkley <scrubs@groombook.app>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: groombook-cto[bot] <269737991+groombook-cto[bot]@users.noreply.github.com>
2026-03-26 08:24:21 +00:00

Groom Book

Open source, self-hostable pet grooming business management and customer relationship platform.

Features

  • Appointment scheduling — calendar management for single or multiple groomers
  • Client & pet records — detailed profiles with grooming history and preferences
  • Service management — pricing, duration, and service catalog
  • Online booking portal — customer-facing self-service booking
  • POS & invoicing — payments, tips, and receipt generation
  • Automated reminders — SMS and email notifications
  • Reporting dashboard — revenue, utilization, and trend analytics
  • Staff impersonation — managers can view the customer portal as any client, with full audit logging and session controls
  • PWA — installable on mobile devices, works offline

Tech Stack

Layer Technology
Backend Hono (TypeScript, Node.js)
Frontend React 19 + Vite + vite-plugin-pwa
Database PostgreSQL via CNPG + Drizzle ORM
Auth OIDC via Authentik
Infra Kubernetes (namespace: groombook), Flux GitOps
CI GitHub Actions (self-hosted groombook-runners)

Repository Structure

groombook/
├── apps/
│   ├── api/          # Hono REST API
│   └── web/          # React PWA
├── packages/
│   ├── db/           # Drizzle schema + migrations
│   └── types/        # Shared TypeScript types
├── .github/
│   └── workflows/    # CI/CD pipelines
└── docker-compose.yml

Getting Started

Prerequisites

  • Node.js >= 20
  • pnpm >= 9 (npm install -g pnpm)
  • Docker & Docker Compose (for local Postgres)

Local Development

# Clone the repo
git clone https://github.com/groombook/groombook.git
cd groombook

# Install dependencies
pnpm install

# Start local Postgres
docker compose up postgres -d

# Run database migrations
DATABASE_URL=postgres://groombook:groombook@localhost:5432/groombook pnpm db:migrate

# Start API and Web in parallel
pnpm dev

API will be available at http://localhost:3000 Web will be available at http://localhost:5173

Environment Variables

API (apps/api/.env)

DATABASE_URL=postgres://groombook:groombook@localhost:5432/groombook
OIDC_ISSUER=https://authentik.example.com
OIDC_AUDIENCE=groombook
CORS_ORIGIN=http://localhost:5173
PORT=3000

Running Tests

# Unit tests (vitest)
pnpm test

# E2E tests (Playwright) — requires the full Docker Compose stack to be running
docker compose up -d --wait
pnpm --filter @groombook/e2e test

# Open the Playwright UI (interactive test runner)
pnpm --filter @groombook/e2e test:ui

# View the last E2E test report
pnpm --filter @groombook/e2e test:report

E2E tests target the Docker Compose stack (http://localhost:8080). They use API route mocking where needed so happy-path tests are deterministic without requiring seed data.

Building

pnpm build

Self-Hosting

The fastest way to run Groom Book is with Docker Compose. This starts PostgreSQL, runs database migrations, and serves both the API and web frontend.

git clone https://github.com/groombook/groombook.git
cd groombook

# Start everything (Postgres + migrate + API + web)
docker compose up --build

The default docker-compose.yml sets AUTH_DISABLED=true so you can explore the app without configuring an OIDC provider. Disable this in any internet-facing deployment.

Production configuration

Copy .env.example to .env and configure:

cp .env.example .env

Key variables to update for production:

Variable Description
DATABASE_URL PostgreSQL connection string
AUTH_DISABLED Set to false in production
OIDC_ISSUER Authentik issuer URL
OIDC_AUDIENCE OAuth2 audience (default: groombook)
CORS_ORIGIN Public URL of the web frontend

To use your .env file with Docker Compose:

docker compose --env-file .env up --build

Kubernetes (production-grade deployments)

See the groombook/infra repository for Kubernetes manifests and Flux configuration.

Groom Book is deployed in the groombook Kubernetes namespace using:

  • CNPG for PostgreSQL
  • Authentik for OIDC authentication
  • Flux for GitOps-managed deployments

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/my-feature)
  3. Commit your changes
  4. Open a pull request

All PRs require CI to pass before merge.

License

MIT

S
Description
Open source, self-hostable pet grooming business management & CRM
Readme AGPL-3.0 49 MiB
Languages
TypeScript 98.5%
Python 0.7%
Go Template 0.3%
Dockerfile 0.2%
CSS 0.2%