🚀 Initial commit: Versão atual do TrackSteel APP
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user