型番 heatmap
ヒートマップ(業務課題診断)
業務領域と評価軸の交点をスコアの濃淡で示し、最高スコアを行・列の座標照準として1件強調する。同じcell identityと座標locatorを保ったまま全体分布から重点課題上位5件へ切り替えられ、補足は図を覆わず下で確認できる
heatmap · assessment · risk · prioritization · data-driven · interactive
LIVE SAMPLE
プレビュー
補足の開閉・表示切替を試せます
こういうときに使う
- 部門・業務領域を複数のリスク軸や成熟度軸で横断評価する場合
- システム刷新・統制改善・DX施策の重点着手領域を決めたい場合
- 多数の交点からスコアの偏りと上位課題を同時に説明したい場合
- 2軸へ項目を配置するのではなく、行と列の全組み合わせを評価する場合
ALTERNATIVES
関連する型
型番 matrix-2x2
2x2 マトリクス(優先度マップ)
項目を2軸のスコアで座標平面に自動配置し、優先順位やポジショニングをデータ駆動で示す。補足は図の下で明示的に展開し、マップ⇄リストを切り替えられる
型番 comparison-table
比較表(製品・ベンダー選定)
製品・ベンダーを評価軸ごとに比較する表。○の数が最も多い列をデータから自動判定し、見出し・各評価の加点/非加点/参考・集計を一続きの推奨spineで示す。390pxでは評価軸ごとの全候補ledgerへ転置し、補足は表の下で明示的に展開できる
型番 raci-matrix
RACIマトリクス(責任分担・統制診断)
活動と役割の交点へR/A/C/Iを配置し、最終責任者Aの不在・重複と実行責任者Rの不在をデータから自動検出する。責任分担表、統制課題ledger、mobile activity ledgerが同じ活動・役割順・記号を共有し、診断を元の根拠へ戻して読める
データスキーマ
- title
- string — スライドタイトル。最高スコアの交点を結論として書く
- subtitle
- string(任意)— 評価時点・評価方法・対象範囲・出典など
- scale
- { min, max, lowLabel, highLabel } — スコア範囲と凡例ラベル。max は min より大きくする
- columns
- string[] — 評価軸(2〜6件)
- rows
- { label, cells }[] — 業務領域(2〜6件)。cells は columns と同じ順・同じ件数
- rows[].cells
- { score, note? }[] — score は scale の範囲内。色濃度・最高点・重点課題ランキングの唯一の出典。note は図の下の補足欄で展開
サンプルコード
template.tsx577 行
import { useId } from "react";
import type {
HeatmapCell,
HeatmapProps,
HeatmapRow,
} from "./schema";
export type FlatHeatmapCell = HeatmapCell & {
rowLabel: string;
columnLabel: string;
rowIndex: number;
columnIndex: number;
originalIndex: number;
identity: string;
coordinate: string;
};
export function cellIdentity(rowIndex: number, columnIndex: number) {
return `r${rowIndex}-c${columnIndex}`;
}
export function cellCoordinate(rowIndex: number, columnIndex: number) {
return `R${String(rowIndex + 1).padStart(2, "0")} · C${String(
columnIndex + 1,
).padStart(2, "0")}`;
}
/** スコアを0〜1へ写す。描画関数として範囲外入力にも防御的に対応する。 */
export function normalizeScore(score: number, min: number, max: number) {
if (max === min) return 0.5;
return Math.max(0, Math.min(1, (score - min) / (max - min)));
}
/** 正規化した値を0〜4の5段階へ写す。最大値だけは必ず最濃色になる。 */
export function intensityBand(score: number, min: number, max: number) {
return Math.min(4, Math.floor(normalizeScore(score, min, max) * 5));
}
/** 行列を入力順つきの一次元セルへ変換する。 */
export function flattenCells(
rows: readonly HeatmapRow[],
columns: readonly string[],
): FlatHeatmapCell[] {
return rows.flatMap((row, rowIndex) =>
row.cells.map((cell, columnIndex) => ({
...cell,
rowLabel: row.label,
columnLabel: columns[columnIndex] ?? "",
rowIndex,
columnIndex,
originalIndex: rowIndex * columns.length + columnIndex,
identity: cellIdentity(rowIndex, columnIndex),
coordinate: cellCoordinate(rowIndex, columnIndex),
})),
);
}
/** スコア降順。同点は元の行列順を保ち、入力が同じなら常に同じ結果になる。 */
export function rankedCells(
rows: readonly HeatmapRow[],
columns: readonly string[],
) {
return flattenCells(rows, columns).sort(
(a, b) => b.score - a.score || a.originalIndex - b.originalIndex,
);
}
/** 最高スコア1件を返す。空の行列はnull。 */
export function priorityCell(
rows: readonly HeatmapRow[],
columns: readonly string[],
) {
return rankedCells(rows, columns)[0] ?? null;
}
const BAND_CLASSES = [
"border-outline bg-surface-subtle text-secondary",
"border-tertiary/10 bg-tertiary/10 text-on-surface",
"border-tertiary/20 bg-tertiary/20 text-on-surface",
"border-tertiary/30 bg-tertiary/40 text-on-surface",
"border-primary-container bg-primary-container text-on-primary",
] as const;
function stagger(index: number) {
return `calc(var(--stagger-step) * ${index})`;
}
function scoreLabel(score: number) {
return new Intl.NumberFormat("ja-JP", { maximumFractionDigits: 2 }).format(
score,
);
}
function CellLocator({
rows,
columns,
cell,
priority,
}: {
rows: readonly HeatmapRow[];
columns: readonly string[];
cell: FlatHeatmapCell;
priority: boolean;
}) {
return (
<span
aria-hidden="true"
className="grid size-10 shrink-0 gap-px border border-outline bg-outline"
style={{
gridTemplateColumns: `repeat(${columns.length}, minmax(0, 1fr))`,
}}
>
{rows.flatMap((_, rowIndex) =>
columns.map((__, columnIndex) => {
const active =
rowIndex === cell.rowIndex && columnIndex === cell.columnIndex;
return (
<span
key={cellIdentity(rowIndex, columnIndex)}
data-heatmap-locator-active={active ? "true" : undefined}
data-row-index={active ? rowIndex : undefined}
data-column-index={active ? columnIndex : undefined}
className={
active
? priority
? "bg-gradient-to-br from-tertiary to-accent-violet"
: "bg-primary"
: "bg-surface"
}
/>
);
}),
)}
</span>
);
}
export default function Heatmap({
title,
subtitle,
scale,
columns,
rows,
instanceId,
}: HeatmapProps) {
const autoId = useId();
const uid = (instanceId ?? autoId).replace(/[^a-zA-Z0-9_-]/g, "") || "heatmap";
const radioName = `heatmap-view-${uid}`;
const cells = flattenCells(rows, columns);
const top = priorityCell(rows, columns);
const topFive = rankedCells(rows, columns).slice(0, 5);
const notes = cells.filter((cell) => cell.note);
const interactionCss = cells
.map(
(cell) => `
[data-heatmap-root="${uid}"]:has([data-heatmap-hotspot="${cell.identity}"]:is(:hover,:focus-visible)) [data-heatmap-row-header="${cell.rowIndex}"] {
background-color: color-mix(in srgb, var(--color-primary) 9%, var(--color-surface));
box-shadow: inset 3px 0 0 var(--color-primary);
color: var(--color-on-surface);
}
[data-heatmap-root="${uid}"]:has([data-heatmap-hotspot="${cell.identity}"]:is(:hover,:focus-visible)) [data-heatmap-column-header="${cell.columnIndex}"] {
background-color: color-mix(in srgb, var(--color-primary) 9%, var(--color-surface));
box-shadow: inset 0 -3px 0 var(--color-primary);
color: var(--color-on-surface);
}
[data-heatmap-root="${uid}"]:has([data-heatmap-hotspot="${cell.identity}"]:is(:hover,:focus-visible)) [data-heatmap-surface="${cell.identity}"] {
border-color: var(--color-primary);
box-shadow: inset 0 0 0 2px var(--color-primary);
color: var(--color-on-surface);
}
[data-heatmap-root="${uid}"]:has([data-heatmap-hotspot="${cell.identity}"]:is(:hover,:focus-visible)) [data-heatmap-default-priority="true"]:not([data-heatmap-surface="${cell.identity}"]) {
filter: saturate(.28) brightness(1.08);
}`,
)
.join("\n");
return (
<section
data-heatmap-root={uid}
className="group/heatmap mx-auto max-w-slide bg-surface p-12 font-sans text-on-surface"
>
<style>{interactionCss}</style>
<header className="mb-6 flex flex-wrap items-end justify-between gap-4">
<div className="max-w-3xl">
<h1 className="text-h1">{title}</h1>
{subtitle && (
<p className="mt-2 text-body-sm text-secondary">{subtitle}</p>
)}
</div>
<div className="flex shrink-0 rounded-sm border border-outline bg-surface-subtle p-1 shadow-card">
<input
id={`${uid}-overview`}
type="radio"
name={radioName}
defaultChecked
className="heatmap-view-overview sr-only"
/>
<label
htmlFor={`${uid}-overview`}
className="cursor-pointer rounded-sm px-4 py-1.5 text-label text-secondary group-has-[.heatmap-view-overview:checked]/heatmap:bg-primary group-has-[.heatmap-view-overview:checked]/heatmap:text-on-primary group-has-[.heatmap-view-overview:focus-visible]/heatmap:ring-2 group-has-[.heatmap-view-overview:focus-visible]/heatmap:ring-tertiary"
>
分布
</label>
<input
id={`${uid}-priorities`}
type="radio"
name={radioName}
className="heatmap-view-priorities sr-only"
/>
<label
htmlFor={`${uid}-priorities`}
className="cursor-pointer rounded-sm px-4 py-1.5 text-label text-secondary group-has-[.heatmap-view-priorities:checked]/heatmap:bg-primary group-has-[.heatmap-view-priorities:checked]/heatmap:text-on-primary group-has-[.heatmap-view-priorities:focus-visible]/heatmap:ring-2 group-has-[.heatmap-view-priorities:focus-visible]/heatmap:ring-tertiary"
>
重点課題
</label>
</div>
</header>
<div
data-testid="heatmap-overview"
className="block group-has-[.heatmap-view-priorities:checked]/heatmap:hidden"
>
{top && (
<div className="mb-5 grid gap-2 border-y border-outline py-4 sm:grid-cols-[auto_minmax(0,1fr)_auto] sm:items-center sm:gap-5">
<div>
<p className="font-mono text-label text-tertiary">
PRIORITY COORDINATE
</p>
<p className="mt-0.5 font-mono text-h3">{top.coordinate}</p>
</div>
<p className="min-w-0 text-body-sm">
<span className="font-semibold">{top.rowLabel}</span>
<span className="mx-2 text-secondary">×</span>
<span>{top.columnLabel}</span>
</p>
<p className="font-mono text-h2 sm:text-right">
{scoreLabel(top.score)}
<span className="ml-1 text-label text-secondary">
/ {scoreLabel(scale.max)}
</span>
</p>
</div>
)}
<div data-heatmap-view="matrix-desktop" className="hidden sm:block">
<table className="w-full table-fixed border-collapse">
<colgroup>
<col className="w-[28%]" />
{columns.map((_, columnIndex) => (
<col key={columnIndex} />
))}
</colgroup>
<thead>
<tr className="border-y border-outline">
<th className="px-3 py-3 text-left font-mono text-label text-secondary">
AREA / AXIS
</th>
{columns.map((column, columnIndex) => (
<th
key={columnIndex}
scope="col"
data-heatmap-column-header={columnIndex}
data-heatmap-default-axis={
columnIndex === top?.columnIndex ? "column" : undefined
}
className={`border-l border-outline px-2 py-3 text-center text-label transition-[background-color,box-shadow,color] ${
columnIndex === top?.columnIndex
? "bg-tertiary-container text-on-tertiary-container"
: "text-secondary"
}`}
>
<span className="mb-1 block font-mono text-label opacity-70">
C{String(columnIndex + 1).padStart(2, "0")}
</span>
{column}
</th>
))}
</tr>
</thead>
<tbody>
{rows.map((row, rowIndex) => (
<tr key={rowIndex}>
<th
scope="row"
data-heatmap-row-header={rowIndex}
data-heatmap-default-axis={
rowIndex === top?.rowIndex ? "row" : undefined
}
className={`border-b border-outline px-3 py-3 text-left text-label transition-[background-color,box-shadow,color] ${
rowIndex === top?.rowIndex
? "bg-tertiary-container text-on-tertiary-container"
: "text-on-surface"
}`}
>
<span className="mb-1 block font-mono text-label opacity-70">
R{String(rowIndex + 1).padStart(2, "0")}
</span>
{row.label}
</th>
{row.cells.map((cell, columnIndex) => {
const flat = cells.find(
(candidate) =>
candidate.rowIndex === rowIndex &&
candidate.columnIndex === columnIndex,
) as FlatHeatmapCell;
const isPriority = flat.originalIndex === top?.originalIndex;
const band = intensityBand(cell.score, scale.min, scale.max);
return (
<td
key={columnIndex}
data-testid="heatmap-cell"
className="animate-rise border-b border-l border-outline p-1.5"
style={{ animationDelay: stagger(flat.originalIndex) }}
>
<button
type="button"
data-row-index={rowIndex}
data-column-index={columnIndex}
data-heatmap-cell-id={flat.identity}
data-heatmap-hotspot={flat.identity}
data-heatmap-surface={flat.identity}
data-heatmap-default-priority={
isPriority ? "true" : undefined
}
data-priority={isPriority ? "true" : "false"}
aria-label={`${flat.coordinate} ${row.label} × ${columns[columnIndex]}: ${scoreLabel(cell.score)}`}
className={`relative flex min-h-16 w-full cursor-default flex-col items-center justify-center border p-2 text-center outline-none transition-[border-color,box-shadow,color,filter] focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 ${
isPriority
? "border-tertiary bg-gradient-to-br from-tertiary to-accent-violet text-on-primary"
: BAND_CLASSES[band]
}`}
>
<span className="font-mono text-h3">
{scoreLabel(cell.score)}
</span>
<span className="mt-0.5 font-mono text-label opacity-70">
{flat.coordinate.replace(" · ", "/")}
</span>
{isPriority && (
<span className="absolute top-1 right-1 border-l border-surface/40 pl-1 font-mono text-label text-on-primary">
重点
</span>
)}
</button>
</td>
);
})}
</tr>
))}
</tbody>
</table>
</div>
<div data-heatmap-view="matrix-mobile" className="sm:hidden">
<div className="mb-3 grid grid-cols-2 gap-x-4 gap-y-2 border-y border-outline py-3">
{columns.map((column, columnIndex) => (
<div
key={columnIndex}
data-heatmap-column-header={columnIndex}
data-heatmap-default-axis={
columnIndex === top?.columnIndex ? "column" : undefined
}
className={`flex min-w-0 items-baseline gap-2 px-1 py-1 transition-[background-color,box-shadow,color] ${
columnIndex === top?.columnIndex
? "bg-tertiary-container text-on-tertiary-container"
: "text-secondary"
}`}
>
<span className="font-mono text-label">
C{String(columnIndex + 1).padStart(2, "0")}
</span>
<span className="min-w-0 text-label">{column}</span>
</div>
))}
</div>
<div className="border-t border-outline">
{rows.map((row, rowIndex) => (
<article
key={rowIndex}
data-heatmap-mobile-row={rowIndex}
className="border-b border-outline py-4"
>
<h2
data-heatmap-row-header={rowIndex}
data-heatmap-default-axis={
rowIndex === top?.rowIndex ? "row" : undefined
}
className={`mb-2 flex items-center gap-2 px-1 py-1 text-body-sm transition-[background-color,box-shadow,color] ${
rowIndex === top?.rowIndex
? "bg-tertiary-container text-on-tertiary-container"
: "text-on-surface"
}`}
>
<span className="font-mono text-label opacity-70">
R{String(rowIndex + 1).padStart(2, "0")}
</span>
{row.label}
</h2>
<div
className="grid gap-1"
style={{
gridTemplateColumns: `repeat(${columns.length}, minmax(0, 1fr))`,
}}
>
{row.cells.map((cell, columnIndex) => {
const flat = cells[
rowIndex * columns.length + columnIndex
] as FlatHeatmapCell;
const isPriority =
flat.originalIndex === top?.originalIndex;
const band = intensityBand(
cell.score,
scale.min,
scale.max,
);
return (
<button
type="button"
key={columnIndex}
data-testid="heatmap-cell"
data-row-index={rowIndex}
data-column-index={columnIndex}
data-heatmap-cell-id={flat.identity}
data-heatmap-hotspot={flat.identity}
data-heatmap-surface={flat.identity}
data-heatmap-default-priority={
isPriority ? "true" : undefined
}
data-priority={isPriority ? "true" : "false"}
aria-label={`${flat.coordinate} ${row.label} × ${columns[columnIndex]}: ${scoreLabel(cell.score)}`}
className={`flex min-w-0 cursor-default flex-col items-center border px-1 py-2 outline-none transition-[border-color,box-shadow,color,filter] focus-visible:ring-2 focus-visible:ring-primary ${
isPriority
? "border-tertiary bg-gradient-to-br from-tertiary to-accent-violet text-on-primary"
: BAND_CLASSES[band]
}`}
>
<span className="font-mono text-label opacity-70">
C{String(columnIndex + 1).padStart(2, "0")}
</span>
<span className="font-mono text-h3">
{scoreLabel(cell.score)}
</span>
</button>
);
})}
</div>
</article>
))}
</div>
</div>
<div className="mt-4 flex flex-wrap items-center justify-end gap-3 font-mono text-label text-secondary">
<span>{scale.lowLabel}</span>
<div className="flex gap-1" aria-label={`${scale.lowLabel}から${scale.highLabel}`}>
{BAND_CLASSES.map((className, index) => (
<span
key={index}
aria-hidden="true"
className={`h-2 w-8 rounded-full border ${className}`}
/>
))}
</div>
<span>{scale.highLabel}</span>
</div>
</div>
<div
data-testid="heatmap-priorities"
className="hidden group-has-[.heatmap-view-priorities:checked]/heatmap:block"
>
<div className="mb-4 flex flex-wrap items-baseline justify-between gap-4 border-y border-outline py-4">
<div>
<p className="font-mono text-label text-tertiary">
PRIORITY LOCATOR
</p>
<h2 className="mt-1 text-h2">優先着手 上位5件</h2>
</div>
<p className="font-mono text-label text-secondary">
score / {scoreLabel(scale.max)}
</p>
</div>
<div className="border-t border-outline">
{topFive.map((cell, index) => {
const isPriority = index === 0;
const width = Math.max(
6,
normalizeScore(cell.score, scale.min, scale.max) * 100,
);
return (
<article
key={cell.originalIndex}
data-testid="heatmap-priority-row"
data-heatmap-cell-id={cell.identity}
data-heatmap-surface={cell.identity}
data-heatmap-default-priority={
isPriority ? "true" : undefined
}
data-row-index={cell.rowIndex}
data-column-index={cell.columnIndex}
data-score={cell.score}
className={`animate-rise border-b transition-[border-color,box-shadow,color,filter] ${
isPriority
? "border-tertiary bg-gradient-to-r from-tertiary/10 via-surface to-surface"
: "border-outline bg-surface"
}`}
style={{ animationDelay: stagger(index) }}
>
<button
type="button"
data-heatmap-hotspot={cell.identity}
aria-label={`${String(index + 1)}位 ${cell.coordinate} ${cell.rowLabel} × ${cell.columnLabel}: ${scoreLabel(cell.score)}`}
className="grid w-full cursor-default grid-cols-[2.5rem_3rem_minmax(0,1fr)_auto] items-center gap-3 px-1 py-4 text-left outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 sm:gap-4"
>
<span className="font-mono text-label text-secondary">
{String(index + 1).padStart(2, "0")}
</span>
<span>
<CellLocator
rows={rows}
columns={columns}
cell={cell}
priority={isPriority}
/>
</span>
<div className="min-w-0">
<p className="font-mono text-label text-tertiary">
{cell.coordinate}
</p>
<p className="mt-0.5 text-label text-on-surface">
{cell.rowLabel}
<span className="mx-2 text-secondary">×</span>
{cell.columnLabel}
</p>
<div className="mt-2 h-1.5 overflow-hidden bg-surface-subtle">
<span
className={`block h-full ${
isPriority
? "bg-gradient-to-r from-tertiary to-accent-violet"
: "bg-tertiary"
}`}
style={{ width: `${width}%` }}
/>
</div>
</div>
<span className="font-mono text-h3">
{scoreLabel(cell.score)}
</span>
</button>
</article>
);
})}
</div>
</div>
{notes.length > 0 && (
<details data-inline-disclosure="true" className="group mt-6 rounded-lg border border-outline bg-surface-subtle">
<summary className="flex cursor-pointer list-none items-center px-4 py-3 text-label text-secondary focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tertiary">
<span>補足を確認</span>
<span className="ml-auto font-mono">{notes.length}件</span>
<span aria-hidden="true" className="ml-2 text-tertiary transition-transform group-open:rotate-45">+</span>
</summary>
<div className="grid gap-3 border-t border-outline p-4 sm:grid-cols-2">
{notes.map((cell) => (
<article key={cell.originalIndex} data-note-item="true" className="rounded-md bg-surface p-4">
<h3 className="text-label">{cell.rowLabel} × {cell.columnLabel}</h3>
<p className="mt-1 font-mono text-label text-secondary">score {scoreLabel(cell.score)}</p>
<p className="mt-2 text-body-sm">{cell.note}</p>
</article>
))}
</div>
</details>
)}
</section>
);
}
schema.ts90 行
import { z } from "zod";
export const heatmapScaleSchema = z
.object({
min: z.number().describe("評価スコアの最小値"),
max: z.number().describe("評価スコアの最大値。minより大きい値"),
lowLabel: z.string().describe("凡例の最小側ラベル(例: 低)"),
highLabel: z.string().describe("凡例の最大側ラベル(例: 高)"),
})
.strict()
.refine((scale) => scale.max > scale.min, {
message: "scale.max は scale.min より大きくすること",
path: ["max"],
});
export const heatmapCellSchema = z
.object({
score: z
.number()
.describe(
"行と列の交点の評価スコア。scale.min〜scale.maxの範囲に収める",
),
note: z
.string()
.optional()
.describe("図の下の補足欄で明示的に展開する根拠・観察事実・対策の補足"),
})
.strict();
export const heatmapRowSchema = z
.object({
label: z.string().describe("行に置く業務領域・部門・対象名"),
cells: z
.array(heatmapCellSchema)
.min(2)
.max(6)
.describe("columnsと同じ順・同じ件数の評価セル(2〜6件)"),
})
.strict();
export const schema = z
.object({
title: z.string().describe("スライドタイトル。最優先の交点を結論として書く"),
subtitle: z
.string()
.optional()
.describe("評価時点、評価方法、対象範囲、出典など"),
scale: heatmapScaleSchema,
columns: z
.array(z.string())
.min(2)
.max(6)
.describe("列に置く評価軸(2〜6件)"),
rows: z
.array(heatmapRowSchema)
.min(2)
.max(6)
.describe("行に置く業務領域とセル(2〜6件)"),
instanceId: z
.string()
.optional()
.describe("同一ページに複数置く場合のビュー切替radio名の識別子"),
})
.strict()
.superRefine((data, ctx) => {
data.rows.forEach((row, rowIndex) => {
if (row.cells.length !== data.columns.length) {
ctx.addIssue({
code: "custom",
message: `cells は columns と同じ${data.columns.length}件にすること`,
path: ["rows", rowIndex, "cells"],
});
}
row.cells.forEach((cell, columnIndex) => {
if (cell.score < data.scale.min || cell.score > data.scale.max) {
ctx.addIssue({
code: "custom",
message: `score は ${data.scale.min}〜${data.scale.max} の範囲にすること`,
path: ["rows", rowIndex, "cells", columnIndex, "score"],
});
}
});
});
});
export type HeatmapScale = z.infer<typeof heatmapScaleSchema>;
export type HeatmapCell = z.infer<typeof heatmapCellSchema>;
export type HeatmapRow = z.infer<typeof heatmapRowSchema>;
export type HeatmapProps = z.infer<typeof schema>;
sample-data.json59 行
{
"title": "基幹業務の刷新優先度 — 請求・入金の事業影響から着手する",
"subtitle": "5業務領域 × 4評価軸を5段階で診断(2026年7月 業務部門ヒアリング)",
"scale": {
"min": 1,
"max": 5,
"lowLabel": "低",
"highLabel": "高"
},
"columns": ["事業影響", "発生頻度", "検知困難性", "改善緊急度"],
"rows": [
{
"label": "営業・顧客管理",
"cells": [
{ "score": 3 },
{ "score": 4, "note": "顧客情報の重複登録が月平均38件発生" },
{ "score": 2 },
{ "score": 3 }
]
},
{
"label": "受注・契約",
"cells": [
{ "score": 4, "note": "契約条件の転記確認に1案件あたり約25分を要する" },
{ "score": 4 },
{ "score": 3 },
{ "score": 4 }
]
},
{
"label": "調達・在庫",
"cells": [
{ "score": 3 },
{ "score": 3 },
{ "score": 4, "note": "棚卸差異は月末まで顕在化せず、補充判断が遅れる" },
{ "score": 3 }
]
},
{
"label": "請求・入金",
"cells": [
{ "score": 5, "note": "請求遅延が売上計上と資金繰りの双方へ直結する" },
{ "score": 4 },
{ "score": 4 },
{ "score": 4, "note": "属人化した照合作業を次期更新前に標準化する必要がある" }
]
},
{
"label": "経営管理",
"cells": [
{ "score": 4 },
{ "score": 3 },
{ "score": 4, "note": "部門別採算の確定が翌月第8営業日まで遅れる" },
{ "score": 3 }
]
}
],
"instanceId": "core-business-risk"
}