Files
Estacion-de-Documentos/frontend/components/ui.tsx
T
2026-05-17 10:14:14 -06:00

68 lines
3.0 KiB
TypeScript

export function Card({ children, className = "" }: { children: React.ReactNode; className?: string }) {
return <div className={`rounded-2xl border border-white/10 bg-white/[0.04] p-5 shadow-xl shadow-black/20 ${className}`}>{children}</div>;
}
export function Button({ children, className = "", ...props }: React.ButtonHTMLAttributes<HTMLButtonElement>) {
return <button className={`rounded-xl bg-blue-600 px-4 py-2 font-medium text-white hover:bg-blue-500 disabled:cursor-not-allowed disabled:opacity-50 ${className}`} {...props}>{children}</button>;
}
export function Input(props: React.InputHTMLAttributes<HTMLInputElement>) {
return <input className="w-full rounded-xl border border-white/10 bg-slate-900 px-4 py-2 text-slate-100 outline-none ring-blue-500/30 focus:ring-4" {...props} />;
}
export function Label({ children }: { children: React.ReactNode }) {
return <label className="text-sm font-medium text-slate-300">{children}</label>;
}
export function StatusBadge({ status }: { status: string }) {
const map: Record<string, string> = {
completed: "bg-emerald-500/15 text-emerald-300",
processing: "bg-blue-500/15 text-blue-300",
queued: "bg-amber-500/15 text-amber-300",
failed: "bg-red-500/15 text-red-300",
stored: "bg-slate-500/15 text-slate-300",
processed: "bg-emerald-500/15 text-emerald-300",
};
return <span className={`rounded-full px-2.5 py-1 text-xs font-medium ${map[status] || "bg-slate-500/15 text-slate-300"}`}>{status}</span>;
}
export function ConfirmDialog({
open,
title,
description,
confirmLabel = "Confirmar",
cancelLabel = "Cancelar",
loading = false,
onConfirm,
onCancel,
}: {
open: boolean;
title: string;
description: string;
confirmLabel?: string;
cancelLabel?: string;
loading?: boolean;
onConfirm: () => void;
onCancel: () => void;
}) {
if (!open) return null;
return (
<div className="fixed inset-0 z-50 grid place-items-center bg-slate-950/75 p-4 backdrop-blur-sm" role="dialog" aria-modal="true" aria-labelledby="confirm-dialog-title">
<div className="w-full max-w-md rounded-2xl border border-white/10 bg-slate-900 p-5 shadow-2xl shadow-black/40">
<div className="flex items-start gap-3">
<div className="grid h-10 w-10 shrink-0 place-items-center rounded-full bg-red-500/15 text-red-200">!</div>
<div>
<h2 id="confirm-dialog-title" className="text-lg font-semibold text-white">{title}</h2>
<p className="mt-2 text-sm leading-6 text-slate-300">{description}</p>
</div>
</div>
<div className="mt-6 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
<button className="rounded-xl border border-white/10 px-4 py-2 text-slate-200 hover:bg-white/10 disabled:cursor-not-allowed disabled:opacity-50" type="button" disabled={loading} onClick={onCancel}>{cancelLabel}</button>
<Button className="bg-red-600 hover:bg-red-500" disabled={loading} onClick={onConfirm}>{loading ? "Eliminando..." : confirmLabel}</Button>
</div>
</div>
</div>
);
}