diff --git a/TODO.md b/TODO.md
index 93639a5..78a61c9 100644
--- a/TODO.md
+++ b/TODO.md
@@ -127,8 +127,11 @@
- [x] Gestion des erreurs et timeouts API
### 3.3 Page d'accueil/dashboard
-- [ ] Créer une page d'accueil moderne avec vue d'ensemble
-- [ ] Widgets de statistiques (tâches par statut, priorité, etc.)
+- [x] Créer une page d'accueil moderne avec vue d'ensemble
+- [x] Widgets de statistiques (tâches par statut, priorité, etc.)
+- [x] Déplacer kanban vers /kanban et créer nouveau dashboard à la racine
+- [x] Actions rapides vers les différentes sections
+- [x] Affichage des tâches récentes
- [ ] Graphiques de productivité (tâches complétées par jour/semaine)
- [ ] Indicateurs de performance personnels
diff --git a/components/HomePageClient.tsx b/components/HomePageClient.tsx
index 6c68cd0..d82a937 100644
--- a/components/HomePageClient.tsx
+++ b/components/HomePageClient.tsx
@@ -1,16 +1,13 @@
'use client';
-import { useState } from 'react';
-import { KanbanBoardContainer } from '@/components/kanban/BoardContainer';
import { Header } from '@/components/ui/Header';
import { TasksProvider, useTasksContext } from '@/contexts/TasksContext';
-import { UserPreferencesProvider, useUserPreferences } from '@/contexts/UserPreferencesContext';
+import { UserPreferencesProvider } from '@/contexts/UserPreferencesContext';
import { Task, Tag, TaskStats, UserPreferences } from '@/lib/types';
import { CreateTaskData } from '@/clients/tasks-client';
-import { CreateTaskForm } from '@/components/forms/CreateTaskForm';
-import { Button } from '@/components/ui/Button';
-import { JiraQuickFilter } from '@/components/kanban/JiraQuickFilter';
-import { FontSizeToggle } from '@/components/ui/FontSizeToggle';
+import { DashboardStats } from '@/components/dashboard/DashboardStats';
+import { QuickActions } from '@/components/dashboard/QuickActions';
+import { RecentTasks } from '@/components/dashboard/RecentTasks';
interface HomePageClientProps {
initialTasks: Task[];
@@ -21,163 +18,32 @@ interface HomePageClientProps {
function HomePageContent() {
- const { stats, syncing, createTask, activeFiltersCount, kanbanFilters, setKanbanFilters } = useTasksContext();
- const { preferences, updateViewPreferences } = useUserPreferences();
- const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
+ const { stats, syncing, createTask, tasks } = useTasksContext();
- // Extraire les préférences du context
- const showFilters = preferences.viewPreferences.showFilters;
- const showObjectives = preferences.viewPreferences.showObjectives;
- const compactView = preferences.viewPreferences.compactView;
- const swimlanesByTags = preferences.viewPreferences.swimlanesByTags;
-
- // Handlers pour les toggles (sauvegarde automatique via le context)
- const handleToggleFilters = () => {
- updateViewPreferences({ showFilters: !showFilters });
- };
-
- const handleToggleObjectives = () => {
- updateViewPreferences({ showObjectives: !showObjectives });
- };
-
- const handleToggleCompactView = () => {
- updateViewPreferences({ compactView: !compactView });
- };
-
- const handleToggleSwimlanes = () => {
- updateViewPreferences({ swimlanesByTags: !swimlanesByTags });
- };
-
- // Handler pour la création de tâche depuis la barre de contrôles
+ // Handler pour la création de tâche
const handleCreateTask = async (data: CreateTaskData) => {
await createTask(data);
- setIsCreateModalOpen(false);
};
return (
- {/* Barre de contrôles de visibilité */}
-
-
-
-
-
-
-
-
-
-
-
- {/* Raccourcis Jira */}
-
-
-
-
-
-
- {/* Font Size Toggle */}
-
-
-
-
-
- {/* Bouton d'ajout de tâche */}
-
-
-
-
-
-
-
+
+ {/* Statistiques */}
+
+
+ {/* Actions rapides */}
+
+
+ {/* Tâches récentes */}
+
-
- {/* Modal de création de tâche */}
- setIsCreateModalOpen(false)}
- onSubmit={handleCreateTask}
- loading={false}
- />
);
}
diff --git a/components/dashboard/DashboardStats.tsx b/components/dashboard/DashboardStats.tsx
new file mode 100644
index 0000000..e2f492e
--- /dev/null
+++ b/components/dashboard/DashboardStats.tsx
@@ -0,0 +1,127 @@
+'use client';
+
+import { TaskStats } from '@/lib/types';
+import { Card } from '@/components/ui/Card';
+
+interface DashboardStatsProps {
+ stats: TaskStats;
+}
+
+export function DashboardStats({ stats }: DashboardStatsProps) {
+ const totalTasks = stats.total;
+ const completionRate = totalTasks > 0 ? Math.round((stats.completed / totalTasks) * 100) : 0;
+ const inProgressRate = totalTasks > 0 ? Math.round((stats.inProgress / totalTasks) * 100) : 0;
+
+ const statCards = [
+ {
+ title: 'Total Tâches',
+ value: stats.total,
+ icon: '📋',
+ color: 'bg-blue-500',
+ textColor: 'text-blue-600'
+ },
+ {
+ title: 'À Faire',
+ value: stats.todo,
+ icon: '⏳',
+ color: 'bg-gray-500',
+ textColor: 'text-gray-600'
+ },
+ {
+ title: 'En Cours',
+ value: stats.inProgress,
+ icon: '🔄',
+ color: 'bg-orange-500',
+ textColor: 'text-orange-600'
+ },
+ {
+ title: 'Terminées',
+ value: stats.completed,
+ icon: '✅',
+ color: 'bg-green-500',
+ textColor: 'text-green-600'
+ }
+ ];
+
+ return (
+
+ {statCards.map((stat, index) => (
+
+
+
+
+ {stat.title}
+
+
+ {stat.value}
+
+
+
+ {stat.icon}
+
+
+
+ ))}
+
+ {/* Cartes de pourcentage */}
+
+ Taux de Completion
+
+
+ Terminées
+ {completionRate}%
+
+
+
+
+ En Cours
+ {inProgressRate}%
+
+
+
+
+
+ {/* Insights rapides */}
+
+ Aperçu Rapide
+
+
+
+
+ {stats.completed} tâches terminées sur {totalTasks}
+
+
+
+
+
+ {stats.inProgress} tâches en cours de traitement
+
+
+
+
+
+ {stats.todo} tâches en attente
+
+
+ {totalTasks > 0 && (
+
+
+ Productivité: {completionRate}% de completion
+
+
+ )}
+
+
+
+ );
+}
diff --git a/components/dashboard/QuickActions.tsx b/components/dashboard/QuickActions.tsx
new file mode 100644
index 0000000..7dcf507
--- /dev/null
+++ b/components/dashboard/QuickActions.tsx
@@ -0,0 +1,93 @@
+'use client';
+
+import { useState } from 'react';
+import { Button } from '@/components/ui/Button';
+import { CreateTaskForm } from '@/components/forms/CreateTaskForm';
+import { CreateTaskData } from '@/clients/tasks-client';
+import Link from 'next/link';
+
+interface QuickActionsProps {
+ onCreateTask: (data: CreateTaskData) => Promise;
+}
+
+export function QuickActions({ onCreateTask }: QuickActionsProps) {
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
+
+ const handleCreateTask = async (data: CreateTaskData) => {
+ await onCreateTask(data);
+ setIsCreateModalOpen(false);
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setIsCreateModalOpen(false)}
+ onSubmit={handleCreateTask}
+ loading={false}
+ />
+ >
+ );
+}
diff --git a/components/dashboard/RecentTasks.tsx b/components/dashboard/RecentTasks.tsx
new file mode 100644
index 0000000..f4f20b8
--- /dev/null
+++ b/components/dashboard/RecentTasks.tsx
@@ -0,0 +1,146 @@
+'use client';
+
+import { Task } from '@/lib/types';
+import { Card } from '@/components/ui/Card';
+import { TagDisplay } from '@/components/ui/TagDisplay';
+import { Badge } from '@/components/ui/Badge';
+import { useTasksContext } from '@/contexts/TasksContext';
+import { getPriorityConfig, getPriorityColorHex } from '@/lib/status-config';
+import Link from 'next/link';
+
+interface RecentTasksProps {
+ tasks: Task[];
+}
+
+export function RecentTasks({ tasks }: RecentTasksProps) {
+ const { tags: availableTags } = useTasksContext();
+
+ // Prendre les 5 tâches les plus récentes (créées ou modifiées)
+ const recentTasks = tasks
+ .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())
+ .slice(0, 5);
+
+ const getStatusColor = (status: string) => {
+ switch (status) {
+ case 'todo': return 'bg-gray-100 text-gray-800';
+ case 'inProgress': return 'bg-orange-100 text-orange-800';
+ case 'done': return 'bg-green-100 text-green-800';
+ default: return 'bg-gray-100 text-gray-800';
+ }
+ };
+
+ const getStatusText = (status: string) => {
+ switch (status) {
+ case 'todo': return 'À faire';
+ case 'inProgress': return 'En cours';
+ case 'done': return 'Terminé';
+ default: return status;
+ }
+ };
+
+ const getPriorityStyle = (priority: string) => {
+ try {
+ const config = getPriorityConfig(priority as any);
+ const hexColor = getPriorityColorHex(config.color);
+ return { color: hexColor };
+ } catch {
+ return { color: '#6b7280' }; // gray-500 par défaut
+ }
+ };
+
+ return (
+
+
+
Tâches Récentes
+
+
+
+
+
+ {recentTasks.length === 0 ? (
+
+
+
Aucune tâche disponible
+
Créez votre première tâche pour commencer
+
+ ) : (
+
+ {recentTasks.map((task) => (
+
+
+
+
+
{task.title}
+ {task.source === 'jira' && (
+
+ Jira
+
+ )}
+
+
+ {task.description && (
+
+ {task.description}
+
+ )}
+
+
+
+ {getStatusText(task.status)}
+
+
+ {task.priority && (
+
+ {(() => {
+ try {
+ return getPriorityConfig(task.priority as any).label;
+ } catch {
+ return task.priority;
+ }
+ })()}
+
+ )}
+
+ {task.tags && task.tags.length > 0 && (
+
+
+ {task.tags.length > 2 && (
+
+ +{task.tags.length - 2}
+
+ )}
+
+ )}
+
+
+
+
+ {new Date(task.updatedAt).toLocaleDateString('fr-FR', {
+ day: 'numeric',
+ month: 'short'
+ })}
+
+
+
+ ))}
+
+ )}
+
+ );
+}
diff --git a/components/ui/Header.tsx b/components/ui/Header.tsx
index 5ff5e35..3c477d3 100644
--- a/components/ui/Header.tsx
+++ b/components/ui/Header.tsx
@@ -40,6 +40,12 @@ export function Header({ title = "TowerControl", subtitle = "Task Management", s
+ Dashboard
+
+
Kanban
diff --git a/src/app/kanban/KanbanPageClient.tsx b/src/app/kanban/KanbanPageClient.tsx
new file mode 100644
index 0000000..43c92b7
--- /dev/null
+++ b/src/app/kanban/KanbanPageClient.tsx
@@ -0,0 +1,196 @@
+'use client';
+
+import { useState } from 'react';
+import { KanbanBoardContainer } from '@/components/kanban/BoardContainer';
+import { Header } from '@/components/ui/Header';
+import { TasksProvider, useTasksContext } from '@/contexts/TasksContext';
+import { UserPreferencesProvider, useUserPreferences } from '@/contexts/UserPreferencesContext';
+import { Task, Tag, TaskStats, UserPreferences } from '@/lib/types';
+import { CreateTaskData } from '@/clients/tasks-client';
+import { CreateTaskForm } from '@/components/forms/CreateTaskForm';
+import { Button } from '@/components/ui/Button';
+import { JiraQuickFilter } from '@/components/kanban/JiraQuickFilter';
+import { FontSizeToggle } from '@/components/ui/FontSizeToggle';
+
+interface KanbanPageClientProps {
+ initialTasks: Task[];
+ initialStats: TaskStats;
+ initialTags: (Tag & { usage: number })[];
+ initialPreferences: UserPreferences;
+}
+
+function KanbanPageContent() {
+ const { stats, syncing, createTask, activeFiltersCount, kanbanFilters, setKanbanFilters } = useTasksContext();
+ const { preferences, updateViewPreferences } = useUserPreferences();
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
+
+ // Extraire les préférences du context
+ const showFilters = preferences.viewPreferences.showFilters;
+ const showObjectives = preferences.viewPreferences.showObjectives;
+ const compactView = preferences.viewPreferences.compactView;
+ const swimlanesByTags = preferences.viewPreferences.swimlanesByTags;
+
+ // Handlers pour les toggles (sauvegarde automatique via le context)
+ const handleToggleFilters = () => {
+ updateViewPreferences({ showFilters: !showFilters });
+ };
+
+ const handleToggleObjectives = () => {
+ updateViewPreferences({ showObjectives: !showObjectives });
+ };
+
+ const handleToggleCompactView = () => {
+ updateViewPreferences({ compactView: !compactView });
+ };
+
+ const handleToggleSwimlanes = () => {
+ updateViewPreferences({ swimlanesByTags: !swimlanesByTags });
+ };
+
+ // Handler pour la création de tâche depuis la barre de contrôles
+ const handleCreateTask = async (data: CreateTaskData) => {
+ await createTask(data);
+ setIsCreateModalOpen(false);
+ };
+
+ return (
+
+
+
+ {/* Barre de contrôles de visibilité */}
+
+
+
+
+
+
+
+
+
+
+
+ {/* Raccourcis Jira */}
+
+
+
+
+
+
+ {/* Font Size Toggle */}
+
+
+
+
+
+ {/* Bouton d'ajout de tâche */}
+
+
+
+
+
+
+
+
+
+ {/* Modal de création de tâche */}
+
setIsCreateModalOpen(false)}
+ onSubmit={handleCreateTask}
+ loading={false}
+ />
+
+ );
+}
+
+export function KanbanPageClient({ initialTasks, initialStats, initialTags, initialPreferences }: KanbanPageClientProps) {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/app/kanban/page.tsx b/src/app/kanban/page.tsx
new file mode 100644
index 0000000..7a9e051
--- /dev/null
+++ b/src/app/kanban/page.tsx
@@ -0,0 +1,26 @@
+import { tasksService } from '@/services/tasks';
+import { tagsService } from '@/services/tags';
+import { userPreferencesService } from '@/services/user-preferences';
+import { KanbanPageClient } from './KanbanPageClient';
+
+// Force dynamic rendering (no static generation)
+export const dynamic = 'force-dynamic';
+
+export default async function KanbanPage() {
+ // SSR - Récupération des données côté serveur
+ const [initialTasks, initialStats, initialTags, initialPreferences] = await Promise.all([
+ tasksService.getTasks(),
+ tasksService.getTaskStats(),
+ tagsService.getTags(),
+ userPreferencesService.getAllPreferences()
+ ]);
+
+ return (
+
+ );
+}