- 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>
50 lines
1.6 KiB
TypeScript
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>
|
|
);
|
|
}
|