"use client";
import { useTheme } from "@/components/theme/ThemeProvider";
import { FilterButton } from "@/components/ui/FilterButton";
import { GradeBadge } from "@/components/ui/GradeBadge";
import { ProgressBar } from "@/components/ui/ProgressBar";
import type { AreaFilter } from "@/lib/clean-logs/areas";
import { displayRate, isProblemGrade } from "@/lib/judge";
import { THEME_TOKENS } from "@/lib/theme";
import type { AreaCompletionsResponse, Grade } from "@/lib/types";
import styles from "./AreaModal.module.css";
interface AreaModalProps {
facilityName: string;
dateLabel: string;
/** ★サーバ側で絞るので、切り替えたら親が再取得する★(指示書Z 決定8) */
filter: AreaFilter;
onChangeFilter: (filter: AreaFilter) => void;
/** 取得できていないあいだは null(`loading` / `error` を見て出し分ける) */
data: AreaCompletionsResponse | null;
loading: boolean;
error: string | null;
onClose: () => void;
}
/** バーの色(モックと同じ対応)。★判定そのものはAPIが返した `grade` を使う★ */
function barTone(grade: Grade): "ok" | "wn" | "ng" {
if (grade === "◎" || grade === "○") return "ok";
if (grade === "△") return "wn";
return "ng";
}
/**
* エリア別清掃完了率のモーダル(F-02 の「エリア別詳細を見る →」から開く)。
*
* ★★並び順・判定・件数はすべてAPIが返したものをそのまま使う★★(`CLAUDE.md` §9.6)—
* ★フロントで並べ替え・再判定をしないこと★。`filter` の絞り込みもサーバ側。
*
* ★行の完了率は1回ごとの完了率★(`runCompletionRate()`。`min(clean_area ÷ task_area × 100, 100)` を
* 小数第1位まで切り捨て。D-155)。
* ★全体率(ヘッダの「全体」)は行の完了率の単純平均(清掃1回=1票)★なので、
* ★★行の完了率を足し合わせて件数で割れば全体率と一致する★★(未実施=完了率が `null` の行は
* 分子にも分母にも入れない)。計算は APIルート側の `buildAreaCompletions()`
* (`lib/clean-logs/areas.ts`)。★以前は全体だけ面積ベースの「意図した非対称」だった★
* (指示書Z 決定7)が、2026-09-30 に単純平均へ揃えた(D-151。指示書AQ §5-5)。
* ★2026-10-02 に、平均する材料を `clean_ratio` 列から1回ごとの完了率へ変えた(D-155。指示書AS)★。
*
* ★未実施のエリアは `completion_rate` が `null`★ → `displayRate()` が `-` を返し、
* バーは 0 幅、判定は `×`。★`0%` と表示しないこと★(D-107 落とし穴1)。
*/
export function AreaModal({
facilityName,
dateLabel,
filter,
onChangeFilter,
data,
loading,
error,
onClose,
}: AreaModalProps) {
const { t, theme } = useTheme();
const c = THEME_TOKENS[theme];
return (
<div className={styles.overlay} role="dialog" aria-modal="true">
<div className={styles.panel}>
<div className={styles.header}>
<div className={styles.headerRow}>
<div>
<div className={styles.title}>{t("エリア別清掃完了率")}</div>
<div className={styles.meta}>
{facilityName} | {dateLabel}
{data && (
<>
{" "}
| {t("全体")} {displayRate(data.overall_completion_rate)}{" "}
{data.overall_grade}
</>
)}
</div>
</div>
<button type="button" className={styles.close} onClick={onClose}>
✕
</button>
</div>
<div className={styles.filters}>
<FilterButton
active={filter === "problem"}
color={c.pr}
onClick={() => onChangeFilter("problem")}
>
{t("問題エリアのみ")}
</FilterButton>
<FilterButton
active={filter === "all"}
color={c.pr}
onClick={() => onChangeFilter("all")}
>
{t("全件")}
</FilterButton>
</div>
</div>
<div className={styles.body}>
{loading && <div className={styles.message}>{t("読み込み中…")}</div>}
{!loading && error && <div className={styles.message}>{error}</div>}
{!loading && !error && data && (
<>
<div className={styles.countLine}>
{filter === "problem"
? t("問題エリア({n} / {m}エリア)")
.replace("{n}", String(data.problem_count))
.replace("{m}", String(data.total_count))
: t("全{n}エリア(完了率昇順)").replace(
"{n}",
String(data.total_count)
)}
</div>
{data.items.length === 0 && (
<div className={styles.message}>
{/* ★「全部◎○だった」と「そもそもエリアが1つも無い」を区別する★ —
同じ文言にすると、データが取れていないことに気づけない */}
{data.total_count === 0
? t("エリアのデータがありません")
: t("問題エリアはありません(全エリア◎○)")}
</div>
)}
{data.items.map((item) => {
// ★APIが返した `grade` を見るだけ(閾値でここで判定し直さない)★
const problem = isProblemGrade(item.grade);
return (
<div key={item.section_id} className={styles.row}>
<div
className={`${styles.areaName} ${problem ? styles.areaNameProblem : ""}`}
title={item.section_name}
>
{item.section_name}
</div>
<div className={styles.bar}>
{/* ★未実施(null)は 0 幅★ */}
<ProgressBar
percent={item.completion_rate ?? 0}
tone={barTone(item.grade)}
/>
</div>
{/* ★丸めるのは表示のときだけ(`CLAUDE.md` §4.3)。未実施は `-`★ */}
<div className={styles.rate} data-tone={barTone(item.grade)}>
{displayRate(item.completion_rate)}
</div>
<GradeBadge grade={item.grade} />
</div>
);
})}
</>
)}
</div>
<div className={styles.footer}>
<button type="button" className={styles.closeBtn} onClick={onClose}>
{t("閉じる")}
</button>
</div>
</div>
</div>
);
}