Release v0.1.2
This commit is contained in:
@@ -1,14 +1,16 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { ExternalLink } from "lucide-react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import type { ApiSettings, CampaignListItem } from "../../types";
|
||||
import { getCampaignSummary, listCampaigns, retryCampaignJobs, sendUnattemptedCampaignJobs, type CampaignSummary } from "../../api/campaigns";
|
||||
import Button from "../../components/Button";
|
||||
import Card from "../../components/Card";
|
||||
import { Button } from "@govoplan/core-webui";
|
||||
import { Card } from "@govoplan/core-webui";
|
||||
import DataGrid, { type DataGridColumn } from "../../components/table/DataGrid";
|
||||
import DismissibleAlert from "../../components/DismissibleAlert";
|
||||
import LoadingFrame from "../../components/LoadingFrame";
|
||||
import PageTitle from "../../components/PageTitle";
|
||||
import StatusBadge from "../../components/StatusBadge";
|
||||
import { DismissibleAlert } from "@govoplan/core-webui";
|
||||
import { LoadingFrame } from "@govoplan/core-webui";
|
||||
import { MetricCard } from "@govoplan/core-webui";
|
||||
import { PageTitle } from "@govoplan/core-webui";
|
||||
import { StatusBadge } from "@govoplan/core-webui";
|
||||
import { asRecord, formatDateTime, humanize } from "../campaigns/utils/campaignView";
|
||||
|
||||
type OperatorRow = {
|
||||
@@ -92,11 +94,13 @@ export default function OperatorQueuePage({ settings }: { settings: ApiSettings
|
||||
{
|
||||
id: "actions",
|
||||
header: "Actions",
|
||||
width: 320,
|
||||
width: 270,
|
||||
sticky: "end",
|
||||
render: (row) => (
|
||||
<div className="button-row compact-actions">
|
||||
<Button onClick={() => navigate(`/campaigns/${row.campaign.id}/report`)}>Open queue</Button>
|
||||
<Button className="admin-icon-button" onClick={() => navigate(`/campaigns/${row.campaign.id}/report`)} aria-label={`Open queue for ${row.campaign.name}`} title={`Open queue for ${row.campaign.name}`}>
|
||||
<ExternalLink />
|
||||
</Button>
|
||||
<Button onClick={() => void runAction(row, "retry")} disabled={row.failed <= 0 || Boolean(busy)}>Retry</Button>
|
||||
<Button onClick={() => void runAction(row, "unattempted")} disabled={row.notAttempted <= 0 || Boolean(busy)}>Queue unsent</Button>
|
||||
</div>
|
||||
@@ -119,15 +123,16 @@ export default function OperatorQueuePage({ settings }: { settings: ApiSettings
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
||||
{message && <DismissibleAlert tone="success" resetKey={message} floating>{message}</DismissibleAlert>}
|
||||
|
||||
<div className="dashboard-grid settings-dashboard-grid">
|
||||
<Card title="Queue pressure"><MetricGrid items={[
|
||||
["Failed", totals.failed],
|
||||
["Outcome unknown", totals.outcomeUnknown],
|
||||
["Unattempted", totals.notAttempted],
|
||||
["Queued/active", totals.queuedOrActive],
|
||||
["IMAP failed", totals.imapFailed],
|
||||
]} /></Card>
|
||||
</div>
|
||||
<section className="queue-pressure-section" aria-labelledby="operator-queue-pressure-title">
|
||||
<h2 id="operator-queue-pressure-title" className="queue-pressure-heading">Queue pressure</h2>
|
||||
<QueuePressureGrid items={[
|
||||
{ label: "Failed", value: totals.failed, tone: "danger" },
|
||||
{ label: "Outcome unknown", value: totals.outcomeUnknown, tone: "warning" },
|
||||
{ label: "Unattempted", value: totals.notAttempted, tone: "info" },
|
||||
{ label: "Queued/active", value: totals.queuedOrActive, tone: "neutral" },
|
||||
{ label: "IMAP failed", value: totals.imapFailed, tone: "warning" },
|
||||
]} />
|
||||
</section>
|
||||
|
||||
<LoadingFrame loading={loading} label="Loading operator queue…">
|
||||
<Card title="Campaign queues">
|
||||
@@ -164,14 +169,11 @@ function numberValue(value: unknown): number {
|
||||
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
||||
}
|
||||
|
||||
function MetricGrid({ items }: { items: Array<[string, number]> }) {
|
||||
function QueuePressureGrid({ items }: { items: Array<{ label: string; value: number; tone: "neutral" | "good" | "warning" | "danger" | "info" }> }) {
|
||||
return (
|
||||
<div className="status-grid">
|
||||
{items.map(([label, value]) => (
|
||||
<div className="status-card" key={label}>
|
||||
<span>{label}</span>
|
||||
<strong>{value}</strong>
|
||||
</div>
|
||||
<div className="metric-grid queue-pressure-grid">
|
||||
{items.map((item) => (
|
||||
<MetricCard key={item.label} label={item.label} value={item.value} tone={item.tone} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user