import { useApp } from '../context/AppContext.js';
import { buildExportHtml } from '../App.jsx';
import {
adminDeleteUser,
adminDeleteProject,
adminGetProject,
adminResetPassword,
} from '../syncService.js';
export default function AdminPage() {
const {
authUser,
authStatus,
goHome,
adminTab, setAdminTab,
adminUsers,
adminProjects,
adminLoading,
adminError,
adminViewProject, setAdminViewProject,
adminResetResult, setAdminResetResult,
loadAdminData,
} = useApp();
const handleDeleteUserAdmin = async (id, email) => {
if (!window.confirm(`Delete account "${email}"? Their projects are kept, not deleted, but become inaccessible until reassigned.`)) return;
const result = await adminDeleteUser(id);
if (result.ok) loadAdminData();
else alert(result.error ?? 'Failed to delete user.');
};
const handleDeleteProjectAdmin = async (id, title) => {
if (!window.confirm(`Permanently delete project "${title}"? This cannot be undone.`)) return;
const result = await adminDeleteProject(id);
if (result.ok) loadAdminData();
else alert(result.error ?? 'Failed to delete project.');
};
const handleViewProjectAdmin = async (id) => {
const result = await adminGetProject(id);
if (result.ok) setAdminViewProject(result.data);
else alert(result.error ?? 'Failed to load project.');
};
const handleResetPasswordAdmin = async (id, email) => {
if (!window.confirm(`Reset the password for "${email}"? They'll be signed out everywhere and need the new temporary password to log back in.`)) return;
const result = await adminResetPassword(id);
if (result.ok) setAdminResetResult({ email, temporaryPassword: result.data.temporaryPassword });
else alert(result.error ?? 'Failed to reset password.');
};
return (
Bible Study Project
π‘ Admin
β Back
{authStatus}
setAdminTab('users')}
className={`rounded-lg px-4 py-2 text-sm font-semibold transition ${adminTab === 'users' ? 'bg-slate-900 text-white' : 'border border-slate-300 bg-white text-slate-600 hover:bg-slate-50'}`}>
Users ({adminUsers.length})
setAdminTab('projects')}
className={`rounded-lg px-4 py-2 text-sm font-semibold transition ${adminTab === 'projects' ? 'bg-slate-900 text-white' : 'border border-slate-300 bg-white text-slate-600 hover:bg-slate-50'}`}>
Projects ({adminProjects.length})
{adminLoading && Loadingβ¦
}
{adminError && {adminError}
}
{!adminLoading && !adminError && adminTab === 'users' && (
Email
Joined
2FA
Projects
{adminUsers.map((u) => (
{u.email}{u.id === authUser.id && (you) }
{new Date(u.createdAt).toLocaleDateString()}
{u.totpEnabled ? 'β' : 'β'}
{u.projectCount}
{u.id !== authUser.id && (
handleResetPasswordAdmin(u.id, u.email)}
className="rounded-lg border border-slate-300 px-3 py-1 text-xs font-semibold text-slate-600 hover:bg-slate-50">
Reset Password
handleDeleteUserAdmin(u.id, u.email)}
className="rounded-lg border border-rose-200 px-3 py-1 text-xs font-semibold text-rose-600 hover:bg-rose-50">
Delete
)}
))}
)}
{!adminLoading && !adminError && adminTab === 'projects' && (
Title
Owner
Passage
Last edited
Shared
{adminProjects.map((p) => (
{p.title}
{p.ownerEmail ?? orphaned }
{p.chapterSummary}
{new Date(p.lastEdited).toLocaleDateString()}
{p.shareToken ? 'π' : 'β'}
handleViewProjectAdmin(p.id)}
className="rounded-lg border border-slate-300 px-3 py-1 text-xs font-semibold text-slate-600 hover:bg-slate-50">
View
handleDeleteProjectAdmin(p.id, p.title)}
className="rounded-lg border border-rose-200 px-3 py-1 text-xs font-semibold text-rose-600 hover:bg-rose-50">
Delete
))}
)}
{adminViewProject && (
setAdminViewProject(null)}>
e.stopPropagation()}>
{adminViewProject.title}
setAdminViewProject(null)}
className="rounded-lg border border-slate-300 px-3 py-1 text-xs text-slate-600 hover:bg-slate-50">
Close
)}
{adminResetResult && (
Password reset
Relay this to {adminResetResult.email} yourself
(text, call, in person) β it won't be shown again. They're signed out everywhere until they log in with it.
{adminResetResult.temporaryPassword}
setAdminResetResult(null)}
className="mt-4 w-full rounded-md bg-sky-500 px-4 py-2 text-sm font-medium text-white shadow-sm transition hover:bg-sky-400">
Done
)}
);
}