- Correction des erreurs TypeScript dans userService.ts et workflowTester.ts - Ajout des propriétés manquantes aux objets User mockés - Conversion des dates de string vers objets Date - Correction des appels asynchrones et des types incompatibles - Ajout de dynamic rendering pour résoudre les erreurs useSearchParams - Enveloppement de useSearchParams dans Suspense boundary - Configuration de force-dynamic au niveau du layout principal Build réussi: 126 pages générées avec succès 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
679 lines
28 KiB
TypeScript
679 lines
28 KiB
TypeScript
'use client';
|
|
export const dynamic = 'force-dynamic';
|
|
|
|
|
|
import React, { useState, useEffect, useRef } from 'react';
|
|
import { Card } from 'primereact/card';
|
|
import { DataTable } from 'primereact/datatable';
|
|
import { Column } from 'primereact/column';
|
|
import { Button } from 'primereact/button';
|
|
import { Tag } from 'primereact/tag';
|
|
import { Dialog } from 'primereact/dialog';
|
|
import { InputText } from 'primereact/inputtext';
|
|
import { InputNumber } from 'primereact/inputnumber';
|
|
import { Toast } from 'primereact/toast';
|
|
import { Divider } from 'primereact/divider';
|
|
import { Badge } from 'primereact/badge';
|
|
import { ProgressBar } from 'primereact/progressbar';
|
|
import { Dropdown } from 'primereact/dropdown';
|
|
import { Calendar } from 'primereact/calendar';
|
|
import { InputTextarea } from 'primereact/inputtextarea';
|
|
import { Knob } from 'primereact/knob';
|
|
import { Toolbar } from 'primereact/toolbar';
|
|
import { Checkbox } from 'primereact/checkbox';
|
|
import { Chart } from 'primereact/chart';
|
|
import {
|
|
ActionButtonGroup,
|
|
ViewButton,
|
|
EditButton,
|
|
DeleteButton,
|
|
ActionButton
|
|
} from '../../../../components/ui/ActionButton';
|
|
|
|
// Types pour l'inventaire
|
|
interface InventoryItem {
|
|
id: string;
|
|
reference: string;
|
|
nom: string;
|
|
categorie: string;
|
|
unite: string;
|
|
stockTheorique: number;
|
|
stockReel: number;
|
|
ecart: number;
|
|
valeurStock: number;
|
|
emplacement: string;
|
|
dateInventaire: Date;
|
|
auditeur: string;
|
|
statut: 'OK' | 'ECART' | 'MANQUANT' | 'EXCEDENT';
|
|
}
|
|
|
|
interface InventorySession {
|
|
id: string;
|
|
nom: string;
|
|
dateDebut: Date;
|
|
dateFin?: Date;
|
|
statut: 'PLANIFIE' | 'EN_COURS' | 'TERMINE';
|
|
emplacements: string[];
|
|
categories: string[];
|
|
auditeur: string;
|
|
commentaires: string;
|
|
}
|
|
|
|
/**
|
|
* Page Inventaire Matériel BTP Express
|
|
* Gestion complète stocks, maintenances et alertes matériel
|
|
*/
|
|
const InventairePage = () => {
|
|
const [inventoryItems, setInventoryItems] = useState<InventoryItem[]>([]);
|
|
const [sessions, setSessions] = useState<InventorySession[]>([]);
|
|
const [selectedItems, setSelectedItems] = useState<InventoryItem[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [inventaireDialog, setInventaireDialog] = useState(false);
|
|
const [maintenanceDialog, setMaintenanceDialog] = useState(false);
|
|
const [alertesDialog, setAlertesDialog] = useState(false);
|
|
const [selectedMateriel, setSelectedMateriel] = useState<any>(null);
|
|
const [nouvelleQuantite, setNouvelleQuantite] = useState<number>(0);
|
|
const [commentaireInventaire, setCommentaireInventaire] = useState('');
|
|
const [dateMaintenance, setDateMaintenance] = useState<Date | null>(null);
|
|
const [typeMaintenance, setTypeMaintenance] = useState('');
|
|
const [descriptionMaintenance, setDescriptionMaintenance] = useState('');
|
|
const [alertes, setAlertes] = useState<any[]>([]);
|
|
const [metriques, setMetriques] = useState<any>({});
|
|
const [globalFilter, setGlobalFilter] = useState('');
|
|
const [sessionDialog, setSessionDialog] = useState(false);
|
|
const [inventoryDialog, setInventoryDialog] = useState(false);
|
|
const [selectedSession, setSelectedSession] = useState<InventorySession | null>(null);
|
|
const [showStats, setShowStats] = useState(false);
|
|
const [submitted, setSubmitted] = useState(false);
|
|
const toast = useRef<Toast>(null);
|
|
const dt = useRef<DataTable<InventoryItem[]>>(null);
|
|
|
|
const [newSession, setNewSession] = useState<InventorySession>({
|
|
id: '',
|
|
nom: '',
|
|
dateDebut: new Date(),
|
|
statut: 'PLANIFIE',
|
|
emplacements: [],
|
|
categories: [],
|
|
auditeur: '',
|
|
commentaires: ''
|
|
});
|
|
|
|
const emplacements = [
|
|
{ label: 'Entrepôt A', value: 'entrepot-a' },
|
|
{ label: 'Entrepôt B', value: 'entrepot-b' },
|
|
{ label: 'Magasin', value: 'magasin' },
|
|
{ label: 'Chantier Mobile', value: 'chantier-mobile' },
|
|
{ label: 'Bureau', value: 'bureau' }
|
|
];
|
|
|
|
const categories = [
|
|
{ label: 'Matériaux', value: 'materiaux' },
|
|
{ label: 'Outillage', value: 'outillage' },
|
|
{ label: 'Équipement', value: 'equipement' },
|
|
{ label: 'Consommables', value: 'consommables' },
|
|
{ label: 'Sécurité', value: 'securite' }
|
|
];
|
|
|
|
useEffect(() => {
|
|
loadInventoryData();
|
|
}, []);
|
|
|
|
const loadInventoryData = async () => {
|
|
try {
|
|
setLoading(true);
|
|
|
|
// Données mockées
|
|
const mockInventoryItems: InventoryItem[] = [
|
|
{
|
|
id: '1',
|
|
reference: 'CIM-001',
|
|
nom: 'Ciment Portland',
|
|
categorie: 'materiaux',
|
|
unite: 'sac',
|
|
stockTheorique: 150,
|
|
stockReel: 148,
|
|
ecart: -2,
|
|
valeurStock: 1258.00,
|
|
emplacement: 'entrepot-a',
|
|
dateInventaire: new Date(),
|
|
auditeur: 'Jean Dupont',
|
|
statut: 'ECART'
|
|
},
|
|
{
|
|
id: '2',
|
|
reference: 'OUT-002',
|
|
nom: 'Perceuse électrique',
|
|
categorie: 'outillage',
|
|
unite: 'unité',
|
|
stockTheorique: 5,
|
|
stockReel: 5,
|
|
ecart: 0,
|
|
valeurStock: 600.00,
|
|
emplacement: 'magasin',
|
|
dateInventaire: new Date(),
|
|
auditeur: 'Marie Martin',
|
|
statut: 'OK'
|
|
},
|
|
{
|
|
id: '3',
|
|
reference: 'SEC-003',
|
|
nom: 'Casque de sécurité',
|
|
categorie: 'securite',
|
|
unite: 'unité',
|
|
stockTheorique: 8,
|
|
stockReel: 10,
|
|
ecart: 2,
|
|
valeurStock: 250.00,
|
|
emplacement: 'bureau',
|
|
dateInventaire: new Date(),
|
|
auditeur: 'Pierre Durand',
|
|
statut: 'EXCEDENT'
|
|
}
|
|
];
|
|
|
|
const mockSessions: InventorySession[] = [
|
|
{
|
|
id: '1',
|
|
nom: 'Inventaire Mensuel Janvier 2024',
|
|
dateDebut: new Date('2024-01-15'),
|
|
dateFin: new Date('2024-01-17'),
|
|
statut: 'TERMINE',
|
|
emplacements: ['entrepot-a', 'magasin'],
|
|
categories: ['materiaux', 'outillage'],
|
|
auditeur: 'Équipe Inventaire',
|
|
commentaires: 'Inventaire mensuel standard'
|
|
},
|
|
{
|
|
id: '2',
|
|
nom: 'Inventaire Sécurité',
|
|
dateDebut: new Date(),
|
|
statut: 'EN_COURS',
|
|
emplacements: ['bureau'],
|
|
categories: ['securite'],
|
|
auditeur: 'Pierre Durand',
|
|
commentaires: 'Contrôle EPI obligatoire'
|
|
}
|
|
];
|
|
|
|
setInventoryItems(mockInventoryItems);
|
|
setSessions(mockSessions);
|
|
} catch (error) {
|
|
console.error('Erreur lors du chargement:', error);
|
|
toast.current?.show({
|
|
severity: 'error',
|
|
summary: 'Erreur',
|
|
detail: 'Impossible de charger les données d\'inventaire',
|
|
life: 3000
|
|
});
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const openNewSession = () => {
|
|
setNewSession({
|
|
id: '',
|
|
nom: '',
|
|
dateDebut: new Date(),
|
|
statut: 'PLANIFIE',
|
|
emplacements: [],
|
|
categories: [],
|
|
auditeur: '',
|
|
commentaires: ''
|
|
});
|
|
setSubmitted(false);
|
|
setSessionDialog(true);
|
|
};
|
|
|
|
const saveSession = () => {
|
|
setSubmitted(true);
|
|
|
|
if (newSession.nom.trim() && newSession.auditeur.trim()) {
|
|
const session = {
|
|
...newSession,
|
|
id: Date.now().toString()
|
|
};
|
|
|
|
setSessions([...sessions, session]);
|
|
setSessionDialog(false);
|
|
|
|
toast.current?.show({
|
|
severity: 'success',
|
|
summary: 'Succès',
|
|
detail: 'Session d\'inventaire créée',
|
|
life: 3000
|
|
});
|
|
}
|
|
};
|
|
|
|
const startInventory = (session: InventorySession) => {
|
|
const updatedSessions = sessions.map(s =>
|
|
s.id === session.id
|
|
? { ...s, statut: 'EN_COURS' as const }
|
|
: s
|
|
);
|
|
setSessions(updatedSessions);
|
|
|
|
toast.current?.show({
|
|
severity: 'info',
|
|
summary: 'Inventaire démarré',
|
|
detail: `Session "${session.nom}" en cours`,
|
|
life: 3000
|
|
});
|
|
};
|
|
|
|
const finishInventory = (session: InventorySession) => {
|
|
const updatedSessions = sessions.map(s =>
|
|
s.id === session.id
|
|
? { ...s, statut: 'TERMINE' as const, dateFin: new Date() }
|
|
: s
|
|
);
|
|
setSessions(updatedSessions);
|
|
|
|
toast.current?.show({
|
|
severity: 'success',
|
|
summary: 'Inventaire terminé',
|
|
detail: `Session "${session.nom}" terminée`,
|
|
life: 3000
|
|
});
|
|
};
|
|
|
|
const exportCSV = () => {
|
|
dt.current?.exportCSV();
|
|
};
|
|
|
|
const generateReport = () => {
|
|
const totalItems = inventoryItems.length;
|
|
const itemsWithIssues = inventoryItems.filter(item => item.statut !== 'OK').length;
|
|
const totalValue = inventoryItems.reduce((sum, item) => sum + item.valeurStock, 0);
|
|
|
|
toast.current?.show({
|
|
severity: 'info',
|
|
summary: 'Rapport généré',
|
|
detail: `${totalItems} articles, ${itemsWithIssues} écarts, Valeur: ${totalValue.toFixed(2)}€`,
|
|
life: 5000
|
|
});
|
|
setShowStats(true);
|
|
};
|
|
|
|
const leftToolbarTemplate = () => {
|
|
return (
|
|
<div className="flex flex-wrap gap-2">
|
|
<Button
|
|
label="Nouvelle Session"
|
|
icon="pi pi-plus"
|
|
severity="success"
|
|
onClick={openNewSession}
|
|
/>
|
|
<Button
|
|
label="Rapport"
|
|
icon="pi pi-chart-bar"
|
|
severity="info"
|
|
onClick={generateReport}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const rightToolbarTemplate = () => {
|
|
return (
|
|
<Button
|
|
label="Exporter"
|
|
icon="pi pi-upload"
|
|
severity="help"
|
|
onClick={exportCSV}
|
|
/>
|
|
);
|
|
};
|
|
|
|
const statusBodyTemplate = (rowData: InventoryItem) => {
|
|
let severity: "success" | "warning" | "danger" | "info" = 'success';
|
|
let label: string = rowData.statut;
|
|
|
|
switch (rowData.statut) {
|
|
case 'OK':
|
|
severity = 'success';
|
|
label = 'OK';
|
|
break;
|
|
case 'ECART':
|
|
severity = 'warning';
|
|
label = 'Écart';
|
|
break;
|
|
case 'MANQUANT':
|
|
severity = 'danger';
|
|
label = 'Manquant';
|
|
break;
|
|
case 'EXCEDENT':
|
|
severity = 'info';
|
|
label = 'Excédent';
|
|
break;
|
|
}
|
|
|
|
return <Tag value={label} severity={severity} />;
|
|
};
|
|
|
|
const ecartBodyTemplate = (rowData: InventoryItem) => {
|
|
const ecart = rowData.ecart;
|
|
const className = ecart === 0 ? '' : ecart > 0 ? 'text-green-600' : 'text-red-600';
|
|
return <span className={className}>{ecart > 0 ? '+' : ''}{ecart}</span>;
|
|
};
|
|
|
|
const valeurBodyTemplate = (rowData: InventoryItem) => {
|
|
return new Intl.NumberFormat('fr-FR', {
|
|
style: 'currency',
|
|
currency: 'EUR'
|
|
}).format(rowData.valeurStock);
|
|
};
|
|
|
|
const categorieBodyTemplate = (rowData: InventoryItem) => {
|
|
const categorie = categories.find(c => c.value === rowData.categorie);
|
|
return categorie ? categorie.label : rowData.categorie;
|
|
};
|
|
|
|
const emplacementBodyTemplate = (rowData: InventoryItem) => {
|
|
const emplacement = emplacements.find(e => e.value === rowData.emplacement);
|
|
return emplacement ? emplacement.label : rowData.emplacement;
|
|
};
|
|
|
|
const sessionActionTemplate = (rowData: InventorySession) => {
|
|
return (
|
|
<ActionButtonGroup>
|
|
{rowData.statut === 'PLANIFIE' && (
|
|
<ActionButton
|
|
icon="pi pi-play"
|
|
color="green"
|
|
tooltip="Démarrer"
|
|
onClick={() => startInventory(rowData)}
|
|
/>
|
|
)}
|
|
{rowData.statut === 'EN_COURS' && (
|
|
<ActionButton
|
|
icon="pi pi-check"
|
|
color="blue"
|
|
tooltip="Terminer"
|
|
onClick={() => finishInventory(rowData)}
|
|
/>
|
|
)}
|
|
<ViewButton
|
|
tooltip="Voir détails"
|
|
onClick={() => setSelectedSession(rowData)}
|
|
/>
|
|
</ActionButtonGroup>
|
|
);
|
|
};
|
|
|
|
const sessionStatusTemplate = (rowData: InventorySession) => {
|
|
let severity: "success" | "warning" | "danger" | "info" = 'info';
|
|
let label: string = rowData.statut;
|
|
|
|
switch (rowData.statut) {
|
|
case 'PLANIFIE':
|
|
severity = 'info';
|
|
label = 'Planifié';
|
|
break;
|
|
case 'EN_COURS':
|
|
severity = 'warning';
|
|
label = 'En cours';
|
|
break;
|
|
case 'TERMINE':
|
|
severity = 'success';
|
|
label = 'Terminé';
|
|
break;
|
|
}
|
|
|
|
return <Tag value={label} severity={severity} />;
|
|
};
|
|
|
|
const header = (
|
|
<div className="flex flex-column md:flex-row md:justify-content-between md:align-items-center">
|
|
<h5 className="m-0">Inventaire du Stock</h5>
|
|
<span className="block mt-2 md:mt-0 p-input-icon-left">
|
|
<i className="pi pi-search" />
|
|
<InputText
|
|
type="search"
|
|
placeholder="Rechercher..."
|
|
onInput={(e) => setGlobalFilter(e.currentTarget.value)}
|
|
/>
|
|
</span>
|
|
</div>
|
|
);
|
|
|
|
const sessionDialogFooter = (
|
|
<div className="flex justify-content-end gap-2">
|
|
<Button label="Annuler" icon="pi pi-times" text onClick={() => setSessionDialog(false)} />
|
|
<Button label="Créer" icon="pi pi-check" onClick={saveSession} />
|
|
</div>
|
|
);
|
|
|
|
// Statistiques pour le graphique
|
|
const getStatsData = () => {
|
|
const statsOK = inventoryItems.filter(item => item.statut === 'OK').length;
|
|
const statsEcart = inventoryItems.filter(item => item.statut === 'ECART').length;
|
|
const statsManquant = inventoryItems.filter(item => item.statut === 'MANQUANT').length;
|
|
const statsExcedent = inventoryItems.filter(item => item.statut === 'EXCEDENT').length;
|
|
|
|
return {
|
|
labels: ['OK', 'Écart', 'Manquant', 'Excédent'],
|
|
datasets: [
|
|
{
|
|
data: [statsOK, statsEcart, statsManquant, statsExcedent],
|
|
backgroundColor: ['#10B981', '#F59E0B', '#EF4444', '#3B82F6'],
|
|
hoverBackgroundColor: ['#059669', '#D97706', '#DC2626', '#2563EB']
|
|
}
|
|
]
|
|
};
|
|
};
|
|
|
|
return (
|
|
<div className="grid">
|
|
<div className="col-12">
|
|
<Card>
|
|
<Toast ref={toast} />
|
|
|
|
{/* Sessions d'inventaire */}
|
|
<div className="mb-4">
|
|
<h6>Sessions d'inventaire</h6>
|
|
<DataTable
|
|
value={sessions}
|
|
responsiveLayout="scroll"
|
|
className="mb-4"
|
|
>
|
|
<Column field="nom" header="Nom de la session" />
|
|
<Column field="dateDebut" header="Date début" body={(rowData) => rowData.dateDebut.toLocaleDateString()} />
|
|
<Column field="dateFin" header="Date fin" body={(rowData) => rowData.dateFin?.toLocaleDateString() || '-'} />
|
|
<Column field="statut" header="Statut" body={sessionStatusTemplate} />
|
|
<Column field="auditeur" header="Auditeur" />
|
|
<Column body={sessionActionTemplate} headerStyle={{ minWidth: '8rem' }} />
|
|
</DataTable>
|
|
</div>
|
|
|
|
<Toolbar className="mb-4" left={leftToolbarTemplate} right={rightToolbarTemplate} />
|
|
|
|
{showStats && (
|
|
<Card className="mb-4" title="Statistiques d'inventaire">
|
|
<div className="grid">
|
|
<div className="col-12 md:col-6">
|
|
<Chart type="doughnut" data={getStatsData()} />
|
|
</div>
|
|
<div className="col-12 md:col-6">
|
|
<div className="grid">
|
|
<div className="col-6">
|
|
<div className="text-center">
|
|
<div className="text-2xl font-semibold text-green-600">
|
|
{inventoryItems.filter(item => item.statut === 'OK').length}
|
|
</div>
|
|
<div className="text-sm">Articles OK</div>
|
|
</div>
|
|
</div>
|
|
<div className="col-6">
|
|
<div className="text-center">
|
|
<div className="text-2xl font-semibold text-orange-600">
|
|
{inventoryItems.filter(item => item.statut !== 'OK').length}
|
|
</div>
|
|
<div className="text-sm">Avec écarts</div>
|
|
</div>
|
|
</div>
|
|
<div className="col-6">
|
|
<div className="text-center">
|
|
<div className="text-2xl font-semibold text-blue-600">
|
|
{inventoryItems.reduce((sum, item) => sum + item.valeurStock, 0).toFixed(0)}€
|
|
</div>
|
|
<div className="text-sm">Valeur totale</div>
|
|
</div>
|
|
</div>
|
|
<div className="col-6">
|
|
<div className="text-center">
|
|
<div className="text-2xl font-semibold text-purple-600">
|
|
{Math.round((inventoryItems.filter(item => item.statut === 'OK').length / inventoryItems.length) * 100)}%
|
|
</div>
|
|
<div className="text-sm">Conformité</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
)}
|
|
|
|
<DataTable
|
|
ref={dt}
|
|
value={inventoryItems}
|
|
selection={selectedItems}
|
|
onSelectionChange={(e) => setSelectedItems(e.value)}
|
|
selectionMode="checkbox"
|
|
dataKey="id"
|
|
paginator
|
|
rows={10}
|
|
rowsPerPageOptions={[5, 10, 25]}
|
|
className="datatable-responsive"
|
|
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown"
|
|
currentPageReportTemplate="Affichage de {first} à {last} sur {totalRecords} articles"
|
|
globalFilter={globalFilter}
|
|
emptyMessage="Aucun article trouvé."
|
|
header={header}
|
|
responsiveLayout="scroll"
|
|
loading={loading}
|
|
>
|
|
<Column selectionMode="multiple" headerStyle={{ width: '4rem' }} />
|
|
<Column field="reference" header="Référence" sortable />
|
|
<Column field="nom" header="Nom" sortable />
|
|
<Column field="categorie" header="Catégorie" body={categorieBodyTemplate} sortable />
|
|
<Column field="stockTheorique" header="Stock théorique" sortable />
|
|
<Column field="stockReel" header="Stock réel" sortable />
|
|
<Column field="ecart" header="Écart" body={ecartBodyTemplate} sortable />
|
|
<Column field="valeurStock" header="Valeur" body={valeurBodyTemplate} sortable />
|
|
<Column field="emplacement" header="Emplacement" body={emplacementBodyTemplate} sortable />
|
|
<Column field="statut" header="Statut" body={statusBodyTemplate} sortable />
|
|
<Column field="auditeur" header="Auditeur" sortable />
|
|
</DataTable>
|
|
|
|
{/* Dialog pour nouvelle session */}
|
|
<Dialog
|
|
visible={sessionDialog}
|
|
style={{ width: '600px' }}
|
|
header="Nouvelle session d'inventaire"
|
|
modal
|
|
className="p-fluid"
|
|
footer={sessionDialogFooter}
|
|
onHide={() => setSessionDialog(false)}
|
|
>
|
|
<div className="formgrid grid">
|
|
<div className="field col-12">
|
|
<label htmlFor="nom">Nom de la session</label>
|
|
<InputText
|
|
id="nom"
|
|
value={newSession.nom}
|
|
onChange={(e) => setNewSession({...newSession, nom: e.target.value})}
|
|
required
|
|
className={submitted && !newSession.nom ? 'p-invalid' : ''}
|
|
/>
|
|
{submitted && !newSession.nom && <small className="p-invalid">Le nom est requis.</small>}
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="dateDebut">Date de début</label>
|
|
<Calendar
|
|
id="dateDebut"
|
|
value={newSession.dateDebut}
|
|
onChange={(e) => setNewSession({...newSession, dateDebut: e.value || new Date()})}
|
|
showIcon
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="auditeur">Auditeur</label>
|
|
<InputText
|
|
id="auditeur"
|
|
value={newSession.auditeur}
|
|
onChange={(e) => setNewSession({...newSession, auditeur: e.target.value})}
|
|
required
|
|
className={submitted && !newSession.auditeur ? 'p-invalid' : ''}
|
|
/>
|
|
{submitted && !newSession.auditeur && <small className="p-invalid">L'auditeur est requis.</small>}
|
|
</div>
|
|
|
|
<div className="field col-12">
|
|
<label>Emplacements à inventorier</label>
|
|
<div className="grid">
|
|
{emplacements.map((emplacement) => (
|
|
<div key={emplacement.value} className="col-6">
|
|
<Checkbox
|
|
inputId={emplacement.value}
|
|
value={emplacement.value}
|
|
onChange={(e) => {
|
|
const selected = [...newSession.emplacements];
|
|
if (e.checked) {
|
|
selected.push(e.value);
|
|
} else {
|
|
const index = selected.indexOf(e.value);
|
|
if (index > -1) {
|
|
selected.splice(index, 1);
|
|
}
|
|
}
|
|
setNewSession({...newSession, emplacements: selected});
|
|
}}
|
|
checked={newSession.emplacements.includes(emplacement.value)}
|
|
/>
|
|
<label htmlFor={emplacement.value} className="ml-2">{emplacement.label}</label>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="field col-12">
|
|
<label>Catégories à inventorier</label>
|
|
<div className="grid">
|
|
{categories.map((categorie) => (
|
|
<div key={categorie.value} className="col-6">
|
|
<Checkbox
|
|
inputId={categorie.value}
|
|
value={categorie.value}
|
|
onChange={(e) => {
|
|
const selected = [...newSession.categories];
|
|
if (e.checked) {
|
|
selected.push(e.value);
|
|
} else {
|
|
const index = selected.indexOf(e.value);
|
|
if (index > -1) {
|
|
selected.splice(index, 1);
|
|
}
|
|
}
|
|
setNewSession({...newSession, categories: selected});
|
|
}}
|
|
checked={newSession.categories.includes(categorie.value)}
|
|
/>
|
|
<label htmlFor={categorie.value} className="ml-2">{categorie.label}</label>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Dialog>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default InventairePage; |