/**
* 制御盤のログモーダルの表示用文言・整形(指示書BJ 段階4。表E)。
* ★純粋関数★。`DeviceLogsModal` から移した。
*/
import { ApiFetchError } from "@/lib/api/client";
import { EXPORT_MAX_BYTES } from "@/lib/config/ds-agent";
import { EXPORT_FILE_IDS_TOO_LARGE_MESSAGE } from "@/lib/ds-logs/validate";
import { formatHm, formatMd } from "@/lib/format/date";
import type { ApiErrorDetails } from "@/lib/types";
export function formatDateTime(iso: string): string {
return `${formatMd(iso)} ${formatHm(iso)}`;
}
/** 大きさの表示(一覧・選択合計・上限の文言)。1MB 未満は KB、以上は MB */
export function formatFileSize(bytes: number): string {
if (bytes >= 1024 * 1024) {
const mb = bytes / (1024 * 1024);
const text = Number.isInteger(mb) ? String(mb) : mb.toFixed(1);
return `${text} MB`;
}
return `${Math.round(bytes / 1024)} KB`;
}
export function detailStrings(details: ApiErrorDetails, key: string): string[] {
if (details === null || typeof details !== "object") return [];
const value = (details as Record<string, unknown>)[key];
if (!Array.isArray(value)) return [];
return value.filter((v): v is string => typeof v === "string");
}
function isAbortCause(cause: unknown): boolean {
return (
cause !== null &&
typeof cause === "object" &&
"name" in cause &&
(cause as { name: unknown }).name === "AbortError"
);
}
/**
* API 失敗を画面の文言に(指示書AY 段階9-2 の表・指示書BJ J1/J2)。
* ★取り消し(`AbortError`)だけ null★。それ以外の status 0 は通信失敗の文言。
*/
export function apiErrorMessage(e: unknown, t: (text: string) => string): string | null {
if (!(e instanceof ApiFetchError)) {
return t("データの取得に失敗しました");
}
if (e.status === 0) {
if (isAbortCause(e.cause)) return null;
return t("通信に失敗しました。時間をおいて再度お試しください");
}
switch (e.code) {
case "agent_offline": {
const last = detailStrings(e.details, "last_seen_at")[0];
const when = last ? formatDateTime(last) : "-";
return t("制御盤が接続されていません(最終接続: {日時})").replace("{日時}", when);
}
case "agent_timeout":
return t("制御盤から応答がありません。しばらくしてから再度お試しください");
case "export_busy":
return t("この制御盤は別のエクスポートを処理中です");
case "agent_busy":
return t("制御盤が混み合っています。しばらくしてから再度お試しください");
case "agent_error": {
const code = detailStrings(e.details, "agent_code")[0] ?? "";
if (code === "result_invalid") {
return t("制御盤の結果の形が不正です");
}
if (code === "too_large") {
return t("選んだファイルが大きすぎます(1回 {max} まで)").replace(
"{max}",
formatFileSize(EXPORT_MAX_BYTES)
);
}
return t("制御盤でエラーが発生しました({code})").replace("{code}", code);
}
case "validation_failed":
if (e.serverMessage === EXPORT_FILE_IDS_TOO_LARGE_MESSAGE) {
return t(EXPORT_FILE_IDS_TOO_LARGE_MESSAGE);
}
return e.serverMessage ?? t("データの取得に失敗しました");
default:
return e.serverMessage ?? t("データの取得に失敗しました");
}
}