feat(webui): add campaign metric drill-downs
This commit is contained in:
@@ -1287,7 +1287,27 @@ export default function ReviewSendPage({
|
||||
}
|
||||
|
||||
function scrollToStage(id: string) {
|
||||
document.getElementById(id)?.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
revealReviewElement(id);
|
||||
}
|
||||
|
||||
function revealReviewElement(id: string) {
|
||||
const target = document.getElementById(id);
|
||||
if (!target) return;
|
||||
const stage = target.classList.contains("review-flow-stage")
|
||||
? target
|
||||
: target.closest<HTMLElement>(".review-flow-stage");
|
||||
stage?.querySelector<HTMLButtonElement>('.card-collapse-toggle[aria-expanded="false"]')?.click();
|
||||
window.requestAnimationFrame(() => {
|
||||
target.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
target.focus({ preventScroll: true });
|
||||
});
|
||||
}
|
||||
|
||||
function showBuiltDetails(filters: Record<string, string> = {}, includeAll = false) {
|
||||
setShowAllReviewJobs(includeAll);
|
||||
setReviewQuery({ sort: null, filters });
|
||||
setReviewPage(1);
|
||||
window.requestAnimationFrame(() => revealReviewElement("campaign-built-message-details"));
|
||||
}
|
||||
|
||||
const matchedAttachments = numberFrom(attachmentSummary, ["total_matched_files"]);
|
||||
@@ -1375,7 +1395,7 @@ export default function ReviewSendPage({
|
||||
actions={<PageActionBar
|
||||
variant="workspace"
|
||||
refreshable
|
||||
reloadAction={<Button onClick={() => void reload({ force: true })} disabled={loading || Boolean(busy)}>i18n:govoplan-campaign.reload.cce71553</Button>}
|
||||
reloadAction={{ onReload: () => void reload({ force: true }), loading: loading || Boolean(busy) }}
|
||||
primaryActions={<Button onClick={() => void refreshQueueStatus(false)} disabled={!version || queueStatusLoading || Boolean(busy)}>
|
||||
{queueStatusLoading ? "i18n:govoplan-campaign.refreshing_status.d8965739" : "i18n:govoplan-campaign.refresh_status.ade15a52"}
|
||||
</Button>}
|
||||
@@ -1400,9 +1420,9 @@ export default function ReviewSendPage({
|
||||
<WorkflowStage stage={stages[0]} nextState={stages[1].state} nextConnectorState={stageConnectorState(stages[1])}>
|
||||
<MetricGrid columns={4} density="compact" spacing="block" minimum="compact">
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.status.bae7d5be" value={validationPresent ? validationOk ? "i18n:govoplan-campaign.passed.271d60f4" : "i18n:govoplan-campaign.needs_attention.a126722e" : "i18n:govoplan-campaign.not_run.9e019cd5"} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.blocking.d785c0d4" value={validationPresent ? validationErrors : "—"} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.warnings.1430f976" value={validationPresent ? validationWarnings : "—"} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.jobs_needing_attention.95613b02" value={cards?.needs_attention ?? "—"} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.blocking.d785c0d4" value={validationPresent ? validationErrors : "—"} drilldown={validationErrors > 0 && visibleValidationIssues.length > 0 ? { label: "i18n:govoplan-campaign.validation_details.aa503267", onActivate: () => revealReviewElement("campaign-validation-details") } : undefined} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.warnings.1430f976" value={validationPresent ? validationWarnings : "—"} drilldown={validationWarnings > 0 && visibleValidationIssues.length > 0 ? { label: "i18n:govoplan-campaign.validation_details.aa503267", onActivate: () => revealReviewElement("campaign-validation-details") } : undefined} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.jobs_needing_attention.95613b02" value={cards?.needs_attention ?? "—"} drilldown={hasBuild && Number(cards?.needs_attention ?? 0) > 0 ? { label: "i18n:govoplan-campaign.review_candidates.438b8b57", onActivate: () => showBuiltDetails() } : undefined} />
|
||||
</MetricGrid>
|
||||
<ValidationWorkflowGuidance
|
||||
errors={validationErrors}
|
||||
@@ -1412,17 +1432,19 @@ export default function ReviewSendPage({
|
||||
{validationPresent && validationErrors === 0 &&
|
||||
<p className="review-flow-inline-note is-complete"><Check size={17} aria-hidden="true" /> i18n:govoplan-campaign.no_blocking_validation_exceptions_remain.73186d0d</p>
|
||||
}
|
||||
<AttachmentLinkingPreview
|
||||
preview={attachmentPreview}
|
||||
loading={attachmentPreviewLoading}
|
||||
error={attachmentPreviewError}
|
||||
linking={attachmentLinking}
|
||||
disabled={!version || readOnlyVersion || readyForDelivery || Boolean(busy)}
|
||||
onRefresh={() => void reloadAttachmentPreview(false)}
|
||||
onLink={() => void linkMatchedAttachmentFiles()} />
|
||||
<div id="campaign-attachment-preview" tabIndex={-1}>
|
||||
<AttachmentLinkingPreview
|
||||
preview={attachmentPreview}
|
||||
loading={attachmentPreviewLoading}
|
||||
error={attachmentPreviewError}
|
||||
linking={attachmentLinking}
|
||||
disabled={!version || readOnlyVersion || readyForDelivery || Boolean(busy)}
|
||||
onRefresh={() => void reloadAttachmentPreview(false)}
|
||||
onLink={() => void linkMatchedAttachmentFiles()} />
|
||||
</div>
|
||||
|
||||
{visibleValidationIssues.length > 0 &&
|
||||
<div className="review-flow-data-section">
|
||||
<div id="campaign-validation-details" className="review-flow-data-section" tabIndex={-1}>
|
||||
<h3>i18n:govoplan-campaign.validation_details.aa503267</h3>
|
||||
<DescriptionList variant="inline">
|
||||
{visibleValidationIssues.map((issue, index) =>
|
||||
@@ -1457,13 +1479,13 @@ export default function ReviewSendPage({
|
||||
|
||||
<WorkflowStage stage={stages[1]} nextState={stages[2].state} nextConnectorState={stageConnectorState(stages[2])}>
|
||||
<MetricGrid columns={4} density="compact" spacing="block" minimum="compact">
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.built.a6ad3f82" value={hasBuild ? builtCount : "—"} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.blocked.99613c74" value={hasBuild ? buildBlocked : "—"} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.need_review.201a4493" value={hasBuild ? buildNeedsReview : "—"} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.reviewed.31ef8593" value={hasBuild ? buildReviewProgress.reviewed : "—"} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.remaining.cc632b5e" value={hasBuild ? buildReviewProgress.remaining : "—"} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.review_candidates.438b8b57" value={reviewJobs.total || "—"} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.attachment_issues.69748336" value={missingAttachments + ambiguousAttachments} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.built.a6ad3f82" value={hasBuild ? builtCount : "—"} drilldown={hasBuild && builtCount > 0 ? { label: "i18n:govoplan-campaign.show_all_messages.1c2107a1", onActivate: () => showBuiltDetails({}, true) } : undefined} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.blocked.99613c74" value={hasBuild ? buildBlocked : "—"} drilldown={buildBlocked > 0 ? { label: "i18n:govoplan-campaign.review.e29a79fe", onActivate: () => showBuiltDetails({ validation: 'list:["blocked"]' }) } : undefined} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.need_review.201a4493" value={hasBuild ? buildNeedsReview : "—"} drilldown={buildNeedsReview > 0 ? { label: "i18n:govoplan-campaign.review_candidates.438b8b57", onActivate: () => showBuiltDetails({ validation: 'list:["warning","needs_review"]' }) } : undefined} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.reviewed.31ef8593" value={hasBuild ? buildReviewProgress.reviewed : "—"} drilldown={buildReviewProgress.reviewed > 0 ? { label: "i18n:govoplan-campaign.reviewed.31ef8593", onActivate: () => showBuiltDetails({ reviewed: 'list:["yes"]' }, true) } : undefined} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.remaining.cc632b5e" value={hasBuild ? buildReviewProgress.remaining : "—"} drilldown={buildReviewProgress.remaining > 0 ? { label: "i18n:govoplan-campaign.review.e29a79fe", onActivate: () => showBuiltDetails({ reviewed: 'list:["no"]' }) } : undefined} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.review_candidates.438b8b57" value={reviewJobs.total || "—"} drilldown={reviewJobs.total > 0 ? { label: "i18n:govoplan-campaign.review_candidates.438b8b57", onActivate: () => showBuiltDetails() } : undefined} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.attachment_issues.69748336" value={missingAttachments + ambiguousAttachments} drilldown={missingAttachments + ambiguousAttachments > 0 ? { label: "i18n:govoplan-campaign.review_attachment_rules.552c2116", onActivate: () => revealReviewElement("campaign-attachment-preview") } : undefined} />
|
||||
</MetricGrid>
|
||||
{hasBuild && <BuiltMessageReviewProgress progress={buildReviewProgress} />}
|
||||
{hasBuild && <BuiltMessageWorkflowGuidance progress={buildReviewProgress} buildWarnings={buildWarnings} />}
|
||||
@@ -1518,7 +1540,7 @@ export default function ReviewSendPage({
|
||||
<p className="review-flow-inline-note is-complete"><Check size={17} aria-hidden="true" /> i18n:govoplan-campaign.all_built_messages_are_ready_no_manual_review_ac.c5549791</p>
|
||||
}
|
||||
{hasBuild &&
|
||||
<div className="review-flow-data-section">
|
||||
<div id="campaign-built-message-details" className="review-flow-data-section" tabIndex={-1}>
|
||||
<div className="page-heading split">
|
||||
<div className="button-row compact-actions">
|
||||
<Button onClick={() => {setShowAllReviewJobs((value) => !value);setReviewPage(1);setJobsLoadedKey("");}} disabled={busy === "inspect"}>
|
||||
@@ -1617,10 +1639,10 @@ export default function ReviewSendPage({
|
||||
|
||||
<WorkflowStage stage={stages[3]} nextState={stages[4].state} nextConnectorState={stageConnectorState(stages[4])}>
|
||||
<MetricGrid columns={4} density="compact" spacing="block" minimum="compact">
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.recipients.78cbf8eb" value={jobsTotal || "—"} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.messages_to_send.f7763bf9" value={builtCount || jobsTotal || "—"} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.matched_attachments.ead1eeb1" value={matchedAttachments || "—"} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.missing_ambiguous.fffdd3f5" value={`${missingAttachments} / ${ambiguousAttachments}`} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.recipients.78cbf8eb" value={jobsTotal || "—"} drilldown={hasBuild && jobsTotal > 0 ? { label: "i18n:govoplan-campaign.show_all_messages.1c2107a1", onActivate: () => showBuiltDetails({}, true) } : undefined} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.messages_to_send.f7763bf9" value={builtCount || jobsTotal || "—"} drilldown={hasBuild && builtCount > 0 ? { label: "i18n:govoplan-campaign.show_all_messages.1c2107a1", onActivate: () => showBuiltDetails({}, true) } : undefined} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.matched_attachments.ead1eeb1" value={matchedAttachments || "—"} drilldown={matchedAttachments > 0 ? { label: "i18n:govoplan-campaign.review_attachment_rules.552c2116", onActivate: () => revealReviewElement("campaign-attachment-preview") } : undefined} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.missing_ambiguous.fffdd3f5" value={`${missingAttachments} / ${ambiguousAttachments}`} drilldown={missingAttachments + ambiguousAttachments > 0 ? { label: "i18n:govoplan-campaign.review_attachment_rules.552c2116", onActivate: () => revealReviewElement("campaign-attachment-preview") } : undefined} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.rate_limit.d08e55f5" value={messagesPerMinute > 0 ? i18nMessage("i18n:govoplan-campaign.value_min.c9d89eae", { value0: messagesPerMinute }) : "i18n:govoplan-campaign.not_set.93039e60"} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.minimum_duration.91a71a6d" value={estimatedMinutes ? i18nMessage("i18n:govoplan-campaign.about_value_min.7c2e77fc", { value0: estimatedMinutes }) : "—"} />
|
||||
<MetricCard density="compact" surface="subtle" label="i18n:govoplan-campaign.imap_append.8c0d9e96" value={Boolean(imapAppend.enabled) ? "i18n:govoplan-campaign.enabled.df174a3f" : "i18n:govoplan-campaign.disabled.f4f4473d"} />
|
||||
|
||||
Reference in New Issue
Block a user