68 lines
3.0 KiB
TypeScript
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>
|
|
);
|
|
}
|