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:
113
dashboard/src/components/layout/icons.tsx
Normal file
113
dashboard/src/components/layout/icons.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
// Inline stroke icons (no icon-library dependency). 18px on a 24 viewBox.
|
||||
import type { SVGProps } from "react";
|
||||
|
||||
type IconProps = SVGProps<SVGSVGElement>;
|
||||
|
||||
function base(props: IconProps) {
|
||||
return {
|
||||
width: 18,
|
||||
height: 18,
|
||||
viewBox: "0 0 24 24",
|
||||
fill: "none",
|
||||
stroke: "currentColor",
|
||||
strokeWidth: 1.8,
|
||||
strokeLinecap: "round" as const,
|
||||
strokeLinejoin: "round" as const,
|
||||
...props,
|
||||
};
|
||||
}
|
||||
|
||||
export function MachinesIcon(props: IconProps) {
|
||||
return (
|
||||
<svg {...base(props)}>
|
||||
<rect x="2" y="4" width="20" height="13" rx="2" />
|
||||
<path d="M8 21h8M12 17v4" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function SessionsIcon(props: IconProps) {
|
||||
return (
|
||||
<svg {...base(props)}>
|
||||
<path d="M4 6h16M4 12h16M4 18h10" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function CodesIcon(props: IconProps) {
|
||||
return (
|
||||
<svg {...base(props)}>
|
||||
<path d="M8 6 3 12l5 6M16 6l5 6-5 6M13 4l-2 16" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function UsersIcon(props: IconProps) {
|
||||
return (
|
||||
<svg {...base(props)}>
|
||||
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
|
||||
<circle cx="9" cy="7" r="4" />
|
||||
<path d="M22 21v-2a4 4 0 0 0-3-3.87M16 3.13A4 4 0 0 1 16 11" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function LogoutIcon(props: IconProps) {
|
||||
return (
|
||||
<svg {...base(props)}>
|
||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function KeyIcon(props: IconProps) {
|
||||
return (
|
||||
<svg {...base(props)}>
|
||||
<circle cx="7.5" cy="15.5" r="4.5" />
|
||||
<path d="m10.7 12.3 8.3-8.3M16 6l3 3M14 8l2 2" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function TrashIcon(props: IconProps) {
|
||||
return (
|
||||
<svg {...base(props)}>
|
||||
<path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function InfoIcon(props: IconProps) {
|
||||
return (
|
||||
<svg {...base(props)}>
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<path d="M12 16v-4M12 8h.01" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function SearchIcon(props: IconProps) {
|
||||
return (
|
||||
<svg {...base(props)}>
|
||||
<circle cx="11" cy="11" r="7" />
|
||||
<path d="m21 21-4.3-4.3" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function RefreshIcon(props: IconProps) {
|
||||
return (
|
||||
<svg {...base(props)}>
|
||||
<path d="M21 12a9 9 0 1 1-3-6.7L21 8M21 3v5h-5" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function CopyIcon(props: IconProps) {
|
||||
return (
|
||||
<svg {...base(props)}>
|
||||
<rect x="9" y="9" width="12" height="12" rx="2" />
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user