// ════════════════════════════════════════════════════════════════
// /admin/invites — отдельная страница для управления приглашениями
// ════════════════════════════════════════════════════════════════
function InvitesPanel({ currentUser, onRoute }) {
const A = window.SDH_ADMIN;
const [openCreate, setOpenCreate] = React.useState(false);
const [createdInvite, setCreatedInvite] = React.useState(null);
const [shareInvite, setShareInvite] = React.useState(null);
const pending = A.invites.filter(i => i.status === 'pending');
const accepted = A.invites.filter(i => i.status === 'accepted');
const archived = A.invites.filter(i => i.status === 'expired' || i.status === 'revoked');
return (
}
/>
{/* Pending */}
{pending.length === 0
?
: pending.map(inv => (
setShareInvite(inv)} />
))}
{/* Accepted */}
{accepted.length === 0
?
: accepted.map(inv => )}
{/* Archived */}
{archived.length === 0
?
: archived.map(inv => )}
{openCreate && (
setOpenCreate(false)}
onCreated={(inv) => { setOpenCreate(false); setCreatedInvite(inv); }} />
)}
{createdInvite && setCreatedInvite(null)} />}
{shareInvite && setShareInvite(null)} />}
);
}
function Bucket({ title, count, hue, subtitle, help, children }) {
return (
{title.toUpperCase()}
{count}
{help &&
что это
}
{subtitle && (
{subtitle}
)}
{children}
);
}
function InviteRow({ invite, archived, onShare }) {
const sentDate = invite.sent_at ? new Date(invite.sent_at) : null;
const daysAgo = sentDate ? Math.round((new Date('2026-05-23T10:00:00Z') - sentDate) / 86400000) : null;
const [busy, setBusy] = React.useState(null); // 'resend' | 'revoke' | null
async function handleResend() {
if (!invite.id || invite.id === 'inv_new') return;
setBusy('resend');
if (window.SDH_USE_API && window.SDH_API) {
try {
await window.SDH_API.post(`/api/admin/invites/${invite.id}/resend`);
alert('Приглашение поставлено в очередь (email отключён, ссылка остаётся в админке).');
} catch (e) {
alert('Ошибка: ' + ((e && e.message) || e));
}
} else {
alert('Mock: resend OK');
}
setBusy(null);
}
async function handleRevoke() {
if (!invite.id || invite.id === 'inv_new') return;
if (!confirm(`Отозвать приглашение для ${invite.email}?`)) return;
setBusy('revoke');
if (window.SDH_USE_API && window.SDH_API) {
try {
await window.SDH_API.del(`/api/admin/invites/${invite.id}`);
if (window.SDH_HYDRATE && window.SDH_HYDRATE.refreshInvites) {
await window.SDH_HYDRATE.refreshInvites();
}
} catch (e) {
alert('Ошибка: ' + ((e && e.message) || e));
setBusy(null);
return;
}
} else {
// Mock: update in-memory
if (window.SDH_ADMIN && Array.isArray(window.SDH_ADMIN.invites)) {
const inv = window.SDH_ADMIN.invites.find(i => i.id === invite.id);
if (inv) inv.status = 'revoked';
}
}
setBusy(null);
}
return (
{invite.email}
{invite.note &&
{invite.note}
}
{invite.status === 'accepted'
? `принято ${daysAgo} д назад`
: invite.status === 'expired'
? `истекло ${daysAgo} д назад`
: invite.status === 'revoked'
? `отозвано ${daysAgo} д назад`
: `отправлено ${daysAgo} д назад`}
{invite.status === 'pending' && (
)}
{invite.status === 'accepted' && (
)}
{archived && invite.status !== 'accepted' && (
)}
);
}
window.InvitesPanel = InvitesPanel;