admin: completeness + UI consistency pass

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>
This commit is contained in:
jules
2026-05-04 15:37:31 +10:00
parent 444516e900
commit 20c592dfa7
44 changed files with 1594 additions and 984 deletions

View File

@@ -1,5 +1,4 @@
import { useCallback, useEffect, useMemo, useState } from "react"
import { Link } from "react-router"
import { Pause, Play, Plus, RefreshCw } from "lucide-react"
import { ArcadiaError, useArcadiaClient } from "@crema/arcadia-client"
@@ -18,6 +17,7 @@ import { SearchInput } from "@crema/search-ui"
import { AlertBanner, ConfirmDialog, EmptyState, LoadingOverlay } from "@crema/feedback-ui"
import { AppShell } from "~/components/layout/app-shell"
import { PageHeader } from "~/components/layout/page-header"
import { Button } from "~/components/ui/button"
import {
Card,
@@ -174,39 +174,13 @@ export default function TenantsRoute() {
table.setSearch(search)
}, [search, table])
if (!session) {
return (
<AppShell title="Tenants">
<div className="p-8">
<Card className="max-w-md">
<CardHeader>
<CardTitle>Sign in required</CardTitle>
<CardDescription>
Tenant administration requires an admin session.
</CardDescription>
</CardHeader>
<CardContent>
<Button asChild>
<Link to="/login?next=/tenants">Sign in</Link>
</Button>
</CardContent>
</Card>
</div>
</AppShell>
)
}
return (
<AppShell title="Tenants">
<div className="flex flex-col gap-4 p-6">
<header className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<div>
<h1 className="text-2xl font-semibold tracking-tight">Tenants</h1>
<p className="text-sm text-muted-foreground">
Multi-tenant workspaces on this arcadia deployment.
</p>
</div>
<div className="flex items-center gap-2">
<AppShell>
<PageHeader
title="Tenants"
description="Multi-tenant workspaces on this arcadia deployment."
actions={
<>
<Button
variant="outline"
size="sm"
@@ -221,8 +195,9 @@ export default function TenantsRoute() {
<Plus className="size-4" />
New tenant
</Button>
</div>
</header>
</>
}
/>
{error ? (
<AlertBanner variant="error" dismissible onDismiss={() => setError(null)}>
@@ -276,7 +251,6 @@ export default function TenantsRoute() {
)}
</CardContent>
</Card>
</div>
<ConfirmDialog
open={pending?.kind === "suspend"}