- 51 fix agents covered every dirty batch from the round-1 verifiers: helper-function labels (roles, statuses, relative time), constant label maps (SETTINGS_SECTIONS, PROVIDER_INFO, EVENT_TYPE_LABELS), TUI help overlay + tab labels, toasts, placeholders, aria-labels - Inline markup flattened by round 1 restored with <Trans> (DbCorruptionBanner storage-docs link, UpdateAvailableBanner code chip) - Catalogs merged: +527 en keys across 5 locales; CLI bundles + app locale tree regenerated (6 locales) - All 23 sweep-caused test regressions fixed: delta vs the clean-main baseline is now zero (remaining 4 local failures reproduce identically on origin/main; CI-green upstream) - typecheck/lint clean; TUI 82/82, core locale 9/9 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
103 lines
4.2 KiB
TypeScript
103 lines
4.2 KiB
TypeScript
import { AlertTriangle, RefreshCw } from "lucide-react";
|
|
import { useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import type { TaskIdIntegrityReport } from "@fusion/core";
|
|
import { refreshDashboardHealth } from "../api";
|
|
import "./TaskIdIntegrityBanner.css";
|
|
|
|
interface TaskIdIntegrityBannerProps {
|
|
report: TaskIdIntegrityReport;
|
|
recommendedAction: string;
|
|
onRefresh?: (report: TaskIdIntegrityReport, recommendedAction: string | null) => void;
|
|
}
|
|
|
|
function getAnomalyLabel(kind: TaskIdIntegrityReport["anomalies"][number]["kind"], t: ReturnType<typeof useTranslation>["t"]): string {
|
|
switch (kind) {
|
|
case "duplicate_active_id":
|
|
return t("health.anomaly.duplicateActiveId", "Duplicate active task ID");
|
|
case "id_in_active_and_archived":
|
|
return t("health.anomaly.idInBothStorages", "Task ID present in active and archived storage");
|
|
case "next_sequence_at_or_below_used":
|
|
return t("health.anomaly.sequenceOverlap", "Allocator next sequence overlaps an existing task ID");
|
|
case "task_row_outside_known_prefix":
|
|
return t("health.anomaly.unknownPrefix", "Task row uses a prefix outside allocator state");
|
|
default:
|
|
return kind;
|
|
}
|
|
}
|
|
|
|
function formatAffectedIds(affectedIds: string[]): string {
|
|
if (affectedIds.length <= 5) {
|
|
return affectedIds.join(", ");
|
|
}
|
|
|
|
const visible = affectedIds.slice(0, 5).join(", ");
|
|
return `${visible} +${affectedIds.length - 5} more`;
|
|
}
|
|
|
|
export function TaskIdIntegrityBanner({ report, recommendedAction, onRefresh }: TaskIdIntegrityBannerProps) {
|
|
const { t } = useTranslation("app");
|
|
const [refreshing, setRefreshing] = useState(false);
|
|
const [refreshError, setRefreshError] = useState<string | null>(null);
|
|
|
|
if (report.status !== "anomaly") {
|
|
return null;
|
|
}
|
|
|
|
const handleRefresh = async () => {
|
|
setRefreshing(true);
|
|
setRefreshError(null);
|
|
try {
|
|
const health = await refreshDashboardHealth();
|
|
onRefresh?.(health.taskIdIntegrity, health.taskIdIntegrity.recommendedAction);
|
|
} catch (error) {
|
|
setRefreshError(error instanceof Error ? error.message : t("health.refreshFailed", "Failed to refresh integrity status."));
|
|
} finally {
|
|
setRefreshing(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<section className="task-id-integrity-banner" role="alert" aria-live="assertive">
|
|
<div className="task-id-integrity-banner__header">
|
|
<div className="task-id-integrity-banner__headline-wrap">
|
|
<span className="status-dot status-dot--error" aria-hidden="true" />
|
|
<AlertTriangle aria-hidden="true" />
|
|
<h2 className="task-id-integrity-banner__headline">{t("health.anomalyDetected", "Task ID integrity anomaly detected")}</h2>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
className="btn btn-sm task-id-integrity-banner__refresh"
|
|
onClick={() => {
|
|
void handleRefresh();
|
|
}}
|
|
disabled={refreshing}
|
|
>
|
|
<RefreshCw className={refreshing ? "task-id-integrity-banner__refresh-icon task-id-integrity-banner__refresh-icon--spinning" : "task-id-integrity-banner__refresh-icon"} aria-hidden="true" />
|
|
{refreshing ? t("health.rechecking", "Re-checking…") : t("health.recheck", "Re-check")}
|
|
</button>
|
|
</div>
|
|
|
|
<p className="task-id-integrity-banner__body">
|
|
{t("health.anomalyBody", "Fusion found allocator state that can cause task IDs to be reused or overwrite live task records.")}
|
|
</p>
|
|
|
|
<ul className="task-id-integrity-banner__list">
|
|
{report.anomalies.map((anomaly) => (
|
|
<li
|
|
key={`${anomaly.kind}:${anomaly.prefix}:${anomaly.affectedIds.join(",")}`}
|
|
className="task-id-integrity-banner__item"
|
|
>
|
|
<strong className="task-id-integrity-banner__item-title">{getAnomalyLabel(anomaly.kind, t)}</strong>
|
|
<span className="task-id-integrity-banner__item-detail">{anomaly.details}</span>
|
|
<code className="task-id-integrity-banner__ids">{formatAffectedIds(anomaly.affectedIds)}</code>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
<p className="task-id-integrity-banner__footer">{recommendedAction}</p>
|
|
{refreshError ? <p className="task-id-integrity-banner__error">{refreshError}</p> : null}
|
|
</section>
|
|
);
|
|
}
|