Files
internal-mail-manager/apps/web/src/App.tsx
T
2026-03-30 13:56:47 +00:00

197 lines
5.0 KiB
TypeScript

import { useEffect, useState } from "react";
import type { AdminSession, CreateUserResponse, MailUser } from "@internal-mail/shared";
import { api } from "./api/client";
import DashboardPage from "./pages/DashboardPage";
import LoginPage from "./pages/LoginPage";
import ResetPasswordPage from "./pages/ResetPasswordPage";
const anonymousSession: AdminSession = {
authenticated: false,
adminEmail: null
};
const getPath = (): string => window.location.pathname;
export default function App() {
const [path, setPath] = useState(getPath());
const [session, setSession] = useState<AdminSession | null>(null);
const [users, setUsers] = useState<MailUser[]>([]);
const [busy, setBusy] = useState(false);
const [loginError, setLoginError] = useState<string | null>(null);
const [feedback, setFeedback] = useState<string | null>(null);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
useEffect(() => {
const handlePopstate = () => setPath(getPath());
window.addEventListener("popstate", handlePopstate);
return () => window.removeEventListener("popstate", handlePopstate);
}, []);
const resetToken = new URLSearchParams(window.location.search).get("token") ?? "";
const loadUsers = async () => {
const response = await api.listUsers();
setUsers(response.items);
};
useEffect(() => {
if (path.startsWith("/reset-password")) {
return;
}
api
.getSession()
.then((response) => {
setSession(response.session);
if (!response.session.authenticated) {
return;
}
loadUsers().catch((error) => {
setErrorMessage(error instanceof Error ? error.message : "Could not load users.");
});
})
.catch(() => {
setSession(anonymousSession);
});
}, [path]);
const handleLogin = async (payload: { email: string; password: string }) => {
setBusy(true);
setLoginError(null);
try {
const response = await api.login(payload);
setSession(response.session);
setErrorMessage(null);
await loadUsers().catch((error) => {
setErrorMessage(error instanceof Error ? error.message : "Signed in, but could not load users.");
});
} catch (error) {
setLoginError(error instanceof Error ? error.message : "Could not sign in.");
} finally {
setBusy(false);
}
};
const handleLogout = async () => {
setBusy(true);
try {
const response = await api.logout();
setSession(response.session);
setUsers([]);
setFeedback(null);
setErrorMessage(null);
} finally {
setBusy(false);
}
};
const handleCreateUser = async (email: string): Promise<CreateUserResponse> => {
setBusy(true);
setFeedback(null);
setErrorMessage(null);
try {
const result = await api.createUser({ email });
await loadUsers();
return result;
} finally {
setBusy(false);
}
};
const handleUpdatePassword = async (email: string, password: string) => {
setBusy(true);
setFeedback(null);
setErrorMessage(null);
try {
await api.updatePassword(email, { password });
await loadUsers();
} finally {
setBusy(false);
}
};
const handleDeleteUser = async (email: string) => {
setBusy(true);
setFeedback(null);
setErrorMessage(null);
try {
await api.deleteUser(email);
await loadUsers();
} finally {
setBusy(false);
}
};
const handleTriggerReset = async (email: string) => {
setBusy(true);
setFeedback(null);
setErrorMessage(null);
try {
const result = await api.triggerPasswordReset(email);
return result.expiresAt;
} finally {
setBusy(false);
}
};
const handleRefresh = async () => {
setBusy(true);
try {
await loadUsers();
setErrorMessage(null);
} catch (error) {
setErrorMessage(error instanceof Error ? error.message : "Could not refresh users.");
} finally {
setBusy(false);
}
};
if (path.startsWith("/reset-password")) {
return <ResetPasswordPage token={resetToken} />;
}
if (session === null) {
return (
<main className="auth-shell">
<section className="auth-card">
<div className="eyebrow">Mail Admin</div>
<h1>Loading</h1>
<p className="auth-copy">Checking your admin session.</p>
</section>
</main>
);
}
if (!session || !session.authenticated || !session.adminEmail) {
return (
<LoginPage
busy={busy}
error={loginError}
onSubmit={handleLogin}
/>
);
}
return (
<DashboardPage
adminEmail={session.adminEmail}
users={users}
busy={busy}
feedback={feedback}
errorMessage={errorMessage}
onCreateUser={handleCreateUser}
onUpdatePassword={handleUpdatePassword}
onDeleteUser={handleDeleteUser}
onTriggerReset={handleTriggerReset}
onRefresh={handleRefresh}
onLogout={handleLogout}
/>
);
}