/**
* Showcase · table-conditional-format — 条件付き書式 (V19)
*
* Conditional row / cell formatting by threshold on a 勤怠 (attendance) summary:
* - 遅刻 (late arrivals) ≥ 5回 → 行全体を danger(茜) で淡くティント
* - 早退 (early leave) > 2.0h → セルを attention(朱) で強調
* - 残業 (overtime) ≥ 45h → セルを warning(山吹) で注意喚起
*
* すべて semantic token 経由(bg-destructive/10・text-attention・text-warning-foreground)で、
* raw hex は一切使わない。閾値判定は純粋関数に集約し、書式は token クラスにマップする。
*
* GAP: DataTable の ColumnDef は per-row / per-cell の className フックを持たない
* (render は ReactNode を返すのみ)。行全体のティントには @godxjp/ui が同梱する
* 実 primitive の Table / TableRow ファミリ(DataTable 内部と同一)で組み立て、
* TableRow に token クラスを渡す。セル単位の書式は ColumnDef でも表現できるため、
* 下段で DataTable + render の cell-only 版も併示する。
*/
import * as React from "react";
import {
Badge,
Card,
CardContent,
CardHeader,
CardTitle,
DataTable,
type ColumnDef,
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@godxjp/ui/data-display";
import { Text } from "@godxjp/ui/general";
import { Flex, PageContainer } from "@godxjp/ui/layout";
import { cn } from "@godxjp/ui/lib/utils";
// ── 勤怠データ(当月締め · 製造部 第2ライン) ────────────────────────────────
type Attendance = {
id: string;
name: string;
dept: string;
workdays: number; // 出勤日数
late: number; // 遅刻回数
earlyLeaveHours: number; // 早退時間 (h)
overtime: number; // 残業時間 (h)
status: "approved" | "pending" | "review";
};
const ROWS: Attendance[] = [
{
id: "E-1042",
name: "田中 美咲",
dept: "製造1課",
workdays: 20,
late: 1,
earlyLeaveHours: 0,
overtime: 12.5,
status: "approved",
},
{
id: "E-1043",
name: "佐藤 健",
dept: "製造1課",
workdays: 19,
late: 6,
earlyLeaveHours: 1.5,
overtime: 8,
status: "review",
},
{
id: "E-1044",
name: "鈴木 陽子",
dept: "製造2課",
workdays: 20,
late: 0,
earlyLeaveHours: 2.5,
overtime: 31,
status: "pending",
},
{
id: "E-1045",
name: "高橋 大輔",
dept: "製造2課",
workdays: 18,
late: 3,
earlyLeaveHours: 0,
overtime: 46.5,
status: "review",
},
{
id: "E-1046",
name: "伊藤 さくら",
dept: "検査課",
workdays: 20,
late: 0,
earlyLeaveHours: 0,
overtime: 4,
status: "approved",
},
{
id: "E-1047",
name: "渡辺 翔",
dept: "検査課",
workdays: 17,
late: 8,
earlyLeaveHours: 3.0,
overtime: 22,
status: "review",
},
{
id: "E-1048",
name: "山本 直樹",
dept: "出荷課",
workdays: 20,
late: 2,
earlyLeaveHours: 0.5,
overtime: 15,
status: "approved",
},
{
id: "E-1049",
name: "中村 あおい",
dept: "出荷課",
workdays: 19,
late: 5,
earlyLeaveHours: 2.25,
overtime: 48,
status: "review",
},
];
// ── 閾値(しきい値) ───────────────────────────────────────────────────────
const LATE_DANGER = 5; // 遅刻 ≥ 5回 → 行ティント (danger 茜)
const EARLY_ATTENTION = 2; // 早退 > 2.0h → セル強調 (attention 朱)
const OVERTIME_WARNING = 45; // 残業 ≥ 45h → セル注意 (warning 山吹)
const isLateRow = (r: Attendance) => r.late >= LATE_DANGER;
const isEarlyCell = (r: Attendance) => r.earlyLeaveHours > EARLY_ATTENTION;
const isOvertimeCell = (r: Attendance) => r.overtime >= OVERTIME_WARNING;
const num = new Intl.NumberFormat("ja-JP", { maximumFractionDigits: 2 });
const hours = (h: number) => `${num.format(h)}h`;
// ── 凡例(しきい値の色対応を明示) ─────────────────────────────────────────
const LEGEND: Array<{ swatch: string; label: string }> = [
{
swatch: "bg-destructive/15 border-destructive/40",
label: `遅刻 ${LATE_DANGER}回以上 → 行を強調(茜)`,
},
{
swatch: "bg-attention/15 border-attention/40",
label: `早退 ${EARLY_ATTENTION}.0h 超 → セル強調(朱)`,
},
{
swatch: "bg-warning/15 border-warning/40",
label: `残業 ${OVERTIME_WARNING}h 以上 → セル注意(山吹)`,
},
];
function Legend() {
return (
{LEGEND.map((l) => (
{l.label}
))}
);
}
export default function Demo() {
// ── 下段 DataTable 用カラム(セル単位の条件付き書式は render で表現可能) ──
const columns: ColumnDef[] = [
{ key: "id", header: "社員番号", width: "w-24" },
{ key: "name", header: "氏名" },
{ key: "dept", header: "部署", hiddenOnMobile: true },
{
key: "late",
header: "遅刻",
align: "right",
sortable: true,
render: (r) => (
= LATE_DANGER ? "destructive" : "default"}
weight={r.late >= LATE_DANGER ? "medium" : "regular"}
>
{r.late}回
),
},
{
key: "earlyLeaveHours",
header: "早退",
align: "right",
sortable: true,
render: (r) =>
isEarlyCell(r) ? (
{hours(r.earlyLeaveHours)}
) : (
{hours(r.earlyLeaveHours)}
),
},
{
key: "overtime",
header: "残業",
align: "right",
sortable: true,
render: (r) =>
isOvertimeCell(r) ? (
{hours(r.overtime)}
) : (
{hours(r.overtime)}
),
},
{
key: "status",
header: "承認",
align: "center",
render: (r) => ,
},
];
return (
{/* ── 行レベル + セルレベルの条件付き書式(実 Table primitive で合成) ── */}
勤怠サマリ · 製造部(当月締め)
対象 {ROWS.length}名 · 2026-05
社員番号
氏名
部署
出勤
遅刻
早退
残業
承認
{ROWS.map((r) => {
const lateRow = isLateRow(r);
return (
{r.id}
{r.name}
{lateRow && (
遅刻多
)}
{r.dept}
{r.workdays}日
{/* 遅刻セル: しきい値以上は茜文字で前景強調 */}
{r.late}回
{/* 早退セル: > 2.0h は attention(朱) で背景+前景強調 */}
{isEarlyCell(r) ? (
{hours(r.earlyLeaveHours)}
) : (
{hours(r.earlyLeaveHours)}
)}
{/* 残業セル: ≥ 45h は warning(山吹) で注意 */}
{isOvertimeCell(r) ? (
{hours(r.overtime)}
) : (
{hours(r.overtime)}
)}
);
})}
{/* ── DataTable 版(セル単位の条件付き書式は ColumnDef.render で表現) ── */}
DataTable 版 · セル単位の条件付き書式
ColumnDef.render 内でしきい値判定 → token クラスを付与。行全体のティントは ColumnDef
では表現できないため上段の実 Table 合成を参照(gapNotes 参照)。
r.id}>
);
}