698 lines
28 KiB
TypeScript
Executable File
698 lines
28 KiB
TypeScript
Executable File
'use client';
|
|
|
|
import React, { useState, useEffect, useRef } from 'react';
|
|
import { DataTable } from 'primereact/datatable';
|
|
import { Column } from 'primereact/column';
|
|
import { Button } from 'primereact/button';
|
|
import { Dialog } from 'primereact/dialog';
|
|
import { InputText } from 'primereact/inputtext';
|
|
import { Dropdown } from 'primereact/dropdown';
|
|
import { InputTextarea } from 'primereact/inputtextarea';
|
|
import { InputNumber } from 'primereact/inputnumber';
|
|
import { Checkbox } from 'primereact/checkbox';
|
|
import { Toast } from 'primereact/toast';
|
|
import { ConfirmDialog, confirmDialog } from 'primereact/confirmdialog';
|
|
import { TreeTable } from 'primereact/treetable';
|
|
import { Card } from 'primereact/card';
|
|
import { Badge } from 'primereact/badge';
|
|
import { ProgressBar } from 'primereact/progressbar';
|
|
import { apiClient } from '../../../../services/api-client';
|
|
|
|
/**
|
|
* Interface pour les templates de tâches
|
|
*/
|
|
interface TacheTemplate {
|
|
id: string;
|
|
nom: string;
|
|
description?: string;
|
|
ordreExecution: number;
|
|
dureeEstimeeMinutes?: number;
|
|
critique: boolean;
|
|
bloquante: boolean;
|
|
priorite: 'BASSE' | 'NORMALE' | 'HAUTE';
|
|
niveauQualification?: 'MANOEUVRE' | 'OUVRIER_SPECIALISE' | 'OUVRIER_QUALIFIE' | 'COMPAGNON' | 'CHEF_EQUIPE' | 'TECHNICIEN' | 'EXPERT';
|
|
nombreOperateursRequis: number;
|
|
outilsRequis?: string[];
|
|
materiauxRequis?: string[];
|
|
conditionsMeteo: 'TOUS_TEMPS' | 'TEMPS_SEC' | 'PAS_DE_VENT_FORT' | 'TEMPERATURE_POSITIVE' | 'PAS_DE_PLUIE' | 'INTERIEUR_UNIQUEMENT';
|
|
sousPhaseParent: {
|
|
id: string;
|
|
nom: string;
|
|
};
|
|
actif: boolean;
|
|
}
|
|
|
|
interface SousPhaseTemplate {
|
|
id: string;
|
|
nom: string;
|
|
description?: string;
|
|
ordreExecution: number;
|
|
taches: TacheTemplate[];
|
|
statistiques?: {
|
|
totalTaches: number;
|
|
tachesCritiques: number;
|
|
tachesBloquantes: number;
|
|
dureeEstimeeMinutes: number;
|
|
};
|
|
}
|
|
|
|
interface PhaseTemplate {
|
|
id: string;
|
|
nom: string;
|
|
description?: string;
|
|
ordreExecution: number;
|
|
sousPhases: SousPhaseTemplate[];
|
|
}
|
|
|
|
/**
|
|
* Page de gestion granulaire des templates de tâches
|
|
* Permet la gestion complète de la hiérarchie Phase → Sous-phase → Tâche
|
|
*/
|
|
const GestionTachesTemplates = () => {
|
|
const toast = useRef<Toast>(null);
|
|
|
|
// États pour les données
|
|
const [phases, setPhases] = useState<PhaseTemplate[]>([]);
|
|
const [selectedPhase, setSelectedPhase] = useState<PhaseTemplate | null>(null);
|
|
const [selectedSousPhase, setSelectedSousPhase] = useState<SousPhaseTemplate | null>(null);
|
|
const [taches, setTaches] = useState<TacheTemplate[]>([]);
|
|
|
|
// États pour les modals
|
|
const [showTacheDialog, setShowTacheDialog] = useState(false);
|
|
const [showSousPhaseDialog, setShowSousPhaseDialog] = useState(false);
|
|
const [editingTache, setEditingTache] = useState<TacheTemplate | null>(null);
|
|
|
|
// États pour les formulaires
|
|
const [formTache, setFormTache] = useState<Partial<TacheTemplate>>({
|
|
nom: '',
|
|
description: '',
|
|
critique: false,
|
|
bloquante: false,
|
|
priorite: 'NORMALE',
|
|
nombreOperateursRequis: 1,
|
|
conditionsMeteo: 'TOUS_TEMPS',
|
|
actif: true
|
|
});
|
|
|
|
// États pour l'interface
|
|
const [loading, setLoading] = useState(false);
|
|
const [expandedRows, setExpandedRows] = useState({});
|
|
|
|
// Options pour les dropdowns
|
|
const prioriteOptions = [
|
|
{ label: 'Basse', value: 'BASSE' },
|
|
{ label: 'Normale', value: 'NORMALE' },
|
|
{ label: 'Haute', value: 'HAUTE' }
|
|
];
|
|
|
|
const qualificationOptions = [
|
|
{ label: 'Manœuvre', value: 'MANOEUVRE' },
|
|
{ label: 'Ouvrier spécialisé', value: 'OUVRIER_SPECIALISE' },
|
|
{ label: 'Ouvrier qualifié', value: 'OUVRIER_QUALIFIE' },
|
|
{ label: 'Compagnon', value: 'COMPAGNON' },
|
|
{ label: 'Chef d\'équipe', value: 'CHEF_EQUIPE' },
|
|
{ label: 'Technicien', value: 'TECHNICIEN' },
|
|
{ label: 'Expert', value: 'EXPERT' }
|
|
];
|
|
|
|
const meteoOptions = [
|
|
{ label: 'Tous temps', value: 'TOUS_TEMPS' },
|
|
{ label: 'Temps sec uniquement', value: 'TEMPS_SEC' },
|
|
{ label: 'Pas de vent fort', value: 'PAS_DE_VENT_FORT' },
|
|
{ label: 'Température positive', value: 'TEMPERATURE_POSITIVE' },
|
|
{ label: 'Pas de pluie', value: 'PAS_DE_PLUIE' },
|
|
{ label: 'Intérieur uniquement', value: 'INTERIEUR_UNIQUEMENT' }
|
|
];
|
|
|
|
// Chargement des données au montage
|
|
useEffect(() => {
|
|
loadPhaseTemplates();
|
|
}, []);
|
|
|
|
// Chargement des tâches quand une sous-phase est sélectionnée
|
|
useEffect(() => {
|
|
if (selectedSousPhase) {
|
|
loadTachesForSousPhase(selectedSousPhase.id);
|
|
}
|
|
}, [selectedSousPhase]);
|
|
|
|
/**
|
|
* Charge tous les templates de phases pour MAISON_INDIVIDUELLE
|
|
*/
|
|
const loadPhaseTemplates = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const response = await apiClient.get('/phase-templates/by-type/MAISON_INDIVIDUELLE');
|
|
|
|
// Pour chaque phase, charger ses sous-phases et tâches
|
|
const phasesWithDetails = await Promise.all(
|
|
response.data.map(async (phase: PhaseTemplate) => {
|
|
const sousPhases = await loadSousPhasesForPhase(phase.id);
|
|
return { ...phase, sousPhases };
|
|
})
|
|
);
|
|
|
|
setPhases(phasesWithDetails);
|
|
if (phasesWithDetails.length > 0) {
|
|
setSelectedPhase(phasesWithDetails[0]);
|
|
}
|
|
} catch (error) {
|
|
showToast('error', 'Erreur', 'Impossible de charger les templates de phases');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Charge les sous-phases pour une phase donnée
|
|
*/
|
|
const loadSousPhasesForPhase = async (phaseId: string): Promise<SousPhaseTemplate[]> => {
|
|
try {
|
|
const response = await apiClient.get(`/sous-phase-templates/by-phase/${phaseId}`);
|
|
|
|
// Pour chaque sous-phase, charger ses tâches et statistiques
|
|
const sousPhasesWithTaches = await Promise.all(
|
|
response.data.map(async (sousPhase: SousPhaseTemplate) => {
|
|
const [taches, statistiques] = await Promise.all([
|
|
loadTachesForSousPhase(sousPhase.id),
|
|
loadStatistiquesForSousPhase(sousPhase.id)
|
|
]);
|
|
return { ...sousPhase, taches, statistiques };
|
|
})
|
|
);
|
|
|
|
return sousPhasesWithTaches;
|
|
} catch (error) {
|
|
console.error('Erreur lors du chargement des sous-phases:', error);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Charge les tâches pour une sous-phase donnée
|
|
*/
|
|
const loadTachesForSousPhase = async (sousPhaseId: string): Promise<TacheTemplate[]> => {
|
|
try {
|
|
const response = await apiClient.get(`/tache-templates/by-sous-phase/${sousPhaseId}`);
|
|
setTaches(response.data);
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error('Erreur lors du chargement des tâches:', error);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Charge les statistiques pour une sous-phase donnée
|
|
*/
|
|
const loadStatistiquesForSousPhase = async (sousPhaseId: string) => {
|
|
try {
|
|
const response = await apiClient.get(`/tache-templates/stats/by-sous-phase/${sousPhaseId}`);
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error('Erreur lors du chargement des statistiques:', error);
|
|
return null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Affiche un toast message
|
|
*/
|
|
const showToast = (severity: 'success' | 'info' | 'warn' | 'error', summary: string, detail: string) => {
|
|
toast.current?.show({ severity, summary, detail, life: 3000 });
|
|
};
|
|
|
|
/**
|
|
* Ouvre le dialog de création/édition d'une tâche
|
|
*/
|
|
const openTacheDialog = (tache?: TacheTemplate) => {
|
|
if (!selectedSousPhase) {
|
|
showToast('warn', 'Attention', 'Veuillez d\'abord sélectionner une sous-phase');
|
|
return;
|
|
}
|
|
|
|
if (tache) {
|
|
setEditingTache(tache);
|
|
setFormTache({
|
|
nom: tache.nom,
|
|
description: tache.description,
|
|
dureeEstimeeMinutes: tache.dureeEstimeeMinutes,
|
|
critique: tache.critique,
|
|
bloquante: tache.bloquante,
|
|
priorite: tache.priorite,
|
|
niveauQualification: tache.niveauQualification,
|
|
nombreOperateursRequis: tache.nombreOperateursRequis,
|
|
conditionsMeteo: tache.conditionsMeteo,
|
|
outilsRequis: tache.outilsRequis,
|
|
materiauxRequis: tache.materiauxRequis
|
|
});
|
|
} else {
|
|
setEditingTache(null);
|
|
setFormTache({
|
|
nom: '',
|
|
description: '',
|
|
critique: false,
|
|
bloquante: false,
|
|
priorite: 'NORMALE',
|
|
nombreOperateursRequis: 1,
|
|
conditionsMeteo: 'TOUS_TEMPS',
|
|
actif: true
|
|
});
|
|
}
|
|
|
|
setShowTacheDialog(true);
|
|
};
|
|
|
|
/**
|
|
* Sauvegarde une tâche (création ou modification)
|
|
*/
|
|
const saveTache = async () => {
|
|
if (!selectedSousPhase || !formTache.nom?.trim()) {
|
|
showToast('warn', 'Attention', 'Le nom de la tâche est obligatoire');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const tacheData = {
|
|
...formTache,
|
|
sousPhaseParent: { id: selectedSousPhase.id }
|
|
};
|
|
|
|
if (editingTache) {
|
|
await apiClient.put(`/tache-templates/${editingTache.id}`, tacheData);
|
|
showToast('success', 'Succès', 'Tâche modifiée avec succès');
|
|
} else {
|
|
await apiClient.post('/tache-templates', tacheData);
|
|
showToast('success', 'Succès', 'Tâche créée avec succès');
|
|
}
|
|
|
|
setShowTacheDialog(false);
|
|
await loadTachesForSousPhase(selectedSousPhase.id);
|
|
await loadPhaseTemplates(); // Recharger pour mettre à jour les statistiques
|
|
} catch (error) {
|
|
showToast('error', 'Erreur', 'Impossible de sauvegarder la tâche');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Supprime une tâche avec confirmation
|
|
*/
|
|
const deleteTache = (tache: TacheTemplate) => {
|
|
confirmDialog({
|
|
message: `Êtes-vous sûr de vouloir supprimer la tâche "${tache.nom}" ?`,
|
|
header: 'Confirmation de suppression',
|
|
icon: 'pi pi-exclamation-triangle',
|
|
acceptLabel: 'Oui',
|
|
rejectLabel: 'Non',
|
|
accept: async () => {
|
|
try {
|
|
await apiClient.delete(`/tache-templates/${tache.id}`);
|
|
showToast('success', 'Succès', 'Tâche supprimée avec succès');
|
|
await loadTachesForSousPhase(selectedSousPhase!.id);
|
|
await loadPhaseTemplates();
|
|
} catch (error) {
|
|
showToast('error', 'Erreur', 'Impossible de supprimer la tâche');
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Template pour l'affichage des actions dans le tableau
|
|
*/
|
|
const actionBodyTemplate = (rowData: TacheTemplate) => {
|
|
return (
|
|
<div className="flex gap-2">
|
|
<Button
|
|
icon="pi pi-pencil"
|
|
className="p-button-sm p-button-text p-button-rounded"
|
|
onClick={() => openTacheDialog(rowData)}
|
|
tooltip="Modifier"
|
|
/>
|
|
<Button
|
|
icon="pi pi-trash"
|
|
className="p-button-sm p-button-text p-button-rounded p-button-danger"
|
|
onClick={() => deleteTache(rowData)}
|
|
tooltip="Supprimer"
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Template pour l'affichage de la priorité
|
|
*/
|
|
const prioriteBodyTemplate = (rowData: TacheTemplate) => {
|
|
const severity = rowData.priorite === 'HAUTE' ? 'danger' :
|
|
rowData.priorite === 'NORMALE' ? 'info' : 'success';
|
|
return <Badge value={rowData.priorite} severity={severity} />;
|
|
};
|
|
|
|
/**
|
|
* Template pour l'affichage de la durée
|
|
*/
|
|
const dureeBodyTemplate = (rowData: TacheTemplate) => {
|
|
if (!rowData.dureeEstimeeMinutes) return '-';
|
|
|
|
const heures = Math.floor(rowData.dureeEstimeeMinutes / 60);
|
|
const minutes = rowData.dureeEstimeeMinutes % 60;
|
|
|
|
if (heures > 0) {
|
|
return `${heures}h${minutes > 0 ? ` ${minutes}min` : ''}`;
|
|
}
|
|
return `${minutes}min`;
|
|
};
|
|
|
|
/**
|
|
* Template pour l'affichage des indicateurs critique/bloquante
|
|
*/
|
|
const indicateursBodyTemplate = (rowData: TacheTemplate) => {
|
|
return (
|
|
<div className="flex gap-1">
|
|
{rowData.critique && (
|
|
<Badge value="C" severity="danger" className="p-badge-sm" />
|
|
)}
|
|
{rowData.bloquante && (
|
|
<Badge value="B" severity="warning" className="p-badge-sm" />
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Template pour l'affichage des statistiques d'une sous-phase
|
|
*/
|
|
const renderSousPhaseStats = (sousPhase: SousPhaseTemplate) => {
|
|
const stats = sousPhase.statistiques;
|
|
if (!stats) return null;
|
|
|
|
const pourcentageCritiques = stats.totalTaches > 0 ?
|
|
(stats.tachesCritiques / stats.totalTaches * 100) : 0;
|
|
|
|
const dureeHeures = stats.dureeEstimeeMinutes / 60;
|
|
|
|
return (
|
|
<div className="flex gap-4 text-sm">
|
|
<span><i className="pi pi-list mr-1"></i>{stats.totalTaches} tâches</span>
|
|
<span><i className="pi pi-exclamation-triangle mr-1"></i>{stats.tachesCritiques} critiques</span>
|
|
<span><i className="pi pi-lock mr-1"></i>{stats.tachesBloquantes} bloquantes</span>
|
|
<span><i className="pi pi-clock mr-1"></i>{dureeHeures.toFixed(1)}h</span>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className="grid">
|
|
<Toast ref={toast} />
|
|
<ConfirmDialog />
|
|
|
|
{/* Header */}
|
|
<div className="col-12">
|
|
<div className="card">
|
|
<div className="flex justify-content-between align-items-center">
|
|
<div>
|
|
<h5>Gestion Granulaire des Tâches</h5>
|
|
<p className="text-600 mt-0">
|
|
Système de tracking détaillé : Phase → Sous-phase → Tâche
|
|
</p>
|
|
</div>
|
|
<Button
|
|
label="Nouvelle Tâche"
|
|
icon="pi pi-plus"
|
|
onClick={() => openTacheDialog()}
|
|
disabled={!selectedSousPhase}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Sélection de Phase et Sous-phase */}
|
|
<div className="col-12">
|
|
<div className="grid">
|
|
{/* Phases */}
|
|
<div className="col-12 md:col-6">
|
|
<Card title="Phases" className="h-full">
|
|
<div className="flex flex-column gap-3">
|
|
{phases.map((phase) => (
|
|
<div
|
|
key={phase.id}
|
|
className={`p-3 border-round cursor-pointer transition-colors ${
|
|
selectedPhase?.id === phase.id
|
|
? 'bg-blue-50 border-blue-200 border-2'
|
|
: 'bg-gray-50 border-gray-200 border-1'
|
|
}`}
|
|
onClick={() => {
|
|
setSelectedPhase(phase);
|
|
setSelectedSousPhase(null);
|
|
setTaches([]);
|
|
}}
|
|
>
|
|
<div className="flex justify-content-between align-items-center">
|
|
<div>
|
|
<div className="font-semibold">{phase.nom}</div>
|
|
<div className="text-sm text-600">
|
|
{phase.sousPhases?.length || 0} sous-phases
|
|
</div>
|
|
</div>
|
|
<Badge value={phase.ordreExecution} className="p-badge-lg" />
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Sous-phases */}
|
|
<div className="col-12 md:col-6">
|
|
<Card title={`Sous-phases ${selectedPhase ? `- ${selectedPhase.nom}` : ''}`} className="h-full">
|
|
{selectedPhase ? (
|
|
<div className="flex flex-column gap-3">
|
|
{selectedPhase.sousPhases?.map((sousPhase) => (
|
|
<div
|
|
key={sousPhase.id}
|
|
className={`p-3 border-round cursor-pointer transition-colors ${
|
|
selectedSousPhase?.id === sousPhase.id
|
|
? 'bg-green-50 border-green-200 border-2'
|
|
: 'bg-gray-50 border-gray-200 border-1'
|
|
}`}
|
|
onClick={() => setSelectedSousPhase(sousPhase)}
|
|
>
|
|
<div className="flex justify-content-between align-items-start mb-2">
|
|
<div className="flex-1">
|
|
<div className="font-semibold">{sousPhase.nom}</div>
|
|
{sousPhase.description && (
|
|
<div className="text-sm text-600 mt-1">
|
|
{sousPhase.description}
|
|
</div>
|
|
)}
|
|
</div>
|
|
<Badge value={sousPhase.ordreExecution} />
|
|
</div>
|
|
{renderSousPhaseStats(sousPhase)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="text-center text-500 mt-4">
|
|
Sélectionnez une phase pour voir ses sous-phases
|
|
</p>
|
|
)}
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Tableau des Tâches */}
|
|
<div className="col-12">
|
|
<Card title={`Tâches ${selectedSousPhase ? `- ${selectedSousPhase.nom}` : ''}`}>
|
|
{selectedSousPhase ? (
|
|
<DataTable
|
|
value={taches}
|
|
loading={loading}
|
|
emptyMessage="Aucune tâche trouvée"
|
|
paginator
|
|
rows={10}
|
|
className="p-datatable-gridlines"
|
|
responsiveLayout="scroll"
|
|
>
|
|
<Column
|
|
field="ordreExecution"
|
|
header="Ordre"
|
|
style={{ width: '80px' }}
|
|
sortable
|
|
/>
|
|
<Column
|
|
field="nom"
|
|
header="Nom de la tâche"
|
|
sortable
|
|
/>
|
|
<Column
|
|
field="dureeEstimeeMinutes"
|
|
header="Durée"
|
|
body={dureeBodyTemplate}
|
|
style={{ width: '100px' }}
|
|
sortable
|
|
/>
|
|
<Column
|
|
field="priorite"
|
|
header="Priorité"
|
|
body={prioriteBodyTemplate}
|
|
style={{ width: '120px' }}
|
|
sortable
|
|
/>
|
|
<Column
|
|
field="nombreOperateursRequis"
|
|
header="Opérateurs"
|
|
style={{ width: '100px' }}
|
|
sortable
|
|
/>
|
|
<Column
|
|
header="Indicateurs"
|
|
body={indicateursBodyTemplate}
|
|
style={{ width: '100px' }}
|
|
/>
|
|
<Column
|
|
field="niveauQualification"
|
|
header="Qualification"
|
|
style={{ width: '140px' }}
|
|
sortable
|
|
/>
|
|
<Column
|
|
header="Actions"
|
|
body={actionBodyTemplate}
|
|
style={{ width: '120px' }}
|
|
/>
|
|
</DataTable>
|
|
) : (
|
|
<p className="text-center text-500 mt-4">
|
|
Sélectionnez une sous-phase pour voir ses tâches
|
|
</p>
|
|
)}
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Dialog de Création/Édition de Tâche */}
|
|
<Dialog
|
|
header={editingTache ? 'Modifier la tâche' : 'Nouvelle tâche'}
|
|
visible={showTacheDialog}
|
|
onHide={() => setShowTacheDialog(false)}
|
|
style={{ width: '60vw' }}
|
|
breakpoints={{ '960px': '75vw', '641px': '90vw' }}
|
|
modal
|
|
>
|
|
<div className="grid formgrid p-fluid">
|
|
<div className="col-12 md:col-6 field">
|
|
<label htmlFor="nom">Nom de la tâche *</label>
|
|
<InputText
|
|
id="nom"
|
|
value={formTache.nom || ''}
|
|
onChange={(e) => setFormTache({ ...formTache, nom: e.target.value })}
|
|
placeholder="Nom de la tâche..."
|
|
/>
|
|
</div>
|
|
|
|
<div className="col-12 md:col-6 field">
|
|
<label htmlFor="duree">Durée estimée (minutes)</label>
|
|
<InputNumber
|
|
id="duree"
|
|
value={formTache.dureeEstimeeMinutes}
|
|
onValueChange={(e) => setFormTache({ ...formTache, dureeEstimeeMinutes: e.value || 0 })}
|
|
min={0}
|
|
placeholder="Durée en minutes"
|
|
/>
|
|
</div>
|
|
|
|
<div className="col-12 field">
|
|
<label htmlFor="description">Description</label>
|
|
<InputTextarea
|
|
id="description"
|
|
value={formTache.description || ''}
|
|
onChange={(e) => setFormTache({ ...formTache, description: e.target.value })}
|
|
rows={3}
|
|
placeholder="Description détaillée de la tâche..."
|
|
/>
|
|
</div>
|
|
|
|
<div className="col-12 md:col-4 field">
|
|
<label htmlFor="priorite">Priorité</label>
|
|
<Dropdown
|
|
id="priorite"
|
|
value={formTache.priorite}
|
|
options={prioriteOptions}
|
|
onChange={(e) => setFormTache({ ...formTache, priorite: e.value })}
|
|
/>
|
|
</div>
|
|
|
|
<div className="col-12 md:col-4 field">
|
|
<label htmlFor="operateurs">Nombre d'opérateurs</label>
|
|
<InputNumber
|
|
id="operateurs"
|
|
value={formTache.nombreOperateursRequis}
|
|
onValueChange={(e) => setFormTache({ ...formTache, nombreOperateursRequis: e.value || 1 })}
|
|
min={1}
|
|
max={10}
|
|
/>
|
|
</div>
|
|
|
|
<div className="col-12 md:col-4 field">
|
|
<label htmlFor="qualification">Niveau de qualification</label>
|
|
<Dropdown
|
|
id="qualification"
|
|
value={formTache.niveauQualification}
|
|
options={qualificationOptions}
|
|
onChange={(e) => setFormTache({ ...formTache, niveauQualification: e.value })}
|
|
placeholder="Sélectionner..."
|
|
/>
|
|
</div>
|
|
|
|
<div className="col-12 md:col-6 field">
|
|
<label htmlFor="meteo">Conditions météorologiques</label>
|
|
<Dropdown
|
|
id="meteo"
|
|
value={formTache.conditionsMeteo}
|
|
options={meteoOptions}
|
|
onChange={(e) => setFormTache({ ...formTache, conditionsMeteo: e.value })}
|
|
/>
|
|
</div>
|
|
|
|
<div className="col-12 md:col-6 field">
|
|
<div className="flex flex-column gap-3">
|
|
<div className="flex align-items-center">
|
|
<Checkbox
|
|
inputId="critique"
|
|
checked={formTache.critique || false}
|
|
onChange={(e) => setFormTache({ ...formTache, critique: e.checked })}
|
|
/>
|
|
<label htmlFor="critique" className="ml-2">Tâche critique</label>
|
|
</div>
|
|
<div className="flex align-items-center">
|
|
<Checkbox
|
|
inputId="bloquante"
|
|
checked={formTache.bloquante || false}
|
|
onChange={(e) => setFormTache({ ...formTache, bloquante: e.checked })}
|
|
/>
|
|
<label htmlFor="bloquante" className="ml-2">Tâche bloquante</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex justify-content-end gap-2 mt-4">
|
|
<Button
|
|
label="Annuler"
|
|
icon="pi pi-times"
|
|
className="p-button-text"
|
|
onClick={() => setShowTacheDialog(false)}
|
|
/>
|
|
<Button
|
|
label="Sauvegarder"
|
|
icon="pi pi-check"
|
|
onClick={saveTache}
|
|
/>
|
|
</div>
|
|
</Dialog>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default GestionTachesTemplates; |