From c2917459a46b965023ab34e48a5d3471947218b8 Mon Sep 17 00:00:00 2001 From: Albrecht Degering Date: Tue, 21 Jul 2026 12:23:31 +0200 Subject: [PATCH] refactor(webui): centralize access table actions --- webui/src/features/admin/ApiKeysPanel.tsx | 16 ++++++++-------- .../admin/ExternalFunctionRoleMappingsPanel.tsx | 12 +++++------- webui/src/features/admin/GroupsPanel.tsx | 14 +++++++------- webui/src/features/admin/RolesPanel.tsx | 14 +++++++------- webui/src/features/admin/SystemRolesPanel.tsx | 12 ++++++------ webui/src/features/admin/SystemUsersPanel.tsx | 12 ++++++------ webui/src/features/admin/TenantsPanel.tsx | 12 ++++++------ webui/src/features/admin/UsersPanel.tsx | 17 +++++++++-------- 8 files changed, 54 insertions(+), 55 deletions(-) diff --git a/webui/src/features/admin/ApiKeysPanel.tsx b/webui/src/features/admin/ApiKeysPanel.tsx index af17111..99c0a1f 100644 --- a/webui/src/features/admin/ApiKeysPanel.tsx +++ b/webui/src/features/admin/ApiKeysPanel.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useRef, useState } from "react"; -import { Pencil, Plus, Search, Trash2 } from "lucide-react"; +import { Plus, Search, Trash2 } from "lucide-react"; import type { ApiSettings, AuthInfo } from "@govoplan/core-webui"; import { createApiKey, fetchApiKeysDelta, fetchPermissionCatalog, fetchUsersDelta, revokeApiKey, type ApiKeyAdminItem, type PermissionItem, type UserAdminItem } from "../../api/admin"; import { Button } from "@govoplan/core-webui"; @@ -8,8 +8,9 @@ import { Dialog } from "@govoplan/core-webui"; import { FormField } from "@govoplan/core-webui"; import { DateTimeField } from "@govoplan/core-webui"; import { StatusBadge } from "@govoplan/core-webui"; +import { ToggleSwitch } from "@govoplan/core-webui"; import { ConfirmDialog } from "@govoplan/core-webui"; -import { AdminIconButton, AdminPageLayout, AdminSelectionList, adminErrorMessage, formatAdminDateTime as formatDateTime, useDeltaWatermarks } from "@govoplan/core-webui"; +import { AdminIconButton, AdminPageLayout, AdminSelectionList, TableActionGroup, adminErrorMessage, formatAdminDateTime as formatDateTime, useDeltaWatermarks } from "@govoplan/core-webui"; import { scopeGrants, i18nMessage, useUnsavedDraftGuard } from "@govoplan/core-webui"; import { loadDeltaRows } from "./utils/deltaRows"; @@ -99,11 +100,10 @@ export default function ApiKeysPanel({ settings, auth, canCreate, canRevoke }: { { id: "status", header: "i18n:govoplan-access.status.bae7d5be", width: 120, resizable: false, sortable: true, filterable: true, value: (row) => row.revoked_at ? "revoked" : "active", render: (row) => }, { id: "last_used", header: "i18n:govoplan-access.last_used.f1109d3d", width: 180, minWidth: 150, resizable: true, sortable: true, value: (row) => row.last_used_at || "", render: (row) => formatDateTime(row.last_used_at) }, { id: "expires", header: "i18n:govoplan-access.expires.a99be3da", width: 180, minWidth: 150, resizable: true, sortable: true, value: (row) => row.expires_at || "", render: (row) => row.expires_at ? formatDateTime(row.expires_at) : "i18n:govoplan-access.no_expiry.39d436aa" }, - { id: "actions", header: "i18n:govoplan-access.actions.c3cd636a", width: 150, sticky: "end", resizable: false, align: "right", render: (row) =>
- } onClick={() => setViewing(row)} /> - } disabled /> - } variant="danger" onClick={() => setRevoking(row)} disabled={!canRevoke || Boolean(row.revoked_at)} /> -
}], + { id: "actions", header: "i18n:govoplan-access.actions.c3cd636a", width: 108, sticky: "end", resizable: false, align: "right", render: (row) => , onClick: () => setViewing(row) }, + { id: "revoke", label: i18nMessage("i18n:govoplan-access.revoke_value.34640d6a", { value0: row.name }), icon: , variant: "danger", applicable: !row.revoked_at, disabled: !canRevoke, onClick: () => setRevoking(row) } + ]} /> }], [canRevoke]); function openCreate() { @@ -151,7 +151,7 @@ export default function ApiKeysPanel({ settings, auth, canCreate, canRevoke }: { return ( <> - } variant="primary" onClick={openCreate} disabled={!canCreate || !users.length} />}> + } variant="primary" onClick={openCreate} disabled={!canCreate || !users.length} />}>
row.id} emptyText="i18n:govoplan-access.no_api_keys_found.1f377128" />
diff --git a/webui/src/features/admin/ExternalFunctionRoleMappingsPanel.tsx b/webui/src/features/admin/ExternalFunctionRoleMappingsPanel.tsx index fa20b6f..45e2667 100644 --- a/webui/src/features/admin/ExternalFunctionRoleMappingsPanel.tsx +++ b/webui/src/features/admin/ExternalFunctionRoleMappingsPanel.tsx @@ -15,7 +15,7 @@ import { DataGrid, type DataGridColumn } from "@govoplan/core-webui"; import { Dialog } from "@govoplan/core-webui"; import { FormField } from "@govoplan/core-webui"; import { ConfirmDialog } from "@govoplan/core-webui"; -import { AdminIconButton, AdminPageLayout, adminErrorMessage, formatAdminDateTime as formatDateTime } from "@govoplan/core-webui"; +import { AdminIconButton, AdminPageLayout, TableActionGroup, adminErrorMessage, formatAdminDateTime as formatDateTime } from "@govoplan/core-webui"; import { i18nMessage, useDeltaWatermarks, useUnsavedDraftGuard } from "@govoplan/core-webui"; import { loadDeltaRows } from "./utils/deltaRows"; @@ -257,12 +257,10 @@ export default function ExternalFunctionRoleMappingsPanel({ sticky: "end", resizable: false, align: "right", - render: (row) => ( -
- } onClick={() => openEdit(row)} disabled={!canWrite} /> - } variant="danger" onClick={() => setDeleting(row)} disabled={!canWrite} /> -
- ) + render: (row) => , disabled: !canWrite, onClick: () => openEdit(row) }, + { id: "delete", label: i18nMessage("i18n:govoplan-access.delete_value.4d18989e", { value0: row.function_id }), icon: , variant: "danger", disabled: !canWrite, onClick: () => setDeleting(row) } + ]} /> } ], [auth, canWrite, functionPicker, roleById, settings] diff --git a/webui/src/features/admin/GroupsPanel.tsx b/webui/src/features/admin/GroupsPanel.tsx index eb6c057..f86e71c 100644 --- a/webui/src/features/admin/GroupsPanel.tsx +++ b/webui/src/features/admin/GroupsPanel.tsx @@ -8,7 +8,7 @@ import { Dialog } from "@govoplan/core-webui"; import { FormField } from "@govoplan/core-webui"; import { StatusBadge } from "@govoplan/core-webui"; import { ConfirmDialog } from "@govoplan/core-webui"; -import { AdminIconButton, AdminPageLayout, AdminSelectionList, adminErrorMessage, formatAdminDateTime as formatDateTime, joinLabels, i18nMessage, useDeltaWatermarks, useUnsavedDraftGuard } from "@govoplan/core-webui"; +import { AdminIconButton, AdminPageLayout, AdminSelectionList, TableActionGroup, adminErrorMessage, formatAdminDateTime as formatDateTime, joinLabels, i18nMessage, useDeltaWatermarks, useUnsavedDraftGuard } from "@govoplan/core-webui"; import { loadDeltaRows } from "./utils/deltaRows"; const emptyDraft = { slug: "", name: "", description: "", isActive: true, memberIds: [] as string[], roleIds: [] as string[] }; @@ -127,15 +127,15 @@ export default function GroupsPanel({ settings, auth, canDefine, canManageMember } const columns = useMemo[]>(() => [ - { id: "group", header: "i18n:govoplan-access.group.171a0606", width: "minmax(220px, 1fr)", minWidth: 190, resizable: true, sticky: "start", sortable: true, filterable: true, value: (row) => `${row.name} ${row.slug}`, render: (row) =>
{row.name}
{row.slug} {row.system_template_id && i18n:govoplan-access.system.bc0792d8{row.system_required ? "i18n:govoplan-access.required.7c65879a" : ""}}
}, + { id: "group", header: "i18n:govoplan-access.group.171a0606", width: "minmax(220px, 1fr)", minWidth: 190, resizable: true, sticky: "start", sortable: true, filterable: true, value: (row) => `${row.name} ${row.slug}`, render: (row) =>
{row.name}
{row.slug} {row.system_template_id && }
}, { id: "members", header: "i18n:govoplan-access.members.1cb449c1", width: 110, resizable: false, sortable: true, filterable: true, filterType: "integer", value: (row) => row.member_count }, { id: "roles", header: "i18n:govoplan-access.inherited_roles.8def9f05", width: 260, minWidth: 180, maxWidth: 520, resizable: true, fill: true, sortable: true, filterable: true, value: (row) => joinLabels(row.roles) }, { id: "status", header: "i18n:govoplan-access.status.bae7d5be", width: 120, resizable: false, sortable: true, filterable: true, value: (row) => row.is_active ? "active" : "inactive", render: (row) => }, - { id: "actions", header: "i18n:govoplan-access.actions.c3cd636a", width: 150, sticky: "end", resizable: false, align: "right", render: (row) =>
- } onClick={() => setViewing(row)} /> - } onClick={() => openEdit(row)} disabled={!(canDefine || canManageMembers || canAssignRoles)} /> - } variant="danger" onClick={() => setDeactivating(row)} disabled={!canDefine || !row.is_active || Boolean(row.system_required)} /> -
}], + { id: "actions", header: "i18n:govoplan-access.actions.c3cd636a", width: 150, sticky: "end", resizable: false, align: "right", render: (row) => , onClick: () => setViewing(row) }, + { id: "edit", label: i18nMessage("i18n:govoplan-access.edit_value.fad75899", { value0: row.name }), icon: , disabled: !(canDefine || canManageMembers || canAssignRoles), onClick: () => openEdit(row) }, + { id: "deactivate", label: i18nMessage("i18n:govoplan-access.deactivate_value.a276a667", { value0: row.name }), icon: , variant: "danger", applicable: row.is_active, disabled: !canDefine || Boolean(row.system_required), onClick: () => setDeactivating(row) } + ]} /> }], [canAssignRoles, canDefine, canManageMembers]); return ( diff --git a/webui/src/features/admin/RolesPanel.tsx b/webui/src/features/admin/RolesPanel.tsx index 4cc97aa..e25fd98 100644 --- a/webui/src/features/admin/RolesPanel.tsx +++ b/webui/src/features/admin/RolesPanel.tsx @@ -8,7 +8,7 @@ import { Dialog } from "@govoplan/core-webui"; import { FormField } from "@govoplan/core-webui"; import { StatusBadge } from "@govoplan/core-webui"; import { ConfirmDialog } from "@govoplan/core-webui"; -import { AdminIconButton, AdminPageLayout, adminErrorMessage } from "@govoplan/core-webui"; +import { AdminIconButton, AdminPageLayout, TableActionGroup, adminErrorMessage } from "@govoplan/core-webui"; import { hasTenantWildcard, i18nMessage, useDeltaWatermarks, useUnsavedDraftGuard } from "@govoplan/core-webui"; import { loadDeltaRows } from "./utils/deltaRows"; @@ -118,15 +118,15 @@ export default function RolesPanel({ settings, auth, canDefine, onAuthRefresh }: } const columns = useMemo[]>(() => [ - { id: "role", header: "i18n:govoplan-access.role.c3f104d1", width: "minmax(220px, 1fr)", minWidth: 190, resizable: true, sticky: "start", sortable: true, filterable: true, value: (row) => `${row.name} ${row.slug}`, render: (row) =>
{row.name}
{row.slug} {row.system_template_id && i18n:govoplan-access.system.bc0792d8{row.system_required ? "i18n:govoplan-access.required.7c65879a" : ""}}
}, + { id: "role", header: "i18n:govoplan-access.role.c3f104d1", width: "minmax(220px, 1fr)", minWidth: 190, resizable: true, sticky: "start", sortable: true, filterable: true, value: (row) => `${row.name} ${row.slug}`, render: (row) =>
{row.name}
{row.slug} {row.system_template_id && }
}, { id: "permissions", header: "i18n:govoplan-access.permissions.d06d5557", width: 170, resizable: false, sortable: true, filterable: true, filterType: "integer", value: (row) => row.effective_permission_count, render: (row) => hasTenantWildcard(row.permissions) ? `${row.effective_permission_count} (tenant:*)` : String(row.effective_permission_count) }, { id: "assignments", header: "i18n:govoplan-access.assignments.057d58c7", width: 220, minWidth: 170, maxWidth: 420, resizable: true, fill: true, sortable: true, value: (row) => row.user_assignments + row.group_assignments, render: (row) => `${row.user_assignments} users / ${row.group_assignments} groups` }, { id: "type", header: "i18n:govoplan-access.type.3deb7456", width: 140, resizable: false, sortable: true, filterable: true, value: (row) => row.is_builtin ? "built-in" : row.system_template_id ? "system-managed" : "custom", render: (row) => }, - { id: "actions", header: "i18n:govoplan-access.actions.c3cd636a", width: 150, sticky: "end", resizable: false, align: "right", render: (row) =>
- } onClick={() => setViewing(row)} /> - } onClick={() => openEdit(row)} disabled={!canDefine || row.is_builtin || Boolean(row.system_template_id)} /> - } variant="danger" onClick={() => setDeleting(row)} disabled={!canDefine || row.is_builtin || Boolean(row.system_template_id) || row.user_assignments + row.group_assignments > 0} /> -
}], + { id: "actions", header: "i18n:govoplan-access.actions.c3cd636a", width: 150, sticky: "end", resizable: false, align: "right", render: (row) => , onClick: () => setViewing(row) }, + { id: "edit", label: i18nMessage("i18n:govoplan-access.edit_value.fad75899", { value0: row.name }), icon: , applicable: !row.is_builtin && !row.system_template_id, disabled: !canDefine, onClick: () => openEdit(row) }, + { id: "delete", label: i18nMessage("i18n:govoplan-access.delete_value.4d18989e", { value0: row.name }), icon: , variant: "danger", applicable: !row.is_builtin && !row.system_template_id, disabled: !canDefine || row.user_assignments + row.group_assignments > 0, onClick: () => setDeleting(row) } + ]} /> }], [canDefine, permissions]); return ( diff --git a/webui/src/features/admin/SystemRolesPanel.tsx b/webui/src/features/admin/SystemRolesPanel.tsx index 687a4e4..87093a8 100644 --- a/webui/src/features/admin/SystemRolesPanel.tsx +++ b/webui/src/features/admin/SystemRolesPanel.tsx @@ -16,7 +16,7 @@ import { DataGrid, type DataGridColumn } from "@govoplan/core-webui"; import { Dialog } from "@govoplan/core-webui"; import { FormField } from "@govoplan/core-webui"; import { StatusBadge } from "@govoplan/core-webui"; -import { AdminIconButton, AdminPageLayout, AdminSelectionList, adminErrorMessage, joinLabels, i18nMessage, useDeltaWatermarks, useUnsavedDraftGuard } from "@govoplan/core-webui"; +import { AdminIconButton, AdminPageLayout, AdminSelectionList, TableActionGroup, adminErrorMessage, joinLabels, i18nMessage, useDeltaWatermarks, useUnsavedDraftGuard } from "@govoplan/core-webui"; import { loadDeltaRows } from "./utils/deltaRows"; const emptyDraft = { @@ -223,11 +223,11 @@ export default function SystemRolesPanel({ align: "right", render: (row) => { const protectedOwner = row.slug === "system_owner"; - return
- } onClick={() => setViewing(row)} /> - } onClick={() => openEdit(row)} disabled={!canWrite || protectedOwner} /> - } variant="danger" onClick={() => setDeleting(row)} disabled={!canWrite || protectedOwner || row.user_assignments > 0} /> -
; + return , onClick: () => setViewing(row) }, + { id: "edit", label: i18nMessage("i18n:govoplan-access.edit_value.fad75899", { value0: row.name }), icon: , applicable: !protectedOwner, disabled: !canWrite, onClick: () => openEdit(row) }, + { id: "delete", label: i18nMessage("i18n:govoplan-access.delete_value.4d18989e", { value0: row.name }), icon: , variant: "danger", applicable: !protectedOwner, disabled: !canWrite || row.user_assignments > 0, onClick: () => setDeleting(row) } + ]} />; } }], [canWrite]); diff --git a/webui/src/features/admin/SystemUsersPanel.tsx b/webui/src/features/admin/SystemUsersPanel.tsx index f9a642a..aaf2b21 100644 --- a/webui/src/features/admin/SystemUsersPanel.tsx +++ b/webui/src/features/admin/SystemUsersPanel.tsx @@ -20,7 +20,7 @@ import { type SystemMembershipDraft, type TenantAdminItem } from "../../api/admin"; -import { AdminIconButton, AdminPageLayout, AdminSelectionList, adminErrorMessage, formatAdminDateTime as formatDateTime, joinLabels, i18nMessage, mergeDeltaRows, useDeltaWatermarks, useUnsavedDraftGuard } from "@govoplan/core-webui"; +import { AdminIconButton, AdminPageLayout, AdminSelectionList, TableActionGroup, adminErrorMessage, formatAdminDateTime as formatDateTime, joinLabels, i18nMessage, mergeDeltaRows, useDeltaWatermarks, useUnsavedDraftGuard } from "@govoplan/core-webui"; const emptyDraft = { email: "", @@ -215,11 +215,11 @@ export default function SystemUsersPanel({ { id: "roles", header: "i18n:govoplan-access.system_roles.a9461aa6", width: 220, minWidth: 170, maxWidth: 420, resizable: true, sortable: true, filterable: true, value: (row) => joinLabels(row.roles) }, { id: "status", header: "i18n:govoplan-access.status.bae7d5be", width: 120, resizable: false, sortable: true, filterable: true, value: (row) => row.is_active ? "active" : "inactive", render: (row) => }, { id: "last_login", header: "i18n:govoplan-access.last_login.43dab84f", width: 180, minWidth: 150, resizable: true, sortable: true, value: (row) => row.last_login_at || "", render: (row) => formatDateTime(row.last_login_at) }, - { id: "actions", header: "i18n:govoplan-access.actions.c3cd636a", width: 150, sticky: "end", resizable: false, align: "right", render: (row) =>
- } onClick={() => setViewing(row)} /> - } onClick={() => openEdit(row)} disabled={!(canUpdate || canSuspend || canAssignRoles || canManageMemberships)} /> - } variant="danger" onClick={() => setDeactivating(row)} disabled={!canSuspend || !row.is_active || row.memberships.some((membership) => membership.is_last_active_owner)} /> -
}], + { id: "actions", header: "i18n:govoplan-access.actions.c3cd636a", width: 150, sticky: "end", resizable: false, align: "right", render: (row) => , onClick: () => setViewing(row) }, + { id: "edit", label: i18nMessage("i18n:govoplan-access.edit_value.fad75899", { value0: row.email }), icon: , disabled: !(canUpdate || canSuspend || canAssignRoles || canManageMemberships), onClick: () => openEdit(row) }, + { id: "deactivate", label: i18nMessage("i18n:govoplan-access.deactivate_value.a276a667", { value0: row.email }), icon: , variant: "danger", applicable: row.is_active, disabled: !canSuspend || row.memberships.some((membership) => membership.is_last_active_owner), onClick: () => setDeactivating(row) } + ]} /> }], [canAssignRoles, canManageMemberships, canSuspend, canUpdate]); return ( diff --git a/webui/src/features/admin/TenantsPanel.tsx b/webui/src/features/admin/TenantsPanel.tsx index 15ccc3e..55f068c 100644 --- a/webui/src/features/admin/TenantsPanel.tsx +++ b/webui/src/features/admin/TenantsPanel.tsx @@ -8,7 +8,7 @@ import { Dialog } from "@govoplan/core-webui"; import { FormField } from "@govoplan/core-webui"; import { StatusBadge } from "@govoplan/core-webui"; import { ConfirmDialog } from "@govoplan/core-webui"; -import { AdminIconButton, AdminPageLayout, adminErrorMessage, formatAdminDateTime as formatDateTime, i18nMessage, useDeltaWatermarks, useUnsavedDraftGuard } from "@govoplan/core-webui"; +import { AdminIconButton, AdminPageLayout, TableActionGroup, adminErrorMessage, formatAdminDateTime as formatDateTime, i18nMessage, useDeltaWatermarks, useUnsavedDraftGuard } from "@govoplan/core-webui"; import { loadDeltaRows } from "./utils/deltaRows"; type OverrideValue = "inherit" | "allow" | "deny"; @@ -225,11 +225,11 @@ export default function TenantsPanel({ { id: "files", header: "i18n:govoplan-access.files.6ce6c512", width: 90, resizable: false, sortable: true, filterable: true, filterType: "integer", value: (row) => row.counts.files ?? 0 }, { id: "locale", header: "i18n:govoplan-access.locale.8970f0e6", width: 120, minWidth: 90, maxWidth: 220, resizable: true, fill: true, sortable: true, filterable: true, value: (row) => row.default_locale }, { id: "status", header: "i18n:govoplan-access.status.bae7d5be", width: 120, resizable: false, sortable: true, filterable: true, value: (row) => row.is_active ? "active" : "inactive", render: (row) => }, - { id: "actions", header: "i18n:govoplan-access.actions.c3cd636a", width: 150, sticky: "end", resizable: false, align: "right", render: (row) =>
- } onClick={() => setViewing(row)} /> - } onClick={() => openEdit(row)} disabled={!canUpdate} /> - } variant="danger" onClick={() => setConfirmSuspend(row)} disabled={!canSuspend || !row.is_active || row.id === activeTenantId} /> -
}], + { id: "actions", header: "i18n:govoplan-access.actions.c3cd636a", width: 150, sticky: "end", resizable: false, align: "right", render: (row) => , onClick: () => setViewing(row) }, + { id: "edit", label: i18nMessage("i18n:govoplan-access.edit_value.fad75899", { value0: row.name }), icon: , disabled: !canUpdate, onClick: () => openEdit(row) }, + { id: "suspend", label: i18nMessage("i18n:govoplan-access.suspend_value.03a74b32", { value0: row.name }), icon: , variant: "danger", applicable: row.is_active, disabled: !canSuspend || row.id === activeTenantId, onClick: () => setConfirmSuspend(row) } + ]} /> }], [activeTenantId, canSuspend, canUpdate]); return ( diff --git a/webui/src/features/admin/UsersPanel.tsx b/webui/src/features/admin/UsersPanel.tsx index 769f695..da3efa6 100644 --- a/webui/src/features/admin/UsersPanel.tsx +++ b/webui/src/features/admin/UsersPanel.tsx @@ -8,8 +8,9 @@ import { Dialog } from "@govoplan/core-webui"; import { FormField } from "@govoplan/core-webui"; import { PasswordField } from "@govoplan/core-webui"; import { StatusBadge } from "@govoplan/core-webui"; +import { ToggleSwitch } from "@govoplan/core-webui"; import { ConfirmDialog } from "@govoplan/core-webui"; -import { AdminIconButton, AdminPageLayout, AdminSelectionList, adminErrorMessage, formatAdminDateTime as formatDateTime, joinLabels } from "@govoplan/core-webui"; +import { AdminIconButton, AdminPageLayout, AdminSelectionList, TableActionGroup, adminErrorMessage, formatAdminDateTime as formatDateTime, joinLabels } from "@govoplan/core-webui"; import { hasTenantWildcard, i18nMessage, useDeltaWatermarks, useUnsavedDraftGuard } from "@govoplan/core-webui"; import { loadDeltaRows } from "./utils/deltaRows"; @@ -186,12 +187,12 @@ export default function UsersPanel({ settings, auth, canCreate, canUpdate, canSu { id: "scope_count", header: "i18n:govoplan-access.permissions.d06d5557", width: 110, resizable: false, sortable: true, filterable: true, filterType: "integer", value: (row) => hasTenantWildcard(row.effective_scopes) ? 999 : row.effective_scopes.length, render: (row) => hasTenantWildcard(row.effective_scopes) ? "i18n:govoplan-access.all.6a720856" : String(row.effective_scopes.length) }, { id: "status", header: "i18n:govoplan-access.status.bae7d5be", width: 120, resizable: false, sortable: true, filterable: true, value: (row) => row.is_active && row.account_is_active ? "active" : "inactive", render: (row) => }, { id: "last_login", header: "i18n:govoplan-access.last_login.43dab84f", width: 180, minWidth: 150, resizable: true, sortable: true, value: (row) => row.last_login_at || "", render: (row) => formatDateTime(row.last_login_at) }, - { id: "actions", header: "i18n:govoplan-access.actions.c3cd636a", width: 190, sticky: "end", resizable: false, align: "right", render: (row) =>
- } onClick={() => setViewing(row)} /> - } onClick={() => void openAccessExplanation(row)} /> - } onClick={() => openEdit(row)} disabled={!(canUpdate || canSuspend || canManageGroups || canAssignRoles)} /> - } variant="danger" onClick={() => setDeactivating(row)} disabled={!canSuspend || !row.is_active || row.is_last_active_owner} /> -
}], + { id: "actions", header: "i18n:govoplan-access.actions.c3cd636a", width: 190, sticky: "end", resizable: false, align: "right", render: (row) => , onClick: () => setViewing(row) }, + { id: "explain", label: i18nMessage("i18n:govoplan-access.explain_access_for_value.3af96e47", { value0: row.email }), icon: , onClick: () => void openAccessExplanation(row) }, + { id: "edit", label: i18nMessage("i18n:govoplan-access.edit_value.fad75899", { value0: row.email }), icon: , disabled: !(canUpdate || canSuspend || canManageGroups || canAssignRoles), onClick: () => openEdit(row) }, + { id: "deactivate", label: i18nMessage("i18n:govoplan-access.deactivate_value.a276a667", { value0: row.email }), icon: , variant: "danger", applicable: row.is_active, disabled: !canSuspend || row.is_last_active_owner, onClick: () => setDeactivating(row) } + ]} /> }], [canAssignRoles, canManageGroups, canSuspend, canUpdate, settings]); return ( @@ -216,7 +217,7 @@ export default function UsersPanel({ settings, auth, canCreate, canUpdate, canSu } - {editing === "new" && } + {editing === "new" && setDraft({ ...draft, passwordResetRequired })} />} {editing && editing !== "new" && editing.is_last_active_owner &&

i18n:govoplan-access.this_membership_is_the_tenant_s_last_active_oper.072b247f

}
i18n:govoplan-access.groups.ae9629f4 group.is_active).map((group) => ({ id: group.id, label: group.name, description: group.description, disabled: !canManageGroups }))} selected={draft.groupIds} onChange={(groupIds) => setDraft({ ...draft, groupIds })} emptyText="i18n:govoplan-access.no_groups_exist_yet.9cd029f6" />