✅ Restauração do código oficial do GPI-JWT-V3
This commit is contained in:
167
src/client/components/modals/CreatePartModal.tsx
Normal file
167
src/client/components/modals/CreatePartModal.tsx
Normal file
@@ -0,0 +1,167 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Modal } from '../Modal';
|
||||
import { Input } from '../Input';
|
||||
import { Button } from '../Button';
|
||||
import { Select } from '../Select';
|
||||
import api from '../../services/api';
|
||||
import * as geometryService from '../../services/geometryTypeService';
|
||||
import { useAuth } from '../../context/useAuth';
|
||||
import { useToast } from '../../hooks/useToast';
|
||||
import type { Part, GeometryType } from '../../types';
|
||||
|
||||
interface CreatePartModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSuccess: () => void;
|
||||
projectId?: string;
|
||||
initialData?: Part;
|
||||
}
|
||||
|
||||
export const CreatePartModal: React.FC<CreatePartModalProps> = ({ isOpen, onClose, onSuccess, projectId, initialData }) => {
|
||||
const { isGuest } = useAuth();
|
||||
const { showGuestWarning } = useToast();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [projects, setProjects] = useState<{ id: string, name: string }[]>([]);
|
||||
const [geometryTypes, setGeometryTypes] = useState<GeometryType[]>([]);
|
||||
const [selectedProjectId, setSelectedProjectId] = useState(projectId || '');
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
description: '',
|
||||
dimensions: '',
|
||||
weight: '',
|
||||
type: '',
|
||||
area: '',
|
||||
complexity: '',
|
||||
quantity: '1',
|
||||
notes: ''
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (initialData) {
|
||||
setFormData({
|
||||
description: initialData.description || '',
|
||||
dimensions: initialData.dimensions || '',
|
||||
weight: initialData.weight?.toString() || '',
|
||||
type: initialData.type || '',
|
||||
area: initialData.area?.toString() || '',
|
||||
complexity: initialData.complexity?.toString() || '',
|
||||
quantity: initialData.quantity?.toString() || '1',
|
||||
notes: initialData.notes || ''
|
||||
});
|
||||
if (initialData.projectId) setSelectedProjectId(initialData.projectId);
|
||||
} else {
|
||||
setFormData({ description: '', dimensions: '', weight: '', type: '', area: '', complexity: '', quantity: '1', notes: '' });
|
||||
if (projectId) setSelectedProjectId(projectId);
|
||||
}
|
||||
}, [initialData, isOpen, projectId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
if (!projectId) {
|
||||
api.get('/projects')
|
||||
.then(res => setProjects(res.data))
|
||||
.catch(err => console.error("Error fetching projects", err));
|
||||
}
|
||||
geometryService.getAllTypes()
|
||||
.then(res => setGeometryTypes(res.data))
|
||||
.catch(err => console.error("Error fetching geometry types", err));
|
||||
}
|
||||
}, [isOpen, projectId]);
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
|
||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (isGuest()) {
|
||||
showGuestWarning();
|
||||
return;
|
||||
}
|
||||
|
||||
const projectToUse = projectId || selectedProjectId;
|
||||
if (!projectToUse) {
|
||||
alert("Por favor, selecione um projeto.");
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const payload = {
|
||||
description: formData.type, // Usar o tipo como descrição
|
||||
projectId: projectToUse,
|
||||
dimensions: formData.dimensions || undefined,
|
||||
weight: formData.weight ? parseFloat(formData.weight) : undefined,
|
||||
type: formData.type || undefined,
|
||||
area: formData.area ? parseFloat(formData.area) : undefined,
|
||||
quantity: formData.quantity ? parseInt(formData.quantity) : 1,
|
||||
notes: formData.notes || undefined
|
||||
};
|
||||
|
||||
if (initialData) {
|
||||
await api.put(`/parts/${initialData.id}`, payload);
|
||||
} else {
|
||||
await api.post('/parts', payload);
|
||||
}
|
||||
|
||||
onSuccess();
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error('Error saving part', error);
|
||||
alert('Erro ao salvar peça');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose} title={initialData ? "Editar Peça" : "Nova Peça / Geometria"}>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
{!projectId && (
|
||||
<Select
|
||||
name="projectId"
|
||||
label="Projeto / Obra"
|
||||
options={projects.map(p => ({ label: p.name, value: p.id }))}
|
||||
value={selectedProjectId}
|
||||
onChange={(e) => setSelectedProjectId(e.target.value)}
|
||||
required
|
||||
/>
|
||||
)}
|
||||
|
||||
<Select
|
||||
name="type"
|
||||
label="Tipo Geometria"
|
||||
options={[
|
||||
{ label: 'Selecione...', value: '' },
|
||||
...geometryTypes.map(t => ({ label: t.name, value: t.name }))
|
||||
]}
|
||||
value={formData.type}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Input name="weight" label="Kg estimado do lote" type="number" step="0.1" value={formData.weight} onChange={handleChange} />
|
||||
<Input name="area" label="Área Superfície (m²)" type="number" step="0.01" value={formData.area} onChange={handleChange} />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1 w-full">
|
||||
<label className="text-[10px] font-bold text-primary uppercase tracking-[0.15em] ml-1 mb-1">Observações</label>
|
||||
<textarea
|
||||
name="notes"
|
||||
aria-label="Observações"
|
||||
className="flex min-h-[80px] w-full rounded-xl border bg-[var(--input-bg)] border-[var(--input-border)] text-[var(--input-text)] px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary disabled:opacity-50 transition-all font-medium placeholder:text-[var(--input-placeholder)]"
|
||||
value={formData.notes}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-2 mt-6">
|
||||
<Button type="button" variant="ghost" onClick={onClose} disabled={loading}>Cancelar</Button>
|
||||
<Button type="submit" disabled={loading}>{loading ? 'Salvando...' : (initialData ? 'Salvar Alterações' : 'Adicionar Peça')}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user