feat: complete campaign wizards and retention reporting

This commit is contained in:
2026-07-31 04:21:34 +02:00
parent e689fdf495
commit fa4eb39e0b
18 changed files with 785 additions and 55 deletions
@@ -111,7 +111,17 @@ type WorkflowBusy = "validate" | "build" | "inspect" | "mock" | "mailbox" | "sen
const MESSAGE_REVIEW_ISSUE_STATUSES = ["warning", "needs_review", "blocked", "excluded"];
export default function ReviewSendPage({ settings, auth, campaignId }: {settings: ApiSettings;auth: AuthInfo;campaignId: string;}) {
export default function ReviewSendPage({
settings,
auth,
campaignId,
initialStageId
}: {
settings: ApiSettings;
auth: AuthInfo;
campaignId: string;
initialStageId?: string;
}) {
const navigate = useGuardedNavigate();
const devMailboxCapability = usePlatformUiCapability<MailDevMailboxUiCapability>("mail.devMailbox");
const { getDeltaWatermark, setDeltaWatermark, resetDeltaWatermark } = useDeltaWatermarks();
@@ -188,6 +198,7 @@ export default function ReviewSendPage({ settings, auth, campaignId }: {settings
const [selectedDeliveryJobDetail, setSelectedDeliveryJobDetail] = useState<Record<string, unknown> | null>(null);
const persistedReview = storedMessageReviewState(version);
const persistedReviewKey = `${persistedReview.buildToken}|${persistedReview.inspectionComplete ? "1" : "0"}|${persistedReview.reviewedMessageKeys.join(",")}|${JSON.stringify(persistedReview.issueDecisions)}`;
const initialStageScrollKey = useRef("");
useEffect(() => {
setBuiltReviewRows([]);
@@ -218,6 +229,20 @@ export default function ReviewSendPage({ settings, auth, campaignId }: {settings
resetDeltaWatermark();
}, [version?.id, resetDeltaWatermark]);
useEffect(() => {
if (!initialStageId || loading || !version?.id) return;
const key = `${version.id}:${initialStageId}`;
if (initialStageScrollKey.current === key) return;
const frame = window.requestAnimationFrame(() => {
const target = document.getElementById(initialStageId);
if (!target) return;
initialStageScrollKey.current = key;
target.scrollIntoView({ behavior: "smooth", block: "start" });
target.focus({ preventScroll: true });
});
return () => window.cancelAnimationFrame(frame);
}, [initialStageId, loading, version?.id]);
useEffect(() => {
setMessageReviewComplete(persistedReview.inspectionComplete);
setReviewedMessageKeys(new Set(persistedReview.reviewedMessageKeys));