learning_ai_common_plat/dashboards/admin-web/src/app/(dashboard)/layout.tsx
saravanakumardb1 b4e450d68a feat(admin-web): add @bytelyst/command-palette (Cmd-K) to dashboard (UX-3)
- Mount CommandRegistryProvider in (dashboard)/layout.tsx and a CommandMenu
  that binds the global Cmd-K / Ctrl-K hotkey (useCommandPalette) and lazy-loads
  the dialog via next/dynamic (own chunk; dynamic target is a local re-export
  command-palette-dialog.tsx because the package declares only an `import`
  export condition).
- src/lib/admin-commands.ts: pure builder for 21 navigate-mode commands across
  the major surfaces (Users, Subscriptions, Licenses, Billing, Usage,
  Broadcasts, Flags, Experiments, Audit, Ops, …) plus theme-toggle and sign-out
  actions wired to the existing auth/theme contexts; onNavigate -> router.push.
- @bytelyst/command-palette added as workspace:* (importer-only lockfile change;
  --frozen-lockfile clean).
- vitest.config: inline command-palette + dedupe react for the interaction test.

Tests: pure command-set assertions + a happy-dom Cmd-K/Ctrl-K interaction test
(react-dom/client + act, no new deps).

Verify: typecheck+lint+build green (123 routes); vitest 19 files / 165 tests
(+6); format:check no new failures; e2e 11 passed / 80 failed (unchanged vs
UX-1 baseline — environmental, no backend).

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-29 13:58:49 -07:00

50 lines
1.6 KiB
TypeScript

'use client';
import { CommandRegistryProvider } from '@bytelyst/command-palette';
import { SidebarNav } from '@/components/sidebar-nav';
import { AuthGuard } from '@/components/auth-guard';
import { CommandMenu } from '@/components/command-menu';
import { ErrorBoundary } from '@/components/error-boundary';
import { useStripeConfig } from '@/lib/stripe-context';
import { FlaskConical, ShieldCheck } from 'lucide-react';
function StripeModeBanner() {
const { mode, isLive } = useStripeConfig();
if (mode === null) return null;
if (isLive) {
return (
<div className="bg-emerald-600 text-white text-xs font-semibold text-center py-1.5 px-4 flex items-center justify-center gap-2">
<ShieldCheck className="h-3.5 w-3.5" />
STRIPE LIVE MODE Real payments active
</div>
);
}
return (
<div className="bg-amber-400 text-amber-950 text-xs font-semibold text-center py-1.5 px-4 flex items-center justify-center gap-2">
<FlaskConical className="h-3.5 w-3.5" />
{mode === 'test'
? 'STRIPE TEST MODE — No real charges, use test cards'
: 'DEV MODE — Stripe not configured, payments disabled'}
</div>
);
}
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<AuthGuard>
<CommandRegistryProvider>
<SidebarNav />
<main className="ml-64 min-h-screen bg-background max-md:ml-0">
<StripeModeBanner />
<div className="p-8 max-md:p-4">
<ErrorBoundary>{children}</ErrorBoundary>
</div>
</main>
<CommandMenu />
</CommandRegistryProvider>
</AuthGuard>
);
}