Add local production order workflow
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m40s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m40s
This commit is contained in:
@@ -4,7 +4,7 @@ import { ArrowLeft, CalendarDays, CheckCircle2, ClipboardList, Clock3, Download,
|
||||
import DateRangePicker from '../components/DateRangePicker';
|
||||
import PaginationControls from '../components/PaginationControls';
|
||||
import RefreshStatus from '../components/RefreshStatus';
|
||||
import { consumeSupplyLotForProduction, exportToCSV, fetchProductionOrders, fetchSupplySummary } from '../dataService';
|
||||
import { consumeSupplyLotForProduction, exportToCSV, fetchProductionOrders, fetchSupplySummary, updateProductionOrderStatus } from '../dataService';
|
||||
import type { DateRange, ProductionOrderItem, ProductionOrderStatus, ProductionOrderSummary, SupplyLot } from '../types';
|
||||
|
||||
type ProductionOrderStatusTab = 'all' | 'open' | 'in_progress' | 'finished' | 'canceled';
|
||||
@@ -22,6 +22,10 @@ const statusTabs: Array<{ key: ProductionOrderStatusTab; label: string; dotClass
|
||||
{ key: 'canceled', label: 'Cancelada', dotClass: 'bg-zinc-500' }
|
||||
];
|
||||
|
||||
const editableStatusOptions: Array<{ value: ProductionOrderStatusTab; label: string }> = statusTabs
|
||||
.filter(tab => tab.key !== 'all')
|
||||
.map(tab => ({ value: tab.key, label: tab.label }));
|
||||
|
||||
const statusStyles: Record<string, { label: string; className: string; dotClass: string }> = {
|
||||
open: {
|
||||
label: 'Em aberto',
|
||||
@@ -121,6 +125,8 @@ const ProductionOrders = () => {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isSupplyBusy, setIsSupplyBusy] = useState(false);
|
||||
const [supplyMessage, setSupplyMessage] = useState('');
|
||||
const [busyStatusOrderId, setBusyStatusOrderId] = useState<number | null>(null);
|
||||
const [statusMessage, setStatusMessage] = useState('');
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [itemsPerPage, setItemsPerPage] = useState(20);
|
||||
const [exitForm, setExitForm] = useState({
|
||||
@@ -190,6 +196,23 @@ const ProductionOrders = () => {
|
||||
void loadProductionOrders({ force: true });
|
||||
};
|
||||
|
||||
const handleStatusChange = async (order: ProductionOrderItem, status: ProductionOrderStatusTab) => {
|
||||
if (status === 'all' || status === order.status) return;
|
||||
|
||||
setBusyStatusOrderId(order.id);
|
||||
setStatusMessage('');
|
||||
try {
|
||||
await updateProductionOrderStatus(order.id, status);
|
||||
const nextSummary = await fetchProductionOrders(dateRange, { search: searchTerm }, { force: true });
|
||||
setSummary(nextSummary);
|
||||
setStatusMessage(`OP ${order.number || `#${order.id}`} atualizada para ${statusStyles[status]?.label || status}.`);
|
||||
} catch (error) {
|
||||
setStatusMessage(error instanceof Error ? error.message : 'Não foi possível atualizar o status da OP.');
|
||||
} finally {
|
||||
setBusyStatusOrderId(null);
|
||||
}
|
||||
};
|
||||
|
||||
const refreshSupplyLots = async () => {
|
||||
const supplySummary = await fetchSupplySummary();
|
||||
setSupplyLots(supplySummary.lots);
|
||||
@@ -248,7 +271,7 @@ const ProductionOrders = () => {
|
||||
Suprimentos
|
||||
</Link>
|
||||
<h1 className="text-2xl font-bold text-dark-text">Ordens de Produção</h1>
|
||||
<p className="mt-2 text-dark-muted font-medium">Acompanhe as ordens de produção sincronizadas do Tiny.</p>
|
||||
<p className="mt-2 text-dark-muted font-medium">Acompanhe as ordens locais geradas pelo plano de corte.</p>
|
||||
</div>
|
||||
<DateRangePicker
|
||||
dateRange={dateRange}
|
||||
@@ -399,6 +422,11 @@ const ProductionOrders = () => {
|
||||
|
||||
<div className="overflow-hidden rounded-2xl border border-dark-border bg-dark-card shadow-sm">
|
||||
<div className="border-b border-dark-border p-5">
|
||||
{statusMessage && (
|
||||
<div className="mb-4 rounded-xl border border-dark-border bg-dark-input px-3 py-2 text-sm font-bold text-dark-muted">
|
||||
{statusMessage}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col gap-4 xl:flex-row xl:items-center xl:justify-between">
|
||||
<div className="relative w-full xl:max-w-xl">
|
||||
<Search className="absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 text-dark-muted" />
|
||||
@@ -515,10 +543,18 @@ const ProductionOrders = () => {
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-3">
|
||||
<span className={`inline-flex items-center gap-2 rounded-full border px-2.5 py-1 text-[10px] font-bold uppercase tracking-wide ${statusStyle.className}`}>
|
||||
<span className={`h-1.5 w-1.5 rounded-full ${statusStyle.dotClass}`} />
|
||||
{statusStyle.label}
|
||||
</span>
|
||||
<label className="sr-only" htmlFor={`production-order-status-${order.id}`}>Status da OP</label>
|
||||
<select
|
||||
id={`production-order-status-${order.id}`}
|
||||
value={order.status}
|
||||
disabled={busyStatusOrderId === order.id}
|
||||
onChange={(event) => void handleStatusChange(order, event.target.value as ProductionOrderStatusTab)}
|
||||
className={`h-8 rounded-lg border bg-dark-input px-2 text-[10px] font-bold uppercase tracking-wide outline-none transition-colors focus:border-brand-primary disabled:cursor-not-allowed disabled:opacity-50 ${statusStyle.className}`}
|
||||
>
|
||||
{editableStatusOptions.map(option => (
|
||||
<option key={option.value} value={option.value}>{option.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
@@ -534,7 +570,7 @@ const ProductionOrders = () => {
|
||||
</div>
|
||||
<p className="mt-4 text-sm font-bold text-dark-text">Nenhuma ordem de produção encontrada.</p>
|
||||
<p className="mt-1 max-w-md text-sm font-medium text-dark-muted">
|
||||
Quando a sincronização com o Tiny estiver ativa, as ordens aparecerão aqui com status, produto, quantidade e marcadores.
|
||||
Gere ordens pelo Plano de Corte para acompanhar status, produto, quantidade e baixa de material dentro do Graphs.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user