+
+
+ À faire ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'todo').length})
-
- {filteredTasks.filter((task: TaskWithRelations) => task.status === 'completed').map((task: TaskWithRelations) => {
- const priorityConfig = getPriorityConfig(task.priority);
- const PriorityIcon = priorityConfig.icon;
-
- return (
-
-
-
-
{task.title}
-
-
- {priorityConfig.label}
-
-
-
-
- Terminée le: {task.completedAt ? format(new Date(task.completedAt), 'dd/MM/yyyy', { locale: fr }) : 'Date inconnue'}
-
-
-
- {canEditTasks && (
- <>
- handleEditTask(task)}
- className="text-xs h-7"
- >
-
-
- handleDeleteClick(task)}
- className="text-red-600 hover:text-red-700 text-xs h-7"
- >
-
-
- >
- )}
-
-
-
- );
+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'todo').map((task: TaskWithRelations) => {
+ return renderKanbanCard(task, canEditTasks);
+ })}
+
+
+
+ {/* Colonne En cours */}
+
+
+
+ En cours ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'in_progress').length})
+
+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'in_progress').map((task: TaskWithRelations) => {
+ return renderKanbanCard(task, canEditTasks);
+ })}
+
+
+
+ {/* Colonne Révision */}
+
+
+
+ Révision ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'review').length})
+
+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'review').map((task: TaskWithRelations) => {
+ return renderKanbanCard(task, canEditTasks);
+ })}
+
+
+
+ {/* Colonne Test */}
+
+
+
+ Test ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'testing').length})
+
+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'testing').map((task: TaskWithRelations) => {
+ return renderKanbanCard(task, canEditTasks);
+ })}
+
+
+
+ {/* Colonne Terminé */}
+
+
+
+ Terminé ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'done').length})
+
+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'done').map((task: TaskWithRelations) => {
+ return renderKanbanCard(task, canEditTasks, true);
+ })}
+
+
+
+ {/* Colonne Archivé */}
+
+
+
+ Archivé ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'archived').length})
+
+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'archived').map((task: TaskWithRelations) => {
+ return renderKanbanCard(task, canEditTasks, true);
})}
diff --git a/replit.md b/replit.md
index 746a952..99301a2 100644
--- a/replit.md
+++ b/replit.md
@@ -34,7 +34,7 @@ The platform is built with a clear separation of concerns, utilizing a modern fu
- **Reconciliation:** Functionality for reconciling Bills of Lading (BL) and invoices with immediate UI updates.
- **Invoice Verification Cache:** Robust caching system for invoice verification results with automatic expiration and conflict resolution. Enhanced with unique constraint handling and graceful error management for production stability (August 2025). Fixed production SSL certificate errors and fetch timeout loops with enhanced error handling, network timeout protection, and error result caching to prevent infinite retry loops (August 2025).
- **UI Scrollbar Optimization:** Eliminated redundant vertical scrollbar on the right side of the screen while preserving necessary internal page scrolling. Implemented global CSS rules with `overflow: hidden` on html/body/root and Layout height management to prevent vertical overflow issues in production (August 2025).
-- **Task Management:** Système complet de gestion des tâches avec fonctionnalités temporelles avancées - tri intelligent (pending > priorité > date), dates d'échéance avec sélecteur calendrier, indicateurs visuels pour tâches en retard/urgentes, filtres par échéance (aujourd'hui, cette semaine, en retard, sans échéance), vue Kanban et liste, badges colorés pour statut temporel. Interface optimisée pour suivi prioritaire des tâches non validées (Août 2025).
+- **Task Management:** Système complet de gestion des tâches avec workflow Kanban 7 colonnes - Backlog, À faire, En cours, Révision, Test, Terminé, Archivé. Fonctionnalités temporelles avancées avec tri intelligent par statut/priorité/date, dates d'échéance avec sélecteur calendrier, indicateurs visuels pour tâches en retard/urgentes, filtres par échéance et statut complets. Interface optimisée avec cartes compactes pour vue Kanban étendue et vue liste détaillée (Août 2025).
- **User and Permission Management:** Granular access controls ensuring that users (e.g., employees) have appropriate permissions for tasks like creating customer orders.
- **Reporting:** Statistical reporting, including monthly summaries of deliveries based on `deliveredDate`.
diff --git a/shared/schema.ts b/shared/schema.ts
index fb440bf..e78fa91 100644
--- a/shared/schema.ts
+++ b/shared/schema.ts
@@ -245,7 +245,7 @@ export const tasks = pgTable("tasks", {
description: text("description"), // Description de la tâche (optionnel)
dueDate: timestamp("due_date"), // Date d'échéance (optionnel)
priority: varchar("priority").notNull().default("medium"), // low, medium, high
- status: varchar("status").notNull().default("pending"), // pending, completed
+ status: varchar("status").notNull().default("todo"), // backlog, todo, in_progress, review, testing, done, archived
assignedTo: text("assigned_to").notNull(), // Utilisateur responsable (champ libre)
createdBy: varchar("created_by").notNull(), // Utilisateur créateur
groupId: integer("group_id").notNull(), // Magasin/groupe associé
From fc4a7e1d304828369bccdd7448d44a5876022540 Mon Sep 17 00:00:00 2001
From: michaelschal <35957947-michaelschal@users.noreply.replit.com>
Date: Fri, 15 Aug 2025 13:20:30 +0000
Subject: [PATCH 2/3] Set the Kanban board as the default task view for users
Update the task page to default to the Kanban board view, reordering the view toggles to place Kanban first.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: d43bd811-9372-45a7-8ac9-4a954c0538e1
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/d43bd811-9372-45a7-8ac9-4a954c0538e1/kjeW1Yy
---
client/src/pages/Tasks.tsx | 140 ++++++++++++++++++++++++++++++++-----
1 file changed, 124 insertions(+), 16 deletions(-)
diff --git a/client/src/pages/Tasks.tsx b/client/src/pages/Tasks.tsx
index ad5f478..5c65890 100644
--- a/client/src/pages/Tasks.tsx
+++ b/client/src/pages/Tasks.tsx
@@ -48,7 +48,7 @@ export default function Tasks() {
const [statusFilter, setStatusFilter] = useState
("all");
const [priorityFilter, setPriorityFilter] = useState("all");
const [dueDateFilter, setDueDateFilter] = useState("all");
- const [viewMode, setViewMode] = useState("list");
+ const [viewMode, setViewMode] = useState("kanban");
// Modals
const [showCreateModal, setShowCreateModal] = useState(false);
@@ -430,14 +430,14 @@ export default function Tasks() {
{/* Sélecteur de vue */}
-
-
- Liste
-
Kanban
+
+
+ Liste
+
@@ -549,6 +549,104 @@ export default function Tasks() {
{/* Contenu selon la vue sélectionnée */}
+
+ {/* Vue Kanban avec 7 colonnes */}
+
+
+ {/* Colonne Backlog */}
+
+
+
+ Backlog ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'backlog').length})
+
+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'backlog').map((task: TaskWithRelations) => {
+ return renderKanbanCard(task, canEditTasks);
+ })}
+
+
+
+ {/* Colonne À faire */}
+
+
+
+ À faire ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'todo').length})
+
+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'todo').map((task: TaskWithRelations) => {
+ return renderKanbanCard(task, canEditTasks);
+ })}
+
+
+
+ {/* Colonne En cours */}
+
+
+
+ En cours ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'in_progress').length})
+
+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'in_progress').map((task: TaskWithRelations) => {
+ return renderKanbanCard(task, canEditTasks);
+ })}
+
+
+
+ {/* Colonne Révision */}
+
+
+
+ Révision ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'review').length})
+
+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'review').map((task: TaskWithRelations) => {
+ return renderKanbanCard(task, canEditTasks);
+ })}
+
+
+
+ {/* Colonne Test */}
+
+
+
+ Test ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'testing').length})
+
+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'testing').map((task: TaskWithRelations) => {
+ return renderKanbanCard(task, canEditTasks);
+ })}
+
+
+
+ {/* Colonne Terminé */}
+
+
+
+ Terminé ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'done').length})
+
+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'done').map((task: TaskWithRelations) => {
+ return renderKanbanCard(task, canEditTasks, true);
+ })}
+
+
+
+ {/* Colonne Archivé */}
+
+
+
+ Archivé ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'archived').length})
+
+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'archived').map((task: TaskWithRelations) => {
+ return renderKanbanCard(task, canEditTasks, true);
+ })}
+
+
+
+
+
+
{/* Liste des tâches */}
@@ -564,15 +662,15 @@ export default function Tasks() {
) : (
- {/* Tâches en cours */}
- {paginatedTasks.filter(task => task.status === 'pending').length > 0 && (
+ {/* Tâches actives (non terminées/archivées) */}
+ {paginatedTasks.filter(task => !['done', 'archived'].includes(task.status)).length > 0 && (
- Tâches en cours ({paginatedTasks.filter(task => task.status === 'pending').length})
+ Tâches actives ({paginatedTasks.filter(task => !['done', 'archived'].includes(task.status)).length})
{paginatedTasks
- .filter(task => task.status === 'pending')
+ .filter(task => !['done', 'archived'].includes(task.status))
.map((task) => {
const priorityConfig = getPriorityConfig(task.priority);
const PriorityIcon = priorityConfig.icon;
@@ -590,6 +688,13 @@ export default function Tasks() {
{priorityConfig.label}
+
+ {task.status === 'backlog' ? 'Backlog' :
+ task.status === 'todo' ? 'À faire' :
+ task.status === 'in_progress' ? 'En cours' :
+ task.status === 'review' ? 'Révision' :
+ task.status === 'testing' ? 'Test' : task.status}
+
{task.description && (
@@ -675,15 +780,15 @@ export default function Tasks() {
)}
- {/* Tâches terminées */}
- {paginatedTasks.filter(task => task.status === 'completed').length > 0 && (
+ {/* Tâches terminées et archivées */}
+ {paginatedTasks.filter(task => ['done', 'archived'].includes(task.status)).length > 0 && (
- Tâches terminées ({paginatedTasks.filter(task => task.status === 'completed').length})
+ Tâches terminées ({paginatedTasks.filter(task => ['done', 'archived'].includes(task.status)).length})
{paginatedTasks
- .filter(task => task.status === 'completed')
+ .filter(task => ['done', 'archived'].includes(task.status))
.map((task) => {
const priorityConfig = getPriorityConfig(task.priority);
const PriorityIcon = priorityConfig.icon;
@@ -697,9 +802,12 @@ export default function Tasks() {
{task.title}
-
-
- Terminée
+
+
+ {task.status === 'done' ? 'Terminée' : 'Archivée'}
From 4587e9517eebdc8c2d5b622655fe5289c835e83b Mon Sep 17 00:00:00 2001
From: michaelschal <35957947-michaelschal@users.noreply.replit.com>
Date: Fri, 15 Aug 2025 13:24:42 +0000
Subject: [PATCH 3/3] Expand task workflow with new status options and kanban
view
Update task schemas and UI to include "blocked," "ready_to_deploy," and "deployed" statuses, and adjust the kanban view to accommodate 10 columns.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: d43bd811-9372-45a7-8ac9-4a954c0538e1
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/d43bd811-9372-45a7-8ac9-4a954c0538e1/aphe8bv
---
client/src/components/tasks/TaskForm.tsx | 7 +-
client/src/pages/Tasks.tsx | 153 ++++++++---------------
shared/schema.ts | 2 +-
3 files changed, 57 insertions(+), 105 deletions(-)
diff --git a/client/src/components/tasks/TaskForm.tsx b/client/src/components/tasks/TaskForm.tsx
index 20ef8b5..81a17c3 100644
--- a/client/src/components/tasks/TaskForm.tsx
+++ b/client/src/components/tasks/TaskForm.tsx
@@ -31,7 +31,7 @@ const taskFormSchema = z.object({
title: z.string().min(1, "Le titre est requis"),
description: z.string().optional(),
priority: z.enum(["low", "medium", "high"]).default("medium"),
- status: z.enum(["backlog", "todo", "in_progress", "review", "testing", "done", "archived"]).default("todo"),
+ status: z.enum(["backlog", "todo", "in_progress", "review", "testing", "blocked", "ready_to_deploy", "deployed", "done", "archived"]).default("todo"),
assignedTo: z.string().min(1, "L'assignation est requise"),
dueDate: z.date().optional(),
});
@@ -118,7 +118,7 @@ export default function TaskForm({ task, onClose }: TaskFormProps) {
title: task?.title || "",
description: task?.description || "",
priority: (task?.priority as "low" | "medium" | "high") || "medium",
- status: (task?.status as "backlog" | "todo" | "in_progress" | "review" | "testing" | "done" | "archived") || "todo",
+ status: (task?.status as "backlog" | "todo" | "in_progress" | "review" | "testing" | "blocked" | "ready_to_deploy" | "deployed" | "done" | "archived") || "todo",
assignedTo: task?.assignedTo || "",
dueDate: task?.dueDate ? new Date(task.dueDate) : undefined,
},
@@ -351,6 +351,9 @@ export default function TaskForm({ task, onClose }: TaskFormProps) {
En cours
Révision
Test
+
Bloqué
+
Prêt Déploiement
+
Déployé
Terminé
Archivé
diff --git a/client/src/pages/Tasks.tsx b/client/src/pages/Tasks.tsx
index 5c65890..54fcc45 100644
--- a/client/src/pages/Tasks.tsx
+++ b/client/src/pages/Tasks.tsx
@@ -499,6 +499,9 @@ export default function Tasks() {
En cours
Révision
Test
+
Bloqué
+
Prêt Déploiement
+
Déployé
Terminé
Archivé
@@ -550,9 +553,9 @@ export default function Tasks() {
{/* Contenu selon la vue sélectionnée */}
- {/* Vue Kanban avec 7 colonnes */}
+ {/* Vue Kanban avec 10 colonnes - Workflow complet */}
-
+
{/* Colonne Backlog */}
@@ -606,7 +609,7 @@ export default function Tasks() {
{/* Colonne Test */}
-
+
Test ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'testing').length})
@@ -618,8 +621,47 @@ export default function Tasks() {
+ {/* Colonne Bloqué */}
+
+
+
+ Bloqué ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'blocked').length})
+
+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'blocked').map((task: TaskWithRelations) => {
+ return renderKanbanCard(task, canEditTasks);
+ })}
+
+
+
+ {/* Colonne Prêt Déploiement */}
+
+
+
+ Prêt Déploiement ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'ready_to_deploy').length})
+
+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'ready_to_deploy').map((task: TaskWithRelations) => {
+ return renderKanbanCard(task, canEditTasks);
+ })}
+
+
+
+ {/* Colonne Déployé */}
+
+
+
+ Déployé ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'deployed').length})
+
+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'deployed').map((task: TaskWithRelations) => {
+ return renderKanbanCard(task, canEditTasks);
+ })}
+
+
+
{/* Colonne Terminé */}
-
+
Terminé ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'done').length})
@@ -632,7 +674,7 @@ export default function Tasks() {
{/* Colonne Archivé */}
-
+
Archivé ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'archived').length})
@@ -693,7 +735,10 @@ export default function Tasks() {
task.status === 'todo' ? 'À faire' :
task.status === 'in_progress' ? 'En cours' :
task.status === 'review' ? 'Révision' :
- task.status === 'testing' ? 'Test' : task.status}
+ task.status === 'testing' ? 'Test' :
+ task.status === 'blocked' ? 'Bloqué' :
+ task.status === 'ready_to_deploy' ? 'Prêt Déploiement' :
+ task.status === 'deployed' ? 'Déployé' : task.status}
@@ -868,103 +913,7 @@ export default function Tasks() {
-
- {/* Vue Kanban */}
-
-
- {/* Colonne Backlog */}
-
-
-
- Backlog ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'backlog').length})
-
-
- {filteredTasks.filter((task: TaskWithRelations) => task.status === 'backlog').map((task: TaskWithRelations) => {
- return renderKanbanCard(task, canEditTasks);
- })}
-
-
- {/* Colonne À faire */}
-
-
-
- À faire ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'todo').length})
-
-
- {filteredTasks.filter((task: TaskWithRelations) => task.status === 'todo').map((task: TaskWithRelations) => {
- return renderKanbanCard(task, canEditTasks);
- })}
-
-
-
- {/* Colonne En cours */}
-
-
-
- En cours ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'in_progress').length})
-
-
- {filteredTasks.filter((task: TaskWithRelations) => task.status === 'in_progress').map((task: TaskWithRelations) => {
- return renderKanbanCard(task, canEditTasks);
- })}
-
-
-
- {/* Colonne Révision */}
-
-
-
- Révision ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'review').length})
-
-
- {filteredTasks.filter((task: TaskWithRelations) => task.status === 'review').map((task: TaskWithRelations) => {
- return renderKanbanCard(task, canEditTasks);
- })}
-
-
-
- {/* Colonne Test */}
-
-
-
- Test ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'testing').length})
-
-
- {filteredTasks.filter((task: TaskWithRelations) => task.status === 'testing').map((task: TaskWithRelations) => {
- return renderKanbanCard(task, canEditTasks);
- })}
-
-
-
- {/* Colonne Terminé */}
-
-
-
- Terminé ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'done').length})
-
-
- {filteredTasks.filter((task: TaskWithRelations) => task.status === 'done').map((task: TaskWithRelations) => {
- return renderKanbanCard(task, canEditTasks, true);
- })}
-
-
-
- {/* Colonne Archivé */}
-
-
-
- Archivé ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'archived').length})
-
-
- {filteredTasks.filter((task: TaskWithRelations) => task.status === 'archived').map((task: TaskWithRelations) => {
- return renderKanbanCard(task, canEditTasks, true);
- })}
-
-
-
-
-
diff --git a/shared/schema.ts b/shared/schema.ts
index e78fa91..b2b2baf 100644
--- a/shared/schema.ts
+++ b/shared/schema.ts
@@ -245,7 +245,7 @@ export const tasks = pgTable("tasks", {
description: text("description"), // Description de la tâche (optionnel)
dueDate: timestamp("due_date"), // Date d'échéance (optionnel)
priority: varchar("priority").notNull().default("medium"), // low, medium, high
- status: varchar("status").notNull().default("todo"), // backlog, todo, in_progress, review, testing, done, archived
+ status: varchar("status").notNull().default("todo"), // backlog, todo, in_progress, review, testing, blocked, ready_to_deploy, deployed, done, archived
assignedTo: text("assigned_to").notNull(), // Utilisateur responsable (champ libre)
createdBy: varchar("created_by").notNull(), // Utilisateur créateur
groupId: integer("group_id").notNull(), // Magasin/groupe associé