import type { AdminUserListView, AdminUserView } from '@dorfteich/shared'; import { keepPreviousData, useQuery } from '@tanstack/react-query'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useAuth } from '../auth/auth-context'; import { apiDelete, apiGet, apiPatch, apiPost } from '../lib/api'; const PAGE_SIZE = 20; /** * Site-Admin user management (issue #59): a searchable, paginated list with the * lifecycle actions. Destructive ones use an inline two-step confirm; the api * enforces the "not on self / not the last Site Admin" rules — this only hides * the buttons on your own row. */ export function UserManager(): React.JSX.Element { const { t } = useTranslation('users'); const { user: me } = useAuth(); const [q, setQ] = useState(''); const [page, setPage] = useState(1); const query = useQuery({ queryKey: ['admin', 'users', q, page], queryFn: () => apiGet( `/admin/users?q=${encodeURIComponent(q)}&page=${page}&pageSize=${PAGE_SIZE}`, ), placeholderData: keepPreviousData, }); const run = async (fn: () => Promise): Promise => { await fn(); await query.refetch(); }; const data = query.data; const totalPages = data ? Math.max(1, Math.ceil(data.total / PAGE_SIZE)) : 1; return (

{t('title')}

{ setQ(e.target.value); setPage(1); }} /> {(data?.users ?? []).map((u) => ( ))}
{t('columns.user')} {t('columns.status')} {t('columns.role')} {t('columns.ponds')} {t('columns.lastLogin')} {t('columns.actions')}
{t('page', { page })}
); } function UserRow({ user, isSelf, run, }: { user: AdminUserView; isSelf: boolean; run: (fn: () => Promise) => Promise; }): React.JSX.Element { const { t } = useTranslation('users'); const [confirmingDelete, setConfirmingDelete] = useState(false); const disabled = user.status === 'DISABLED'; return ( {user.displayName} @{user.username} {isSelf && {t('actions.you')}}
{user.email}
{t(`status.${user.status}`)} {user.isSiteAdmin ? t('role.admin') : t('role.user')} {user.pondCount} {user.lastLoginAt ? new Date(user.lastLoginAt).toLocaleDateString() : t('never')} {!isSelf && ( <> {user.status === 'PENDING_VERIFICATION' && ( )} {confirmingDelete ? ( ) : ( )} )} ); }