248 lines
7.9 KiB
TypeScript
248 lines
7.9 KiB
TypeScript
|
|
import React from 'react';
|
|
import { Table, TableBody, TableHeader } from '@/components/ui/table';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Package, FileText, AlertTriangle } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Peca } from '@/hooks/usePecas';
|
|
import { usePecasTable } from '@/hooks/usePecasTable';
|
|
import { TableFilters } from './table/TableFilters';
|
|
import { TableActions } from './table/TableActions';
|
|
import { TableHeaderSortable } from './table/TableHeaderSortable';
|
|
import { PDFGenerator } from './table/PDFGenerator';
|
|
import { BatchEditButton } from './table/BatchEditButton';
|
|
import { TableRow as PecaTableRow } from './table/TableRow';
|
|
import { EmptyState } from './table/EmptyState';
|
|
import { toast } from 'sonner';
|
|
|
|
interface PecasTableProps {
|
|
pecas: Peca[];
|
|
onOpenComponentPopup: (pecaId: string) => void;
|
|
onDeletePeca?: (pecaId: string) => void;
|
|
onDeletePecasBatch?: (pecaIds: string[]) => Promise<any>;
|
|
onEditPeca?: (peca: Peca) => void;
|
|
onDeleteLastImport?: () => void;
|
|
onBatchUpdatePecas?: (pecaIds: string[], updates: any) => Promise<void>;
|
|
onOpenRevisaoPeca?: (peca: Peca) => void;
|
|
onOpenPainelRevisoes?: () => void;
|
|
totalRevisoesOF?: number;
|
|
hasRecentImport: boolean;
|
|
canEdit?: boolean;
|
|
canDelete?: boolean;
|
|
ofNumber?: string;
|
|
}
|
|
|
|
export function PecasTable({
|
|
pecas,
|
|
onOpenComponentPopup,
|
|
onDeletePeca,
|
|
onDeletePecasBatch,
|
|
onEditPeca,
|
|
onDeleteLastImport,
|
|
onBatchUpdatePecas,
|
|
onOpenRevisaoPeca,
|
|
onOpenPainelRevisoes,
|
|
totalRevisoesOF,
|
|
hasRecentImport,
|
|
canEdit = true,
|
|
canDelete = true,
|
|
ofNumber = ''
|
|
}: PecasTableProps) {
|
|
const {
|
|
ofFilter,
|
|
setOfFilter,
|
|
faseFilter,
|
|
setFaseFilter,
|
|
pecaFilter,
|
|
setPecaFilter,
|
|
descricaoFilter,
|
|
setDescricaoFilter,
|
|
priorityFilter,
|
|
setPriorityFilter,
|
|
semComponentesFilter,
|
|
setSemComponentesFilter,
|
|
sortField,
|
|
sortOrder,
|
|
handleSort,
|
|
filteredAndSortedPecas,
|
|
selectedPecas,
|
|
selectAll,
|
|
handleSelectAll,
|
|
handleSelectPeca,
|
|
searchPecasByComponent,
|
|
pesoTotal
|
|
} = usePecasTable(pecas);
|
|
|
|
const handleExportCSV = () => {
|
|
const selectedPecasArray = Array.from(selectedPecas);
|
|
const pecasToExport = pecas.filter(peca => selectedPecasArray.includes(peca.id));
|
|
|
|
if (pecasToExport.length === 0) {
|
|
toast.error('Nenhuma peça selecionada para exportar.');
|
|
return;
|
|
}
|
|
|
|
const csvContent = "data:text/csv;charset=utf-8," +
|
|
[
|
|
Object.keys(pecasToExport[0]).join(','),
|
|
...pecasToExport.map(peca => Object.values(peca).join(','))
|
|
].join('\n');
|
|
|
|
const encodedUri = encodeURI(csvContent);
|
|
const link = document.createElement("a");
|
|
link.setAttribute("href", encodedUri);
|
|
link.setAttribute("download", "pecas.csv");
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
};
|
|
|
|
const handleDeleteSelected = async () => {
|
|
if (!canDelete || (!onDeletePecasBatch && !onDeletePeca)) {
|
|
toast.error('Você não tem permissão para excluir peças');
|
|
return;
|
|
}
|
|
|
|
if (window.confirm(`Tem certeza que deseja excluir ${selectedPecas.size} peças selecionadas e seus vínculos?`)) {
|
|
try {
|
|
const pecaIdsArray = Array.from(selectedPecas);
|
|
if (onDeletePecasBatch) {
|
|
await onDeletePecasBatch(pecaIdsArray);
|
|
} else if (onDeletePeca) {
|
|
for (const pecaId of pecaIdsArray) {
|
|
await onDeletePeca(pecaId);
|
|
}
|
|
toast.success('Peças excluídas com sucesso!');
|
|
}
|
|
} catch (error) {
|
|
console.error('Erro ao excluir peças:', error);
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleBatchUpdate = async (updates: any) => {
|
|
if (!canEdit || !onBatchUpdatePecas) {
|
|
toast.error('Você não tem permissão para editar peças');
|
|
throw new Error('Sem permissão');
|
|
}
|
|
|
|
const selectedPecasArray = Array.from(selectedPecas);
|
|
await onBatchUpdatePecas(selectedPecasArray, updates);
|
|
};
|
|
|
|
const handleEditPeca = (peca: Peca) => {
|
|
if (!onEditPeca) {
|
|
return;
|
|
}
|
|
onEditPeca(peca);
|
|
};
|
|
|
|
const handleDeletePeca = (pecaId: string) => {
|
|
if (!canDelete || !onDeletePeca) {
|
|
toast.error('Você não tem permissão para excluir peças');
|
|
return;
|
|
}
|
|
onDeletePeca(pecaId);
|
|
};
|
|
|
|
const hasActiveFilters = Boolean(ofFilter || faseFilter || pecaFilter || descricaoFilter || priorityFilter !== 'all' || semComponentesFilter !== 'all');
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<TableFilters
|
|
ofFilter={ofFilter}
|
|
onOfFilterChange={setOfFilter}
|
|
faseFilter={faseFilter}
|
|
onFaseFilterChange={setFaseFilter}
|
|
pecaFilter={pecaFilter}
|
|
onPecaFilterChange={setPecaFilter}
|
|
descricaoFilter={descricaoFilter}
|
|
onDescricaoFilterChange={setDescricaoFilter}
|
|
priorityFilter={priorityFilter}
|
|
onPriorityFilterChange={setPriorityFilter}
|
|
semComponentesFilter={semComponentesFilter}
|
|
onSemComponentesFilterChange={setSemComponentesFilter}
|
|
onSearchComponent={searchPecasByComponent}
|
|
pesoTotal={pesoTotal}
|
|
/>
|
|
|
|
<div className="flex justify-between items-center">
|
|
<TableActions
|
|
selectedPecas={selectedPecas}
|
|
onExportCSV={handleExportCSV}
|
|
onDeleteSelected={handleDeleteSelected}
|
|
hasRecentImport={hasRecentImport}
|
|
onDeleteLastImport={onDeleteLastImport || (() => {})}
|
|
/>
|
|
|
|
<div className="flex items-center gap-2">
|
|
{onOpenPainelRevisoes && (
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={onOpenPainelRevisoes}
|
|
className="h-8 border-red-500/60 bg-red-950/20 text-red-400 hover:bg-red-900/40 hover:text-red-200 font-bold flex items-center gap-1.5 transition-all shadow-sm"
|
|
title="Abrir Registro e Gestão de Revisões da OF"
|
|
>
|
|
<FileText className="h-3.5 w-3.5 text-red-400" />
|
|
<span>REG.REVISOES</span>
|
|
{totalRevisoesOF !== undefined && totalRevisoesOF > 0 && (
|
|
<span className="ml-1 px-1.5 py-0.2 rounded-full text-[10px] bg-red-500 text-white font-black">
|
|
{totalRevisoesOF}
|
|
</span>
|
|
)}
|
|
</Button>
|
|
)}
|
|
|
|
{canEdit && onBatchUpdatePecas && (
|
|
<BatchEditButton
|
|
selectedPecas={selectedPecas}
|
|
onBatchUpdate={handleBatchUpdate}
|
|
disabled={!canEdit}
|
|
/>
|
|
)}
|
|
|
|
<PDFGenerator
|
|
selectedPecas={selectedPecas}
|
|
allPecas={filteredAndSortedPecas}
|
|
ofNumber={ofNumber}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{filteredAndSortedPecas.length === 0 ? (
|
|
<EmptyState hasActiveFilters={hasActiveFilters} />
|
|
) : (
|
|
<div className="rounded-md border border-border bg-card">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableHeaderSortable
|
|
selectAll={selectAll}
|
|
onSelectAll={handleSelectAll}
|
|
sortField={sortField}
|
|
sortOrder={sortOrder}
|
|
onSort={handleSort}
|
|
/>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{filteredAndSortedPecas.map((peca) => (
|
|
<PecaTableRow
|
|
key={peca.id}
|
|
peca={peca}
|
|
isSelected={selectedPecas.has(peca.id)}
|
|
onSelect={(checked) => handleSelectPeca(peca.id, checked)}
|
|
onEdit={() => handleEditPeca(peca)}
|
|
onDelete={() => handleDeletePeca(peca.id)}
|
|
onOpenComponentPopup={() => onOpenComponentPopup(peca.id)}
|
|
onOpenRevisao={() => onOpenRevisaoPeca?.(peca)}
|
|
/>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|