Compare commits

..

33 Commits

Author SHA1 Message Date
Flea Flicker a6c4c30fb2 Promote dev → uat: GRO-2572 SSO redirect fix (#91) (#92)
CI / Test (push) Failing after 11s
CI / Lint & Typecheck (push) Successful in 31s
CI / Build & Push Docker Image (push) Has been skipped
CI / Test (pull_request) Successful in 23s
CI / Lint & Typecheck (pull_request) Successful in 46s
CI / Build & Push Docker Image (pull_request) Successful in 14s
Promote dev → uat: GRO-2572 SSO redirect fix (#91)

Co-authored-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
Co-committed-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
2026-06-26 12:20:21 +00:00
Flea Flicker 82e119875b Promote dev → uat: GRO-1026 portal mobile overflow fix (#89)
CI / Test (push) Successful in 21s
CI / Lint & Typecheck (pull_request) Successful in 29s
CI / Test (pull_request) Successful in 47s
CI / Lint & Typecheck (push) Successful in 1m2s
CI / Build & Push Docker Image (pull_request) Successful in 23s
CI / Build & Push Docker Image (push) Successful in 16s
Promote dev → uat: GRO-1026 portal mobile overflow fix (scrollbar-hide re-apply)
2026-06-26 09:08:01 +00:00
Flea Flicker d0745ed802 feat(GRO-2516): promote agent-runtime .gitignore stanza dev → uat (#85)
CI / Test (push) Successful in 23s
CI / Lint & Typecheck (push) Successful in 29s
CI / Build & Push Docker Image (push) Successful in 13s
CI / Test (pull_request) Successful in 31s
CI / Lint & Typecheck (pull_request) Successful in 37s
CI / Build & Push Docker Image (pull_request) Successful in 43s
feat: .gitignore hardening - agent-runtime files (GRO-2516)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-25 02:52:49 +00:00
Flea Flicker 8253e8a84d Merge pull request 'Promote dev → uat: GRO-2513 Settings role-gate' (#83) from dev into uat
CI / Test (push) Successful in 26s
CI / Lint & Typecheck (push) Successful in 43s
CI / Build & Push Docker Image (push) Successful in 22s
CI / Test (pull_request) Successful in 18s
CI / Lint & Typecheck (pull_request) Successful in 29s
CI / Build & Push Docker Image (pull_request) Successful in 15s
Promote dev → uat: GRO-2513 Settings role-gate

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-25 02:08:52 +00:00
Flea Flicker 532869f926 Promote dev → uat: GRO-2373 in-portal chrome sign-out button (#78)
CI / Test (push) Successful in 21s
CI / Lint & Typecheck (push) Successful in 27s
CI / Build & Push Docker Image (push) Successful in 48s
2026-06-11 22:03:10 +00:00
Flea Flicker a7f2e2e6b3 Merge pull request 'Promote dev → uat: GRO-2359 OOBE portal-creation routing' (#76) from promote/GRO-2359-dev-to-uat into uat
CI / Test (push) Successful in 22s
CI / Lint & Typecheck (push) Successful in 28s
CI / Build & Push Docker Image (push) Successful in 14s
Promote dev → uat: GRO-2359 OOBE portal-creation routing (#76)
2026-06-11 16:44:50 +00:00
Flea Flicker a12bf019fa feat(GRO-2359): route Authentik new-SSO users into OOBE (web)
CI / Test (pull_request) Successful in 21s
CI / Lint & Typecheck (pull_request) Successful in 29s
CI / Build & Push Docker Image (pull_request) Successful in 15s
The post-auth handler in CustomerPortal previously rendered the
"Portal access not configured" card when the SSO bridge returned 404
(no client row for the user's email). That trapped first-time SSO
users on a dead-end screen with no path to portal creation.

This change routes the 404 to a new OOBE component (src/portal/OOBE.tsx)
that drives portal creation:
  * Mounts inline inside CustomerPortal so the post-auth flow stays
    inside the portal render tree (no App-level router needed).
  * Also reachable as a direct deep-link via the new /onboarding route
    in App.tsx (for grooming admins or recovery flows).
  * Submits to a new POST /api/portal/clients-from-auth endpoint in
    groombook-api (companion commit) that creates a fresh client row
    bound to the Better Auth email. 409 means the email already has a
    portal record — the OOBE shows a portal-selection message.
  * Uses the canonical shared signOut() from lib/auth-client (GRO-2358
    invariant) for the Sign out button.
  * Full window.location.href reload on submit success to reset the
    bridge's cached state and land the user in their portal.

The no-access card itself is preserved for the deep-link deleted-portal
case (a customer whose portal was disabled/deleted), signalled via
?noAccess=deleted-portal on a portal sub-route. The OOBE handles the
first-time-creation case; the no-access card handles the "had a portal
but lost it" case.

Test coverage:
  * "routes to /onboarding when session-from-auth returns 404 (GRO-2359)"
    — proves the post-auth 404 mounts the OOBE inline, not the legacy
    no-access card.
  * 6 new OOBE tests: render from direct link, name prefill, form
    submission, 409 portal-selection, required-name validation, shared
    signOut(), redirect on no-session.
  * P1 no-access tests reworked to use ?noAccess=deleted-portal so the
    GRO-2358 signOut invariant is still verified on the only surviving
    path to the no-access card.

UAT_PLAYBOOK §5.25.5–6e rewritten to cover the OOBE flow (form submit,
409, deep-link mount, deleted-portal no-access card).

Paired with the api PR on feature/2357-p2-portal-clients-from-auth.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
(cherry picked from commit 250c7a5ac9)
2026-06-11 16:35:14 +00:00
Flea Flicker bfe3ccf3b2 Promote dev → uat: GRO-2358 logout on no-access screen (#73)
CI / Test (push) Successful in 19s
CI / Lint & Typecheck (push) Successful in 24s
CI / Build & Push Docker Image (push) Successful in 10s
2026-06-11 14:33:13 +00:00
Flea Flicker b52b8e10ad feat(GRO-2319): dev→uat — live StatusBadge palette (web) (#70)
CI / Test (push) Successful in 21s
CI / Lint & Typecheck (push) Successful in 27s
CI / Build & Push Docker Image (push) Successful in 14s
2026-06-09 11:04:15 +00:00
Flea Flicker f58a0e569b Promote dev → uat: GRO-2160 route nav export + offline polish (#67)
CI / Test (push) Successful in 20s
CI / Lint & Typecheck (push) Successful in 26s
CI / Build & Push Docker Image (push) Successful in 10s
CI / Test (pull_request) Successful in 21s
CI / Lint & Typecheck (pull_request) Successful in 28s
CI / Build & Push Docker Image (pull_request) Successful in 14s
2026-06-09 04:40:32 +00:00
Flea Flicker e93017b279 Promote dev → uat: GRO-2159 drag-to-reorder + re-optimize (#64)
CI / Test (push) Successful in 35s
CI / Lint & Typecheck (push) Successful in 44s
CI / Build & Push Docker Image (push) Successful in 11s
CI / Test (pull_request) Successful in 21s
CI / Lint & Typecheck (pull_request) Successful in 27s
CI / Build & Push Docker Image (pull_request) Successful in 12s
2026-06-09 03:10:17 +00:00
Flea Flicker db11e5f2bd Merge pull request 'Promote dev → uat: GRO-2236 portal Book New service cards price + duration' (#58) from flea/dev-to-uat-gro-2236 into uat
CI / Test (push) Successful in 24s
CI / Lint & Typecheck (push) Successful in 30s
CI / Build & Push Docker Image (push) Successful in 48s
2026-06-09 02:13:08 +00:00
Flea Flicker 980615b8e6 Promote dev → uat: GRO-2158 route planner page (#61)
CI / Test (push) Successful in 18s
CI / Lint & Typecheck (push) Successful in 27s
CI / Build & Push Docker Image (push) Successful in 14s
CI / Test (pull_request) Successful in 20s
CI / Lint & Typecheck (pull_request) Successful in 30s
CI / Build & Push Docker Image (pull_request) Successful in 41s
2026-06-09 02:00:55 +00:00
The Dogfather f549101962 fix(GRO-2236): portal Book New service cards show price + duration (#57)
CI / Test (pull_request) Successful in 20s
CI / Lint & Typecheck (pull_request) Successful in 28s
CI / Build & Push Docker Image (pull_request) Successful in 14s
Co-authored-by: The Dogfather <20+gb_dogfather@noreply.git.farh.net>
Co-committed-by: The Dogfather <20+gb_dogfather@noreply.git.farh.net>
2026-06-08 23:32:19 +00:00
Flea Flicker 62dc85b560 Promote dev → uat: GRO-2211/2218/2207 + GRO-2234 portal Book New (cumulative) (#56)
CI / Lint & Typecheck (push) Successful in 28s
CI / Test (push) Successful in 28s
CI / Build & Push Docker Image (push) Successful in 41s
CI / Test (pull_request) Successful in 21s
CI / Lint & Typecheck (pull_request) Successful in 27s
CI / Build & Push Docker Image (pull_request) Successful in 47s
2026-06-08 19:58:43 +00:00
Flea Flicker bc21d6de09 Promote dev → uat: GRO-2213 portal booking preferredTime HH:MM:SS fix (#52)
CI / Test (push) Successful in 21s
CI / Test (pull_request) Successful in 22s
CI / Lint & Typecheck (push) Successful in 26s
CI / Lint & Typecheck (pull_request) Successful in 28s
CI / Build & Push Docker Image (push) Successful in 25s
CI / Build & Push Docker Image (pull_request) Successful in 20s
2026-06-08 17:36:16 +00:00
Flea Flicker 32ef3bca4d Merge pull request 'Promote dev → uat: GRO-2180 portal Appointments ISO startTime fix' (#50) from dev into uat
CI / Lint & Typecheck (pull_request) Failing after 10m47s
CI / Test (push) Failing after 10m51s
CI / Lint & Typecheck (push) Failing after 10m52s
CI / Build & Push Docker Image (push) Has been skipped
CI / Test (pull_request) Failing after 15m38s
CI / Build & Push Docker Image (pull_request) Has been skipped
Merge PR #50: fix(GRO-2180) portal Appointments ISO startTime (dev → uat)

QA-approved (gb_lint); PR CI green after transient runner re-run.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 10:28:50 +00:00
Flea Flicker 47c29ecbc2 Promote to UAT: GRO-2105 BookingFlow/RescheduleFlow availability fix (#47)
CI / Test (push) Successful in 17s
CI / Lint & Typecheck (push) Successful in 23s
CI / Build & Push Docker Image (push) Successful in 19s
CI / Test (pull_request) Failing after 10m34s
CI / Lint & Typecheck (pull_request) Failing after 10m34s
CI / Build & Push Docker Image (pull_request) Has been skipped
2026-06-02 19:17:03 +00:00
The Dogfather de7386e47a Promote to UAT: GRO-2094 React bootstrap error instrumentation (#45)
CI / Test (push) Successful in 23s
CI / Lint & Typecheck (push) Successful in 30s
CI / Build & Push Docker Image (push) Successful in 13s
Co-authored-by: The Dogfather <20+gb_dogfather@noreply.git.farh.net>
Co-committed-by: The Dogfather <20+gb_dogfather@noreply.git.farh.net>
2026-06-02 18:42:25 +00:00
The Dogfather ec29f71974 Merge pull request 'Promote to UAT: GRO-2012 RescheduleFlow portalSessionId fallback' (#39) from dev into uat
CI / Test (push) Successful in 21s
CI / Lint & Typecheck (push) Successful in 30s
CI / Build & Push Docker Image (push) Successful in 10s
CI / Test (pull_request) Successful in 21s
CI / Lint & Typecheck (pull_request) Successful in 28s
CI / Build & Push Docker Image (pull_request) Successful in 13s
2026-06-01 17:46:35 +00:00
The Dogfather bd2a0d9516 Merge pull request 'Promote dev -> uat: GRO-2011 login-blank fix (+ GRO-1867)' (#37) from dev into uat
CI / Test (push) Successful in 19s
CI / Lint & Typecheck (push) Successful in 23s
CI / Build & Push Docker Image (push) Successful in 10s
2026-06-01 16:38:14 +00:00
The Dogfather 0e5e9d1f16 Merge pull request 'chore: promote dev → uat (GRO-1829 SW fix)' (#32) from dev into uat
CI / Test (push) Successful in 13s
CI / Lint & Typecheck (push) Successful in 23s
CI / Build & Push Docker Image (push) Successful in 15s
Merge: promote dev → uat (GRO-1829 SW fix)
2026-05-27 02:27:32 +00:00
The Dogfather 3b4d0f15f6 Merge pull request 'chore: promote dev → uat (GRO-1795 StatusBadge)' (#28) from dev into uat
CI / Lint & Typecheck (push) Successful in 17s
CI / Test (push) Successful in 13s
CI / Build & Push Docker Image (push) Successful in 34s
Merge PR #28: promote dev → uat (GRO-1795 StatusBadge)
2026-05-26 13:23:52 +00:00
The Dogfather 87939e5413 Merge pull request 'chore: promote dev → uat (GRO-1794 booking analytics)' (#27) from dev into uat
CI / Test (push) Successful in 19s
CI / Lint & Typecheck (push) Successful in 22s
CI / Build & Push Docker Image (push) Successful in 12s
Merge dev → uat: GRO-1794 booking funnel analytics events
2026-05-26 13:16:39 +00:00
The Dogfather 4e3a038bf3 Merge pull request 'Promote dev → uat (GRO-1793: dynamic time slots)' (#25) from dev into uat
CI / Test (push) Successful in 14s
CI / Lint & Typecheck (push) Successful in 16s
CI / Build & Push Docker Image (push) Failing after 6s
Promote dev → uat: GRO-1793 dynamic portal time slots (#25)
2026-05-26 13:02:16 +00:00
Lint Roller 8349ea00de Merge pull request 'promote: dev → uat (GRO-1757 SSO auto-provision fix)' (#19) from dev into uat
CI / Test (push) Successful in 13s
CI / Lint & Typecheck (push) Successful in 33s
CI / Build & Push Docker Image (push) Successful in 14s
CI / Test (pull_request) Successful in 19s
CI / Lint & Typecheck (pull_request) Successful in 24s
CI / Build & Push Docker Image (pull_request) Successful in 15s
promote: dev → uat (GRO-1757 SSO auto-provision fix)
2026-05-25 23:48:10 +00:00
The Dogfather 0306c7fbd9 Merge pull request 'chore(GRO-1592): promote dev→uat SSO session cookie fix' (#16) from promote-uat-gro1592 into uat
CI / Test (push) Successful in 12s
CI / Lint & Typecheck (push) Successful in 18s
CI / Build & Push Docker Image (push) Failing after 39s
2026-05-23 14:13:43 +00:00
Chris Farhood 93da2f1dd8 chore: promote dev→uat for GRO-1592 SSO session cookie fix
CI / Lint & Typecheck (pull_request) Successful in 17s
CI / Test (pull_request) Successful in 18s
CI / Build & Push Docker Image (pull_request) Failing after 41s
- Fixed frontend auth client baseURL fallback to use window.location.origin
- Added UAT test coverage (TC-AUTH-5.3.4)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-23 14:13:12 +00:00
The Dogfather 62cbfe4e43 Merge pull request 'promote: dev → uat (GRO-1173 buffer rules + GRO-1470 pet save persistence)' (#14) from dev into uat
CI / Test (push) Successful in 14s
CI / Lint & Typecheck (push) Successful in 19s
CI / Build & Push Docker Image (push) Successful in 9s
promote: dev → uat (GRO-1173 buffer rules + GRO-1470 pet save persistence) (#14)

Merged-By: The Dogfather (CTO)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-21 19:46:41 +00:00
The Dogfather db6a2a1bbf Merge pull request 'promote: dev → uat (Renovate config, GRO-1081)' (#11) from dev into uat
promote: dev → uat (Renovate config, GRO-1081)

Merge PR #11: dev → uat promotion
Includes: chore: add Renovate config (GRO-1081)
2026-05-20 12:42:04 +00:00
The Dogfather 032a3796ba Merge pull request 'chore: promote dev to uat (CI Docker registry fix)' (#10) from dev into uat
chore: promote dev to uat (CI Docker registry fix) (#10)

Promotes GRO-1348 CI registry fix to UAT.
2026-05-20 11:17:21 +00:00
the-dogfather-cto[bot] cac8fc947e chore(GRO-1289): promote dev to uat — add UAT_PLAYBOOK.md
chore(GRO-1289): promote dev to uat — add UAT_PLAYBOOK.md
2026-05-14 21:13:56 +00:00
the-dogfather-cto[bot] 592be1301c chore: promote dev to uat (#3)
chore: promote dev to uat
2026-05-11 13:19:33 +00:00
3 changed files with 67 additions and 1 deletions
+1 -1
View File
@@ -86,7 +86,7 @@ export const { signIn, signOut, useSession, changePassword } = authClient;
| # | Scenario | Steps | Pass Criteria | Fail Criteria |
|---|----------|-------|---------------|---------------|
| TC-WEB-SSO-1 | Sign-in page shows SSO button | Navigate to app root URL | Sign-in page displayed with "Sign in with SSO" button visible | No SSO button, 403 before page loads |
| TC-WEB-SSO-2 | Click SSO redirects to Authentik | Click "Sign in with SSO" button | Browser redirected to Authentik login at auth.farh.net | No redirect, error shown, button does nothing |
| TC-WEB-SSO-2 | Click SSO redirects to Authentik (GRO-2572) | **Fresh session only (no pre-existing auth cookie).** Click "Sign in with SSO" button | Browser navigates to Authentik login at auth.farh.net within ~1 s — address bar changes to auth.farh.net URL | No redirect, error shown, button stays disabled, user remains on /login. Regression: prior to GRO-2572 fix the client never followed the `data.url` returned by Better Auth. Run from a clean incognito context to avoid a stale cookie masking the defect. |
| TC-WEB-SSO-3 | Valid OIDC credentials authenticate | At Authentik, enter valid credentials and authenticate | Redirected back to app with active session | Redirect loop, 403, session not established |
| TC-WEB-SSO-4 | Post-login dashboard accessible | After SSO flow completes, dashboard loads | Dashboard displays correctly with user identity shown | Blank page, 403, session not active |
| TC-WEB-SSO-5 | User identity displayed correctly | After SSO login, check header/nav | User name/email/initials shown in nav, role reflected in UI | No user indicator, wrong user shown |
+6
View File
@@ -45,6 +45,12 @@ function LoginPage() {
if (result?.error) {
setError(result.error.message ?? "Sign-in failed");
setIsLoading(false);
return;
}
// Better Auth returns the IdP authorize URL in data.url with redirect:true rather than
// issuing an HTTP 30x — the client must follow it (GRO-2572).
if (result?.data?.url) {
window.location.href = result.data.url;
}
};
+60
View File
@@ -1,5 +1,6 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, within, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { MemoryRouter } from "react-router-dom";
import { App } from "../App";
@@ -232,6 +233,7 @@ describe("Dev login selector", () => {
});
it("does not redirect when a dev user is already selected", async () => {
localStorage.setItem("dev-user", JSON.stringify({ type: "staff", id: "s1", name: "Sarah" }));
global.fetch = vi.fn((url: string) => {
@@ -269,3 +271,61 @@ describe("Dev login selector", () => {
).toBeInTheDocument();
});
});
describe("GRO-2572 — SSO button follows redirect URL", () => {
it("navigates to data.url when signIn.social returns a redirect", async () => {
// Mock signIn.social to return the redirect payload Better Auth sends
vi.mock("../lib/auth-client.js", () => ({
useSession: () => ({ data: null, isPending: false }),
signIn: {
social: vi.fn().mockResolvedValue({
data: { redirect: true, url: "https://auth.farh.net/application/o/authorize/?test=1" },
error: null,
}),
},
signOut: vi.fn(),
changePassword: vi.fn(),
}));
const assignMock = vi.fn();
Object.defineProperty(window, "location", {
value: { ...window.location, href: "", origin: "https://uat.groombook.dev" },
writable: true,
});
Object.defineProperty(window.location, "href", {
set: assignMock,
get: () => "",
});
global.fetch = vi.fn((url: string) => {
if (url === "/api/dev/config") {
return Promise.resolve({ ok: true, json: async () => ({ authDisabled: false }) } as Response);
}
if (url === "/api/auth/get-session") {
return Promise.resolve({ ok: true, json: async () => null } as unknown as Response);
}
if (url === "/api/setup/status") {
return Promise.resolve({ ok: true, json: async () => ({ needsSetup: false }) } as Response);
}
if (url === "/api/auth/providers") {
return Promise.resolve({ ok: true, json: async () => ({ providers: ["authentik"] }) } as Response);
}
return Promise.resolve({ ok: true, json: async () => [] } as Response);
}) as unknown as typeof fetch;
render(
<MemoryRouter initialEntries={["/login"]}>
<App />
</MemoryRouter>
);
const ssoButton = await screen.findByRole("button", { name: /sign in with sso/i });
await userEvent.click(ssoButton);
await waitFor(() => {
expect(assignMock).toHaveBeenCalledWith(
"https://auth.farh.net/application/o/authorize/?test=1"
);
});
});
});