Add policies version history
Signed-off-by: Bryan Frimin <bryan@getprobo.com> Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
committed by
Sacha Al Himdani
parent
abe0ce1dde
commit
2e6741942b
@@ -20,7 +20,6 @@ export function ControlledField({
|
||||
name={name}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
{/* @ts-expect-error field is too dynamic */}
|
||||
<Field
|
||||
{...props}
|
||||
{...field}
|
||||
|
||||
@@ -12,6 +12,8 @@ export const policiesQuery = graphql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const PoliciesConnectionKey = "PoliciesPageFragment_policies";
|
||||
|
||||
const deletePolicyMutation = graphql`
|
||||
mutation PolicyGraphDeleteMutation(
|
||||
$input: DeletePolicyInput!
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<88a40781a78a282ebde97d4cb73bb996>>
|
||||
* @generated SignedSource<<bfeead50dec41ac2f5fac7c6d70f47fb>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -39,12 +39,33 @@ v1 = [
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
};
|
||||
},
|
||||
v4 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
},
|
||||
v5 = [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 20
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
@@ -93,14 +114,8 @@ return {
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
@@ -119,26 +134,14 @@ return {
|
||||
"name": "owner",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
}
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 1
|
||||
}
|
||||
],
|
||||
"args": (v5/*: any*/),
|
||||
"concreteType": "PolicyVersionConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "versions",
|
||||
@@ -160,7 +163,7 @@ return {
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -226,7 +229,7 @@ return {
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -242,15 +245,89 @@ return {
|
||||
}
|
||||
],
|
||||
"storageKey": "signatures(first:100)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "changelog",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "publishedBy",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v4/*: any*/),
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "cursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PageInfo",
|
||||
"kind": "LinkedField",
|
||||
"name": "pageInfo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "endCursor",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasNextPage",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"kind": "ClientExtension",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__id",
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": "versions(first:1)"
|
||||
"storageKey": "versions(first:20)"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v5/*: any*/),
|
||||
"filters": null,
|
||||
"handle": "connection",
|
||||
"key": "PolicyPage_versions",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "versions"
|
||||
}
|
||||
],
|
||||
"type": "Policy",
|
||||
@@ -262,12 +339,12 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "061b3441def7dfcf73881e57077e1738",
|
||||
"cacheID": "e3a62b161b9fce47237c98f90a5d6d2e",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "PolicyGraphNodeQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query PolicyGraphNodeQuery(\n $policyId: ID!\n) {\n node(id: $policyId) {\n __typename\n ... on Policy {\n ...PolicyPagePolicyFragment\n }\n id\n }\n}\n\nfragment PolicyPagePolicyFragment on Policy {\n id\n title\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n content\n status\n publishedAt\n version\n updatedAt\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
|
||||
"text": "query PolicyGraphNodeQuery(\n $policyId: ID!\n) {\n node(id: $policyId) {\n __typename\n ... on Policy {\n ...PolicyPagePolicyFragment\n }\n id\n }\n}\n\nfragment PolicyPagePolicyFragment on Policy {\n id\n title\n owner {\n id\n fullName\n }\n versions(first: 20) {\n edges {\n node {\n id\n content\n status\n publishedAt\n version\n updatedAt\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n ...PolicyVersionHistoryDialogFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment PolicyVersionHistoryDialogFragment on PolicyVersion {\n id\n version\n status\n content\n changelog\n publishedAt\n updatedAt\n publishedBy {\n fullName\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -28,7 +28,8 @@ export function useMutationWithToasts<T extends MutationParameters>(
|
||||
const options = { ...baseOptions, ...queryOptions };
|
||||
mutate({
|
||||
...options,
|
||||
onCompleted: (_, error) => {
|
||||
onCompleted: (response, error) => {
|
||||
options.onCompleted?.(response, error);
|
||||
if (error) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
useConfirmDialogRef,
|
||||
} from "@probo/ui";
|
||||
import {
|
||||
measuresQuery,
|
||||
@@ -156,6 +157,7 @@ export default function MeasuresPage(props: Props) {
|
||||
key={category}
|
||||
category={category}
|
||||
measures={measuresPerCategory[category]}
|
||||
connectionId={connectionId}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -165,6 +167,7 @@ export default function MeasuresPage(props: Props) {
|
||||
type CategoryProps = {
|
||||
category: string;
|
||||
measures: NodeOf<MeasuresPageFragment$data["measures"]>[];
|
||||
connectionId: string;
|
||||
};
|
||||
|
||||
function Category(props: CategoryProps) {
|
||||
@@ -210,7 +213,11 @@ function Category(props: CategoryProps) {
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{measures.map((measure) => (
|
||||
<MeasureRow key={measure.id} measure={measure} />
|
||||
<MeasureRow
|
||||
key={measure.id}
|
||||
measure={measure}
|
||||
connectionId={props.connectionId}
|
||||
/>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
@@ -232,6 +239,7 @@ function Category(props: CategoryProps) {
|
||||
|
||||
type MeasureRowProps = {
|
||||
measure: NodeOf<MeasuresPageFragment$data["measures"]>;
|
||||
connectionId: string;
|
||||
};
|
||||
|
||||
function MeasureRow(props: MeasureRowProps) {
|
||||
@@ -239,42 +247,50 @@ function MeasureRow(props: MeasureRowProps) {
|
||||
const [deleteMeasure, isDeleting] = useDeleteMeasureMutation();
|
||||
|
||||
const onDelete = () => {
|
||||
deleteMeasure({
|
||||
variables: {
|
||||
input: { measureId: props.measure.id },
|
||||
connections: ["MeasuresListQuery_measures"],
|
||||
},
|
||||
return new Promise<void>((resolve) => {
|
||||
deleteMeasure({
|
||||
variables: {
|
||||
input: { measureId: props.measure.id },
|
||||
connections: [props.connectionId],
|
||||
},
|
||||
onCompleted: () => resolve(),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const confirmRef = useConfirmDialogRef();
|
||||
|
||||
return (
|
||||
<Tr>
|
||||
<Td>{props.measure.name}</Td>
|
||||
<Td width={120}>
|
||||
<MeasureBadge state={props.measure.state} />
|
||||
</Td>
|
||||
<Td width={50} className="text-end">
|
||||
<ActionDropdown>
|
||||
<DropdownItem icon={IconPencil}>{__("Edit")}</DropdownItem>
|
||||
<ConfirmDialog
|
||||
message={sprintf(
|
||||
__(
|
||||
'This will permanently delete the measure "%s". This action cannot be undone.'
|
||||
),
|
||||
props.measure.name
|
||||
)}
|
||||
onConfirm={() => onDelete()}
|
||||
>
|
||||
<>
|
||||
<ConfirmDialog
|
||||
message={sprintf(
|
||||
__(
|
||||
'This will permanently delete the measure "%s". This action cannot be undone.'
|
||||
),
|
||||
props.measure.name
|
||||
)}
|
||||
onConfirm={onDelete}
|
||||
ref={confirmRef}
|
||||
/>
|
||||
<Tr>
|
||||
<Td>{props.measure.name}</Td>
|
||||
<Td width={120}>
|
||||
<MeasureBadge state={props.measure.state} />
|
||||
</Td>
|
||||
<Td width={50} className="text-end">
|
||||
<ActionDropdown>
|
||||
<DropdownItem icon={IconPencil}>{__("Edit")}</DropdownItem>
|
||||
<DropdownItem
|
||||
onClick={() => confirmRef.current?.open()}
|
||||
disabled={isDeleting}
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
</ConfirmDialog>
|
||||
</ActionDropdown>
|
||||
</Td>
|
||||
</Tr>
|
||||
</ActionDropdown>
|
||||
</Td>
|
||||
</Tr>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,9 +9,6 @@ import {
|
||||
Td,
|
||||
Avatar,
|
||||
Badge,
|
||||
ActionDropdown,
|
||||
DropdownItem,
|
||||
IconPencil,
|
||||
ConfirmDialog,
|
||||
IconTrashCan,
|
||||
Button,
|
||||
@@ -31,7 +28,7 @@ import {
|
||||
import type { PoliciesPageListFragment$key } from "./__generated__/PoliciesPageListFragment.graphql";
|
||||
import { usePageTitle } from "@probo/hooks";
|
||||
import { sprintf } from "@probo/helpers";
|
||||
import FormPolicyDialog from "./FormPolicyDialog";
|
||||
import { CreatePolicyDialog } from "./dialogs/CreatePolicyDialog";
|
||||
import type { PoliciesPageRowFragment$key } from "./__generated__/PoliciesPageRowFragment.graphql";
|
||||
|
||||
const policiesFragment = graphql`
|
||||
@@ -80,7 +77,7 @@ export default function PoliciesPage(props: Props) {
|
||||
title={__("Policies")}
|
||||
description={__("Manage your organization’s policies")}
|
||||
>
|
||||
<FormPolicyDialog
|
||||
<CreatePolicyDialog
|
||||
connection={connectionId}
|
||||
trigger={<Button icon={IconPlusLarge}>{__("New policy")}</Button>}
|
||||
/>
|
||||
@@ -160,45 +157,47 @@ function PolicyRow({
|
||||
const signedCount = signatures.filter(
|
||||
(signature) => signature.state === "SIGNED"
|
||||
).length;
|
||||
const [deletePolicy, isDeleting] = useDeletePolicyMutation();
|
||||
const [deletePolicy] = useDeletePolicyMutation();
|
||||
|
||||
const handleDelete = () => {
|
||||
deletePolicy({
|
||||
variables: {
|
||||
input: { policyId: policy.id },
|
||||
connections: [connectionId],
|
||||
},
|
||||
return new Promise<void>((resolve) => {
|
||||
deletePolicy({
|
||||
variables: {
|
||||
input: { policyId: policy.id },
|
||||
connections: [connectionId],
|
||||
},
|
||||
onCompleted: () => resolve(),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Tr to={`/organizations/${organizationId}/policies/${policy.id}`}>
|
||||
<Td>{policy.title}</Td>
|
||||
<Td>
|
||||
<Badge variant={isDraft ? "neutral" : "success"}>
|
||||
{isDraft ? __("Draft") : __("Published")}
|
||||
</Badge>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex gap-2 items-center">
|
||||
<Avatar name={policy.owner.fullName} />
|
||||
{policy.owner.fullName}
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
{dateFormat(policy.updatedAt, {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
weekday: "short",
|
||||
})}
|
||||
</Td>
|
||||
<Td>
|
||||
{signedCount}/{signatures.length}
|
||||
</Td>
|
||||
<Td noLink width={50} className="text-end">
|
||||
<ActionDropdown>
|
||||
<DropdownItem icon={IconPencil}>{__("Edit")}</DropdownItem>
|
||||
<>
|
||||
<Tr to={`/organizations/${organizationId}/policies/${policy.id}`}>
|
||||
<Td>{policy.title}</Td>
|
||||
<Td>
|
||||
<Badge variant={isDraft ? "neutral" : "success"}>
|
||||
{isDraft ? __("Draft") : __("Published")}
|
||||
</Badge>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex gap-2 items-center">
|
||||
<Avatar name={policy.owner.fullName} />
|
||||
{policy.owner.fullName}
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
{dateFormat(policy.updatedAt, {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
weekday: "short",
|
||||
})}
|
||||
</Td>
|
||||
<Td>
|
||||
{signedCount}/{signatures.length}
|
||||
</Td>
|
||||
<Td noLink width={50} className="text-end">
|
||||
<ConfirmDialog
|
||||
message={sprintf(
|
||||
__(
|
||||
@@ -208,16 +207,10 @@ function PolicyRow({
|
||||
)}
|
||||
onConfirm={handleDelete}
|
||||
>
|
||||
<DropdownItem
|
||||
disabled={isDeleting}
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
<Button icon={IconTrashCan} variant="danger" />
|
||||
</ConfirmDialog>
|
||||
</ActionDropdown>
|
||||
</Td>
|
||||
</Tr>
|
||||
</Td>
|
||||
</Tr>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import type { PreloadedQuery } from "react-relay";
|
||||
import type { PolicyGraphNodeQuery } from "/hooks/graph/__generated__/PolicyGraphNodeQuery.graphql";
|
||||
import {
|
||||
ConnectionHandler,
|
||||
graphql,
|
||||
loadQuery,
|
||||
useFragment,
|
||||
usePreloadedQuery,
|
||||
} from "react-relay";
|
||||
import { policyNodeQuery } from "/hooks/graph/PolicyGraph";
|
||||
import {
|
||||
PoliciesConnectionKey,
|
||||
policyNodeQuery,
|
||||
useDeletePolicyMutation,
|
||||
} from "/hooks/graph/PolicyGraph";
|
||||
import { usePageTitle } from "@probo/hooks";
|
||||
import type { PolicyPagePolicyFragment$key } from "./__generated__/PolicyPagePolicyFragment.graphql";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
@@ -19,10 +24,22 @@ import {
|
||||
Drawer,
|
||||
Badge,
|
||||
Avatar,
|
||||
DropdownItem,
|
||||
ConfirmDialog,
|
||||
ActionDropdown,
|
||||
IconTrashCan,
|
||||
IconPencil,
|
||||
useConfirmDialogRef,
|
||||
IconClock,
|
||||
} from "@probo/ui";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { Button } from "@probo/ui";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
import { sprintf } from "@probo/helpers";
|
||||
import { useNavigate } from "react-router";
|
||||
import UpdateVersionDialog from "./dialogs/UpdateVersionDialog";
|
||||
import { useRef } from "react";
|
||||
import { PolicyVersionHistoryDialog } from "./dialogs/PolicyVersionHistoryDialog";
|
||||
|
||||
type Props = {
|
||||
queryRef: PreloadedQuery<PolicyGraphNodeQuery>;
|
||||
@@ -36,7 +53,8 @@ const policyFragment = graphql`
|
||||
id
|
||||
fullName
|
||||
}
|
||||
versions(first: 1) {
|
||||
versions(first: 20) @connection(key: "PolicyPage_versions") {
|
||||
__id
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
@@ -53,6 +71,7 @@ const policyFragment = graphql`
|
||||
}
|
||||
}
|
||||
}
|
||||
...PolicyVersionHistoryDialogFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +96,7 @@ export default function PolicyPage(props: Props) {
|
||||
);
|
||||
const { __, dateFormat } = useTranslate();
|
||||
const organizationId = useOrganizationId();
|
||||
const navigate = useNavigate();
|
||||
const lastVersion = policy.versions.edges[0].node;
|
||||
const isDraft = lastVersion.status === "DRAFT";
|
||||
const [publishPolicyVersion, isPublishing] = useMutationWithToasts(
|
||||
@@ -86,6 +106,10 @@ export default function PolicyPage(props: Props) {
|
||||
errorMessage: __("Failed to publish policy. Please try again."),
|
||||
}
|
||||
);
|
||||
const [deletePolicy, isDeleting] = useDeletePolicyMutation();
|
||||
const versionConnectionId = policy.versions.__id;
|
||||
const versions = policy.versions.edges.map((edge) => edge.node);
|
||||
|
||||
usePageTitle(policy.title);
|
||||
|
||||
const handlePublish = () => {
|
||||
@@ -105,8 +129,46 @@ export default function PolicyPage(props: Props) {
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
return new Promise<void>((resolve) => {
|
||||
const connectionId = ConnectionHandler.getConnectionID(
|
||||
organizationId,
|
||||
PoliciesConnectionKey
|
||||
);
|
||||
deletePolicy({
|
||||
variables: {
|
||||
input: { policyId: policy.id },
|
||||
connections: [connectionId],
|
||||
},
|
||||
onSuccess() {
|
||||
navigate(`/organizations/${organizationId}/policies`);
|
||||
},
|
||||
onError: () => resolve(),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const updateDialogRef = useRef<{ open: () => void }>(null);
|
||||
const confirmRef = useConfirmDialogRef();
|
||||
|
||||
return (
|
||||
<>
|
||||
<UpdateVersionDialog
|
||||
ref={updateDialogRef}
|
||||
policy={policy}
|
||||
connectionId={versionConnectionId}
|
||||
/>
|
||||
|
||||
<ConfirmDialog
|
||||
ref={confirmRef}
|
||||
message={sprintf(
|
||||
__(
|
||||
'This will permanently delete the policy "%s". This action cannot be undone.'
|
||||
),
|
||||
policy.title
|
||||
)}
|
||||
onConfirm={handleDelete}
|
||||
/>
|
||||
<div className="space-y-6">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<Breadcrumb
|
||||
@@ -130,6 +192,28 @@ export default function PolicyPage(props: Props) {
|
||||
{__("Publish")}
|
||||
</Button>
|
||||
)}
|
||||
<PolicyVersionHistoryDialog policy={policy}>
|
||||
<Button icon={IconClock} variant="secondary">
|
||||
{__("Version history")}
|
||||
</Button>
|
||||
</PolicyVersionHistoryDialog>
|
||||
|
||||
<ActionDropdown variant="secondary">
|
||||
<DropdownItem
|
||||
onClick={() => updateDialogRef.current?.open()}
|
||||
icon={IconPencil}
|
||||
>
|
||||
{isDraft ? __("Edit draft policy") : __("Create new draft")}
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
disabled={isDeleting}
|
||||
onClick={() => confirmRef.current?.open()}
|
||||
>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
</ActionDropdown>
|
||||
</div>
|
||||
</div>
|
||||
<PageHeader title={policy.title} />
|
||||
@@ -147,11 +231,11 @@ export default function PolicyPage(props: Props) {
|
||||
</PropertyRow>
|
||||
<PropertyRow label={__("Status")}>
|
||||
<Badge
|
||||
variant={lastVersion.status === "DRAFT" ? "highlight" : "success"}
|
||||
variant={isDraft ? "highlight" : "success"}
|
||||
size="md"
|
||||
className="gap-2"
|
||||
>
|
||||
{lastVersion.status === "DRAFT" ? __("Draft") : __("Published")}
|
||||
{isDraft ? __("Draft") : __("Published")}
|
||||
</Badge>
|
||||
</PropertyRow>
|
||||
<PropertyRow label={__("Version")}>
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* @generated SignedSource<<0c65ab6f2531f65b9687e3ca13074774>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type PolicyStatus = "DRAFT" | "PUBLISHED" | "%future added value";
|
||||
export type CreateDraftPolicyVersionInput = {
|
||||
policyID: string;
|
||||
};
|
||||
export type PolicyPageCreateDraftPolicyVersionMutation$variables = {
|
||||
input: CreateDraftPolicyVersionInput;
|
||||
};
|
||||
export type PolicyPageCreateDraftPolicyVersionMutation$data = {
|
||||
readonly createDraftPolicyVersion: {
|
||||
readonly policyVersionEdge: {
|
||||
readonly node: {
|
||||
readonly id: string;
|
||||
readonly status: PolicyStatus;
|
||||
readonly version: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
export type PolicyPageCreateDraftPolicyVersionMutation = {
|
||||
response: PolicyPageCreateDraftPolicyVersionMutation$data;
|
||||
variables: PolicyPageCreateDraftPolicyVersionMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "CreateDraftPolicyVersionPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createDraftPolicyVersion",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PolicyVersionEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "policyVersionEdge",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PolicyVersion",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "version",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "status",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "PolicyPageCreateDraftPolicyVersionMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "PolicyPageCreateDraftPolicyVersionMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "197f53d67c5393a8163be2a8ce356cd0",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "PolicyPageCreateDraftPolicyVersionMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation PolicyPageCreateDraftPolicyVersionMutation(\n $input: CreateDraftPolicyVersionInput!\n) {\n createDraftPolicyVersion(input: $input) {\n policyVersionEdge {\n node {\n id\n version\n status\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "7fcf72158c1dc58ae8f9d7efaa6d93a9";
|
||||
|
||||
export default node;
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<c59210c06978720ee4b52c41f0337ecf>>
|
||||
* @generated SignedSource<<769555856fa8bff973c4685249abbb2f>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -20,6 +20,7 @@ export type PolicyPagePolicyFragment$data = {
|
||||
};
|
||||
readonly title: string;
|
||||
readonly versions: {
|
||||
readonly __id: string;
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly content: string;
|
||||
@@ -36,6 +37,7 @@ export type PolicyPagePolicyFragment$data = {
|
||||
readonly status: PolicyStatus;
|
||||
readonly updatedAt: any;
|
||||
readonly version: number;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"PolicyVersionHistoryDialogFragment">;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
@@ -57,7 +59,18 @@ var v0 = {
|
||||
return {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"metadata": {
|
||||
"connection": [
|
||||
{
|
||||
"count": null,
|
||||
"cursor": null,
|
||||
"direction": "forward",
|
||||
"path": [
|
||||
"versions"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "PolicyPagePolicyFragment",
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
@@ -88,17 +101,11 @@ return {
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 1
|
||||
}
|
||||
],
|
||||
"alias": "versions",
|
||||
"args": null,
|
||||
"concreteType": "PolicyVersionConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "versions",
|
||||
"name": "__PolicyPage_versions_connection",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
@@ -199,15 +206,71 @@ return {
|
||||
}
|
||||
],
|
||||
"storageKey": "signatures(first:100)"
|
||||
},
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "PolicyVersionHistoryDialogFragment"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "cursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PageInfo",
|
||||
"kind": "LinkedField",
|
||||
"name": "pageInfo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "endCursor",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasNextPage",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"kind": "ClientExtension",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__id",
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": "versions(first:1)"
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Policy",
|
||||
@@ -215,6 +278,6 @@ return {
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "6b70d6d05d6e1081afae758389d070b2";
|
||||
(node as any).hash = "684db4b721c50638ee5ec1f9f4ebdd16";
|
||||
|
||||
export default node;
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
/**
|
||||
* @generated SignedSource<<f3471e799b803d8bd39ad2a76603716e>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type PolicyPagePolicyQuery$variables = {
|
||||
id: string;
|
||||
};
|
||||
export type PolicyPagePolicyQuery$data = {
|
||||
readonly node: {
|
||||
readonly " $fragmentSpreads": FragmentRefs<"PolicyPagePolicyFragment">;
|
||||
};
|
||||
};
|
||||
export type PolicyPagePolicyQuery = {
|
||||
response: PolicyPagePolicyQuery$data;
|
||||
variables: PolicyPagePolicyQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "id"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "id"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "PolicyPagePolicyQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "PolicyPagePolicyFragment"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "PolicyPagePolicyQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "title",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "description",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "owner",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 1
|
||||
}
|
||||
],
|
||||
"concreteType": "PolicyVersionConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "versions",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PolicyVersionEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PolicyVersion",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "status",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 100
|
||||
}
|
||||
],
|
||||
"concreteType": "PolicyVersionSignatureConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "signatures",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PolicyVersionSignatureEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PolicyVersionSignature",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": "signatures(first:100)"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": "versions(first:1)"
|
||||
}
|
||||
],
|
||||
"type": "Policy",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "a7dd624a52cc7f0193b7670476c9de0d",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "PolicyPagePolicyQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query PolicyPagePolicyQuery(\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...PolicyPagePolicyFragment\n id\n }\n}\n\nfragment PolicyPagePolicyFragment on Policy {\n id\n title\n description\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "a1317341ce16c9f3cfd8f6a6a620c077";
|
||||
|
||||
export default node;
|
||||
@@ -1,249 +0,0 @@
|
||||
/**
|
||||
* @generated SignedSource<<9647dfc253447fe799b8178349c656d0>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type PolicyPagePolicyRefetch$variables = {
|
||||
id: string;
|
||||
};
|
||||
export type PolicyPagePolicyRefetch$data = {
|
||||
readonly node: {
|
||||
readonly " $fragmentSpreads": FragmentRefs<"PolicyPagePolicyFragment">;
|
||||
};
|
||||
};
|
||||
export type PolicyPagePolicyRefetch = {
|
||||
response: PolicyPagePolicyRefetch$data;
|
||||
variables: PolicyPagePolicyRefetch$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "id"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "id"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "PolicyPagePolicyRefetch",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "PolicyPagePolicyFragment"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "PolicyPagePolicyRefetch",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "title",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "description",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "owner",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 1
|
||||
}
|
||||
],
|
||||
"concreteType": "PolicyVersionConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "versions",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PolicyVersionEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PolicyVersion",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "status",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 100
|
||||
}
|
||||
],
|
||||
"concreteType": "PolicyVersionSignatureConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "signatures",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PolicyVersionSignatureEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PolicyVersionSignature",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": "signatures(first:100)"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": "versions(first:1)"
|
||||
}
|
||||
],
|
||||
"type": "Policy",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "6eec3f5bb581dbce013634d36d9b43d9",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "PolicyPagePolicyRefetch",
|
||||
"operationKind": "query",
|
||||
"text": "query PolicyPagePolicyRefetch(\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...PolicyPagePolicyFragment\n id\n }\n}\n\nfragment PolicyPagePolicyFragment on Policy {\n id\n title\n description\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "e553858c09ded278d1d30aaad4f7ede9";
|
||||
|
||||
export default node;
|
||||
@@ -10,24 +10,23 @@ import {
|
||||
Label,
|
||||
PropertyRow,
|
||||
Textarea,
|
||||
useDialogRef,
|
||||
} from "@probo/ui";
|
||||
import { useState, type ReactNode } from "react";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { usePolicyForm } from "../../../hooks/forms/usePolicyForm";
|
||||
import { usePolicyForm } from "../../../../hooks/forms/usePolicyForm";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
import type { FormPolicyDialogMutation } from "./__generated__/FormPolicyDialogMutation.graphql";
|
||||
import { PeopleSelect } from "/components/form/PeopleSelect";
|
||||
import type { CreatePolicyDialogMutation } from "./__generated__/CreatePolicyDialogMutation.graphql";
|
||||
|
||||
type Props = {
|
||||
trigger?: ReactNode;
|
||||
open?: boolean;
|
||||
onSuccess?: () => void;
|
||||
connection: string;
|
||||
};
|
||||
|
||||
const createPolicyMutation = graphql`
|
||||
mutation FormPolicyDialogMutation(
|
||||
mutation CreatePolicyDialogMutation(
|
||||
$input: CreatePolicyInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
@@ -44,19 +43,14 @@ const createPolicyMutation = graphql`
|
||||
/**
|
||||
* Dialog to create or update a policy
|
||||
*/
|
||||
export default function FormPolicyDialog({
|
||||
trigger,
|
||||
onSuccess,
|
||||
open,
|
||||
connection,
|
||||
}: Props) {
|
||||
export function CreatePolicyDialog({ trigger, connection }: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const organizationId = useOrganizationId();
|
||||
|
||||
const { control, handleSubmit, register, formState, reset } = usePolicyForm();
|
||||
const errors = formState.errors ?? {};
|
||||
const [createPolicy, isLoading] =
|
||||
useMutationWithToasts<FormPolicyDialogMutation>(createPolicyMutation);
|
||||
useMutationWithToasts<CreatePolicyDialogMutation>(createPolicyMutation);
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
createPolicy({
|
||||
@@ -70,19 +64,17 @@ export default function FormPolicyDialog({
|
||||
successMessage: __("Policy created successfully."),
|
||||
errorMessage: __("Failed to create policy. Please try again."),
|
||||
onSuccess: () => {
|
||||
setOpen(false);
|
||||
dialogRef.current?.close();
|
||||
reset();
|
||||
onSuccess?.();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const [isOpen, setOpen] = useState(!!open);
|
||||
const dialogRef = useDialogRef();
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
onOpenChange={setOpen}
|
||||
ref={dialogRef}
|
||||
trigger={trigger}
|
||||
title={<Breadcrumb items={[__("Policies"), __("New Policy")]} />}
|
||||
>
|
||||
@@ -0,0 +1,108 @@
|
||||
import {
|
||||
Avatar,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogTitle,
|
||||
Markdown,
|
||||
} from "@probo/ui";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { useState, type ReactNode } from "react";
|
||||
import type {
|
||||
PolicyPagePolicyFragment$data,
|
||||
PolicyPagePolicyFragment$key,
|
||||
} from "../__generated__/PolicyPagePolicyFragment.graphql";
|
||||
import { useFragment } from "react-relay";
|
||||
import type {
|
||||
PolicyVersionHistoryDialogFragment$data,
|
||||
PolicyVersionHistoryDialogFragment$key,
|
||||
} from "./__generated__/PolicyVersionHistoryDialogFragment.graphql";
|
||||
import clsx from "clsx";
|
||||
import type { NodeOf } from "/types";
|
||||
|
||||
const historyFragment = graphql`
|
||||
fragment PolicyVersionHistoryDialogFragment on PolicyVersion {
|
||||
id
|
||||
version
|
||||
status
|
||||
content
|
||||
changelog
|
||||
publishedAt
|
||||
updatedAt
|
||||
publishedBy {
|
||||
fullName
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
policy: PolicyPagePolicyFragment$data;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
type Version = NodeOf<PolicyPagePolicyFragment$data["versions"]>;
|
||||
|
||||
export function PolicyVersionHistoryDialog(props: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const versions = props.policy.versions.edges.map((edge) => edge.node);
|
||||
const [selectedVersion, setSelectedVersion] = useState<Version>(versions[0]);
|
||||
return (
|
||||
<Dialog defaultOpen trigger={props.children}>
|
||||
<DialogContent className="flex" scrollableChildren>
|
||||
<aside className="p-6 overflow-y-auto w-60 flex-none">
|
||||
<DialogTitle className="text-base text-txt-primary font-medium mb-4">
|
||||
{__("Version History")}
|
||||
</DialogTitle>
|
||||
{versions.map((version) => (
|
||||
<VersionItem
|
||||
key={version.id}
|
||||
version={version}
|
||||
active={selectedVersion === version}
|
||||
onSelect={setSelectedVersion}
|
||||
/>
|
||||
))}
|
||||
</aside>
|
||||
<main className="flex-1 px-12 py-8">
|
||||
<Markdown content={selectedVersion.content} />
|
||||
</main>
|
||||
</DialogContent>
|
||||
<DialogFooter></DialogFooter>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
function VersionItem(props: {
|
||||
version: Version;
|
||||
active?: boolean;
|
||||
onSelect: (v: Version) => void;
|
||||
}) {
|
||||
const version = useFragment<PolicyVersionHistoryDialogFragment$key>(
|
||||
historyFragment,
|
||||
props.version
|
||||
);
|
||||
const { dateFormat } = useTranslate();
|
||||
return (
|
||||
<button
|
||||
onClick={() => props.onSelect(props.version)}
|
||||
className={clsx(
|
||||
"flex items-center gap-1 py-2 px-[10px] w-full hover:bg-tertiary-hover cursor-pointer rounded",
|
||||
props.active && "bg-tertiary-pressed"
|
||||
)}
|
||||
>
|
||||
<Avatar name={version.publishedBy?.fullName ?? ""} size="l" />
|
||||
<div className="space-y-[2px]">
|
||||
<div className="text-sm text-txt-primary">
|
||||
{version.publishedBy?.fullName ?? "Anonymous"}
|
||||
</div>
|
||||
<div className="text-xs text-txt-secondary">
|
||||
{dateFormat(version.publishedAt, {
|
||||
day: "numeric",
|
||||
month: "short",
|
||||
year: "numeric",
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import {
|
||||
Breadcrumb,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
Spinner,
|
||||
Textarea,
|
||||
useDialogRef,
|
||||
useToast,
|
||||
} from "@probo/ui";
|
||||
import { useState, type RefObject } from "react";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { useMutation } from "react-relay";
|
||||
import type { PolicyPagePolicyFragment$data } from "../__generated__/PolicyPagePolicyFragment.graphql";
|
||||
import type { UpdateVersionDialogCreateMutation } from "./__generated__/UpdateVersionDialogCreateMutation.graphql";
|
||||
import type { UpdateVersionDialogUpdateMutation } from "./__generated__/UpdateVersionDialogUpdateMutation.graphql";
|
||||
import { z } from "zod";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
|
||||
const createDraftPolicy = graphql`
|
||||
mutation UpdateVersionDialogCreateMutation(
|
||||
$input: CreateDraftPolicyVersionInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
createDraftPolicyVersion(input: $input) {
|
||||
policyVersionEdge @prependEdge(connections: $connections) {
|
||||
node {
|
||||
id
|
||||
content
|
||||
status
|
||||
publishedAt
|
||||
version
|
||||
updatedAt
|
||||
signatures(first: 100) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const UpdatePolicyMutation = graphql`
|
||||
mutation UpdateVersionDialogUpdateMutation(
|
||||
$input: UpdatePolicyVersionInput!
|
||||
) {
|
||||
updatePolicyVersion(input: $input) {
|
||||
policyVersion {
|
||||
id
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
policy: PolicyPagePolicyFragment$data;
|
||||
connectionId: string;
|
||||
ref: RefObject<{ open: () => void } | null>;
|
||||
};
|
||||
|
||||
const versionSchema = z.object({
|
||||
content: z.string(),
|
||||
});
|
||||
|
||||
export default function UpdateVersionDialog({
|
||||
policy,
|
||||
connectionId,
|
||||
ref,
|
||||
}: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const { toast } = useToast();
|
||||
const dialogRef = useDialogRef();
|
||||
|
||||
const version = policy.versions.edges[0].node;
|
||||
const isDraft = version?.status === "DRAFT";
|
||||
const [createDraftPolicyVersion, isCreatingDraft] =
|
||||
useMutation<UpdateVersionDialogCreateMutation>(createDraftPolicy);
|
||||
const [updatePolicyVersion, isUpdating] =
|
||||
useMutationWithToasts<UpdateVersionDialogUpdateMutation>(
|
||||
UpdatePolicyMutation,
|
||||
{
|
||||
successMessage: __("Policy updated successfully."),
|
||||
errorMessage: __("Failed to update policy. Please try again."),
|
||||
}
|
||||
);
|
||||
const { handleSubmit, register } = useFormWithSchema(versionSchema, {
|
||||
defaultValues: {
|
||||
content: version.content,
|
||||
},
|
||||
});
|
||||
|
||||
ref.current = {
|
||||
open: () => {
|
||||
dialogRef.current?.open();
|
||||
if (!isDraft) {
|
||||
createDraftPolicyVersion({
|
||||
variables: {
|
||||
input: {
|
||||
policyID: policy.id,
|
||||
},
|
||||
connections: [connectionId],
|
||||
},
|
||||
onCompleted: (_, errors) => {
|
||||
if (errors) {
|
||||
toast({
|
||||
variant: "error",
|
||||
title: __("Error creating draft"),
|
||||
description:
|
||||
errors[0]?.message || __("An unknown error occurred"),
|
||||
});
|
||||
dialogRef.current?.close();
|
||||
return;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if (!version) {
|
||||
return;
|
||||
}
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
updatePolicyVersion({
|
||||
variables: {
|
||||
input: {
|
||||
policyVersionId: version.id,
|
||||
content: data.content,
|
||||
},
|
||||
},
|
||||
onSuccess: () => {
|
||||
dialogRef.current?.close();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
ref={dialogRef}
|
||||
title={<Breadcrumb items={[__("Policies"), __("Edit policy")]} />}
|
||||
>
|
||||
{isCreatingDraft && <Spinner centered />}
|
||||
<form onSubmit={onSubmit}>
|
||||
<DialogContent>
|
||||
<Textarea
|
||||
id="content"
|
||||
variant="ghost"
|
||||
autogrow
|
||||
required
|
||||
placeholder={__("Add description")}
|
||||
aria-label={__("Description")}
|
||||
className="p-6"
|
||||
{...register("content")}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<Button disabled={isUpdating} type="submit">
|
||||
{__("Update policy")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<418554f5765131f61a3dfed83db0da76>>
|
||||
* @generated SignedSource<<0471409a1735c49fc0b05b5e63e97853>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -16,11 +16,11 @@ export type CreatePolicyInput = {
|
||||
ownerId: string;
|
||||
title: string;
|
||||
};
|
||||
export type FormPolicyDialogMutation$variables = {
|
||||
export type CreatePolicyDialogMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: CreatePolicyInput;
|
||||
};
|
||||
export type FormPolicyDialogMutation$data = {
|
||||
export type CreatePolicyDialogMutation$data = {
|
||||
readonly createPolicy: {
|
||||
readonly policyEdge: {
|
||||
readonly node: {
|
||||
@@ -29,9 +29,9 @@ export type FormPolicyDialogMutation$data = {
|
||||
};
|
||||
};
|
||||
};
|
||||
export type FormPolicyDialogMutation = {
|
||||
response: FormPolicyDialogMutation$data;
|
||||
variables: FormPolicyDialogMutation$variables;
|
||||
export type CreatePolicyDialogMutation = {
|
||||
response: CreatePolicyDialogMutation$data;
|
||||
variables: CreatePolicyDialogMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
@@ -67,7 +67,7 @@ return {
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "FormPolicyDialogMutation",
|
||||
"name": "CreatePolicyDialogMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
@@ -118,7 +118,7 @@ return {
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "FormPolicyDialogMutation",
|
||||
"name": "CreatePolicyDialogMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
@@ -307,16 +307,16 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "5967e987ab20d7823e7456a22aec54f2",
|
||||
"cacheID": "7ca1b2811e06da7ba97064950fb8f632",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "FormPolicyDialogMutation",
|
||||
"name": "CreatePolicyDialogMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation FormPolicyDialogMutation(\n $input: CreatePolicyInput!\n) {\n createPolicy(input: $input) {\n policyEdge {\n node {\n ...PoliciesPageRowFragment\n id\n }\n }\n }\n}\n\nfragment PoliciesPageRowFragment on Policy {\n id\n title\n description\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
|
||||
"text": "mutation CreatePolicyDialogMutation(\n $input: CreatePolicyInput!\n) {\n createPolicy(input: $input) {\n policyEdge {\n node {\n ...PoliciesPageRowFragment\n id\n }\n }\n }\n}\n\nfragment PoliciesPageRowFragment on Policy {\n id\n title\n description\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "c2612f85b9170d81cb9fb4908f752069";
|
||||
(node as any).hash = "6c749af4def68b742bc5eb80dd120c8a";
|
||||
|
||||
export default node;
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* @generated SignedSource<<929e2bdd729e8b9654b27cd9e9dbccb2>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ReaderFragment } from 'relay-runtime';
|
||||
export type PolicyStatus = "DRAFT" | "PUBLISHED" | "%future added value";
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type PolicyVersionHistoryDialogFragment$data = {
|
||||
readonly changelog: string;
|
||||
readonly content: string;
|
||||
readonly id: string;
|
||||
readonly publishedAt: any | null | undefined;
|
||||
readonly publishedBy: {
|
||||
readonly fullName: string;
|
||||
} | null | undefined;
|
||||
readonly status: PolicyStatus;
|
||||
readonly updatedAt: any;
|
||||
readonly version: number;
|
||||
readonly " $fragmentType": "PolicyVersionHistoryDialogFragment";
|
||||
};
|
||||
export type PolicyVersionHistoryDialogFragment$key = {
|
||||
readonly " $data"?: PolicyVersionHistoryDialogFragment$data;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"PolicyVersionHistoryDialogFragment">;
|
||||
};
|
||||
|
||||
const node: ReaderFragment = {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "PolicyVersionHistoryDialogFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "version",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "status",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "content",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "changelog",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "publishedAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "updatedAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "People",
|
||||
"kind": "LinkedField",
|
||||
"name": "publishedBy",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fullName",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "PolicyVersion",
|
||||
"abstractKey": null
|
||||
};
|
||||
|
||||
(node as any).hash = "63ce12a876b10889d9ff2e4ee20af968";
|
||||
|
||||
export default node;
|
||||
@@ -0,0 +1,256 @@
|
||||
/**
|
||||
* @generated SignedSource<<e90085131e94bcac0f94ecf5b1329712>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type PolicyStatus = "DRAFT" | "PUBLISHED" | "%future added value";
|
||||
export type PolicyVersionSignatureState = "REQUESTED" | "SIGNED" | "%future added value";
|
||||
export type CreateDraftPolicyVersionInput = {
|
||||
policyID: string;
|
||||
};
|
||||
export type UpdateVersionDialogCreateMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: CreateDraftPolicyVersionInput;
|
||||
};
|
||||
export type UpdateVersionDialogCreateMutation$data = {
|
||||
readonly createDraftPolicyVersion: {
|
||||
readonly policyVersionEdge: {
|
||||
readonly node: {
|
||||
readonly content: string;
|
||||
readonly id: string;
|
||||
readonly publishedAt: any | null | undefined;
|
||||
readonly signatures: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly id: string;
|
||||
readonly state: PolicyVersionSignatureState;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly status: PolicyStatus;
|
||||
readonly updatedAt: any;
|
||||
readonly version: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
export type UpdateVersionDialogCreateMutation = {
|
||||
response: UpdateVersionDialogCreateMutation$data;
|
||||
variables: UpdateVersionDialogCreateMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "connections"
|
||||
},
|
||||
v1 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
},
|
||||
v2 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PolicyVersionEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "policyVersionEdge",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PolicyVersion",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "content",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "status",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "publishedAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "version",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "updatedAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 100
|
||||
}
|
||||
],
|
||||
"concreteType": "PolicyVersionSignatureConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "signatures",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PolicyVersionSignatureEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PolicyVersionSignature",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": "signatures(first:100)"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "UpdateVersionDialogCreateMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "CreateDraftPolicyVersionPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createDraftPolicyVersion",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v4/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "UpdateVersionDialogCreateMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "CreateDraftPolicyVersionPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createDraftPolicyVersion",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v4/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"filters": null,
|
||||
"handle": "prependEdge",
|
||||
"key": "",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "policyVersionEdge",
|
||||
"handleArgs": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "connections",
|
||||
"variableName": "connections"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "c5b0f449a70a4043368de55bba176329",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "UpdateVersionDialogCreateMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation UpdateVersionDialogCreateMutation(\n $input: CreateDraftPolicyVersionInput!\n) {\n createDraftPolicyVersion(input: $input) {\n policyVersionEdge {\n node {\n id\n content\n status\n publishedAt\n version\n updatedAt\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "6a451392a86776d33c376bfdcea7613c";
|
||||
|
||||
export default node;
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* @generated SignedSource<<6cbec0c0b675969ecdebebf74c7465ab>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type UpdatePolicyVersionInput = {
|
||||
content: string;
|
||||
policyVersionId: string;
|
||||
};
|
||||
export type UpdateVersionDialogUpdateMutation$variables = {
|
||||
input: UpdatePolicyVersionInput;
|
||||
};
|
||||
export type UpdateVersionDialogUpdateMutation$data = {
|
||||
readonly updatePolicyVersion: {
|
||||
readonly policyVersion: {
|
||||
readonly content: string;
|
||||
readonly id: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
export type UpdateVersionDialogUpdateMutation = {
|
||||
response: UpdateVersionDialogUpdateMutation$data;
|
||||
variables: UpdateVersionDialogUpdateMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "UpdatePolicyVersionPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "updatePolicyVersion",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PolicyVersion",
|
||||
"kind": "LinkedField",
|
||||
"name": "policyVersion",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "content",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "UpdateVersionDialogUpdateMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "UpdateVersionDialogUpdateMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "374557bf1319583b7d590f5bea438e63",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "UpdateVersionDialogUpdateMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation UpdateVersionDialogUpdateMutation(\n $input: UpdatePolicyVersionInput!\n) {\n updatePolicyVersion(input: $input) {\n policyVersion {\n id\n content\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "d863d893bfa969b02768096e024275a4";
|
||||
|
||||
export default node;
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
PropertyRow,
|
||||
IconPlusLarge,
|
||||
Textarea,
|
||||
useDialogRef,
|
||||
} from "@probo/ui";
|
||||
import { useMemo, useState, type ReactNode } from "react";
|
||||
import { useFetchQuery } from "/hooks/useFetchQuery";
|
||||
@@ -37,10 +38,9 @@ import { getRiskImpacts, getRiskLikelihoods } from "@probo/helpers";
|
||||
|
||||
type Props = {
|
||||
trigger?: ReactNode;
|
||||
open?: boolean;
|
||||
risk?: RiskKey;
|
||||
onSuccess?: () => void;
|
||||
connection?: string;
|
||||
ref?: ReturnType<typeof useDialogRef>;
|
||||
};
|
||||
|
||||
type RiskTemplate = {
|
||||
@@ -80,12 +80,12 @@ const updateRiskMutation = graphql`
|
||||
export default function FormRiskDialog({
|
||||
trigger,
|
||||
risk,
|
||||
onSuccess,
|
||||
open,
|
||||
connection,
|
||||
ref: refProps,
|
||||
}: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const organizationId = useOrganizationId();
|
||||
const ref = refProps ?? useDialogRef();
|
||||
|
||||
const { control, handleSubmit, setValue, register, watch, formState, reset } =
|
||||
useRiskForm(risk);
|
||||
@@ -113,8 +113,7 @@ export default function FormRiskDialog({
|
||||
successMessage: __("Risk updated successfully."),
|
||||
errorMessage: __("Failed to update risk. Please try again."),
|
||||
onSuccess: () => {
|
||||
setOpen(false);
|
||||
onSuccess?.();
|
||||
ref?.current?.close();
|
||||
},
|
||||
});
|
||||
return;
|
||||
@@ -130,20 +129,17 @@ export default function FormRiskDialog({
|
||||
successMessage: __("Risk created successfully."),
|
||||
errorMessage: __("Failed to create risk. Please try again."),
|
||||
onSuccess: () => {
|
||||
setOpen(false);
|
||||
ref?.current?.close();
|
||||
reset();
|
||||
onSuccess?.();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const [showNote, toggleNote] = useToggle(false);
|
||||
const [isOpen, setOpen] = useState(!!open);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
onOpenChange={setOpen}
|
||||
ref={ref}
|
||||
trigger={trigger}
|
||||
title={
|
||||
<Breadcrumb
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
PropertyRow,
|
||||
TabLink,
|
||||
Tabs,
|
||||
useConfirmDialogRef,
|
||||
} from "@probo/ui";
|
||||
import { Outlet, useNavigate, useParams } from "react-router";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
@@ -54,17 +55,22 @@ export default function RiskDetailPage(props: Props) {
|
||||
organizationId,
|
||||
RisksConnectionKey
|
||||
);
|
||||
deleteRisk({
|
||||
variables: {
|
||||
input: { riskId },
|
||||
connections: [connectionId],
|
||||
},
|
||||
onSuccess() {
|
||||
navigate(`/organizations/${organizationId}/risks`);
|
||||
},
|
||||
return new Promise<void>((resolve) => {
|
||||
deleteRisk({
|
||||
variables: {
|
||||
input: { riskId },
|
||||
connections: [connectionId],
|
||||
},
|
||||
onSuccess() {
|
||||
navigate(`/organizations/${organizationId}/risks`);
|
||||
resolve();
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const confirmRef = useConfirmDialogRef();
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Header */}
|
||||
@@ -90,23 +96,28 @@ export default function RiskDetailPage(props: Props) {
|
||||
risk={{ id: riskId, ...risk }}
|
||||
/>
|
||||
<ActionDropdown variant="secondary">
|
||||
<ConfirmDialog
|
||||
message={sprintf(
|
||||
__(
|
||||
'This will permanently delete the risk "%s". This action cannot be undone.'
|
||||
),
|
||||
risk.name
|
||||
)}
|
||||
onConfirm={() => onDelete(riskId)}
|
||||
<DropdownItem
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
onClick={() => confirmRef.current?.open()}
|
||||
>
|
||||
<DropdownItem variant="danger" icon={IconTrashCan}>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
</ConfirmDialog>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
</ActionDropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ConfirmDialog
|
||||
ref={confirmRef}
|
||||
message={sprintf(
|
||||
__(
|
||||
'This will permanently delete the risk "%s". This action cannot be undone.'
|
||||
),
|
||||
risk.name
|
||||
)}
|
||||
onConfirm={() => onDelete(riskId)}
|
||||
/>
|
||||
|
||||
<PageHeader title={risk.name} />
|
||||
|
||||
<Tabs>
|
||||
|
||||
@@ -14,6 +14,8 @@ import {
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
useConfirmDialogRef,
|
||||
useDialogRef,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import FormRiskDialog from "./FormRiskDialog";
|
||||
@@ -21,11 +23,12 @@ import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { useState } from "react";
|
||||
import { usePageTitle } from "@probo/hooks";
|
||||
import { getTreatment, sprintf } from "@probo/helpers";
|
||||
import type { ItemOf } from "/types";
|
||||
import type { ItemOf, NodeOf } from "/types";
|
||||
import { useDeleteRiskMutation, useRisksQuery } from "/hooks/graph/RiskGraph";
|
||||
import { SortableTable, SortableTh } from "/components/SortableTable";
|
||||
import type { PreloadedQuery } from "react-relay";
|
||||
import type { RiskGraphListQuery } from "/hooks/graph/__generated__/RiskGraphListQuery.graphql";
|
||||
import type { RiskGraphFragment$data } from "/hooks/graph/__generated__/RiskGraphFragment.graphql";
|
||||
|
||||
type Props = {
|
||||
queryRef: PreloadedQuery<RiskGraphListQuery>;
|
||||
@@ -33,23 +36,10 @@ type Props = {
|
||||
|
||||
export default function RisksPage(props: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const [editedRisk, setEditedRisk] = useState<ItemOf<typeof risks> | null>(
|
||||
null
|
||||
);
|
||||
const organizationId = useOrganizationId();
|
||||
|
||||
const [deleteRisk] = useDeleteRiskMutation();
|
||||
const { connectionId, risks, refetch } = useRisksQuery(props.queryRef);
|
||||
|
||||
const onDelete = (riskId: string) => {
|
||||
deleteRisk({
|
||||
variables: {
|
||||
input: { riskId },
|
||||
connections: [connectionId],
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
usePageTitle(__("Risks"));
|
||||
|
||||
return (
|
||||
@@ -60,14 +50,6 @@ export default function RisksPage(props: Props) {
|
||||
trigger={<Button icon={IconPlusLarge}>{__("New Risk")}</Button>}
|
||||
/>
|
||||
</PageHeader>
|
||||
{editedRisk && (
|
||||
<FormRiskDialog
|
||||
open
|
||||
risk={editedRisk}
|
||||
connection={connectionId}
|
||||
onSuccess={() => setEditedRisk(null)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<RisksChart
|
||||
@@ -98,46 +80,88 @@ export default function RisksPage(props: Props) {
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{risks?.map((risk) => (
|
||||
<Tr
|
||||
<RiskRow
|
||||
risk={risk}
|
||||
key={risk.id}
|
||||
to={`/organizations/${organizationId}/risks/${risk.id}`}
|
||||
>
|
||||
<Td>{risk.name}</Td>
|
||||
<Td>{risk.category}</Td>
|
||||
<Td>{getTreatment(__, risk.treatment)}</Td>
|
||||
<Td>
|
||||
<SeverityBadge score={risk.inherentRiskScore} />
|
||||
</Td>
|
||||
<Td>
|
||||
<SeverityBadge score={risk.residualRiskScore} />
|
||||
</Td>
|
||||
<Td noLink className="text-end">
|
||||
<ActionDropdown>
|
||||
<DropdownItem
|
||||
icon={IconPencil}
|
||||
onClick={() => setEditedRisk(risk)}
|
||||
>
|
||||
{__("Edit")}
|
||||
</DropdownItem>
|
||||
<ConfirmDialog
|
||||
message={sprintf(
|
||||
__(
|
||||
'This will permanently delete the risk "%s". This action cannot be undone.'
|
||||
),
|
||||
risk.name
|
||||
)}
|
||||
onConfirm={() => onDelete(risk.id)}
|
||||
>
|
||||
<DropdownItem variant="danger" icon={IconTrashCan}>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
</ConfirmDialog>
|
||||
</ActionDropdown>
|
||||
</Td>
|
||||
</Tr>
|
||||
connectionId={connectionId}
|
||||
organizationId={organizationId}
|
||||
/>
|
||||
))}
|
||||
</Tbody>
|
||||
</SortableTable>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type RowProps = {
|
||||
risk: NodeOf<RiskGraphFragment$data["risks"]>;
|
||||
connectionId: string;
|
||||
organizationId: string;
|
||||
};
|
||||
|
||||
function RiskRow(props: RowProps) {
|
||||
const { __ } = useTranslate();
|
||||
const { risk, connectionId, organizationId } = props;
|
||||
const [deleteRisk] = useDeleteRiskMutation();
|
||||
const onDelete = (riskId: string) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
deleteRisk({
|
||||
variables: {
|
||||
input: { riskId },
|
||||
connections: [connectionId],
|
||||
},
|
||||
onCompleted: () => resolve(),
|
||||
});
|
||||
});
|
||||
};
|
||||
const confirmRef = useConfirmDialogRef();
|
||||
const formDialogRef = useDialogRef();
|
||||
return (
|
||||
<>
|
||||
<ConfirmDialog
|
||||
ref={confirmRef}
|
||||
message={sprintf(
|
||||
__(
|
||||
'This will permanently delete the risk "%s". This action cannot be undone.'
|
||||
),
|
||||
risk.name
|
||||
)}
|
||||
onConfirm={() => onDelete(risk.id)}
|
||||
/>
|
||||
<FormRiskDialog
|
||||
ref={formDialogRef}
|
||||
risk={risk}
|
||||
connection={connectionId}
|
||||
/>
|
||||
<Tr to={`/organizations/${organizationId}/risks/${risk.id}`}>
|
||||
<Td>{risk.name}</Td>
|
||||
<Td>{risk.category}</Td>
|
||||
<Td>{getTreatment(__, risk.treatment)}</Td>
|
||||
<Td>
|
||||
<SeverityBadge score={risk.inherentRiskScore} />
|
||||
</Td>
|
||||
<Td>
|
||||
<SeverityBadge score={risk.residualRiskScore} />
|
||||
</Td>
|
||||
<Td noLink className="text-end">
|
||||
<ActionDropdown>
|
||||
<DropdownItem
|
||||
icon={IconPencil}
|
||||
onClick={() => confirmRef.current?.open()}
|
||||
>
|
||||
{__("Edit")}
|
||||
</DropdownItem>
|
||||
|
||||
<DropdownItem
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
onClick={() => confirmRef.current?.open()}
|
||||
>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
</ActionDropdown>
|
||||
</Td>
|
||||
</Tr>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
DropdownItem,
|
||||
IconTrashCan,
|
||||
ConfirmDialog,
|
||||
useConfirmDialogRef,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { usePageTitle } from "@probo/hooks";
|
||||
@@ -134,66 +135,77 @@ function VendorRow({
|
||||
const [deleteVendor] = useDeleteVendorMutation();
|
||||
|
||||
const onDelete = (vendorId: string) => {
|
||||
deleteVendor({
|
||||
variables: {
|
||||
input: {
|
||||
vendorId,
|
||||
return new Promise<void>((resolve) => {
|
||||
deleteVendor({
|
||||
variables: {
|
||||
input: {
|
||||
vendorId,
|
||||
},
|
||||
connections: [
|
||||
ConnectionHandler.getConnectionID(
|
||||
organizationId,
|
||||
"VendorsPage_vendors"
|
||||
),
|
||||
],
|
||||
},
|
||||
connections: [
|
||||
ConnectionHandler.getConnectionID(
|
||||
organizationId,
|
||||
"VendorsPage_vendors"
|
||||
),
|
||||
],
|
||||
},
|
||||
onCompleted: () => resolve(),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const confirmRef = useConfirmDialogRef();
|
||||
|
||||
return (
|
||||
<Tr to={`/organizations/${organizationId}/vendors/${vendor.id}`}>
|
||||
<Td>
|
||||
<div className="flex gap-2 items-center">
|
||||
<Avatar name={vendor.name} src={faviconUrl(vendor.websiteUrl)} />
|
||||
<div>{vendor.name}</div>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
{latestAssessment?.assessedAt
|
||||
? dateFormat(latestAssessment.assessedAt, {
|
||||
day: "2-digit",
|
||||
weekday: "short",
|
||||
month: "short",
|
||||
})
|
||||
: __("Not assessed")}
|
||||
</Td>
|
||||
<Td>
|
||||
<RiskBadge level={latestAssessment?.dataSensitivity ?? "NONE"} />
|
||||
</Td>
|
||||
<Td>
|
||||
<RiskBadge level={latestAssessment?.businessImpact ?? "NONE"} />
|
||||
</Td>
|
||||
<Td>
|
||||
<Badge variant={isExpired ? "danger" : "warning"}>
|
||||
{isExpired ? __("Late") : __("In progress")}
|
||||
</Badge>
|
||||
</Td>
|
||||
<Td noLink>
|
||||
<ActionDropdown>
|
||||
<ConfirmDialog
|
||||
message={sprintf(
|
||||
__(
|
||||
'This will permanently delete the vendor "%s". This action cannot be undone.'
|
||||
),
|
||||
vendor.name
|
||||
)}
|
||||
onConfirm={() => onDelete(vendor.id)}
|
||||
>
|
||||
<DropdownItem variant="danger" icon={IconTrashCan}>
|
||||
<>
|
||||
<ConfirmDialog
|
||||
ref={confirmRef}
|
||||
message={sprintf(
|
||||
__(
|
||||
'This will permanently delete the vendor "%s". This action cannot be undone.'
|
||||
),
|
||||
vendor.name
|
||||
)}
|
||||
onConfirm={() => onDelete(vendor.id)}
|
||||
/>
|
||||
<Tr to={`/organizations/${organizationId}/vendors/${vendor.id}`}>
|
||||
<Td>
|
||||
<div className="flex gap-2 items-center">
|
||||
<Avatar name={vendor.name} src={faviconUrl(vendor.websiteUrl)} />
|
||||
<div>{vendor.name}</div>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
{latestAssessment?.assessedAt
|
||||
? dateFormat(latestAssessment.assessedAt, {
|
||||
day: "2-digit",
|
||||
weekday: "short",
|
||||
month: "short",
|
||||
})
|
||||
: __("Not assessed")}
|
||||
</Td>
|
||||
<Td>
|
||||
<RiskBadge level={latestAssessment?.dataSensitivity ?? "NONE"} />
|
||||
</Td>
|
||||
<Td>
|
||||
<RiskBadge level={latestAssessment?.businessImpact ?? "NONE"} />
|
||||
</Td>
|
||||
<Td>
|
||||
<Badge variant={isExpired ? "danger" : "warning"}>
|
||||
{isExpired ? __("Late") : __("In progress")}
|
||||
</Badge>
|
||||
</Td>
|
||||
<Td noLink>
|
||||
<ActionDropdown>
|
||||
<DropdownItem
|
||||
onClick={() => confirmRef.current?.open()}
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
>
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
</ConfirmDialog>
|
||||
</ActionDropdown>
|
||||
</Td>
|
||||
</Tr>
|
||||
</ActionDropdown>
|
||||
</Td>
|
||||
</Tr>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user