This commit is contained in:
root
2025-11-06 11:07:57 +00:00
parent 71ab82b592
commit d88edcd6ef
7 changed files with 80 additions and 67 deletions
+33 -29
View File
@@ -119,8 +119,8 @@ export function UserDetails() {
const canEditUsers = Boolean(authUser?.isSuperuser || hasPermission("users-edit", "full"));
const canReadUsers = Boolean(authUser?.isSuperuser || hasPermission("users-edit", "read"));
const canViewSecurityPhrase = Boolean(authUser?.isSuperuser || hasPermission("users-security-phrase", "read"));
const canRenewSecurityPhrase = Boolean(authUser?.isSuperuser || hasPermission("users-security-phrase", "full"));
const canManageSecurityPhrase = Boolean(authUser?.isSuperuser || hasPermission("users-security-phrase", "full"));
const canRenewSecurityPhrase = canManageSecurityPhrase;
if (!canReadUsers) {
return null;
@@ -393,7 +393,7 @@ export function UserDetails() {
}, [canEditUsers, user, hasChanges, formValues, showToast, isSuperuserUser]);
const fetchSecurityPhrase = useCallback(async () => {
if (!canViewSecurityPhrase || !numericUserId) {
if (!canManageSecurityPhrase || !numericUserId) {
return;
}
@@ -423,14 +423,17 @@ export function UserDetails() {
} finally {
setSecurityPhraseLoading(false);
}
}, [canViewSecurityPhrase, numericUserId]);
}, [canManageSecurityPhrase, numericUserId]);
const handleReloadSecurityPhrase = useCallback(() => {
if (!canRenewSecurityPhrase) {
return;
}
if (securityPhraseLoading || renewingSecurityPhrase) {
return;
}
fetchSecurityPhrase();
}, [fetchSecurityPhrase, renewingSecurityPhrase, securityPhraseLoading]);
}, [canRenewSecurityPhrase, fetchSecurityPhrase, renewingSecurityPhrase, securityPhraseLoading]);
const handleRenewSecurityPhrase = useCallback(async () => {
if (!canRenewSecurityPhrase || !numericUserId || maintenanceActive || renewingSecurityPhrase) {
@@ -546,7 +549,7 @@ export function UserDetails() {
return;
}
if (!canViewSecurityPhrase || !numericUserId) {
if (!canManageSecurityPhrase || !numericUserId) {
setSecurityPhraseWords([]);
setSecurityPhraseDownloadedAt(null);
setSecurityPhraseError("");
@@ -555,7 +558,7 @@ export function UserDetails() {
}
fetchSecurityPhrase();
}, [hasLoaded, canViewSecurityPhrase, fetchSecurityPhrase, numericUserId]);
}, [hasLoaded, canManageSecurityPhrase, fetchSecurityPhrase, numericUserId]);
const inputDisabled = maintenanceActive || savingUser || !user || !canEditUsers;
const selectDisabled = maintenanceActive || savingUser || !user || groupsLoading || !canEditUsers || isSuperuserUser;
@@ -765,7 +768,7 @@ export function UserDetails() {
</div>
</div>
{canViewSecurityPhrase && (
{canManageSecurityPhrase && (
<div className="mt-10 border-t border-blue-gray-50 pt-8">
<div className="mb-4 flex flex-wrap items-center justify-between gap-4">
<div>
@@ -783,27 +786,26 @@ export function UserDetails() {
className="normal-case"
onClick={handleReloadSecurityPhrase}
disabled={
!canViewSecurityPhrase ||
!canRenewSecurityPhrase ||
securityPhraseLoading ||
renewingSecurityPhrase
}
>
Aktualisieren
</Button>
{canRenewSecurityPhrase && (
<Button
color="red"
className="normal-case"
onClick={handleRenewSecurityPhrase}
disabled={
maintenanceActive ||
securityPhraseLoading ||
renewingSecurityPhrase
}
>
{renewingSecurityPhrase ? "Erneuert ..." : "Schlüssel erneuern"}
</Button>
)}
<Button
color="red"
className="normal-case"
onClick={handleRenewSecurityPhrase}
disabled={
!canRenewSecurityPhrase ||
maintenanceActive ||
securityPhraseLoading ||
renewingSecurityPhrase
}
>
{renewingSecurityPhrase ? "Erneuert ..." : "Schlüssel erneuern"}
</Button>
</div>
</div>
{securityPhraseLoading ? (
@@ -820,7 +822,11 @@ export function UserDetails() {
color="red"
className="normal-case"
onClick={handleReloadSecurityPhrase}
disabled={securityPhraseLoading || renewingSecurityPhrase}
disabled={
!canRenewSecurityPhrase ||
securityPhraseLoading ||
renewingSecurityPhrase
}
>
Erneut laden
</Button>
@@ -850,11 +856,9 @@ export function UserDetails() {
Kein Sicherheitsschlüssel verfügbar.
</Typography>
)}
{canRenewSecurityPhrase && (
<Typography className="mt-3 text-sm text-blue-gray-500">
Nach dem Erneuern muss der Benutzer den neuen Sicherheitsschlüssel erneut herunterladen.
</Typography>
)}
<Typography className="mt-3 text-sm text-blue-gray-500">
Nach dem Erneuern muss der Benutzer den neuen Sicherheitsschlüssel erneut herunterladen.
</Typography>
</div>
)}
@@ -126,16 +126,24 @@ export function UserGroupDetail() {
if (!item || typeof item !== "object") {
return null;
}
const key = item.key || "";
const defaultLevel =
typeof item.defaultLevel === "string" && item.defaultLevel ? item.defaultLevel : "none";
defaults[item.key] = defaultLevel;
const assigned = rawValues[item.key];
initialSelection[item.key] = typeof assigned === "string" && assigned ? assigned : defaultLevel;
defaults[key] = defaultLevel;
const assigned = rawValues[key];
let normalizedAssigned =
typeof assigned === "string" && assigned ? assigned : defaultLevel;
const availableLevels =
Array.isArray(item.availableLevels) && item.availableLevels.length
? item.availableLevels
: LEVEL_OPTIONS.map((option) => option.value);
if (!availableLevels.includes(normalizedAssigned)) {
normalizedAssigned = defaultLevel;
}
initialSelection[key] = normalizedAssigned;
const dependencies = Array.isArray(item.dependencies) ? item.dependencies : [];
@@ -359,10 +367,6 @@ export function UserGroupDetail() {
return;
}
if (permissionsChanged && isSuperuserGroup) {
return;
}
if (detailsChanged && !canEditGroupDetails) {
setSaveError("Dir fehlt die Berechtigung, diese Gruppe zu bearbeiten.");
return;
@@ -397,10 +401,14 @@ export function UserGroupDetail() {
}
}
if (permissionsChanged && !isSuperuserGroup) {
if (permissionsChanged) {
await axios.put(`/api/groups/${group.id}/permissions`, {
values: permissionSelection
});
setPermissionSelection((prev) => ({
...prev,
...permissionSelection
}));
setInitialPermissionSelection({ ...permissionSelection });
}
@@ -577,6 +585,7 @@ export function UserGroupDetail() {
? row.availableLevels
: LEVEL_OPTIONS.map((option) => option.value)
);
const levelOptions = LEVEL_OPTIONS;
return (
<div key={`${ownerKey}-${row.key}`} className={rowClasses}>
@@ -584,7 +593,7 @@ export function UserGroupDetail() {
{row.label}
</div>
<List className="flex-col gap-1.5 md:ml-auto md:flex-row md:flex-nowrap md:w-1/2 md:items-center md:justify-end md:gap-3">
{LEVEL_OPTIONS.map((option) => {
{levelOptions.map((option) => {
const optionId = `${rowName}-${option.value}`;
const checked = currentValue === option.value;
const disabled =