import { Head, Link, router } from '@inertiajs/react'; import { Ban, KeyRound, Lock, Radio, Shield, StopCircle, Undo2, Unlock, UserCheck, Users, UserX, } from 'lucide-react'; import type { LucideIcon } from 'lucide-react'; import { useState } from 'react'; import { updateDefault as updateChannelCreationDefault, updateUser as updateUserCreatorAccess, } from '@/actions/App/Http/Controllers/AdminChannelCreationController'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { useTranslation } from '@/lib/translations'; import { dashboard as adminDashboard } from '@/routes/admin'; import { stop as stopBroadcast } from '@/routes/admin/broadcasts'; import { restore, suspend } from '@/routes/admin/channels'; import { show as showChannel } from '@/routes/channels'; type LiveBroadcast = { id: number; title: string; started_at: string | null; channel: { id: number; slug: string; display_name: string; thumbnail_url: string | null; owner: string; }; }; type AdminChannel = { id: number; slug: string; display_name: string; avatar_url: string | null; thumbnail_url: string | null; is_live: boolean; suspended_at: string | null; owner: { id: number; name: string; }; }; type AdminUser = { id: number; name: string; email: string; is_admin: boolean; can_create_channel: boolean; suspended_at: string | null; created_at: string | null; channel: { id: number; slug: string; display_name: string; } | null; }; type Props = { stats: { users: number; channels: number; live_channels: number; vods: number; creator_access_grants: number; }; channelCreationPolicy: { channel_creation_open: boolean; }; liveBroadcasts: LiveBroadcast[]; channels: AdminChannel[]; users: AdminUser[]; }; type ModerationAction = | { kind: 'stop'; broadcast: LiveBroadcast } | { kind: 'suspend'; channel: AdminChannel } | { kind: 'restore'; channel: AdminChannel } | null; export default function AdminDashboard({ stats, channelCreationPolicy, liveBroadcasts, channels, users, }: Props) { const [action, setAction] = useState(null); const { t } = useTranslation(); function setChannelCreationDefault(channelCreationOpen: boolean) { router.patch( updateChannelCreationDefault(), { channel_creation_open: channelCreationOpen, }, { preserveScroll: true, }, ); } function setUserCreatorAccess(user: AdminUser, canCreateChannel: boolean) { router.patch( updateUserCreatorAccess(user.id), { can_create_channel: canCreateChannel, }, { preserveScroll: true, }, ); } function confirmAction() { if (!action) { return; } if (action.kind === 'stop') { router.post(stopBroadcast(action.broadcast.id), undefined, { onFinish: () => setAction(null), }); return; } router.post( action.kind === 'suspend' ? suspend(action.channel.slug) : restore(action.channel.slug), undefined, { onFinish: () => setAction(null) }, ); } return ( <>

{t('Moderation console')}

{t( 'Review live streams, suspended states, users, and recordings.', )}

{t(':count active broadcasts', { count: liveBroadcasts.length, })}

{t('Channel creation access')}

{t( 'Control who can create a creator channel.', )}

{users.map((user) => ( setUserCreatorAccess(user, canCreateChannel) } /> ))} {users.length === 0 && ( )}

{t('Live broadcasts')}

{t(':count rows', { count: liveBroadcasts.length, })}
{liveBroadcasts.map((broadcast) => (
{broadcast.title}
{t(':channel by :owner', { channel: broadcast.channel.display_name, owner: broadcast.channel.owner, })}
{t('LIVE')}
))} {liveBroadcasts.length === 0 && ( )}

{t('Recent channels')}

{t(':count rows', { count: channels.length, })}
{channels.map((channel) => (
{channel.display_name}
{t('@:slug owned by :owner', { slug: channel.slug, owner: channel.owner.name, })}
{channel.is_live && ( {t('LIVE')} )} {channel.suspended_at && ( {t('SUSPENDED')} )}
{channel.suspended_at ? ( ) : ( )}
))}
!open && setAction(null)} > {t(dialogTitle(action))} {t(dialogDescription(action))} ); } AdminDashboard.layout = { breadcrumbs: [ { title: 'Admin', href: adminDashboard(), }, ], }; function Stat({ icon: Icon, label, value, live = false, }: { icon: LucideIcon; label: string; value: number; live?: boolean; }) { const { t } = useTranslation(); return (
{t(label)}
{value}
); } function CreatorAccessRow({ user, defaultOpen, onChange, }: { user: AdminUser; defaultOpen: boolean; onChange: (canCreateChannel: boolean) => void; }) { const effectiveAccess = defaultOpen || user.can_create_channel; const { t } = useTranslation(); return (
{user.name} {user.is_admin && ( {t('ADMIN')} )} {user.suspended_at && ( {t('SUSPENDED')} )} {effectiveAccess ? t('CAN CREATE') : t('NEEDS APPROVAL')} {defaultOpen && !user.can_create_channel && ( {t('DEFAULT')} )}
{user.email} {user.channel && ( <> {' - '} {user.channel.display_name} )}
); } function Thumb({ url }: { url: string | null }) { return (
{url ? ( ) : (
)}
); } function Avatar({ channel }: { channel: AdminChannel }) { return (
{channel.avatar_url ? ( ) : ( )}
); } function EmptyState({ text }: { text: string }) { const { t } = useTranslation(); return (
{t(text)}
); } function dialogTitle(action: ModerationAction): string { if (action?.kind === 'stop') { return 'Stop live broadcast?'; } if (action?.kind === 'restore') { return 'Restore channel?'; } return 'Suspend channel?'; } function dialogDescription(action: ModerationAction): string { if (action?.kind === 'stop') { return 'The live session will end immediately and the channel will move offline.'; } if (action?.kind === 'restore') { return 'The channel will be visible again if the owning account is active.'; } return 'The channel will be hidden from public browsing and watch pages.'; }