752 lines
31 KiB
TypeScript
752 lines
31 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState, useEffect, useRef } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import { Card } from 'primereact/card';
|
|
import { Button } from 'primereact/button';
|
|
import { InputText } from 'primereact/inputtext';
|
|
import { InputTextarea } from 'primereact/inputtextarea';
|
|
import { InputNumber } from 'primereact/inputnumber';
|
|
import { Calendar } from 'primereact/calendar';
|
|
import { Dropdown } from 'primereact/dropdown';
|
|
import { Toast } from 'primereact/toast';
|
|
import { Divider } from 'primereact/divider';
|
|
import { DataTable } from 'primereact/datatable';
|
|
import { Column } from 'primereact/column';
|
|
import { Steps } from 'primereact/steps';
|
|
import { devisService, clientService } from '../../../../services/api';
|
|
import type { Devis, Client } from '../../../../types/btp';
|
|
|
|
interface DevisLigne {
|
|
id: string;
|
|
designation: string;
|
|
quantite: number;
|
|
unite: string;
|
|
prixUnitaire: number;
|
|
montantHT: number;
|
|
}
|
|
|
|
const NouveauDevisPage = () => {
|
|
const router = useRouter();
|
|
const toast = useRef<Toast>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
const [submitted, setSubmitted] = useState(false);
|
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
const [clients, setClients] = useState<any[]>([]);
|
|
const [lignes, setLignes] = useState<DevisLigne[]>([]);
|
|
|
|
const [devis, setDevis] = useState<Partial<Devis>>({
|
|
id: '',
|
|
numero: '',
|
|
dateEmission: new Date().toISOString(),
|
|
dateValidite: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
|
|
objet: '',
|
|
description: '',
|
|
montantHT: 0,
|
|
montantTTC: 0,
|
|
tauxTVA: 20,
|
|
statut: 'BROUILLON' as any,
|
|
actif: true,
|
|
client: undefined,
|
|
chantier: undefined
|
|
});
|
|
|
|
const [nouvelleLigne, setNouvelleLigne] = useState<DevisLigne>({
|
|
id: '',
|
|
designation: '',
|
|
quantite: 1,
|
|
unite: 'u',
|
|
prixUnitaire: 0,
|
|
montantHT: 0
|
|
});
|
|
|
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
|
|
const statuts = [
|
|
{ label: 'Brouillon', value: 'BROUILLON' },
|
|
{ label: 'Envoyé', value: 'ENVOYE' },
|
|
{ label: 'Accepté', value: 'ACCEPTE' },
|
|
{ label: 'Refusé', value: 'REFUSE' },
|
|
{ label: 'Expiré', value: 'EXPIRE' }
|
|
];
|
|
|
|
const unites = [
|
|
{ label: 'Unité', value: 'u' },
|
|
{ label: 'Mètre', value: 'm' },
|
|
{ label: 'Mètre carré', value: 'm²' },
|
|
{ label: 'Mètre cube', value: 'm³' },
|
|
{ label: 'Kilogramme', value: 'kg' },
|
|
{ label: 'Heure', value: 'h' },
|
|
{ label: 'Jour', value: 'j' },
|
|
{ label: 'Forfait', value: 'forfait' }
|
|
];
|
|
|
|
const steps = [
|
|
{ label: 'Informations générales' },
|
|
{ label: 'Lignes de devis' },
|
|
{ label: 'Montants et TVA' },
|
|
{ label: 'Validation' }
|
|
];
|
|
|
|
useEffect(() => {
|
|
loadClients();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
// Recalcul automatique des montants
|
|
const totalHT = lignes.reduce((sum, ligne) => sum + ligne.montantHT, 0);
|
|
const totalTTC = totalHT * (1 + devis.tauxTVA / 100);
|
|
|
|
setDevis(prev => ({
|
|
...prev,
|
|
montantHT: totalHT,
|
|
montantTTC: totalTTC
|
|
}));
|
|
}, [lignes, devis.tauxTVA]);
|
|
|
|
const loadClients = async () => {
|
|
try {
|
|
const data = await clientService.getAll();
|
|
setClients(data.map(client => ({
|
|
label: `${client.prenom} ${client.nom}${client.entreprise ? ' - ' + client.entreprise : ''}`,
|
|
value: client.id,
|
|
client: client
|
|
})));
|
|
} catch (error) {
|
|
console.error('Erreur lors du chargement des clients:', error);
|
|
}
|
|
};
|
|
|
|
const validateStep = (step: number) => {
|
|
const newErrors: Record<string, string> = {};
|
|
|
|
switch (step) {
|
|
case 0: // Informations générales
|
|
if (!devis.objet.trim()) {
|
|
newErrors.objet = 'L\'objet du devis est obligatoire';
|
|
}
|
|
if (!devis.client) {
|
|
newErrors.client = 'Le client est obligatoire';
|
|
}
|
|
if (!devis.dateEmission) {
|
|
newErrors.dateEmission = 'La date d\'émission est obligatoire';
|
|
}
|
|
if (!devis.dateValidite) {
|
|
newErrors.dateValidite = 'La date de validité est obligatoire';
|
|
}
|
|
if (devis.dateEmission && devis.dateValidite && devis.dateEmission > devis.dateValidite) {
|
|
newErrors.dateValidite = 'La date de validité doit être postérieure à la date d\'émission';
|
|
}
|
|
break;
|
|
|
|
case 1: // Lignes de devis
|
|
if (lignes.length === 0) {
|
|
newErrors.lignes = 'Au moins une ligne de devis est requise';
|
|
}
|
|
break;
|
|
|
|
case 2: // Montants et TVA
|
|
if (!devis.tauxTVA || devis.tauxTVA < 0 || devis.tauxTVA > 100) {
|
|
newErrors.tauxTVA = 'Le taux de TVA doit être compris entre 0 et 100%';
|
|
}
|
|
break;
|
|
}
|
|
|
|
setErrors(newErrors);
|
|
return Object.keys(newErrors).length === 0;
|
|
};
|
|
|
|
const nextStep = () => {
|
|
if (validateStep(activeIndex)) {
|
|
setActiveIndex(prev => Math.min(prev + 1, steps.length - 1));
|
|
}
|
|
};
|
|
|
|
const prevStep = () => {
|
|
setActiveIndex(prev => Math.max(prev - 1, 0));
|
|
};
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setSubmitted(true);
|
|
|
|
if (!validateStep(activeIndex)) {
|
|
toast.current?.show({
|
|
severity: 'error',
|
|
summary: 'Erreur',
|
|
detail: 'Veuillez corriger les erreurs du formulaire',
|
|
life: 3000
|
|
});
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
try {
|
|
const devisToSave = {
|
|
...devis,
|
|
client: { id: devis.client }, // Envoyer seulement l'ID du client
|
|
lignes: lignes
|
|
};
|
|
|
|
// TODO: Implement when backend supports it
|
|
toast.current?.show({
|
|
severity: 'warn',
|
|
summary: 'Non implémenté',
|
|
detail: 'La création de devis n\'est pas encore disponible',
|
|
life: 3000
|
|
});
|
|
|
|
// Simulate success for demo
|
|
setTimeout(() => {
|
|
toast.current?.show({
|
|
severity: 'success',
|
|
summary: 'Succès',
|
|
detail: 'Devis créé avec succès (simulation)',
|
|
life: 3000
|
|
});
|
|
|
|
setTimeout(() => {
|
|
router.push('/devis');
|
|
}, 1000);
|
|
}, 1000);
|
|
} catch (error) {
|
|
console.error('Erreur lors de la création:', error);
|
|
toast.current?.show({
|
|
severity: 'error',
|
|
summary: 'Erreur',
|
|
detail: 'Impossible de créer le devis',
|
|
life: 3000
|
|
});
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleCancel = () => {
|
|
router.push('/devis');
|
|
};
|
|
|
|
const onInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, name: string) => {
|
|
const val = (e.target && e.target.value) || '';
|
|
let _devis = { ...devis };
|
|
(_devis as any)[name] = val;
|
|
setDevis(_devis);
|
|
|
|
if (errors[name]) {
|
|
const newErrors = { ...errors };
|
|
delete newErrors[name];
|
|
setErrors(newErrors);
|
|
}
|
|
};
|
|
|
|
const onDateChange = (e: any, name: string) => {
|
|
let _devis = { ...devis };
|
|
(_devis as any)[name] = e.value;
|
|
setDevis(_devis);
|
|
|
|
if (errors[name]) {
|
|
const newErrors = { ...errors };
|
|
delete newErrors[name];
|
|
setErrors(newErrors);
|
|
}
|
|
};
|
|
|
|
const onNumberChange = (e: any, name: string) => {
|
|
let _devis = { ...devis };
|
|
(_devis as any)[name] = e.value;
|
|
setDevis(_devis);
|
|
|
|
if (errors[name]) {
|
|
const newErrors = { ...errors };
|
|
delete newErrors[name];
|
|
setErrors(newErrors);
|
|
}
|
|
};
|
|
|
|
const onDropdownChange = (e: any, name: string) => {
|
|
let _devis = { ...devis };
|
|
(_devis as any)[name] = e.value;
|
|
setDevis(_devis);
|
|
|
|
if (errors[name]) {
|
|
const newErrors = { ...errors };
|
|
delete newErrors[name];
|
|
setErrors(newErrors);
|
|
}
|
|
};
|
|
|
|
const onLigneInputChange = (e: React.ChangeEvent<HTMLInputElement>, name: string) => {
|
|
const val = (e.target && e.target.value) || '';
|
|
let _ligne = { ...nouvelleLigne };
|
|
(_ligne as any)[name] = val;
|
|
setNouvelleLigne(_ligne);
|
|
};
|
|
|
|
const onLigneNumberChange = (e: any, name: string) => {
|
|
let _ligne = { ...nouvelleLigne };
|
|
(_ligne as any)[name] = e.value;
|
|
|
|
// Recalcul automatique du montant HT
|
|
if (name === 'quantite' || name === 'prixUnitaire') {
|
|
const quantite = name === 'quantite' ? e.value : _ligne.quantite;
|
|
const prixUnitaire = name === 'prixUnitaire' ? e.value : _ligne.prixUnitaire;
|
|
_ligne.montantHT = (quantite || 0) * (prixUnitaire || 0);
|
|
}
|
|
|
|
setNouvelleLigne(_ligne);
|
|
};
|
|
|
|
const onLigneDropdownChange = (e: any, name: string) => {
|
|
let _ligne = { ...nouvelleLigne };
|
|
(_ligne as any)[name] = e.value;
|
|
setNouvelleLigne(_ligne);
|
|
};
|
|
|
|
const ajouterLigne = () => {
|
|
if (!nouvelleLigne.designation.trim()) {
|
|
toast.current?.show({
|
|
severity: 'warn',
|
|
summary: 'Attention',
|
|
detail: 'La désignation est obligatoire',
|
|
life: 3000
|
|
});
|
|
return;
|
|
}
|
|
|
|
const ligne: DevisLigne = {
|
|
...nouvelleLigne,
|
|
id: Date.now().toString() // Simple ID pour la démo
|
|
};
|
|
|
|
setLignes(prev => [...prev, ligne]);
|
|
setNouvelleLigne({
|
|
id: '',
|
|
designation: '',
|
|
quantite: 1,
|
|
unite: 'u',
|
|
prixUnitaire: 0,
|
|
montantHT: 0
|
|
});
|
|
|
|
if (errors.lignes) {
|
|
const newErrors = { ...errors };
|
|
delete newErrors.lignes;
|
|
setErrors(newErrors);
|
|
}
|
|
};
|
|
|
|
const supprimerLigne = (id: string) => {
|
|
setLignes(prev => prev.filter(ligne => ligne.id !== id));
|
|
};
|
|
|
|
const renderStepContent = () => {
|
|
switch (activeIndex) {
|
|
case 0:
|
|
return (
|
|
<div className="formgrid grid">
|
|
<div className="field col-12">
|
|
<label htmlFor="objet" className="font-bold">
|
|
Objet du devis <span className="text-red-500">*</span>
|
|
</label>
|
|
<InputText
|
|
id="objet"
|
|
value={devis.objet}
|
|
onChange={(e) => onInputChange(e, 'objet')}
|
|
className={errors.objet ? 'p-invalid' : ''}
|
|
placeholder="Objet du devis"
|
|
/>
|
|
{errors.objet && <small className="p-error">{errors.objet}</small>}
|
|
</div>
|
|
|
|
<div className="field col-12">
|
|
<label htmlFor="client" className="font-bold">
|
|
Client <span className="text-red-500">*</span>
|
|
</label>
|
|
<Dropdown
|
|
id="client"
|
|
value={devis.client}
|
|
options={clients}
|
|
onChange={(e) => onDropdownChange(e, 'client')}
|
|
placeholder="Sélectionnez un client"
|
|
className={errors.client ? 'p-invalid' : ''}
|
|
filter
|
|
/>
|
|
{errors.client && <small className="p-error">{errors.client}</small>}
|
|
</div>
|
|
|
|
<div className="field col-12">
|
|
<label htmlFor="description" className="font-bold">Description</label>
|
|
<InputTextarea
|
|
id="description"
|
|
value={devis.description}
|
|
onChange={(e) => onInputChange(e, 'description')}
|
|
rows={4}
|
|
placeholder="Description détaillée des travaux"
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="dateEmission" className="font-bold">
|
|
Date d'émission <span className="text-red-500">*</span>
|
|
</label>
|
|
<Calendar
|
|
id="dateEmission"
|
|
value={devis.dateEmission ? new Date(devis.dateEmission) : null}
|
|
onChange={(e) => onDateChange(e, 'dateEmission')}
|
|
dateFormat="dd/mm/yy"
|
|
showIcon
|
|
className={errors.dateEmission ? 'p-invalid' : ''}
|
|
/>
|
|
{errors.dateEmission && <small className="p-error">{errors.dateEmission}</small>}
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="dateValidite" className="font-bold">
|
|
Date de validité <span className="text-red-500">*</span>
|
|
</label>
|
|
<Calendar
|
|
id="dateValidite"
|
|
value={devis.dateValidite ? new Date(devis.dateValidite) : null}
|
|
onChange={(e) => onDateChange(e, 'dateValidite')}
|
|
dateFormat="dd/mm/yy"
|
|
showIcon
|
|
className={errors.dateValidite ? 'p-invalid' : ''}
|
|
minDate={devis.dateEmission ? new Date(devis.dateEmission) : undefined}
|
|
/>
|
|
{errors.dateValidite && <small className="p-error">{errors.dateValidite}</small>}
|
|
</div>
|
|
|
|
<div className="field col-12">
|
|
<label htmlFor="statut" className="font-bold">Statut</label>
|
|
<Dropdown
|
|
id="statut"
|
|
value={devis.statut}
|
|
options={statuts}
|
|
onChange={(e) => onDropdownChange(e, 'statut')}
|
|
placeholder="Sélectionnez un statut"
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
case 1:
|
|
return (
|
|
<div>
|
|
<h4 className="text-primary mb-4">Lignes de devis</h4>
|
|
|
|
{/* Formulaire d'ajout de ligne */}
|
|
<Card className="mb-4">
|
|
<h5>Ajouter une ligne</h5>
|
|
<div className="formgrid grid">
|
|
<div className="field col-12 md:col-4">
|
|
<label htmlFor="designation" className="font-bold">Désignation</label>
|
|
<InputText
|
|
id="designation"
|
|
value={nouvelleLigne.designation}
|
|
onChange={(e) => onLigneInputChange(e, 'designation')}
|
|
placeholder="Description du poste"
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-2">
|
|
<label htmlFor="quantite" className="font-bold">Quantité</label>
|
|
<InputNumber
|
|
id="quantite"
|
|
value={nouvelleLigne.quantite}
|
|
onValueChange={(e) => onLigneNumberChange(e, 'quantite')}
|
|
min={0}
|
|
maxFractionDigits={2}
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-2">
|
|
<label htmlFor="unite" className="font-bold">Unité</label>
|
|
<Dropdown
|
|
id="unite"
|
|
value={nouvelleLigne.unite}
|
|
options={unites}
|
|
onChange={(e) => onLigneDropdownChange(e, 'unite')}
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-2">
|
|
<label htmlFor="prixUnitaire" className="font-bold">Prix unitaire</label>
|
|
<InputNumber
|
|
id="prixUnitaire"
|
|
value={nouvelleLigne.prixUnitaire}
|
|
onValueChange={(e) => onLigneNumberChange(e, 'prixUnitaire')}
|
|
mode="currency"
|
|
currency="EUR"
|
|
locale="fr-FR"
|
|
min={0}
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-2">
|
|
<label htmlFor="montantHT" className="font-bold">Montant HT</label>
|
|
<InputNumber
|
|
id="montantHT"
|
|
value={nouvelleLigne.montantHT}
|
|
mode="currency"
|
|
currency="EUR"
|
|
locale="fr-FR"
|
|
disabled
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex justify-content-end">
|
|
<Button
|
|
label="Ajouter"
|
|
icon="pi pi-plus"
|
|
onClick={ajouterLigne}
|
|
size="small"
|
|
/>
|
|
</div>
|
|
</Card>
|
|
|
|
{/* Tableau des lignes */}
|
|
<DataTable value={lignes} emptyMessage="Aucune ligne ajoutée">
|
|
<Column field="designation" header="Désignation" />
|
|
<Column
|
|
field="quantite"
|
|
header="Quantité"
|
|
body={(rowData) => `${rowData.quantite} ${rowData.unite}`}
|
|
/>
|
|
<Column
|
|
field="prixUnitaire"
|
|
header="Prix unitaire"
|
|
body={(rowData) => rowData.prixUnitaire.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' })}
|
|
/>
|
|
<Column
|
|
field="montantHT"
|
|
header="Montant HT"
|
|
body={(rowData) => rowData.montantHT.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' })}
|
|
/>
|
|
<Column
|
|
body={(rowData) => (
|
|
<Button
|
|
icon="pi pi-trash"
|
|
rounded
|
|
severity="danger"
|
|
size="small"
|
|
onClick={() => supprimerLigne(rowData.id)}
|
|
/>
|
|
)}
|
|
/>
|
|
</DataTable>
|
|
|
|
{errors.lignes && <small className="p-error">{errors.lignes}</small>}
|
|
</div>
|
|
);
|
|
|
|
case 2:
|
|
return (
|
|
<div className="formgrid grid">
|
|
<div className="field col-12">
|
|
<h4 className="text-primary mb-4">Récapitulatif des montants</h4>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="montantHT" className="font-bold">Montant HT</label>
|
|
<InputNumber
|
|
id="montantHT"
|
|
value={devis.montantHT}
|
|
mode="currency"
|
|
currency="EUR"
|
|
locale="fr-FR"
|
|
disabled
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="tauxTVA" className="font-bold">
|
|
Taux TVA (%) <span className="text-red-500">*</span>
|
|
</label>
|
|
<InputNumber
|
|
id="tauxTVA"
|
|
value={devis.tauxTVA}
|
|
onValueChange={(e) => onNumberChange(e, 'tauxTVA')}
|
|
suffix="%"
|
|
min={0}
|
|
max={100}
|
|
className={errors.tauxTVA ? 'p-invalid' : ''}
|
|
/>
|
|
{errors.tauxTVA && <small className="p-error">{errors.tauxTVA}</small>}
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="montantTVA" className="font-bold">Montant TVA</label>
|
|
<InputNumber
|
|
id="montantTVA"
|
|
value={devis.montantTTC - devis.montantHT}
|
|
mode="currency"
|
|
currency="EUR"
|
|
locale="fr-FR"
|
|
disabled
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="montantTTC" className="font-bold">Montant TTC</label>
|
|
<InputNumber
|
|
id="montantTTC"
|
|
value={devis.montantTTC}
|
|
mode="currency"
|
|
currency="EUR"
|
|
locale="fr-FR"
|
|
disabled
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
case 3:
|
|
return (
|
|
<div className="formgrid grid">
|
|
<div className="field col-12">
|
|
<h3 className="text-primary">Récapitulatif du devis</h3>
|
|
<Divider />
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label className="font-bold">Objet :</label>
|
|
<p>{devis.objet}</p>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label className="font-bold">Client :</label>
|
|
<p>{clients.find(c => c.value === devis.client)?.label}</p>
|
|
</div>
|
|
|
|
<div className="field col-12">
|
|
<label className="font-bold">Description :</label>
|
|
<p>{devis.description || 'Aucune description'}</p>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label className="font-bold">Date d'émission :</label>
|
|
<p>{devis.dateEmission ? new Date(devis.dateEmission).toLocaleDateString('fr-FR') : ''}</p>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label className="font-bold">Date de validité :</label>
|
|
<p>{devis.dateValidite ? new Date(devis.dateValidite).toLocaleDateString('fr-FR') : ''}</p>
|
|
</div>
|
|
|
|
<div className="field col-12">
|
|
<label className="font-bold">Lignes du devis :</label>
|
|
<DataTable value={lignes} size="normal">
|
|
<Column field="designation" header="Désignation" />
|
|
<Column
|
|
field="quantite"
|
|
header="Quantité"
|
|
body={(rowData) => `${rowData.quantite} ${rowData.unite}`}
|
|
/>
|
|
<Column
|
|
field="prixUnitaire"
|
|
header="Prix unitaire"
|
|
body={(rowData) => rowData.prixUnitaire.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' })}
|
|
/>
|
|
<Column
|
|
field="montantHT"
|
|
header="Montant HT"
|
|
body={(rowData) => rowData.montantHT.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' })}
|
|
/>
|
|
</DataTable>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-4">
|
|
<label className="font-bold">Montant HT :</label>
|
|
<p>{devis.montantHT?.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' })}</p>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-4">
|
|
<label className="font-bold">TVA ({devis.tauxTVA}%) :</label>
|
|
<p>{(devis.montantTTC - devis.montantHT)?.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' })}</p>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-4">
|
|
<label className="font-bold">Montant TTC :</label>
|
|
<p className="text-primary font-bold text-xl">{devis.montantTTC?.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' })}</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="grid">
|
|
<div className="col-12">
|
|
<Card>
|
|
<Toast ref={toast} />
|
|
|
|
<div className="flex justify-content-between align-items-center mb-4">
|
|
<h2>Nouveau Devis</h2>
|
|
<Button
|
|
icon="pi pi-arrow-left"
|
|
label="Retour"
|
|
className="p-button-text"
|
|
onClick={handleCancel}
|
|
/>
|
|
</div>
|
|
|
|
<Steps model={steps} activeIndex={activeIndex} className="mb-4" />
|
|
|
|
<form onSubmit={handleSubmit} className="p-fluid">
|
|
{renderStepContent()}
|
|
|
|
<Divider />
|
|
|
|
<div className="flex justify-content-between">
|
|
<Button
|
|
type="button"
|
|
label="Précédent"
|
|
icon="pi pi-chevron-left"
|
|
className="p-button-text"
|
|
onClick={prevStep}
|
|
disabled={activeIndex === 0}
|
|
/>
|
|
|
|
<div className="flex gap-2">
|
|
<Button
|
|
type="button"
|
|
label="Annuler"
|
|
icon="pi pi-times"
|
|
className="p-button-text"
|
|
onClick={handleCancel}
|
|
disabled={loading}
|
|
/>
|
|
|
|
{activeIndex < steps.length - 1 ? (
|
|
<Button
|
|
type="button"
|
|
label="Suivant"
|
|
icon="pi pi-chevron-right"
|
|
iconPos="right"
|
|
onClick={nextStep}
|
|
/>
|
|
) : (
|
|
<Button
|
|
type="submit"
|
|
label="Créer le devis"
|
|
icon="pi pi-check"
|
|
loading={loading}
|
|
disabled={loading}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default NouveauDevisPage;
|
|
|