import React, { useState, useEffect } from 'react'; import { useUser } from '../context/UserContext'; import EditGroupModal from '../modals/EditGroupModal'; import CreateGroupModal from '../modals/CreateGroupModal'; import EditSubscriberModal from '../modals/EditSubscriberModal'; import CreateSubscriberModal from '../modals/CreateSubscriberModal'; import Paginator from '../components/Paginator'; import styles from '../styles/Common.module.css'; const PAGE_SIZE = 10; function GroupsPage() { const { token, user } = useUser(); const [groups, setGroups] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [editGroup, setEditGroup] = useState(null); const [editLoading, setEditLoading] = useState(false); const [deleteLoading, setDeleteLoading] = useState(null); const [createGroup, setCreateGroup] = useState(null); const [createLoading, setCreateLoading] = useState(false); const [selectedGroup, setSelectedGroup] = useState(null); useEffect(() => { if (!selectedGroup) fetchGroups(page); // eslint-disable-next-line }, [page, selectedGroup]); const fetchGroups = async (page) => { setLoading(true); setError(''); try { const offset = (page - 1) * PAGE_SIZE; const res = await fetch(`/api/mail/mailing-groups?limit=${PAGE_SIZE}&offset=${offset}`, { headers: token ? { Authorization: `Bearer ${token}` } : {} }); const data = await res.json(); if (!res.ok) { setError(data.error || 'Ошибка загрузки'); setGroups([]); setTotal(0); } else { setGroups(Array.isArray(data.rows) ? data.rows : []); setTotal(data.count || 0); } } catch (e) { setError('Ошибка сети'); setGroups([]); setTotal(0); } finally { setLoading(false); } }; const handleDelete = async (id) => { if (!window.confirm('Удалить группу?')) return; setDeleteLoading(id); try { const res = await fetch(`/api/mail/mailing-groups/${id}`, { method: 'DELETE', headers: token ? { Authorization: `Bearer ${token}` } : {} }); if (!res.ok) { const data = await res.json(); alert(data.error || 'Ошибка удаления'); } else { fetchGroups(page); } } catch (e) { alert('Ошибка сети'); } finally { setDeleteLoading(null); } }; const handleEdit = (group) => { setEditGroup(group); }; const handleEditSave = async (e) => { e.preventDefault(); setEditLoading(true); try { const res = await fetch(`/api/mail/mailing-groups/${editGroup.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) }, body: JSON.stringify({ ...editGroup, user_id: user?.id }) }); if (!res.ok) { const data = await res.json(); alert(data.error || 'Ошибка обновления'); } else { setEditGroup(null); fetchGroups(page); } } catch (e) { alert('Ошибка сети'); } finally { setEditLoading(false); } }; const handleCreate = () => { setCreateGroup({ name: '', description: '' }); }; const handleCreateSave = async (e) => { e.preventDefault(); setCreateLoading(true); try { const res = await fetch('/api/mail/mailing-groups', { method: 'POST', headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) }, body: JSON.stringify({ ...createGroup, user_id: user?.id }) }); if (!res.ok) { const data = await res.json(); alert(data.error || 'Ошибка создания'); } else { setCreateGroup(null); fetchGroups(page); } } catch (e) { alert('Ошибка сети'); } finally { setCreateLoading(false); } }; if (selectedGroup) { return setSelectedGroup(null)} token={token} />; } return (

Подписчики и группы

{loading &&
Загрузка...
} {error &&
{error}
} {!loading && !error && ( <> {groups.map(g => ( setSelectedGroup(g)}> ))} {groups.length === 0 && ( )}
ID Название Описание Действия
{g.id} {g.name} {g.description}
👥
Нет групп
Создайте первую группу для организации подписчиков
)} {editGroup && ( setEditGroup(null)} group={editGroup} loading={editLoading} onChange={setEditGroup} onSave={handleEditSave} /> )} {createGroup && ( setCreateGroup(null)} group={createGroup} loading={createLoading} onChange={setCreateGroup} onSave={handleCreateSave} /> )}
); } function SubscribersInGroupPage({ group, onBack, token }) { const PAGE_SIZE = 10; const [subs, setSubs] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [editSub, setEditSub] = useState(null); const [editLoading, setEditLoading] = useState(false); const [deleteLoading, setDeleteLoading] = useState(null); const [createSub, setCreateSub] = useState(null); const [createLoading, setCreateLoading] = useState(false); useEffect(() => { fetchSubs(page); // eslint-disable-next-line }, [page, group.id]); const fetchSubs = async (page) => { setLoading(true); setError(''); try { const offset = (page - 1) * PAGE_SIZE; const res = await fetch(`/api/mail/group-subscribers?limit=${PAGE_SIZE}&offset=${offset}&group_id=${group.id}`, { headers: token ? { Authorization: `Bearer ${token}` } : {} }); const data = await res.json(); if (!res.ok) { setError(data.error || 'Ошибка загрузки'); setSubs([]); setTotal(0); } else { setSubs(Array.isArray(data.rows) ? data.rows : []); setTotal(data.count || 0); } } catch (e) { setError('Ошибка сети'); setSubs([]); setTotal(0); } finally { setLoading(false); } }; const handleDelete = async (id) => { if (!window.confirm('Удалить подписчика?')) return; setDeleteLoading(id); try { const res = await fetch(`/api/mail/group-subscribers/${id}`, { method: 'DELETE', headers: token ? { Authorization: `Bearer ${token}` } : {} }); if (!res.ok) { const data = await res.json(); alert(data.error || 'Ошибка удаления'); } else { fetchSubs(page); } } catch (e) { alert('Ошибка сети'); } finally { setDeleteLoading(null); } }; const handleEdit = (gs) => { setEditSub({ ...gs.Subscriber, groupSubId: gs.id }); }; const handleEditSave = async (e) => { e.preventDefault(); setEditLoading(true); try { const res = await fetch(`/api/mail/subscribers/${editSub.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) }, body: JSON.stringify(editSub) }); if (!res.ok) { const data = await res.json(); alert(data.error || 'Ошибка обновления'); } else { setEditSub(null); fetchSubs(page); } } catch (e) { alert('Ошибка сети'); } finally { setEditLoading(false); } }; const handleCreate = () => { setCreateSub({ email: '', name: '', status: 'active' }); }; const handleCreateSave = async (e) => { e.preventDefault(); setCreateLoading(true); try { // 1. Создать подписчика const res = await fetch('/api/mail/subscribers', { method: 'POST', headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) }, body: JSON.stringify(createSub) }); const data = await res.json(); if (!res.ok) { alert(data.error || 'Ошибка создания'); } else { // 2. Привязать к группе const res2 = await fetch('/api/mail/group-subscribers', { method: 'POST', headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) }, body: JSON.stringify({ group_id: group.id, subscriber_id: data.id }) }); if (!res2.ok) { const data2 = await res2.json(); alert(data2.error || 'Ошибка привязки к группе'); } else { setCreateSub(null); fetchSubs(page); } } } catch (e) { alert('Ошибка сети'); } finally { setCreateLoading(false); } }; return (

Подписчики группы: {group.name}

{loading &&
Загрузка...
} {error &&
{error}
} {!loading && !error && ( <> {subs.map(gs => ( ))} {subs.length === 0 && ( )}
ID Email Имя Статус Действия
{gs.Subscriber?.id || ''} {gs.Subscriber?.email || ''} {gs.Subscriber?.name || ''} {gs.Subscriber?.status || ''}
📧
Нет подписчиков
В этой группе пока нет подписчиков
)} {editSub && ( setEditSub(null)} sub={editSub} loading={editLoading} onChange={setEditSub} onSave={handleEditSave} /> )} {createSub && ( setCreateSub(null)} sub={createSub} loading={createLoading} onChange={setCreateSub} onSave={handleCreateSave} /> )}
); } export default GroupsPage;