users: admin CRUD API and admin shell UI
ci / check (push) Canceled after 0s
ci / images (push) Canceled after 0s

Login, forced password change, admin layout with every 05 §2 page in the
sidebar (unbuilt ones name their milestone), Users table/cards + drawer
(General tab), one-time temporary passwords, reset password. The last
active admin can't be demoted, deactivated or deleted. Audit diffs only
mask secret strings. scripts/e2e has a dependency-free CDP browser check
for this task's acceptance flow.

Task: M1-T4
This commit is contained in:
iamdoubz committed 2026-10-17 15:18:08 -05:00
1 parent 13733ac262
commit 9a81e37725
25 files changed
+1774 -33

No files matched your search

+47
View File
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
// My Phone (05 §4) arrives in M4/M5; for now a signed-in landing page for non-admins.
import { useQueryClient } from '@tanstack/react-query';
import { Link, useNavigate } from 'react-router';
import { APP_NAME, type AuthUser } from '@selfphoneng/shared';
import { authApi } from '../../api/client.ts';
import { ThemeToggle } from '../../components/ThemeToggle.tsx';
import { Button } from '../../components/ui.tsx';
export function MePage({ user }: { user: AuthUser }) {
const qc = useQueryClient();
const nav = useNavigate();
return (
<main className="mx-auto flex min-h-dvh max-w-3xl flex-col gap-6 p-4 sm:p-8">
<header className="flex items-center justify-between gap-2">
<h1 className="text-xl font-semibold">{APP_NAME}</h1>
<div className="flex items-center gap-2">
<ThemeToggle />
<Button
variant="ghost"
onClick={async () => {
await authApi.logout().catch(() => undefined);
qc.clear();
nav('/login', { replace: true });
}}
>
Sign out
</Button>
</div>
</header>
<p>
Hi {user.displayName}. Voicemail, call history and the browser softphone are coming in
M4–M5.
</p>
<p className="flex gap-4 text-sm">
<Link className="text-sky-700 underline dark:text-sky-400" to="/change-password">
Change password
</Link>
{user.role === 'admin' && (
<Link className="text-sky-700 underline dark:text-sky-400" to="/admin">
Admin
</Link>
)}
</p>
</main>
);
}