Release govoplan-campaign v0.1.28: stabilize saving, review and delivery recovery
Module Package Release / publish-packages (push) Successful in 12s

This commit is contained in:
2026-09-08 01:32:26 +02:00
parent 1b32427813
commit c51fc180fb
111 changed files with 6905 additions and 980 deletions
+44 -10
View File
@@ -5,6 +5,7 @@ import {
type ReferenceOptionProvider
} from "@govoplan/core-webui";
import { campaignJobsQueryParams, type CampaignJobsQueryParameters } from "../features/campaigns/utils/jobListQuery";
import { campaignVersionUpdateForRequest } from "../features/campaigns/utils/editorState";
export {
fetchResourceAccessExplanation,
fetchResourceAccessExplanationSubjects
@@ -340,6 +341,7 @@ export type CampaignScheduleCreate = {
};
export type CampaignVersionDetail = CampaignVersionListItem & {
review_build_token?: string | null;
raw_json: Record<string, unknown>;
campaign_json?: Record<string, unknown>;
mail_profile_migration_required?: boolean;
@@ -936,6 +938,7 @@ export type CampaignSendNowPayload = {
};
export type CampaignAppendSentPayload = {
version_id?: string;
dry_run?: boolean;
enqueue_celery?: boolean;
run_inline?: boolean;
@@ -1017,6 +1020,7 @@ export type CampaignAttachmentLinkMatchesResponse = {
};
export type CampaignMockSendPayload = {
use_reviewed_build?: boolean;
version_id?: string | null;
send?: boolean;
include_warnings?: boolean;
@@ -1027,6 +1031,10 @@ export type CampaignMockSendPayload = {
};
export type CampaignReviewStatePayload = {
merge_progress?: boolean;
build_token?: string | null;
base_revision?: number | null;
decision_category_key?: string | null;
inspection_complete: boolean;
reviewed_message_keys: string[];
issue_decisions: Array<{
@@ -1517,7 +1525,8 @@ options: CampaignWorkspaceQuery = {})
export async function getCampaignWorkspaceDelta(
settings: ApiSettings,
campaignId: string,
options: CampaignWorkspaceQuery = {})
options: CampaignWorkspaceQuery = {},
requestOptions?: Pick<RequestInit, "cache" | "signal">)
: Promise<CampaignWorkspaceDeltaResponse> {
const params = new URLSearchParams();
if (options.versionId) params.set("version_id", options.versionId);
@@ -1527,7 +1536,7 @@ options: CampaignWorkspaceQuery = {})
if (options.since) params.set("since", options.since);
if (options.limit) params.set("limit", String(options.limit));
const suffix = params.size > 0 ? `?${params.toString()}` : "";
return apiFetch<CampaignWorkspaceDeltaResponse>(settings, `/api/v1/campaigns/${campaignId}/workspace/delta${suffix}`);
return apiFetch<CampaignWorkspaceDeltaResponse>(settings, `/api/v1/campaigns/${campaignId}/workspace/delta${suffix}`, requestOptions);
}
export async function listCampaignVersions(
@@ -1542,7 +1551,9 @@ settings: ApiSettings,
campaignId: string,
versionId: string)
: Promise<CampaignVersionDetail> {
return apiFetch<CampaignVersionDetail>(settings, `/api/v1/campaigns/${campaignId}/versions/${versionId}`);
// Conflict reconciliation and discard/reload require the current revision,
// never an older coalesced or short-lived cached read.
return apiFetch<CampaignVersionDetail>(settings, `/api/v1/campaigns/${campaignId}/versions/${versionId}`, { cache: "no-store" });
}
export async function unlockCampaignVersionValidation(
@@ -1595,7 +1606,7 @@ ifMatch: string)
return apiFetch<CampaignVersionDetail>(settings, `/api/v1/campaigns/${campaignId}/versions/${versionId}`, {
method: "PUT",
headers: { "If-Match": ifMatch },
body: JSON.stringify(payload)
body: JSON.stringify(campaignVersionUpdateForRequest(payload))
});
}
@@ -1607,7 +1618,7 @@ payload: CampaignVersionUpdatePayload = {})
: Promise<CampaignCreateResponse> {
return apiFetch<CampaignCreateResponse>(settings, `/api/v1/campaigns/${campaignId}/versions/${versionId}/fork`, {
method: "POST",
body: JSON.stringify(payload)
body: JSON.stringify(campaignVersionUpdateForRequest(payload))
});
}
@@ -1621,7 +1632,7 @@ ifMatch: string)
return apiFetch<CampaignVersionDetail>(settings, `/api/v1/campaigns/${campaignId}/versions/${versionId}/autosave`, {
method: "POST",
headers: { "If-Match": ifMatch },
body: JSON.stringify(payload)
body: JSON.stringify(campaignVersionUpdateForRequest(payload))
});
}
@@ -1719,24 +1730,46 @@ versionId?: string)
return apiFetch<CampaignSummary>(settings, `/api/v1/campaigns/${campaignId}/summary${suffix}`);
}
export type CampaignDeliveryProgress = {
delivery_mode?: string | null;
workflow_state?: string;
campaign_id: string;
version_id: string;
generated_at: string;
total_jobs: number;
smtp: { total: number; processed: number; accepted: number; active: number; pending: number; failed: number; outcome_unknown: number; excluded: number; paused: number; cancelled: number };
imap: { total: number; processed: number; appended: number; active: number; pending: number; failed: number; outcome_unknown: number; excluded: number };
status_counts: { send: Record<string, number>; queue: Record<string, number>; imap: Record<string, number> };
};
export async function getCampaignDeliveryProgress(
settings: ApiSettings, campaignId: string, versionId: string, signal?: AbortSignal
): Promise<CampaignDeliveryProgress> {
return apiFetch<CampaignDeliveryProgress>(settings,
`/api/v1/campaigns/${campaignId}/delivery-progress?version_id=${encodeURIComponent(versionId)}`,
{ cache: "no-store", signal });
}
export async function getCampaignJobs(
settings: ApiSettings,
campaignId: string,
options: CampaignJobsQuery = {})
options: CampaignJobsQuery = {},
init?: Pick<RequestInit, "cache" | "signal">)
: Promise<CampaignJobsResponse> {
const params = campaignJobsQueryParams(options);
const suffix = params.size > 0 ? `?${params.toString()}` : "";
return apiFetch<CampaignJobsResponse>(settings, `/api/v1/campaigns/${campaignId}/jobs${suffix}`);
return apiFetch<CampaignJobsResponse>(settings, `/api/v1/campaigns/${campaignId}/jobs${suffix}`, init);
}
export async function getCampaignJobsDelta(
settings: ApiSettings,
campaignId: string,
options: CampaignJobsQuery & {since?: string | null;limit?: number;} = {})
options: CampaignJobsQuery & {since?: string | null;limit?: number;} = {},
init?: Pick<RequestInit, "cache" | "signal">)
: Promise<CampaignJobsDeltaResponse> {
const params = campaignJobsQueryParams(options);
const suffix = params.size > 0 ? `?${params.toString()}` : "";
return apiFetch<CampaignJobsDeltaResponse>(settings, `/api/v1/campaigns/${campaignId}/jobs/delta${suffix}`);
return apiFetch<CampaignJobsDeltaResponse>(settings, `/api/v1/campaigns/${campaignId}/jobs/delta${suffix}`, init);
}
export async function getCampaignJobDetail(
@@ -1941,6 +1974,7 @@ payload: CampaignAppendSentPayload = {})
return apiFetch<Record<string, unknown>>(settings, `/api/v1/campaigns/${campaignId}/append-sent`, {
method: "POST",
body: JSON.stringify({
version_id: payload.version_id,
dry_run: payload.dry_run ?? false,
enqueue_celery: payload.enqueue_celery ?? true,
run_inline: payload.run_inline ?? false