This commit is contained in:
Jonas Bengtsson
2026-09-16 06:08:37 +02:00
parent cce6a4960e
commit 8d2c54beae
54 changed files with 8480 additions and 41 deletions
+84
View File
@@ -0,0 +1,84 @@
import { FormEvent, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { api, type Group } from "../api";
export function GroupsPage() {
const queryClient = useQueryClient();
const [name, setName] = useState("");
const [error, setError] = useState<string | null>(null);
const groups = useQuery({
queryKey: ["groups"],
queryFn: () => api.get<Group[]>("/mqtt/groups"),
});
const create = useMutation({
mutationFn: (groupname: string) => api.post("/mqtt/groups", { groupname }),
onSuccess: () => {
setName("");
queryClient.invalidateQueries({ queryKey: ["groups"] });
},
onError: (e: Error) => setError(e.message),
});
const remove = useMutation({
mutationFn: (groupname: string) => api.delete(`/mqtt/groups/${groupname}`),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["groups"] }),
onError: (e: Error) => setError(e.message),
});
const submit = (e: FormEvent) => {
e.preventDefault();
if (name) create.mutate(name);
};
return (
<div>
<h2>Groups</h2>
<p>Assign roles to collections of clients (dynamic security groups).</p>
{error && <div className="banner error">{error}</div>}
<form className="stack" onSubmit={submit}>
<label htmlFor="group-name">New group name</label>
<input
id="group-name"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="e.g. sensors"
/>
<p />
<button className="primary" disabled={!name || create.isPending}>
Create group
</button>
</form>
<table style={{ marginTop: "1.5rem" }}>
<thead>
<tr>
<th>Group</th>
<th>Roles</th>
<th></th>
</tr>
</thead>
<tbody>
{(groups.data ?? []).map((g) => (
<tr key={g.groupname}>
<td className="mono">{g.groupname}</td>
<td>{(g.roles ?? []).map((r) => r.rolename).join(", ") || "—"}</td>
<td>
<button
className="danger"
onClick={() => {
if (confirm(`Delete group ${g.groupname}?`)) remove.mutate(g.groupname);
}}
>
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}