import React, { useState, useEffect, useMemo, useCallback } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Textarea } from '@/components/ui/textarea'; import { Card, CardContent } from '@/components/ui/card'; import { Checkbox } from '@/components/ui/checkbox'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Info, Package, RefreshCw, Loader2 } from 'lucide-react'; import { SeletorPecasSimples } from './SeletorPecasSimples'; import { toast } from 'sonner'; import { useAuth } from '@/hooks/useAuth'; import { supabase } from '@/integrations/supabase/client'; interface ItemDisponivel { id: string; marca: string; descricao: string; tipo: 'peca' | 'componente'; quantidade_disponivel: number; processo_atual_permitido: number; } interface ApontamentoFormCoreProps { pecas: any[]; ofs: any[]; componentesAgrupados: any[]; processosOrdenados: any[]; onCarregarItensDisponiveis: (ofNumber: string, processoId: string, filteredPecas: any[], componentesAgrupados: any[]) => Promise<{ pecasDisponiveis: ItemDisponivel[]; componentesDisponiveis: ItemDisponivel[]; }>; onValidarSequencia: (ofNumber: string, marca: string, processoId: string, quantidade: number, fase?: string) => Promise<{ valido: boolean; erro?: string }>; onCriarApontamento: (data: any) => Promise<{ success: boolean; error?: any }>; onRefetch: () => Promise; onInvalidateCache: (ofNumber: string, fase?: string) => void; loading: boolean; validacaoLoading: boolean; } const getStoredCache = () => { try { const stored = localStorage.getItem('apontamento_form_cache'); return stored ? JSON.parse(stored) : { of_number: '', fase: '', processo_id: '', data_apontamento: new Date().toISOString().split('T')[0] }; } catch { return { of_number: '', fase: '', processo_id: '', data_apontamento: new Date().toISOString().split('T')[0] }; } }; export const ApontamentoFormCore: React.FC = ({ pecas = [], ofs = [], componentesAgrupados = [], processosOrdenados = [], onCarregarItensDisponiveis, onValidarSequencia, onCriarApontamento, onRefetch, onInvalidateCache, loading, validacaoLoading }) => { const { user } = useAuth(); const [formData, setFormData] = useState(() => ({ ...getStoredCache(), quantidade_produzida: '', observacoes: '', todas_disponiveis: false })); const [itemSelecionado, setItemSelecionado] = useState(null); const [itensDisponiveis, setItensDisponiveis] = useState<{ pecasDisponiveis: ItemDisponivel[]; componentesDisponiveis: ItemDisponivel[]; }>({ pecasDisponiveis: [], componentesDisponiveis: [] }); const [saving, setSaving] = useState(false); const [loadingItens, setLoadingItens] = useState(false); const [lastApontamentoId, setLastApontamentoId] = useState(null); const [canUndo, setCanUndo] = useState(false); const [undoLoading, setUndoLoading] = useState(false); const fasesDisponiveis = useMemo(() => { if (!pecas || !Array.isArray(pecas) || !formData.of_number) return []; return pecas .filter(peca => peca && peca.of_number === formData.of_number) .map(peca => peca.etapa_fase) .filter((fase, index, array) => fase && array.indexOf(fase) === index) .sort(); }, [pecas, formData.of_number]); const filteredPecas = useMemo(() => { if (!pecas || !Array.isArray(pecas)) return []; return pecas.filter(peca => peca && peca.of_number === formData.of_number && peca.etapa_fase === formData.fase ); }, [pecas, formData.of_number, formData.fase]); const updateCache = useCallback((updates: Partial) => { const newCache = { ...getStoredCache(), ...updates }; localStorage.setItem('apontamento_form_cache', JSON.stringify(newCache)); }, []); const carregarItensDisponiveis = useCallback(async () => { if (!formData.of_number || !formData.fase || !formData.processo_id) { console.log('⚠️ Dados insuficientes para carregar itens:', { of: formData.of_number, fase: formData.fase, processo: formData.processo_id }); setItensDisponiveis({ pecasDisponiveis: [], componentesDisponiveis: [] }); return; } if (!filteredPecas || (filteredPecas.length === 0 && (!componentesAgrupados || componentesAgrupados.length === 0))) { console.log('⚠️ Sem peças ou componentes para processar'); setItensDisponiveis({ pecasDisponiveis: [], componentesDisponiveis: [] }); return; } setLoadingItens(true); console.log('🔄 Carregando itens disponíveis...', { of: formData.of_number, fase: formData.fase, processo: formData.processo_id, pecasCount: filteredPecas.length, componentesCount: componentesAgrupados?.length || 0 }); try { const itens = await onCarregarItensDisponiveis( formData.of_number, formData.processo_id, filteredPecas || [], componentesAgrupados || [] ); console.log('✅ Itens carregados:', { pecasDisponiveis: itens?.pecasDisponiveis?.length || 0, componentesDisponiveis: itens?.componentesDisponiveis?.length || 0 }); setItensDisponiveis(itens || { pecasDisponiveis: [], componentesDisponiveis: [] }); } catch (error) { console.error('❌ Erro ao carregar itens:', error); setItensDisponiveis({ pecasDisponiveis: [], componentesDisponiveis: [] }); } finally { setLoadingItens(false); } }, [ formData.of_number, formData.fase, formData.processo_id, filteredPecas, componentesAgrupados, onCarregarItensDisponiveis ]); useEffect(() => { const timeoutId = setTimeout(() => { carregarItensDisponiveis(); }, 300); return () => clearTimeout(timeoutId); }, [carregarItensDisponiveis]); useEffect(() => { setItemSelecionado(null); setFormData(prev => ({ ...prev, quantidade_produzida: '', todas_disponiveis: false })); }, [formData.of_number, formData.fase, formData.processo_id]); useEffect(() => { if (formData.todas_disponiveis && itemSelecionado) { setFormData(prev => ({ ...prev, quantidade_produzida: itemSelecionado.quantidade_disponivel.toString() })); } }, [formData.todas_disponiveis, itemSelecionado]); const handleOFChange = (ofNumber: string) => { const updates = { of_number: ofNumber, fase: '', processo_id: '', quantidade_produzida: '', todas_disponiveis: false }; setFormData(prev => ({ ...prev, ...updates })); updateCache({ of_number: ofNumber, fase: '', processo_id: '' }); setItemSelecionado(null); }; const handleFaseChange = (fase: string) => { const updates = { fase: fase, processo_id: '', quantidade_produzida: '', todas_disponiveis: false }; setFormData(prev => ({ ...prev, ...updates })); updateCache({ fase: fase, processo_id: '' }); setItemSelecionado(null); }; const handleProcessoChange = (processoId: string) => { console.log('🔄 Mudança de processo detectada, limpando cache específico...'); // LIMPAR CACHE ESPECÍFICO ANTES DE ALTERAR O PROCESSO if (formData.of_number && formData.fase) { console.log(`🗑️ Invalidando cache para OF: ${formData.of_number}, Fase: ${formData.fase}`); onInvalidateCache(formData.of_number, formData.fase); } const updates = { processo_id: processoId, quantidade_produzida: '', todas_disponiveis: false }; setFormData(prev => ({ ...prev, ...updates })); updateCache({ processo_id: processoId }); setItemSelecionado(null); // Limpar itens disponíveis imediatamente para forçar nova consulta setItensDisponiveis({ pecasDisponiveis: [], componentesDisponiveis: [] }); console.log('✅ Cache limpo, nova consulta será realizada automaticamente'); }; const handleItemSelect = useCallback((item: ItemDisponivel) => { console.log('🎯 Item selecionado:', { marca: item.marca, tipo: item.tipo, quantidade_disponivel: item.quantidade_disponivel }); setItemSelecionado(item); setFormData(prev => ({ ...prev, quantidade_produzida: '', todas_disponiveis: false })); }, []); const handleQuantidadeChange = (value: string) => { const quantidade = parseInt(value); if (itemSelecionado && quantidade > itemSelecionado.quantidade_disponivel) { toast.error(`Quantidade não pode ser maior que ${itemSelecionado.quantidade_disponivel} unidades disponíveis`); return; } setFormData(prev => ({ ...prev, quantidade_produzida: value, todas_disponiveis: false })); }; const handleTodasDisponiveisChange = (checked: boolean) => { console.log('🔄 Checkbox "Todas" alterado:', { checked, itemSelecionado: itemSelecionado?.marca, quantidade_disponivel: itemSelecionado?.quantidade_disponivel }); setFormData(prev => ({ ...prev, todas_disponiveis: checked, quantidade_produzida: checked && itemSelecionado ? itemSelecionado.quantidade_disponivel.toString() : prev.quantidade_produzida })); }; const resetFormForNewEntry = async () => { console.log('🔄 Resetando formulário para nova entrada...'); setItemSelecionado(null); setFormData(prev => ({ ...prev, quantidade_produzida: '', observacoes: '', todas_disponiveis: false })); console.log('📋 Recarregando itens disponíveis após reset...'); await carregarItensDisponiveis(); }; const handleBatchSelect = async (items: ItemDisponivel[], tipo: 'peca' | 'componente') => { setSaving(true); let sucessos = 0; let erros = 0; try { for (const item of items) { try { const validacao = await onValidarSequencia( formData.of_number, item.marca, formData.processo_id, item.quantidade_disponivel, formData.fase ); if (!validacao.valido) { erros++; continue; } const apontamentoData: any = { of_number: formData.of_number, tipo_apontamento: item.tipo, processo_id: formData.processo_id, quantidade_produzida: item.quantidade_disponivel, data_apontamento: formData.data_apontamento, observacoes: `Apontamento em lote - ${tipo}` }; if (item.tipo === 'componente') { apontamentoData.componente_id = item.id; } else { apontamentoData.peca_id = item.id; } const result = await onCriarApontamento(apontamentoData); if (result.success) { sucessos++; } else { erros++; } } catch (error) { erros++; } } if (sucessos > 0) { toast.success(`${sucessos} apontamentos realizados com sucesso!`); await Promise.all([ onRefetch(), resetFormForNewEntry() ]); } if (erros > 0) { toast.error(`${erros} apontamentos falharam.`); } } catch (error) { toast.error('Erro ao realizar apontamento em lote'); } finally { setSaving(false); } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!itemSelecionado || !formData.processo_id || !formData.quantidade_produzida) { toast.error('Preencha todos os campos obrigatórios'); return; } const quantidade = parseInt(formData.quantidade_produzida); if (quantidade <= 0 || quantidade > itemSelecionado.quantidade_disponivel) { toast.error('Quantidade inválida'); return; } setSaving(true); try { console.log('🔍 Iniciando validação de sequência:', { of: formData.of_number, marca: itemSelecionado.marca, processo: formData.processo_id, quantidade, metodo: formData.todas_disponiveis ? 'CHECKBOX_TODAS' : 'MANUAL' }); const validacao = await onValidarSequencia( formData.of_number, itemSelecionado.marca, formData.processo_id, quantidade, formData.fase ); if (!validacao.valido) { toast.error(validacao.erro); return; } console.log('✅ Validação aprovada, criando apontamento...'); const apontamentoData: any = { of_number: formData.of_number, tipo_apontamento: itemSelecionado.tipo, processo_id: formData.processo_id, quantidade_produzida: quantidade, data_apontamento: formData.data_apontamento, observacoes: formData.observacoes || null }; if (itemSelecionado.tipo === 'componente') { apontamentoData.componente_id = itemSelecionado.id; } else { apontamentoData.peca_id = itemSelecionado.id; } console.log('📝 Dados do apontamento sendo criado:', { ...apontamentoData, metodo_utilizado: formData.todas_disponiveis ? 'CHECKBOX_TODAS' : 'DIGITACAO_MANUAL', quantidade_original_disponivel: itemSelecionado.quantidade_disponivel, quantidade_sendo_apontada: quantidade }); const result = await onCriarApontamento(apontamentoData); if (result.success) { console.log('✅ Apontamento criado com sucesso!'); toast.success('Apontamento registrado com sucesso!'); // Atualizar estado do último apontamento if (result.success) { // Para desfazer, buscar o último apontamento criado const { data: lastApontamento } = await supabase .from('apontamentos_producao') .select('id') .eq('created_by', user.id) .order('created_at', { ascending: false }) .limit(1) .single(); if (lastApontamento) { setLastApontamentoId(lastApontamento.id); setCanUndo(true); } } // INVALIDAR CACHE ESPECÍFICO DA OF E FASE ANTES DE RECARREGAR console.log('🗑️ Invalidando cache após apontamento bem-sucedido...'); onInvalidateCache(formData.of_number, formData.fase); // Aguardar atualização e reset await Promise.all([ onRefetch(), resetFormForNewEntry() ]); console.log('✅ Formulário resetado e dados atualizados após apontamento'); } } catch (error) { console.error('❌ Erro ao registrar apontamento:', error); toast.error('Erro ao registrar apontamento'); } finally { setSaving(false); } }; const limparCacheCompleto = () => { localStorage.removeItem('apontamento_form_cache'); setFormData({ of_number: '', fase: '', data_apontamento: new Date().toISOString().split('T')[0], processo_id: '', quantidade_produzida: '', observacoes: '', todas_disponiveis: false }); setItemSelecionado(null); setItensDisponiveis({ pecasDisponiveis: [], componentesDisponiveis: [] }); toast.success('Cache limpo completamente!'); }; // Função para desfazer último apontamento const handleUndo = useCallback(async () => { if (!lastApontamentoId || !user?.id) return; setUndoLoading(true); try { const { error } = await supabase .from('apontamentos_producao') .delete() .eq('id', lastApontamentoId) .eq('created_by', user.id); if (error) { console.error('Erro ao desfazer apontamento:', error); toast.error('Erro ao desfazer apontamento'); return; } // Resetar estado do botão de desfazer setLastApontamentoId(null); setCanUndo(false); // Recarregar itens disponíveis se necessário if (formData.of_number && formData.fase && formData.processo_id) { await carregarItensDisponiveis(); } toast.success('Apontamento desfeito com sucesso!'); } catch (error) { console.error('Erro inesperado ao desfazer:', error); toast.error('Erro inesperado ao desfazer apontamento'); } finally { setUndoLoading(false); } }, [lastApontamentoId, user?.id, supabase, formData.of_number, formData.fase, formData.processo_id, carregarItensDisponiveis]); const processoSelecionado = processosOrdenados?.find(p => p.id === formData.processo_id); if (loading) { return (
Carregando dados...
); } return (
{/* Cache status */} {(formData.of_number || formData.fase || formData.processo_id) && ( Formulário restaurado do cache )} {/* Campos de seleção básicos */}
{formData.of_number && (
)}
{formData.fase && (
{ const newDate = e.target.value; setFormData(prev => ({ ...prev, data_apontamento: newDate })); updateCache({ data_apontamento: newDate }); }} />
)} {/* Informação sobre o processo */} {processoSelecionado && ( {processoSelecionado.ordem === 1 ? `Processo inicial: ${processoSelecionado.nome}. Todos os itens estão disponíveis.` : `Processo ${processoSelecionado.ordem}: ${processoSelecionado.nome}. Apenas itens que passaram pelos processos anteriores estão disponíveis.` } )} {/* Seletor de Itens */} {formData.processo_id && ( handleSubmit({} as React.FormEvent)} submitDisabled={saving || !itemSelecionado || !formData.processo_id || !formData.quantidade_produzida || loadingItens} submitLoading={saving} canUndo={canUndo} onUndo={handleUndo} undoLoading={undoLoading} lastApontamentoId={lastApontamentoId} /> )} {/* Campos de Quantidade */} {itemSelecionado && (

Quantidade a Apontar

handleQuantidadeChange(e.target.value)} disabled={formData.todas_disponiveis} className="w-full text-lg h-12 bg-slate-800 border-slate-600" />
)}