Commit Graph

12 Commits

Author SHA1 Message Date
groombook-cto[bot] 8ab6319311 feat: quick-find search for clients and pets (GRO-46)
Closes #119

- Debounced typeahead search on clients/pets pages
- Auto-select client from GlobalSearch highlight param
- Mobile-friendly with big touch targets
- 141-line test suite, all 70 web tests pass

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-27 07:16:52 +00:00
groombook-ceo[bot] 020e758916 Merge branch 'main' into feat/pet-photo-upload-gh93 2026-03-22 19:45:35 +00:00
Scrubs McBarkley 1380848aea feat: pet photo upload via presigned S3 URLs (GH #93, GRO-123)
- DB migration 0012: add photo_key and photo_uploaded_at columns to pets table
- S3 client utility (apps/api/src/lib/s3.ts): presigned PUT/GET, delete via Rook-Ceph RGW
- API photo routes on petsRouter:
  - POST /:petId/photo/upload-url — returns presigned PUT URL + object key
  - POST /:petId/photo/confirm    — records key in DB after successful upload
  - DELETE /:petId/photo          — deletes from storage and clears DB
  - GET /:petId/photo             — returns presigned GET URL
- RBAC: all staff roles (manager, receptionist, groomer) may upload/delete photos;
  restructured index.ts guards so groomer-accessible photo paths don't overlap
  with the manager/receptionist-only general pets write guard
- Frontend PetPhotoDisplay: responsive image with shimmer skeleton and paw placeholder
- Frontend PetPhotoUpload: client-side resize to max 1200px, XHR with progress,
  presigned PUT flow — binary data never passes through the API server
- Wired both components into Clients.tsx staff portal pet cards
- Unit tests: 14 test cases covering all four routes (happy path + error cases)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-22 00:07:48 +00:00
Scrubs McBarkley afde6b7857 feat: unify site theming via CSS custom properties (GH #91)
Replace all hardcoded brand color hex values with CSS custom properties
so BrandingContext drives both the customer portal and staff site.

- index.css: add derived accent/primary vars using color-mix()
  (--color-accent-hover, --color-accent-dark, --color-accent-light,
  --color-accent-lighter, --color-primary-dark); fix focus ring styles
  to use var(--color-primary) instead of hardcoded hex
- BrandingContext.tsx: also update <meta name="theme-color"> in sync
  with primaryColor so PWA theme-color tracks branding at runtime
- portal/sections: replace bg-[#8b7355], text-[#6b5a42], bg-[#f0ebe4],
  bg-[#faf5ef], hover:bg-[#7a6549] etc. with Tailwind v4 CSS var
  utilities (bg-(--color-accent), text-(--color-accent-dark), etc.)
- pages: replace inline style "#4f8a6f"/"#3d7a5f" with
  var(--color-primary) / var(--color-primary-dark) across Appointments,
  Book, Clients, GroupBooking, Invoices, Reports, Services, Staff, and
  DevSessionIndicator

Closes #91

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-21 23:50:43 +00:00
Scrubs McBarkley 8de6528bd3 feat: wire customer portal impersonation to real backend API
Replaces the local impersonationReducer (mock-based) with real API calls
to the /api/impersonation/sessions endpoints added in PR #75.

Changes:
- CustomerPortal: reads ?sessionId= param via useSearchParams, fetches
  real session on mount, calls /extend and /end on user action, logs
  page views to /sessions/:id/log. Removes demo sidebar button.
- ImpersonationBanner: updated to use ImpersonationSession from
  @groombook/types instead of the old mockData shape. Accepts isExtended
  prop to control Extend button visibility.
- AuditLogViewer: now fetches from /api/impersonation/sessions/:id/audit-log
  instead of receiving auditLog[] as a prop. Handles loading/error states.
- Clients.tsx: "View as Customer" button now POSTs to
  /api/impersonation/sessions first, then navigates to /?sessionId=<id>.
  Handles 409 (existing active session) by reusing it.
- mockData.ts: removed ImpersonationSession and AuditEntry interfaces
  (now live in @groombook/types).
- test/setup.ts: set NODE_ENV=test for React 19 + testing-library compat.
- portal.test.tsx: 13 new tests covering banner, audit log viewer, and
  portal session loading behavior (20 total pass).

Closes #76

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-20 17:26:45 +00:00
groombook-paperclip[bot] 19e0f5e3ca feat: client disable/deletion with soft-delete (#69)
* feat: add client disable/deletion with soft-delete (#67)

Add soft-delete support for clients: disable is the default action
(hiding from client list and booking flow), with permanent deletion
requiring explicit type-to-confirm. Disabled clients remain in
reporting and can be re-enabled by staff.

- Add client_status enum (active/disabled) and disabled_at column
- API defaults GET /api/clients to active-only, ?includeDisabled=true shows all
- PATCH /api/clients/:id accepts status field for disable/enable
- DELETE requires ?confirm=true query param
- Booking flow skips disabled clients
- Frontend: show disabled toggle, disable/enable buttons, delete confirmation modal

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove unused updateClientSchema (lint error)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Groom Book CTO <cto@groombook.app>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 20:03:18 +00:00
groombook-paperclip[bot] 12ad7c66a0 feat: add View as Customer impersonation button on Clients page (#64)
Staff can now click "View as Customer" on any client profile in the admin
panel. This navigates to the customer portal with impersonation auto-activated,
showing the portal exactly as that customer would see it (read-only, with
full audit trail).

The portal reads impersonate/clientName/reason/staffName from URL search
params on mount, auto-starts the impersonation session, then cleans up the
URL.

Co-authored-by: Groom Book CTO <cto@groombook.dev>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-03-19 12:47:26 +00:00
groombook-paperclip[bot] 1cf1f19e1d Improve admin UI visual design — polish look and feel (#59)
* Improve admin UI visual design — polish look and feel

- Sticky nav bar with subtle shadow, branded GroomBook wordmark, green gradient Book button
- Consistent brand green (#4f8a6f) for primary buttons across all admin pages
- Tables wrapped in white cards with rounded corners and soft shadows
- Uppercase table headers with better spacing and hierarchy
- Input/button border-radius increased to 6px for softer feel
- Global CSS: button transitions, input focus states with brand green ring, subtle card shadows
- Background changed from plain white to light gray (#f0f2f5) for depth
- Reports: polished stat cards with shadows, refined section headers, card-wrapped tables
- Custom scrollbar styling for a cleaner look

Closes groombook/groombook#58

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

* Fix test selectors for branded nav text

- Use regex /Groom\s*Book/ to match split-element brand text
- Use getByRole("link") for Book CTA to avoid matching brand <strong>

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

* Fix brand text test to handle split-element rendering

The nav brand was changed to <span>Groom</span>Book for color styling,
but getByText with a regex can't match text split across child elements.
Use a custom text matcher that checks the STRONG element's textContent.

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

* Fix E2E tests for split-element brand name

The brand is now <span>Groom</span>Book (no space), so Playwright's
getByText needs "GroomBook" instead of "Groom Book".

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

---------

Co-authored-by: Groom Book CTO <cto@groombook.dev>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Groom Book CTO <cto@groombook.app>
2026-03-19 03:33:34 +00:00
groombook-paperclip[bot] 14ed19497f feat: detailed pet profile attributes and grooming visit history (closes #13)
- Add cut_style, shampoo_preference, special_care_notes, custom_fields columns to pets table
- Add grooming_visit_logs table to track per-visit grooming details (cut, products, notes)
- Extend pets API to accept and return new profile fields
- Add /api/grooming-logs endpoint (GET by petId, POST, DELETE)
- Update Pet type with new fields; add GroomingVisitLog type
- Update Clients page: grooming preferences section in pet card, "Log visit" button,
  visit history panel showing last 3 visits, expanded pet form with grooming preferences

Co-authored-by: Groom Book CTO <cto@groombook.app>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-03-17 21:46:40 +00:00
groombook-paperclip[bot] eb9255eee0 feat: add health alerts field and delete actions for pets and clients (#25)
- 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

Closes groombook/groombook#2

Co-authored-by: Groom Book CTO <cto@groombook.app>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-03-17 19:40:29 +00:00
groombook-paperclip[bot] 4f92b8bffb feat: appointment scheduling, client/pet/service/staff CRUD UI
* feat: appointment scheduling, client/pet/service/staff CRUD UI

- Weekly calendar view with navigation, color-coded by status
- Booking form with client→pet→service→staff→date/time flow
- Double-booking conflict detection on POST/PATCH appointments
- DELETE /api/appointments endpoint
- Staff API route (/api/staff) with full CRUD
- Clients page: searchable list, create/edit clients, add/edit pets
- Services page: table with create/edit/toggle-active
- Staff page: table with create/edit/toggle-active
- Nav bar with active-link highlighting, Staff link added

Resolves GitHub groombook/groombook#1, #2, #8

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

* fix: remove unused import, fix useCallback deps

- Remove unused `or` import from drizzle-orm in appointments route
- Compute week end directly in loadAppointments callback to avoid
  exhaustive-deps lint warning (weekEnd derived from weekStart)

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

* chore: add pnpm lockfile

Required for CI --frozen-lockfile installs.

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

* fix: resolve all typecheck, lint, and test failures

- Add @types/node to packages/db devDependencies (typecheck was missing process)
- Re-export drizzle-orm helpers (eq, gte, etc.) from @groombook/db to avoid
  duplicate-instance type conflicts; remove drizzle-orm direct dep from API
- Add @hono/zod-validator and jose as direct API dependencies
- Merge duplicate @groombook/db imports in all route files
- Fix noUncheckedIndexedAccess errors: appointments PATCH, web calendar grid
- Fix weightKg/dateOfBirth type conversion in pets route (numeric→string, string→Date)
- Add eslint.config.js for API and web (ESLint 9 flat config format)
- Add vitest.config.ts with passWithNoTests for API and web

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

---------

Co-authored-by: Groom Book CTO <cto@groombook.app>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-03-17 18:45:28 +00:00
Groom Book CTO a36436d128 Bootstrap monorepo: Hono API, React PWA, Drizzle DB, CI/CD
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>
2026-03-17 16:11:04 +00:00