diff --git a/apps/web/src/router.tsx b/apps/web/src/router.tsx
index 384d1b0..24728ef 100644
--- a/apps/web/src/router.tsx
+++ b/apps/web/src/router.tsx
@@ -117,9 +117,10 @@ function SetupLayout() {
}
/** Subscriptions layout — a standalone top-level section (its own header nav entry),
- * with tabs for the subscriber catalog, the plan catalog, and the tariff lab. Each
- * tab is a gated child route; an operator with only subscription:read sees just the
- * first tab. */
+ * with tabs for the subscriber catalog and the plan catalog. Each tab is a gated
+ * child route; an operator with only subscription:read sees just the first tab.
+ * (The tariff lab moved to /setup/tariff/lab, 2026-07-05 — it tests the tariff, so
+ * it lives with the tariff.) */
function SubscriptionsLayout() {
const { user } = rootRoute.useRouteContext();
const { t } = useTranslation();
@@ -129,7 +130,21 @@ function SubscriptionsLayout() {
+
+
+ );
+}
+
+/** Tariff layout — the rate-card hub under Setup: the composer (index) and the
+ * pricing LAB as sub-tabs. One tariff:read gate on the parent covers both. */
+function TariffLayout() {
+ const { t } = useTranslation();
+ return (
+
+
@@ -519,7 +534,10 @@ const legacyRedirects = (
["/shift", "/shifts"],
["/setup/subscriptions", "/subscriptions"],
["/setup/plans", "/subscriptions/plans"],
- ["/setup/tariff-lab", "/subscriptions/tariff-lab"],
+ // The tariff lab bounced twice: /setup/tariff-lab → /subscriptions/tariff-lab
+ // (2026-06-21) → /setup/tariff/lab (2026-07-05, back with the tariff it tests).
+ ["/setup/tariff-lab", "/setup/tariff/lab"],
+ ["/subscriptions/tariff-lab", "/setup/tariff/lab"],
["/setup/shifts", "/shifts"],
["/setup/reports", "/reports"],
] as const
@@ -633,8 +651,20 @@ const tariffRoute = createRoute({
getParentRoute: () => setupRoute,
path: "tariff",
beforeLoad: ({ context }) => requirePerm("tariff:read")(context),
+ component: TariffLayout,
+});
+const tariffComposerRoute = createRoute({
+ getParentRoute: () => tariffRoute,
+ path: "/",
component: () => ,
});
+// The tariff LAB — lives with the tariff it tests (moved from /subscriptions,
+// 2026-07-05). The parent's tariff:read gate covers it.
+const tariffLabRoute = createRoute({
+ getParentRoute: () => tariffRoute,
+ path: "lab",
+ component: () => ,
+});
// --- /subscriptions — a standalone top-level section with its own tabs. The catalog
// (index), the plan catalog, and the tariff lab live here, not under /setup. ---
@@ -651,7 +681,6 @@ const subscriptionsIndexRoute = createRoute({
beforeLoad: ({ context }) => {
if (can(context.user, "subscription:read")) return;
if (can(context.user, "subscription:plan")) throw redirect({ to: "/subscriptions/plans" });
- if (can(context.user, "tariff:read")) throw redirect({ to: "/subscriptions/tariff-lab" });
throw redirect({ to: "/booth" });
},
component: function SubscriptionsRoute() {
@@ -665,12 +694,6 @@ const subscriptionPlansRoute = createRoute({
beforeLoad: ({ context }) => requirePerm("subscription:plan")(context),
component: () => ,
});
-const tariffLabRoute = createRoute({
- getParentRoute: () => subscriptionsRoute,
- path: "tariff-lab",
- beforeLoad: ({ context }) => requirePerm("tariff:read")(context),
- component: () => ,
-});
const siteRoute = createRoute({
getParentRoute: () => setupRoute,
path: "site",
@@ -751,11 +774,10 @@ const routeTree = rootRoute.addChildren([
subscriptionsRoute.addChildren([
subscriptionsIndexRoute,
subscriptionPlansRoute,
- tariffLabRoute,
]),
setupRoute.addChildren([
setupDevicesRoute,
- tariffRoute,
+ tariffRoute.addChildren([tariffComposerRoute, tariffLabRoute]),
siteRoute,
usersRoute,
rolesRoute,