Expand production order status inline

This commit is contained in:
Cauê Faleiros
2026-08-11 14:47:39 -03:00
parent 8d5fef6d2a
commit f9456924c0

View File

@@ -196,7 +196,6 @@ const ProductionOrders = () => {
const finishedCount = summary.counts.finished || 0;
const totalQuantity = summary.orders.reduce((total, order) => total + order.quantity, 0);
const selectedOrder = summary.orders.find(order => order.id === selectedOrderId) || null;
const statusEditorOrder = summary.orders.find(order => order.id === statusEditorOrderId) || null;
const handleManualRefresh = () => {
void loadProductionOrders({ force: true });
@@ -282,11 +281,13 @@ const ProductionOrders = () => {
<div className="grid grid-cols-2 gap-3 md:grid-cols-4">
<div className="rounded-xl border border-dark-border bg-dark-card p-3"><p className="text-[10px] font-bold uppercase tracking-wider text-dark-muted">Quantidade</p><p className="mt-1 text-lg font-bold text-dark-text">{formatQuantity(order.quantity)} <span className="text-xs text-dark-muted">{order.unit}</span></p></div>
<div className="rounded-xl border border-dark-border bg-dark-card p-3"><p className="text-[10px] font-bold uppercase tracking-wider text-dark-muted">Status</p><button type="button" onClick={() => setStatusEditorOrderId(order.id)} className={`mt-2 inline-flex items-center gap-2 rounded-lg border px-2.5 py-1 text-xs font-bold ${getStatusStyle(order.status, order.statusLabel).className}`}><span className={`h-2 w-2 rounded-full ${getStatusStyle(order.status, order.statusLabel).dotClass}`} />{getStatusStyle(order.status, order.statusLabel).label}</button></div>
<div className="rounded-xl border border-dark-border bg-dark-card p-3"><p className="text-[10px] font-bold uppercase tracking-wider text-dark-muted">Status</p><button type="button" onClick={() => setStatusEditorOrderId(current => current === order.id ? null : order.id)} aria-expanded={statusEditorOrderId === order.id} className={`mt-2 inline-flex cursor-pointer items-center gap-2 rounded-lg border px-2.5 py-1 text-xs font-bold ${getStatusStyle(order.status, order.statusLabel).className}`}><span className={`h-2 w-2 rounded-full ${getStatusStyle(order.status, order.statusLabel).dotClass}`} />{getStatusStyle(order.status, order.statusLabel).label}{statusEditorOrderId === order.id ? <ChevronUp className="h-3.5 w-3.5" /> : <ChevronDown className="h-3.5 w-3.5" />}</button></div>
<div className="rounded-xl border border-dark-border bg-dark-card p-3"><p className="text-[10px] font-bold uppercase tracking-wider text-dark-muted">Emissão</p><p className="mt-1 text-sm font-bold text-dark-text">{formatDate(order.issueDate)}</p></div>
<div className="rounded-xl border border-dark-border bg-dark-card p-3"><p className="text-[10px] font-bold uppercase tracking-wider text-dark-muted">Previsão</p><p className="mt-1 text-sm font-bold text-dark-text">{formatDate(order.expectedDate)}</p></div>
</div>
{statusEditorOrderId === order.id && <section className="rounded-xl border border-dark-border bg-dark-card p-4"><p className="text-xs font-bold uppercase tracking-widest text-dark-muted">Alterar status</p><div className="mt-3 grid grid-cols-1 gap-2 sm:grid-cols-2">{editableStatusOptions.map(option => { const style = getStatusStyle(option.value, option.label); const isCurrent = order.status === option.value; const Icon = option.value === 'finished' ? CheckCircle2 : option.value === 'canceled' ? XCircle : option.value === 'in_progress' ? PlayCircle : Clock3; return <button key={option.value} type="button" disabled={isCurrent || busyStatusOrderId === order.id} onClick={() => void handleStatusChange(order, option.value)} className={`flex cursor-pointer items-center gap-3 rounded-xl border p-3 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-60 ${isCurrent ? style.className : 'border-dark-border bg-dark-input/45 hover:border-brand-primary/60'}`}><span className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border ${isCurrent ? style.className : 'border-dark-border bg-dark-card text-dark-muted'}`}><Icon className="h-4 w-4" /></span><span><span className="block text-xs font-bold text-dark-text">{option.label}{isCurrent && ' · Atual'}</span><span className="mt-0.5 block text-[10px] font-semibold text-dark-muted">{option.description}</span></span></button>; })}</div></section>}
<section>
<h3 className="text-xs font-bold uppercase tracking-widest text-dark-muted">Produto e marcadores</h3>
<div className="mt-3 rounded-xl border border-dark-border bg-dark-card p-4">
@@ -517,11 +518,16 @@ const ProductionOrders = () => {
<button
type="button"
disabled={busyStatusOrderId === order.id}
onClick={() => setStatusEditorOrderId(order.id)}
className={`inline-flex min-h-9 items-center gap-2 rounded-lg border px-3 text-xs font-bold transition-colors hover:brightness-125 disabled:cursor-not-allowed disabled:opacity-50 ${statusStyle.className}`}
onClick={() => {
setSelectedOrderId(order.id);
setStatusEditorOrderId(current => current === order.id ? null : order.id);
}}
aria-expanded={statusEditorOrderId === order.id}
className={`inline-flex min-h-9 cursor-pointer items-center gap-2 rounded-lg border px-3 text-xs font-bold transition-colors hover:brightness-125 disabled:cursor-not-allowed disabled:opacity-50 ${statusStyle.className}`}
>
<span className={`h-2 w-2 rounded-full ${statusStyle.dotClass}`} />
{statusStyle.label}
{statusEditorOrderId === order.id ? <ChevronUp className="h-3.5 w-3.5" /> : <ChevronDown className="h-3.5 w-3.5" />}
</button>
</td>
<td className="px-6 py-3 text-right">
@@ -583,16 +589,6 @@ const ProductionOrders = () => {
</div>
)}
{statusEditorOrder && (
<div className="fixed inset-0 z-[60] flex items-center justify-center bg-black/70 p-4 backdrop-blur-sm" role="dialog" aria-modal="true" aria-labelledby="production-status-title">
<button type="button" aria-label="Fechar alteração de status" onClick={() => setStatusEditorOrderId(null)} className="absolute inset-0 cursor-default" />
<div className="relative w-full max-w-lg rounded-2xl border border-dark-border bg-dark-card shadow-2xl">
<div className="flex items-start justify-between gap-4 border-b border-dark-border px-5 py-4"><div><h2 id="production-status-title" className="text-lg font-bold text-dark-text">Status da OP</h2><p className="mt-1 text-sm font-semibold text-dark-muted">{statusEditorOrder.number || `OP #${statusEditorOrder.id}`} · {statusEditorOrder.productDescription}</p></div><button type="button" onClick={() => setStatusEditorOrderId(null)} aria-label="Fechar alteração de status" className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border border-dark-border bg-dark-input text-dark-muted transition-colors hover:border-brand-primary hover:text-brand-primary"><X className="h-4 w-4" /></button></div>
<div className="space-y-2 p-5">{editableStatusOptions.map(option => { const style = getStatusStyle(option.value, option.label); const isCurrent = statusEditorOrder.status === option.value; const Icon = option.value === 'finished' ? CheckCircle2 : option.value === 'canceled' ? XCircle : option.value === 'in_progress' ? PlayCircle : Clock3; return <button key={option.value} type="button" disabled={isCurrent || busyStatusOrderId === statusEditorOrder.id} onClick={() => void handleStatusChange(statusEditorOrder, option.value)} className={`flex w-full items-center gap-4 rounded-xl border p-4 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-60 ${isCurrent ? style.className : 'border-dark-border bg-dark-input/45 hover:border-brand-primary/60'}`}><span className={`flex h-10 w-10 shrink-0 items-center justify-center rounded-xl border ${isCurrent ? style.className : 'border-dark-border bg-dark-card text-dark-muted'}`}><Icon className="h-5 w-5" /></span><span><span className="block text-sm font-bold text-dark-text">{option.label}{isCurrent && ' · Atual'}</span><span className="mt-1 block text-xs font-semibold text-dark-muted">{option.description}</span></span></button>; })}</div>
</div>
</div>
)}
</div>
);
};