Release govoplan-campaign v0.1.28: stabilize saving, review and delivery recovery
Module Package Release / publish-packages (push) Successful in 12s
Module Package Release / publish-packages (push) Successful in 12s
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import {
|
||||
Button,
|
||||
DismissibleAlert,
|
||||
FormField,
|
||||
i18nMessage
|
||||
} from "@govoplan/core-webui";
|
||||
import { useState } from "react";
|
||||
|
||||
import CampaignMessagePreviewOverlay, {
|
||||
type CampaignMessagePreviewAttachment
|
||||
@@ -34,6 +36,9 @@ export default function BuiltMessagePreview({
|
||||
singleMessageSendBusy,
|
||||
reviewed,
|
||||
reviewReason,
|
||||
reviewSaving = false,
|
||||
reviewDisabled = false,
|
||||
reviewError = "",
|
||||
onReviewReasonChange,
|
||||
onAcceptReview,
|
||||
onSelect,
|
||||
@@ -48,8 +53,11 @@ export default function BuiltMessagePreview({
|
||||
singleMessageSendBusy: boolean;
|
||||
reviewed: boolean;
|
||||
reviewReason: string;
|
||||
reviewSaving?: boolean;
|
||||
reviewDisabled?: boolean;
|
||||
reviewError?: string;
|
||||
onReviewReasonChange: (value: string) => void;
|
||||
onAcceptReview: (reasonRequired: boolean) => void;
|
||||
onAcceptReview: (reasonRequired: boolean, reason: string) => Promise<void>;
|
||||
onSelect: (index: number) => void;
|
||||
onSendSingle: (index: number) => void;
|
||||
onClose: () => void;
|
||||
@@ -128,40 +136,16 @@ export default function BuiltMessagePreview({
|
||||
}}
|
||||
actions={
|
||||
<div className="built-message-review-actions">
|
||||
{reviewError && <DismissibleAlert tone="danger" dismissible={false}>{reviewError}</DismissibleAlert>}
|
||||
{explicitReview && !reviewed ? (
|
||||
<>
|
||||
<FormField
|
||||
label={
|
||||
reasonRequired
|
||||
? "Reason for accepting attachment exception"
|
||||
: "Review note"
|
||||
}
|
||||
help={
|
||||
reasonRequired
|
||||
? "Required. This reason is stored with the frozen build evidence."
|
||||
: "Optional note stored with the review decision."
|
||||
}
|
||||
>
|
||||
<input
|
||||
value={reviewReason}
|
||||
maxLength={4000}
|
||||
onChange={(event) =>
|
||||
onReviewReasonChange(event.target.value)
|
||||
}
|
||||
/>
|
||||
</FormField>
|
||||
<Button
|
||||
variant="primary"
|
||||
disabled={reasonRequired && !reviewReason.trim()}
|
||||
onClick={() => onAcceptReview(reasonRequired)}
|
||||
>
|
||||
Accept review conditions
|
||||
</Button>
|
||||
</>
|
||||
<ReviewDecisionForm key={String(row.id ?? row.review_key ?? index)} initialReason={reviewReason}
|
||||
reasonRequired={reasonRequired} saving={reviewSaving} disabled={reviewDisabled}
|
||||
onReasonChange={onReviewReasonChange} onAccept={(reason) => onAcceptReview(reasonRequired, reason)} />
|
||||
) : null}
|
||||
{reviewed && <p role="status">i18n:govoplan-campaign.review_decision_saved</p>}
|
||||
<Button
|
||||
variant={explicitReview && !reviewed ? undefined : "primary"}
|
||||
disabled={singleMessageSendBusy || Boolean(singleSendDisabledReason)}
|
||||
disabled={reviewSaving || singleMessageSendBusy || Boolean(singleSendDisabledReason)}
|
||||
title={singleSendDisabledReason || "Test, send, or resend only this built message"}
|
||||
onClick={() => onSendSingle(index)}
|
||||
>
|
||||
@@ -169,11 +153,32 @@ export default function BuiltMessagePreview({
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
closeDisabled={reviewSaving}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Keystrokes rerender only this small form, not the campaign table, template
|
||||
// rendering and message body. The parent retains a draft ref for navigation.
|
||||
function ReviewDecisionForm({ initialReason, reasonRequired, saving, disabled, onReasonChange, onAccept }: {
|
||||
initialReason: string; reasonRequired: boolean; saving: boolean; disabled: boolean;
|
||||
onReasonChange: (value: string) => void; onAccept: (reason: string) => Promise<void>;
|
||||
}) {
|
||||
const [reason, setReason] = useState(initialReason);
|
||||
return <>
|
||||
<FormField label={reasonRequired ? "i18n:govoplan-campaign.review_reason_label" : "i18n:govoplan-campaign.review_note_label"}
|
||||
help="i18n:govoplan-campaign.review_reason_help">
|
||||
<input value={reason} maxLength={4000} disabled={saving || disabled} onChange={event => {
|
||||
setReason(event.target.value); onReasonChange(event.target.value);
|
||||
}} />
|
||||
</FormField>
|
||||
<Button variant="primary" disabled={saving || disabled || reasonRequired && !reason.trim()} onClick={() => void onAccept(reason)}>
|
||||
{saving ? "i18n:govoplan-campaign.review_saving_decision" : "i18n:govoplan-campaign.review_accept_next"}
|
||||
</Button>
|
||||
</>;
|
||||
}
|
||||
|
||||
function singleMessageSendDisabledReason(
|
||||
row: Record<string, unknown>,
|
||||
canStartSingleMessageSend: boolean
|
||||
@@ -247,7 +252,7 @@ function builtMessageMetaItems(row: Record<string, unknown>) {
|
||||
function builtMessageAttachments(
|
||||
row: Record<string, unknown>
|
||||
): CampaignMessagePreviewAttachment[] {
|
||||
return asArray(row.attachments).flatMap((value, index) => {
|
||||
return asArray(row.attachments).flatMap<CampaignMessagePreviewAttachment>((value, index) => {
|
||||
const attachment = asRecord(value);
|
||||
const zipProtection = zipProtectionFromBuiltAttachment(attachment);
|
||||
const managedMatches = asArray(attachment.managed_matches).map(asRecord);
|
||||
|
||||
Reference in New Issue
Block a user