From 75b353153f5907d346e943853d9beb30575bfe20 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Fri, 15 Aug 2025 13:57:04 +0000 Subject: [PATCH] Restored to '1e8d8f8d195518d29262c2b52d36fcaac4155d9f' Replit-Restored-To: 1e8d8f8d195518d29262c2b52d36fcaac4155d9f --- client/src/components/tasks/TaskForm.tsx | 16 +- client/src/pages/Tasks.tsx | 337 +++++++++++------------ replit.md | 2 +- shared/schema.ts | 2 +- 4 files changed, 172 insertions(+), 185 deletions(-) diff --git a/client/src/components/tasks/TaskForm.tsx b/client/src/components/tasks/TaskForm.tsx index 81a17c3..e278adc 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", "blocked", "ready_to_deploy", "deployed", "done", "archived"]).default("todo"), + status: z.enum(["pending", "completed"]).default("pending"), 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" | "blocked" | "ready_to_deploy" | "deployed" | "done" | "archived") || "todo", + status: (task?.status as "pending" | "completed") || "pending", assignedTo: task?.assignedTo || "", dueDate: task?.dueDate ? new Date(task.dueDate) : undefined, }, @@ -346,16 +346,8 @@ export default function TaskForm({ task, onClose }: TaskFormProps) { - Backlog - À faire - En cours - Révision - Test - Bloqué - Prêt Déploiement - Déployé - Terminé - Archivé + En cours + Terminée diff --git a/client/src/pages/Tasks.tsx b/client/src/pages/Tasks.tsx index 0b5fc66..d55737b 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("kanban"); + const [viewMode, setViewMode] = useState("list"); // Modals const [showCreateModal, setShowCreateModal] = useState(false); @@ -194,7 +194,7 @@ export default function Tasks() { if (!isThisWeek(dueDate)) return false; break; case "overdue": - if (!isPast(dueDate) || task.status === 'done' || task.status === 'archived') return false; + if (!isPast(dueDate) || task.status === 'completed') return false; break; case "no_due_date": if (task.dueDate) return false; @@ -207,22 +207,9 @@ export default function Tasks() { return true; }) .sort((a: TaskWithRelations, b: TaskWithRelations) => { - // Ordre des statuts (backlog > todo > in_progress > review > testing > done > archived) - const statusOrder = { - backlog: 7, - todo: 6, - in_progress: 5, - review: 4, - testing: 3, - done: 2, - archived: 1 - }; - const aStatusOrder = statusOrder[a.status as keyof typeof statusOrder] || 0; - const bStatusOrder = statusOrder[b.status as keyof typeof statusOrder] || 0; - - if (aStatusOrder !== bStatusOrder) { - return bStatusOrder - aStatusOrder; // Ordre décroissant - } + // Faire remonter les tâches non validées (pending) en premier + if (a.status === 'pending' && b.status === 'completed') return -1; + if (a.status === 'completed' && b.status === 'pending') return 1; // Pour les tâches de même statut, trier par priorité (high > medium > low) const priorityOrder = { 'high': 3, 'medium': 2, 'low': 1 }; @@ -279,8 +266,8 @@ export default function Tasks() { } }; - const getDueDateStatus = (dueDate: string | Date | null, status: string) => { - if (!dueDate || status === 'done' || status === 'archived') return null; + const getDueDateStatus = (dueDate: string | null, status: string) => { + if (!dueDate || status === 'completed') return null; const due = new Date(dueDate); const now = new Date(); @@ -321,86 +308,6 @@ export default function Tasks() { }; }; - // Fonction pour rendre une carte Kanban - const renderKanbanCard = (task: TaskWithRelations, canEditTasks: boolean, isCompleted = false) => { - const priorityConfig = getPriorityConfig(task.priority); - const PriorityIcon = priorityConfig.icon; - const dueDateStatus = getDueDateStatus(task.dueDate, task.status); - const DueDateIcon = dueDateStatus?.icon; - - return ( - - -
-
{task.title}
- - - {priorityConfig.label.charAt(0)} - -
- - {task.description && ( -

- {task.description} -

- )} - -
- {task.assignedTo} -
- - {dueDateStatus && DueDateIcon && ( -
- - - {dueDateStatus.text} - -
- )} - - {isCompleted && ( -
- {task.completedAt ? format(new Date(task.completedAt), 'dd/MM', { locale: fr }) : ''} -
- )} - -
- {canEditTasks && ( - <> - {!isCompleted && ( - - )} - - - - )} -
-
-
- ); - }; - const canCreateTasks = user?.role === 'admin' || user?.role === 'manager' || user?.role === 'directeur'; const canEditTasks = user?.role === 'admin' || user?.role === 'manager' || user?.role === 'directeur'; @@ -430,14 +337,14 @@ export default function Tasks() { {/* Sélecteur de vue */} - - - Kanban - Liste + + + Kanban + @@ -494,16 +401,8 @@ export default function Tasks() { Toutes - Backlog - À faire - En cours - Révision - Test - Bloqué - Prêt Déploiement - Déployé - Terminé - Archivé + En cours + Terminées @@ -552,39 +451,6 @@ export default function Tasks() {
{/* Contenu selon la vue sélectionnée */} - - {/* Vue Kanban simple - 2 colonnes */} -
-
- {/* Colonne À faire */} -
-

- - À faire ({filteredTasks.filter((task: TaskWithRelations) => task.status !== 'done' && task.status !== 'archived').length}) -

-
- {filteredTasks.filter((task: TaskWithRelations) => task.status !== 'done' && task.status !== 'archived').map((task: TaskWithRelations) => { - return renderKanbanCard(task, canEditTasks); - })} -
-
- - {/* Colonne Terminé */} -
-

- - Terminé ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'done' || task.status === 'archived').length}) -

