fix(customer): unwrap /customer/* from RequireFeature gate
RequireFeature calls useFeatureFlags(), which throws unless mounted inside FeatureFlagsProvider — and that provider only wraps AdminLayout. So unauthenticated visitors hitting /customer/login crashed into the React error boundary with 'Oops! Something went wrong'. The customerPortal flag continues to hide every admin-side surface (sidebar entry, /admin/customers routes, CustomerAccountPicker on event forms), which is what the flag is actually for. The customer-side tree stays reachable so existing customers can still log in even if the admin flips the flag off temporarily. Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
+33
-28
@@ -175,36 +175,41 @@ function App() {
|
|||||||
<Route path="/invite/:token" element={<AcceptInvitePage />} />
|
<Route path="/invite/:token" element={<AcceptInvitePage />} />
|
||||||
|
|
||||||
{/* Customer surface (#354). Strictly separate provider /
|
{/* Customer surface (#354). Strictly separate provider /
|
||||||
cookie / API surface from /admin/*. Gated by the
|
cookie / API surface from /admin/*. The customerPortal
|
||||||
customerPortal feature flag — when off, all
|
feature flag hides the *admin-side* surfaces (sidebar
|
||||||
/customer/* URLs redirect to /admin/dashboard. */}
|
entry, /admin/customers routes, CustomerAccountPicker)
|
||||||
<Route element={<RequireFeature flag="customerPortal" fallback="/admin/login" />}>
|
via RequireFeature. The customer-side /customer/*
|
||||||
<Route path="/customer/*" element={
|
tree stays publicly reachable so existing customers
|
||||||
<CustomerAuthProvider>
|
can still log in even if the admin temporarily flips
|
||||||
<Routes>
|
the flag off — and because RequireFeature reads from
|
||||||
{/* Public surfaces: login, accept-invite, reset —
|
FeatureFlagsProvider (admin-only context), gating
|
||||||
no CustomerLayout (their own branded shells). */}
|
these routes here would crash unauthenticated
|
||||||
<Route path="login" element={<CustomerLoginPage />} />
|
visitors with an unmounted-provider error. */}
|
||||||
<Route path="invite/:token" element={<CustomerAcceptInvitePage />} />
|
<Route path="/customer/*" element={
|
||||||
<Route path="reset-password/:token" element={<CustomerResetPasswordPage />} />
|
<CustomerAuthProvider>
|
||||||
|
<Routes>
|
||||||
|
{/* Public surfaces: login, accept-invite, reset —
|
||||||
|
no CustomerLayout (their own branded shells). */}
|
||||||
|
<Route path="login" element={<CustomerLoginPage />} />
|
||||||
|
<Route path="invite/:token" element={<CustomerAcceptInvitePage />} />
|
||||||
|
<Route path="reset-password/:token" element={<CustomerResetPasswordPage />} />
|
||||||
|
|
||||||
{/* Authenticated surfaces share the sidebar layout
|
{/* Authenticated surfaces share the sidebar layout
|
||||||
(Outlet pattern, mirrors AdminLayout). The
|
(Outlet pattern, mirrors AdminLayout). The
|
||||||
CustomerLayout itself enforces auth — bouncing
|
CustomerLayout itself enforces auth — bouncing
|
||||||
unauthenticated visitors to /customer/login. */}
|
unauthenticated visitors to /customer/login. */}
|
||||||
<Route element={<CustomerLayout />}>
|
<Route element={<CustomerLayout />}>
|
||||||
<Route path="dashboard" element={<CustomerDashboardPage />} />
|
<Route path="dashboard" element={<CustomerDashboardPage />} />
|
||||||
<Route path="calendar" element={<CustomerCalendarPage />} />
|
<Route path="calendar" element={<CustomerCalendarPage />} />
|
||||||
<Route path="quotes" element={<CustomerQuotesPage />} />
|
<Route path="quotes" element={<CustomerQuotesPage />} />
|
||||||
<Route path="bills" element={<CustomerBillsPage />} />
|
<Route path="bills" element={<CustomerBillsPage />} />
|
||||||
<Route path="profile" element={<CustomerProfilePage />} />
|
<Route path="profile" element={<CustomerProfilePage />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route index element={<Navigate to="/customer/dashboard" replace />} />
|
<Route index element={<Navigate to="/customer/dashboard" replace />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</CustomerAuthProvider>
|
</CustomerAuthProvider>
|
||||||
} />
|
} />
|
||||||
</Route>
|
|
||||||
|
|
||||||
{/* Public legal pages */}
|
{/* Public legal pages */}
|
||||||
<Route path="/impressum" element={<LegalPage />} />
|
<Route path="/impressum" element={<LegalPage />} />
|
||||||
|
|||||||
Reference in New Issue
Block a user