({
bindings,
allowEdit,
onUpdateBinding,
onRevokeBinding,
scope,
}: {
bindings: MemberBinding[];
allowEdit: boolean;
onUpdateBinding: (binding: MemberBinding) => void;
onRevokeBinding: (binding: MemberBinding) => void;
scope: "workspace" | "project";
})
| 608 | // ============================================================ |
| 609 | |
| 610 | function MemberTableByRole({ |
| 611 | bindings, |
| 612 | allowEdit, |
| 613 | onUpdateBinding, |
| 614 | onRevokeBinding, |
| 615 | scope, |
| 616 | }: { |
| 617 | bindings: MemberBinding[]; |
| 618 | allowEdit: boolean; |
| 619 | onUpdateBinding: (binding: MemberBinding) => void; |
| 620 | onRevokeBinding: (binding: MemberBinding) => void; |
| 621 | scope: "workspace" | "project"; |
| 622 | }) { |
| 623 | const { t } = useTranslation(); |
| 624 | const currentUser = useCurrentUser(); |
| 625 | const isSaaSMode = useAppStore((s) => s.isSaaSMode()); |
| 626 | const roleList = useAppStore((state) => state.roleList); |
| 627 | const [expandedRoles, setExpandedRoles] = useState<Set<string>>(new Set()); |
| 628 | const initializedRef = useRef(false); |
| 629 | |
| 630 | type RoleMember = { member: MemberBinding; allExpired: boolean }; |
| 631 | const roleToBindings = useMemo(() => { |
| 632 | const map = new Map<string, RoleMember[]>(); |
| 633 | const appendToRole = (role: string, entry: RoleMember) => { |
| 634 | const arr = map.get(role) ?? []; |
| 635 | arr.push(entry); |
| 636 | map.set(role, arr); |
| 637 | }; |
| 638 | for (const mb of bindings) { |
| 639 | if (scope === "project") { |
| 640 | // Group this member's bindings by role so we can mark a row expired |
| 641 | // only when ALL of its bindings for that role are expired. |
| 642 | const bindingsByRole = new Map<string, Binding[]>(); |
| 643 | for (const b of mb.projectRoleBindings) { |
| 644 | const arr = bindingsByRole.get(b.role) ?? []; |
| 645 | arr.push(b); |
| 646 | bindingsByRole.set(b.role, arr); |
| 647 | } |
| 648 | for (const [role, roleBindings] of bindingsByRole) { |
| 649 | const allExpired = roleBindings.every((b) => |
| 650 | isBindingPolicyExpired(b) |
| 651 | ); |
| 652 | appendToRole(role, { member: mb, allExpired }); |
| 653 | } |
| 654 | } else { |
| 655 | for (const role of mb.workspaceLevelRoles) { |
| 656 | appendToRole(role, { member: mb, allExpired: false }); |
| 657 | } |
| 658 | } |
| 659 | } |
| 660 | const sortedRoles = sortRoles([...map.keys()]); |
| 661 | return sortedRoles.map((role) => ({ |
| 662 | role, |
| 663 | // Active members first, expired-only members last. |
| 664 | members: (map.get(role) ?? []).slice().sort((a, b) => { |
| 665 | return Number(a.allExpired) - Number(b.allExpired); |
| 666 | }), |
| 667 | })); |
nothing calls this directly
no test coverage detected