Add role management

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-11-07 09:20:38 +01:00
parent 696adb7d79
commit 21c4b7cd9d
143 changed files with 5976 additions and 2094 deletions

View File

@@ -18,6 +18,7 @@ import { useMemo, useState, useCallback, useEffect } from "react";
import { sprintf, getAuditStateVariant, getAuditStateLabel, formatDate, getTrustCenterVisibilityOptions } from "@probo/helpers";
import { useOrganizationId } from "/hooks/useOrganizationId";
import type { TrustCenterAuditsCardFragment$key } from "./__generated__/TrustCenterAuditsCardFragment.graphql";
import { isAuthorized } from "/permissions";
const trustCenterAuditFragment = graphql`
fragment TrustCenterAuditsCardFragment on Audit {
@@ -124,6 +125,8 @@ function AuditRow(props: {
const { __ } = useTranslate();
const [optimisticValue, setOptimisticValue] = useState<string | null>(null);
const canUpdate = organizationId ? isAuthorized(organizationId, "TrustCenter", "updateTrustCenter") : false;
const handleValueChange = useCallback((value: string | {}) => {
const stringValue = typeof value === 'string' ? value : '';
const typedValue = stringValue as "NONE" | "PRIVATE" | "PUBLIC";
@@ -164,7 +167,7 @@ function AuditRow(props: {
type="select"
value={currentValue}
onValueChange={handleValueChange}
disabled={props.disabled}
disabled={props.disabled || !canUpdate}
className="w-[105px]"
>
{visibilityOptions.map((option) => (

View File

@@ -20,6 +20,7 @@ import { useFragment } from "react-relay";
import { useMemo, useState, useCallback, useEffect } from "react";
import { sprintf, getTrustCenterVisibilityOptions } from "@probo/helpers";
import { useOrganizationId } from "/hooks/useOrganizationId";
import { isAuthorized } from "/permissions";
const trustCenterDocumentFragment = graphql`
fragment TrustCenterDocumentsCardFragment on Document {
@@ -129,6 +130,8 @@ function DocumentRow(props: {
const { __ } = useTranslate();
const [optimisticValue, setOptimisticValue] = useState<string | null>(null);
const canUpdate = organizationId ? isAuthorized(organizationId, "TrustCenter", "updateTrustCenter") : false;
const handleValueChange = useCallback((value: string | {}) => {
const stringValue = typeof value === 'string' ? value : '';
const typedValue = stringValue as "NONE" | "PRIVATE" | "PUBLIC";
@@ -164,7 +167,7 @@ function DocumentRow(props: {
type="select"
value={currentValue}
onValueChange={handleValueChange}
disabled={props.disabled}
disabled={props.disabled || !canUpdate}
className="w-[105px]"
>
{visibilityOptions.map((option) => (

View File

@@ -21,6 +21,9 @@ import { useFragment } from "react-relay";
import { useMemo, useState, useCallback, useEffect } from "react";
import { sprintf, getTrustCenterVisibilityOptions } from "@probo/helpers";
import { formatDate } from "@probo/helpers";
import { Authorized } from "/permissions";
import { isAuthorized } from "/permissions";
import { useParams } from "react-router";
const trustCenterFileFragment = graphql`
fragment TrustCenterFilesCardFragment on TrustCenterFile {
@@ -147,6 +150,9 @@ function FileRow(props: {
const file = props.file;
const { __ } = useTranslate();
const [optimisticValue, setOptimisticValue] = useState<string | null>(null);
const { organizationId } = useParams();
const canUpdate = organizationId ? isAuthorized(organizationId, "TrustCenter", "updateTrustCenter") : false;
const handleValueChange = useCallback((value: string | {}) => {
const stringValue = typeof value === 'string' ? value : '';
@@ -179,7 +185,7 @@ function FileRow(props: {
type="select"
value={currentValue}
onValueChange={handleValueChange}
disabled={props.disabled}
disabled={props.disabled || !canUpdate}
className="w-[105px]"
>
{visibilityOptions.map((option) => (
@@ -201,20 +207,24 @@ function FileRow(props: {
onClick={() => window.open(file.fileUrl, '_blank', 'noopener,noreferrer')}
title={__("Download")}
/>
<Button
variant="secondary"
icon={IconPencil}
onClick={() => props.onEdit({ id: file.id, name: file.name, category: file.category })}
disabled={props.disabled}
title={__("Edit")}
/>
<Button
variant="danger"
icon={IconTrashCan}
onClick={() => props.onDelete(file.id)}
disabled={props.disabled}
title={__("Delete")}
/>
<Authorized entity="TrustCenterFile" action="updateTrustCenterFile">
<Button
variant="secondary"
icon={IconPencil}
onClick={() => props.onEdit({ id: file.id, name: file.name, category: file.category })}
disabled={props.disabled}
title={__("Edit")}
/>
</Authorized>
<Authorized entity="TrustCenterFile" action="deleteTrustCenterFile">
<Button
variant="danger"
icon={IconTrashCan}
onClick={() => props.onDelete(file.id)}
disabled={props.disabled}
title={__("Delete")}
/>
</Authorized>
</div>
</Td>
</Tr>

View File

@@ -21,6 +21,7 @@ import {
} from "/hooks/graph/TrustCenterReferenceGraph";
import { TrustCenterReferenceDialog, type TrustCenterReferenceDialogRef } from "./TrustCenterReferenceDialog";
import { DeleteTrustCenterReferenceDialog } from "./DeleteTrustCenterReferenceDialog";
import { Authorized } from "/permissions";
type Props = {
trustCenterId: string;
@@ -111,12 +112,14 @@ export function TrustCenterReferencesSection({ trustCenterId }: Props) {
{__("Showcase your customers and partners on your trust center")}
</p>
</div>
<Button
icon={IconPlusLarge}
onClick={handleCreate}
>
{__("Add Reference")}
</Button>
<Authorized entity="TrustCenter" action="createTrustCenterReference">
<Button
icon={IconPlusLarge}
onClick={handleCreate}
>
{__("Add Reference")}
</Button>
</Authorized>
</div>
<Table>
@@ -228,21 +231,25 @@ function ReferenceRow({
icon={IconArrowLink}
onClick={onVisitWebsite}
/>
<Button
variant="secondary"
icon={IconPencil}
onClick={onEdit}
/>
<DeleteTrustCenterReferenceDialog
referenceId={reference.id}
referenceName={reference.name}
connectionId={connectionId}
>
<Authorized entity="TrustCenterReference" action="updateTrustCenterReference">
<Button
variant="danger"
icon={IconTrashCan}
variant="secondary"
icon={IconPencil}
onClick={onEdit}
/>
</DeleteTrustCenterReferenceDialog>
</Authorized>
<Authorized entity="TrustCenterReference" action="deleteTrustCenterReference">
<DeleteTrustCenterReferenceDialog
referenceId={reference.id}
referenceName={reference.name}
connectionId={connectionId}
>
<Button
variant="danger"
icon={IconTrashCan}
/>
</DeleteTrustCenterReferenceDialog>
</Authorized>
</div>
</Td>
</Tr>

View File

@@ -14,9 +14,10 @@ import {
} from "@probo/ui";
import { useTranslate } from "@probo/i18n";
import { useFragment } from "react-relay";
import { useMemo, useState } from "react";
import { useMemo, useState, useEffect } from "react";
import { sprintf } from "@probo/helpers";
import { useOrganizationId } from "/hooks/useOrganizationId";
import { isAuthorized } from "/permissions/permissions";
import type { TrustCenterVendorsCardFragment$key } from "./__generated__/TrustCenterVendorsCardFragment.graphql";
const trustCenterVendorFragment = graphql`
@@ -48,12 +49,44 @@ type Props<Params> = {
export function TrustCenterVendorsCard<Params>(props: Props<Params>) {
const { __ } = useTranslate();
const organizationId = useOrganizationId();
const [limit, setLimit] = useState<number | null>(100);
const [canUpdate, setCanUpdate] = useState<boolean>(false);
const vendors = useMemo(() => {
return limit ? props.vendors.slice(0, limit) : props.vendors;
}, [props.vendors, limit]);
const showMoreButton = limit !== null && props.vendors.length > limit;
useEffect(() => {
if (!organizationId) {
setCanUpdate(false);
return;
}
try {
const authorized = isAuthorized(organizationId, "Vendor", "updateVendor");
setCanUpdate(authorized);
} catch (promise) {
if (promise instanceof Promise) {
promise
.then(() => {
try {
const authorized = isAuthorized(organizationId, "Vendor", "updateVendor");
setCanUpdate(authorized);
} catch {
setCanUpdate(false);
}
})
.catch(() => {
setCanUpdate(false);
});
} else {
setCanUpdate(false);
}
}
}, [organizationId]);
const onToggleVisibility = (vendorId: string, showOnTrustCenter: boolean) => {
props.onToggleVisibility({
variables: {
@@ -74,13 +107,13 @@ export function TrustCenterVendorsCard<Params>(props: Props<Params>) {
<Th>{__("Name")}</Th>
<Th>{__("Category")}</Th>
<Th>{__("Visibility")}</Th>
<Th></Th>
{canUpdate && <Th></Th>}
</Tr>
</Thead>
<Tbody>
{vendors.length === 0 && (
<Tr>
<Td colSpan={4} className="text-center text-txt-secondary">
<Td colSpan={canUpdate ? 4 : 3} className="text-center text-txt-secondary">
{__("No vendors available")}
</Td>
</Tr>
@@ -91,6 +124,7 @@ export function TrustCenterVendorsCard<Params>(props: Props<Params>) {
vendor={vendor}
onToggleVisibility={onToggleVisibility}
disabled={props.disabled}
canUpdate={canUpdate}
/>
))}
</Tbody>
@@ -113,6 +147,7 @@ function VendorRow(props: {
vendor: TrustCenterVendorsCardFragment$key;
onToggleVisibility: (vendorId: string, showOnTrustCenter: boolean) => void;
disabled?: boolean;
canUpdate: boolean;
}) {
const vendor = useFragment(trustCenterVendorFragment, props.vendor);
const organizationId = useOrganizationId();
@@ -135,16 +170,18 @@ function VendorRow(props: {
{vendor.showOnTrustCenter ? __("Visible") : __("None")}
</Badge>
</Td>
<Td noLink width={100} className="text-end">
<Button
variant="secondary"
onClick={() => props.onToggleVisibility(vendor.id, !vendor.showOnTrustCenter)}
icon={vendor.showOnTrustCenter ? IconCrossLargeX : IconCheckmark1}
disabled={props.disabled}
>
{vendor.showOnTrustCenter ? __("Hide") : __("Show")}
</Button>
</Td>
{props.canUpdate && (
<Td noLink width={100} className="text-end">
<Button
variant="secondary"
onClick={() => props.onToggleVisibility(vendor.id, !vendor.showOnTrustCenter)}
icon={vendor.showOnTrustCenter ? IconCrossLargeX : IconCheckmark1}
disabled={props.disabled}
>
{vendor.showOnTrustCenter ? __("Hide") : __("Show")}
</Button>
</Td>
)}
</Tr>
);
}