🚀 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
+213
View File
@@ -0,0 +1,213 @@
import React from 'react';
import { Table, TableBody, TableHeader } from '@/components/ui/table';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Package } from 'lucide-react';
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;
onEditPeca?: (peca: Peca) => void;
onDeleteLastImport?: () => void;
onBatchUpdatePecas?: (pecaIds: string[], updates: any) => Promise<void>;
hasRecentImport: boolean;
canEdit?: boolean;
canDelete?: boolean;
ofNumber?: string;
}
export function PecasTable({
pecas,
onOpenComponentPopup,
onDeletePeca,
onEditPeca,
onDeleteLastImport,
onBatchUpdatePecas,
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 || !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?`)) {
try {
for (const pecaId of selectedPecas) {
await onDeletePeca(pecaId);
}
toast.success('Peças excluídas com sucesso!');
} catch (error) {
toast.error('Erro ao excluir peças.');
}
}
};
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">
{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)}
/>
))}
</TableBody>
</Table>
</div>
)}
</div>
);
}