+
+
+
+
Users
+ {meta ? {meta.total} total : null}
+
+
Invite and manage workspace users
+
+
+
+
+
+
+
+
+
+
+
+ ← Back to Settings
+
+
+
+ {error ? (
+
+
+
+ {error}
+
+
+
+ ) : null}
+
+ {isLoading ? (
+
+
+ Loading users...
+
+
+ ) : users.length === 0 ? (
+
+
+ No Users Yet
+ Invite the first user to get started.
+
+
+ ) : (
+
+
+ User Directory
+ Name, email, role, and account status.
+
+
+ {users.map((user) => {
+ const primaryRole = getPrimaryRole(user);
+ const isActive = user.deactivatedAt === null;
+
+ return (
+
+
+
{user.name || "Unnamed User"}
+
{user.email}
+
+
+
+
+ {primaryRole ? toRoleLabel(primaryRole) : "No role"}
+
+
+ {isActive ? "Active" : "Inactive"}
+
+ {isActive ? (
+
+ ) : null}
+
+
+ );
+ })}
+
+
+ )}
+
+
{
+ if (!open && !isDeactivating) {
+ setDeactivateTarget(null);
+ }
+ }}
+ >
+
+
+ Deactivate User
+
+ Deactivate {deactivateTarget?.email}? They will no longer be able to access the
+ system.
+
+
+
+ Cancel
+ {
+ void confirmDeactivate();
+ }}
+ >
+ {isDeactivating ? "Deactivating..." : "Deactivate"}
+
+
+
+
+
+ );
+}
diff --git a/apps/web/src/lib/api/admin.ts b/apps/web/src/lib/api/admin.ts
new file mode 100644
index 0000000..cb032ac
--- /dev/null
+++ b/apps/web/src/lib/api/admin.ts
@@ -0,0 +1,98 @@
+/**
+ * Admin API Client
+ * Handles admin user management requests
+ */
+
+import type { WorkspaceMemberRole } from "@mosaic/shared";
+import { apiGet, apiPatch, apiPost, apiDelete } from "./client";
+
+export interface AdminWorkspaceMembership {
+ workspaceId: string;
+ workspaceName: string;
+ role: WorkspaceMemberRole;
+ joinedAt: string;
+}
+
+export interface AdminUser {
+ id: string;
+ name: string;
+ email: string;
+ emailVerified: boolean;
+ image: string | null;
+ createdAt: string;
+ deactivatedAt: string | null;
+ isLocalAuth: boolean;
+ invitedAt: string | null;
+ invitedBy: string | null;
+ workspaceMemberships: AdminWorkspaceMembership[];
+}
+
+export interface AdminUsersResponse {
+ data: AdminUser[];
+ meta: {
+ total: number;
+ page: number;
+ limit: number;
+ totalPages: number;
+ };
+}
+
+export interface InviteUserDto {
+ email: string;
+ name?: string;
+ workspaceId?: string;
+ role?: WorkspaceMemberRole;
+}
+
+export interface InvitationResponse {
+ userId: string;
+ invitationToken: string;
+ email: string;
+ invitedAt: string;
+}
+
+export interface UpdateUserDto {
+ name?: string;
+ deactivatedAt?: string | null;
+ emailVerified?: boolean;
+ preferences?: Record