Arcadia wiring: - home: real Overview dashboard (tenants/users/audit/health probe) replacing the inherited Vibespace welcome tiles; skeleton loaders, refresh button, registers admin context - profile: split into Account (synced via getUser/updateUser of session user) and local Preferences; updateSessionUser keeps the appbar in sync after edits - session: drop unused signIn mock, add updateSessionUser, refresh tests - profile schema: drop redundant Profile.name/email (session is the source of truth) - routes: delete orphaned resources route + lib Auth flows that previously 404'd: - /signup, /login/forgot, /login/reset, /login/2fa wired via @crema/arcadia-auth-ui - shared AuthShell + AuthBrand wrapper Assistant tools (admin-tools.ts): - +10 tools: deactivate_tenant, set_user_status, delete_user, list_memberships, list_roles, revoke_api_key, create_user, update_user, assign_role, remove_role - list_memberships gains user_id filter for "tenants this user belongs to" queries - search_kb / read_chunk: new token resolution (window override → VITE_ARCADIA_SEARCH_TOKEN service token → operator session JWT → "dev"); on 401/403 emit a tailored hint based on which token was used UI consistency: - new PageHeader component - AppShell.title was unrendered — dropped; first-child padding on #main-content keeps the floating actions pill from colliding with header content - removed dead "Sign in required" fallback cards from 14 routes (AppShell already redirects) - stripped p-6 from outer wrappers across 14 routes (was double-padding under AppShell's own p-6) - migrated home + tenants to PageHeader arcadia-search ergonomics: - scripts/mint-search-token.mjs + `npm run mint:search-token` mints HS512 JWT with required tenant_id claim, upserts VITE_ARCADIA_SEARCH_TOKEN into .env.local - README/.env document the new VITE_ARCADIA_SEARCH_URL / VITE_ARCADIA_SEARCH_TOKEN knobs - .env.local now gitignored Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
101 lines
3.3 KiB
TypeScript
101 lines
3.3 KiB
TypeScript
import {
|
|
Links,
|
|
Meta,
|
|
Outlet,
|
|
Scripts,
|
|
ScrollRestoration,
|
|
isRouteErrorResponse,
|
|
} from "react-router"
|
|
|
|
import type { Route } from "./+types/root"
|
|
import "./app.css"
|
|
|
|
import { ToastProvider, Toaster } from "@crema/notification-ui"
|
|
import { CommandBusProvider } from "@crema/action-bus"
|
|
import { ArcadiaProvider } from "@crema/arcadia-client"
|
|
// CREMA:PROVIDERS-IMPORTS
|
|
|
|
const ARCADIA_URL = import.meta.env.VITE_ARCADIA_URL ?? "http://localhost:4000"
|
|
const ARCADIA_TENANT = import.meta.env.VITE_ARCADIA_TENANT ?? "default"
|
|
|
|
export function Layout({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<html lang="en" suppressHydrationWarning>
|
|
<head>
|
|
<meta charSet="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<Meta />
|
|
<Links />
|
|
<script
|
|
dangerouslySetInnerHTML={{
|
|
__html: `(function(){try{var t=localStorage.getItem('crema-theme');if(!t)t='dark';if(t==='dark')document.documentElement.classList.add('dark');var f=localStorage.getItem('crema-font-scale');if(!f||!/^(sm|md|lg|xl)$/.test(f))f='sm';document.documentElement.dataset.fontScale=f;var b=localStorage.getItem('crema-bg');if(b&&/^(drift|static)$/.test(b)){document.addEventListener('DOMContentLoaded',function(){document.body.dataset.bg=b;});}var s=localStorage.getItem('crema-surface');if(s&&/^(snow|stone|sage|slate)$/.test(s)){document.addEventListener('DOMContentLoaded',function(){document.body.dataset.surface=s;});}}catch(e){}})();`,
|
|
}}
|
|
/>
|
|
</head>
|
|
<body suppressHydrationWarning>
|
|
<div data-slot="aurora-field" aria-hidden="true">
|
|
<div className="aurora-blob aurora-blob-1" />
|
|
<div className="aurora-blob aurora-blob-2" />
|
|
</div>
|
|
{children}
|
|
<ScrollRestoration />
|
|
<Scripts />
|
|
</body>
|
|
</html>
|
|
)
|
|
}
|
|
|
|
export default function App() {
|
|
return (
|
|
/* CREMA:PROVIDERS-WRAP-OPEN */
|
|
<ToastProvider>
|
|
<ArcadiaProvider
|
|
baseUrl={ARCADIA_URL}
|
|
initialTenantId={ARCADIA_TENANT}
|
|
getToken={() => (typeof window === "undefined" ? null : sessionStorage.getItem("arcadia_access_token"))}
|
|
onUnauthorized={() => {
|
|
if (typeof window !== "undefined") {
|
|
sessionStorage.removeItem("arcadia_access_token")
|
|
sessionStorage.removeItem("arcadia_refresh_token")
|
|
}
|
|
}}
|
|
>
|
|
<CommandBusProvider>
|
|
<Outlet />
|
|
<Toaster />
|
|
</CommandBusProvider>
|
|
</ArcadiaProvider>
|
|
</ToastProvider>
|
|
/* CREMA:PROVIDERS-WRAP-CLOSE */
|
|
)
|
|
}
|
|
|
|
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
|
|
let message = "Oops!"
|
|
let details = "An unexpected error occurred."
|
|
let stack: string | undefined
|
|
|
|
if (isRouteErrorResponse(error)) {
|
|
message = error.status === 404 ? "404" : "Error"
|
|
details =
|
|
error.status === 404
|
|
? "The requested page could not be found."
|
|
: error.statusText || details
|
|
} else if (import.meta.env.DEV && error && error instanceof Error) {
|
|
details = error.message
|
|
stack = error.stack
|
|
}
|
|
|
|
return (
|
|
<main className="container mx-auto p-4 pt-16">
|
|
<h1>{message}</h1>
|
|
<p>{details}</p>
|
|
{stack && (
|
|
<pre className="w-full overflow-x-auto p-4">
|
|
<code>{stack}</code>
|
|
</pre>
|
|
)}
|
|
</main>
|
|
)
|
|
}
|