diff --git a/apps/console2/package.json b/apps/console2/package.json index a1ad45b18..b49abeee9 100644 --- a/apps/console2/package.json +++ b/apps/console2/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "vite", - "relay": "npx relay-compiler", + "relay": "find src -type d -name \"__generated\" -exec rm -rf {} + && npx relay-compiler", "build": "tsc -b && vite build", "lint": "eslint .", "check": "tsc --noEmit -p tsconfig.app.json", diff --git a/apps/console2/src/components/form/ControlledField.tsx b/apps/console2/src/components/form/ControlledField.tsx index 353c72ceb..005d455ca 100644 --- a/apps/console2/src/components/form/ControlledField.tsx +++ b/apps/console2/src/components/form/ControlledField.tsx @@ -20,7 +20,6 @@ export function ControlledField({ name={name} render={({ field }) => ( <> - {/* @ts-expect-error field is too dynamic */} > + * @generated SignedSource<> * @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" } }; })(); diff --git a/apps/console2/src/hooks/useMutationWithToasts.ts b/apps/console2/src/hooks/useMutationWithToasts.ts index 71a3d2f2f..125dfe400 100644 --- a/apps/console2/src/hooks/useMutationWithToasts.ts +++ b/apps/console2/src/hooks/useMutationWithToasts.ts @@ -28,7 +28,8 @@ export function useMutationWithToasts( const options = { ...baseOptions, ...queryOptions }; mutate({ ...options, - onCompleted: (_, error) => { + onCompleted: (response, error) => { + options.onCompleted?.(response, error); if (error) { toast({ title: __("Error"), diff --git a/apps/console2/src/pages/organizations/measures/MeasuresPage.tsx b/apps/console2/src/pages/organizations/measures/MeasuresPage.tsx index 57093b6db..760398d8e 100644 --- a/apps/console2/src/pages/organizations/measures/MeasuresPage.tsx +++ b/apps/console2/src/pages/organizations/measures/MeasuresPage.tsx @@ -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} /> ))} @@ -165,6 +167,7 @@ export default function MeasuresPage(props: Props) { type CategoryProps = { category: string; measures: NodeOf[]; + connectionId: string; }; function Category(props: CategoryProps) { @@ -210,7 +213,11 @@ function Category(props: CategoryProps) { {measures.map((measure) => ( - + ))} @@ -232,6 +239,7 @@ function Category(props: CategoryProps) { type MeasureRowProps = { measure: NodeOf; + 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((resolve) => { + deleteMeasure({ + variables: { + input: { measureId: props.measure.id }, + connections: [props.connectionId], + }, + onCompleted: () => resolve(), + }); }); }; + const confirmRef = useConfirmDialogRef(); + return ( - - {props.measure.name} - - - - - - {__("Edit")} - onDelete()} - > + <> + + + {props.measure.name} + + + + + + {__("Edit")} confirmRef.current?.open()} disabled={isDeleting} variant="danger" icon={IconTrashCan} > {__("Delete")} - - - - + + + + ); } diff --git a/apps/console2/src/pages/organizations/policies/PoliciesPage.tsx b/apps/console2/src/pages/organizations/policies/PoliciesPage.tsx index 68b9b3721..54f9f338d 100644 --- a/apps/console2/src/pages/organizations/policies/PoliciesPage.tsx +++ b/apps/console2/src/pages/organizations/policies/PoliciesPage.tsx @@ -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")} > - {__("New policy")}} /> @@ -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((resolve) => { + deletePolicy({ + variables: { + input: { policyId: policy.id }, + connections: [connectionId], + }, + onCompleted: () => resolve(), + }); }); }; return ( - - {policy.title} - - - {isDraft ? __("Draft") : __("Published")} - - - -
- - {policy.owner.fullName} -
- - - {dateFormat(policy.updatedAt, { - year: "numeric", - month: "short", - day: "numeric", - weekday: "short", - })} - - - {signedCount}/{signatures.length} - - - - {__("Edit")} + <> + + {policy.title} + + + {isDraft ? __("Draft") : __("Published")} + + + +
+ + {policy.owner.fullName} +
+ + + {dateFormat(policy.updatedAt, { + year: "numeric", + month: "short", + day: "numeric", + weekday: "short", + })} + + + {signedCount}/{signatures.length} + + - - {__("Delete")} - + + + + + updateDialogRef.current?.open()} + icon={IconPencil} + > + {isDraft ? __("Edit draft policy") : __("Create new draft")} + + confirmRef.current?.open()} + > + {__("Delete")} + + @@ -147,11 +231,11 @@ export default function PolicyPage(props: Props) { - {lastVersion.status === "DRAFT" ? __("Draft") : __("Published")} + {isDraft ? __("Draft") : __("Published")} diff --git a/apps/console2/src/pages/organizations/policies/__generated__/PolicyPageCreateDraftPolicyVersionMutation.graphql.ts b/apps/console2/src/pages/organizations/policies/__generated__/PolicyPageCreateDraftPolicyVersionMutation.graphql.ts new file mode 100644 index 000000000..76e9164c3 --- /dev/null +++ b/apps/console2/src/pages/organizations/policies/__generated__/PolicyPageCreateDraftPolicyVersionMutation.graphql.ts @@ -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; diff --git a/apps/console2/src/pages/organizations/policies/__generated__/PolicyPagePolicyFragment.graphql.ts b/apps/console2/src/pages/organizations/policies/__generated__/PolicyPagePolicyFragment.graphql.ts index 6ab993382..6b02e64e4 100644 --- a/apps/console2/src/pages/organizations/policies/__generated__/PolicyPagePolicyFragment.graphql.ts +++ b/apps/console2/src/pages/organizations/policies/__generated__/PolicyPagePolicyFragment.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @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; diff --git a/apps/console2/src/pages/organizations/policies/__generated__/PolicyPagePolicyQuery.graphql.ts b/apps/console2/src/pages/organizations/policies/__generated__/PolicyPagePolicyQuery.graphql.ts deleted file mode 100644 index 57ce03bb0..000000000 --- a/apps/console2/src/pages/organizations/policies/__generated__/PolicyPagePolicyQuery.graphql.ts +++ /dev/null @@ -1,249 +0,0 @@ -/** - * @generated SignedSource<> - * @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; diff --git a/apps/console2/src/pages/organizations/policies/__generated__/PolicyPagePolicyRefetch.graphql.ts b/apps/console2/src/pages/organizations/policies/__generated__/PolicyPagePolicyRefetch.graphql.ts deleted file mode 100644 index a313c7fc5..000000000 --- a/apps/console2/src/pages/organizations/policies/__generated__/PolicyPagePolicyRefetch.graphql.ts +++ /dev/null @@ -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; diff --git a/apps/console2/src/pages/organizations/policies/FormPolicyDialog.tsx b/apps/console2/src/pages/organizations/policies/dialogs/CreatePolicyDialog.tsx similarity index 84% rename from apps/console2/src/pages/organizations/policies/FormPolicyDialog.tsx rename to apps/console2/src/pages/organizations/policies/dialogs/CreatePolicyDialog.tsx index 1769ba24a..d00b4a377 100644 --- a/apps/console2/src/pages/organizations/policies/FormPolicyDialog.tsx +++ b/apps/console2/src/pages/organizations/policies/dialogs/CreatePolicyDialog.tsx @@ -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(createPolicyMutation); + useMutationWithToasts(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 ( } > diff --git a/apps/console2/src/pages/organizations/policies/dialogs/PolicyVersionHistoryDialog.tsx b/apps/console2/src/pages/organizations/policies/dialogs/PolicyVersionHistoryDialog.tsx new file mode 100644 index 000000000..5bb226814 --- /dev/null +++ b/apps/console2/src/pages/organizations/policies/dialogs/PolicyVersionHistoryDialog.tsx @@ -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; + +export function PolicyVersionHistoryDialog(props: Props) { + const { __ } = useTranslate(); + const versions = props.policy.versions.edges.map((edge) => edge.node); + const [selectedVersion, setSelectedVersion] = useState(versions[0]); + return ( + + + +
+ +
+
+ +
+ ); +} + +function VersionItem(props: { + version: Version; + active?: boolean; + onSelect: (v: Version) => void; +}) { + const version = useFragment( + historyFragment, + props.version + ); + const { dateFormat } = useTranslate(); + return ( + + ); +} diff --git a/apps/console2/src/pages/organizations/policies/dialogs/UpdateVersionDialog.tsx b/apps/console2/src/pages/organizations/policies/dialogs/UpdateVersionDialog.tsx new file mode 100644 index 000000000..530fc5751 --- /dev/null +++ b/apps/console2/src/pages/organizations/policies/dialogs/UpdateVersionDialog.tsx @@ -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(createDraftPolicy); + const [updatePolicyVersion, isUpdating] = + useMutationWithToasts( + 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 ( + } + > + {isCreatingDraft && } +
+ +