Release v0.1.8
This commit is contained in:
@@ -19,7 +19,7 @@ import {
|
||||
type PermissionItem,
|
||||
type TenantAdminItem } from
|
||||
"../../api/admin";
|
||||
import { AdminIconButton, AdminPageLayout, AdminSelectionList, adminErrorMessage, joinLabels, i18nMessage, useUnsavedDraftGuard } from "@govoplan/core-webui";
|
||||
import { AdminIconButton, AdminPageLayout, AdminSelectionList, adminErrorMessage, i18nMessage, useUnsavedDraftGuard } from "@govoplan/core-webui";
|
||||
|
||||
const emptyDraft = {
|
||||
slug: "",
|
||||
@@ -54,6 +54,7 @@ export default function GovernanceTemplatesPanel({
|
||||
const [error, setError] = useState("");
|
||||
const [success, setSuccess] = useState("");
|
||||
const dirty = editing !== null && draftKey(draft) !== savedDraftKey;
|
||||
const permissionsByScope = useMemo(() => new Map(permissions.map((permission) => [permission.scope, permission])), [permissions]);
|
||||
|
||||
useUnsavedDraftGuard({
|
||||
dirty,
|
||||
@@ -238,7 +239,7 @@ export default function GovernanceTemplatesPanel({
|
||||
</Dialog>
|
||||
|
||||
<Dialog open={Boolean(viewing)} title={viewing?.name || "i18n:govoplan-admin.template_details.d5d75e4d"} onClose={() => setViewing(null)} className="admin-dialog admin-dialog-wide" footer={<Button onClick={() => setViewing(null)}>i18n:govoplan-admin.close.bbfa773e</Button>}>
|
||||
{viewing && <dl className="admin-details-grid"><div><dt>i18n:govoplan-admin.kind.e00ac23f</dt><dd>{viewing.kind}</dd></div><div><dt>i18n:govoplan-admin.slug.094da9b9</dt><dd>{viewing.slug}</dd></div><div><dt>i18n:govoplan-admin.status.bae7d5be</dt><dd>{viewing.is_active ? "i18n:govoplan-admin.active.a733b809" : "i18n:govoplan-admin.inactive.09af574c"}</dd></div><div><dt>i18n:govoplan-admin.tenants.1f7ae776</dt><dd>{viewing.assignments.length || "i18n:govoplan-admin.none.6eef6648"}</dd></div><div><dt>i18n:govoplan-admin.description.55f8ebc8</dt><dd>{viewing.description || "—"}</dd></div><div><dt>i18n:govoplan-admin.permissions.d06d5557</dt><dd>{viewing.permissions.length ? joinLabels(viewing.permissions.map((name) => ({ name }))) : "—"}</dd></div></dl>}
|
||||
{viewing && <dl className="admin-details-grid"><div><dt>i18n:govoplan-admin.kind.e00ac23f</dt><dd>{viewing.kind}</dd></div><div><dt>i18n:govoplan-admin.slug.094da9b9</dt><dd>{viewing.slug}</dd></div><div><dt>i18n:govoplan-admin.status.bae7d5be</dt><dd>{viewing.is_active ? "i18n:govoplan-admin.active.a733b809" : "i18n:govoplan-admin.inactive.09af574c"}</dd></div><div><dt>i18n:govoplan-admin.tenants.1f7ae776</dt><dd>{viewing.assignments.length || "i18n:govoplan-admin.none.6eef6648"}</dd></div><div><dt>i18n:govoplan-admin.description.55f8ebc8</dt><dd>{viewing.description || "—"}</dd></div><div><dt>i18n:govoplan-admin.permissions.d06d5557</dt><dd>{viewing.permissions.length ? <PermissionDetails scopes={viewing.permissions} permissionsByScope={permissionsByScope} /> : "—"}</dd></div></dl>}
|
||||
</Dialog>
|
||||
|
||||
<ConfirmDialog open={Boolean(deleting)} title={kind === "group" ? "i18n:govoplan-admin.delete_group_template.8745d842" : "i18n:govoplan-admin.delete_tenant_role.4efa0813"} message={i18nMessage("i18n:govoplan-admin.delete_value_removal_is_blocked_while_a_material.d4eba73d", { value0: deleting?.name })} confirmLabel={kind === "group" ? "i18n:govoplan-admin.delete_template.399bf72a" : "i18n:govoplan-admin.delete_tenant_role.4efa0813"} tone="danger" busy={busy} onCancel={() => setDeleting(null)} onConfirm={() => void remove()} />
|
||||
@@ -249,3 +250,31 @@ export default function GovernanceTemplatesPanel({
|
||||
function draftKey(draft: typeof emptyDraft): string {
|
||||
return JSON.stringify(draft);
|
||||
}
|
||||
|
||||
function PermissionDetails({ scopes, permissionsByScope }: {scopes: string[];permissionsByScope: ReadonlyMap<string, PermissionItem>;}) {
|
||||
const groups = groupPermissionScopes(scopes, permissionsByScope);
|
||||
return (
|
||||
<div className="admin-permission-details">
|
||||
{groups.map((group) => <section key={group.module}>
|
||||
<strong>{group.module}</strong>
|
||||
<ul>
|
||||
{group.permissions.map((permission) => <li key={permission.scope}>
|
||||
<span>{permission.label}</span>
|
||||
<code>{permission.scope}</code>
|
||||
</li>)}
|
||||
</ul>
|
||||
</section>)}
|
||||
</div>);
|
||||
}
|
||||
|
||||
function groupPermissionScopes(scopes: string[], permissionsByScope: ReadonlyMap<string, PermissionItem>) {
|
||||
const groups = new Map<string, {scope: string;label: string}[]>();
|
||||
for (const scope of [...scopes].sort()) {
|
||||
const moduleId = scope.split(":", 1)[0] || "other";
|
||||
const permission = permissionsByScope.get(scope);
|
||||
const group = groups.get(moduleId) ?? [];
|
||||
group.push({ scope, label: permission?.label || scope });
|
||||
groups.set(moduleId, group);
|
||||
}
|
||||
return [...groups.entries()].map(([module, permissions]) => ({ module, permissions }));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user