Newer
Older
client_dwatch / lib / ds-alerts / edit.ts
/**
 * D-Sec アラート画面の保存(編集モード)で使う純粋関数(指示書AX 2-6・表3)。
 *
 * ★書き込みはエラー一覧と同じ `PATCH /errors/{error_id}/status`★(指示書AX D6)。応答は `ErrorReportItem` なので、
 *   `dsAlertWithResolution()` で画面の `DsAlert` に戻す。
 * ★保存後に一覧を取り直さない★(D15)— 成功した行だけ差し替え、`summary` は `adjustDsAlertSummary()` で付け替える。
 *
 * テストは `lib/ds-alerts/__tests__/edit.test.ts`(表3 S1〜S6)。
 */
import type { DsAlertSummary } from "@/lib/ds-alerts/query";
import type { DsAlert, ErrorReportItem, PrecheckStatus } from "@/lib/types";

/**
 * PATCH /errors/{error_id}/status の応答(`ErrorReportItem`)を `DsAlert` に戻す。
 * ★4項目(`resolution_status` / `responded_at` / `resolved_at` / `note`)だけ差し替え★ — 他は `row` のまま。
 * ★id が違えば例外★(呼び出しの誤り。別の行の応答を貼ると誰も気づけない)。
 */
export function dsAlertWithResolution(row: DsAlert, updated: ErrorReportItem): DsAlert {
  if (updated.id !== row.id) {
    throw new Error(
      `[ds-alerts/edit] 応答の id が食い違っています(行 id=${row.id} に id=${updated.id} の応答が渡された)`
    );
  }
  return {
    ...row,
    resolution_status: updated.resolution_status,
    responded_at: updated.responded_at,
    resolved_at: updated.resolved_at,
    note: updated.note,
  };
}

/**
 * 保存で状態が変わった行の分だけ `summary` を付け替える(旧状態 −1・新状態 +1)。★入力を書き換えない★。
 *
 * @param before 保存前の画面の行(`id` で `updated` と突き合わせる)
 * @param updated 保存に成功した行(id → 保存後の `DsAlert`)。★無い行・状態が同じ行は数えない★
 */
export function adjustDsAlertSummary(
  summary: DsAlertSummary,
  before: readonly DsAlert[],
  updated: ReadonlyMap<string, DsAlert>
): DsAlertSummary {
  const next: Record<PrecheckStatus, number> = { ...summary };
  for (const row of before) {
    const after = updated.get(row.id);
    if (after === undefined || after.resolution_status === row.resolution_status) continue;
    next[row.resolution_status] -= 1;
    next[after.resolution_status] += 1;
  }
  return { unresolved: next.unresolved, in_progress: next.in_progress, resolved: next.resolved };
}