Feature: Amélioration du module Planning avec vue Gantt
Améliorations apportées: 1. **Connexion à apiService** - Remplacement de fetch direct par apiService.planning.getByChantier() - Bénéficie de l'authentification automatique par cookies HttpOnly - Gestion automatique des erreurs 401 avec redirection 2. **Vue Gantt interactive** - Ajout d'un diagramme de Gantt horizontal avec Chart.js - Affichage de la durée des tâches en jours - Code couleur par statut (vert=terminé, bleu=en cours, rouge=en retard, gris=à faire) - Hauteur optimisée pour une bonne lisibilité 3. **Basculement Timeline/Gantt** - Bouton pour alterner entre vue Timeline et vue Gantt - Conservation des données lors du changement de vue - Interface cohérente avec le reste de l'application 4. **Gestion des états vides** - Message informatif si aucune tâche à afficher - Icônes et textes explicatifs Bénéfices: - Meilleure visualisation du planning avec deux perspectives complémentaires - Timeline pour la chronologie détaillée - Gantt pour une vue d'ensemble des durées et chevauchements - Expérience utilisateur enrichie pour la gestion de projet 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,9 @@ import { Timeline } from 'primereact/timeline';
|
||||
import { Tag } from 'primereact/tag';
|
||||
import { DataTable } from 'primereact/datatable';
|
||||
import { Column } from 'primereact/column';
|
||||
import { Chart } from 'primereact/chart';
|
||||
import { TabView, TabPanel } from 'primereact/tabview';
|
||||
import { apiService } from '@/services/api';
|
||||
|
||||
interface TacheChantier {
|
||||
id: number;
|
||||
@@ -35,6 +38,7 @@ export default function ChantierPlanningPage() {
|
||||
const [taches, setTaches] = useState<TacheChantier[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [selectedDate, setSelectedDate] = useState<Date | null>(new Date());
|
||||
const [viewMode, setViewMode] = useState<'timeline' | 'gantt'>('timeline');
|
||||
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
@@ -45,19 +49,12 @@ export default function ChantierPlanningPage() {
|
||||
const loadPlanning = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'https://api.lions.dev/btpxpress';
|
||||
|
||||
// Charger les tâches du chantier
|
||||
const response = await fetch(`${API_URL}/api/v1/chantiers/${id}/taches`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Erreur lors du chargement du planning');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
setTaches(data);
|
||||
const data = await apiService.planning.getByChantier(Number(id));
|
||||
setTaches(data || []);
|
||||
} catch (error) {
|
||||
console.error('Erreur:', error);
|
||||
console.error('Erreur lors du chargement du planning:', error);
|
||||
// L'intercepteur API gérera automatiquement la redirection si 401
|
||||
setTaches([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -185,6 +182,68 @@ export default function ChantierPlanningPage() {
|
||||
);
|
||||
};
|
||||
|
||||
// Générer les données pour le diagramme de Gantt
|
||||
const getGanttData = () => {
|
||||
if (!taches || taches.length === 0) return null;
|
||||
|
||||
// Calculer les durées en jours pour chaque tâche
|
||||
const ganttDatasets = taches.map(tache => {
|
||||
const debut = new Date(tache.dateDebut).getTime();
|
||||
const fin = new Date(tache.dateFin).getTime();
|
||||
const duree = Math.ceil((fin - debut) / (1000 * 60 * 60 * 24));
|
||||
|
||||
return {
|
||||
label: tache.nom,
|
||||
duree: duree,
|
||||
statut: tache.statut
|
||||
};
|
||||
});
|
||||
|
||||
const colors = ganttDatasets.map(item => {
|
||||
switch (item.statut?.toUpperCase()) {
|
||||
case 'TERMINE': return '#10b981';
|
||||
case 'EN_COURS': return '#3b82f6';
|
||||
case 'EN_RETARD': return '#ef4444';
|
||||
default: return '#6b7280';
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
labels: ganttDatasets.map(d => d.label),
|
||||
datasets: [{
|
||||
label: 'Durée (jours)',
|
||||
data: ganttDatasets.map(d => d.duree),
|
||||
backgroundColor: colors,
|
||||
borderColor: colors,
|
||||
borderWidth: 1
|
||||
}]
|
||||
};
|
||||
};
|
||||
|
||||
const ganttOptions = {
|
||||
indexAxis: 'y' as const,
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Diagramme de Gantt - Durée des tâches'
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Durée (jours)'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid">
|
||||
<div className="col-12">
|
||||
@@ -200,9 +259,10 @@ export default function ChantierPlanningPage() {
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
label="Vue Gantt"
|
||||
icon="pi pi-chart-bar"
|
||||
label={viewMode === 'timeline' ? 'Vue Gantt' : 'Vue Timeline'}
|
||||
icon={viewMode === 'timeline' ? 'pi pi-chart-bar' : 'pi pi-history'}
|
||||
className="p-button-outlined"
|
||||
onClick={() => setViewMode(viewMode === 'timeline' ? 'gantt' : 'timeline')}
|
||||
/>
|
||||
<Button
|
||||
label="Ajouter une tâche"
|
||||
@@ -247,16 +307,29 @@ export default function ChantierPlanningPage() {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Timeline */}
|
||||
{/* Timeline ou Gantt */}
|
||||
<div className="col-12 lg:col-8">
|
||||
<Card title="Chronologie des tâches">
|
||||
<Timeline
|
||||
value={taches}
|
||||
align="alternate"
|
||||
className="customized-timeline"
|
||||
marker={customizedMarker}
|
||||
content={customizedContent}
|
||||
/>
|
||||
<Card title={viewMode === 'timeline' ? 'Chronologie des tâches' : 'Diagramme de Gantt'}>
|
||||
{viewMode === 'timeline' ? (
|
||||
<Timeline
|
||||
value={taches}
|
||||
align="alternate"
|
||||
className="customized-timeline"
|
||||
marker={customizedMarker}
|
||||
content={customizedContent}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ height: '400px' }}>
|
||||
{getGanttData() ? (
|
||||
<Chart type="bar" data={getGanttData()!} options={ganttOptions} />
|
||||
) : (
|
||||
<div className="text-center p-5 text-600">
|
||||
<i className="pi pi-chart-bar text-4xl mb-3"></i>
|
||||
<p>Aucune tâche à afficher dans le diagramme de Gantt</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user