/* Users — admin-only account management. Invite by email (the invitee sets their own * password through a one-shot link, emailed when SMTP is configured and always shown * here), toggle admin, disable, delete, and mint reset links. Deleting a user turns * their projects/skills/tools into shared resources rather than removing them. */ "use client"; import { useState } from "react"; import { useDashboard } from "@/components/store"; import { Button } from "@/components/ui"; import { fmtFull } from "@/lib/format"; export function UsersSection() { const s = useDashboard(); const [email, setEmail] = useState(""); const [isAdmin, setIsAdmin] = useState(false); const [lastLink, setLastLink] = useState<{ email: string; url: string } | null>(null); const invite = async (e: React.FormEvent) => { e.preventDefault(); if (!email.trim()) return; const created = await s.createUser(email, isAdmin); if (created) { setLastLink({ email: created.user.email, url: created.invite_url }); setEmail(""); setIsAdmin(false); } }; const resetLink = async (id: number, userEmail: string) => { const link = await s.mintResetLink(id); if (link) setLastLink({ email: userEmail, url: link.reset_url }); }; const isSelf = (id: number) => s.me?.kind === "user" && s.me.user_id === id; return ( <>
Users
Accounts for this Handler. Each user's projects, skills, and tools are theirs alone; shared (unowned) resources are visible to everyone and managed by admins.
setEmail(e.target.value)} style={{ maxWidth: 320 }} required />
{lastLink && (
One-shot set-password link for {lastLink.email} (share it over a channel you trust; it expires): {lastLink.url}
)} {s.users.length === 0 ? (
No users loaded (admin access required).
) : (
{s.users.map((u) => ( ))}
Email Role Status Created Actions
{u.email} {isSelf(u.id) ? (you) : null} {u.is_admin ? "admin" : "user"} {u.disabled ? "disabled" : u.has_password ? "active" : "invited — awaiting password"} {fmtFull(u.created_at)}
)}
); }