feat(dashboard): GuruConnect v2 operator console (pass 1)
All checks were successful
All checks were successful
React + Vite + TypeScript SPA: scaffold, operations-terminal design system, Bearer-token auth, and the Machines view. - Design system: OKLCH-tinted dark theme (ink-slate + signal-cyan), Hanken Grotesk + JetBrains Mono, status-color language (online/offline/granted/pending/denied/not_required), motion with prefers-reduced-motion honored. - Auth: token in sessionStorage via ref (never React state), protected routes, 401 session teardown, admin-gated per-agent-key UI. - Machines view: data table (sticky header, keyboard-activated rows, skeleton loading, actionable empty/error states), non-blocking detail drawer, delete confirm, admin key management with copy-once reveal. - UI primitives: Modal (focus trap + inert + portal + dialogStack), Drawer, Table, Badge/StatusDot, toast, states. - Typed API client normalizing the two error-envelope shapes. Passed Code Review (no blockers), impeccable critique-and-polish, and local gates (tsc/lint/build green). Dev-only Vite proxy to :3002. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
72
dashboard/src/features/machines/KeyRevealModal.tsx
Normal file
72
dashboard/src/features/machines/KeyRevealModal.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import { Button } from "../../components/ui/Button";
|
||||
import { Modal } from "../../components/ui/Modal";
|
||||
import { CopyIcon } from "../../components/layout/icons";
|
||||
import { useClipboard } from "../../lib/useClipboard";
|
||||
|
||||
interface KeyRevealModalProps {
|
||||
/** The plaintext `cak_...` key, or null when closed. */
|
||||
plaintextKey: string | null;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy-once key reveal. The server returns the plaintext key exactly once on
|
||||
* creation; this is the only place it is ever shown. The user is warned and
|
||||
* given a copy button. Closing dismisses it for good.
|
||||
*/
|
||||
export function KeyRevealModal({ plaintextKey, onClose }: KeyRevealModalProps) {
|
||||
const { copied, copy } = useClipboard();
|
||||
const open = plaintextKey != null;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
title="Agent key created"
|
||||
onClose={onClose}
|
||||
footer={<Button variant="primary" onClick={onClose}>Done</Button>}
|
||||
>
|
||||
<div className="keyreveal__warn" role="alert">
|
||||
<svg
|
||||
className="keyreveal__warnicon"
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.8"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M10.3 3.7 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.7a2 2 0 0 0-3.4 0Z" />
|
||||
<path d="M12 9v4M12 17h.01" />
|
||||
</svg>
|
||||
<div>
|
||||
<strong>Copy this key now. You will not see it again.</strong>
|
||||
<span>
|
||||
The key is shown only at creation and cannot be recovered. If you
|
||||
lose it, revoke it and create a new one.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="keyreveal__value">
|
||||
<code className="keyreveal__key">{plaintextKey}</code>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => plaintextKey && void copy(plaintextKey)}
|
||||
aria-label={copied ? "Key copied to clipboard" : "Copy key to clipboard"}
|
||||
>
|
||||
<CopyIcon width={14} height={14} />
|
||||
{copied ? "Copied" : "Copy"}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<p className="keyreveal__hint">
|
||||
Use this key to enroll the agent as a persistent, individually revocable
|
||||
identity.
|
||||
</p>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user