From a052d6f4814e28708b409ec13231271151142f65 Mon Sep 17 00:00:00 2001 From: Sacha Al Himdani Date: Thu, 7 May 2026 14:59:14 +0200 Subject: [PATCH] Allow editing SOA generated document approvers inline Adds an "Approvers" row to the SOA details card with the same inline edit pattern as DocumentDetailsCard, calling updateDocument with defaultApproverIds. Visible only after first publish, when the SOA has an associated document. Signed-off-by: Sacha Al Himdani --- .../StatementOfApplicabilityDetailPage.tsx | 134 +++++++++++++++++- 1 file changed, 132 insertions(+), 2 deletions(-) diff --git a/apps/console/src/pages/organizations/statements-of-applicability/StatementOfApplicabilityDetailPage.tsx b/apps/console/src/pages/organizations/statements-of-applicability/StatementOfApplicabilityDetailPage.tsx index a060ba877..fbccf0b50 100644 --- a/apps/console/src/pages/organizations/statements-of-applicability/StatementOfApplicabilityDetailPage.tsx +++ b/apps/console/src/pages/organizations/statements-of-applicability/StatementOfApplicabilityDetailPage.tsx @@ -45,7 +45,9 @@ import { z } from "zod"; import type { StatementOfApplicabilityDetailPageDeleteMutation } from "#/__generated__/core/StatementOfApplicabilityDetailPageDeleteMutation.graphql"; import type { StatementOfApplicabilityDetailPageQuery } from "#/__generated__/core/StatementOfApplicabilityDetailPageQuery.graphql"; +import type { StatementOfApplicabilityDetailPageUpdateApproversMutation } from "#/__generated__/core/StatementOfApplicabilityDetailPageUpdateApproversMutation.graphql"; import type { StatementOfApplicabilityDetailPageUpdateMutation } from "#/__generated__/core/StatementOfApplicabilityDetailPageUpdateMutation.graphql"; +import { PeopleMultiSelectField } from "#/components/form/PeopleMultiSelectField"; import { useFormWithSchema } from "#/hooks/useFormWithSchema"; import { useOrganizationId } from "#/hooks/useOrganizationId"; @@ -62,8 +64,11 @@ export const statementOfApplicabilityDetailPageQuery = graphql` updatedAt document { id + canUpdateDocument: permission(action: "core:document:update") defaultApprovers { id + fullName + emailAddress } } canUpdate: permission(action: "core:statement-of-applicability:update") @@ -101,6 +106,27 @@ const deleteMutation = graphql` } `; +const updateApproversMutation = graphql` + mutation StatementOfApplicabilityDetailPageUpdateApproversMutation( + $input: UpdateDocumentInput! + ) { + updateDocument(input: $input) { + document { + id + defaultApprovers { + id + fullName + emailAddress + } + } + } + } +`; + +const approversSchema = z.object({ + approverIds: z.array(z.string()), +}); + const StatementOfApplicabilityConnectionKey = "StatementsOfApplicabilityPage_statementsOfApplicability"; type Props = { @@ -233,7 +259,59 @@ export default function StatementOfApplicabilityDetailPage(props: Props) { }); }; - const defaultApproverIds = (statementOfApplicability.document?.defaultApprovers ?? []).map(a => a.id); + const defaultApprovers = statementOfApplicability.document?.defaultApprovers ?? []; + const defaultApproverIds = defaultApprovers.map(a => a.id); + const documentId = statementOfApplicability.document?.id; + const canUpdateDocument = statementOfApplicability.document?.canUpdateDocument ?? false; + + const [isEditingApprovers, setIsEditingApprovers] = useState(false); + const { + control: approversControl, + handleSubmit: handleApproversSubmit, + reset: resetApprovers, + } = useFormWithSchema(approversSchema, { + values: { + approverIds: defaultApproverIds, + }, + }); + + const [updateApprovers, isUpdatingApprovers] + = useMutation(updateApproversMutation); + + const handleUpdateApprovers = handleApproversSubmit((data) => { + if (!documentId) return; + updateApprovers({ + variables: { + input: { + id: documentId, + defaultApproverIds: data.approverIds, + }, + }, + onCompleted() { + toast({ + title: __("Success"), + description: __("Approvers updated successfully."), + variant: "success", + }); + setIsEditingApprovers(false); + }, + onError(error) { + toast({ + title: __("Error"), + description: formatError( + __("Failed to update approvers"), + error as GraphQLError, + ), + variant: "error", + }); + }, + }); + }); + + const handleCancelApproversEdit = () => { + setIsEditingApprovers(false); + resetApprovers({ approverIds: defaultApproverIds }); + }; const listUrl = `/organizations/${organizationId}/statements-of-applicability`; @@ -343,7 +421,7 @@ export default function StatementOfApplicabilityDetailPage(props: Props) {

{__("Details")}

-
+
{__("Created at")} @@ -360,6 +438,58 @@ export default function StatementOfApplicabilityDetailPage(props: Props) { {formatDate(statementOfApplicability.updatedAt)}
+ {documentId && ( +
+
+ {__("Approvers")} +
+ {isEditingApprovers + ? ( +
+
+ ({ + id: a.id, + fullName: a.fullName, + emailAddress: a.emailAddress, + }))} + placeholder={__("Add approvers...")} + /> +
+
+ ) + : ( +
+
+ {defaultApprovers.length > 0 + ? defaultApprovers.map(a => a.fullName).join(", ") + : __("None")} +
+ {canUpdateDocument && ( +
+ )} +
+ )}