184 lines
5.1 KiB
TypeScript
184 lines
5.1 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { TextField, SelectField, useToast } from "@nexarch/shl";
|
|
import {
|
|
ApiError,
|
|
Group,
|
|
Role,
|
|
addGroupMember,
|
|
createGroup,
|
|
fetchGroups,
|
|
removeGroupMember,
|
|
setGroupRole,
|
|
} from "../../lib/api";
|
|
|
|
export default function GroupsPage() {
|
|
const { push } = useToast();
|
|
const [groups, setGroups] = useState<Group[]>([]);
|
|
const [loadError, setLoadError] = useState<string | null>(null);
|
|
|
|
const reload = () =>
|
|
fetchGroups()
|
|
.then(setGroups)
|
|
.catch(() => setLoadError("Gruppen konnten nicht geladen werden. Sind Sie als Tenant-Admin angemeldet?"));
|
|
|
|
useEffect(() => {
|
|
reload();
|
|
}, []);
|
|
|
|
const [newGroupName, setNewGroupName] = useState("");
|
|
const [createError, setCreateError] = useState<string | null>(null);
|
|
|
|
async function onCreate(event: React.FormEvent<HTMLFormElement>) {
|
|
event.preventDefault();
|
|
setCreateError(null);
|
|
try {
|
|
await createGroup(newGroupName);
|
|
setNewGroupName("");
|
|
push("Gruppe angelegt.", "success");
|
|
await reload();
|
|
} catch (err) {
|
|
setCreateError(err instanceof ApiError ? err.message : "Gruppe konnte nicht angelegt werden.");
|
|
}
|
|
}
|
|
|
|
if (loadError) {
|
|
return (
|
|
<main style={{ maxWidth: 640, margin: "40px auto", padding: "0 16px" }}>
|
|
<p role="alert">{loadError}</p>
|
|
</main>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<main style={{ maxWidth: 640, margin: "40px auto", padding: "0 16px" }}>
|
|
<h1>Gruppen & Abteilungen</h1>
|
|
|
|
<section aria-labelledby="neue-gruppe-heading">
|
|
<h2 id="neue-gruppe-heading">Neue Gruppe anlegen</h2>
|
|
<form onSubmit={onCreate} noValidate>
|
|
<TextField
|
|
label="Gruppenname"
|
|
required
|
|
value={newGroupName}
|
|
onChange={(e) => setNewGroupName(e.target.value)}
|
|
/>
|
|
{createError && (
|
|
<p role="alert" style={{ color: "var(--shl-color-danger)" }}>
|
|
{createError}
|
|
</p>
|
|
)}
|
|
<button type="submit">Anlegen</button>
|
|
</form>
|
|
</section>
|
|
|
|
<section aria-labelledby="gruppenliste-heading">
|
|
<h2 id="gruppenliste-heading">Bestehende Gruppen</h2>
|
|
{groups.length === 0 ? (
|
|
<p>Keine Gruppen vorhanden.</p>
|
|
) : (
|
|
groups.map((g) => <GroupCard key={g.ID} group={g} onChanged={reload} onNotice={(t) => push(t, "success")} />)
|
|
)}
|
|
</section>
|
|
</main>
|
|
);
|
|
}
|
|
|
|
function GroupCard({
|
|
group,
|
|
onChanged,
|
|
onNotice,
|
|
}: {
|
|
group: Group;
|
|
onChanged: () => void;
|
|
onNotice: (text: string) => void;
|
|
}) {
|
|
const [role, setRole] = useState<Role>(group.Role || "user");
|
|
const [newMemberId, setNewMemberId] = useState("");
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
async function onSetRole() {
|
|
setError(null);
|
|
try {
|
|
await setGroupRole(group.ID, role);
|
|
onNotice("Gruppenrolle gesetzt.");
|
|
onChanged();
|
|
} catch (err) {
|
|
setError(err instanceof ApiError ? err.message : "Gruppenrolle konnte nicht gesetzt werden.");
|
|
}
|
|
}
|
|
|
|
async function onAddMember(event: React.FormEvent<HTMLFormElement>) {
|
|
event.preventDefault();
|
|
setError(null);
|
|
try {
|
|
await addGroupMember(group.ID, newMemberId);
|
|
setNewMemberId("");
|
|
onNotice("Mitglied hinzugefügt.");
|
|
onChanged();
|
|
} catch (err) {
|
|
setError(err instanceof ApiError ? err.message : "Mitglied konnte nicht hinzugefügt werden.");
|
|
}
|
|
}
|
|
|
|
async function onRemoveMember(userId: string) {
|
|
setError(null);
|
|
try {
|
|
await removeGroupMember(group.ID, userId);
|
|
onNotice("Mitglied entfernt.");
|
|
onChanged();
|
|
} catch (err) {
|
|
setError(err instanceof ApiError ? err.message : "Mitglied konnte nicht entfernt werden.");
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div style={{ border: "1px solid var(--shl-color-border)", borderRadius: 8, padding: 16, marginBottom: 16 }}>
|
|
<h3>{group.Name}</h3>
|
|
|
|
<div style={{ display: "flex", alignItems: "flex-end", gap: 8 }}>
|
|
<SelectField label="Gruppenrolle" value={role} onChange={(e) => setRole(e.target.value as Role)}>
|
|
<option value="user">Benutzer</option>
|
|
<option value="tenant_admin">Tenant-Admin</option>
|
|
</SelectField>
|
|
<button type="button" onClick={onSetRole}>
|
|
Setzen
|
|
</button>
|
|
</div>
|
|
|
|
<h4>Mitglieder</h4>
|
|
{group.members.length === 0 ? (
|
|
<p>Keine Mitglieder.</p>
|
|
) : (
|
|
<ul>
|
|
{group.members.map((userId) => (
|
|
<li key={userId}>
|
|
{userId}{" "}
|
|
<button type="button" onClick={() => onRemoveMember(userId)}>
|
|
Entfernen
|
|
</button>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
|
|
<form onSubmit={onAddMember} noValidate>
|
|
<TextField
|
|
label="Benutzer-ID hinzufügen"
|
|
required
|
|
value={newMemberId}
|
|
onChange={(e) => setNewMemberId(e.target.value)}
|
|
/>
|
|
<button type="submit">Hinzufügen</button>
|
|
</form>
|
|
|
|
{error && (
|
|
<p role="alert" style={{ color: "var(--shl-color-danger)" }}>
|
|
{error}
|
|
</p>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|