Files
fusion/packages/dashboard/app/components/AgentErrorDetailsModal.tsx
gsxdsm a2e96b132a feat(i18n): round-2 sweep — 488 residual strings migrated, markup fidelity restored (#1352)
- 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>
2026-06-03 20:52:44 -07:00

128 lines
4.3 KiB
TypeScript

import "./AgentErrorDetailsModal.css";
import { useMemo, useState } from "react";
import { AlertCircle, Check, Copy, ExternalLink } from "lucide-react";
import { useTranslation } from "react-i18next";
const DEFAULT_ISSUE_URL = "https://github.com/Runfusion/Fusion/issues/new";
export interface AgentErrorIssueContext {
surface: string;
agentId?: string;
agentName?: string;
agentState?: string;
runId?: string;
taskId?: string;
timestamp?: string;
}
interface AgentErrorDetailsModalProps {
open: boolean;
onClose: () => void;
errorText: string;
issueContext: AgentErrorIssueContext;
}
export function buildAgentErrorIssueUrl(errorText: string, context: AgentErrorIssueContext): string {
const title = `[Agent Error] ${context.surface}${context.agentName ? ` - ${context.agentName}` : ""}`;
const bodyLines = [
"## Agent Error Report",
"",
`- Surface: ${context.surface}`,
`- Agent ID: ${context.agentId ?? "unknown"}`,
`- Agent Name: ${context.agentName ?? "unknown"}`,
`- Agent State: ${context.agentState ?? "unknown"}`,
`- Run ID: ${context.runId ?? "n/a"}`,
`- Task ID: ${context.taskId ?? "n/a"}`,
`- Timestamp: ${context.timestamp ?? new Date().toISOString()}`,
"",
"## Error",
"```text",
errorText,
"```",
];
const params = new URLSearchParams({
title,
body: bodyLines.join("\n"),
});
return `${DEFAULT_ISSUE_URL}?${params.toString()}`;
}
export function AgentErrorDetailsModal({ open, onClose, errorText, issueContext }: AgentErrorDetailsModalProps) {
const [copied, setCopied] = useState(false);
const { t } = useTranslation("app");
const issueUrl = useMemo(() => buildAgentErrorIssueUrl(errorText, issueContext), [errorText, issueContext]);
if (!open) {
return null;
}
return (
<div className="modal-overlay open" onClick={(event) => event.target === event.currentTarget && onClose()} role="dialog" aria-modal="true" aria-label={t("agentError.dialogLabel", "Agent error details")}>
<div className="modal agent-error-modal">
<div className="modal-header">
<h2 className="modal-title">
<AlertCircle size={16} />
{t("agentError.title", "Agent Error Details")}
</h2>
<button className="modal-close" onClick={onClose} aria-label={t("common.close", "Close")}>&times;</button>
</div>
<div className="agent-error-modal__content">
<pre className="agent-error-modal__error">{errorText}</pre>
</div>
<div className="modal-actions">
<button
type="button"
className="btn btn-sm"
onClick={() => {
void navigator.clipboard.writeText(errorText).then(() => {
setCopied(true);
setTimeout(() => setCopied(false), 1500);
});
}}
aria-label={copied ? t("agentError.copiedLabel", "Copied error to clipboard") : t("agentError.copyLabel", "Copy error to clipboard")}
>
{copied ? <Check size={14} /> : <Copy size={14} />}
{copied ? t("agentError.copied", "Copied") : t("agentError.copy", "Copy")}
</button>
<a
className="btn btn-sm btn-warning"
href={issueUrl}
target="_blank"
rel="noreferrer"
onClick={(event) => {
event.preventDefault();
window.open(issueUrl, "_blank", "noopener,noreferrer");
}}
>
<ExternalLink size={14} />
{t("agentError.reportOnGithub", "Report on GitHub")}
</a>
</div>
</div>
</div>
);
}
interface AgentErrorIndicatorProps {
errorText: string;
issueContext: AgentErrorIssueContext;
summaryPrefix?: string;
}
export function AgentErrorIndicator({ errorText, issueContext, summaryPrefix = "Error" }: AgentErrorIndicatorProps) {
const [open, setOpen] = useState(false);
const { t } = useTranslation("app");
return (
<>
<button type="button" className="agent-error-indicator" onClick={() => setOpen(true)} aria-label={t("agentError.openDetails", "Open error details")}>
<AlertCircle size={14} />
<span className="agent-error-indicator__label">{summaryPrefix}</span>
</button>
<AgentErrorDetailsModal open={open} onClose={() => setOpen(false)} errorText={errorText} issueContext={issueContext} />
</>
);
}