238 lines
9.8 KiB
TypeScript
238 lines
9.8 KiB
TypeScript
|
|
import React, { useState } from 'react';
|
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Search, Package } from 'lucide-react';
|
|
import { useApontamentosProducao } from '@/hooks/useApontamentosProducao';
|
|
import { format } from 'date-fns';
|
|
import { ptBR } from 'date-fns/locale';
|
|
|
|
export const ApontamentosList = () => {
|
|
const { apontamentos, loading } = useApontamentosProducao();
|
|
const [filtroOF, setFiltroOF] = useState('');
|
|
const [filtroProcesso, setFiltroProcesso] = useState('');
|
|
const [filtroData, setFiltroData] = useState('');
|
|
const [filtroPecaComponente, setFiltroPecaComponente] = useState('');
|
|
|
|
const apontamentosFiltrados = apontamentos.filter(apt => {
|
|
const matchOF = !filtroOF || apt.of_number.toLowerCase().includes(filtroOF.toLowerCase());
|
|
const matchProcesso = !filtroProcesso || apt.processo?.nome.toLowerCase().includes(filtroProcesso.toLowerCase());
|
|
const matchData = !filtroData || apt.data_apontamento.includes(filtroData);
|
|
|
|
// Filtro por peça/componente - busca na marca da peça ou componente
|
|
const matchPecaComponente = !filtroPecaComponente ||
|
|
getMarcaExibida(apt).toLowerCase().includes(filtroPecaComponente.toLowerCase());
|
|
|
|
return matchOF && matchProcesso && matchData && matchPecaComponente;
|
|
});
|
|
|
|
// Função para determinar a marca exibida baseada no tipo de apontamento
|
|
const getMarcaExibida = (apontamento: any) => {
|
|
if (apontamento.tipo_apontamento === 'componente' && apontamento.componente?.marca_componente) {
|
|
return apontamento.componente.marca_componente;
|
|
}
|
|
if (apontamento.tipo_apontamento === 'peca' && apontamento.peca?.marca) {
|
|
return apontamento.peca.marca;
|
|
}
|
|
return 'N/A';
|
|
};
|
|
|
|
// Função para calcular peso total baseado no tipo correto (peça ou componente)
|
|
const calcularPesoTotal = (apontamento: any) => {
|
|
let pesoUnitario = 0;
|
|
|
|
if (apontamento.tipo_apontamento === 'componente' && apontamento.componente?.peso_unitario) {
|
|
pesoUnitario = Number(apontamento.componente.peso_unitario);
|
|
} else if (apontamento.tipo_apontamento === 'peca' && apontamento.peca?.peso_unitario) {
|
|
pesoUnitario = Number(apontamento.peca.peso_unitario);
|
|
}
|
|
|
|
const quantidade = Number(apontamento.quantidade_produzida) || 0;
|
|
const pesoTotal = pesoUnitario * quantidade;
|
|
|
|
console.log(`Apontamento ${apontamento.id}: Tipo: ${apontamento.tipo_apontamento}, Peso unitário: ${pesoUnitario}, Quantidade: ${quantidade}, Peso total: ${pesoTotal}`);
|
|
|
|
return pesoTotal.toFixed(3);
|
|
};
|
|
|
|
// Função para obter a descrição correta baseada no tipo
|
|
const getDescricaoExibida = (apontamento: any) => {
|
|
if (apontamento.tipo_apontamento === 'componente' && apontamento.componente?.descricao) {
|
|
return apontamento.componente.descricao;
|
|
}
|
|
if (apontamento.tipo_apontamento === 'peca' && apontamento.peca?.descricao) {
|
|
return apontamento.peca.descricao;
|
|
}
|
|
return '';
|
|
};
|
|
|
|
// Calcular totais para debug
|
|
const pesoTotalApontamentos = apontamentosFiltrados.reduce((total, apt) => {
|
|
return total + Number(calcularPesoTotal(apt));
|
|
}, 0);
|
|
|
|
console.log('Total de peso dos apontamentos filtrados:', pesoTotalApontamentos);
|
|
|
|
if (loading) {
|
|
return <div className="text-center py-8">Carregando apontamentos...</div>;
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Filtros */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Search className="h-4 w-4" />
|
|
Filtros
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="grid grid-cols-1 md:grid-cols-5 gap-3">
|
|
<div>
|
|
<label className="text-sm font-medium mb-2 block">OF</label>
|
|
<Input
|
|
placeholder="Filtrar por OF..."
|
|
value={filtroOF}
|
|
onChange={(e) => setFiltroOF(e.target.value)}
|
|
className="h-8"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="text-sm font-medium mb-2 block">Processo</label>
|
|
<Input
|
|
placeholder="Filtrar por processo..."
|
|
value={filtroProcesso}
|
|
onChange={(e) => setFiltroProcesso(e.target.value)}
|
|
className="h-8"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="text-sm font-medium mb-2 block">Data</label>
|
|
<Input
|
|
type="date"
|
|
value={filtroData}
|
|
onChange={(e) => setFiltroData(e.target.value)}
|
|
className="h-8"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="text-sm font-medium mb-2 block">Peça/Componente</label>
|
|
<Input
|
|
placeholder="Filtrar por peça/componente..."
|
|
value={filtroPecaComponente}
|
|
onChange={(e) => setFiltroPecaComponente(e.target.value)}
|
|
className="h-8"
|
|
/>
|
|
</div>
|
|
<div className="flex flex-col items-start">
|
|
<div className="text-xs text-muted-foreground">
|
|
{apontamentosFiltrados.length} registros encontrados
|
|
</div>
|
|
<div className="text-xs text-muted-foreground mt-1">
|
|
Peso total: {pesoTotalApontamentos.toFixed(3)} kg
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Tabela de Apontamentos */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center justify-between">
|
|
<div className="flex items-center gap-2">
|
|
<Package className="h-4 w-4" />
|
|
Histórico de Apontamentos
|
|
</div>
|
|
<Badge variant="secondary">
|
|
{apontamentosFiltrados.length} registros
|
|
</Badge>
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{apontamentosFiltrados.length === 0 ? (
|
|
<div className="text-center py-8 text-muted-foreground">
|
|
Nenhum apontamento encontrado
|
|
</div>
|
|
) : (
|
|
<div className="overflow-x-auto">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>OF</TableHead>
|
|
<TableHead>Peça/Componente</TableHead>
|
|
<TableHead>Tipo</TableHead>
|
|
<TableHead>Processo</TableHead>
|
|
<TableHead className="text-center">Quantidade</TableHead>
|
|
<TableHead className="text-center">Peso Unit. (kg)</TableHead>
|
|
<TableHead className="text-center">Peso Total (kg)</TableHead>
|
|
<TableHead>Data</TableHead>
|
|
<TableHead>Observações</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{apontamentosFiltrados.map((apontamento) => {
|
|
const pesoUnitario = apontamento.tipo_apontamento === 'componente'
|
|
? Number(apontamento.componente?.peso_unitario || 0)
|
|
: Number(apontamento.peca?.peso_unitario || 0);
|
|
|
|
return (
|
|
<TableRow key={apontamento.id} className="h-9">
|
|
<TableCell className="font-medium py-1">
|
|
{apontamento.of_number}
|
|
</TableCell>
|
|
<TableCell className="py-1">
|
|
<div className="font-medium">
|
|
{getMarcaExibida(apontamento)}
|
|
</div>
|
|
{getDescricaoExibida(apontamento) && (
|
|
<div className="text-xs text-muted-foreground">
|
|
{getDescricaoExibida(apontamento)}
|
|
</div>
|
|
)}
|
|
</TableCell>
|
|
<TableCell className="py-1">
|
|
<Badge variant={apontamento.tipo_apontamento === 'componente' ? 'secondary' : 'default'}>
|
|
{apontamento.tipo_apontamento === 'componente' ? 'Componente' : 'Peça'}
|
|
</Badge>
|
|
</TableCell>
|
|
<TableCell className="py-1">
|
|
<Badge variant="outline">
|
|
{apontamento.processo?.nome || 'N/A'}
|
|
</Badge>
|
|
</TableCell>
|
|
<TableCell className="text-center font-medium py-1">
|
|
{apontamento.quantidade_produzida}
|
|
</TableCell>
|
|
<TableCell className="text-center py-1">
|
|
{pesoUnitario.toFixed(3)}
|
|
</TableCell>
|
|
<TableCell className="text-center font-medium py-1">
|
|
{calcularPesoTotal(apontamento)}
|
|
</TableCell>
|
|
<TableCell className="py-1">
|
|
{format(new Date(apontamento.data_apontamento), 'dd/MM/yyyy', {
|
|
locale: ptBR
|
|
})}
|
|
</TableCell>
|
|
<TableCell className="max-w-xs py-1">
|
|
<div className="text-sm text-muted-foreground truncate">
|
|
{apontamento.observacoes || '-'}
|
|
</div>
|
|
</TableCell>
|
|
</TableRow>
|
|
);
|
|
})}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
};
|