diff --git a/src/components/apontamento/historico/ApontamentosFilters.tsx b/src/components/apontamento/historico/ApontamentosFilters.tsx index 0da7412..9055a56 100644 --- a/src/components/apontamento/historico/ApontamentosFilters.tsx +++ b/src/components/apontamento/historico/ApontamentosFilters.tsx @@ -59,11 +59,11 @@ export const ApontamentosFilters: React.FC = ({ {/* Primeira linha de filtros - mais compacta */}
- +
setSearchTerm(e.target.value)} className="pl-7 h-8 text-xs" diff --git a/src/components/apontamento/historico/useApontamentosFilters.tsx b/src/components/apontamento/historico/useApontamentosFilters.tsx index 1846dc8..6373e77 100644 --- a/src/components/apontamento/historico/useApontamentosFilters.tsx +++ b/src/components/apontamento/historico/useApontamentosFilters.tsx @@ -143,18 +143,17 @@ export const useApontamentosFilters = (apontamentos: ApontamentoProducao[]) => { let resultado = [...apontamentos]; // Começar com TODOS os apontamentos - // Aplicar filtro de busca textual APENAS se preenchido + // Aplicar filtro de busca por MARCA DA PEÇA apenas se preenchido if (searchTerm && searchTerm.trim() !== '') { - const searchLower = searchTerm.toLowerCase(); + const searchLower = searchTerm.toLowerCase().trim(); const beforeSearch = resultado.length; resultado = resultado.filter(apt => - (apt.of_number && apt.of_number.toLowerCase().includes(searchLower)) || (apt.peca?.marca && apt.peca.marca.toLowerCase().includes(searchLower)) || (apt.componente?.marca_componente && apt.componente.marca_componente.toLowerCase().includes(searchLower)) ); - console.log(`🔎 Filtro texto "${searchTerm}": ${beforeSearch} → ${resultado.length} registros`); + console.log(`🔎 Filtro marca "${searchTerm}": ${beforeSearch} → ${resultado.length} registros`); } // Aplicar filtro por OF APENAS se selecionado diff --git a/src/components/expedicao/ItensRomaneioModal.tsx b/src/components/expedicao/ItensRomaneioModal.tsx index fdb8927..5eeeca3 100644 --- a/src/components/expedicao/ItensRomaneioModal.tsx +++ b/src/components/expedicao/ItensRomaneioModal.tsx @@ -10,7 +10,7 @@ import { ScrollArea } from '@/components/ui/scroll-area'; import { Card, CardContent } from '@/components/ui/card'; import { Checkbox } from '@/components/ui/checkbox'; import { toast } from 'sonner'; -import { Package, Plus, Wrench, Expand } from 'lucide-react'; +import { Package, Plus, Wrench, Expand, AlertTriangle } from 'lucide-react'; import { RomaneioExpedicao } from '@/hooks/useRomaneios'; import { useProcessosFabricacao } from '@/hooks/useProcessosFabricacao'; import { usePecas } from '@/hooks/usePecas'; @@ -31,6 +31,7 @@ interface PecaDisponivel { etapa_fase: string; quantidade_disponivel: number; peso_unitario: number; + comprimento?: number | null; prioridade: string; } @@ -40,6 +41,7 @@ interface ItemRomaneioPeca { quantidade: number; peso_unitario: number; peso_total: number; + comprimento?: number | null; marca: string; descricao: string; etapa_fase: string; @@ -111,6 +113,7 @@ export const ItensRomaneioModal: React.FC = ({ descricao, etapa_fase, peso_unitario, + comprimento, prioridade, of_number ) @@ -165,6 +168,7 @@ export const ItensRomaneioModal: React.FC = ({ etapa_fase: apontamento.peca.etapa_fase || '', quantidade_disponivel: quantidade, // Bruto temporário peso_unitario: apontamento.peca.peso_unitario || 0, + comprimento: apontamento.peca.comprimento || null, prioridade: apontamento.peca.prioridade || 'P4' }); } @@ -210,7 +214,7 @@ export const ItensRomaneioModal: React.FC = ({ .from('itens_romaneio_pecas') .select(` *, - pecas!inner(marca, descricao, etapa_fase, peso_unitario, prioridade) + pecas!inner(marca, descricao, etapa_fase, peso_unitario, comprimento, prioridade) `) .eq('romaneio_id', romaneio.id); @@ -224,6 +228,7 @@ export const ItensRomaneioModal: React.FC = ({ quantidade: item.quantidade_expedida, peso_unitario: item.peso_unitario, peso_total: item.peso_total, + comprimento: item.pecas?.comprimento || item.comprimento || null, marca: item.marca, descricao: item.descricao || '', etapa_fase: item.fase || '', @@ -308,7 +313,26 @@ export const ItensRomaneioModal: React.FC = ({ }, [pecasDisponiveis, faseFilter, marcaFilter]); const handleAdicionarPeca = async () => { + const limiteVeiculo = romaneio.comprimento_maximo_veiculo || 12000; + if (adicionarTodas) { + // Verificar se há peças que excedem a capacidade do veículo + const pecasExcedentes = pecasFiltradas.filter(p => p.comprimento && p.comprimento > limiteVeiculo); + if (pecasExcedentes.length > 0) { + const listaMarcas = pecasExcedentes.map(p => `${p.marca} (${p.comprimento}mm)`).join(', '); + const confirmar = window.confirm( + `⚠️ ALERTA DE CAPACIDADE DO VEÍCULO:\n\n` + + `Atenção: ${pecasExcedentes.length} peça(s) excedem o limite de ${limiteVeiculo} mm (${(limiteVeiculo / 1000).toFixed(1)}m) do veículo:\n` + + `${listaMarcas}\n\n` + + `Essas peças são maiores que o espaço de carga do veículo selecionado.\n\n` + + `Deseja realmente incluí-las no romaneio?` + ); + if (!confirmar) { + toast.warning('Operação cancelada para evitar carregar peças que não cabem no caminhão.'); + return; + } + } + // Adicionar todas as peças filtradas com suas quantidades disponíveis for (const peca of pecasFiltradas) { try { @@ -345,6 +369,21 @@ export const ItensRomaneioModal: React.FC = ({ return; } + // Validação de comprimento do veículo + if (pecaSelecionada.comprimento && pecaSelecionada.comprimento > limiteVeiculo) { + const confirmar = window.confirm( + `⚠️ ALERTA DE CAPACIDADE DO VEÍCULO:\n\n` + + `A peça "${pecaSelecionada.marca}" tem comprimento de ${pecaSelecionada.comprimento} mm (${(pecaSelecionada.comprimento / 1000).toFixed(1)}m), ` + + `que ULTRAPASSA o comprimento máximo suportado pelo veículo (${limiteVeiculo} mm / ${(limiteVeiculo / 1000).toFixed(1)}m)!\n\n` + + `Essa peça NÃO caberá na carroceria do transporte selecionado.\n\n` + + `Deseja realmente adicionar esta peça mesmo com essa advertência?` + ); + if (!confirmar) { + toast.warning(`Inclusão da peça ${pecaSelecionada.marca} (${pecaSelecionada.comprimento}mm) cancelada por restrição dimensional.`); + return; + } + } + try { const quantidade = parseInt(quantidadePeca); @@ -594,56 +633,89 @@ export const ItensRomaneioModal: React.FC = ({ - - {loading ? ( - Carregando... - ) : ( - <> - {pecasFiltradas.length === 0 ? ( - - Nenhuma peça disponível para este processo - - ) : ( - pecasFiltradas.map((peca) => ( + + {loading ? ( + Carregando... + ) : ( + <> + {pecasFiltradas.length === 0 ? ( + + Nenhuma peça disponível para este processo + + ) : ( + pecasFiltradas.map((peca) => { + const limite = romaneio.comprimento_maximo_veiculo || 12000; + const naoCabe = Boolean(peca.comprimento && peca.comprimento > limite); + return ( - - {peca.marca} - {peca.descricao} (Qtd: {peca.quantidade_disponivel}) - +
+ + {peca.marca} - {peca.descricao} (Qtd: {peca.quantidade_disponivel}) + + {peca.comprimento && ( + + {peca.comprimento}mm {naoCabe ? '⚠️ NÃO CABE' : ''} + + )} +
- )) - )} - - )} -
- -
- -
- - p.id === pecaDisponivel)?.quantidade_disponivel || 1} - value={quantidadePeca} - onChange={(e) => setQuantidadePeca(e.target.value)} - placeholder="Quantidade" - className={isMobile ? 'h-10' : 'h-11'} - /> -
+ ); + }) + )} + + )} + +
- - )} - +
+ + p.id === pecaDisponivel)?.quantidade_disponivel || 1} + value={quantidadePeca} + onChange={(e) => setQuantidadePeca(e.target.value)} + placeholder="Quantidade" + className={isMobile ? 'h-10' : 'h-11'} + /> +
+
+ + {/* Alerta de dimensão se a peça exceder a capacidade do veículo */} + {(() => { + const pSel = pecasDisponiveis.find(p => p.id === pecaDisponivel); + const limite = romaneio.comprimento_maximo_veiculo || 12000; + if (pSel?.comprimento && pSel.comprimento > limite) { + return ( +
+
+ + ALERTA DE DIMENSÃO: Peça não cabe no veículo ({limite}mm) +
+

+ A peça {pSel.marca} possui {pSel.comprimento} mm ({(pSel.comprimento / 1000).toFixed(1)}m) e ULTRAPASSA o comprimento máximo de carregamento deste veículo ({(limite / 1000).toFixed(1)}m). +

+
+ ); + } + return null; + })()} + + )} + + diff --git a/src/components/expedicao/PecasRomaneioExpandido.tsx b/src/components/expedicao/PecasRomaneioExpandido.tsx index 85f26a9..cd3978b 100644 --- a/src/components/expedicao/PecasRomaneioExpandido.tsx +++ b/src/components/expedicao/PecasRomaneioExpandido.tsx @@ -22,6 +22,7 @@ interface ItemRomaneioPeca { quantidade_expedida: number; peso_unitario: number; peso_total: number; + comprimento?: number | null; marca: string; descricao: string; fase: string; @@ -44,7 +45,7 @@ export const PecasRomaneioExpandido: React.FC = ({ try { const { data, error } = await supabase .from('itens_romaneio_pecas') - .select('*') + .select('*, pecas(comprimento)') .eq('romaneio_id', romaneio.id) .order('marca'); @@ -57,6 +58,7 @@ export const PecasRomaneioExpandido: React.FC = ({ quantidade_expedida: item.quantidade_expedida, peso_unitario: item.peso_unitario, peso_total: item.peso_total, + comprimento: item.pecas?.comprimento || item.comprimento || null, marca: item.marca, descricao: item.descricao || '', fase: item.fase || '', @@ -170,11 +172,12 @@ export const PecasRomaneioExpandido: React.FC = ({ {/* Cabeçalho da tabela - Desktop/Tablet */} {!isMobile && (
Marca
Fase
Descrição
+
Comp. (mm)
Qtd
Peso Unit.
Peso Total
@@ -184,16 +187,29 @@ export const PecasRomaneioExpandido: React.FC = ({ )} {/* Linhas de dados */} - {itensPeca.map((item, index) => ( + {itensPeca.map((item, index) => { + const limite = romaneio.comprimento_maximo_veiculo || 12000; + const naoCabe = Boolean(item.comprimento && item.comprimento > limite); + + return (
{isMobile ? ( // Layout mobile - card style -
-
{item.marca}
+
+ {item.marca} + {naoCabe && ( + + Excede Veículo + + )} +
{item.fase}
@@ -218,7 +234,13 @@ export const PecasRomaneioExpandido: React.FC = ({
{item.descricao}
-
+
+
+
Comp:
+
+ {item.comprimento ? `${item.comprimento}mm` : '-'} +
+
Qtd:
{item.quantidade_expedida}
@@ -237,12 +259,18 @@ export const PecasRomaneioExpandido: React.FC = ({ // Layout desktop/tablet - table style
-
{item.marca}
+ } ${isTablet ? 'grid-cols-9' : 'grid-cols-11'} ${naoCabe ? 'bg-red-950/20' : ''}`}> +
+ {item.marca} + {naoCabe && ⚠️} +
{item.fase}
{item.descricao}
+
+ {item.comprimento ? `${item.comprimento}` : '-'} +
{item.quantidade_expedida}
@@ -280,7 +308,8 @@ export const PecasRomaneioExpandido: React.FC = ({
)}
- ))} + ); + })}
)} diff --git a/src/components/expedicao/RomaneioForm.tsx b/src/components/expedicao/RomaneioForm.tsx index c46e032..55fa0d8 100644 --- a/src/components/expedicao/RomaneioForm.tsx +++ b/src/components/expedicao/RomaneioForm.tsx @@ -52,6 +52,7 @@ export function RomaneioForm({ romaneio, ofNumbers, selectedOF, onSave, onCancel peso_total_romaneio: romaneio?.peso_total_romaneio || 0, previsao_kg: romaneio?.previsao_kg || 0, maior_dimensao: romaneio?.maior_dimensao || '', + comprimento_maximo_veiculo: romaneio?.comprimento_maximo_veiculo || 12000, tipo_transporte: romaneio?.tipo_transporte || 'carro' as 'carro' | 'utilitario' | 'caminho_pequeno' | 'caminhao_trucado' | 'caminhao_munck' | 'carreta_12m' | 'carreta_15m' | 'especial', frete_tipo: (romaneio?.frete_tipo as 'terceiros' | 'proprio') || 'terceiros', nome_motorista: romaneio?.nome_motorista || '' @@ -74,6 +75,7 @@ export function RomaneioForm({ romaneio, ofNumbers, selectedOF, onSave, onCancel peso_total_romaneio: romaneio.peso_total_romaneio || 0, previsao_kg: romaneio.previsao_kg || 0, maior_dimensao: romaneio.maior_dimensao || '', + comprimento_maximo_veiculo: romaneio.comprimento_maximo_veiculo || 12000, tipo_transporte: romaneio.tipo_transporte || 'carro', frete_tipo: (romaneio.frete_tipo as 'terceiros' | 'proprio') || 'terceiros', nome_motorista: romaneio.nome_motorista || '' @@ -92,6 +94,7 @@ export function RomaneioForm({ romaneio, ofNumbers, selectedOF, onSave, onCancel peso_total_romaneio: 0, previsao_kg: 0, maior_dimensao: '', + comprimento_maximo_veiculo: 12000, tipo_transporte: 'carro', frete_tipo: 'terceiros', nome_motorista: '' @@ -374,7 +377,23 @@ export function RomaneioForm({ romaneio, ofNumbers, selectedOF, onSave, onCancel
+
+
+ + {formData.comprimento_maximo_veiculo > 0 && ( + + {(formData.comprimento_maximo_veiculo / 1000).toFixed(1)}m + + )} +
+ setFormData(prev => ({ ...prev, comprimento_maximo_veiculo: parseFloat(e.target.value) || 0 }))} + placeholder="Ex: 10000 (10 metros)" + /> +

+ Limite de comprimento para carregar peças neste veículo. +

+
+
+
+ + setFormData(prev => ({ ...prev, comprimento: parseFloat(e.target.value) || 0 }))} + placeholder="Ex: 500" + className="h-8" + /> +
+
+ handleSort('comprimento')} + > +
+ Comp. (mm) + {getSortIcon('comprimento')} +
+
handleSort('quantidade_por_peca')} @@ -366,6 +392,7 @@ export function ComponentesPopup({ isOpen, onClose, peca }: ComponentesPopupProp {componente.marca_componente} {componente.descricao || '-'} {componente.perfil || '-'} + {componente.comprimento ? `${componente.comprimento}` : '-'} {componente.quantidade_por_peca} {calculateTotalQuantity(componente.quantidade_por_peca)} diff --git a/src/components/pecas/PecaForm.tsx b/src/components/pecas/PecaForm.tsx index 64d07f8..4d39f76 100644 --- a/src/components/pecas/PecaForm.tsx +++ b/src/components/pecas/PecaForm.tsx @@ -55,6 +55,7 @@ export function PecaForm({ quantidade: 1, peso_unitario: 0, peso_total: 0, + comprimento: 0, tratamento_superficial: '', material: '', perfil_principal: '', @@ -71,6 +72,7 @@ export function PecaForm({ quantidade: editingPeca.quantidade, peso_unitario: editingPeca.peso_unitario, peso_total: editingPeca.peso_total, + comprimento: editingPeca.comprimento || 0, tratamento_superficial: editingPeca.tratamento_superficial || '', material: editingPeca.material || '', perfil_principal: editingPeca.perfil_principal || '', @@ -85,6 +87,7 @@ export function PecaForm({ quantidade: 1, peso_unitario: 0, peso_total: 0, + comprimento: 0, tratamento_superficial: '', material: '', perfil_principal: '', @@ -148,6 +151,7 @@ export function PecaForm({ quantidade: 1, peso_unitario: 0, peso_total: 0, + comprimento: 0, tratamento_superficial: '', material: '', perfil_principal: '', @@ -246,6 +250,19 @@ export function PecaForm({ />
+
+ + handleInputChange('comprimento', parseFloat(e.target.value) || 0)} + placeholder="Ex: 12000" + /> +
+
Peso Total + + Comp. (mm) + + Material diff --git a/src/components/pecas/table/TableRow.tsx b/src/components/pecas/table/TableRow.tsx index dd13302..14be7ca 100644 --- a/src/components/pecas/table/TableRow.tsx +++ b/src/components/pecas/table/TableRow.tsx @@ -144,6 +144,10 @@ export function TableRow({ {(peca.peso_total || (peca.quantidade * peca.peso_unitario)).toFixed(2)} kg + + {peca.comprimento != null && Number(peca.comprimento) > 0 ? `${Number(peca.comprimento).toLocaleString('pt-BR')} mm` : '-'} + + {peca.material || '-'} @@ -246,6 +250,7 @@ export function TableRow({ Marca {labels.componenteLabel} Descrição / Perfil + Comp. (mm) Qtd / {labels.pecaLabel} Qtd Total no Lote Peso Unit. (kg) @@ -269,6 +274,9 @@ export function TableRow({ {comp.perfil || comp.descricao || '-'} + + {comp.comprimento != null && Number(comp.comprimento) > 0 ? `${Number(comp.comprimento).toLocaleString('pt-BR')} mm` : '-'} + {qtdPorPeca} un diff --git a/src/hooks/useComponentesPeca.tsx b/src/hooks/useComponentesPeca.tsx index a31c498..23e4be6 100644 --- a/src/hooks/useComponentesPeca.tsx +++ b/src/hooks/useComponentesPeca.tsx @@ -11,6 +11,7 @@ export interface ComponentePeca { descricao: string | null; perfil: string | null; peso_unitario: number; + comprimento?: number | null; quantidade_por_peca: number; created_at: string; updated_at: string; @@ -21,6 +22,7 @@ export interface ComponenteFormData { descricao: string; perfil: string; peso_unitario: number; + comprimento?: number | null; quantidade_por_peca: number; } diff --git a/src/hooks/usePecas.tsx b/src/hooks/usePecas.tsx index 1df4895..2229bb7 100644 --- a/src/hooks/usePecas.tsx +++ b/src/hooks/usePecas.tsx @@ -13,6 +13,7 @@ export interface Peca { quantidade: number; peso_unitario: number; peso_total: number; + comprimento?: number | null; tratamento_superficial: string; material: string; perfil_principal: string; @@ -430,6 +431,7 @@ export function usePecas() { quantidade: Number(item.quantidade) || 0, peso_unitario: Number(item.peso_unitario) || 0, peso_total: Number(item.peso_total) || 0, + comprimento: item.comprimento ? Number(item.comprimento) : (item.comprimento_ref ? Number(item.comprimento_ref) : null), tratamento_superficial: item.tratamento_superficial || '', material: item.material || '', perfil_principal: item.perfil_principal || '', @@ -470,6 +472,7 @@ export function usePecas() { descricao: item.descricao_componente || item.descricao || '', perfil: item.perfil_componente || item.perfil_principal || '', peso_unitario: Number(item.peso_unitario_componente) || 0, + comprimento: item.comprimento_componente ? Number(item.comprimento_componente) : null, quantidade_por_peca: Math.max(1, Math.round(Number(item.quantidade_por_peca) || 1)), user_id: user.id }; @@ -527,6 +530,7 @@ export function usePecas() { quantidade: peca.quantidade, peso_unitario: peca.peso_unitario, peso_total: peca.peso_total, + comprimento: peca.comprimento, tratamento_superficial: peca.tratamento_superficial, material: peca.material, perfil_principal: peca.perfil_principal, diff --git a/src/hooks/usePecasTable.tsx b/src/hooks/usePecasTable.tsx index 62e014a..00b1e79 100644 --- a/src/hooks/usePecasTable.tsx +++ b/src/hooks/usePecasTable.tsx @@ -4,7 +4,7 @@ import { Peca } from './usePecas'; import { supabase } from '@/integrations/supabase/client'; import { applyMarcaFilter } from '@/utils/rangeFilter'; -export type SortField = 'of_number' | 'etapa_fase' | 'marca' | 'descricao' | 'prioridade' | 'quantidade' | 'peso_unitario' | 'peso_total' | 'perfil_principal' | 'tem_componentes'; +export type SortField = 'of_number' | 'etapa_fase' | 'marca' | 'descricao' | 'prioridade' | 'quantidade' | 'peso_unitario' | 'peso_total' | 'comprimento' | 'perfil_principal' | 'tem_componentes'; export type SortOrder = 'asc' | 'desc'; export const usePecasTable = (pecas: Peca[]) => { diff --git a/src/hooks/useRomaneios.tsx b/src/hooks/useRomaneios.tsx index 1956879..da1799b 100644 --- a/src/hooks/useRomaneios.tsx +++ b/src/hooks/useRomaneios.tsx @@ -19,6 +19,7 @@ export interface RomaneioExpedicao { peso_total_romaneio: number; previsao_kg?: number; maior_dimensao?: string; + comprimento_maximo_veiculo?: number; tipo_transporte?: string; frete_tipo?: string; nome_motorista?: string; @@ -62,6 +63,7 @@ export interface PecaPintura { marca: string; etapa_fase?: string; descricao?: string; + comprimento?: number | null; peso_unitario: number; quantidade_produzida_pintura: number; quantidade_ja_expedida: number; @@ -133,6 +135,7 @@ export const usePecasPintura = (ofNumber?: string) => { etapa_fase, descricao, peso_unitario, + comprimento, of_number ), processos_fabricacao ( @@ -171,6 +174,7 @@ export const usePecasPintura = (ofNumber?: string) => { etapa_fase: peca.etapa_fase, descricao: peca.descricao, peso_unitario: peca.peso_unitario, + comprimento: peca.comprimento, quantidade_produzida_pintura: apontamento.quantidade_produzida, of_number: peca.of_number }); diff --git a/src/integrations/supabase/types.ts b/src/integrations/supabase/types.ts index 9e91da6..073740e 100644 --- a/src/integrations/supabase/types.ts +++ b/src/integrations/supabase/types.ts @@ -445,6 +445,7 @@ export type Database = { } componentes_peca: { Row: { + comprimento: number | null created_at: string descricao: string | null id: string @@ -457,6 +458,7 @@ export type Database = { user_id: string | null } Insert: { + comprimento?: number | null created_at?: string descricao?: string | null id?: string @@ -469,6 +471,7 @@ export type Database = { user_id?: string | null } Update: { + comprimento?: number | null created_at?: string descricao?: string | null id?: string @@ -2095,6 +2098,7 @@ export type Database = { } pecas: { Row: { + comprimento: number | null created_at: string descricao: string | null etapa_fase: string | null @@ -2114,6 +2118,7 @@ export type Database = { user_id: string | null } Insert: { + comprimento?: number | null created_at?: string descricao?: string | null etapa_fase?: string | null @@ -2133,6 +2138,7 @@ export type Database = { user_id?: string | null } Update: { + comprimento?: number | null created_at?: string descricao?: string | null etapa_fase?: string | null @@ -2737,6 +2743,7 @@ export type Database = { } romaneios_expedicao: { Row: { + comprimento_maximo_veiculo: number | null created_at: string created_by: string | null data_criacao: string | null @@ -2759,6 +2766,7 @@ export type Database = { updated_at: string } Insert: { + comprimento_maximo_veiculo?: number | null created_at?: string created_by?: string | null data_criacao?: string | null @@ -2781,6 +2789,7 @@ export type Database = { updated_at?: string } Update: { + comprimento_maximo_veiculo?: number | null created_at?: string created_by?: string | null data_criacao?: string | null