From 1b7aedb2045c1d50330fba4f38bcb42f2a8fd139 Mon Sep 17 00:00:00 2001 From: Michael Chihlas Date: Sat, 30 May 2026 20:43:59 -0400 Subject: [PATCH] feat(l1): admin L1 category settings page + route + settings card New owner-gated pages/account/L1CategoriesPage.tsx: checkbox list of available categories toggling enabled via l1Api.getCategories/setCategories, plus a read-only 'always excluded (safety)' hard-floor list. Registered lazy route /account/l1-categories (ProtectedRoute requiredRole=owner) and an 'L1 AI build categories' card in the AccountSettingsPage owner section. tsc -b + eslint clean. Co-Authored-By: Claude Opus 4.7 --- frontend/src/pages/AccountSettingsPage.tsx | 7 ++ .../src/pages/account/L1CategoriesPage.tsx | 98 +++++++++++++++++++ frontend/src/router.tsx | 9 ++ 3 files changed, 114 insertions(+) create mode 100644 frontend/src/pages/account/L1CategoriesPage.tsx diff --git a/frontend/src/pages/AccountSettingsPage.tsx b/frontend/src/pages/AccountSettingsPage.tsx index fe8ecffc..1037ba0f 100644 --- a/frontend/src/pages/AccountSettingsPage.tsx +++ b/frontend/src/pages/AccountSettingsPage.tsx @@ -18,6 +18,7 @@ import { RefreshCw, Server, Shield, + Wand2, UserCog, X, } from 'lucide-react' @@ -662,6 +663,12 @@ export function AccountSettingsPage() { title="Team categories" description="Shared flow categories for your workspace" /> + } + title="L1 AI build categories" + description="Which problem types the L1 assistant may build trees for" + /> } diff --git a/frontend/src/pages/account/L1CategoriesPage.tsx b/frontend/src/pages/account/L1CategoriesPage.tsx new file mode 100644 index 00000000..ca923d2e --- /dev/null +++ b/frontend/src/pages/account/L1CategoriesPage.tsx @@ -0,0 +1,98 @@ +import { useEffect, useState } from 'react' +import { PageMeta } from '@/components/common/PageMeta' +import { l1Api } from '@/api/l1' +import { toast } from '@/lib/toast' +import type { L1Categories } from '@/types/l1' + +const prettify = (key: string) => + key.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()) + +export default function L1CategoriesPage() { + const [data, setData] = useState(null) + const [saving, setSaving] = useState(null) + + useEffect(() => { + l1Api + .getCategories() + .then(setData) + .catch(() => toast.error('Failed to load L1 categories.')) + }, []) + + const toggle = async (cat: string) => { + if (!data) return + const next = data.enabled.includes(cat) + ? data.enabled.filter((c) => c !== cat) + : [...data.enabled, cat] + setSaving(cat) + try { + const updated = await l1Api.setCategories(next) + setData({ ...data, enabled: updated.enabled }) + toast.success('L1 categories updated.') + } catch { + toast.error('Could not update categories.') + } finally { + setSaving(null) + } + } + + if (!data) { + return ( +
+ +

Loading…

+
+ ) + } + + return ( +
+ +
+

+ L1 AI build categories +

+

+ When an L1 tech describes a problem with no matching published flow, the + assistant can build a troubleshooting tree on the fly — but only for the + categories you enable here. Disabled categories fall back to an ad-hoc walk + or escalation. +

+
+ +
+ {data.available.map((cat) => { + const checked = data.enabled.includes(cat) + return ( + + ) + })} +
+ +
+

+ Always excluded (safety) +

+

+ These action classes are never built automatically and cannot be enabled. +

+
    + {data.hard_floor.map((h) => ( +
  • {prettify(h)}
  • + ))} +
+
+
+ ) +} diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index 78c6336d..61f07f00 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -114,6 +114,7 @@ const IntegrationsPage = lazyWithRetry(() => import('@/pages/account/Integration const BrandingSettingsPage = lazyWithRetry(() => import('@/pages/account/BrandingSettingsPage')) const BillingPage = lazyWithRetry(() => import('@/pages/account/BillingPage')) const SelectPlanPage = lazyWithRetry(() => import('@/pages/account/SelectPlanPage')) +const L1CategoriesPage = lazyWithRetry(() => import('@/pages/account/L1CategoriesPage')) /** Wraps a lazy-loaded page with Suspense + ErrorBoundary */ function page(Component: React.LazyExoticComponent) { @@ -363,6 +364,14 @@ export const router = sentryCreateBrowserRouter([ ), }, + { + path: 'l1-categories', + element: ( + + {page(L1CategoriesPage)} + + ), + }, { path: 'chat-retention', element: (