95 lines
5.9 KiB
HTML
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>
|