Newer
Older
client_dwatch / lib / clean-area / visibility.ts
/**
 * 清掃面積の内訳を★画面で何件見せるか★を決める純粋関数(指示書AN 段階2 / §5-3 C-3・C-4)。
 *
 * ★★`lib/dashboard/rescue-visibility.ts` と同じ形の別関数★★ —
 *   件数の設定値が違う(`CLEAN_AREA_INITIAL_VISIBLE` / `RESCUE_INITIAL_VISIBLE`)だけで
 *   判定は同じ。★どちらかの設定値を変えたときに、もう一方が巻き添えにならないように分けてある★。
 *
 * ★★`components/` に置かない理由★★:
 *   `vitest.config.ts` の `include` は `lib/**` と `app/**` なので、
 *   ★`components/` に置いたテストは走りません★(D-120 項2 / 指示書AN 落とし穴12)。
 *   「何件表示するか」は `>` と `>=` を取り違えるだけで静かに壊れる種類の判定
 *   (★ちょうど10件のときに「もっと表示」が出る★というバグは、
 *   例外もテスト失敗も出ないまま運用担当だけが困る)。
 *
 * ★API は件数で切らない★ — ここで絞るのは★見せ方だけ★で、`items` には常に全件が入っている。
 *   ★`items.length` がそのまま総数★なので、件数表示は絞る前を使うこと。
 */
import { CLEAN_AREA_INITIAL_VISIBLE } from "@/lib/config";

export interface CleanAreaVisibility<T> {
  /** 実際に並べる行(`expanded` なら全件、そうでなければ先頭 `CLEAN_AREA_INITIAL_VISIBLE` 件) */
  visible: readonly T[];
  /** まだ見えていない件数(`expanded` のときは 0)。★「もっと表示(あと{n}件)」の {n}★ */
  remaining: number;
  /**
   * 開閉ボタンを出すか。
   * ★総数が初期表示件数を「超える」ときだけ★ — ★ちょうど同数なら出さない★
   *   (出すと押しても何も増えない)。`expanded` の有無では変わらない
   *   (開いたあとは「閉じる」として同じボタンを使うため)。
   */
  canToggle: boolean;
}

/**
 * @param items API が返した内訳(★全件★。並び替え済み)
 * @param expanded 「もっと表示」が押された状態か(★画面のローカル state。URL に持たせない★)
 */
export function cleanAreaVisibility<T>(
  items: readonly T[],
  expanded: boolean
): CleanAreaVisibility<T> {
  // ★件数を直書きしないこと★(`CLAUDE.md` §7)— 設定値は `lib/config` から読む
  const canToggle = items.length > CLEAN_AREA_INITIAL_VISIBLE;
  const visible =
    expanded || !canToggle ? items : items.slice(0, CLEAN_AREA_INITIAL_VISIBLE);
  return {
    visible,
    remaining: items.length - visible.length,
    canToggle,
  };
}