Return open orders to cut plan
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m4s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m4s
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { Fragment, type FormEvent, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Link, useOutletContext } from 'react-router-dom';
|
||||
import { ArrowLeft, CheckCircle2, ChevronDown, ChevronUp, ClipboardList, Clock3, Download, PackageCheck, PlayCircle, Search, XCircle } from 'lucide-react';
|
||||
import { Link, useNavigate, useOutletContext } from 'react-router-dom';
|
||||
import { ArrowLeft, CheckCircle2, ChevronDown, ChevronUp, ClipboardList, Clock3, Download, PackageCheck, PlayCircle, Search, Undo2, XCircle } from 'lucide-react';
|
||||
import DateRangePicker from '../components/DateRangePicker';
|
||||
import PaginationControls from '../components/PaginationControls';
|
||||
import RefreshStatus from '../components/RefreshStatus';
|
||||
@@ -121,6 +121,7 @@ const ProductionOrders = () => {
|
||||
refreshInterval: number;
|
||||
setRefreshInterval: (interval: number) => void;
|
||||
}>();
|
||||
const navigate = useNavigate();
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [statusFilter, setStatusFilter] = useState<ProductionOrderStatusTab>('all');
|
||||
const [summary, setSummary] = useState<ProductionOrderSummary>(emptySummary);
|
||||
@@ -219,6 +220,25 @@ const ProductionOrders = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const returnOrderToCutPlan = async (order: ProductionOrderItem) => {
|
||||
if (order.status !== 'open' || (order.integrationStatus || '').toLowerCase() !== 'local') return;
|
||||
const orderNumber = order.number || `#${order.id}`;
|
||||
if (!window.confirm(`Devolver a OP ${orderNumber} ao Plano de Corte?\n\nA OP será cancelada, preservando o histórico. A necessidade voltará para o Plano de Corte e poderá ser gerada novamente quando você decidir.`)) return;
|
||||
|
||||
setBusyStatusOrderId(order.id);
|
||||
setStatusMessage('');
|
||||
try {
|
||||
await updateProductionOrderStatus(order.id, 'canceled');
|
||||
setSelectedOrderId(null);
|
||||
setStatusMessage(`OP ${orderNumber} devolvida ao Plano de Corte.`);
|
||||
navigate('/supplies/cutting');
|
||||
} catch (error) {
|
||||
setStatusMessage(error instanceof Error ? error.message : 'Não foi possível devolver a OP ao Plano de Corte.');
|
||||
} finally {
|
||||
setBusyStatusOrderId(null);
|
||||
}
|
||||
};
|
||||
|
||||
const refreshSupplyLots = async () => {
|
||||
const supplySummary = await fetchSupplySummary();
|
||||
setSupplyLots(supplySummary.lots);
|
||||
@@ -267,11 +287,17 @@ const ProductionOrders = () => {
|
||||
exportToCSV(exportData, `ordens_producao_${new Date().toISOString().split('T')[0]}.csv`);
|
||||
};
|
||||
|
||||
const renderOrderDetails = (order: ProductionOrderItem) => (
|
||||
const renderOrderDetails = (order: ProductionOrderItem) => {
|
||||
const canReturnToCutPlan = order.status === 'open' && (order.integrationStatus || '').toLowerCase() === 'local';
|
||||
|
||||
return (
|
||||
<div className="space-y-6 rounded-xl border border-dark-border bg-dark-input/25 p-5">
|
||||
<div>
|
||||
<p className="text-xs font-bold uppercase tracking-widest text-dark-muted">Detalhes da OP</p>
|
||||
<p className="mt-1 text-sm font-bold text-dark-text">{order.number || `OP #${order.id}`} · {order.productDescription}</p>
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||
<div>
|
||||
<p className="text-xs font-bold uppercase tracking-widest text-dark-muted">Detalhes da OP</p>
|
||||
<p className="mt-1 text-sm font-bold text-dark-text">{order.number || `OP #${order.id}`} · {order.productDescription}</p>
|
||||
</div>
|
||||
{canReturnToCutPlan && <button type="button" onClick={() => void returnOrderToCutPlan(order)} disabled={busyStatusOrderId === order.id} className="inline-flex h-9 shrink-0 items-center justify-center gap-2 rounded-lg border border-amber-400/30 bg-amber-400/10 px-3 text-xs font-bold text-amber-200 transition-colors hover:border-amber-400/60 hover:bg-amber-400/15 disabled:cursor-not-allowed disabled:opacity-50 cursor-pointer"><Undo2 className="h-4 w-4" />Devolver ao Plano de Corte</button>}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3 md:grid-cols-4">
|
||||
@@ -302,7 +328,8 @@ const ProductionOrders = () => {
|
||||
|
||||
{(order.steps.length > 0 || order.notes || order.supplier || order.lotCode || order.rollQuantity || order.fabricKg || order.ribKg || order.yieldPiecesPerKg) && <section><h3 className="text-xs font-bold uppercase tracking-widest text-dark-muted">Informações adicionais</h3><div className="mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2">{(order.supplier || order.lotCode || order.rollQuantity || order.fabricKg || order.ribKg || order.yieldPiecesPerKg) && <dl className="grid grid-cols-2 gap-3 rounded-xl border border-dark-border bg-dark-card p-4 text-xs sm:col-span-2">{order.supplier && <div><dt className="font-bold text-dark-muted">Fornecedor</dt><dd className="mt-1 font-bold text-dark-text">{order.supplier}</dd></div>}{order.lotCode && <div><dt className="font-bold text-dark-muted">Lote</dt><dd className="mt-1 font-bold text-dark-text">{order.lotCode}</dd></div>}{order.rollQuantity !== null && <div><dt className="font-bold text-dark-muted">Rolos</dt><dd className="mt-1 font-bold text-dark-text">{formatQuantity(order.rollQuantity)}</dd></div>}{order.fabricKg !== null && <div><dt className="font-bold text-dark-muted">Malha</dt><dd className="mt-1 font-bold text-dark-text">{formatQuantity(order.fabricKg)} kg</dd></div>}{order.ribKg !== null && <div><dt className="font-bold text-dark-muted">Ribana</dt><dd className="mt-1 font-bold text-dark-text">{formatQuantity(order.ribKg)} kg</dd></div>}{order.yieldPiecesPerKg !== null && <div><dt className="font-bold text-dark-muted">Rendimento</dt><dd className="mt-1 font-bold text-dark-text">{formatQuantity(order.yieldPiecesPerKg)} un./kg</dd></div>}</dl>}{order.steps.length > 0 && <div className="rounded-xl border border-dark-border bg-dark-card p-4 sm:col-span-2"><p className="text-xs font-bold text-dark-text">Etapas</p><div className="mt-3 divide-y divide-dark-border">{order.steps.map(step => <div key={step.id} className="flex items-center gap-3 py-2 text-xs"><span className="h-2.5 w-2.5 rounded-full" style={{ backgroundColor: step.color || 'var(--color-brand-primary)' }} /><span className="font-bold text-dark-text">{step.name}</span><span className="ml-auto font-semibold text-dark-muted">{formatDate(step.startDate)} - {formatDate(step.endDate)}</span></div>)}</div></div>}{order.notes && <p className="whitespace-pre-wrap rounded-xl border border-dark-border bg-dark-card p-4 text-sm font-semibold text-dark-muted sm:col-span-2">{order.notes}</p>}</div></section>}
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
|
||||
Reference in New Issue
Block a user