refactor(webui): use central campaign components
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Check, RotateCcw, Search, X } from "lucide-react";
|
||||
import type { ApiSettings } from "../../types";
|
||||
import {
|
||||
downloadCampaignJobsCsv,
|
||||
@@ -21,7 +22,7 @@ import { DismissibleAlert } from "@govoplan/core-webui";
|
||||
import { PageTitle } from "@govoplan/core-webui";
|
||||
import { StatusBadge } from "@govoplan/core-webui";
|
||||
import VersionLine from "./components/VersionLine";
|
||||
import { LoadingFrame, i18nMessage, useDeltaWatermarks } from "@govoplan/core-webui";
|
||||
import { LoadingFrame, TableActionGroup, ToggleSwitch, i18nMessage, useDeltaWatermarks } from "@govoplan/core-webui";
|
||||
import { useCampaignWorkspaceData } from "./hooks/useCampaignWorkspaceData";
|
||||
import { asRecord, formatDateTime, humanize } from "./utils/campaignView";
|
||||
import { emptyCampaignJobsResponse, mergeCampaignJobsDelta } from "./utils/jobDeltas";
|
||||
@@ -358,18 +359,17 @@ export default function CampaignReportPage({ settings, campaignId }: {settings:
|
||||
{
|
||||
id: "actions",
|
||||
header: "i18n:govoplan-campaign.actions.c3cd636a",
|
||||
width: 250,
|
||||
width: 190,
|
||||
sticky: "end",
|
||||
render: (row) => {
|
||||
const id = String(row.id ?? "");
|
||||
const status = String(row.send_status ?? "");
|
||||
return (
|
||||
<div className="button-row compact-actions">
|
||||
<Button onClick={() => void openJob(id)} disabled={!id || busyAction === "detail"}>i18n:govoplan-campaign.details.dc3decbb</Button>
|
||||
{retryableFailedStatus(status) && <Button onClick={() => void retryFailedSynchronously([row])} disabled={!id || Boolean(busyAction)}>{busyAction === `retry-sync:${id}` ? "Sending..." : "Retry now"}</Button>}
|
||||
{status === "outcome_unknown" && <Button onClick={() => setReconcile({ jobId: id, decision: "smtp_accepted" })}>i18n:govoplan-campaign.accepted.61a0572c</Button>}
|
||||
{status === "outcome_unknown" && <Button onClick={() => setReconcile({ jobId: id, decision: "not_sent" })}>i18n:govoplan-campaign.not_sent.587c501e</Button>}
|
||||
</div>);
|
||||
return <TableActionGroup actions={[
|
||||
{ id: "details", label: "i18n:govoplan-campaign.details.dc3decbb", icon: <Search aria-hidden="true" />, disabled: !id || busyAction === "detail", onClick: () => void openJob(id) },
|
||||
{ id: "retry", label: busyAction === `retry-sync:${id}` ? "Sending..." : "Retry now", icon: <RotateCcw aria-hidden="true" />, applicable: retryableFailedStatus(status), disabled: !id || Boolean(busyAction), onClick: () => void retryFailedSynchronously([row]) },
|
||||
{ id: "accepted", label: "i18n:govoplan-campaign.accepted.61a0572c", icon: <Check aria-hidden="true" />, applicable: status === "outcome_unknown", onClick: () => setReconcile({ jobId: id, decision: "smtp_accepted" }) },
|
||||
{ id: "not-sent", label: "i18n:govoplan-campaign.not_sent.587c501e", icon: <X aria-hidden="true" />, variant: "danger", applicable: status === "outcome_unknown", onClick: () => setReconcile({ jobId: id, decision: "not_sent" }) }
|
||||
]} />;
|
||||
|
||||
}
|
||||
}],
|
||||
@@ -473,8 +473,8 @@ export default function CampaignReportPage({ settings, campaignId }: {settings:
|
||||
<span>i18n:govoplan-campaign.recipients.78cbf8eb</span>
|
||||
<textarea value={emailRecipients} onChange={(event) => setEmailRecipients(event.target.value)} placeholder="audit@example.org; owner@example.org" rows={3} />
|
||||
</label>
|
||||
<label><input type="checkbox" checked={attachCsv} onChange={(event) => setAttachCsv(event.target.checked)} /> i18n:govoplan-campaign.attach_job_csv.adb76197</label>
|
||||
<label><input type="checkbox" checked={attachJson} onChange={(event) => setAttachJson(event.target.checked)} /> i18n:govoplan-campaign.attach_json_report.d70883b5</label>
|
||||
<ToggleSwitch label="i18n:govoplan-campaign.attach_job_csv.adb76197" checked={attachCsv} onChange={setAttachCsv} />
|
||||
<ToggleSwitch label="i18n:govoplan-campaign.attach_json_report.d70883b5" checked={attachJson} onChange={setAttachJson} />
|
||||
</Dialog>
|
||||
|
||||
<Dialog
|
||||
@@ -526,39 +526,21 @@ function AttemptHistoryTable({ kind, rows }: {kind: "smtp" | "imap";rows: Record
|
||||
|
||||
}
|
||||
|
||||
const columns: DataGridColumn<Record<string, unknown>>[] = [
|
||||
{ id: "attempt", header: "#", width: 72, sortable: true, value: (row, index) => Number(row.attempt_number ?? index + 1), render: (row, index) => String(row.attempt_number ?? index + 1) },
|
||||
{ id: "status", header: "i18n:govoplan-campaign.status.bae7d5be", width: 150, sortable: true, filterable: true, value: (row) => String(row.status ?? "unknown"), render: (row) => <StatusBadge status={String(row.status ?? "unknown")} /> },
|
||||
kind === "imap" ?
|
||||
{ id: "folder", header: "i18n:govoplan-campaign.folder.30baa249", width: 180, sortable: true, filterable: true, value: (row) => String(row.folder ?? "—"), render: (row) => String(row.folder ?? "—") } :
|
||||
{ id: "code", header: "i18n:govoplan-campaign.code.adac6937", width: 110, sortable: true, value: (row) => String(row.smtp_status_code ?? "—"), render: (row) => String(row.smtp_status_code ?? "—") },
|
||||
{ id: "started", header: "i18n:govoplan-campaign.started.faa9e7e7", width: 180, sortable: true, value: (row) => String(row.started_at ?? row.created_at ?? ""), render: (row) => formatDateTime(String(row.started_at ?? row.created_at ?? "")) },
|
||||
{ id: "finished", header: "i18n:govoplan-campaign.finished.355bcc57", width: 180, sortable: true, value: (row) => String(row.finished_at ?? row.updated_at ?? ""), render: (row) => formatDateTime(String(row.finished_at ?? row.updated_at ?? "")) },
|
||||
{ id: "result", header: "i18n:govoplan-campaign.result.5faa59d4", width: "minmax(240px, 1fr)", minWidth: 200, resizable: true, filterable: true, value: (row) => String(row.smtp_response ?? row.error_message ?? "—"), render: (row) => <span title={String(row.smtp_response ?? row.error_message ?? "")}>{String(row.smtp_response ?? row.error_message ?? "—")}</span> }
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="attempt-history-section">
|
||||
<h3>{title}</h3>
|
||||
<div className="attempt-history-wrap">
|
||||
<table className="attempt-history-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>i18n:govoplan-campaign.status.bae7d5be</th>
|
||||
{kind === "imap" && <th>i18n:govoplan-campaign.folder.30baa249</th>}
|
||||
{kind === "smtp" && <th>i18n:govoplan-campaign.code.adac6937</th>}
|
||||
<th>i18n:govoplan-campaign.started.faa9e7e7</th>
|
||||
<th>i18n:govoplan-campaign.finished.355bcc57</th>
|
||||
<th>i18n:govoplan-campaign.result.5faa59d4</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map((row, index) =>
|
||||
<tr key={String(row.id ?? `${kind}-${index}`)}>
|
||||
<td>{String(row.attempt_number ?? index + 1)}</td>
|
||||
<td><StatusBadge status={String(row.status ?? "unknown")} /></td>
|
||||
{kind === "imap" && <td>{String(row.folder ?? "—")}</td>}
|
||||
{kind === "smtp" && <td>{String(row.smtp_status_code ?? "—")}</td>}
|
||||
<td>{formatDateTime(String(row.started_at ?? row.created_at ?? ""))}</td>
|
||||
<td>{formatDateTime(String(row.finished_at ?? row.updated_at ?? ""))}</td>
|
||||
<td title={String(row.smtp_response ?? row.error_message ?? "")}>
|
||||
{String(row.smtp_response ?? row.error_message ?? "—")}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<DataGrid id={`campaign-${kind}-attempt-history`} rows={rows} columns={columns} getRowKey={(row, index) => String(row.id ?? `${kind}-${index}`)} />
|
||||
</section>);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user