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 "@/contexts/StoreContext"; 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, FormDescription } from "@/components/ui/form"; import { apiRequest } from "@/lib/queryClient"; import { insertTaskSchema, Task, Group } from "@shared/schema"; import { z } from "zod"; import { CalendarIcon, Clock, AlertTriangle, Info } from "lucide-react"; import { format } from "date-fns"; import { fr } from "date-fns/locale"; 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; }; isFutureTask?: boolean; }; // Schéma avec les deux dates : départ et échéance 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(["pending", "completed"]).default("pending"), assignedTo: z.string().min(1, "L'assignation est requise"), startDate: z.date().optional(), // Date de départ dueDate: z.date().optional(), // Date d'échéance }).refine((data) => { // Validation : la date d'échéance ne peut pas être antérieure à la date de départ if (data.startDate && data.dueDate) { return data.dueDate >= data.startDate; } return true; }, { message: "La date d'échéance ne peut pas être antérieure à la date de départ", path: ["dueDate"] }); type TaskFormData = z.infer; interface NewTaskFormProps { task?: TaskWithRelations; onClose: () => void; } export default function NewTaskForm({ task, onClose }: NewTaskFormProps) { const { user } = useAuthUnified(); const { selectedStoreId } = useStore(); const { toast } = useToast(); const queryClient = useQueryClient(); // État local pour gérer le magasin sélectionné (auto-sélection intelligente) const [localSelectedStoreId, setLocalSelectedStoreId] = useState(null); // 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 && g.id === selectedStoreId) : groupsData.filter(g => g) ) : []; // Auto-sélectionner le magasin selon les règles 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); } } if (user?.role === 'admin' && selectedStoreId && selectedStoreId !== localSelectedStoreId) { setLocalSelectedStoreId(selectedStoreId); } }, [groups, selectedStoreId, user?.role, localSelectedStoreId]); const form = useForm({ resolver: zodResolver(taskFormSchema), 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 || "", startDate: task?.startDate ? new Date(task.startDate) : undefined, dueDate: task?.dueDate ? new Date(task.dueDate) : undefined, }, }); // Mutation pour créer/modifier une tâche const createMutation = useMutation({ mutationFn: (data: any) => { const taskData = { ...data, groupId: localSelectedStoreId, createdBy: user?.id, startDate: data.startDate || null, dueDate: data.dueDate || null }; console.log('🚀 Creating task with complete data:', { ...taskData, startDate: taskData.startDate, startDateType: typeof taskData.startDate, dueDate: taskData.dueDate, dueDateType: typeof taskData.dueDate }); 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) => { console.log('🔄 Updating task with complete data:', { taskId: task?.id, data, startDate: data.startDate, startDateType: typeof data.startDate, dueDate: data.dueDate, dueDateType: typeof data.dueDate }); 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: TaskFormData) => { console.log('📝 NewTaskForm onSubmit:', { isEdit: !!task, taskId: task?.id, formData: data, startDate: data.startDate, dueDate: data.dueDate, localSelectedStoreId }); // Vérifier qu'un magasin est auto-sélectionné if (!localSelectedStoreId) { toast({ title: "Erreur", description: "Aucun magasin disponible pour créer une tâche", variant: "destructive", }); return; } if (task) { // Modification de tâche existante const submitData = { title: data.title, description: data.description, priority: data.priority, status: data.status, assignedTo: data.assignedTo, startDate: data.startDate || null, dueDate: data.dueDate || null, }; console.log('🔄 Update task submitData:', submitData); updateMutation.mutate(submitData); } else { // Création d'une nouvelle tâche console.log('➕ Create task data:', data); createMutation.mutate(data); } }; const getPriorityLabel = (priority: string) => { switch (priority) { case 'high': return 'Élevée'; case 'medium': return 'Moyenne'; case 'low': return 'Faible'; default: return 'Moyenne'; } }; const getStatusLabel = (status: string) => { switch (status) { case 'pending': return 'En cours'; case 'completed': return 'Terminée'; default: return 'En cours'; } }; const today = new Date(); return (

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

{task ? "Modifiez les informations de cette tâche" : "Créez une nouvelle tâche avec dates de départ et d'échéance"}

{/* Affichage du magasin auto-sélectionné */} {localSelectedStoreId && (

Magasin sélectionné :{" "} {(() => { const selectedGroup = groups.find(g => g.id === localSelectedStoreId); return selectedGroup ? (

{selectedGroup.name} ) : `Magasin ID: ${localSelectedStoreId}`; })()}

)} {/* Avertissement si aucun magasin n'est disponible */} {!localSelectedStoreId && groups.length === 0 && (

Aucun magasin disponible. Veuillez contacter un administrateur.

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