Add role management
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
@@ -68,6 +68,20 @@ export function PageError({ resetErrorBoundary, error: propsError }: Props) {
|
||||
);
|
||||
}
|
||||
|
||||
if (error && error.toString().includes("FORBIDDEN")) {
|
||||
return (
|
||||
<div className={classNames.wrapper}>
|
||||
<h1 className={classNames.title}>
|
||||
<IconPageCross size={26} />
|
||||
{__("Page not found")}
|
||||
</h1>
|
||||
<p className={classNames.description}>
|
||||
{__("The page you are looking for does not exist")}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error && error.toString().includes("UNAUTHORIZED")) {
|
||||
return (
|
||||
<div className={classNames.wrapper}>
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { CustomDomainManagerDeleteMutation } from "./__generated__/CustomDo
|
||||
import { CreateCustomDomainDialog } from "./CreateCustomDomainDialog";
|
||||
import { DeleteCustomDomainDialog } from "./DeleteCustomDomainDialog";
|
||||
import { DomainDetailsDialog } from "./DomainDetailsDialog";
|
||||
import { Authorized } from "/permissions";
|
||||
|
||||
const deleteCustomDomainMutation = graphql`
|
||||
mutation CustomDomainManagerDeleteMutation($input: DeleteCustomDomainInput!) {
|
||||
@@ -106,9 +107,11 @@ export function CustomDomainManager({
|
||||
)}
|
||||
</p>
|
||||
<div className="flex justify-center">
|
||||
<CreateCustomDomainDialog organizationId={organizationId}>
|
||||
<Button icon={IconPlusLarge}>{__("Add Domain")}</Button>
|
||||
</CreateCustomDomainDialog>
|
||||
<Authorized entity="Organization" action="createCustomDomain">
|
||||
<CreateCustomDomainDialog organizationId={organizationId}>
|
||||
<Button icon={IconPlusLarge}>{__("Add Domain")}</Button>
|
||||
</CreateCustomDomainDialog>
|
||||
</Authorized>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -136,12 +139,14 @@ export function CustomDomainManager({
|
||||
<Button variant="secondary">{__("View Details")}</Button>
|
||||
</DomainDetailsDialog>
|
||||
|
||||
<DeleteCustomDomainDialog
|
||||
domainName={domain.domain}
|
||||
onConfirm={handleDeleteDomain}
|
||||
>
|
||||
<Button variant="danger">{__("Delete")}</Button>
|
||||
</DeleteCustomDomainDialog>
|
||||
<Authorized entity="CustomDomain" action="deleteCustomDomain">
|
||||
<DeleteCustomDomainDialog
|
||||
domainName={domain.domain}
|
||||
onConfirm={handleDeleteDomain}
|
||||
>
|
||||
<Button variant="danger">{__("Delete")}</Button>
|
||||
</DeleteCustomDomainDialog>
|
||||
</Authorized>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
DialogFooter,
|
||||
Field,
|
||||
Checkbox,
|
||||
Select,
|
||||
Option,
|
||||
useDialogRef,
|
||||
} from "@probo/ui";
|
||||
import type { PropsWithChildren } from "react";
|
||||
@@ -15,6 +17,8 @@ import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
import { z } from "zod";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
import { Controller } from "react-hook-form";
|
||||
import { Suspense } from "react";
|
||||
import { getAssignableRoles } from "/permissions";
|
||||
|
||||
const inviteMutation = graphql`
|
||||
mutation InviteUserDialogMutation(
|
||||
@@ -40,6 +44,7 @@ const inviteMutation = graphql`
|
||||
const schema = z.object({
|
||||
email: z.string().email(),
|
||||
fullName: z.string(),
|
||||
role: z.enum(["OWNER", "ADMIN", "FULL", "VIEWER"]).default("VIEWER"),
|
||||
createPeople: z.boolean().default(false),
|
||||
});
|
||||
|
||||
@@ -48,16 +53,17 @@ type Props = PropsWithChildren & {
|
||||
onRefetch: () => void;
|
||||
};
|
||||
|
||||
export function InviteUserDialog({ children, connectionId, onRefetch }: Props) {
|
||||
function InviteUserDialogContent({ children, connectionId, onRefetch }: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const organizationId = useOrganizationId();
|
||||
const assignableRoles = getAssignableRoles(organizationId);
|
||||
const [inviteUser, isInviting] = useMutationWithToasts(inviteMutation, {
|
||||
successMessage: __("Invitation sent successfully"),
|
||||
errorMessage: __("Failed to send invitation"),
|
||||
});
|
||||
const { register, handleSubmit, formState, reset, control } = useFormWithSchema(
|
||||
schema,
|
||||
{ defaultValues: { createPeople: false } },
|
||||
{ defaultValues: { role: "VIEWER", createPeople: false } },
|
||||
);
|
||||
|
||||
const dialogRef = useDialogRef();
|
||||
@@ -69,6 +75,7 @@ export function InviteUserDialog({ children, connectionId, onRefetch }: Props) {
|
||||
organizationId,
|
||||
email: data.email,
|
||||
fullName: data.fullName,
|
||||
role: data.role,
|
||||
createPeople: data.createPeople,
|
||||
},
|
||||
connections: connectionId ? [connectionId] : ["SettingsPageInvitations_invitations"],
|
||||
@@ -107,6 +114,32 @@ export function InviteUserDialog({ children, connectionId, onRefetch }: Props) {
|
||||
{...register("fullName")}
|
||||
error={formState.errors.fullName?.message}
|
||||
/>
|
||||
<Field label={__("Role")} required>
|
||||
<Controller
|
||||
name="role"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<Select value={field.value} onValueChange={field.onChange}>
|
||||
{assignableRoles.includes("OWNER") && <Option value="OWNER">{__("Owner")}</Option>}
|
||||
{assignableRoles.includes("ADMIN") && <Option value="ADMIN">{__("Admin")}</Option>}
|
||||
{assignableRoles.includes("VIEWER") && <Option value="VIEWER">{__("Viewer")}</Option>}
|
||||
</Select>
|
||||
<div className="mt-2 text-sm text-txt-tertiary">
|
||||
{field.value === "OWNER" && (
|
||||
<p>{__("Full access to everything")}</p>
|
||||
)}
|
||||
{field.value === "ADMIN" && (
|
||||
<p>{__("Full access except organization setup and API keys")}</p>
|
||||
)}
|
||||
{field.value === "VIEWER" && (
|
||||
<p>{__("Read-only access")}</p>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Field>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center space-x-3">
|
||||
<Controller
|
||||
@@ -142,3 +175,11 @@ export function InviteUserDialog({ children, connectionId, onRefetch }: Props) {
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export function InviteUserDialog(props: Props) {
|
||||
return (
|
||||
<Suspense fallback={props.children}>
|
||||
<InviteUserDialogContent {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Badge, Button, Card } from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { sprintf } from "@probo/helpers";
|
||||
import type { TrustCenterGraphQuery$data } from "/hooks/graph/__generated__/TrustCenterGraphQuery.graphql";
|
||||
import { Authorized } from "/permissions";
|
||||
|
||||
type Props = {
|
||||
organizationId: string;
|
||||
@@ -76,9 +77,11 @@ export function SlackConnections({ organizationId, slackConnections: connectedSl
|
||||
</Badge>
|
||||
</div>
|
||||
) : (
|
||||
<Button variant="secondary" asChild>
|
||||
<a href={getUrl(slackConnection.id)}>{__("Connect")}</a>
|
||||
</Button>
|
||||
<Authorized entity="TrustCenter" action="updateTrustCenter">
|
||||
<Button variant="secondary" asChild>
|
||||
<a href={getUrl(slackConnection.id)}>{__("Connect")}</a>
|
||||
</Button>
|
||||
</Authorized>
|
||||
)}
|
||||
</Card>
|
||||
))}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<617ea2708c8402c706671dc1a63b1316>>
|
||||
* @generated SignedSource<<eda42f72473c65692ddd9cee68c0ce81>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -9,12 +9,13 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type Role = "ADMIN" | "MEMBER" | "OWNER" | "VIEWER";
|
||||
export type MembershipRole = "ADMIN" | "OWNER" | "VIEWER";
|
||||
export type InviteUserInput = {
|
||||
createPeople: boolean;
|
||||
email: string;
|
||||
fullName: string;
|
||||
organizationId: string;
|
||||
role: MembershipRole;
|
||||
};
|
||||
export type InviteUserDialogMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
@@ -30,7 +31,7 @@ export type InviteUserDialogMutation$data = {
|
||||
readonly expiresAt: any;
|
||||
readonly fullName: string;
|
||||
readonly id: string;
|
||||
readonly role: Role;
|
||||
readonly role: MembershipRole;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -26,6 +26,8 @@ import TaskFormDialog, {
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { Link, useLocation, useParams } from "react-router";
|
||||
import { promisifyMutation } from "@probo/helpers";
|
||||
import { Authorized } from "/permissions";
|
||||
import { isAuthorized } from "/permissions";
|
||||
import type { TaskFormDialogFragment$key } from "./__generated__/TaskFormDialogFragment.graphql";
|
||||
import { updateStoreCounter } from "/hooks/useMutationWithIncrement";
|
||||
|
||||
@@ -49,6 +51,7 @@ type Props = {
|
||||
|
||||
export default function TasksCard({ tasks, connectionId }: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const organizationId = useOrganizationId();
|
||||
const hash = useLocation().hash.replace("#", "");
|
||||
|
||||
const hashes = [
|
||||
@@ -67,6 +70,9 @@ export default function TasksCard({ tasks, connectionId }: Props) {
|
||||
|
||||
usePageTitle(__("Tasks"));
|
||||
|
||||
const hasAnyAction = isAuthorized(organizationId, "Task", "updateTask") ||
|
||||
isAuthorized(organizationId, "Task", "deleteTask");
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{tasks?.length === 0 ? (
|
||||
@@ -100,6 +106,7 @@ export default function TasksCard({ tasks, connectionId }: Props) {
|
||||
key={task.id}
|
||||
task={task}
|
||||
connectionId={connectionId}
|
||||
hasAnyAction={hasAnyAction}
|
||||
/>
|
||||
))}
|
||||
</Fragment>
|
||||
@@ -110,6 +117,7 @@ export default function TasksCard({ tasks, connectionId }: Props) {
|
||||
key={task.id}
|
||||
task={task}
|
||||
connectionId={connectionId}
|
||||
hasAnyAction={hasAnyAction}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -122,6 +130,7 @@ export default function TasksCard({ tasks, connectionId }: Props) {
|
||||
type TaskRowProps = {
|
||||
task: ItemOf<Props["tasks"]> & TaskFormDialogFragment$key;
|
||||
connectionId: string;
|
||||
hasAnyAction: boolean;
|
||||
};
|
||||
|
||||
const deleteMutation = graphql`
|
||||
@@ -221,21 +230,27 @@ function TaskRow(props: TaskRowProps) {
|
||||
<Avatar name={props.task.assignedTo?.fullName ?? ""} />
|
||||
</Link>
|
||||
)}
|
||||
<ActionDropdown>
|
||||
<DropdownItem
|
||||
icon={IconPencil}
|
||||
onClick={() => dialogRef.current?.open()}
|
||||
>
|
||||
{__("Edit")}
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
onClick={onDelete}
|
||||
>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
</ActionDropdown>
|
||||
{props.hasAnyAction && (
|
||||
<ActionDropdown>
|
||||
<Authorized entity="Task" action="updateTask">
|
||||
<DropdownItem
|
||||
icon={IconPencil}
|
||||
onClick={() => dialogRef.current?.open()}
|
||||
>
|
||||
{__("Edit")}
|
||||
</DropdownItem>
|
||||
</Authorized>
|
||||
<Authorized entity="Task" action="deleteTask">
|
||||
<DropdownItem
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
onClick={onDelete}
|
||||
>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
</Authorized>
|
||||
</ActionDropdown>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -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) => (
|
||||
|
||||
@@ -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) => (
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user