Restauração do código oficial do GPI-JWT-V3

This commit is contained in:
2026-03-18 21:55:33 +00:00
commit 405d121b0e
208 changed files with 38123 additions and 0 deletions

View 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>
);
};