mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-22 23:42:16 +00:00
docs: Dynamic Manager Permission Table (#11120)
<!-- Please inform yourself about the contribution guidelines on submitting a PR here: https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#submit-a-pull-request-pr. Take note of how PR/commit titles should be written and replace the template texts in the sections below. Don't remove any of the sections. It is important that the commit history clearly shows what is changed and why. Important: By submitting a contribution you agree to the terms from our Licensing Policy as described here: https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions. --> # Which Problems Are Solved Permissions for each manager role are stored in a .yaml file and can't be easily searched in our docs. # How the Problems Are Solved We reference anyways the defaults.yaml file in our docs. The script parses the yaml and produces a markdown table containing all manager roles by manager permissions. # Additional Changes * Updated README with correct production build command # Additional Context --------- Co-authored-by: Florian Forster <[email protected]> Co-authored-by: Livio Spring <[email protected]>
This commit is contained in:
co-authored by
Florian Forster
Livio Spring
parent
541f8058cb
commit
94f9ef966f
@@ -0,0 +1,77 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
// Note: We no longer need 'js-yaml' here because the loader parses it before the component sees it.
|
||||
|
||||
export default function PermissionTable({ data }) {
|
||||
const { roles, permissions, matrix } = useMemo(() => {
|
||||
if (!data) return { roles: [], permissions: [], matrix: {} };
|
||||
|
||||
// 1. Identify all sources. The 'data' prop is already a JS Object.
|
||||
const sources = [
|
||||
data?.InternalAuthZ?.RolePermissionMappings,
|
||||
data?.SystemAuthZ?.RolePermissionMappings,
|
||||
].filter(Boolean).flat();
|
||||
|
||||
// 2. Aggregate Data
|
||||
const roleMap = new Map();
|
||||
const allPermissions = new Set();
|
||||
|
||||
sources.forEach((mapping) => {
|
||||
const roleName = mapping.Role;
|
||||
const rolePerms = mapping.Permissions || [];
|
||||
|
||||
if (!roleMap.has(roleName)) {
|
||||
roleMap.set(roleName, new Set());
|
||||
}
|
||||
|
||||
rolePerms.forEach((perm) => {
|
||||
roleMap.get(roleName).add(perm);
|
||||
allPermissions.add(perm);
|
||||
});
|
||||
});
|
||||
|
||||
// 3. Sort for display
|
||||
const sortedRoles = Array.from(roleMap.keys()).sort();
|
||||
const sortedPermissions = Array.from(allPermissions).sort();
|
||||
|
||||
return {
|
||||
roles: sortedRoles,
|
||||
permissions: sortedPermissions,
|
||||
matrix: roleMap,
|
||||
};
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<table className={styles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Permission</th>
|
||||
{roles.map((role) => (
|
||||
<th key={role}>{role}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{permissions.map((perm) => (
|
||||
<tr key={perm}>
|
||||
<th>{perm}</th>
|
||||
{roles.map((role) => {
|
||||
const hasPermission = matrix.get(role).has(perm);
|
||||
return (
|
||||
<td
|
||||
key={`${role}-${perm}`}
|
||||
className={hasPermission ? styles.yes : styles.no}
|
||||
>
|
||||
{hasPermission ? 'yes' : 'no'}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
.container {
|
||||
overflow-x: auto;
|
||||
max-width: 100%;
|
||||
margin: 2rem 0;
|
||||
border: 1px solid var(--ifm-color-emphasis-200);
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--ifm-global-shadow-lw);
|
||||
}
|
||||
|
||||
.table {
|
||||
border-collapse: separate; /* Required for sticky to work */
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid var(--ifm-color-emphasis-200);
|
||||
border-right: 1px solid var(--ifm-color-emphasis-200);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Sticky Header Row */
|
||||
.table thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: var(--ifm-background-surface-color);
|
||||
z-index: 10;
|
||||
border-bottom: 2px solid var(--ifm-color-emphasis-300);
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Sticky First Column (Permission Name) */
|
||||
.table tbody th {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
background-color: var(--ifm-background-surface-color);
|
||||
z-index: 5;
|
||||
border-right: 2px solid var(--ifm-color-emphasis-300);
|
||||
text-align: left;
|
||||
/*font-family: var(--ifm-font-family-monospace);
|
||||
color: var(--ifm-color-primary);*/
|
||||
}
|
||||
|
||||
/* Top Left Corner Intersection */
|
||||
.table thead th:first-child {
|
||||
left: 0;
|
||||
z-index: 20;
|
||||
border-right: 2px solid var(--ifm-color-emphasis-300);
|
||||
}
|
||||
|
||||
/* Cell Coloring */
|
||||
.yes {
|
||||
background-color: rgba(0, 255, 0, 0.1); /* Light green tint */
|
||||
color: var(--ifm-color-success);
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.no {
|
||||
color: var(--ifm-color-emphasis-400);
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
/* Dark mode adjustments automatically handled by Docusaurus CSS vars,
|
||||
but ensuring background opacity works */
|
||||
[data-theme='dark'] .yes {
|
||||
background-color: rgba(0, 255, 0, 0.15);
|
||||
}
|
||||
Reference in New Issue
Block a user