From 4b66e91442a41fb2d74ef276d82cbfef1612f263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Thu, 27 Aug 2026 19:53:50 -0300 Subject: [PATCH] Refine production plan actions and deadlines --- src/pages/ProductionPlans.tsx | 40 +++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/src/pages/ProductionPlans.tsx b/src/pages/ProductionPlans.tsx index 0bd5f51..7e16049 100644 --- a/src/pages/ProductionPlans.tsx +++ b/src/pages/ProductionPlans.tsx @@ -67,6 +67,12 @@ const ProductionPlans = () => { return () => window.clearTimeout(timer); }, [loadOperationalUsers, loadPlans]); + useEffect(() => { + if (!message) return undefined; + const timer = window.setTimeout(() => setMessage(''), 5_000); + return () => window.clearTimeout(timer); + }, [message]); + const activePlans = useMemo(() => plans.filter(plan => !['finished', 'canceled'].includes(plan.status)), [plans]); const totalOpenQuantity = useMemo(() => activePlans.reduce((total, plan) => total + plan.totalQuantity, 0), [activePlans]); @@ -123,6 +129,29 @@ const ProductionPlans = () => { } }; + const changeExpectedDate = async (plan: ProductionPlan, expectedDate: string) => { + setBusyPlanId(plan.id); + setMessage(''); + try { + const updated = await updateProductionPlan(plan.id, { + name: plan.name, + contractor: plan.contractor, + responsibleUserId: plan.responsibleUserId, + responsibleName: plan.responsibleName, + plannedStartDate: plan.plannedStartDate, + expectedDate: expectedDate || null, + notes: plan.notes, + source: plan.source + }); + setPlans(current => current.map(item => item.id === updated.id ? updated : item)); + setMessage(expectedDate ? `Prazo de ${updated.number} atualizado.` : `Prazo de ${updated.number} removido.`); + } catch (error) { + setMessage(error instanceof Error ? error.message : 'Não foi possível atualizar o prazo.'); + } finally { + setBusyPlanId(null); + } + }; + const openReview = (plan: ProductionPlan) => { const checklist = plan.review?.checklist || {}; setReviewForm({ @@ -175,8 +204,7 @@ const ProductionPlans = () => {
Suprimentos -

PCP

-

Planos de Produção

+

Planos de Produção

Acompanhe cada frente de produção, o responsável e o estágio das OPs sem perder o histórico do plano.

@@ -203,16 +231,14 @@ const ProductionPlans = () => {
{plans.map(plan => (
-
-
{plan.number}{plan.statusLabel}{plan.review && {reviewDecisionLabel[plan.review.decision]}}

{plan.name}

{plan.notes &&

{plan.notes}

}
-
-
+
{plan.number}{plan.statusLabel}{plan.review && {reviewDecisionLabel[plan.review.decision]}}

{plan.name}

{plan.notes &&

{plan.notes}

}

Escopo

{plan.orderCount} {plan.orderCount === 1 ? 'OP' : 'OPs'} · {formatNumber(plan.totalQuantity)} un.

Corte previsto pelo plano

Andamento

Cortado {formatNumber(plan.actualCutQuantity)} · final {formatNumber(plan.finishedQuantity)}

{statusOptions.filter(option => Number(plan.statusCounts[option.value] || 0) > 0).map(option => {plan.statusCounts[option.value]} {option.label.toLowerCase()})}
-

Prazo

{formatDate(plan.expectedDate)}

Início: {formatDate(plan.plannedStartDate)}

+

Início: {formatDate(plan.plannedStartDate)}

Responsável

{plan.contractor || 'Sem facção definida'}

+

Ações do plano

Revise a liberação antes de avançar a operação.

{responsiblePickerPlanId === plan.id &&

Atribuir responsável

Selecione um usuário ativo do Graphs para acompanhar este plano.

{isLoadingOperationalUsers ?
Carregando usuários...
: operationalUsers.map(user => )}{!isLoadingOperationalUsers && !operationalUsers.length &&

Nenhum usuário ativo encontrado. Cadastre ou reative um usuário para atribuir o plano.

}
{plan.responsibleName && }
} {reviewingPlanId === plan.id &&

Liberação operacional

Registre a decisão antes de avançar o plano. Este registro não muda o estoque nem substitui o recebimento.

{[{ key: 'materialsReserved', label: 'Materiais reservados' }, { key: 'cutSpecificationReady', label: 'Ficha e corte definidos' }, { key: 'sizeAndColorReviewed', label: 'Tamanhos e cores revisados' }, { key: 'responsibleAndDateDefined', label: 'Responsável e data definidos' }].map(item => )}
}