2026-03-03 11:25:56 -06:00
import { useEffect , useRef } from "react" ;
2026-03-11 11:40:38 -05:00
import { Navigate , Outlet , Route , Routes , useLocation , useParams } from "@/lib/router" ;
2026-02-23 14:41:21 -06:00
import { useQuery } from "@tanstack/react-query" ;
2026-03-03 11:25:56 -06:00
import { Button } from "@/components/ui/button" ;
2026-02-16 13:32:04 -06:00
import { Layout } from "./components/Layout" ;
2026-03-03 11:25:56 -06:00
import { OnboardingWizard } from "./components/OnboardingWizard" ;
2026-02-23 14:41:21 -06:00
import { authApi } from "./api/auth" ;
import { healthApi } from "./api/health" ;
2026-02-16 13:32:04 -06:00
import { Dashboard } from "./pages/Dashboard" ;
Overhaul UI with shadcn components and new pages
Add shadcn/ui components (badge, button, card, input, select,
separator). Add company context provider. New pages: Activity,
Approvals, Companies, Costs, Org chart. Restyle existing pages
(Dashboard, Agents, Issues, Goals, Projects) with shadcn components
and dark theme. Update layout, sidebar navigation, and routing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 09:07:32 -06:00
import { Companies } from "./pages/Companies" ;
2026-02-16 13:32:04 -06:00
import { Agents } from "./pages/Agents" ;
Add detail pages, property panels, and restyle list pages
New pages: AgentDetail, GoalDetail, IssueDetail, ProjectDetail, Inbox,
MyIssues. New feature components: AgentProperties, GoalProperties,
IssueProperties, ProjectProperties, GoalTree, NewIssueDialog. Add
heartbeats API client. Restyle all list pages (Agents, Issues, Goals,
Projects, Dashboard, Costs, Activity, Org) with EntityRow, FilterBar,
and improved layouts. Add routing for detail views.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 09:57:06 -06:00
import { AgentDetail } from "./pages/AgentDetail" ;
2026-02-16 13:32:04 -06:00
import { Projects } from "./pages/Projects" ;
Add detail pages, property panels, and restyle list pages
New pages: AgentDetail, GoalDetail, IssueDetail, ProjectDetail, Inbox,
MyIssues. New feature components: AgentProperties, GoalProperties,
IssueProperties, ProjectProperties, GoalTree, NewIssueDialog. Add
heartbeats API client. Restyle all list pages (Agents, Issues, Goals,
Projects, Dashboard, Costs, Activity, Org) with EntityRow, FilterBar,
and improved layouts. Add routing for detail views.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 09:57:06 -06:00
import { ProjectDetail } from "./pages/ProjectDetail" ;
2026-02-16 13:32:04 -06:00
import { Issues } from "./pages/Issues" ;
Add detail pages, property panels, and restyle list pages
New pages: AgentDetail, GoalDetail, IssueDetail, ProjectDetail, Inbox,
MyIssues. New feature components: AgentProperties, GoalProperties,
IssueProperties, ProjectProperties, GoalTree, NewIssueDialog. Add
heartbeats API client. Restyle all list pages (Agents, Issues, Goals,
Projects, Dashboard, Costs, Activity, Org) with EntityRow, FilterBar,
and improved layouts. Add routing for detail views.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 09:57:06 -06:00
import { IssueDetail } from "./pages/IssueDetail" ;
2026-02-16 13:32:04 -06:00
import { Goals } from "./pages/Goals" ;
Add detail pages, property panels, and restyle list pages
New pages: AgentDetail, GoalDetail, IssueDetail, ProjectDetail, Inbox,
MyIssues. New feature components: AgentProperties, GoalProperties,
IssueProperties, ProjectProperties, GoalTree, NewIssueDialog. Add
heartbeats API client. Restyle all list pages (Agents, Issues, Goals,
Projects, Dashboard, Costs, Activity, Org) with EntityRow, FilterBar,
and improved layouts. Add routing for detail views.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 09:57:06 -06:00
import { GoalDetail } from "./pages/GoalDetail" ;
Overhaul UI with shadcn components and new pages
Add shadcn/ui components (badge, button, card, input, select,
separator). Add company context provider. New pages: Activity,
Approvals, Companies, Costs, Org chart. Restyle existing pages
(Dashboard, Agents, Issues, Goals, Projects) with shadcn components
and dark theme. Update layout, sidebar navigation, and routing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 09:07:32 -06:00
import { Approvals } from "./pages/Approvals" ;
UI: approval detail page, agent hiring UX, costs breakdown, sidebar badges, and dashboard improvements
Add ApprovalDetail page with comment thread, revision request/resubmit flow,
and ApprovalPayload component for structured payload display. Extend AgentDetail
with permissions management, config revision history, and duplicate action.
Add agent hire dialog with permission-gated access. Rework Costs page with
per-agent breakdown table and period filtering. Add sidebar badge counts for
pending approvals and inbox items. Enhance Dashboard with live metrics and
sparkline trends. Extend Agents list with pending_approval status and bulk
actions. Update IssueDetail with approval linking. Various component improvements
to MetricCard, InlineEditor, CommentThread, and StatusBadge.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 13:03:08 -06:00
import { ApprovalDetail } from "./pages/ApprovalDetail" ;
Overhaul UI with shadcn components and new pages
Add shadcn/ui components (badge, button, card, input, select,
separator). Add company context provider. New pages: Activity,
Approvals, Companies, Costs, Org chart. Restyle existing pages
(Dashboard, Agents, Issues, Goals, Projects) with shadcn components
and dark theme. Update layout, sidebar navigation, and routing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 09:07:32 -06:00
import { Costs } from "./pages/Costs" ;
import { Activity } from "./pages/Activity" ;
Add detail pages, property panels, and restyle list pages
New pages: AgentDetail, GoalDetail, IssueDetail, ProjectDetail, Inbox,
MyIssues. New feature components: AgentProperties, GoalProperties,
IssueProperties, ProjectProperties, GoalTree, NewIssueDialog. Add
heartbeats API client. Restyle all list pages (Agents, Issues, Goals,
Projects, Dashboard, Costs, Activity, Org) with EntityRow, FilterBar,
and improved layouts. Add routing for detail views.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 09:57:06 -06:00
import { Inbox } from "./pages/Inbox" ;
2026-02-19 14:02:29 -06:00
import { CompanySettings } from "./pages/CompanySettings" ;
2026-03-14 10:55:04 -05:00
import { CompanySkills } from "./pages/CompanySkills" ;
2026-02-17 20:07:49 -06:00
import { DesignGuide } from "./pages/DesignGuide" ;
2026-03-12 08:03:55 -05:00
import { InstanceSettings } from "./pages/InstanceSettings" ;
2026-03-13 16:22:34 -05:00
import { PluginManager } from "./pages/PluginManager" ;
import { PluginSettings } from "./pages/PluginSettings" ;
import { PluginPage } from "./pages/PluginPage" ;
2026-03-11 10:47:22 -05:00
import { RunTranscriptUxLab } from "./pages/RunTranscriptUxLab" ;
2026-02-25 08:39:31 -06:00
import { OrgChart } from "./pages/OrgChart" ;
2026-03-07 08:26:49 -06:00
import { NewAgent } from "./pages/NewAgent" ;
2026-02-23 14:41:21 -06:00
import { AuthPage } from "./pages/Auth" ;
2026-02-23 16:25:31 -06:00
import { BoardClaimPage } from "./pages/BoardClaim" ;
2026-02-23 14:41:21 -06:00
import { InviteLandingPage } from "./pages/InviteLanding" ;
2026-03-10 16:38:46 -05:00
import { NotFoundPage } from "./pages/NotFound" ;
2026-02-23 14:41:21 -06:00
import { queryKeys } from "./lib/queryKeys" ;
2026-03-02 16:44:03 -06:00
import { useCompany } from "./context/CompanyContext" ;
2026-03-03 11:25:56 -06:00
import { useDialog } from "./context/DialogContext" ;
2026-03-11 07:42:19 -05:00
import { loadLastInboxTab } from "./lib/inbox" ;
2026-02-23 14:41:21 -06:00
2026-03-09 15:30:08 -05:00
function BootstrapPendingPage ( { hasActiveInvite = false } : { hasActiveInvite? : boolean } ) {
2026-02-23 14:41:21 -06:00
return (
< div className = "mx-auto max-w-xl py-10" >
< div className = "rounded-lg border border-border bg-card p-6" >
< h1 className = "text-xl font-semibold" > Instance setup required < / h1 >
< p className = "mt-2 text-sm text-muted-foreground" >
2026-03-09 15:30:08 -05:00
{ hasActiveInvite
? "No instance admin exists yet. A bootstrap invite is already active. Check your Paperclip startup logs for the first admin invite URL, or run this command to rotate it:"
: "No instance admin exists yet. Run this command in your Paperclip environment to generate the first admin invite URL:" }
2026-02-23 14:41:21 -06:00
< / p >
< pre className = "mt-4 overflow-x-auto rounded-md border border-border bg-muted/30 p-3 text-xs" >
2026-03-03 08:45:26 -06:00
{ ` pnpm paperclipai auth bootstrap-ceo ` }
2026-02-23 14:41:21 -06:00
< / pre >
< / div >
< / div >
) ;
}
function CloudAccessGate() {
const location = useLocation ( ) ;
const healthQuery = useQuery ( {
queryKey : queryKeys.health ,
queryFn : ( ) = > healthApi . get ( ) ,
retry : false ,
2026-03-09 16:13:15 -05:00
refetchInterval : ( query ) = > {
const data = query . state . data as
| { deploymentMode ? : "local_trusted" | "authenticated" ; bootstrapStatus ? : "ready" | "bootstrap_pending" }
| undefined ;
return data ? . deploymentMode === "authenticated" && data . bootstrapStatus === "bootstrap_pending"
? 2000
: false ;
} ,
refetchIntervalInBackground : true ,
2026-02-23 14:41:21 -06:00
} ) ;
const isAuthenticatedMode = healthQuery . data ? . deploymentMode === "authenticated" ;
const sessionQuery = useQuery ( {
queryKey : queryKeys.auth.session ,
queryFn : ( ) = > authApi . getSession ( ) ,
enabled : isAuthenticatedMode ,
retry : false ,
} ) ;
if ( healthQuery . isLoading || ( isAuthenticatedMode && sessionQuery . isLoading ) ) {
return < div className = "mx-auto max-w-xl py-10 text-sm text-muted-foreground" > Loading . . . < / div > ;
}
if ( healthQuery . error ) {
return (
< div className = "mx-auto max-w-xl py-10 text-sm text-destructive" >
{ healthQuery . error instanceof Error ? healthQuery . error . message : "Failed to load app state" }
< / div >
) ;
}
if ( isAuthenticatedMode && healthQuery . data ? . bootstrapStatus === "bootstrap_pending" ) {
2026-03-09 15:30:08 -05:00
return < BootstrapPendingPage hasActiveInvite = { healthQuery . data . bootstrapInviteActive } / > ;
2026-02-23 14:41:21 -06:00
}
if ( isAuthenticatedMode && ! sessionQuery . data ) {
const next = encodeURIComponent ( ` ${ location . pathname } ${ location . search } ` ) ;
return < Navigate to = { ` /auth?next= ${ next } ` } replace / > ;
}
return < Outlet / > ;
}
2026-02-16 13:32:04 -06:00
2026-03-02 10:31:54 -06:00
function boardRoutes() {
return (
< >
< Route index element = { < Navigate to = "dashboard" replace / > } / >
< Route path = "dashboard" element = { < Dashboard / > } / >
2026-03-11 11:40:38 -05:00
< Route path = "onboarding" element = { < OnboardingRoutePage / > } / >
2026-03-02 10:31:54 -06:00
< Route path = "companies" element = { < Companies / > } / >
< Route path = "company/settings" element = { < CompanySettings / > } / >
2026-03-14 13:52:20 -05:00
< Route path = "skills/*" element = { < CompanySkills / > } / >
2026-03-12 08:03:55 -05:00
< Route path = "settings" element = { < LegacySettingsRedirect / > } / >
< Route path = "settings/*" element = { < LegacySettingsRedirect / > } / >
2026-03-13 16:22:34 -05:00
< Route path = "plugins/:pluginId" element = { < PluginPage / > } / >
2026-03-02 10:31:54 -06:00
< Route path = "org" element = { < OrgChart / > } / >
2026-03-02 17:02:15 -06:00
< Route path = "agents" element = { < Navigate to = "/agents/all" replace / > } / >
2026-03-02 10:31:54 -06:00
< Route path = "agents/all" element = { < Agents / > } / >
< Route path = "agents/active" element = { < Agents / > } / >
< Route path = "agents/paused" element = { < Agents / > } / >
< Route path = "agents/error" element = { < Agents / > } / >
2026-03-07 08:26:49 -06:00
< Route path = "agents/new" element = { < NewAgent / > } / >
2026-03-02 10:31:54 -06:00
< Route path = "agents/:agentId" element = { < AgentDetail / > } / >
< Route path = "agents/:agentId/:tab" element = { < AgentDetail / > } / >
< Route path = "agents/:agentId/runs/:runId" element = { < AgentDetail / > } / >
< Route path = "projects" element = { < Projects / > } / >
< Route path = "projects/:projectId" element = { < ProjectDetail / > } / >
< Route path = "projects/:projectId/overview" element = { < ProjectDetail / > } / >
< Route path = "projects/:projectId/issues" element = { < ProjectDetail / > } / >
< Route path = "projects/:projectId/issues/:filter" element = { < ProjectDetail / > } / >
2026-03-10 09:08:20 -05:00
< Route path = "projects/:projectId/configuration" element = { < ProjectDetail / > } / >
2026-03-02 10:31:54 -06:00
< Route path = "issues" element = { < Issues / > } / >
2026-03-02 17:02:15 -06:00
< Route path = "issues/all" element = { < Navigate to = "/issues" replace / > } / >
< Route path = "issues/active" element = { < Navigate to = "/issues" replace / > } / >
< Route path = "issues/backlog" element = { < Navigate to = "/issues" replace / > } / >
< Route path = "issues/done" element = { < Navigate to = "/issues" replace / > } / >
< Route path = "issues/recent" element = { < Navigate to = "/issues" replace / > } / >
2026-03-02 10:31:54 -06:00
< Route path = "issues/:issueId" element = { < IssueDetail / > } / >
< Route path = "goals" element = { < Goals / > } / >
< Route path = "goals/:goalId" element = { < GoalDetail / > } / >
2026-03-02 17:02:15 -06:00
< Route path = "approvals" element = { < Navigate to = "/approvals/pending" replace / > } / >
2026-03-02 10:31:54 -06:00
< Route path = "approvals/pending" element = { < Approvals / > } / >
< Route path = "approvals/all" element = { < Approvals / > } / >
< Route path = "approvals/:approvalId" element = { < ApprovalDetail / > } / >
< Route path = "costs" element = { < Costs / > } / >
< Route path = "activity" element = { < Activity / > } / >
2026-03-11 07:42:19 -05:00
< Route path = "inbox" element = { < InboxRootRedirect / > } / >
2026-03-11 08:26:41 -05:00
< Route path = "inbox/recent" element = { < Inbox / > } / >
< Route path = "inbox/unread" element = { < Inbox / > } / >
2026-03-02 10:31:54 -06:00
< Route path = "inbox/all" element = { < Inbox / > } / >
2026-03-11 08:26:41 -05:00
< Route path = "inbox/new" element = { < Navigate to = "/inbox/recent" replace / > } / >
2026-03-02 10:31:54 -06:00
< Route path = "design-guide" element = { < DesignGuide / > } / >
2026-03-11 10:47:22 -05:00
< Route path = "tests/ux/runs" element = { < RunTranscriptUxLab / > } / >
2026-03-14 09:26:45 -05:00
< Route path = ":pluginRoutePath" element = { < PluginPage / > } / >
2026-03-10 16:38:46 -05:00
< Route path = "*" element = { < NotFoundPage scope = "board" / > } / >
2026-03-02 10:31:54 -06:00
< / >
) ;
}
2026-03-11 07:42:19 -05:00
function InboxRootRedirect() {
return < Navigate to = { ` /inbox/ ${ loadLastInboxTab ( ) } ` } replace / > ;
}
2026-03-12 08:03:55 -05:00
function LegacySettingsRedirect() {
const location = useLocation ( ) ;
2026-03-13 16:22:34 -05:00
return < Navigate to = { ` /instance/settings/heartbeats ${ location . search } ${ location . hash } ` } replace / > ;
2026-03-12 08:03:55 -05:00
}
2026-03-11 11:40:38 -05:00
function OnboardingRoutePage() {
const { companies , loading } = useCompany ( ) ;
const { onboardingOpen , openOnboarding } = useDialog ( ) ;
const { companyPrefix } = useParams < { companyPrefix? : string } > ( ) ;
const opened = useRef ( false ) ;
const matchedCompany = companyPrefix
? companies . find ( ( company ) = > company . issuePrefix . toUpperCase ( ) === companyPrefix . toUpperCase ( ) ) ? ? null
: null ;
useEffect ( ( ) = > {
if ( loading || opened . current || onboardingOpen ) return ;
opened . current = true ;
if ( matchedCompany ) {
openOnboarding ( { initialStep : 2 , companyId : matchedCompany.id } ) ;
return ;
}
openOnboarding ( ) ;
} , [ companyPrefix , loading , matchedCompany , onboardingOpen , openOnboarding ] ) ;
const title = matchedCompany
? ` Add another agent to ${ matchedCompany . name } `
: companies . length > 0
? "Create another company"
: "Create your first company" ;
const description = matchedCompany
? "Run onboarding again to add an agent and a starter task for this company."
: companies . length > 0
? "Run onboarding again to create another company and seed its first agent."
: "Get started by creating a company and your first agent." ;
return (
< div className = "mx-auto max-w-xl py-10" >
< div className = "rounded-lg border border-border bg-card p-6" >
< h1 className = "text-xl font-semibold" > { title } < / h1 >
< p className = "mt-2 text-sm text-muted-foreground" > { description } < / p >
< div className = "mt-4" >
< Button
onClick = { ( ) = >
matchedCompany
? openOnboarding ( { initialStep : 2 , companyId : matchedCompany.id } )
: openOnboarding ( )
}
>
{ matchedCompany ? "Add Agent" : "Start Onboarding" }
< / Button >
< / div >
< / div >
< / div >
) ;
}
2026-03-02 16:44:03 -06:00
function CompanyRootRedirect() {
const { companies , selectedCompany , loading } = useCompany ( ) ;
2026-03-03 11:45:54 -06:00
const { onboardingOpen } = useDialog ( ) ;
2026-03-02 16:44:03 -06:00
if ( loading ) {
return < div className = "mx-auto max-w-xl py-10 text-sm text-muted-foreground" > Loading . . . < / div > ;
}
2026-03-03 11:45:54 -06:00
// Keep the first-run onboarding mounted until it completes.
if ( onboardingOpen ) {
return < NoCompaniesStartPage autoOpen = { false } / > ;
}
2026-03-02 16:44:03 -06:00
const targetCompany = selectedCompany ? ? companies [ 0 ] ? ? null ;
if ( ! targetCompany ) {
2026-03-03 11:25:56 -06:00
return < NoCompaniesStartPage / > ;
2026-03-02 16:44:03 -06:00
}
return < Navigate to = { ` / ${ targetCompany . issuePrefix } /dashboard ` } replace / > ;
}
function UnprefixedBoardRedirect() {
const location = useLocation ( ) ;
const { companies , selectedCompany , loading } = useCompany ( ) ;
if ( loading ) {
return < div className = "mx-auto max-w-xl py-10 text-sm text-muted-foreground" > Loading . . . < / div > ;
}
const targetCompany = selectedCompany ? ? companies [ 0 ] ? ? null ;
if ( ! targetCompany ) {
2026-03-03 11:25:56 -06:00
return < NoCompaniesStartPage / > ;
2026-03-02 16:44:03 -06:00
}
return (
< Navigate
to = { ` / ${ targetCompany . issuePrefix } ${ location . pathname } ${ location . search } ${ location . hash } ` }
replace
/ >
) ;
}
2026-03-03 11:45:54 -06:00
function NoCompaniesStartPage ( { autoOpen = true } : { autoOpen? : boolean } ) {
2026-03-03 11:25:56 -06:00
const { openOnboarding } = useDialog ( ) ;
const opened = useRef ( false ) ;
useEffect ( ( ) = > {
2026-03-03 11:45:54 -06:00
if ( ! autoOpen ) return ;
2026-03-03 11:25:56 -06:00
if ( opened . current ) return ;
opened . current = true ;
openOnboarding ( ) ;
2026-03-03 11:45:54 -06:00
} , [ autoOpen , openOnboarding ] ) ;
2026-03-03 11:25:56 -06:00
return (
< div className = "mx-auto max-w-xl py-10" >
< div className = "rounded-lg border border-border bg-card p-6" >
< h1 className = "text-xl font-semibold" > Create your first company < / h1 >
< p className = "mt-2 text-sm text-muted-foreground" >
Get started by creating a company .
< / p >
< div className = "mt-4" >
2026-03-03 11:37:19 -06:00
< Button onClick = { ( ) = > openOnboarding ( ) } > New Company < / Button >
2026-03-03 11:25:56 -06:00
< / div >
< / div >
< / div >
) ;
}
2026-02-16 13:32:04 -06:00
export function App() {
return (
2026-03-03 12:03:23 -06:00
< >
< Routes >
< Route path = "auth" element = { < AuthPage / > } / >
< Route path = "board-claim/:token" element = { < BoardClaimPage / > } / >
< Route path = "invite/:token" element = { < InviteLandingPage / > } / >
< Route element = { < CloudAccessGate / > } >
< Route index element = { < CompanyRootRedirect / > } / >
2026-03-11 11:40:38 -05:00
< Route path = "onboarding" element = { < OnboardingRoutePage / > } / >
2026-03-13 16:22:34 -05:00
< Route path = "instance" element = { < Navigate to = "/instance/settings/heartbeats" replace / > } / >
2026-03-12 08:03:55 -05:00
< Route path = "instance/settings" element = { < Layout / > } >
2026-03-13 16:22:34 -05:00
< Route index element = { < Navigate to = "heartbeats" replace / > } / >
< Route path = "heartbeats" element = { < InstanceSettings / > } / >
< Route path = "plugins" element = { < PluginManager / > } / >
< Route path = "plugins/:pluginId" element = { < PluginSettings / > } / >
2026-03-12 08:03:55 -05:00
< / Route >
2026-03-03 12:03:23 -06:00
< Route path = "companies" element = { < UnprefixedBoardRedirect / > } / >
< Route path = "issues" element = { < UnprefixedBoardRedirect / > } / >
< Route path = "issues/:issueId" element = { < UnprefixedBoardRedirect / > } / >
2026-03-14 13:52:20 -05:00
< Route path = "skills/*" element = { < UnprefixedBoardRedirect / > } / >
2026-03-12 08:03:55 -05:00
< Route path = "settings" element = { < LegacySettingsRedirect / > } / >
< Route path = "settings/*" element = { < LegacySettingsRedirect / > } / >
2026-03-03 12:03:23 -06:00
< Route path = "agents" element = { < UnprefixedBoardRedirect / > } / >
2026-03-07 08:26:49 -06:00
< Route path = "agents/new" element = { < UnprefixedBoardRedirect / > } / >
2026-03-03 12:03:23 -06:00
< Route path = "agents/:agentId" element = { < UnprefixedBoardRedirect / > } / >
< Route path = "agents/:agentId/:tab" element = { < UnprefixedBoardRedirect / > } / >
< Route path = "agents/:agentId/runs/:runId" element = { < UnprefixedBoardRedirect / > } / >
< Route path = "projects" element = { < UnprefixedBoardRedirect / > } / >
< Route path = "projects/:projectId" element = { < UnprefixedBoardRedirect / > } / >
< Route path = "projects/:projectId/overview" element = { < UnprefixedBoardRedirect / > } / >
< Route path = "projects/:projectId/issues" element = { < UnprefixedBoardRedirect / > } / >
< Route path = "projects/:projectId/issues/:filter" element = { < UnprefixedBoardRedirect / > } / >
2026-03-10 09:08:20 -05:00
< Route path = "projects/:projectId/configuration" element = { < UnprefixedBoardRedirect / > } / >
2026-03-11 10:47:22 -05:00
< Route path = "tests/ux/runs" element = { < UnprefixedBoardRedirect / > } / >
2026-03-03 12:03:23 -06:00
< Route path = ":companyPrefix" element = { < Layout / > } >
{ boardRoutes ( ) }
< / Route >
2026-03-10 16:38:46 -05:00
< Route path = "*" element = { < NotFoundPage scope = "global" / > } / >
2026-03-02 10:31:54 -06:00
< / Route >
2026-03-03 12:03:23 -06:00
< / Routes >
< OnboardingWizard / >
< / >
2026-02-16 13:32:04 -06:00
) ;
}