Files
server-26/drb-frontend/components/ui/EmptyState.tsx
Logan Cusano 3a786bc227
Build & Deploy / Build & push images (push) Successful in 5m11s
Build & Deploy / Deploy to VM (push) Failing after 3m33s
Frontend redesign chunk 2: primitives and marks
Rewrite components/ui/* (Button, Card, Badge, PageHeader, EmptyState,
Skeleton) against the chunk-1 tokens instead of hardcoded gray-9xx classes,
and drop the remaining font-mono from label/heading text.

Add the three colour-blindness-validated encoding components from
UI_REDESIGN.md §2.3:
- components/marks/SeverityMark.tsx — glyph (filled triangle / outline
  triangle / outline circle) + optional spine + optional label, from the
  four-level severity ladder. Colour is never the only channel.
- components/marks/TypeGlyph.tsx — five stroked SVG glyphs (fire, police,
  ems, collision, other) in currentColor. Incident type is now shape, not
  hue, since five hues can't clear an all-pairs CVD gate.
- components/marks/NodeMark.tsx — diamond at four weights (filled+ring /
  filled / hollow / hollow-dashed). Green is gone from node state entirely.

lib/severity.tsx now renders through SeverityMark; SEVERITY_COLORS reads
the validated sev-moderate/sev-major tokens with routine/minor neutral.
IncidentBadges.tsx's TypeBadge is reimplemented on TypeGlyph instead of a
coloured pill.

Per UI_REDESIGN.md chunk 2.
2026-08-19 22:56:02 -04:00

30 lines
1.1 KiB
TypeScript

import type { ReactNode } from "react";
interface EmptyStateProps {
icon?: ReactNode;
title: string;
description?: string;
action?: ReactNode;
}
/** Consistent "nothing here yet" panel — replaces the ad-hoc `<p className="text-gray-600">` scattered across pages. */
export function EmptyState({ icon, title, description, action }: EmptyStateProps) {
return (
<div className="flex flex-col items-center justify-center text-center py-12 px-6 border border-dashed border-line-strong rounded-xl">
{icon && <div className="text-ink-muted mb-3">{icon}</div>}
<p className="text-ink text-sm font-semibold">{title}</p>
{description && <p className="text-ink-muted text-xs mt-1 max-w-sm">{description}</p>}
{action && <div className="mt-4">{action}</div>}
</div>
);
}
/** Inline error banner — for API/Firestore errors surfaced within a page section. */
export function ErrorBanner({ message }: { message: string }) {
return (
<div className="bg-sev-major/10 border border-sev-major/40 rounded-lg p-4">
<p className="text-sev-major text-sm">{message}</p>
</div>
);
}