diff --git a/CORRECTION_PRODUCTION_TASKS.md b/CORRECTION_PRODUCTION_TASKS.md new file mode 100644 index 0000000..4086f7f --- /dev/null +++ b/CORRECTION_PRODUCTION_TASKS.md @@ -0,0 +1,48 @@ +# CORRECTION PRODUCTION - MODULE TÂCHES (19 Août 2025) + +## PROBLÈME IDENTIFIÉ +L'implémentation des dates de départ a causé des erreurs JavaScript en production qui empêchent le module tâches de se charger. + +## CORRECTION IMMÉDIATE + +### 1. Backend - Robustesse des données + +Le backend doit retourner des données sécurisées : +- Filtrage des tâches nulles +- Gestion sécurisée des dates +- Validation des propriétés + +### 2. Frontend - Gestion d'erreur + +Le frontend doit gérer les cas d'erreur : +- Validation des données reçues +- Protection contre les propriétés undefined +- Fallback pour les erreurs de parsing de dates + +### 3. Fichiers à copier en production + +**Priorité CRITIQUE :** Copier ces fichiers dans l'ordre : + +1. `server/storage.ts` - Méthode getTasks corrigée +2. `client/src/pages/Tasks.tsx` - Gestion d'erreur renforcée +3. `client/src/components/tasks/NewTaskForm.tsx` - Validation des données +4. `shared/schema.ts` - Types mis à jour + +## TEST DE VALIDATION + +Après copie des fichiers : +1. Redémarrer le serveur Docker +2. Tester l'accès au module tâches +3. Vérifier la création d'une nouvelle tâche +4. Confirmer l'affichage correct des dates + +## ROLLBACK SI PROBLÈME + +Si les erreurs persistent, remplacer uniquement `client/src/components/tasks/TaskForm.tsx` par : + +```typescript +// Version simplifiée sans dates de départ +export { default } from "./SimpleTaskForm"; +``` + +Et créer un SimpleTaskForm.tsx basique sans les nouvelles fonctionnalités. \ No newline at end of file diff --git a/attached_assets/Pasted-Administration-User-related-Environment-related-Registries-Logs-Notifications-Se-1755588965036_1755588965037.txt b/attached_assets/Pasted-Administration-User-related-Environment-related-Registries-Logs-Notifications-Se-1755588965036_1755588965037.txt new file mode 100644 index 0000000..568f758 --- /dev/null +++ b/attached_assets/Pasted-Administration-User-related-Environment-related-Registries-Logs-Notifications-Se-1755588965036_1755588965037.txt @@ -0,0 +1,238 @@ +Administration + +User-related + + +Environment-related + +Registries + +Logs + +Notifications + +Settings + +New version available 2.27.9 +DismissSee what's new +Community Edition +2.27.6 LTS +Containers>logiflow-logiflow-1>Logs +Container logs + + +admin +Log viewer settings +Auto-refresh logs +Wrap lines +Display timestamps +Fetch +All logs +Search +Filter... +Lines +100 +Actions + + + + + taskGroups: [ + + { id: 99, title: 'test', groupId: 1 }, + + { id: 98, title: 'test', groupId: 1 }, + + { id: 91, title: 'refaire podium lot assiettes', groupId: 2 } + + ] + +} + +GET /api/tasks 200 in 461ms + +GET /api/user 304 in 37ms + +📢 [SERVER] User found: { username: 'admin', role: 'admin' } + +🎯 [PRODUCTION] Using PostgreSQL DASHBOARD_MESSAGES table + +🔍 [PRODUCTION] Raw messages from DB: 2 items: [ + + { + + id: 16, + + title: 'Mise à jour Prix', + + content: 'Merci de mettre vos prix a jour tous les matins, certain magasin ne le font pas.', + + type: 'success', + + storeId: null, + + createdBy: 'admin', + + createdAt: 2025-08-18T10:45:08.585Z + + }, + + { + + id: 8, + + title: 'Bon de Livraison', + + content: 'Lorsque vous rentrez un numéro de bon de livraison, faite le a l identique, même les majuscules. Pour My candy Shop, prendre le numéro de commande.', + + type: 'info', + + storeId: null, + + createdBy: 'admin_local', + + createdAt: 2025-08-07T11:04:38.123Z + + } + +] + +🔍 [PRODUCTION] Looking for user with username: admin + +🔍 [PRODUCTION] Looking for user with username: admin_local + +🔍 [PRODUCTION] User search result: Found + +🔍 [PRODUCTION] User search result: Not found + +🎯 [PRODUCTION] Found announcements: 2 + +❌ [PRODUCTION] User not found with username: admin_local + +GET /api/announcements 200 in 36ms + +GET /api/publicities 304 in 28ms + +GET /api/user 304 in 6ms + +Orders API called with: { + + startDate: undefined, + + endDate: undefined, + + storeId: '1', + + userRole: 'admin' + +} + +Admin filtering with groupIds: [ 1 ] + +Fetching all orders + +📢 [SERVER] Fetching announcements for user: admin_local environment: production + +Deliveries API called with: { + + startDate: undefined, + + endDate: undefined, + + storeId: '1', + + withBL: undefined, + + userRole: 'admin' + +} + +Admin filtering deliveries with groupIds: [ 1 ] + +Fetching all deliveries + +🔗 PRODUCTION: getOrders() récupéré 33 commandes avec relations + +Orders returned: 33 items + +GET /api/orders 200 in 94ms + +📢 [SERVER] User found: { username: 'admin', role: 'admin' } + +🎯 [PRODUCTION] Using PostgreSQL DASHBOARD_MESSAGES table + +Customer Orders API called with: { groupIds: [ 1 ], userRole: 'admin' } + +🔗 PRODUCTION: getDeliveries() récupéré 49 livraisons avec relations + +Deliveries returned: 49 items + +GET /api/deliveries 304 in 150ms + +GET /api/groups 304 in 94ms + +🔍 [PRODUCTION] Raw messages from DB: 2 items: [ + + { + + id: 16, + + title: 'Mise à jour Prix', + + content: 'Merci de mettre vos prix a jour tous les matins, certain magasin ne le font pas.', + + type: 'success', + + storeId: null, + + createdBy: 'admin', + + createdAt: 2025-08-18T10:45:08.585Z + + }, + + { + + id: 8, + + title: 'Bon de Livraison', + + content: 'Lorsque vous rentrez un numéro de bon de livraison, faite le a l identique, même les majuscules. Pour My candy Shop, prendre le numéro de commande.', + + type: 'info', + + storeId: null, + + createdBy: 'admin_local', + + createdAt: 2025-08-07T11:04:38.123Z + + } + +] + +🔍 [PRODUCTION] Looking for user with username: admin + +🔍 [PRODUCTION] Looking for user with username: admin_local + +Customer Orders returned: 10 items + +GET /api/customer-orders 304 in 156ms + +🔍 [PRODUCTION] User search result: Found + +🔍 [PRODUCTION] User search result: Not found + +❌ [PRODUCTION] User not found with username: admin_local + +🎯 [PRODUCTION] Found announcements: 2 + +GET /api/announcements 200 in 157ms + +GET /api/user 304 in 7ms + +GET /api/publicities 304 in 12ms + +GET /api/publicities 200 in 10ms + +HEAD /api/health 200 in 0ms \ No newline at end of file diff --git a/attached_assets/{418F692C-F3C5-4832-A3C4-C4AA2FEFEDB7}_1755588938495.png b/attached_assets/{418F692C-F3C5-4832-A3C4-C4AA2FEFEDB7}_1755588938495.png new file mode 100644 index 0000000..2168edb Binary files /dev/null and b/attached_assets/{418F692C-F3C5-4832-A3C4-C4AA2FEFEDB7}_1755588938495.png differ diff --git a/client/src/components/tasks/NewTaskForm.tsx b/client/src/components/tasks/NewTaskForm.tsx index 2caec60..acc2c65 100644 --- a/client/src/components/tasks/NewTaskForm.tsx +++ b/client/src/components/tasks/NewTaskForm.tsx @@ -66,13 +66,14 @@ export default function NewTaskForm({ task, onClose }: NewTaskFormProps) { // Récupération des magasins pour l'auto-sélection const { data: groupsData = [] } = useQuery({ queryKey: ['/api/groups'], + enabled: !!user, // Seulement si l'utilisateur est connecté }); // Filtrer les groupes selon le magasin sélectionné pour les admins const groups = Array.isArray(groupsData) ? ( user?.role === 'admin' && selectedStoreId - ? groupsData.filter(g => g.id === selectedStoreId) - : groupsData + ? groupsData.filter(g => g && g.id === selectedStoreId) + : groupsData.filter(g => g) ) : []; // Auto-sélectionner le magasin selon les règles diff --git a/client/src/components/tasks/SimpleTaskForm.tsx b/client/src/components/tasks/SimpleTaskForm.tsx new file mode 100644 index 0000000..868d88e --- /dev/null +++ b/client/src/components/tasks/SimpleTaskForm.tsx @@ -0,0 +1,343 @@ +import { useState, useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useMutation, useQueryClient, useQuery } from "@tanstack/react-query"; +import { useAuthUnified } from "@/hooks/useAuthUnified"; +import { useStore } from "@/components/Layout"; +import { useToast } from "@/hooks/use-toast"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Calendar } from "@/components/ui/calendar"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; +import { apiRequest } from "@/lib/queryClient"; +import { insertTaskSchema, Task, Group } from "@shared/schema"; +import { z } from "zod"; +import { CalendarIcon } from "lucide-react"; +import { format } from "date-fns"; +import { fr } from "date-fns/locale"; +import { cn } from "@/lib/utils"; + +// Version simplifiée du schéma sans startDate pour éviter les erreurs de production +const simpleTaskFormSchema = 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(["pending", "completed"]).default("pending"), + assignedTo: z.string().min(1, "L'assignation est requise"), + dueDate: z.date().optional(), // Seulement la date d'échéance +}); + +type SimpleTaskFormData = z.infer; + +type TaskWithRelations = Task & { + assignedUser?: { id: string; username: string; firstName?: string; lastName?: string; }; + creator?: { id: string; username: string; firstName?: string; lastName?: string; }; + group?: { id: number; name: string; color: string; }; +}; + +interface SimpleTaskFormProps { + task?: TaskWithRelations; + onClose: () => void; +} + +export default function SimpleTaskForm({ task, onClose }: SimpleTaskFormProps) { + const { user } = useAuthUnified(); + const { selectedStoreId } = useStore(); + const { toast } = useToast(); + const queryClient = useQueryClient(); + + // État local pour gérer le magasin sélectionné + const [localSelectedStoreId, setLocalSelectedStoreId] = useState(null); + + // Récupération sécurisée des magasins + const { data: groupsData = [] } = useQuery({ + queryKey: ['/api/groups'], + enabled: !!user, + onError: () => console.warn('Erreur lors du chargement des magasins') + }); + + const groups = Array.isArray(groupsData) ? groupsData.filter(g => g && g.id) : []; + + // Auto-sélection du magasin + useEffect(() => { + if (groups.length > 0 && !localSelectedStoreId) { + let defaultStoreId: number | null = null; + + if (user?.role === 'admin') { + if (selectedStoreId && groups.find(g => g.id === selectedStoreId)) { + defaultStoreId = selectedStoreId; + } else { + defaultStoreId = groups[0].id; + } + } else { + defaultStoreId = groups[0].id; + } + + if (defaultStoreId) { + setLocalSelectedStoreId(defaultStoreId); + } + } + }, [groups, selectedStoreId, user?.role, localSelectedStoreId]); + + const form = useForm({ + resolver: zodResolver(simpleTaskFormSchema), + defaultValues: { + title: task?.title || "", + description: task?.description || "", + priority: (task?.priority as "low" | "medium" | "high") || "medium", + status: (task?.status as "pending" | "completed") || "pending", + assignedTo: task?.assignedTo || "", + dueDate: task?.dueDate ? new Date(task.dueDate) : undefined, + }, + }); + + // Mutations sécurisées + const createMutation = useMutation({ + mutationFn: (data: any) => { + const taskData = { + ...data, + groupId: localSelectedStoreId, + createdBy: user?.id, + dueDate: data.dueDate || null + }; + return apiRequest("/api/tasks", "POST", taskData); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["/api/tasks"] }); + toast({ + title: "Succès", + description: "Tâche créée avec succès", + }); + onClose(); + }, + onError: (error: any) => { + console.error("Error creating task:", error); + toast({ + title: "Erreur", + description: "Impossible de créer la tâche", + variant: "destructive", + }); + }, + }); + + const updateMutation = useMutation({ + mutationFn: (data: any) => { + return apiRequest(`/api/tasks/${task?.id}`, "PUT", data); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["/api/tasks"] }); + toast({ + title: "Succès", + description: "Tâche modifiée avec succès", + }); + onClose(); + }, + onError: (error: any) => { + console.error("Error updating task:", error); + toast({ + title: "Erreur", + description: "Impossible de modifier la tâche", + variant: "destructive", + }); + }, + }); + + const onSubmit = (data: SimpleTaskFormData) => { + if (!localSelectedStoreId) { + toast({ + title: "Erreur", + description: "Aucun magasin disponible", + variant: "destructive", + }); + return; + } + + if (task) { + updateMutation.mutate(data); + } else { + createMutation.mutate(data); + } + }; + + return ( +
+
+
+

+ {task ? "Modifier la tâche" : "Nouvelle tâche"} +

+
+
+ +
+ + {/* Titre */} + ( + + Titre * + + + + + + )} + /> + + {/* Description */} + ( + + Description + +