import { useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Megaphone, Plus, Edit2, X, Info, AlertTriangle, Star, Sparkles } from "lucide-react"; import { useAuthUnified } from "@/hooks/useAuthUnified"; import { useStore } from "@/components/Layout"; import { safeFormat, safeDate } from "@/lib/dateUtils"; import { insertAnnouncementSchema, type InsertAnnouncement, type DashboardMessage } from "@shared/schema"; import { useToast } from "@/hooks/use-toast"; // Type pour les annonces avec relations type AnnouncementWithRelations = DashboardMessage & { author: { id: string | number; firstName?: string; lastName?: string; username: string; }; group?: { id: number; name: string; }; }; // Configuration des priorités avec icônes const getPriorityConfig = (type: string) => { switch (type) { case 'error': return { label: 'Nouveauté', color: 'bg-purple-100 text-purple-800', icon: Sparkles, iconColor: 'text-purple-600' }; case 'warning': return { label: 'Attention', color: 'bg-orange-100 text-orange-800', icon: AlertTriangle, iconColor: 'text-orange-600' }; case 'success': return { label: 'Important', color: 'bg-red-100 text-red-800', icon: Star, iconColor: 'text-red-600' }; case 'info': default: return { label: 'Information', color: 'bg-blue-100 text-blue-800', icon: Info, iconColor: 'text-blue-600' }; } }; export default function AnnouncementCard() { const { user } = useAuthUnified(); const { selectedStoreId } = useStore(); const queryClient = useQueryClient(); const { toast } = useToast(); const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false); const [editingAnnouncement, setEditingAnnouncement] = useState(null); // Configuration du formulaire const form = useForm({ resolver: zodResolver(insertAnnouncementSchema), defaultValues: { title: '', content: '', type: 'info', storeId: selectedStoreId ? parseInt(selectedStoreId.toString()) : null, createdBy: user?.username || '', }, }); // Construire l'URL avec les paramètres appropriés const buildApiUrl = () => { const params = new URLSearchParams(); // Pour les admins, permettre le filtrage par magasin sélectionné if (user?.role === 'admin' && selectedStoreId) { params.append('storeId', selectedStoreId.toString()); } return `/api/announcements${params.toString() ? `?${params.toString()}` : ''}`; }; // Récupérer les annonces const { data: announcements = [], isLoading } = useQuery({ queryKey: ['/api/announcements', selectedStoreId, user?.role], queryFn: async () => { const url = buildApiUrl(); console.log('🔍 [ANNOUNCEMENTS] Fetching from:', url); const response = await fetch(url, { credentials: 'include' }); if (!response.ok) { console.error('❌ [ANNOUNCEMENTS] Fetch failed:', response.status, response.statusText); if (response.status === 401) { console.log('🔑 [ANNOUNCEMENTS] Authentication required'); return []; } throw new Error(`Failed to fetch announcements: ${response.status}`); } const data = await response.json(); console.log('✅ [ANNOUNCEMENTS] Data received:', data); if (!Array.isArray(data)) { console.warn('⚠️ [ANNOUNCEMENTS] Data is not an array:', data); return []; } return data; }, enabled: !!user, }); // Mutation pour créer/modifier une annonce const createMutation = useMutation({ mutationFn: async (announcementData: InsertAnnouncement) => { const url = editingAnnouncement ? `/api/announcements/${editingAnnouncement.id}` : '/api/announcements'; const method = editingAnnouncement ? 'PUT' : 'POST'; console.log(`🔍 [ANNOUNCEMENTS] ${method} ${url}`, announcementData); const response = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(announcementData), }); if (!response.ok) { const errorText = await response.text(); console.error(`❌ [ANNOUNCEMENTS] ${method} failed:`, response.status, errorText); throw new Error(`Failed to ${editingAnnouncement ? 'update' : 'create'} announcement: ${response.status}`); } return response.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['/api/announcements'] }); setIsCreateDialogOpen(false); setEditingAnnouncement(null); form.reset(); toast({ title: editingAnnouncement ? "Information modifiée" : "Information créée", description: editingAnnouncement ? "L'information a été modifiée avec succès." : "L'information a été créée avec succès.", }); }, onError: (error) => { console.error('❌ [ANNOUNCEMENTS] Mutation error:', error); toast({ title: "Erreur", description: `Impossible de ${editingAnnouncement ? 'modifier' : 'créer'} l'information.`, variant: "destructive", }); }, }); // Mutation pour supprimer une annonce const deleteMutation = useMutation({ mutationFn: async (announcementId: number) => { const response = await fetch(`/api/announcements/${announcementId}`, { method: 'DELETE', credentials: 'include', }); if (!response.ok) { throw new Error(`Failed to delete announcement: ${response.status}`); } return response.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['/api/announcements'] }); toast({ title: "Information supprimée", description: "L'information a été supprimée avec succès.", }); }, onError: (error) => { console.error('❌ [ANNOUNCEMENTS] Delete error:', error); toast({ title: "Erreur", description: "Impossible de supprimer l'information.", variant: "destructive", }); }, }); // Soumission du formulaire const onSubmit = (data: InsertAnnouncement) => { console.log('🔍 [ANNOUNCEMENTS] Form submitted:', data); const announcementData = { ...data, createdBy: user?.username || '', storeId: data.storeId || null }; createMutation.mutate(announcementData); }; // Ouvrir le dialogue pour éditer const handleEdit = (announcement: AnnouncementWithRelations) => { setEditingAnnouncement(announcement); form.reset({ title: announcement.title, content: announcement.content, type: announcement.type as any, storeId: announcement.storeId, createdBy: announcement.createdBy, }); setIsCreateDialogOpen(true); }; return ( <>
Informations {user?.role === 'admin' && ( )}
{isLoading ? (
) : !Array.isArray(announcements) || announcements.length === 0 ? (

Aucune information disponible

{user?.role === 'admin' ? 'Créez la première annonce' : 'Aucune annonce pour le moment'}

) : ( announcements.map((announcement) => { const priorityConfig = getPriorityConfig(announcement.type); return (

{announcement.title}

{announcement.content && (

{announcement.content}

)} {announcement.group && (

{announcement.group.name}

)}
{priorityConfig.label}

{safeFormat(safeDate(announcement.createdAt), 'dd/MM à HH:mm')}

{user?.role === 'admin' && (
)}
); }) )}
{/* Dialog pour créer/modifier une annonce */} { setIsCreateDialogOpen(open); if (!open) { setEditingAnnouncement(null); form.reset(); } }}> {editingAnnouncement ? 'Modifier l\'information' : 'Nouvelle information'}
( Titre )} /> ( Contenu