Files
GPI/src/client/components/admin/GeometrySettings.tsx
T
2026-03-14 21:27:32 -03:00

206 lines
9.5 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { Plus, Pencil, Trash2, Box, RefreshCw } from 'lucide-react';
import { Button } from '../Button';
import { Modal } from '../Modal';
import { Input } from '../Input';
import * as geometryService from '../../services/geometryTypeService';
import type { GeometryType } from '../../types';
import { useAuth } from '../../context/useAuth';
export const GeometrySettings: React.FC = () => {
const { appUser } = useAuth();
const [types, setTypes] = useState<GeometryType[]>([]);
const [loading, setLoading] = useState(true);
const [isModalOpen, setIsModalOpen] = useState(false);
const [editingItem, setEditingItem] = useState<GeometryType | null>(null);
const [saving, setSaving] = useState(false);
const [form, setForm] = useState({
name: '',
efficiencyLoss: '20'
});
const fetchTypes = React.useCallback(async () => {
setLoading(true);
try {
const response = await geometryService.getAllTypes();
setTypes(response.data);
} catch (error) {
console.error('Error fetching geometry types', error);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
if (appUser) {
fetchTypes();
}
}, [appUser, fetchTypes]);
const handleOpenModal = (item?: GeometryType) => {
if (item) {
setEditingItem(item);
setForm({ name: item.name, efficiencyLoss: (item.efficiencyLoss ?? 0).toString() });
} else {
setEditingItem(null);
setForm({ name: '', efficiencyLoss: '20' });
}
setIsModalOpen(true);
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setSaving(true);
try {
const payload = {
name: form.name,
efficiencyLoss: parseFloat(form.efficiencyLoss) || 0
};
if (editingItem) {
await geometryService.updateType(editingItem.id || editingItem._id!, payload);
} else {
await geometryService.createType(payload);
}
setIsModalOpen(false);
fetchTypes();
} catch (error) {
console.error('Error saving type', error);
alert('Erro ao salvar tipo de geometria');
} finally {
setSaving(false);
}
};
const handleDelete = async (id: string) => {
if (!confirm('Tem certeza que deseja excluir? Isso pode afetar peças criadas com este tipo.')) return;
try {
await geometryService.deleteType(id);
fetchTypes();
} catch (error) {
console.error('Error deleting type', error);
}
};
const handleRestoreDefaults = async () => {
if (!confirm('Isso irá apagar todos os tipos atuais e restaurar a lista padrão com 20% de perda. Continuar?')) return;
setLoading(true);
try {
await geometryService.restoreDefaults();
fetchTypes();
} catch (error) {
console.error('Error restoring defaults', error);
alert('Erro ao restaurar padrões');
} finally {
setLoading(false);
}
};
return (
<div className="space-y-6 animate-in fade-in slide-in-from-left-4 duration-300">
<div className="flex justify-between items-center bg-surface-soft/30 p-6 rounded-2xl border border-border/20">
<div className="flex items-center gap-3">
<div className="w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center">
<Box size={24} className="text-primary" />
</div>
<div>
<h2 className="text-xl font-bold text-text-main">Tipos de Geometria/Peças</h2>
<p className="text-sm text-text-muted">Gerencie a lista padrão de peças e suas perdas de eficiência</p>
</div>
</div>
<div className="flex gap-2">
<Button variant="ghost" onClick={handleRestoreDefaults} title="Restaurar lista original">
<RefreshCw size={20} className={loading ? 'animate-spin' : ''} />
</Button>
<Button onClick={() => handleOpenModal()}>
<Plus className="w-5 h-5 mr-2" /> Novo Tipo
</Button>
</div>
</div>
<div className="bg-surface rounded-2xl border border-border/40 overflow-hidden shadow-soft">
{loading ? (
<div className="flex items-center justify-center py-20">
<RefreshCw size={32} className="animate-spin text-primary" />
</div>
) : (
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="border-b border-border/40 bg-surface-soft">
<th className="text-left px-6 py-4 text-xs font-bold text-text-muted uppercase tracking-wider">Nome da Geometria</th>
<th className="text-left px-6 py-4 text-xs font-bold text-text-muted uppercase tracking-wider">Perda de Eficiência (%)</th>
<th className="text-right px-6 py-4 text-xs font-bold text-text-muted uppercase tracking-wider">Ações</th>
</tr>
</thead>
<tbody className="divide-y divide-border/40">
{types.map((type) => (
<tr key={type.id || type._id} className="hover:bg-surface-hover transition-colors">
<td className="px-6 py-4 font-semibold text-text-main">{type.name}</td>
<td className="px-6 py-4">
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-primary/10 text-primary border border-primary/20">
{type.efficiencyLoss}%
</span>
</td>
<td className="px-6 py-4 text-right flex justify-end gap-2">
<button
onClick={() => handleOpenModal(type)}
className="p-2 text-text-muted hover:text-primary hover:bg-primary/5 rounded-lg transition-all"
title="Editar"
>
<Pencil size={18} />
</button>
<button
onClick={() => handleDelete(type.id || type._id!)}
className="p-2 text-text-muted hover:text-error hover:bg-error/5 rounded-lg transition-all"
title="Excluir"
>
<Trash2 size={18} />
</button>
</td>
</tr>
))}
{types.length === 0 && (
<tr>
<td colSpan={3} className="px-6 py-12 text-center text-text-muted">
Nenhum tipo cadastrado.
</td>
</tr>
)}
</tbody>
</table>
</div>
)}
</div>
<Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} title={editingItem ? 'Editar Tipo' : 'Novo Tipo de Geometria'}>
<form onSubmit={handleSubmit} className="space-y-4">
<Input
name="name"
label="Nome (Ex: Vigas médias)"
value={form.name}
onChange={e => setForm({ ...form, name: e.target.value })}
required
/>
<Input
name="efficiencyLoss"
label="Perda de Eficiência (%)"
type="number"
step="0.1"
min="0"
max="100"
value={form.efficiencyLoss}
onChange={e => setForm({ ...form, efficiencyLoss: e.target.value })}
required
/>
<div className="flex justify-end gap-2 mt-6">
<Button type="button" variant="ghost" onClick={() => setIsModalOpen(false)} disabled={saving}>Cancelar</Button>
<Button type="submit" disabled={saving}>{saving ? 'Salvando...' : 'Salvar'}</Button>
</div>
</form>
</Modal>
</div>
);
};