New files:
manager/src/lib/env-file.ts — a shared, surgical .env reader/updater. It only ever touches the specific keys you tell it to (never regenerates the file), so anything hand-edited into a business's .env since provisioning is preserved exactly.
manager/src/app/dashboard/[slug]/edit/ — the edit page, its form component, and its server action.
Modified:
provisioning.ts gained updateBusiness() alongside the existing provisionBusiness(), reusing the same Caddy-site-writing and container-recreation logic (refactored into a shared writeCaddySiteAndReload helper).
stacks.ts/compose-template.ts — extended to track a BUSINESS_NAME field (previously collected on the Add Business form but silently discarded — that gap is now fixed too, so newly created businesses get their name persisted from the start).
The detail page and dashboard list now show the business name, and the detail page has an Edit button.
Per the requirements:
Slug is read-only in the edit form (disabled input, never accepted by the server action — it's bound in from the route, not form data).
Domain uniqueness is validated against every other business (and correctly self-excludes the business being edited).
A domain change rewrites the Caddy site file, reloads Caddy, and force-recreates the container; a name-only change does neither.
No secrets (DATABASE_PASSWORD, AUTH_SECRET, etc.) are ever read into the edit UI — only name/domain/databaseName/status are exposed.
Clear success banners ("Changes saved" vs. "...the container was restarted...") and inline error display, matching the existing Add Business pattern.
The redirect loop was caused by the Manager cookie configuration:
1. secure was based on NODE_ENV=production, so HTTP deployments silently dropped the cookie.
2. Path=/manager excluded /api/manager/*, preventing authenticated Manager API requests.
Implemented
- Root cause fixed in [src/proxy.ts]: Auth.js is now initialized lazily and never invoked when MANAGER_MODE=true.
- Manager requests call handleProxyRequest directly, preserving redirects and CSP.
- Business requests continue through Auth.js with existing protected-route behavior.
- Added coverage in [proxy-manager-mode.test.ts]
1. Root cause of the database error
The Manager proxy was bypassing installation checks, but / still resolved to src/app/page.tsx, which statically imported:
- @/auth
- Prisma
- business branding
- business permissions
- primary business data
The root route now redirects to /manager inside the proxy before the business homepage is evaluated.
2. Root cause of the Auth.js error
The global business SessionProvider was mounted around Manager pages. It automatically requested:
The business homepage is not evaluated first.
5. Provider and authentication separation
Manager mode:
- Uses ManagerProviders
- Uses the signed Manager cookie session
- Does not mount SessionProvider
- Does not call /api/auth/session
- Does not import business Auth.js or Prisma
Business mode:
- Dynamically loads the existing business Providers
- Continues using Auth.js and SessionProvider
- Preserves installation, /owner, and /staff behavior
6. Database independence
Manager mode now avoids database initialization for:
- Proxy requests
- Root metadata and viewport
- Root routing
- Manager pages
- Manager APIs
- Manifest requests
- Health requests
- Business Auth.js requests
- Business cron requests
No fake DATABASE_* variables were added.
7. Tests
Added coverage for:
- Manager / routing
- Manager /manager access
- No installation-state check in Manager mode
- Prisma import protection
- Business installation checks
- /owner protection
- Business Auth.js 404 boundary
- Manager health route without Prisma
- Manager manifest without business branding
8. Validation
Root cause
src/proxy.ts statically imported installation-state.ts, which statically imported Prisma. Additionally, the root layout statically imported business branding, which also loads Prisma. The pathname check was also incorrect for Manager deployments served at /.
Key additions:
* Separate platform Manager stack: [stacks/manager/compose.yaml]
* Dedicated manager authentication with signed sessions.
* Business overview dashboard.
* Add Business workflow with slug suggestions and deployment progress.
* Business detail page with start, stop, restart, update, logs, Dockge, and conservative removal actions.
* Explicit management API endpoints under [src/app/api/manager]
* Shared provisioning service layer in [provisioning.service.ts]
* CLI and UI now use the same [create-business.sh] provisioning logic.
* Registry metadata, database status, container health, and media usage reporting.
* Manager-only entrypoint mode that does not require a business database.
* Updated deployment documentation with Manager setup and Caddy routing.
Validation passed:
* Infra, Caddy, Manager, and generated business Compose configurations.
* Shell syntax checks.
* Registry generation with registry image tags.
* git diff --check.
Scaled back google reviews to just get the star rating and number of reviews as google limits the number of reviews to 5.
Also removed the reviews tab from homepage.
And updated Appointments button layout
1. Card title fix: The "top-right text" feature was renamed and repositioned to be the actual card title (top-left, replacing the hardcoded "Loyalty Card"), matching what you asked for. This included a proper database migration (cardTopRightText → cardTitleText, etc., using CHANGE COLUMN so any already-configured value is preserved, not dropped) and updates through every layer — schema, service, owner form, and both the owner preview and customer-facing card. Verified live: typed "VIP Rewards" into the new "Card title" field and confirmed it renders top-left on both the owner's live preview and the real customer loyalty page, with the member number staying in its own badge top-right.
2. Tabs: The long "Card Appearance" control list is now split into Colours / Stamp / Text tabs (using the same Tabs component used elsewhere in the app), exactly as you suggested — Colours holds every colour + the background image, Stamp holds just the icon picker, Text holds the title, font, size, and terms line.
Along the way, while running full E2E regression, I found 6 test failures — but traced them via git stash to prove they're pre-existing bugs unrelated to this change (a booking/appointments-list bug), so I reverted-then-confirmed before restoring my work, and filed a separate follow-up task for them rather than mixing it into this change.
Both fixes are typechecked, linted, verified with the full unit (253) and integration (185) suites, confirmed live in a real browser end-to-end, and deployed to your running Docker container — the migration applied cleanly against your real database with no data loss.
Fixed an issue where media manager only shows files uploaded in specific sections.
Fixed an issue with icon picker names.
Removed jumbo rounding and a few other style tweaks.
Delivered:
Super Administrator — env-var provisioned, no StaffProfile at all (structural exclusion from Team/barber-picker/appointments/staff-totals, not a filter). hasPermission short-circuit + new getActingBusinessContext() helper retrofitted across 16 owner pages so it can actually manage the business.
Media Manager — one upload/library/external-URL modal + field picker, backing every image field app-wide. Local-disk storage + Docker volume, SVG rejected, size-capped.
Welcome backgrounds — desktop/mobile images with CSS breakpoint swap.
Business Branding settings — logo/cover/welcome/favicon/icon/colours all editable post-install; cover image finally rendered (was dead config).
Service descriptions — now surfaced on Home and in the booking flow.
Loyalty card — live preview, ColorPicker/MediaPicker/IconPicker, flip removed for a static premium-styled card, Loyalty added to bottom nav. Fixed a real pre-existing bug where the card's configured colour/image/icon were stored but never actually rendered.
Home Screen Redesign — hero gallery + sticky Services/Portfolio/Reviews/Details tabs, backed by a new unified gallery model/service (no hero/portfolio duplication). Details tab surfaces social links and a map that were never shown before.
Google Reviews — real sync pipeline (24h cron, CRON_SECRET-gated, mirrors the existing notification cron exactly), DB-only customer reads, owner hide/unhide + policy management page.
Architecture cleanup — SettingsPageShell extracted from 6 duplicated page wrappers; fixed a second pre-existing bug where branding image fields could never actually be cleared once set.
Verification: 418 tests passing (236 unit + 182 integration, all against a real MySQL database), full typecheck/lint clean across src, and a real Docker build+run cycle — which caught and fixed a genuine bug (scripts/ wasn't copied into the production image, so Super Admin sync would have crashed on every real deployment).
Owner Administration is complete. Summary of this final stretch of work:
Seed data extended with an inactive staff member, a pending invitation, a redeemed-reward fixture, a service with no eligible staff, and a future-appointment fixture for conflict testing — all idempotent, verified against the dev DB.
Testing brought to 179 unit / 86 integration / 27 E2E tests, all passing together on a freshly seeded, disposable Postgres:
Unit tests for every new pure validator (business hours, categories/services, booking rules, loyalty config, staff creation).
Integration tests covering opening-hours conflicts, last-owner protection, staff deactivation/eligibility conflicts, loyalty redemption's atomic race-guard, and owner-vs-barber/business-isolation authorization — all against a real Postgres.
12 new Playwright owner journeys, which surfaced and fixed two genuine issues along the way: a cookie-clearing race condition in the sign-out-mid-test flow (a real, reproducible timing bug, not just a test artifact), and confirmation that seed fixtures upserted by deterministic ID go stale once "today" has moved on — now handled defensively.
Desktop responsiveness: converted the single-column Business Details, Booking Rules, and Loyalty Configuration forms to a genuine two-column layout at the lg: breakpoint (verified via measured element positions, not just screenshots), confirmed mobile stacking is unaffected.
Final verification: clean tsc, eslint, and next build, plus the full three-suite run against one fresh database as the authoritative pass.
Phase 5 is complete. All 53 tasks are done, verified on a fresh database in a single clean run: **121 unit tests**, **55 integration tests**, and **15 E2E journeys** (6 Phase 4 + 9 new Phase 5) all passing, plus a clean `tsc`/`eslint`/`build`.
Summary of this final stretch:
- **Fixed a real bug**: `StaffService`'s break/time-off update/delete functions threw uncaught instead of failing gracefully on a permission mismatch — fixed with proper `PERMISSION_DENIED` handling, covered by new integration tests.
- **Built the remaining Phase 5 UI**: Breaks editor, Time Off editor, Barber Profile, and Notification Preferences — all wired to real services, no placeholders.
- **Extended seed data**: more customers, loyalty accounts/transactions/rewards, and appointments spanning every status today (anchored to real time so the "Current Appointment" priority logic is reliably exercised).
- **Found and fixed a second real bug live in the browser**: the Break/Time-Off edit sheets showed stale default values instead of the item being edited (a classic `useState`-doesn't-reinitialize-on-prop-change bug) — fixed with a `key` prop to force remount.
- **Wrote the full Phase 5 test suite**: unit tests for `selectCurrentAppointment` and all `StaffService` validators, integration tests for `LoyaltyService` and `StaffService` (including the permission-fix regression tests), and 9 new Playwright E2E journeys covering the brief's exact required list.
- **Did the desktop-responsive pass**: a persistent sidebar nav replacing the bottom bar at `lg:`, a genuine calendar+"Right Now" side-by-side split, and 2-column layouts for the clients list and customer details — addressing the brief's explicit "more than simple centering" requirement.
- **Wrote [docs/phase-5-summary.md](docs/phase-5-summary.md)** documenting everything above in full.
Nothing is committed — the whole session (Phases 2–5) sits uncommitted on top of the initial template commit, same as you left it. Let me know if you'd like me to commit, or if you want to move on to Phase 6.
Seed data extended with a second barber (different hours/services), a beard-trim service + category, breaks, time off, a guest customer, and appointments in all three key states — verified against a live throwaway Postgres.
14 new integration tests (38 total with Phase 3's) covering guest/authenticated booking, any-barber assignment, slot-conflict handling, ownership enforcement, guest-data isolation, and book-again repricing.
Playwright E2E suite for the 6 required customer journeys, with its own global-setup.ts that migrates and seeds a database automatically.
Full verification pass, which caught and fixed three real bugs along the way (not just test-workarounds):
A date off-by-one bug in the calendar picker that made real weekdays show as closed for anyone in a UTC+ timezone (including the business's own).
A dialog that could vanish mid-confirmation on cancellation, before the customer could read the late-cancellation notice.
A silent test-cleanup leak in the Phase 3 fixtures that had been leaking stray businesses since Phase 3.
Plus a baseline desktop-responsive fix (centered max-w-2xl columns) across Home/Appointments/Settings/booking flow, since desktop had been effectively unstyled.
Final state: 90 unit tests, 38 integration tests, and all 6 E2E journeys passing together on a fresh database; clean tsc, eslint, and next build. Full writeup with routes, security model, guest-matching rules, known limitations, and what to check before Phase 5 is in docs/phase-4-summary.md.
Architecture: Six focused services with the BookingService/SchedulingService overlap resolved by giving SchedulingService exactly one job, the transactional double-booking guard , while BookingService owns the create/reschedule/cancel workflow around it.
59 unit tests, 24 integration tests
Base app architecture:
Route groups for customer, staff and owner.
* Reusable design system with around 20 UI components.
* Complete Prisma schema.
* Authentication with email/password and Google.
* Role and permission model.
* Protected routes.
* Clean separation between features, services, components and shared utilities.