🚀 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
+241
View File
@@ -0,0 +1,241 @@
import React, { useState } from 'react';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { toast } from 'sonner';
import { usePecas, Peca } from '@/hooks/usePecas';
import { usePecasP4Listener } from '@/hooks/usePecasP4Listener';
import { PecaForm } from '@/components/pecas/PecaForm';
import { PecasTable } from '@/components/pecas/PecasTable';
import { Package, Plus, RefreshCw } from 'lucide-react';
import { usePermissionControl } from '@/hooks/usePermissionControl';
export default function CadastroPecas() {
// Inicializar listener para peças P4
usePecasP4Listener();
const {
pecas,
loading,
ofNumbers,
savePeca,
updatePeca,
deletePeca,
importCSV,
importPecas,
undoLastImport,
deleteLastImport,
hasRecentImport,
loadPecas,
sincronizarPrioridades
} = usePecas();
const { canCreate, canEdit, canDelete } = usePermissionControl();
const [saving, setSaving] = useState(false);
const [syncing, setSyncing] = useState(false);
const [editingPeca, setEditingPeca] = useState<Peca | null>(null);
const [showForm, setShowForm] = useState(false);
const handleSave = async (formData: any) => {
if (!canCreate()) return false;
setSaving(true);
try {
const success = await savePeca(formData);
if (success) {
setShowForm(false);
}
return success;
} finally {
setSaving(false);
}
};
const handleUpdate = async (id: string, formData: any) => {
if (!canEdit()) return false;
setSaving(true);
try {
const success = await updatePeca(id, formData);
if (success) {
setEditingPeca(null);
setShowForm(false);
}
return success;
} finally {
setSaving(false);
}
};
const handleDelete = async (pecaId: string) => {
if (!canDelete()) return;
if (confirm('Tem certeza que deseja apagar esta peça?')) {
await deletePeca(pecaId);
}
};
const handleEdit = (peca: Peca) => {
if (!canEdit()) return;
setEditingPeca(peca);
setShowForm(true);
};
const handleCancelEdit = () => {
setEditingPeca(null);
setShowForm(false);
};
const handleDeleteLastImport = async () => {
if (!canDelete()) return;
if (confirm('Tem certeza que deseja apagar todas as peças da última importação?')) {
await deleteLastImport();
}
};
const handleOpenComponentPopup = (pecaId: string) => {
console.log('Abrir popup de componentes para peça:', pecaId);
toast.info('Funcionalidade de componentes será implementada em breve');
};
const handleImportPecas = async (pecasData: any[]) => {
if (!canCreate()) {
toast.error('Você não tem permissão para importar peças');
throw new Error('Sem permissão');
}
try {
await importPecas(pecasData);
} catch (error) {
throw error;
}
};
const handleImportCSV = async (file: File): Promise<boolean> => {
if (!canCreate()) {
toast.error('Você não tem permissão para importar peças');
return false;
}
try {
await importCSV(file);
return true;
} catch (error) {
console.error('Erro ao importar CSV:', error);
return false;
}
};
const handleSyncPriorities = async () => {
setSyncing(true);
try {
await sincronizarPrioridades();
toast.success('Prioridades sincronizadas com sucesso!');
} catch (error) {
console.error('Erro ao sincronizar prioridades:', error);
toast.error('Erro ao sincronizar prioridades');
} finally {
setSyncing(false);
}
};
if (loading) {
return (
<div className="min-h-screen bg-background p-6">
<div className="flex items-center justify-center h-64">
<div className="text-lg text-foreground">Carregando cadastro de peças...</div>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-background p-6">
<div className="max-w-7xl mx-auto space-y-6">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold text-foreground mb-2">Cadastro de Peças</h1>
<p className="text-muted-foreground">
Gerencie o cadastro de peças do sistema.
<span className="text-sm block mt-1">
ℹ️ As prioridades são controladas automaticamente pelo sistema de Prioridades de Fabricação e são sincronizadas em tempo real.
</span>
</p>
</div>
<div className="flex items-center gap-4">
<Badge variant="secondary" className="bg-secondary text-secondary-foreground border-border">
{pecas.length} {pecas.length === 1 ? 'peça' : 'peças'}
</Badge>
<Button
onClick={handleSyncPriorities}
disabled={syncing}
variant="outline"
className="flex items-center gap-2"
>
<RefreshCw className={`h-4 w-4 ${syncing ? 'animate-spin' : ''}`} />
{syncing ? 'Sincronizando...' : 'Sincronizar Prioridades'}
</Button>
{canCreate() && (
<Button
onClick={() => setShowForm(true)}
className="bg-primary hover:bg-primary/90 text-primary-foreground flex items-center gap-2"
>
<Plus className="h-4 w-4" />
Nova Peça
</Button>
)}
</div>
</div>
{/* Form Card */}
{showForm && canCreate() && (
<Card className="bg-card border-border">
<CardHeader>
<CardTitle className="text-card-foreground flex items-center gap-2">
<Package className="h-5 w-5" />
{editingPeca ? 'Editar Peça' : 'Nova Peça'}
</CardTitle>
</CardHeader>
<CardContent>
<PecaForm
ofNumbers={ofNumbers}
onSave={handleSave}
onUpdate={handleUpdate}
onImportCSV={handleImportCSV}
saving={saving}
editingPeca={editingPeca}
onCancelEdit={handleCancelEdit}
hasRecentImport={hasRecentImport}
onDeleteLastImport={handleDeleteLastImport}
pecas={pecas}
onImportPecas={handleImportPecas}
onUndoLastImport={undoLastImport}
canImport={canCreate()}
canDelete={canDelete()}
/>
</CardContent>
</Card>
)}
{/* Table Card */}
<Card className="bg-card border-border">
<CardHeader>
<CardTitle className="text-card-foreground flex items-center gap-2">
<Package className="h-5 w-5" />
Peças Cadastradas
</CardTitle>
</CardHeader>
<CardContent>
<PecasTable
pecas={pecas}
onOpenComponentPopup={handleOpenComponentPopup}
onDeletePeca={canDelete() ? handleDelete : undefined}
onEditPeca={canEdit() ? handleEdit : undefined}
onDeleteLastImport={canDelete() ? handleDeleteLastImport : undefined}
hasRecentImport={hasRecentImport}
canEdit={canEdit()}
canDelete={canDelete()}
/>
</CardContent>
</Card>
</div>
</div>
);
}