Initial commit
This commit is contained in:
668
app/(main)/factures/templates/page.tsx
Normal file
668
app/(main)/factures/templates/page.tsx
Normal file
@@ -0,0 +1,668 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Card } from 'primereact/card';
|
||||
import { Button } from 'primereact/button';
|
||||
import { DataTable } from 'primereact/datatable';
|
||||
import { Column } from 'primereact/column';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import { InputTextarea } from 'primereact/inputtextarea';
|
||||
import { Dropdown } from 'primereact/dropdown';
|
||||
import { Tag } from 'primereact/tag';
|
||||
import { Toast } from 'primereact/toast';
|
||||
import { Toolbar } from 'primereact/toolbar';
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { ConfirmDialog } from 'primereact/confirmdialog';
|
||||
import { Menu } from 'primereact/menu';
|
||||
import { Badge } from 'primereact/badge';
|
||||
import { Checkbox } from 'primereact/checkbox';
|
||||
import { factureService } from '../../../../services/api';
|
||||
import { formatDate } from '../../../../utils/formatters';
|
||||
|
||||
interface FactureTemplate {
|
||||
id: string;
|
||||
nom: string;
|
||||
description: string;
|
||||
type: string;
|
||||
categorie: string;
|
||||
lignes: Array<{
|
||||
designation: string;
|
||||
quantite: number;
|
||||
unite: string;
|
||||
prixUnitaire: number;
|
||||
}>;
|
||||
tauxTVA: number;
|
||||
conditionsPaiement: string;
|
||||
actif: boolean;
|
||||
dateCreation: Date;
|
||||
utilisations: number;
|
||||
}
|
||||
|
||||
const FactureTemplatesPage = () => {
|
||||
const toast = useRef<Toast>(null);
|
||||
const menuRef = useRef<Menu>(null);
|
||||
|
||||
const [templates, setTemplates] = useState<FactureTemplate[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [showDialog, setShowDialog] = useState(false);
|
||||
const [editingTemplate, setEditingTemplate] = useState<FactureTemplate | null>(null);
|
||||
const [selectedTemplate, setSelectedTemplate] = useState<FactureTemplate | null>(null);
|
||||
const [globalFilter, setGlobalFilter] = useState('');
|
||||
|
||||
const [formData, setFormData] = useState<Partial<FactureTemplate>>({
|
||||
nom: '',
|
||||
description: '',
|
||||
type: 'FACTURE',
|
||||
categorie: '',
|
||||
lignes: [],
|
||||
tauxTVA: 20,
|
||||
conditionsPaiement: 'Paiement à 30 jours',
|
||||
actif: true
|
||||
});
|
||||
|
||||
const typeOptions = [
|
||||
{ label: 'Facture', value: 'FACTURE' },
|
||||
{ label: 'Acompte', value: 'ACOMPTE' },
|
||||
{ label: 'Facture de situation', value: 'SITUATION' },
|
||||
{ label: 'Facture de solde', value: 'SOLDE' }
|
||||
];
|
||||
|
||||
const categorieOptions = [
|
||||
{ label: 'Gros œuvre', value: 'GROS_OEUVRE' },
|
||||
{ label: 'Second œuvre', value: 'SECOND_OEUVRE' },
|
||||
{ label: 'Finitions', value: 'FINITIONS' },
|
||||
{ label: 'Plomberie', value: 'PLOMBERIE' },
|
||||
{ label: 'Électricité', value: 'ELECTRICITE' },
|
||||
{ label: 'Chauffage', value: 'CHAUFFAGE' },
|
||||
{ label: 'Rénovation', value: 'RENOVATION' },
|
||||
{ label: 'Maintenance', value: 'MAINTENANCE' }
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
loadTemplates();
|
||||
}, []);
|
||||
|
||||
const loadTemplates = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
// TODO: Remplacer par un vrai appel API
|
||||
// const response = await factureService.getTemplates();
|
||||
|
||||
// Données simulées pour la démonstration
|
||||
const mockTemplates: FactureTemplate[] = [
|
||||
{
|
||||
id: '1',
|
||||
nom: 'Facture Rénovation Standard',
|
||||
description: 'Template pour factures de rénovation complète',
|
||||
type: 'FACTURE',
|
||||
categorie: 'RENOVATION',
|
||||
lignes: [
|
||||
{ designation: 'Main d\'œuvre', quantite: 1, unite: 'forfait', prixUnitaire: 2500 },
|
||||
{ designation: 'Matériaux', quantite: 1, unite: 'forfait', prixUnitaire: 1800 },
|
||||
{ designation: 'Évacuation déchets', quantite: 1, unite: 'forfait', prixUnitaire: 300 }
|
||||
],
|
||||
tauxTVA: 20,
|
||||
conditionsPaiement: 'Paiement à 30 jours',
|
||||
actif: true,
|
||||
dateCreation: new Date('2024-01-15'),
|
||||
utilisations: 45
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
nom: 'Acompte Gros Œuvre',
|
||||
description: 'Template pour acomptes sur travaux de gros œuvre',
|
||||
type: 'ACOMPTE',
|
||||
categorie: 'GROS_OEUVRE',
|
||||
lignes: [
|
||||
{ designation: 'Acompte 30% - Fondations', quantite: 1, unite: 'forfait', prixUnitaire: 0 }
|
||||
],
|
||||
tauxTVA: 20,
|
||||
conditionsPaiement: 'Paiement à réception',
|
||||
actif: true,
|
||||
dateCreation: new Date('2024-02-10'),
|
||||
utilisations: 28
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
nom: 'Facture Maintenance Préventive',
|
||||
description: 'Template pour factures de maintenance préventive',
|
||||
type: 'FACTURE',
|
||||
categorie: 'MAINTENANCE',
|
||||
lignes: [
|
||||
{ designation: 'Contrôle technique', quantite: 1, unite: 'forfait', prixUnitaire: 150 },
|
||||
{ designation: 'Remplacement pièces', quantite: 1, unite: 'forfait', prixUnitaire: 200 },
|
||||
{ designation: 'Rapport de maintenance', quantite: 1, unite: 'forfait', prixUnitaire: 50 }
|
||||
],
|
||||
tauxTVA: 20,
|
||||
conditionsPaiement: 'Paiement à 15 jours',
|
||||
actif: true,
|
||||
dateCreation: new Date('2024-03-05'),
|
||||
utilisations: 67
|
||||
}
|
||||
];
|
||||
|
||||
setTemplates(mockTemplates);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Erreur lors du chargement des templates:', error);
|
||||
toast.current?.show({
|
||||
severity: 'error',
|
||||
summary: 'Erreur',
|
||||
detail: 'Impossible de charger les templates'
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleNew = () => {
|
||||
setEditingTemplate(null);
|
||||
setFormData({
|
||||
nom: '',
|
||||
description: '',
|
||||
type: 'FACTURE',
|
||||
categorie: '',
|
||||
lignes: [],
|
||||
tauxTVA: 20,
|
||||
conditionsPaiement: 'Paiement à 30 jours',
|
||||
actif: true
|
||||
});
|
||||
setShowDialog(true);
|
||||
};
|
||||
|
||||
const handleEdit = (template: FactureTemplate) => {
|
||||
setEditingTemplate(template);
|
||||
setFormData({ ...template });
|
||||
setShowDialog(true);
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
if (editingTemplate) {
|
||||
// TODO: Appel API pour mise à jour
|
||||
// await factureService.updateTemplate(editingTemplate.id, formData);
|
||||
toast.current?.show({
|
||||
severity: 'success',
|
||||
summary: 'Succès',
|
||||
detail: 'Template modifié avec succès'
|
||||
});
|
||||
} else {
|
||||
// TODO: Appel API pour création
|
||||
// await factureService.createTemplate(formData);
|
||||
toast.current?.show({
|
||||
severity: 'success',
|
||||
summary: 'Succès',
|
||||
detail: 'Template créé avec succès'
|
||||
});
|
||||
}
|
||||
|
||||
setShowDialog(false);
|
||||
loadTemplates();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la sauvegarde:', error);
|
||||
toast.current?.show({
|
||||
severity: 'error',
|
||||
summary: 'Erreur',
|
||||
detail: 'Erreur lors de la sauvegarde'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (template: FactureTemplate) => {
|
||||
try {
|
||||
// TODO: Appel API pour suppression
|
||||
// await factureService.deleteTemplate(template.id);
|
||||
|
||||
toast.current?.show({
|
||||
severity: 'success',
|
||||
summary: 'Succès',
|
||||
detail: 'Template supprimé avec succès'
|
||||
});
|
||||
|
||||
loadTemplates();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la suppression:', error);
|
||||
toast.current?.show({
|
||||
severity: 'error',
|
||||
summary: 'Erreur',
|
||||
detail: 'Erreur lors de la suppression'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleUseTemplate = async (template: FactureTemplate) => {
|
||||
try {
|
||||
// TODO: Créer une nouvelle facture basée sur le template
|
||||
toast.current?.show({
|
||||
severity: 'info',
|
||||
summary: 'Info',
|
||||
detail: 'Redirection vers la création de facture...'
|
||||
});
|
||||
|
||||
// Simuler la redirection
|
||||
setTimeout(() => {
|
||||
window.location.href = `/factures/nouveau?template=${template.id}`;
|
||||
}, 1000);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de l\'utilisation du template:', error);
|
||||
toast.current?.show({
|
||||
severity: 'error',
|
||||
summary: 'Erreur',
|
||||
detail: 'Erreur lors de l\'utilisation du template'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const getMenuItems = (template: FactureTemplate) => [
|
||||
{
|
||||
label: 'Utiliser',
|
||||
icon: 'pi pi-plus',
|
||||
command: () => handleUseTemplate(template)
|
||||
},
|
||||
{
|
||||
label: 'Modifier',
|
||||
icon: 'pi pi-pencil',
|
||||
command: () => handleEdit(template)
|
||||
},
|
||||
{
|
||||
label: 'Dupliquer',
|
||||
icon: 'pi pi-copy',
|
||||
command: () => {
|
||||
setEditingTemplate(null);
|
||||
setFormData({
|
||||
...template,
|
||||
nom: `${template.nom} (Copie)`,
|
||||
id: undefined
|
||||
});
|
||||
setShowDialog(true);
|
||||
}
|
||||
},
|
||||
{
|
||||
separator: true
|
||||
},
|
||||
{
|
||||
label: 'Supprimer',
|
||||
icon: 'pi pi-trash',
|
||||
className: 'text-red-500',
|
||||
command: () => {
|
||||
setSelectedTemplate(template);
|
||||
// TODO: Afficher dialog de confirmation
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const actionBodyTemplate = (rowData: FactureTemplate) => (
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
icon="pi pi-plus"
|
||||
className="p-button-text p-button-sm p-button-success"
|
||||
tooltip="Utiliser ce template"
|
||||
onClick={() => handleUseTemplate(rowData)}
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-ellipsis-v"
|
||||
className="p-button-text p-button-sm"
|
||||
onClick={(e) => {
|
||||
setSelectedTemplate(rowData);
|
||||
menuRef.current?.toggle(e);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const typeBodyTemplate = (rowData: FactureTemplate) => (
|
||||
<Tag
|
||||
value={rowData.type}
|
||||
severity={rowData.type === 'FACTURE' ? 'primary' : 'info'}
|
||||
/>
|
||||
);
|
||||
|
||||
const categorieBodyTemplate = (rowData: FactureTemplate) => {
|
||||
const categorie = categorieOptions.find(opt => opt.value === rowData.categorie);
|
||||
return (
|
||||
<Tag
|
||||
value={categorie?.label || rowData.categorie}
|
||||
severity="info"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const statutBodyTemplate = (rowData: FactureTemplate) => (
|
||||
<Tag
|
||||
value={rowData.actif ? 'Actif' : 'Inactif'}
|
||||
severity={rowData.actif ? 'success' : 'danger'}
|
||||
/>
|
||||
);
|
||||
|
||||
const utilisationsBodyTemplate = (rowData: FactureTemplate) => (
|
||||
<Badge value={rowData.utilisations} severity="info" />
|
||||
);
|
||||
|
||||
const toolbarStartTemplate = () => (
|
||||
<div className="flex align-items-center gap-2">
|
||||
<h2 className="text-xl font-bold m-0">Templates de Factures</h2>
|
||||
</div>
|
||||
);
|
||||
|
||||
const toolbarEndTemplate = () => (
|
||||
<div className="flex align-items-center gap-2">
|
||||
<span className="p-input-icon-left">
|
||||
<i className="pi pi-search" />
|
||||
<InputText
|
||||
value={globalFilter}
|
||||
onChange={(e) => setGlobalFilter(e.target.value)}
|
||||
placeholder="Rechercher..."
|
||||
/>
|
||||
</span>
|
||||
<Button
|
||||
label="Nouveau Template"
|
||||
icon="pi pi-plus"
|
||||
onClick={handleNew}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="grid">
|
||||
<Toast ref={toast} />
|
||||
<ConfirmDialog />
|
||||
<Menu ref={menuRef} model={selectedTemplate ? getMenuItems(selectedTemplate) : []} popup />
|
||||
|
||||
<div className="col-12">
|
||||
<Toolbar start={toolbarStartTemplate} end={toolbarEndTemplate} />
|
||||
</div>
|
||||
|
||||
{/* Statistiques rapides */}
|
||||
<div className="col-12 lg:col-3 md:col-6">
|
||||
<Card className="h-full">
|
||||
<div className="flex justify-content-between mb-3">
|
||||
<div>
|
||||
<span className="block text-500 font-medium mb-3">Total Templates</span>
|
||||
<div className="text-900 font-medium text-xl">{templates.length}</div>
|
||||
</div>
|
||||
<div className="flex align-items-center justify-content-center bg-blue-100 border-round" style={{ width: '2.5rem', height: '2.5rem' }}>
|
||||
<i className="pi pi-file text-blue-500 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="col-12 lg:col-3 md:col-6">
|
||||
<Card className="h-full">
|
||||
<div className="flex justify-content-between mb-3">
|
||||
<div>
|
||||
<span className="block text-500 font-medium mb-3">Templates Actifs</span>
|
||||
<div className="text-900 font-medium text-xl">
|
||||
{templates.filter(t => t.actif).length}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex align-items-center justify-content-center bg-green-100 border-round" style={{ width: '2.5rem', height: '2.5rem' }}>
|
||||
<i className="pi pi-check-circle text-green-500 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="col-12 lg:col-3 md:col-6">
|
||||
<Card className="h-full">
|
||||
<div className="flex justify-content-between mb-3">
|
||||
<div>
|
||||
<span className="block text-500 font-medium mb-3">Plus Utilisé</span>
|
||||
<div className="text-900 font-medium text-xl">
|
||||
{Math.max(...templates.map(t => t.utilisations), 0)} fois
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex align-items-center justify-content-center bg-orange-100 border-round" style={{ width: '2.5rem', height: '2.5rem' }}>
|
||||
<i className="pi pi-star text-orange-500 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="col-12 lg:col-3 md:col-6">
|
||||
<Card className="h-full">
|
||||
<div className="flex justify-content-between mb-3">
|
||||
<div>
|
||||
<span className="block text-500 font-medium mb-3">Types</span>
|
||||
<div className="text-900 font-medium text-xl">
|
||||
{new Set(templates.map(t => t.type)).size}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex align-items-center justify-content-center bg-purple-100 border-round" style={{ width: '2.5rem', height: '2.5rem' }}>
|
||||
<i className="pi pi-tags text-purple-500 text-xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Table des templates */}
|
||||
<div className="col-12">
|
||||
<Card>
|
||||
<DataTable
|
||||
value={templates}
|
||||
loading={loading}
|
||||
globalFilter={globalFilter}
|
||||
responsiveLayout="scroll"
|
||||
paginator
|
||||
rows={10}
|
||||
rowsPerPageOptions={[5, 10, 25]}
|
||||
emptyMessage="Aucun template trouvé"
|
||||
header={
|
||||
<div className="flex justify-content-between align-items-center">
|
||||
<span className="text-xl font-bold">Liste des Templates</span>
|
||||
<span className="text-sm text-600">{templates.length} template(s)</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Column field="nom" header="Nom" sortable />
|
||||
<Column field="description" header="Description" />
|
||||
<Column
|
||||
field="type"
|
||||
header="Type"
|
||||
body={typeBodyTemplate}
|
||||
sortable
|
||||
/>
|
||||
<Column
|
||||
field="categorie"
|
||||
header="Catégorie"
|
||||
body={categorieBodyTemplate}
|
||||
sortable
|
||||
/>
|
||||
<Column
|
||||
field="lignes"
|
||||
header="Nb Lignes"
|
||||
body={(rowData) => rowData.lignes?.length || 0}
|
||||
style={{ width: '100px' }}
|
||||
/>
|
||||
<Column
|
||||
field="utilisations"
|
||||
header="Utilisations"
|
||||
body={utilisationsBodyTemplate}
|
||||
sortable
|
||||
style={{ width: '120px' }}
|
||||
/>
|
||||
<Column
|
||||
field="actif"
|
||||
header="Statut"
|
||||
body={statutBodyTemplate}
|
||||
style={{ width: '100px' }}
|
||||
/>
|
||||
<Column
|
||||
field="dateCreation"
|
||||
header="Créé le"
|
||||
body={(rowData) => formatDate(rowData.dateCreation)}
|
||||
sortable
|
||||
style={{ width: '120px' }}
|
||||
/>
|
||||
<Column
|
||||
header="Actions"
|
||||
body={actionBodyTemplate}
|
||||
style={{ width: '120px' }}
|
||||
/>
|
||||
</DataTable>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Dialog de création/modification */}
|
||||
<Dialog
|
||||
header={editingTemplate ? "Modifier le template" : "Nouveau template"}
|
||||
visible={showDialog}
|
||||
onHide={() => setShowDialog(false)}
|
||||
style={{ width: '800px' }}
|
||||
footer={
|
||||
<div className="flex justify-content-end gap-2">
|
||||
<Button
|
||||
label="Annuler"
|
||||
icon="pi pi-times"
|
||||
className="p-button-outlined"
|
||||
onClick={() => setShowDialog(false)}
|
||||
/>
|
||||
<Button
|
||||
label="Enregistrer"
|
||||
icon="pi pi-save"
|
||||
onClick={handleSave}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="grid">
|
||||
<div className="col-12 md:col-6">
|
||||
<div className="field">
|
||||
<label htmlFor="nom" className="font-semibold">Nom *</label>
|
||||
<InputText
|
||||
id="nom"
|
||||
value={formData.nom}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, nom: e.target.value }))}
|
||||
className="w-full"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-12 md:col-6">
|
||||
<div className="field">
|
||||
<label htmlFor="type" className="font-semibold">Type *</label>
|
||||
<Dropdown
|
||||
id="type"
|
||||
value={formData.type}
|
||||
options={typeOptions}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, type: e.value }))}
|
||||
className="w-full"
|
||||
placeholder="Sélectionner un type"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-12">
|
||||
<div className="field">
|
||||
<label htmlFor="categorie" className="font-semibold">Catégorie *</label>
|
||||
<Dropdown
|
||||
id="categorie"
|
||||
value={formData.categorie}
|
||||
options={categorieOptions}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, categorie: e.value }))}
|
||||
className="w-full"
|
||||
placeholder="Sélectionner une catégorie"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-12">
|
||||
<div className="field">
|
||||
<label htmlFor="description" className="font-semibold">Description</label>
|
||||
<InputTextarea
|
||||
id="description"
|
||||
value={formData.description}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, description: e.target.value }))}
|
||||
className="w-full"
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-12 md:col-6">
|
||||
<div className="field">
|
||||
<label htmlFor="tauxTVA" className="font-semibold">Taux TVA (%)</label>
|
||||
<InputText
|
||||
id="tauxTVA"
|
||||
value={formData.tauxTVA?.toString()}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, tauxTVA: parseFloat(e.target.value) || 0 }))}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-12 md:col-6">
|
||||
<div className="field">
|
||||
<label htmlFor="conditionsPaiement" className="font-semibold">Conditions de paiement</label>
|
||||
<InputText
|
||||
id="conditionsPaiement"
|
||||
value={formData.conditionsPaiement}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, conditionsPaiement: e.target.value }))}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-12">
|
||||
<div className="field">
|
||||
<div className="flex align-items-center">
|
||||
<Checkbox
|
||||
inputId="actif"
|
||||
checked={formData.actif}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, actif: e.checked || false }))}
|
||||
/>
|
||||
<label htmlFor="actif" className="ml-2">Template actif</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-12">
|
||||
<h6>Lignes du template</h6>
|
||||
<p className="text-sm text-600 mb-3">
|
||||
Les lignes seront automatiquement ajoutées lors de l'utilisation du template.
|
||||
</p>
|
||||
|
||||
{formData.lignes && formData.lignes.length > 0 ? (
|
||||
<DataTable value={formData.lignes} responsiveLayout="scroll">
|
||||
<Column field="designation" header="Désignation" />
|
||||
<Column field="quantite" header="Quantité" style={{ width: '100px' }} />
|
||||
<Column field="unite" header="Unité" style={{ width: '80px' }} />
|
||||
<Column
|
||||
field="prixUnitaire"
|
||||
header="Prix unitaire"
|
||||
style={{ width: '120px' }}
|
||||
body={(rowData) => `${rowData.prixUnitaire}€`}
|
||||
/>
|
||||
</DataTable>
|
||||
) : (
|
||||
<div className="text-center p-4 border-2 border-dashed border-300 border-round">
|
||||
<i className="pi pi-inbox text-4xl text-400 mb-3"></i>
|
||||
<p className="text-600">Aucune ligne définie</p>
|
||||
<Button
|
||||
label="Ajouter des lignes"
|
||||
icon="pi pi-plus"
|
||||
className="p-button-outlined p-button-sm"
|
||||
onClick={() => {
|
||||
// TODO: Ouvrir dialog pour ajouter des lignes
|
||||
toast.current?.show({
|
||||
severity: 'info',
|
||||
summary: 'Info',
|
||||
detail: 'Fonctionnalité en cours de développement'
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FactureTemplatesPage;
|
||||
Reference in New Issue
Block a user