Initial commit
This commit is contained in:
294
app/(main)/materiels/nouveau/page.tsx
Normal file
294
app/(main)/materiels/nouveau/page.tsx
Normal file
@@ -0,0 +1,294 @@
|
||||
'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;
|
||||
Reference in New Issue
Block a user