feat(schema-workflow): Phase 1b — SchemaDraftDrawer with status-aware actions
This commit is contained in:
@@ -7,11 +7,17 @@ import {
|
||||
type CascadeCloseResult,
|
||||
type CreateDictionaryRequest,
|
||||
type CreateRecordRequest,
|
||||
type CreateSchemaDraftRequest,
|
||||
type CreateWebhookSubscriptionRequest,
|
||||
type DecisionRequest,
|
||||
type DictionaryDetail,
|
||||
type DraftResponse,
|
||||
type PublishSchemaDraftRequest,
|
||||
type PublishSchemaDraftResult,
|
||||
type RecordResponse,
|
||||
type SchemaDraft,
|
||||
type SubmitDraftRequest,
|
||||
type SubmitForReviewRequest,
|
||||
type WebhookDelivery,
|
||||
type WebhookSubscription,
|
||||
type WebhookTestPingResult,
|
||||
@@ -480,6 +486,136 @@ export const useWithdrawDraft = () => {
|
||||
})
|
||||
}
|
||||
|
||||
// === Schema workflow mutations (Phase 1a, v2.2.0 backend) =============
|
||||
|
||||
/**
|
||||
* Создаёт schema draft от current HEAD. Backend проверяет fromVersion
|
||||
* против live schemaVersion (409 version_mismatch если расходится).
|
||||
* Также 409 active_draft_exists если на dict'е уже есть non-terminal draft.
|
||||
*
|
||||
* <p>onSuccess invalidates: active-draft query (banner picks up) +
|
||||
* drafts list + reviewer pool queue.
|
||||
*/
|
||||
export const useCreateSchemaDraft = (dictionaryName: string) => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: async (payload: CreateSchemaDraftRequest): Promise<SchemaDraft> => {
|
||||
const { data } = await apiClient.post<SchemaDraft>(
|
||||
`/dictionaries/${dictionaryName}/drafts`,
|
||||
payload,
|
||||
)
|
||||
return data
|
||||
},
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['schema-draft-active', dictionaryName] })
|
||||
qc.invalidateQueries({ queryKey: ['schema-drafts', dictionaryName] })
|
||||
qc.invalidateQueries({ queryKey: ['schema-review-queue'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/** Maker submits draft → review_pending. 202 Accepted. */
|
||||
export const useSubmitSchemaDraftForReview = (dictionaryName: string) => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: async (params: {
|
||||
draftId: string
|
||||
body?: SubmitForReviewRequest
|
||||
}): Promise<SchemaDraft> => {
|
||||
const { data } = await apiClient.post<SchemaDraft>(
|
||||
`/dictionaries/${dictionaryName}/drafts/${params.draftId}/review`,
|
||||
params.body ?? {},
|
||||
)
|
||||
return data
|
||||
},
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['schema-draft-active', dictionaryName] })
|
||||
qc.invalidateQueries({ queryKey: ['schema-draft', dictionaryName] })
|
||||
qc.invalidateQueries({ queryKey: ['schema-drafts', dictionaryName] })
|
||||
qc.invalidateQueries({ queryKey: ['schema-review-queue'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Reviewer decide: approve / request_changes / reject. Backend
|
||||
* проверяет self_approve_forbidden (maker_id != actor) → 403.
|
||||
*/
|
||||
export const useDecideSchemaDraft = (dictionaryName: string) => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: async (params: {
|
||||
draftId: string
|
||||
body: DecisionRequest
|
||||
}): Promise<SchemaDraft> => {
|
||||
const { data } = await apiClient.post<SchemaDraft>(
|
||||
`/dictionaries/${dictionaryName}/drafts/${params.draftId}/decision`,
|
||||
params.body,
|
||||
)
|
||||
return data
|
||||
},
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['schema-draft-active', dictionaryName] })
|
||||
qc.invalidateQueries({ queryKey: ['schema-draft', dictionaryName] })
|
||||
qc.invalidateQueries({ queryKey: ['schema-drafts', dictionaryName] })
|
||||
qc.invalidateQueries({ queryKey: ['schema-review-queue'] })
|
||||
// Changelog gets new entry (draft_approve / draft_reject / draft_changes_requested)
|
||||
qc.invalidateQueries({ queryKey: ['changelog', dictionaryName] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish approved draft → bump live HEAD. Backend computes new version
|
||||
* (minor default, major на breaking). Returns wrapped {draft, newVersion, publishedAt}.
|
||||
*
|
||||
* <p>onSuccess invalidates dict detail (schema/version changed) + changelog +
|
||||
* draft queries.
|
||||
*/
|
||||
export const usePublishSchemaDraft = (dictionaryName: string) => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: async (params: {
|
||||
draftId: string
|
||||
body: PublishSchemaDraftRequest
|
||||
}): Promise<PublishSchemaDraftResult> => {
|
||||
const { data } = await apiClient.post<PublishSchemaDraftResult>(
|
||||
`/dictionaries/${dictionaryName}/drafts/${params.draftId}/publish`,
|
||||
params.body,
|
||||
)
|
||||
return data
|
||||
},
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['schema-draft-active', dictionaryName] })
|
||||
qc.invalidateQueries({ queryKey: ['schema-draft', dictionaryName] })
|
||||
qc.invalidateQueries({ queryKey: ['schema-drafts', dictionaryName] })
|
||||
qc.invalidateQueries({ queryKey: ['schema-review-queue'] })
|
||||
qc.invalidateQueries({ queryKey: ['dictionary', dictionaryName] })
|
||||
qc.invalidateQueries({ queryKey: ['changelog', dictionaryName] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/** Maker withdraws non-terminal draft (terminal state). */
|
||||
export const useWithdrawSchemaDraft = (dictionaryName: string) => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: async (draftId: string): Promise<SchemaDraft> => {
|
||||
const { data } = await apiClient.delete<SchemaDraft>(
|
||||
`/dictionaries/${dictionaryName}/drafts/${draftId}`,
|
||||
)
|
||||
return data
|
||||
},
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['schema-draft-active', dictionaryName] })
|
||||
qc.invalidateQueries({ queryKey: ['schema-draft', dictionaryName] })
|
||||
qc.invalidateQueries({ queryKey: ['schema-drafts', dictionaryName] })
|
||||
qc.invalidateQueries({ queryKey: ['schema-review-queue'] })
|
||||
qc.invalidateQueries({ queryKey: ['changelog', dictionaryName] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const useRetryWebhookDelivery = () => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
|
||||
Reference in New Issue
Block a user