Files
apex/client/src/components/MaterialModal.jsx
T
lejayandCursor 6c0d7e47b8 v2.0
Co-authored-by: Cursor <[email protected]>
2026-05-28 07:20:38 +02:00

318 lines
10 KiB
React

import { useState, useEffect } from 'react';
import { createFormexMaterial, updateFormexMaterial } from '../api/client';
import styles from './MaterialModal.module.css';
export default function MaterialModal({ material, collections, onClose, onSave }) {
const isEditing = !!material;
const [formData, setFormData] = useState({
formex_code: '',
name: '',
collection: '',
width_mm: '',
height_mm: '',
thickness_mm: '',
diameter_mm: '',
length_mm: '',
bore_diameter_mm: '',
bore_depth_base_mm: '',
bore_depth_piece_mm: '',
price_chf: '',
price_unit: '',
formex_url: ''
});
const [error, setError] = useState('');
const [saving, setSaving] = useState(false);
useEffect(() => {
if (material) {
setFormData({
formex_code: material.formex_code || '',
name: material.name || '',
collection: material.collection || '',
width_mm: material.width_mm?.toString() || '',
height_mm: material.height_mm?.toString() || '',
thickness_mm: material.thickness_mm?.toString() || '',
diameter_mm: material.diameter_mm?.toString() || '',
length_mm: material.length_mm?.toString() || '',
bore_diameter_mm: material.bore_diameter_mm?.toString() || '',
bore_depth_base_mm: material.bore_depth_base_mm?.toString() || '',
bore_depth_piece_mm: material.bore_depth_piece_mm?.toString() || '',
price_chf: material.price_chf?.toString() || '',
price_unit: material.price_unit || '',
formex_url: material.formex_url || ''
});
}
}, [material]);
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
const handleSubmit = async (e) => {
e.preventDefault();
setError('');
// Validation
if (!formData.formex_code.trim() || !formData.name.trim() || !formData.collection.trim()) {
setError('Le code, le nom et la collection sont requis.');
return;
}
setSaving(true);
try {
const payload = {
formex_code: formData.formex_code.trim(),
name: formData.name.trim(),
collection: formData.collection.trim(),
width_mm: formData.width_mm ? parseInt(formData.width_mm, 10) : null,
height_mm: formData.height_mm ? parseInt(formData.height_mm, 10) : null,
thickness_mm: formData.thickness_mm ? parseFloat(formData.thickness_mm) : null,
diameter_mm: formData.diameter_mm ? parseFloat(formData.diameter_mm) : null,
length_mm: formData.length_mm ? parseFloat(formData.length_mm) : null,
bore_diameter_mm: formData.bore_diameter_mm ? parseFloat(formData.bore_diameter_mm) : null,
bore_depth_base_mm: formData.bore_depth_base_mm ? parseFloat(formData.bore_depth_base_mm) : null,
bore_depth_piece_mm: formData.bore_depth_piece_mm ? parseFloat(formData.bore_depth_piece_mm) : null,
price_chf: formData.price_chf ? parseFloat(formData.price_chf) : null,
price_unit: formData.price_unit.trim() || null,
formex_url: formData.formex_url.trim() || null
};
if (isEditing) {
await updateFormexMaterial(material.id, payload);
} else {
await createFormexMaterial(payload);
}
onSave();
} catch (err) {
setError(err.message);
} finally {
setSaving(false);
}
};
const handleOverlayClick = (e) => {
if (e.target === e.currentTarget) {
onClose();
}
};
return (
<div className={styles.overlay} onClick={handleOverlayClick}>
<div className={styles.modal}>
<div className={styles.header}>
<h2>{isEditing ? 'Modifier le matériau' : 'Nouveau matériau'}</h2>
<button type="button" onClick={onClose} className={styles.closeBtn}>
&times;
</button>
</div>
<form onSubmit={handleSubmit} className={styles.form}>
{error && <p className={styles.error}>{error}</p>}
<div className={styles.field}>
<label htmlFor="formex_code">Code Formex *</label>
<input
type="text"
id="formex_code"
name="formex_code"
value={formData.formex_code}
onChange={handleChange}
placeholder="ex: 10000"
required
/>
</div>
<div className={styles.field}>
<label htmlFor="name">Nom *</label>
<input
type="text"
id="name"
name="name"
value={formData.name}
onChange={handleChange}
placeholder="ex: Panneaux mélaminés Duplex Plus 10000 Tilia"
required
/>
</div>
<div className={styles.field}>
<label htmlFor="collection">Collection *</label>
<input
type="text"
id="collection"
name="collection"
value={formData.collection}
onChange={handleChange}
list="collections-list"
placeholder="ex: Duplex Plus"
required
/>
<datalist id="collections-list">
{collections.map((c) => (
<option key={c} value={c} />
))}
</datalist>
</div>
<div className={styles.row}>
<div className={styles.field}>
<label htmlFor="width_mm">Largeur (mm)</label>
<input
type="number"
id="width_mm"
name="width_mm"
value={formData.width_mm}
onChange={handleChange}
placeholder="2800"
/>
</div>
<div className={styles.field}>
<label htmlFor="height_mm">Hauteur (mm)</label>
<input
type="number"
id="height_mm"
name="height_mm"
value={formData.height_mm}
onChange={handleChange}
placeholder="2070"
/>
</div>
<div className={styles.field}>
<label htmlFor="thickness_mm">Épaisseur (mm)</label>
<input
type="number"
id="thickness_mm"
name="thickness_mm"
value={formData.thickness_mm}
onChange={handleChange}
placeholder="19"
/>
</div>
</div>
<div className={styles.row}>
<div className={styles.field}>
<label htmlFor="diameter_mm">Ø Diamètre (mm)</label>
<input
type="number"
id="diameter_mm"
name="diameter_mm"
value={formData.diameter_mm}
onChange={handleChange}
placeholder="8"
step="0.1"
/>
</div>
<div className={styles.field}>
<label htmlFor="length_mm">Longueur (mm)</label>
<input
type="number"
id="length_mm"
name="length_mm"
value={formData.length_mm}
onChange={handleChange}
placeholder="55"
step="0.1"
/>
</div>
</div>
<fieldset className={styles.fieldset}>
<legend>Perçage</legend>
<div className={styles.row}>
<div className={styles.field}>
<label htmlFor="bore_diameter_mm">Ø Perçage (mm)</label>
<input
type="number"
id="bore_diameter_mm"
name="bore_diameter_mm"
value={formData.bore_diameter_mm}
onChange={handleChange}
placeholder="8"
step="0.1"
/>
</div>
<div className={styles.field}>
<label htmlFor="bore_depth_base_mm">Prof. fond (mm)</label>
<input
type="number"
id="bore_depth_base_mm"
name="bore_depth_base_mm"
value={formData.bore_depth_base_mm}
onChange={handleChange}
placeholder="11"
step="0.1"
/>
</div>
<div className={styles.field}>
<label htmlFor="bore_depth_piece_mm">Prof. pièce (mm)</label>
<input
type="number"
id="bore_depth_piece_mm"
name="bore_depth_piece_mm"
value={formData.bore_depth_piece_mm}
onChange={handleChange}
placeholder="8"
step="0.1"
/>
</div>
</div>
</fieldset>
<div className={styles.row}>
<div className={styles.field}>
<label htmlFor="price_chf">Prix CHF</label>
<input
type="number"
id="price_chf"
name="price_chf"
value={formData.price_chf}
onChange={handleChange}
placeholder="0.60"
step="0.01"
/>
</div>
<div className={styles.field}>
<label htmlFor="price_unit">Unité de prix</label>
<input
type="text"
id="price_unit"
name="price_unit"
value={formData.price_unit}
onChange={handleChange}
placeholder="par Pièce (PC)"
/>
</div>
</div>
<div className={styles.field}>
<label htmlFor="formex_url">URL</label>
<input
type="url"
id="formex_url"
name="formex_url"
value={formData.formex_url}
onChange={handleChange}
placeholder="https://..."
/>
</div>
<div className={styles.actions}>
<button type="button" onClick={onClose} className={styles.cancelBtn}>
Annuler
</button>
<button type="submit" disabled={saving} className={styles.saveBtn}>
{saving ? 'Enregistrement…' : (isEditing ? 'Enregistrer' : 'Créer')}
</button>
</div>
</form>
</div>
</div>
);
}