162 lines
6.1 KiB
TypeScript
162 lines
6.1 KiB
TypeScript
import { Link } from '@inertiajs/react';
|
|
import { ArrowUpRight, ClipboardList } from 'lucide-react';
|
|
import type { ReactNode } from 'react';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { date, money, statusLabels } from '@/types/procurement';
|
|
import type { Requisition } from '@/types/procurement';
|
|
|
|
export const controlClass =
|
|
'h-10 w-full rounded-lg border border-input bg-background px-3 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring';
|
|
export function PageTitle({
|
|
eyebrow,
|
|
title,
|
|
description,
|
|
action,
|
|
}: {
|
|
eyebrow: string;
|
|
title: string;
|
|
description: string;
|
|
action?: ReactNode;
|
|
}) {
|
|
return (
|
|
<div className="flex flex-wrap items-end justify-between gap-4">
|
|
<div>
|
|
<p className="mb-2 text-xs font-semibold tracking-widest text-teal-700 uppercase dark:text-teal-400">
|
|
{eyebrow}
|
|
</p>
|
|
<h1 className="text-3xl font-semibold tracking-tight">
|
|
{title}
|
|
</h1>
|
|
<p className="mt-2 max-w-2xl text-sm text-muted-foreground">
|
|
{description}
|
|
</p>
|
|
</div>
|
|
{action}
|
|
</div>
|
|
);
|
|
}
|
|
export function Panel({
|
|
title,
|
|
description,
|
|
children,
|
|
}: {
|
|
title: string;
|
|
description?: string;
|
|
children: ReactNode;
|
|
}) {
|
|
return (
|
|
<section className="rounded-xl border bg-card p-5 shadow-xs">
|
|
<h2 className="font-semibold">{title}</h2>
|
|
{description && (
|
|
<p className="mt-1 text-sm text-muted-foreground">
|
|
{description}
|
|
</p>
|
|
)}
|
|
<div className="mt-5">{children}</div>
|
|
</section>
|
|
);
|
|
}
|
|
export function Status({ value }: { value: string }) {
|
|
return (
|
|
<Badge
|
|
variant="outline"
|
|
className={
|
|
value === 'approved'
|
|
? 'border-teal-200 bg-teal-50 text-teal-800 dark:bg-teal-950 dark:text-teal-200'
|
|
: value === 'pending'
|
|
? 'border-amber-200 bg-amber-50 text-amber-800 dark:bg-amber-950 dark:text-amber-200'
|
|
: value === 'rejected'
|
|
? 'border-rose-200 bg-rose-50 text-rose-800 dark:bg-rose-950 dark:text-rose-200'
|
|
: ''
|
|
}
|
|
>
|
|
{statusLabels[value] ?? value}
|
|
</Badge>
|
|
);
|
|
}
|
|
export function RequisitionTable({ rows }: { rows: Requisition[] }) {
|
|
if (!rows.length)
|
|
return (
|
|
<div className="py-12 text-center">
|
|
<ClipboardList className="mx-auto mb-3 size-9 text-muted-foreground/50" />
|
|
<p className="font-medium">Sem requisições para apresentar</p>
|
|
<p className="mt-1 text-sm text-muted-foreground">
|
|
Os pedidos de compra aparecem aqui depois de criados.
|
|
</p>
|
|
</div>
|
|
);
|
|
return (
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full text-left text-sm">
|
|
<thead className="border-b text-xs text-muted-foreground">
|
|
<tr>
|
|
<th className="pb-3 font-medium">Requisição</th>
|
|
<th className="pb-3 font-medium">Departamento</th>
|
|
<th className="pb-3 font-medium">Data</th>
|
|
<th className="pb-3 font-medium">Estado</th>
|
|
<th className="pb-3 text-right font-medium">
|
|
Total com IVA
|
|
</th>
|
|
<th>
|
|
<span className="sr-only">Abrir</span>
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{rows.map((r) => (
|
|
<tr key={r.id} className="border-b last:border-0">
|
|
<td className="py-4 pr-4">
|
|
<Link
|
|
className="font-semibold hover:text-teal-700"
|
|
href={`/requisicoes/${r.id}`}
|
|
>
|
|
{r.number}
|
|
</Link>
|
|
<div className="mt-1 text-xs text-muted-foreground">
|
|
{r.requester?.name}
|
|
</div>
|
|
</td>
|
|
<td className="pr-4">
|
|
{r.department?.name ?? '—'}
|
|
</td>
|
|
<td className="pr-4 whitespace-nowrap">
|
|
{date(r.created_at)}
|
|
</td>
|
|
<td className="pr-4">
|
|
<Status value={r.status} />
|
|
</td>
|
|
<td className="text-right font-medium whitespace-nowrap">
|
|
{money(Number(r.total_minor))}
|
|
</td>
|
|
<td className="pl-3">
|
|
<Link
|
|
href={`/requisicoes/${r.id}`}
|
|
aria-label={`Abrir ${r.number}`}
|
|
>
|
|
<ArrowUpRight className="size-4" />
|
|
</Link>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
);
|
|
}
|
|
export function FormErrors({ errors }: { errors: Record<string, string> }) {
|
|
if (!Object.keys(errors).length) return null;
|
|
return (
|
|
<div
|
|
role="alert"
|
|
className="rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-sm text-destructive"
|
|
>
|
|
<p className="mb-1 font-medium">Verifique os dados:</p>
|
|
<ul className="list-inside list-disc">
|
|
{Object.entries(errors).map(([key, value]) => (
|
|
<li key={key}>{value}</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
);
|
|
}
|