Redesign document approval flow

Replace the per-approver add/remove model with a quorum-based approval
system. Documents now have default approvers that are pre-populated when
requesting approval, and the publish dialog lets users adjust the list
before submitting.

Key changes:

- Add PENDING_APPROVAL document version status with dedicated transitions
- Introduce approval quorums with request/approve/reject/void lifecycle
- Add default approvers per document (stored in document_default_approvers)
  with MERGE-based upsert for efficient sync
- Add NoDuplicates validator for slice fields
- Split ALTER TYPE ADD VALUE migrations into separate files (required by
  PostgreSQL when run inside transactions)
- Use VOIDED consistently for both quorum status and decision state enums
- Expose void/approve/reject through GraphQL and MCP, with e2e tests
- Add approval management UI: publish dialog with approver selection,
  approval list with void support, and external approve/reject page

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2026-04-09 11:17:51 +02:00
parent 17f579b8a2
commit ab5f42ad74
38 changed files with 2125 additions and 922 deletions

View File

@@ -104,6 +104,10 @@ enum DocumentVersionStatus
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionStatusDraft"
)
PENDING_APPROVAL
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionStatusPendingApproval"
)
PUBLISHED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionStatusPublished"
@@ -2583,6 +2587,8 @@ type Document implements Node {
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
defaultApprovers: [Profile!]! @goField(forceResolver: true)
status: DocumentStatus!
archivedAt: Datetime
@@ -3934,6 +3940,9 @@ type Mutation {
requestDocumentVersionApproval(
input: RequestDocumentVersionApprovalInput!
): RequestDocumentVersionApprovalPayload!
voidDocumentVersionApproval(
input: VoidDocumentVersionApprovalInput!
): VoidDocumentVersionApprovalPayload!
bulkDeleteDocuments(
input: BulkDeleteDocumentsInput!
): BulkDeleteDocumentsPayload!
@@ -3969,12 +3978,6 @@ type Mutation {
input: CancelSignatureRequestInput!
): CancelSignatureRequestPayload!
signDocument(input: SignDocumentInput!): SignDocumentPayload!
addDocumentVersionApprover(
input: AddDocumentVersionApproverInput!
): AddDocumentVersionApproverPayload!
removeDocumentVersionApprover(
input: RemoveDocumentVersionApproverInput!
): RemoveDocumentVersionApproverPayload!
approveDocumentVersion(
input: ApproveDocumentVersionInput!
): ApproveDocumentVersionPayload!
@@ -4668,6 +4671,7 @@ input CreateDocumentInput {
documentType: DocumentType!
classification: DocumentClassification!
trustCenterVisibility: TrustCenterVisibility
defaultApproverIds: [ID!]
}
input UpdateDocumentInput {
@@ -4675,6 +4679,7 @@ input UpdateDocumentInput {
title: String
content: String
trustCenterVisibility: TrustCenterVisibility
defaultApproverIds: [ID!]
}
input ExportDocumentVersionPDFInput {
@@ -5686,6 +5691,10 @@ enum DocumentVersionApprovalDecisionState
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalDecisionStateRejected"
)
VOIDED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalDecisionStateVoided"
)
}
enum DocumentVersionApprovalDecisionOrderField
@@ -5714,6 +5723,10 @@ enum DocumentVersionApprovalQuorumStatus
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalQuorumStatusRejected"
)
VOIDED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalQuorumStatusVoided"
)
}
enum DocumentVersionApprovalQuorumOrderField
@@ -5816,23 +5829,6 @@ type RejectDocumentVersionPayload {
approvalDecision: DocumentVersionApprovalDecision!
}
input AddDocumentVersionApproverInput {
documentVersionId: ID!
approverId: ID!
}
type AddDocumentVersionApproverPayload {
approvalDecisionEdge: DocumentVersionApprovalDecisionEdge!
}
input RemoveDocumentVersionApproverInput {
approvalDecisionId: ID!
}
type RemoveDocumentVersionApproverPayload {
deletedApprovalDecisionId: ID!
documentVersion: DocumentVersion!
}
input RequestSignatureInput {
documentVersionId: ID!
@@ -5897,6 +5893,15 @@ type RequestDocumentVersionApprovalPayload {
approvalQuorum: DocumentVersionApprovalQuorum!
}
input VoidDocumentVersionApprovalInput {
documentVersionId: ID!
}
type VoidDocumentVersionApprovalPayload {
approvalQuorum: DocumentVersionApprovalQuorum!
documentVersion: DocumentVersion!
}
input PublishMajorDocumentVersionInput {
documentId: ID!
changelog: String