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: (