-
- {filteredTasks.filter((task: TaskWithRelations) => task.status === 'done' || task.status === 'archived').map((task: TaskWithRelations) => { - return renderKanbanCard(task, canEditTasks, true); - })} -
-
-
-
-
- {/* Liste des tâches */}
@@ -600,15 +466,15 @@ export default function Tasks() {
) : (
- {/* Tâches À faire en premier */} - {paginatedTasks.filter(task => ['todo', 'backlog', 'in_progress', 'review', 'testing', 'blocked', 'ready_to_deploy', 'deployed'].includes(task.status)).length > 0 && ( + {/* Tâches en cours */} + {paginatedTasks.filter(task => task.status === 'pending').length > 0 && (

- Tâches à faire ({paginatedTasks.filter(task => ['todo', 'backlog', 'in_progress', 'review', 'testing', 'blocked', 'ready_to_deploy', 'deployed'].includes(task.status)).length}) + Tâches en cours ({paginatedTasks.filter(task => task.status === 'pending').length})

{paginatedTasks - .filter(task => ['todo', 'backlog', 'in_progress', 'review', 'testing', 'blocked', 'ready_to_deploy', 'deployed'].includes(task.status)) + .filter(task => task.status === 'pending') .map((task) => { const priorityConfig = getPriorityConfig(task.priority); const PriorityIcon = priorityConfig.icon; @@ -626,16 +492,6 @@ 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 === 'blocked' ? 'Bloqué' : - task.status === 'ready_to_deploy' ? 'Prêt Déploiement' : - task.status === 'deployed' ? 'Déployé' : task.status} -
{task.description && ( @@ -721,15 +577,15 @@ export default function Tasks() {
)} - {/* 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 > 0 && (

- Tâches terminées ({paginatedTasks.filter(task => ['done', 'archived'].includes(task.status)).length}) + Tâches terminées ({paginatedTasks.filter(task => task.status === 'completed').length})

{paginatedTasks - .filter(task => ['done', 'archived'].includes(task.status)) + .filter(task => task.status === 'completed') .map((task) => { const priorityConfig = getPriorityConfig(task.priority); const PriorityIcon = priorityConfig.icon; @@ -743,12 +599,9 @@ export default function Tasks() {
{task.title}
- - - {task.status === 'done' ? 'Terminée' : 'Archivée'} + + + Terminée
@@ -808,6 +661,148 @@ export default function Tasks() { )}
+ + + {/* Vue Kanban */} +
+
+ {/* Colonne En cours */} +
+

+ + En cours ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'pending').length}) +

+
+ {filteredTasks.filter((task: TaskWithRelations) => task.status === 'pending').map((task: TaskWithRelations) => { + const priorityConfig = getPriorityConfig(task.priority); + const PriorityIcon = priorityConfig.icon; + const dueDateStatus = getDueDateStatus(task.dueDate, task.status); + const DueDateIcon = dueDateStatus?.icon; + + return ( + + +
+
{task.title}
+ + + {priorityConfig.label} + +
+ + {task.description && ( +

+ {task.description} +

+ )} + +
+ Assigné à: {task.assignedTo} +
+ + {dueDateStatus && DueDateIcon && ( +
+ + + {dueDateStatus.text} + +
+ )} + +
+ {canEditTasks && ( + <> + + + + + )} +
+
+
+ ); + })} +
+
+ + {/* Colonne Terminées */} +
+

+ + Terminées ({filteredTasks.filter((task: TaskWithRelations) => task.status === 'completed').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 && ( + <> + + + + )} +
+
+
+ ); + })} +
+
+
+
+
diff --git a/replit.md b/replit.md index db8a8ba..746a952 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 simple de gestion des tâches avec workflow Kanban 2 colonnes - À faire et Terminé. Préférence utilisateur pour un workflow simplifié. 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 vue Kanban simple 2 colonnes et vue liste détaillée (Août 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). - **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 b2b2baf..fb440bf 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, blocked, ready_to_deploy, deployed, done, archived + status: varchar("status").notNull().default("pending"), // pending, completed 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é