import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useStore } from "@/components/Layout"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Megaphone, Plus, X, AlertTriangle, Clock, Circle, Edit2, Bell, Info, CheckCircle } from "lucide-react"; import { useState } from "react"; import React from "react"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { insertAnnouncementSchema } from "@shared/schema"; import type { AnnouncementWithRelations } from "@shared/schema"; import { z } from "zod"; import { safeFormat, safeDate } from "@/lib/dateUtils"; import { useToast } from "@/hooks/use-toast"; // Schéma adapté pour DASHBOARD_MESSAGES const formSchema = z.object({ title: z.string().min(1, "Le titre est obligatoire"), content: z.string().min(1, "Le contenu est obligatoire"), type: z.enum(["info", "warning", "error", "success"]).default("info"), }); export default function AnnouncementCard() { const { user } = useAuthUnified(); const { selectedStoreId } = useStore(); const queryClient = useQueryClient(); const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false); const [editingAnnouncement, setEditingAnnouncement] = useState(null); const { toast } = useToast(); const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { title: "", content: "", type: "info", }, }); // Reset form when editing announcement changes React.useEffect(() => { if (editingAnnouncement) { form.reset({ title: editingAnnouncement.title, content: editingAnnouncement.content, type: editingAnnouncement.type as "info" | "warning" | "error" | "success", }); } else { form.reset({ title: "", content: "", type: "info", }); } }, [editingAnnouncement, form]); // Fetch announcements const { data: announcements = [], isLoading } = useQuery({ queryKey: ['/api/announcements', selectedStoreId], queryFn: async () => { const params = new URLSearchParams(); if (selectedStoreId && user?.role === 'admin') { params.append('storeId', selectedStoreId.toString()); } const response = await fetch(`/api/announcements?${params}`, { credentials: 'include' }); if (!response.ok) { throw new Error('Failed to fetch announcements'); } return response.json(); }, enabled: !!user, }); // Create/Update announcement mutation const createMutation = useMutation({ mutationFn: async (data: z.infer) => { const isEditing = !!editingAnnouncement; console.log('📢 Submitting announcement:', { data, isEditing }); const url = isEditing ? `/api/announcements/${editingAnnouncement.id}` : '/api/announcements'; const method = isEditing ? 'PUT' : 'POST'; const response = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(data), }); console.log('📢 Announcement response:', { status: response.status, statusText: response.statusText, ok: response.ok }); if (!response.ok) { const errorText = await response.text(); console.error('📢 Announcement failed:', errorText); throw new Error(`Failed to ${isEditing ? 'update' : 'create'} announcement: ${response.status} - ${errorText}`); } const result = await response.json(); console.log('📢 Announcement success:', result); return result; }, onSuccess: (data) => { const isEditing = !!editingAnnouncement; console.log('📢 Announcement success callback:', data); queryClient.invalidateQueries({ queryKey: ['/api/announcements'] }); setIsCreateDialogOpen(false); setEditingAnnouncement(null); form.reset(); toast({ title: "Succès", description: isEditing ? "Information modifiée avec succès" : "Information créée avec succès", }); }, onError: (error) => { const isEditing = !!editingAnnouncement; console.error('📢 Announcement error:', error); toast({ title: "Erreur", description: error.message || `Impossible de ${isEditing ? 'modifier' : 'créer'} l'information`, variant: "destructive", }); }, }); // Delete announcement mutation const deleteMutation = useMutation({ mutationFn: async (id: number) => { const response = await fetch(`/api/announcements/${id}`, { method: 'DELETE', credentials: 'include', }); if (!response.ok) { throw new Error('Failed to delete announcement'); } return response.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['/api/announcements'] }); }, }); const onSubmit = (data: z.infer) => { console.log('🔥 [FORM] onSubmit called with data:', data); console.log('🔥 [FORM] Form errors:', form.formState.errors); console.log('🔥 [FORM] Form is valid:', form.formState.isValid); console.log('🔥 [FORM] Form is submitting:', form.formState.isSubmitting); if (!data.title || !data.content) { console.error('🔥 [FORM] Missing required fields:', { title: data.title, content: data.content }); toast({ title: "Erreur", description: "Le titre et le contenu sont obligatoires", variant: "destructive", }); return; } console.log('🔥 [FORM] Calling createMutation.mutate'); createMutation.mutate(data); }; const getPriorityIcon = (priority: string) => { switch (priority) { case 'urgent': return ; case 'important': return ; default: return ; } }; const getPriorityColor = (priority: string) => { switch (priority) { case 'urgent': return 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'; case 'important': return 'bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200'; default: return 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200'; } }; if (isLoading) { return ( Informations
); } const getPriorityConfig = (type: string) => { switch (type) { case 'error': return { color: 'destructive' as const, icon: AlertTriangle, label: 'Erreur' }; case 'warning': return { color: 'default' as const, icon: Bell, label: 'Attention' }; case 'success': return { color: 'default' as const, icon: CheckCircle, label: 'Succès' }; case 'info': default: return { color: 'secondary' as const, icon: Info, label: 'Information' }; } }; const getTypeIcon = (type: string) => { switch (type) { case 'error': return AlertTriangle; case 'warning': return Bell; case 'success': return CheckCircle; case 'info': default: return Info; } }; return (
Informations
{user?.role === 'admin' && ( { setIsCreateDialogOpen(open); if (!open) { setEditingAnnouncement(null); form.reset(); } }}> {editingAnnouncement ? 'Modifier l\'information' : 'Nouvelle information'}
( Titre )} /> ( Contenu