import { useState, useMemo } from 'react'; import { Box, Button, Checkbox, Flex, Heading, IconButton, Menu, MenuButton, MenuItem, MenuList, Select, Stack, Table, Tbody, Td, Text, Th, Thead, Tr, useDisclosure, useToast, Badge, InputGroup, InputLeftElement, Input, HStack, useColorModeValue, Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, ModalFooter, ModalCloseButton, FormControl, FormLabel, VStack } from '@chakra-ui/react'; import { AddIcon, DeleteIcon, EmailIcon, Search2Icon, StarIcon, ArrowForwardIcon } from '@chakra-ui/icons'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { format } from 'date-fns'; import { cs } from 'date-fns/locale'; import AdminLayout from '../../layouts/AdminLayout'; import { getContactMessages, markAsRead, deleteMessage, deleteMultipleMessages, forwardAllMessages, ContactMessage } from '../../services/admin/contactMessages'; import Pagination from '../../components/common/Pagination'; import MessageDetailModal from '../../components/admin/MessageDetailModal'; import ConfirmationDialog from '../../components/common/ConfirmationDialog'; export default function MessagesAdminPage() { const cardBg = useColorModeValue('white', 'gray.800'); const borderColor = useColorModeValue('gray.200', 'gray.700'); const inputBg = useColorModeValue('white', 'gray.700'); const [selectedMessages, setSelectedMessages] = useState([]); const [searchTerm, setSearchTerm] = useState(''); const [statusFilter, setStatusFilter] = useState<'all' | 'read' | 'unread'>('all'); const [sortBy, setSortBy] = useState<{ field: string; order: 'asc' | 'desc' }>({ field: 'createdAt', order: 'desc', }); const [pagination, setPagination] = useState({ page: 1, limit: 10, }); const { isOpen: isDetailOpen, onOpen: onDetailOpen, onClose: onDetailClose } = useDisclosure(); const { isOpen: isDeleteOpen, onOpen: onDeleteOpen, onClose: onDeleteClose } = useDisclosure(); const { isOpen: isForwardAllOpen, onOpen: onForwardAllOpen, onClose: onForwardAllClose } = useDisclosure(); const [forwardAllEmail, setForwardAllEmail] = useState(''); const [selectedMessage, setSelectedMessage] = useState(null); const toast = useToast(); const queryClient = useQueryClient(); const { data, isLoading, isError } = useQuery({ queryKey: ['admin', 'contact-messages', { ...pagination, searchTerm, statusFilter, sortBy }], queryFn: () => getContactMessages({ page: pagination.page, limit: pagination.limit, search: searchTerm, isRead: statusFilter === 'all' ? undefined : statusFilter === 'read', sortBy: sortBy.field, sortOrder: sortBy.order, }), keepPreviousData: true, }); const markAsReadMutation = useMutation({ mutationFn: markAsRead, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin', 'contact-messages'] }); toast({ title: 'Zpráva označena jako přečtená', status: 'success', duration: 3000, isClosable: true, }); }, }); const deleteMessageMutation = useMutation({ mutationFn: deleteMessage, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin', 'contact-messages'] }); toast({ title: 'Zpráva smazána', status: 'success', duration: 3000, isClosable: true, }); }, }); const deleteMultipleMutation = useMutation({ mutationFn: deleteMultipleMessages, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin', 'contact-messages'] }); setSelectedMessages([]); toast({ title: 'Vybrané zprávy byly smazány', status: 'success', duration: 3000, isClosable: true, }); }, }); const forwardAllMutation = useMutation({ mutationFn: forwardAllMessages, onSuccess: (data) => { toast({ title: 'Zprávy se přeposílají', description: data.message || 'Všechny zprávy budou přeposlány na zadaný e-mail', status: 'success', duration: 5000, isClosable: true, }); setForwardAllEmail(''); onForwardAllClose(); }, onError: () => { toast({ title: 'Chyba', description: 'Nepodařilo se přeposlat zprávy', status: 'error', duration: 3000, isClosable: true, }); }, }); const handleViewMessage = (message: ContactMessage) => { setSelectedMessage(message); if (!message.isRead) { markAsReadMutation.mutate(message.id); } onDetailOpen(); }; const handleDeleteClick = (message: ContactMessage) => { setSelectedMessage(message); onDeleteOpen(); }; const handleDeleteConfirm = () => { if (selectedMessage) { deleteMessageMutation.mutate(selectedMessage.id); } onDeleteClose(); }; const handleBulkDelete = () => { if (selectedMessages.length > 0) { deleteMultipleMutation.mutate(selectedMessages); } }; const handleForwardAll = () => { if (!forwardAllEmail || !forwardAllEmail.includes('@')) { toast({ title: 'Chyba', description: 'Zadejte platnou e-mailovou adresu', status: 'error', duration: 3000, }); return; } forwardAllMutation.mutate(forwardAllEmail); }; const handleSelectAll = (e: React.ChangeEvent) => { if (e.target.checked) { setSelectedMessages(data?.data.map((msg) => msg.id) || []); } else { setSelectedMessages([]); } }; const handleSelectMessage = (id: string, isSelected: boolean) => { if (isSelected) { setSelectedMessages((prev) => [...prev, id]); } else { setSelectedMessages((prev) => prev.filter((msgId) => msgId !== id)); } }; const handleSort = (field: string) => { setSortBy((prev) => ({ field, order: prev.field === field && prev.order === 'asc' ? 'desc' : 'asc', })); }; const formatDate = (dateString: string) => { return format(new Date(dateString), 'd. M. yyyy HH:mm', { locale: cs }); }; const getSortIcon = (field: string) => { if (sortBy.field !== field) return null; return sortBy.order === 'asc' ? '↑' : '↓'; }; return ( Příchozí zprávy Spravujte příchozí zprávy z kontaktního formuláře {selectedMessages.length > 0 && ( )} setSearchTerm(e.target.value)} /> {isLoading ? ( ) : isError ? ( ) : data?.data.length === 0 ? ( ) : ( data?.data.map((message) => ( )) )}
0 && selectedMessages.length === data?.data.length} onChange={handleSelectAll} /> handleSort('name')} _hover={{ textDecoration: 'underline' }} > Jméno {getSortIcon('name')} handleSort('email')} _hover={{ textDecoration: 'underline' }} > E-mail {getSortIcon('email')} Předmět Zdroj handleSort('createdAt')} _hover={{ textDecoration: 'underline' }} > Datum {getSortIcon('createdAt')} Stav Akce
Načítání...
Chyba při načítání zpráv
Žádné zprávy nenalezeny
handleSelectMessage(message.id, e.target.checked)} /> {message.name} {message.email} {message.subject || '—'} {message.source === 'sponsor' ? ( Sponzor ) : ( Kontakt )} {formatDate(message.createdAt)} {message.isRead ? ( Přečteno ) : ( Nová zpráva )} } size="sm" colorScheme="blue" variant="ghost" onClick={() => handleViewMessage(message)} /> } size="sm" colorScheme="red" variant="ghost" onClick={() => handleDeleteClick(message)} isLoading={ deleteMessageMutation.isLoading && deleteMessageMutation.variables === message.id } />
{data && data.total > 0 && ( Zobrazeno {data.data.length} z {data.total} zpráv setPagination((p) => ({ ...p, page }))} /> )}
{selectedMessage && ( { onDetailClose(); handleDeleteClick(selectedMessage); }} onMarkAsRead={() => markAsReadMutation.mutate(selectedMessage.id)} /> )} Přeposlat všechny zprávy Všechny příchozí zprávy budou přeposlány na zadanou e-mailovou adresu. E-mailová adresa setForwardAllEmail(e.target.value)} />
); }