Files
HMDesign/docs/patterns/dashboard-status-static.html
T

95 lines
5.9 KiB
HTML

<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>HM Dashboard Status Contract - Static HTML</title>
<link rel="stylesheet" href="../../packages/ui/dist/theme.css" />
<link rel="stylesheet" href="../../src/patterns/dashboard-status-contract.css" />
<style>
@font-face { font-family: "Noto Sans KR"; src: url("../../public/fonts/NotoSansKR-variable.ttf") format("truetype"); font-weight: 100 900; }
:root {
--dashboard-work-progress: var(--hm-dashboard-status-active);
--dashboard-work-hold: var(--hm-dashboard-status-hold);
--dashboard-work-issue: var(--hm-dashboard-status-issue);
--dashboard-work-surface: var(--hm-sem-palette-green-dark-2);
}
body { margin: 0; min-width: 960px; background: var(--hm-sem-bg-content); color: var(--hm-sem-fg-primary); font-family: "Noto Sans KR", sans-serif; }
main { max-width: 1120px; margin: 0 auto; padding: 32px; }
.panel { padding: 24px; border-radius: var(--hm-radius-container); background: var(--hm-sem-bg-surface); box-shadow: var(--hm-card-shadow); }
.toolbar, .filters, .badges { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.toolbar { justify-content: space-between; margin-bottom: 20px; }
.filters { padding: 10px; border-radius: var(--hm-radius-container); background: var(--dashboard-work-surface); }
button { min-height: 32px; padding: 0 12px; border: 1px solid color-mix(in srgb, var(--hm-sem-palette-black) 50%, transparent); border-radius: var(--hm-radius-input); background: color-mix(in srgb, var(--hm-sem-palette-black) 35%, transparent); color: var(--hm-sem-palette-white); font: inherit; cursor: pointer; }
button:hover { background: color-mix(in srgb, var(--hm-sem-palette-black) 55%, transparent); }
button:focus-visible { outline: var(--hm-dashboard-nav-focus-outline); }
button[aria-pressed="true"] { background: color-mix(in srgb, var(--hm-sem-palette-green-dark-2) 90%, transparent); border-color: var(--hm-sem-palette-green-light-4); }
button[data-filter="issue"] { background: color-mix(in srgb, var(--hm-sem-action-destructive) 25%, transparent); }
[data-slot="badge"] { background: color-mix(in srgb, var(--hm-sem-palette-green-dark-2) 90%, transparent); }
[data-slot="badge"][data-status] { color: var(--dashboard-status-color); border-color: color-mix(in srgb, var(--hm-sem-palette-black) 50%, transparent); }
[data-status="active"] { --dashboard-status-color: var(--dashboard-work-progress); }
[data-status="hold"] { --dashboard-status-color: var(--dashboard-work-hold); }
[data-status="done"] { --dashboard-status-color: var(--hm-dashboard-status-done); }
[data-status="issue"][data-issue="true"] { --dashboard-status-color: var(--dashboard-work-issue); }
.count { margin-left: auto; color: var(--hm-sem-fg-secondary); }
.result { margin-top: 16px; }
[hidden] { display: none !important; }
</style>
</head>
<body>
<main>
<section class="panel" aria-labelledby="title">
<div class="toolbar">
<div><h1 id="title">Dashboard 업무 상태</h1><p id="summary" role="status">전체 업무 0건</p></div>
<div class="filters" role="group" aria-label="업무 상태 필터" data-status-filter-group></div>
</div>
<div class="result" aria-live="polite"><div class="badges" id="results" aria-label="필터 결과"></div><p id="empty" hidden>조건에 맞는 업무가 없습니다.</p></div>
</section>
</main>
<script>
const contract = {
filters: ["all", "active", "hold", "done", "issue"],
labels: { all: "전체", active: "진행", hold: "보류", done: "완료", issue: "이슈" },
records: [
{ name: "대시보드 화면 검토", status: "active" },
{ name: "업무 자료 정리", status: "hold", issue: true },
{ name: "현장 점검 보고", status: "done" },
],
};
const state = { selected: "all" };
const matches = (record, filter) => filter === "all" || (filter === "issue" ? record.issue === true : record.status === filter);
const count = (filter) => contract.records.filter((record) => matches(record, filter)).length;
const group = document.querySelector("[data-status-filter-group]");
const results = document.querySelector("#results");
const summary = document.querySelector("#summary");
const empty = document.querySelector("#empty");
function render() {
group.replaceChildren(...contract.filters.map((filter) => {
const button = document.createElement("button");
button.type = "button";
button.dataset.filter = filter;
button.dataset.status = filter;
if (filter === "issue") button.dataset.issue = "true";
button.setAttribute("aria-pressed", String(state.selected === filter));
button.setAttribute("aria-controls", "results");
button.textContent = `${contract.labels[filter]} ${count(filter)}건`;
button.addEventListener("click", () => { state.selected = filter; render(); });
return button;
}));
const filtered = contract.records.filter((record) => matches(record, state.selected));
results.replaceChildren(...filtered.map((record) => {
const item = document.createElement("span");
item.dataset.slot = "badge";
item.dataset.status = record.status;
if (record.issue) item.dataset.issue = "true";
item.textContent = `${record.name} · ${contract.labels[record.status]}${record.issue ? " · 이슈" : ""}`;
return item;
}));
summary.textContent = `${contract.labels[state.selected]} 업무 ${filtered.length}건`;
empty.hidden = filtered.length !== 0;
}
render();
</script>
</body>
</html>