🚀 Initial commit: Versão atual do TrackSteel APP

This commit is contained in:
2026-03-18 21:17:53 +00:00
commit bde410c9ad
633 changed files with 108150 additions and 0 deletions
@@ -0,0 +1,789 @@
import React, { useState, useEffect, useMemo } from 'react';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
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 { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Badge } from '@/components/ui/badge';
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 { RomaneioExpedicao } from '@/hooks/useRomaneios';
import { useProcessosFabricacao } from '@/hooks/useProcessosFabricacao';
import { usePecas } from '@/hooks/usePecas';
import { useMobileResponsive } from '@/hooks/useMobileResponsive';
import { supabase } from '@/integrations/supabase/client';
import { PecasRomaneioExpandido } from './PecasRomaneioExpandido';
interface ItensRomaneioModalProps {
romaneio: RomaneioExpedicao;
isOpen: boolean;
onClose: () => void;
}
interface PecaDisponivel {
id: string;
marca: string;
descricao: string;
etapa_fase: string;
quantidade_disponivel: number;
peso_unitario: number;
prioridade: string;
}
interface ItemRomaneioPeca {
id: string;
peca_id: string;
quantidade: number;
peso_unitario: number;
peso_total: number;
marca: string;
descricao: string;
etapa_fase: string;
prioridade: string;
}
interface ItemRomaneioInsumo {
id: string;
nome: string;
quantidade: number;
unidade: string;
}
export const ItensRomaneioModal: React.FC<ItensRomaneioModalProps> = ({
romaneio,
isOpen,
onClose
}) => {
const { isMobile, isTablet } = useMobileResponsive();
const { data: processos } = useProcessosFabricacao();
const { pecas: todasPecas } = usePecas();
// Estados para filtros
const [processoSelecionado, setProcessoSelecionado] = useState('Pintura/Galv');
const [faseFilter, setFaseFilter] = useState('Todas as fases');
const [marcaFilter, setMarcaFilter] = useState('');
const [pecaDisponivel, setPecaDisponivel] = useState('');
const [quantidadePeca, setQuantidadePeca] = useState('');
// Estados para dados dinâmicos
const [pecasDisponiveis, setPecasDisponiveis] = useState<PecaDisponivel[]>([]);
const [fasesDisponiveis, setFasesDisponiveis] = useState<string[]>([]);
const [marcasDisponiveis, setMarcasDisponiveis] = useState<string[]>([]);
const [loading, setLoading] = useState(false);
// Estados para itens do romaneio
const [itensPeca, setItensPeca] = useState<ItemRomaneioPeca[]>([]);
const [itensInsumo, setItensInsumo] = useState<ItemRomaneioInsumo[]>([]);
const [loadingItens, setLoadingItens] = useState(false);
// Estados para insumos
const [nomeInsumo, setNomeInsumo] = useState('');
const [quantidadeInsumo, setQuantidadeInsumo] = useState('');
const [unidadeInsumo, setUnidadeInsumo] = useState('');
// Estados para adicionar todas as peças
const [adicionarTodas, setAdicionarTodas] = useState(false);
// Novo estado para o modal expandido
const [showExpandedModal, setShowExpandedModal] = useState(false);
// Buscar peças disponíveis baseadas no processo selecionado
const buscarPecasDisponiveis = async (processo: string) => {
if (!romaneio.of_number) return;
setLoading(true);
try {
// Buscar peças que estão no processo selecionado e disponíveis para expedição
const { data: apontamentosData, error } = await supabase
.from('apontamentos_producao')
.select(`
peca_id,
quantidade_produzida,
processo_id,
processos_fabricacao!inner(nome),
pecas!inner(
id,
marca,
descricao,
etapa_fase,
peso_unitario,
prioridade,
of_number
)
`)
.eq('pecas.of_number', romaneio.of_number)
.eq('processos_fabricacao.nome', processo);
if (error) {
console.error('Erro ao buscar peças disponíveis:', error);
return;
}
// Agrupar por peça e calcular quantidade disponível
const pecasAgrupadas = new Map<string, PecaDisponivel>();
apontamentosData?.forEach(apontamento => {
const pecaId = apontamento.pecas.id;
const quantidade = apontamento.quantidade_produzida || 0;
if (pecasAgrupadas.has(pecaId)) {
const pecaExistente = pecasAgrupadas.get(pecaId)!;
pecaExistente.quantidade_disponivel += quantidade;
} else {
pecasAgrupadas.set(pecaId, {
id: apontamento.pecas.id,
marca: apontamento.pecas.marca,
descricao: apontamento.pecas.descricao || '',
etapa_fase: apontamento.pecas.etapa_fase || '',
quantidade_disponivel: quantidade,
peso_unitario: apontamento.pecas.peso_unitario || 0,
prioridade: apontamento.pecas.prioridade || 'P4'
});
}
});
const pecasArray = Array.from(pecasAgrupadas.values());
setPecasDisponiveis(pecasArray);
// Extrair fases e marcas únicas
const fasesUnicas = [...new Set(pecasArray.map(p => p.etapa_fase).filter(Boolean))];
const marcasUnicas = [...new Set(pecasArray.map(p => p.marca).filter(Boolean))];
setFasesDisponiveis(fasesUnicas);
setMarcasDisponiveis(marcasUnicas);
} catch (error) {
console.error('Erro ao buscar peças disponíveis:', error);
toast.error('Erro ao carregar peças disponíveis');
} finally {
setLoading(false);
}
};
// Carregar itens existentes do romaneio
const carregarItensRomaneio = async () => {
setLoadingItens(true);
try {
// Carregar peças do romaneio - usando a tabela correta
const { data: pecasData, error: pecasError } = await supabase
.from('itens_romaneio_pecas')
.select(`
*,
pecas!inner(marca, descricao, etapa_fase, peso_unitario, prioridade)
`)
.eq('romaneio_id', romaneio.id);
if (pecasError) {
console.error('Erro ao carregar peças do romaneio:', pecasError);
} else if (pecasData) {
const itensPecaFormatados = pecasData.map((item: any) => ({
id: item.id,
peca_id: item.peca_id,
quantidade: item.quantidade_expedida,
peso_unitario: item.peso_unitario,
peso_total: item.peso_total,
marca: item.marca,
descricao: item.descricao || '',
etapa_fase: item.fase || '',
prioridade: 'P4'
}));
setItensPeca(itensPecaFormatados);
}
// Carregar insumos do romaneio - usando a tabela correta
const { data: insumosData, error: insumosError } = await supabase
.from('itens_romaneio_insumos')
.select('*')
.eq('romaneio_id', romaneio.id);
if (insumosError) {
console.error('Erro ao carregar insumos do romaneio:', insumosError);
} else if (insumosData) {
const itensInsumoFormatados = insumosData.map((item: any) => ({
id: item.id,
nome: item.descricao,
quantidade: item.quantidade_expedida,
unidade: item.unidade
}));
setItensInsumo(itensInsumoFormatados);
}
} catch (error) {
console.error('Erro ao carregar itens do romaneio:', error);
toast.error('Erro ao carregar itens do romaneio');
} finally {
setLoadingItens(false);
}
};
// Carregar peças quando processo selecionado mudar
useEffect(() => {
if (processoSelecionado && isOpen) {
buscarPecasDisponiveis(processoSelecionado);
}
}, [processoSelecionado, isOpen, romaneio.of_number]);
// Definir processo padrão quando abrir o modal
useEffect(() => {
if (isOpen) {
setProcessoSelecionado('Pintura/Galv');
setFaseFilter('Todas as fases');
setMarcaFilter('');
setPecaDisponivel('');
setQuantidadePeca('');
setAdicionarTodas(false);
carregarItensRomaneio();
}
}, [isOpen]);
// Filtrar peças baseado nos filtros selecionados
const pecasFiltradas = useMemo(() => {
let filtradas = [...pecasDisponiveis];
if (faseFilter !== 'Todas as fases') {
filtradas = filtradas.filter(p => p.etapa_fase === faseFilter);
}
if (marcaFilter.trim()) {
filtradas = filtradas.filter(p =>
p.marca.toLowerCase().includes(marcaFilter.toLowerCase())
);
}
return filtradas.sort((a, b) => {
const aNum = a.marca.match(/\d+/);
const bNum = b.marca.match(/\d+/);
if (aNum && bNum) {
return parseInt(aNum[0]) - parseInt(bNum[0]);
}
return a.marca.localeCompare(b.marca);
});
}, [pecasDisponiveis, faseFilter, marcaFilter]);
const handleAdicionarPeca = async () => {
if (adicionarTodas) {
// Adicionar todas as peças filtradas com suas quantidades disponíveis
for (const peca of pecasFiltradas) {
try {
const { error } = await supabase
.from('itens_romaneio_pecas')
.insert({
romaneio_id: romaneio.id,
peca_id: peca.id,
quantidade_expedida: peca.quantidade_disponivel,
peso_unitario: peca.peso_unitario,
peso_total: peca.quantidade_disponivel * peca.peso_unitario,
marca: peca.marca,
descricao: peca.descricao,
fase: peca.etapa_fase
});
if (error) throw error;
} catch (error) {
console.error('Erro ao adicionar peça:', error);
toast.error(`Erro ao adicionar peça ${peca.marca}`);
}
}
toast.success('Todas as peças foram adicionadas ao romaneio');
} else {
// Adicionar peça individual
if (!pecaDisponivel || !quantidadePeca) {
toast.error('Selecione uma peça e informe a quantidade');
return;
}
const pecaSelecionada = pecasDisponiveis.find(p => p.id === pecaDisponivel);
if (!pecaSelecionada) {
toast.error('Peça não encontrada');
return;
}
try {
const quantidade = parseInt(quantidadePeca);
const { error } = await supabase
.from('itens_romaneio_pecas')
.insert({
romaneio_id: romaneio.id,
peca_id: pecaDisponivel,
quantidade_expedida: quantidade,
peso_unitario: pecaSelecionada.peso_unitario,
peso_total: quantidade * pecaSelecionada.peso_unitario,
marca: pecaSelecionada.marca,
descricao: pecaSelecionada.descricao,
fase: pecaSelecionada.etapa_fase
});
if (error) throw error;
toast.success('Peça adicionada ao romaneio');
} catch (error) {
console.error('Erro ao adicionar peça:', error);
toast.error('Erro ao adicionar peça ao romaneio');
}
}
// Recarregar itens e limpar formulário
carregarItensRomaneio();
setPecaDisponivel('');
setQuantidadePeca('');
setAdicionarTodas(false);
};
const handleAdicionarInsumo = async () => {
if (!nomeInsumo || !quantidadeInsumo || !unidadeInsumo) {
toast.error('Preencha todos os campos do insumo');
return;
}
try {
const { error } = await supabase
.from('itens_romaneio_insumos')
.insert({
romaneio_id: romaneio.id,
descricao: nomeInsumo,
quantidade_expedida: parseFloat(quantidadeInsumo),
unidade: unidadeInsumo,
tipo_insumo: 'Manual'
});
if (error) throw error;
toast.success('Insumo adicionado ao romaneio');
setNomeInsumo('');
setQuantidadeInsumo('');
setUnidadeInsumo('');
carregarItensRomaneio();
} catch (error) {
console.error('Erro ao adicionar insumo:', error);
toast.error('Erro ao adicionar insumo ao romaneio');
}
};
const handleRemoverItemPeca = async (itemId: string) => {
try {
const { error } = await supabase
.from('itens_romaneio_pecas')
.delete()
.eq('id', itemId);
if (error) throw error;
toast.success('Item removido do romaneio');
carregarItensRomaneio();
} catch (error) {
console.error('Erro ao remover item:', error);
toast.error('Erro ao remover item do romaneio');
}
};
const handleRemoverItemInsumo = async (itemId: string) => {
try {
const { error } = await supabase
.from('itens_romaneio_insumos')
.delete()
.eq('id', itemId);
if (error) throw error;
toast.success('Insumo removido do romaneio');
carregarItensRomaneio();
} catch (error) {
console.error('Erro ao remover insumo:', error);
toast.error('Erro ao remover insumo do romaneio');
}
};
const getPrioridadeColor = (prioridade: string) => {
switch (prioridade) {
case 'P1': return 'bg-red-100 text-red-800';
case 'P2': return 'bg-orange-100 text-orange-800';
case 'P3': return 'bg-blue-100 text-blue-800';
default: return 'bg-gray-100 text-gray-800';
}
};
const qtdTotalPecas = itensPeca.reduce((total, item) => total + item.quantidade, 0);
const pesoTotalPecas = itensPeca.reduce((total, item) => total + item.peso_total, 0);
return (
<>
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className={`
${isMobile
? 'w-[95vw] max-w-none h-[95vh] m-2'
: isTablet
? 'w-[90vw] max-w-4xl max-h-[85vh]'
: 'max-w-6xl max-h-[90vh]'
}
overflow-hidden flex flex-col
`}>
<DialogHeader className="pb-3 shrink-0">
<DialogTitle className={`flex items-center gap-2 ${isMobile ? 'text-base' : 'text-lg'}`}>
<Package className={`${isMobile ? 'h-4 w-4' : 'h-5 w-5'}`} />
<span className="truncate">
Itens do Romaneio {romaneio.numero_romaneio} - OF: {romaneio.of_number}
</span>
</DialogTitle>
</DialogHeader>
<ScrollArea className="flex-1 overflow-auto">
<div className="px-1">
<Tabs defaultValue="pecas" className="flex flex-col h-full">
<TabsList className={`grid w-full grid-cols-2 ${isMobile ? 'h-10' : 'h-11'} mb-4`}>
<TabsTrigger value="pecas" className={isMobile ? 'text-sm' : 'text-base'}>
Peças
</TabsTrigger>
<TabsTrigger value="insumos" className={isMobile ? 'text-sm' : 'text-base'}>
Insumos
</TabsTrigger>
</TabsList>
<TabsContent value="pecas" className="flex-1 space-y-3">
{/* Seção Adicionar Peça */}
<Card className="shrink-0">
<CardContent className={`${isMobile ? 'p-3' : 'p-4'}`}>
<h3 className={`font-semibold mb-3 ${isMobile ? 'text-base' : 'text-lg'}`}>
Adicionar Peça
</h3>
{/* Filtro de Processo */}
<div className="grid grid-cols-1 gap-3 mb-3">
<div>
<Label className={`flex items-center gap-2 font-medium mb-2 ${isMobile ? 'text-sm' : 'text-sm'}`}>
<Wrench className="h-4 w-4" />
Processo * (Obrigatório)
</Label>
<Select
value={processoSelecionado}
onValueChange={(value) => {
setProcessoSelecionado(value);
setFaseFilter('Todas as fases');
setMarcaFilter('');
setPecaDisponivel('');
}}
>
<SelectTrigger className={isMobile ? 'h-10' : 'h-11'}>
<SelectValue placeholder="Selecione o processo" />
</SelectTrigger>
<SelectContent>
{processos?.map((processo) => (
<SelectItem key={processo.id} value={processo.nome}>
{processo.nome}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
{/* Filtros dinâmicos */}
<div className={`grid gap-3 mb-3 ${isMobile ? 'grid-cols-1' : 'grid-cols-1 md:grid-cols-2'}`}>
<div>
<Label className={`font-medium mb-2 block ${isMobile ? 'text-sm' : 'text-sm'}`}>
Filtrar por Fase
</Label>
<Select value={faseFilter} onValueChange={setFaseFilter}>
<SelectTrigger className={isMobile ? 'h-10' : 'h-11'}>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="Todas as fases">Todas as fases</SelectItem>
{fasesDisponiveis.map((fase) => (
<SelectItem key={fase} value={fase}>
{fase}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label className={`font-medium mb-2 block ${isMobile ? 'text-sm' : 'text-sm'}`}>
Filtrar por Marca
</Label>
<Input
value={marcaFilter}
onChange={(e) => setMarcaFilter(e.target.value)}
placeholder="Digite a marca da peça..."
className={isMobile ? 'h-10' : 'h-11'}
/>
</div>
</div>
{/* Opção para adicionar todas as peças */}
<div className="flex items-start space-x-2 mb-3">
<Checkbox
id="adicionar-todas"
checked={adicionarTodas}
onCheckedChange={(checked) => setAdicionarTodas(checked as boolean)}
className="mt-1 shrink-0"
/>
<Label
htmlFor="adicionar-todas"
className={`leading-relaxed ${isMobile ? 'text-sm' : 'text-sm'}`}
>
Adicionar todas as peças listadas com suas respectivas quantidades
</Label>
</div>
{!adicionarTodas && (
<>
{/* Seletor de peça disponível */}
<div className={`grid gap-3 mb-3 ${isMobile ? 'grid-cols-1' : 'grid-cols-1 md:grid-cols-2'}`}>
<div>
<Label className={`font-medium mb-2 block ${isMobile ? 'text-sm' : 'text-sm'}`}>
Peça Disponível
</Label>
<Select value={pecaDisponivel} onValueChange={setPecaDisponivel}>
<SelectTrigger className={isMobile ? 'h-10' : 'h-11'}>
<SelectValue placeholder="Selecionar peça" />
</SelectTrigger>
<SelectContent>
{loading ? (
<SelectItem value="loading" disabled>Carregando...</SelectItem>
) : (
<>
{pecasFiltradas.length === 0 ? (
<SelectItem value="empty" disabled>
Nenhuma peça disponível para este processo
</SelectItem>
) : (
pecasFiltradas.map((peca) => (
<SelectItem key={peca.id} value={peca.id}>
<span className="truncate">
{peca.marca} - {peca.descricao} (Qtd: {peca.quantidade_disponivel})
</span>
</SelectItem>
))
)}
</>
)}
</SelectContent>
</Select>
</div>
<div>
<Label className={`font-medium mb-2 block ${isMobile ? 'text-sm' : 'text-sm'}`}>
Quantidade
</Label>
<Input
type="number"
min="1"
value={quantidadePeca}
onChange={(e) => setQuantidadePeca(e.target.value)}
placeholder="Quantidade"
className={isMobile ? 'h-10' : 'h-11'}
/>
</div>
</div>
</>
)}
<Button
onClick={handleAdicionarPeca}
disabled={(!pecaDisponivel && !adicionarTodas) || loading}
className={`w-full ${isMobile ? 'h-10 text-sm' : 'h-11'}`}
>
<Plus className="h-4 w-4 mr-2" />
{adicionarTodas ? 'Adicionar Todas as Peças' : 'Adicionar Peça'}
</Button>
</CardContent>
</Card>
{/* Seção de resumo das peças no romaneio */}
<Card className="shrink-0">
<CardContent className={`${isMobile ? 'p-3' : 'p-4'}`}>
<div className={`flex justify-between items-start mb-3 ${isMobile ? 'flex-col gap-2' : 'flex-row'}`}>
<h4 className={`font-semibold ${isMobile ? 'text-sm' : 'text-md'}`}>
Peças no Romaneio
</h4>
<div className={`flex gap-2 ${isMobile ? 'text-xs flex-wrap' : 'text-sm'}`}>
<Badge variant="outline" className={isMobile ? 'text-xs px-2 py-1' : ''}>
Qtd Total: {qtdTotalPecas}
</Badge>
<Badge variant="outline" className={isMobile ? 'text-xs px-2 py-1' : ''}>
Peso Total: {pesoTotalPecas.toFixed(2)} kg
</Badge>
</div>
</div>
<div className="flex justify-center">
<Button
onClick={() => setShowExpandedModal(true)}
className={`bg-red-600 hover:bg-red-700 text-white ${isMobile ? 'h-10 px-4 text-sm' : 'px-6 py-2'}`}
>
<Expand className="h-4 w-4 mr-2" />
Expandir
</Button>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="insumos" className="flex-1 space-y-3">
{/* Seção Adicionar Insumo */}
<Card className="shrink-0">
<CardContent className={`${isMobile ? 'p-3' : 'p-4'}`}>
<h3 className={`font-semibold mb-3 ${isMobile ? 'text-base' : 'text-lg'}`}>
Adicionar Insumo
</h3>
<div className={`grid gap-3 mb-3 ${isMobile ? 'grid-cols-1' : 'grid-cols-1 md:grid-cols-3'}`}>
<div>
<Label className={`font-medium mb-2 block ${isMobile ? 'text-sm' : 'text-sm'}`}>
Nome do Insumo
</Label>
<Input
value={nomeInsumo}
onChange={(e) => setNomeInsumo(e.target.value)}
placeholder="Nome do insumo"
className={isMobile ? 'h-10' : 'h-11'}
/>
</div>
<div>
<Label className={`font-medium mb-2 block ${isMobile ? 'text-sm' : 'text-sm'}`}>
Quantidade
</Label>
<Input
type="number"
min="0"
step="0.01"
value={quantidadeInsumo}
onChange={(e) => setQuantidadeInsumo(e.target.value)}
placeholder="Quantidade"
className={isMobile ? 'h-10' : 'h-11'}
/>
</div>
<div>
<Label className={`font-medium mb-2 block ${isMobile ? 'text-sm' : 'text-sm'}`}>
Unidade
</Label>
<Input
value={unidadeInsumo}
onChange={(e) => setUnidadeInsumo(e.target.value)}
placeholder="Ex: kg, m, unid"
className={isMobile ? 'h-10' : 'h-11'}
/>
</div>
</div>
<Button
onClick={handleAdicionarInsumo}
disabled={!nomeInsumo || !quantidadeInsumo || !unidadeInsumo}
className={`w-full ${isMobile ? 'h-10 text-sm' : 'h-11'}`}
>
<Plus className="h-4 w-4 mr-2" />
Adicionar Insumo
</Button>
</CardContent>
</Card>
{/* Lista de insumos no romaneio */}
<Card className="flex-1">
<CardContent className={`${isMobile ? 'p-3' : 'p-4'} h-full flex flex-col`}>
<h4 className={`font-semibold mb-3 ${isMobile ? 'text-sm' : 'text-md'}`}>
Insumos no Romaneio
</h4>
<ScrollArea className="flex-1 min-h-[200px]">
{loadingItens ? (
<div className="text-center py-8">
<div className={`${isMobile ? 'text-sm' : 'text-base'}`}>Carregando...</div>
</div>
) : itensInsumo.length === 0 ? (
<div className={`text-center py-8 text-gray-500 ${isMobile ? 'text-sm' : 'text-base'}`}>
Nenhum insumo adicionado
</div>
) : (
<div className="space-y-2 pr-2">
<div className={`grid gap-2 font-medium text-gray-600 border-b pb-2 ${
isMobile
? 'grid-cols-2 text-xs'
: 'grid-cols-4 text-xs'
}`}>
<div>Nome</div>
{!isMobile && <div>Quantidade</div>}
{!isMobile && <div>Unidade</div>}
<div className={isMobile ? 'text-center' : ''}>
{isMobile ? 'Qtd/Unid/Ações' : 'Ações'}
</div>
</div>
{itensInsumo.map((item) => (
<div
key={item.id}
className={`grid gap-2 border-b py-2 ${
isMobile
? 'grid-cols-2 text-sm'
: 'grid-cols-4 text-sm'
}`}
>
<div className="font-medium truncate">{item.nome}</div>
{isMobile ? (
<div className="text-right">
<div className="text-xs text-gray-600">
{item.quantidade} {item.unidade}
</div>
<Button
size="sm"
variant="destructive"
onClick={() => handleRemoverItemInsumo(item.id)}
className="mt-1 h-6 w-6 p-0"
>
×
</Button>
</div>
) : (
<>
<div className="text-center">{item.quantidade}</div>
<div>{item.unidade}</div>
<div className="text-center">
<Button
size="sm"
variant="destructive"
onClick={() => handleRemoverItemInsumo(item.id)}
className="h-7 w-7 p-0"
>
×
</Button>
</div>
</>
)}
</div>
))}
</div>
)}
</ScrollArea>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
</ScrollArea>
<div className={`flex justify-end pt-3 border-t shrink-0 ${isMobile ? 'px-1' : ''}`}>
<Button
variant="outline"
onClick={onClose}
className={isMobile ? 'h-10 px-4 text-sm' : ''}
>
Fechar
</Button>
</div>
</DialogContent>
</Dialog>
{/* Modal expandido para peças */}
<PecasRomaneioExpandido
isOpen={showExpandedModal}
onClose={() => setShowExpandedModal(false)}
romaneio={romaneio}
onRefresh={carregarItensRomaneio}
/>
</>
);
};