Files
NexaVPN/admin-web/src/features/users/UsersPage.tsx
nessi 6ec5133773 docs: update README with desktop requirements, helper builds, and realistic MVP usage notes
Expand README with desktop platform requirements (Windows x86, macOS ARM), helper build commands, gateway utility scripts, and updated local test flow. Add realistic MVP usage section clarifying current platform build status, gateway configuration needs, and admin debug profile behavior with client private key handling.
2026-03-16 06:30:08 +01:00

76 lines
2.8 KiB
TypeScript

import { FormEvent, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { api } from "../../api/client";
import { Page } from "../../components/Page";
import { Table } from "../../components/Table";
const columns = [
{ key: "username", label: "Username" },
{ key: "name", label: "Display name" },
{ key: "role", label: "Role" },
{ key: "status", label: "Status" }
];
export function UsersPage() {
const queryClient = useQueryClient();
const query = useQuery({
queryKey: ["users"],
queryFn: api.users
});
const [form, setForm] = useState({
username: "",
display_name: "",
email: "",
password: "",
role: "user"
});
const createMutation = useMutation({
mutationFn: api.createUser,
onSuccess: () => {
setForm({ username: "", display_name: "", email: "", password: "", role: "user" });
void queryClient.invalidateQueries({ queryKey: ["users"] });
}
});
const rows = query.data?.map((user) => ({
username: user.username,
name: user.display_name,
role: user.role,
status: user.is_active ? "active" : "disabled"
})) ?? [];
function onSubmit(event: FormEvent) {
event.preventDefault();
createMutation.mutate(form);
}
return (
<Page
title="Users"
subtitle="Create, disable, and manage platform identities."
actions={<span className="pill">Admin managed</span>}
>
<form className="inline-form" onSubmit={onSubmit}>
<input placeholder="username" value={form.username} onChange={(event) => setForm((value) => ({ ...value, username: event.target.value }))} />
<input placeholder="display name" value={form.display_name} onChange={(event) => setForm((value) => ({ ...value, display_name: event.target.value }))} />
<input placeholder="email" value={form.email} onChange={(event) => setForm((value) => ({ ...value, email: event.target.value }))} />
<input placeholder="password" type="password" value={form.password} onChange={(event) => setForm((value) => ({ ...value, password: event.target.value }))} />
<select value={form.role} onChange={(event) => setForm((value) => ({ ...value, role: event.target.value }))}>
<option value="user">user</option>
<option value="admin">admin</option>
</select>
<button className="button" type="submit" disabled={createMutation.isPending}>Create user</button>
</form>
{query.isError ? <p className="notice">Unable to load users from the API.</p> : null}
{createMutation.isError ? <p className="notice">Unable to create user.</p> : null}
<Table
columns={columns}
rows={rows}
renderCell={(row, column) => <span>{row[column.key as keyof (typeof rows)[number]]}</span>}
/>
</Page>
);
}