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