294 lines
13 KiB
TypeScript
294 lines
13 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState, useRef } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import { Card } from 'primereact/card';
|
|
import { Button } from 'primereact/button';
|
|
import { InputText } from 'primereact/inputtext';
|
|
import { InputNumber } from 'primereact/inputnumber';
|
|
import { Dropdown } from 'primereact/dropdown';
|
|
import { Calendar } from 'primereact/calendar';
|
|
import { InputTextarea } from 'primereact/inputtextarea';
|
|
import { Toast } from 'primereact/toast';
|
|
import { materielService } from '../../../../services/api';
|
|
import { TypeMateriel, StatutMateriel } from '../../../../types/btp';
|
|
import { useApiCall } from '../../../../hooks/useApiCall';
|
|
|
|
const NouveauMaterielPage = () => {
|
|
const router = useRouter();
|
|
const [materiel, setMateriel] = useState({
|
|
nom: '',
|
|
marque: '',
|
|
modele: '',
|
|
numeroSerie: '',
|
|
type: '' as TypeMateriel,
|
|
description: '',
|
|
dateAchat: null as Date | null,
|
|
valeurAchat: 0,
|
|
valeurActuelle: 0,
|
|
statut: StatutMateriel.DISPONIBLE,
|
|
localisation: '',
|
|
proprietaire: '',
|
|
quantiteStock: 0,
|
|
seuilMinimum: 0,
|
|
unite: 'unité',
|
|
actif: true
|
|
});
|
|
const [submitted, setSubmitted] = useState(false);
|
|
|
|
// Utilisation du hook pour la création de matériel avec gestion d'erreurs automatique
|
|
const createMaterielCall = useApiCall(
|
|
(data: any) => materielService.create(data),
|
|
{
|
|
showSuccessToast: true,
|
|
successMessage: 'Matériel créé avec succès',
|
|
retryAttempts: 2,
|
|
retryDelay: 2000,
|
|
onSuccess: () => {
|
|
// Rediriger vers la liste après succès
|
|
setTimeout(() => {
|
|
router.push('/materiels');
|
|
}, 2000);
|
|
}
|
|
}
|
|
);
|
|
|
|
const typeOptions = Object.values(TypeMateriel).map(type => ({
|
|
label: type.replace('_', ' '),
|
|
value: type
|
|
}));
|
|
|
|
const statutOptions = Object.values(StatutMateriel).map(statut => ({
|
|
label: statut.replace('_', ' '),
|
|
value: statut
|
|
}));
|
|
|
|
const onInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, name: string) => {
|
|
const val = (e.target && e.target.value) || '';
|
|
setMateriel(prev => ({ ...prev, [name]: val }));
|
|
};
|
|
|
|
const onInputNumberChange = (value: number | null, name: string) => {
|
|
setMateriel(prev => ({ ...prev, [name]: value || 0 }));
|
|
};
|
|
|
|
const onDropdownChange = (e: any, name: string) => {
|
|
setMateriel(prev => ({ ...prev, [name]: e.value }));
|
|
};
|
|
|
|
const onDateChange = (e: any, name: string) => {
|
|
setMateriel(prev => ({ ...prev, [name]: e.value }));
|
|
};
|
|
|
|
const saveMateriel = async () => {
|
|
setSubmitted(true);
|
|
|
|
if (materiel.nom?.trim() && materiel.type) {
|
|
await createMaterielCall.execute(materiel);
|
|
}
|
|
};
|
|
|
|
const annuler = () => {
|
|
router.back();
|
|
};
|
|
|
|
return (
|
|
<div className="grid">
|
|
<div className="col-12">
|
|
<Toast ref={createMaterielCall.toast} />
|
|
|
|
<div className="flex align-items-center justify-content-between mb-4">
|
|
<h2 className="m-0">Nouveau Matériel</h2>
|
|
<div className="flex gap-2">
|
|
<Button
|
|
label="Annuler"
|
|
icon="pi pi-times"
|
|
className="p-button-text p-button-rounded"
|
|
onClick={annuler}
|
|
/>
|
|
<Button
|
|
label="Sauvegarder"
|
|
icon="pi pi-check"
|
|
className="p-button-text p-button-rounded"
|
|
onClick={saveMateriel}
|
|
loading={createMaterielCall.loading}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<Card>
|
|
<div className="p-fluid formgrid grid">
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="nom" className="font-bold">
|
|
Nom <span className="text-red-500">*</span>
|
|
</label>
|
|
<InputText
|
|
id="nom"
|
|
value={materiel.nom}
|
|
onChange={(e) => onInputChange(e, 'nom')}
|
|
required
|
|
autoFocus
|
|
className={submitted && !materiel.nom ? 'p-invalid' : ''}
|
|
/>
|
|
{submitted && !materiel.nom && <small className="p-error">Le nom est obligatoire.</small>}
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="type" className="font-bold">
|
|
Type <span className="text-red-500">*</span>
|
|
</label>
|
|
<Dropdown
|
|
id="type"
|
|
value={materiel.type}
|
|
onChange={(e) => onDropdownChange(e, 'type')}
|
|
options={typeOptions}
|
|
placeholder="Sélectionner un type"
|
|
className={submitted && !materiel.type ? 'p-invalid' : ''}
|
|
/>
|
|
{submitted && !materiel.type && <small className="p-error">Le type est obligatoire.</small>}
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="marque" className="font-bold">Marque</label>
|
|
<InputText
|
|
id="marque"
|
|
value={materiel.marque}
|
|
onChange={(e) => onInputChange(e, 'marque')}
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="modele" className="font-bold">Modèle</label>
|
|
<InputText
|
|
id="modele"
|
|
value={materiel.modele}
|
|
onChange={(e) => onInputChange(e, 'modele')}
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="numeroSerie" className="font-bold">Numéro de série</label>
|
|
<InputText
|
|
id="numeroSerie"
|
|
value={materiel.numeroSerie}
|
|
onChange={(e) => onInputChange(e, 'numeroSerie')}
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="statut" className="font-bold">Statut</label>
|
|
<Dropdown
|
|
id="statut"
|
|
value={materiel.statut}
|
|
onChange={(e) => onDropdownChange(e, 'statut')}
|
|
options={statutOptions}
|
|
placeholder="Sélectionner un statut"
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="dateAchat" className="font-bold">Date d'achat</label>
|
|
<Calendar
|
|
id="dateAchat"
|
|
value={materiel.dateAchat}
|
|
onChange={(e) => onDateChange(e, 'dateAchat')}
|
|
dateFormat="dd/mm/yy"
|
|
showIcon
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="valeurAchat" className="font-bold">Valeur d'achat</label>
|
|
<InputNumber
|
|
id="valeurAchat"
|
|
value={materiel.valeurAchat}
|
|
onValueChange={(e) => onInputNumberChange(e.value, 'valeurAchat')}
|
|
mode="currency"
|
|
currency="EUR"
|
|
locale="fr-FR"
|
|
min={0}
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="valeurActuelle" className="font-bold">Valeur actuelle</label>
|
|
<InputNumber
|
|
id="valeurActuelle"
|
|
value={materiel.valeurActuelle}
|
|
onValueChange={(e) => onInputNumberChange(e.value, 'valeurActuelle')}
|
|
mode="currency"
|
|
currency="EUR"
|
|
locale="fr-FR"
|
|
min={0}
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="localisation" className="font-bold">Localisation</label>
|
|
<InputText
|
|
id="localisation"
|
|
value={materiel.localisation}
|
|
onChange={(e) => onInputChange(e, 'localisation')}
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="proprietaire" className="font-bold">Propriétaire</label>
|
|
<InputText
|
|
id="proprietaire"
|
|
value={materiel.proprietaire}
|
|
onChange={(e) => onInputChange(e, 'proprietaire')}
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="quantiteStock" className="font-bold">Quantité en stock</label>
|
|
<InputNumber
|
|
id="quantiteStock"
|
|
value={materiel.quantiteStock}
|
|
onValueChange={(e) => onInputNumberChange(e.value, 'quantiteStock')}
|
|
min={0}
|
|
showButtons
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="seuilMinimum" className="font-bold">Seuil minimum</label>
|
|
<InputNumber
|
|
id="seuilMinimum"
|
|
value={materiel.seuilMinimum}
|
|
onValueChange={(e) => onInputNumberChange(e.value, 'seuilMinimum')}
|
|
min={0}
|
|
showButtons
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="unite" className="font-bold">Unité</label>
|
|
<InputText
|
|
id="unite"
|
|
value={materiel.unite}
|
|
onChange={(e) => onInputChange(e, 'unite')}
|
|
placeholder="ex: unité, kg, m, etc."
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12">
|
|
<label htmlFor="description" className="font-bold">Description</label>
|
|
<InputTextarea
|
|
id="description"
|
|
value={materiel.description}
|
|
onChange={(e) => onInputChange(e, 'description')}
|
|
rows={3}
|
|
cols={20}
|
|
placeholder="Description détaillée du matériel..."
|
|
/>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default NouveauMaterielPage; |