Merge branch 'feat/admin-console': Administration page (Users + Server tabs)
This commit is contained in:
+15
-11
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* @file Users.tsx
|
||||
* @description The Users page (`/users`, a sidebar item), server mode only and
|
||||
* @description The Users tab of the Administration page (`/admin`, which
|
||||
* `/users` redirects to — `Administration.tsx`), server mode only and
|
||||
* only for an admin or analyst (docs/SERVER-MODE.md): every account with its
|
||||
* role, state, workspaces, sessions, the tokens/$ it used in the last 7 and
|
||||
* 30 days (`GET /api/users`) and how much disk its home folder takes
|
||||
@@ -9,9 +10,9 @@
|
||||
* an admin — add a user, and ONE Edit per row (`UserEditDialog`: profile,
|
||||
* role, active/disabled, password, owned workspaces, sign out everywhere, sent
|
||||
* as one change), and the Claude/Codex account pool every user's sessions run
|
||||
* on (`AccountPoolCard`), and (one Linux user per account) each member's own
|
||||
* tmux server beside the installed version, restartable by an admin
|
||||
* (`MemberTmuxCard`). Every change asks for the admin's OWN password again (the
|
||||
* on (`AccountPoolCard`). (Each member's own tmux server — one Linux user per
|
||||
* account — moved to the Server tab, `MemberTmuxCard` in `ServerPanel`.)
|
||||
* Every change asks for the admin's OWN password again (the
|
||||
* server refuses without it); an analyst only reads. A row shows the display
|
||||
* name and email under the username when set; right-clicking a row offers
|
||||
* Edit, Open home (in /files), and copying the username or email.
|
||||
@@ -51,7 +52,6 @@ import { ConfirmModal } from "../components/ConfirmModal";
|
||||
import { StatCard } from "../components/StatCard";
|
||||
import { ROLE_BADGE, UserEditDialog } from "../components/users/UserEditDialog";
|
||||
import { AccountPoolCard } from "../components/users/AccountPoolCard";
|
||||
import { MemberTmuxCard } from "../components/users/MemberTmuxCard";
|
||||
import { TableRowSkeleton } from "../components/Skeleton";
|
||||
import { ContextMenu, MenuItem, MenuSeparator, useContextMenu } from "../components/ContextMenu";
|
||||
import { copyText } from "../lib/clipboard";
|
||||
@@ -59,7 +59,7 @@ import { copyText } from "../lib/clipboard";
|
||||
type Role = AuthUser["role"];
|
||||
const ROLES: Role[] = ["admin", "analyst", "developer"];
|
||||
|
||||
export function Users() {
|
||||
export function Users({ embedded = false }: { embedded?: boolean } = {}) {
|
||||
const { t } = useTranslation("users");
|
||||
const auth = useAuth();
|
||||
const me = auth.state.mode === "server" ? auth.state.user : null;
|
||||
@@ -132,11 +132,16 @@ export function Users() {
|
||||
<div className="animate-fade-in space-y-6">
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-9 h-9 rounded-xl bg-accent/15 flex items-center justify-center">
|
||||
<UsersIcon className="w-4.5 h-4.5 text-accent" />
|
||||
</div>
|
||||
{!embedded && (
|
||||
<div className="w-9 h-9 rounded-xl bg-accent/15 flex items-center justify-center">
|
||||
<UsersIcon className="w-4.5 h-4.5 text-accent" />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<h1 className="text-lg font-semibold text-fg-primary">{t("title")}</h1>
|
||||
{/* Under Administration's tab bar the tab already names the page. */}
|
||||
<h1 className={embedded ? "sr-only" : "text-lg font-semibold text-fg-primary"}>
|
||||
{t("title")}
|
||||
</h1>
|
||||
<p className="text-xs text-fg-muted">{t("description")}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -346,7 +351,6 @@ export function Users() {
|
||||
})()}
|
||||
|
||||
<AccountPoolCard isAdmin={isAdmin} />
|
||||
<MemberTmuxCard isAdmin={isAdmin} />
|
||||
|
||||
<section className="card">
|
||||
<div className="flex items-center gap-2 px-5 py-4 border-b border-border">
|
||||
|
||||
Reference in New Issue
Block a user