A Vite + React 18 + TypeScript SPA, same-origin with the API (no CORS, no Go server changes). Full management parity with the urapt CLI: - First-run setup (admin bootstrap via needs_setup), login/logout - Repositories: list, create, tabbed detail (overview edit/delete, members CRUD, distributions/components/architectures manager, package list + .deb upload + detail + delete, apt-config panel) - API tokens: show-once creation + revoke - Admin users: toggle admin / delete - Settings + dark mode Stack: Tailwind + shadcn/ui (New York), React Router v7 (lazy routes), TanStack Query v5, Zustand, Vitest. Hand-written typed API client mirroring shared/api + shared/models. Dev proxy forwards /api + /apt to a local urapt-server; deploy behind nginx/Caddy (see urapt-web/README).
37 lines
863 B
TypeScript
37 lines
863 B
TypeScript
import type { ReactNode } from "react";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface EmptyStateProps {
|
|
icon?: ReactNode;
|
|
title: string;
|
|
description?: string;
|
|
action?: ReactNode;
|
|
className?: string;
|
|
}
|
|
|
|
export function EmptyState({
|
|
icon,
|
|
title,
|
|
description,
|
|
action,
|
|
className,
|
|
}: EmptyStateProps) {
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"flex flex-col items-center justify-center gap-3 rounded-lg border border-dashed p-10 text-center",
|
|
className,
|
|
)}
|
|
>
|
|
{icon ? <div className="text-muted-foreground">{icon}</div> : null}
|
|
<div className="space-y-1">
|
|
<p className="font-medium">{title}</p>
|
|
{description ? (
|
|
<p className="text-sm text-muted-foreground">{description}</p>
|
|
) : null}
|
|
</div>
|
|
{action ? <div className="mt-1">{action}</div> : null}
|
|
</div>
|
|
);
|
|
}
|