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:
@@ -47,6 +47,36 @@ func Required() ValidatorFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// NoDuplicates validates that a slice contains no duplicate elements.
|
||||
func NoDuplicates() ValidatorFunc {
|
||||
return func(value any) *ValidationError {
|
||||
actualValue, isNil := dereferenceValue(value)
|
||||
if isNil {
|
||||
return nil
|
||||
}
|
||||
|
||||
rv := reflect.ValueOf(actualValue)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return newValidationError(ErrorCodeInvalidFormat, "value must be a slice")
|
||||
}
|
||||
|
||||
if !rv.Type().Elem().Comparable() {
|
||||
return newValidationError(ErrorCodeInvalidFormat, "slice elements must be comparable")
|
||||
}
|
||||
|
||||
seen := make(map[any]struct{}, rv.Len())
|
||||
for i := range rv.Len() {
|
||||
elem := rv.Index(i).Interface()
|
||||
if _, ok := seen[elem]; ok {
|
||||
return newValidationError(ErrorCodeInvalidFormat, "must not contain duplicates")
|
||||
}
|
||||
seen[elem] = struct{}{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NotEmpty validates that a field is not empty.
|
||||
// Similar to Required, but can be used independently.
|
||||
func NotEmpty() ValidatorFunc {
|
||||
|
||||
Reference in New Issue
Block a user