@
chore: delete old layout/common/tabs components before redesign @
This commit is contained in:
@@ -0,0 +1,535 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>AI + Compliance Hub - Compliance Analysis</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #fafafa;
|
||||
--surface: #ffffff;
|
||||
--surface-warm: var(--surface);
|
||||
--fg: #111111;
|
||||
--fg-2: var(--fg);
|
||||
--muted: #6b6b6b;
|
||||
--meta: var(--muted);
|
||||
--border: #e5e5e5;
|
||||
--border-soft: var(--border);
|
||||
--primary: #e20074;
|
||||
--accent: var(--primary);
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), black 8%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 14%);
|
||||
--success: #17a34a;
|
||||
--warn: #eab308;
|
||||
--danger: #dc2626;
|
||||
--font-display: "TeleNeoWeb-Bold", "TeleNeoWeb-Medium", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-body: "TeleNeoWeb-Regular", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-mono: ui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospace;
|
||||
--text-xs: 12px;
|
||||
--text-sm: 14px;
|
||||
--text-base: 16px;
|
||||
--text-lg: 20px;
|
||||
--text-xl: 24px;
|
||||
--text-2xl: 32px;
|
||||
--text-3xl: 48px;
|
||||
--text-4xl: 64px;
|
||||
--leading-body: 1.5;
|
||||
--leading-tight: 1.2;
|
||||
--tracking-display: -0.01em;
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px;
|
||||
--space-5: 20px;
|
||||
--space-6: 24px;
|
||||
--space-8: 32px;
|
||||
--space-12: 48px;
|
||||
--space-20: 80px;
|
||||
--section-y-desktop: 80px;
|
||||
--section-y-tablet: 48px;
|
||||
--section-y-phone: 32px;
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 16px;
|
||||
--radius-pill: 9999px;
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 2px 8px color-mix(in oklab, var(--fg), transparent 92%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
--container-max: 1600px;
|
||||
--container-gutter-desktop: 24px;
|
||||
--container-gutter-tablet: 16px;
|
||||
--container-gutter-phone: 12px;
|
||||
--sidebar-w: 240px;
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme="light"]) {
|
||||
--bg: #0f1014;
|
||||
--surface: #17181d;
|
||||
--surface-warm: #1d1f26;
|
||||
--fg: #f5f7fb;
|
||||
--fg-2: #e5e8ef;
|
||||
--muted: #a2a9b8;
|
||||
--meta: #858d9c;
|
||||
--border: #2a2d35;
|
||||
--border-soft: #21242c;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), white 12%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 6%);
|
||||
--success: #22c55e;
|
||||
--warn: #facc15;
|
||||
--danger: #f87171;
|
||||
--elev-raised: 0 14px 36px color-mix(in oklab, black, transparent 74%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 56%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
|
||||
:root[data-theme="dark"] {
|
||||
--bg: #0f1014;
|
||||
--surface: #17181d;
|
||||
--surface-warm: #1d1f26;
|
||||
--fg: #f5f7fb;
|
||||
--fg-2: #e5e8ef;
|
||||
--muted: #a2a9b8;
|
||||
--meta: #858d9c;
|
||||
--border: #2a2d35;
|
||||
--border-soft: #21242c;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), white 12%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 6%);
|
||||
--success: #22c55e;
|
||||
--warn: #facc15;
|
||||
--danger: #f87171;
|
||||
--elev-raised: 0 14px 36px color-mix(in oklab, black, transparent 74%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 56%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
:root[data-theme="light"] {
|
||||
color-scheme: light;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--leading-body);
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
h1, h2, h3, h4 {
|
||||
margin: 0;
|
||||
font-family: var(--font-display);
|
||||
line-height: var(--leading-tight);
|
||||
letter-spacing: var(--tracking-display);
|
||||
}
|
||||
p { margin: 0; text-wrap: pretty; }
|
||||
a { color: inherit; text-decoration: none; }
|
||||
a:hover { color: var(--fg); text-decoration: underline; }
|
||||
button { font: inherit; }
|
||||
|
||||
/* ── Sidebar shell ── */
|
||||
.app-shell { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }
|
||||
.sidebar { position: sticky; top: 0; height: 100vh; overflow-y: auto; display: flex; flex-direction: column; background: var(--surface); border-right: 1px solid var(--border); z-index: 10; }
|
||||
.sidebar-brand { display: flex; align-items: center; gap: 10px; height: 56px; padding: 0 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
|
||||
.brand-logo { width: 26px; height: 26px; background: var(--accent); border-radius: 6px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||||
.brand-logo svg { color: #fff; }
|
||||
.sidebar-brand-name { font-family: var(--font-display); font-size: 13px; font-weight: 700; line-height: 1.2; }
|
||||
.sidebar-brand-sub { font-size: 10px; color: var(--muted); font-family: var(--font-mono); letter-spacing: 0.04em; }
|
||||
.sidebar-nav { flex: 1; padding: 12px 0; overflow-y: auto; }
|
||||
.nav-group { padding: 0 8px 4px; }
|
||||
.nav-group + .nav-group { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
|
||||
.nav-group-label { font-family: var(--font-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); padding: 0 8px 6px; display: block; }
|
||||
.nav-item { display: flex; align-items: center; gap: 10px; height: 36px; padding: 0 8px; border-radius: 6px; color: var(--muted); font-size: 13px; cursor: pointer; transition: background 140ms, color 140ms; position: relative; }
|
||||
.nav-item:hover { background: color-mix(in oklab, var(--fg), transparent 94%); color: var(--fg); text-decoration: none; }
|
||||
.nav-item.active { background: color-mix(in oklab, var(--accent), transparent 90%); color: var(--accent); font-weight: 600; }
|
||||
.nav-item.active::before { content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 3px; border-radius: 0 3px 3px 0; background: var(--accent); }
|
||||
.nav-icon { width: 16px; height: 16px; flex-shrink: 0; opacity: 0.7; }
|
||||
.nav-item.active .nav-icon { opacity: 1; }
|
||||
.sidebar-footer { border-top: 1px solid var(--border); padding: 10px 8px; flex-shrink: 0; display: flex; flex-direction: column; gap: 4px; }
|
||||
.sidebar-user { display: flex; align-items: center; gap: 10px; padding: 8px; border-radius: 6px; cursor: pointer; }
|
||||
.sidebar-user:hover { background: color-mix(in oklab, var(--fg), transparent 94%); }
|
||||
.avatar { width: 30px; height: 30px; border-radius: 50%; background: var(--accent); color: #fff; font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||||
.sidebar-user-info { min-width: 0; }
|
||||
.sidebar-user-name { font-size: 13px; font-weight: 600; color: var(--fg); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.sidebar-user-role { font-size: 11px; color: var(--muted); font-family: var(--font-mono); }
|
||||
.sidebar-action { display: flex; align-items: center; gap: 10px; height: 34px; padding: 0 8px; border-radius: 6px; color: var(--muted); font-size: 13px; cursor: pointer; border: none; background: transparent; width: 100%; text-align: left; transition: background 140ms, color 140ms; }
|
||||
.sidebar-action:hover { background: color-mix(in oklab, var(--fg), transparent 94%); color: var(--fg); }
|
||||
.content-area { display: flex; flex-direction: column; min-width: 0; min-height: 100vh; }
|
||||
.content-topbar { position: sticky; top: 0; z-index: 5; display: flex; align-items: center; gap: 12px; height: 56px; padding: 0 24px; border-bottom: 1px solid var(--border); background: color-mix(in oklab, var(--bg), transparent 4%); backdrop-filter: blur(10px); }
|
||||
.topbar-title { font-weight: 600; font-size: 15px; color: var(--fg); flex: 1; }
|
||||
.search { display: flex; align-items: center; gap: 8px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); padding: 0 12px; height: 34px; width: 240px; }
|
||||
.search input { border: 0; outline: none; background: transparent; width: 100%; color: var(--fg); font-size: 13px; }
|
||||
.search input::placeholder { color: var(--muted); }
|
||||
.footer { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-height: 34px; padding: 0 24px; border-top: 1px solid var(--border); color: var(--muted); font-size: 11px; font-family: var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase; }
|
||||
.footer-status { display: inline-flex; align-items: center; gap: 8px; }
|
||||
.footer-dot { width: 7px; height: 7px; border-radius: 50%; background: #19d3a2; box-shadow: 0 0 0 3px color-mix(in oklab, #19d3a2, transparent 82%); }
|
||||
@media (max-width: 700px) { .app-shell { grid-template-columns: 1fr; } .sidebar { display: none; } }
|
||||
|
||||
/* ── Page / component styles ── */
|
||||
.page {
|
||||
max-width: 1600px;
|
||||
margin: 0 auto;
|
||||
padding: 24px;
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
}
|
||||
.hero {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
}
|
||||
.eyebrow {
|
||||
color: var(--accent);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-xs);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.hero h1 { font-size: clamp(30px, 4vw, 44px); }
|
||||
.hero p { color: var(--muted); max-width: 74ch; }
|
||||
.btn {
|
||||
min-height: 44px;
|
||||
padding: 0 16px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
}
|
||||
.btn-primary {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: var(--accent-on);
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background: var(--accent-hover);
|
||||
border-color: var(--accent-hover);
|
||||
}
|
||||
.workspace {
|
||||
display: grid;
|
||||
grid-template-columns: 0.95fr 1.25fr 0.9fr;
|
||||
gap: 16px;
|
||||
min-height: 760px;
|
||||
}
|
||||
.card {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--surface);
|
||||
padding: 18px;
|
||||
min-width: 0;
|
||||
}
|
||||
.section-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.section-head h2 { font-size: var(--text-xl); }
|
||||
.helper {
|
||||
color: var(--muted);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.mono {
|
||||
font-family: var(--font-mono);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.source-list,
|
||||
.finding-list,
|
||||
.actions {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
.source-item,
|
||||
.finding,
|
||||
.action-item,
|
||||
.stage {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: color-mix(in oklab, var(--surface), var(--bg) 14%);
|
||||
padding: 14px;
|
||||
}
|
||||
.pill,
|
||||
.status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: fit-content;
|
||||
padding: 4px 10px;
|
||||
border-radius: var(--radius-pill);
|
||||
border: 1px solid var(--border);
|
||||
font-size: var(--text-xs);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
.status::before {
|
||||
content: "";
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 999px;
|
||||
background: currentColor;
|
||||
}
|
||||
.status.ok { color: var(--success); }
|
||||
.status.warn { color: color-mix(in oklab, var(--warn), black 24%); }
|
||||
.status.risk { color: var(--danger); }
|
||||
.paragraph {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface);
|
||||
padding: 18px;
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
.paragraph mark {
|
||||
background: color-mix(in oklab, var(--accent), white 80%);
|
||||
color: inherit;
|
||||
padding: 0 3px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.stage-list {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.finding strong,
|
||||
.source-item strong,
|
||||
.action-item strong,
|
||||
.stage strong {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.score-row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.progress {
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in oklab, var(--fg), transparent 95%);
|
||||
overflow: hidden;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.progress > span {
|
||||
display: block;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
background: color-mix(in oklab, var(--accent), white 28%);
|
||||
}
|
||||
.conclusion {
|
||||
margin-top: 16px;
|
||||
border-top: 1px solid var(--border);
|
||||
padding-top: 16px;
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
.conclusion-box {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 16px;
|
||||
background: color-mix(in oklab, var(--surface), var(--bg) 10%);
|
||||
}
|
||||
@media (max-width: 1280px) {
|
||||
.workspace { grid-template-columns: 1fr; }
|
||||
.hero { flex-direction: column; align-items: start; }
|
||||
}
|
||||
@media (max-width: 760px) {
|
||||
.page { padding: 12px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body data-page="analysis">
|
||||
<div class="app-shell">
|
||||
<aside class="sidebar" aria-label="Primary navigation">
|
||||
<div class="sidebar-brand">
|
||||
<div class="brand-logo">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M2 3.5h12v1.5H2zm5.25 1.5h1.5v8h-1.5zm-3 2h2.25v1.5H4.25zm5.25 0h2.25v1.5H9.5zm-3 3h2.5v1.5H6.5z" fill="currentColor"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sidebar-brand-name">T-Systems</div>
|
||||
<div class="sidebar-brand-sub">Regulation Hub</div>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="sidebar-nav" aria-label="Primary">
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">主导航</span>
|
||||
<a class="nav-item" href="index.html"><svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M2 2h5v5H2zm7 0h5v5H9zM2 9h5v5H2zm7 0h5v5H9z" fill="currentColor" opacity=".7"/></svg>概览</a>
|
||||
<a class="nav-item" href="dashboard.html"><svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M1.5 2.5h13v1H1.5zm0 3h13v1H1.5zm0 3h8v1h-8zm0 3h6v1h-6z" fill="currentColor"/></svg>系统状态</a>
|
||||
</div>
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">工作台</span>
|
||||
<a class="nav-item" href="document-management.html"><svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M3 1h7l3 3v11H3V1zm1 1v12h8V5h-3V2H4zm5 .5V4h1.5L9 1.5zM6 7h4v1H6zm0 2h4v1H6zm0 2h3v1H6z" fill="currentColor"/></svg>文档管理</a>
|
||||
<a class="nav-item active" href="compliance-analysis.html"><svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M8 1l7 3-1 6a7 7 0 01-6 5A7 7 0 011 10L0 4l8-3zm0 1.2L1.3 4.8l.8 5.1A6 6 0 008 14.8a6 6 0 005.9-4.9l.8-5.1L8 2.2zM7.5 5h1v4.5l-1 .5V5zm0 5.5h1v1h-1v-1z" fill="currentColor"/></svg>合规分析</a>
|
||||
</div>
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">对话</span>
|
||||
<a class="nav-item" href="regulation-chat.html"><svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M2 2h12a1 1 0 011 1v8a1 1 0 01-1 1H5l-3 2.5V3a1 1 0 011-1zm0 1v9.5L4.5 11H14V3H2zm2 2h8v1H4zm0 2h6v1H4z" fill="currentColor"/></svg>法规对话</a>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="sidebar-footer">
|
||||
<div class="sidebar-user">
|
||||
<div class="avatar">TS</div>
|
||||
<div class="sidebar-user-info">
|
||||
<div class="sidebar-user-name">T-Systems User</div>
|
||||
<div class="sidebar-user-role">Compliance Analyst</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="sidebar-action od-theme-toggle" type="button" data-od-theme aria-label="Toggle theme">
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M8 3a5 5 0 100 10A5 5 0 008 3zM2 8a6 6 0 1112 0A6 6 0 012 8z" fill="currentColor"/></svg>
|
||||
主题
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
<div class="content-area">
|
||||
<header class="content-topbar">
|
||||
<div class="topbar-title">合规分析</div>
|
||||
<div class="search">
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M6.5 1a5.5 5.5 0 014.23 9.02l3.62 3.62-.7.71-3.63-3.63A5.5 5.5 0 116.5 1zm0 1a4.5 4.5 0 100 9 4.5 4.5 0 000-9z" fill="currentColor" opacity=".5"/></svg>
|
||||
<input type="search" placeholder="Search findings…" aria-label="Search" />
|
||||
</div>
|
||||
<button class="btn btn-primary">New analysis</button>
|
||||
</header>
|
||||
<main class="page">
|
||||
<section class="hero" data-od-id="analysis-hero">
|
||||
<div>
|
||||
<div class="eyebrow">Compliance analysis workspace</div>
|
||||
<h1>From retrieved regulation to conclusion-ready action.</h1>
|
||||
<p>This screen is built for a reviewer comparing one paragraph of product documentation against candidate regulations, system reasoning, and recommended changes. The rhythm is left-to-right: evidence retrieval, close reading, and decision output.</p>
|
||||
</div>
|
||||
<div style="display:flex; gap:12px; flex-wrap:wrap;">
|
||||
<a class="btn" href="document-detail.html">Open parse detail</a>
|
||||
<a class="btn btn-primary" href="regulation-chat.html">Ask follow-up in chat</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="workspace" data-od-id="analysis-workspace">
|
||||
<aside class="card">
|
||||
<div class="section-head">
|
||||
<h2>Retrieved regulations</h2>
|
||||
<span class="helper">Top 10 by dense similarity</span>
|
||||
</div>
|
||||
<div class="source-list">
|
||||
<div class="source-item">
|
||||
<strong>GB 26112-2010 §4.2 Roof crush resistance</strong>
|
||||
<div class="helper">Primary match · mandatory requirement · promoted as lead citation</div>
|
||||
<div class="score-row">
|
||||
<span class="pill">Vehicle safety</span>
|
||||
<span class="status risk">High impact</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="source-item">
|
||||
<strong>C-NCAP rulebook §3.1 occupant safety context</strong>
|
||||
<div class="helper">Supporting match · interpretation context · not mandatory on its own</div>
|
||||
<div class="score-row">
|
||||
<span class="pill">Assessment</span>
|
||||
<span class="status warn">Context only</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="source-item">
|
||||
<strong>Internal design note: structure validation test plan</strong>
|
||||
<div class="helper">Evidence match · useful support artifact with no direct legal force</div>
|
||||
<div class="score-row">
|
||||
<span class="pill">Evidence</span>
|
||||
<span class="status ok">Supporting</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<section class="card">
|
||||
<div class="section-head">
|
||||
<h2>Document paragraph under review</h2>
|
||||
<span class="helper">Chunk 148 · supplier safety narrative</span>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><strong>Source text</strong></p>
|
||||
<p>The roof support structure is designed to satisfy national crush-resistance requirements, and the module enclosure preserves occupant safety under static load conditions. Validation results are available in the body engineering report and are considered aligned with the current certification baseline.</p>
|
||||
<p><strong>Analysis focus</strong></p>
|
||||
<p>The system flags that the narrative claims compliance but omits the explicit load threshold. The phrase <mark>satisfy national crush-resistance requirements</mark> should be tied to a stated requirement derived from <mark>GB 26112-2010 §4.2</mark> to avoid unsupported compliance language.</p>
|
||||
</div>
|
||||
|
||||
<div class="section-head" style="margin-top:18px;">
|
||||
<h2>Analysis stages</h2>
|
||||
<span class="helper">Streaming reasoning workflow</span>
|
||||
</div>
|
||||
<div class="stage-list">
|
||||
<div class="stage">
|
||||
<strong>1. Clause retrieval</strong>
|
||||
<div class="helper">Dense retrieval found 10 nearby standards; 3 were promoted after category scoring.</div>
|
||||
<div class="progress"><span style="width:100%"></span></div>
|
||||
</div>
|
||||
<div class="stage">
|
||||
<strong>2. Requirement extraction</strong>
|
||||
<div class="helper">Relevant mandatory threshold identified and isolated for reviewer verification.</div>
|
||||
<div class="progress"><span style="width:100%"></span></div>
|
||||
</div>
|
||||
<div class="stage">
|
||||
<strong>3. Gap analysis</strong>
|
||||
<div class="helper">Document contains claim language but no direct numeric evidence or linked report identifier.</div>
|
||||
<div class="progress"><span style="width:88%"></span></div>
|
||||
</div>
|
||||
<div class="stage">
|
||||
<strong>4. Recommendation synthesis</strong>
|
||||
<div class="helper">Drafting precise remediation text for reviewer approval.</div>
|
||||
<div class="progress"><span style="width:62%"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<aside class="card">
|
||||
<div class="section-head">
|
||||
<h2>Findings and conclusion</h2>
|
||||
<span class="helper">Reviewer-ready output</span>
|
||||
</div>
|
||||
<div class="finding-list">
|
||||
<div class="finding">
|
||||
<strong>Unsupported compliance statement</strong>
|
||||
<p class="helper">The paragraph asserts conformity without quoting the actual threshold or citing a specific verification artifact.</p>
|
||||
<span class="status risk" style="margin-top:10px;">Needs revision</span>
|
||||
</div>
|
||||
<div class="finding">
|
||||
<strong>Evidence linkage incomplete</strong>
|
||||
<p class="helper">Body engineering report exists, but the report ID and page range are missing from the narrative used in the dossier.</p>
|
||||
<span class="status warn" style="margin-top:10px;">Evidence gap</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="conclusion">
|
||||
<div class="conclusion-box">
|
||||
<strong>Recommended replacement text</strong>
|
||||
<p class="helper">"The roof support structure was validated in accordance with GB 26112-2010 §4.2; see the linked body engineering report for the supporting test result and formal threshold statement."</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="action-item">
|
||||
<strong>Next action</strong>
|
||||
<p class="helper">Assign to Body Structure team for wording update, then rerun the same paragraph through clause verification.</p>
|
||||
</div>
|
||||
<div class="action-item">
|
||||
<strong>Escalation</strong>
|
||||
<p class="helper">If report ID cannot be linked within 24 hours, downgrade dossier status and notify homologation lead.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
</main>
|
||||
<footer class="footer">
|
||||
<span>T-Systems Regulation Hub</span>
|
||||
<div class="footer-status"><span class="footer-dot" aria-hidden="true"></span><span>Online</span></div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<script src="ui-preferences.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
808
Prototype/cc29bcb0-df2d-4d50-9428-7caa406ecb29/dashboard.html
Normal file
808
Prototype/cc29bcb0-df2d-4d50-9428-7caa406ecb29/dashboard.html
Normal file
@@ -0,0 +1,808 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>AI + Compliance Hub - Dashboard</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #fafafa;
|
||||
--surface: #ffffff;
|
||||
--surface-warm: var(--surface);
|
||||
--fg: #111111;
|
||||
--fg-2: var(--fg);
|
||||
--muted: #6b6b6b;
|
||||
--meta: var(--muted);
|
||||
--border: #e5e5e5;
|
||||
--border-soft: var(--border);
|
||||
--primary: #e20074;
|
||||
--accent: var(--primary);
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), black 8%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 14%);
|
||||
--success: #17a34a;
|
||||
--warn: #d97706;
|
||||
--danger: #dc2626;
|
||||
--font-display: "TeleNeoWeb-Bold", "TeleNeoWeb-Medium", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-body: "TeleNeoWeb-Regular", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-mono: ui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospace;
|
||||
--text-xs: 11px;
|
||||
--text-sm: 13px;
|
||||
--text-base: 15px;
|
||||
--text-lg: 18px;
|
||||
--text-xl: 22px;
|
||||
--text-2xl: 28px;
|
||||
--leading-body: 1.55;
|
||||
--leading-tight: 1.2;
|
||||
--tracking-display: -0.01em;
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px;
|
||||
--space-5: 20px;
|
||||
--space-6: 24px;
|
||||
--space-8: 32px;
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 14px;
|
||||
--radius-pill: 9999px;
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 2px 8px color-mix(in oklab, var(--fg), transparent 92%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
|
||||
--motion-fast: 140ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
--sidebar-w: 240px;
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme="light"]) {
|
||||
--bg: #0f1014;
|
||||
--surface: #17181d;
|
||||
--fg: #f2f4f8;
|
||||
--fg-2: #e2e6ef;
|
||||
--muted: #9aa2b0;
|
||||
--border: #252830;
|
||||
--border-soft: #1e2028;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), white 12%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 6%);
|
||||
--success: #22c55e;
|
||||
--warn: #facc15;
|
||||
--danger: #f87171;
|
||||
--elev-raised: 0 14px 36px color-mix(in oklab, black, transparent 72%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
:root[data-theme="dark"] {
|
||||
--bg: #0f1014; --surface: #17181d; --fg: #f2f4f8; --fg-2: #e2e6ef;
|
||||
--muted: #9aa2b0; --border: #252830; --border-soft: #1e2028;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), white 12%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 6%);
|
||||
--success: #22c55e; --warn: #facc15; --danger: #f87171;
|
||||
--elev-raised: 0 14px 36px color-mix(in oklab, black, transparent 72%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
:root[data-theme="light"] { color-scheme: light; }
|
||||
|
||||
* { box-sizing: border-box; margin: 0; }
|
||||
html { -webkit-text-size-adjust: 100%; height: 100%; }
|
||||
body {
|
||||
height: 100%;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--leading-body);
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
h1,h2,h3,h4 {
|
||||
font-family: var(--font-display);
|
||||
line-height: var(--leading-tight);
|
||||
letter-spacing: var(--tracking-display);
|
||||
text-wrap: balance;
|
||||
}
|
||||
p { text-wrap: pretty; }
|
||||
a { color: inherit; text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
button, input, select, textarea { font: inherit; }
|
||||
|
||||
/* ── App chrome ─────────────────────────────── */
|
||||
.app-shell {
|
||||
display: grid;
|
||||
grid-template-columns: var(--sidebar-w) 1fr;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ── Sidebar ────────────────────────────────── */
|
||||
.sidebar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--surface);
|
||||
border-right: 1px solid var(--border);
|
||||
padding: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
.sidebar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
height: 56px;
|
||||
padding: 0 16px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.brand-logo {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
background: var(--accent);
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.brand-logo svg { color: #fff; }
|
||||
.sidebar-brand-name {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
color: var(--fg);
|
||||
}
|
||||
.sidebar-brand-sub {
|
||||
font-size: 10px;
|
||||
color: var(--muted);
|
||||
font-family: var(--font-mono);
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.sidebar-nav {
|
||||
flex: 1;
|
||||
padding: 12px 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.nav-group {
|
||||
padding: 0 8px 4px;
|
||||
}
|
||||
.nav-group + .nav-group {
|
||||
margin-top: 12px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
.nav-group-label {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--muted);
|
||||
padding: 0 8px 6px;
|
||||
display: block;
|
||||
}
|
||||
.nav-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
height: 36px;
|
||||
padding: 0 8px;
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--muted);
|
||||
font-size: var(--text-sm);
|
||||
cursor: pointer;
|
||||
transition: background var(--motion-fast) var(--ease-standard),
|
||||
color var(--motion-fast) var(--ease-standard);
|
||||
position: relative;
|
||||
}
|
||||
.nav-item:hover {
|
||||
background: color-mix(in oklab, var(--fg), transparent 94%);
|
||||
color: var(--fg);
|
||||
text-decoration: none;
|
||||
}
|
||||
.nav-item.active {
|
||||
background: color-mix(in oklab, var(--accent), transparent 90%);
|
||||
color: var(--accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
.nav-item.active::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 6px;
|
||||
bottom: 6px;
|
||||
width: 3px;
|
||||
border-radius: 0 3px 3px 0;
|
||||
background: var(--accent);
|
||||
}
|
||||
.nav-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.nav-item.active .nav-icon { opacity: 1; }
|
||||
.nav-badge {
|
||||
margin-left: auto;
|
||||
min-width: 20px;
|
||||
height: 18px;
|
||||
padding: 0 6px;
|
||||
border-radius: var(--radius-pill);
|
||||
background: color-mix(in oklab, var(--accent), transparent 84%);
|
||||
color: var(--accent);
|
||||
font-size: 10px;
|
||||
font-family: var(--font-mono);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.sidebar-footer {
|
||||
border-top: 1px solid var(--border);
|
||||
padding: 10px 8px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.sidebar-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px 8px;
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
}
|
||||
.sidebar-user:hover { background: color-mix(in oklab, var(--fg), transparent 94%); }
|
||||
.avatar {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar-user-info { min-width: 0; }
|
||||
.sidebar-user-name {
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 600;
|
||||
color: var(--fg);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.sidebar-user-role {
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
.sidebar-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
height: 34px;
|
||||
padding: 0 8px;
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--muted);
|
||||
font-size: var(--text-sm);
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
background: transparent;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
transition: background var(--motion-fast), color var(--motion-fast);
|
||||
}
|
||||
.sidebar-action:hover {
|
||||
background: color-mix(in oklab, var(--fg), transparent 94%);
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
/* ── Content area ─────────────────────────── */
|
||||
.content-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.content-topbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
height: 56px;
|
||||
padding: 0 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: color-mix(in oklab, var(--bg), transparent 4%);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
.topbar-title {
|
||||
font-weight: 600;
|
||||
font-size: var(--text-base);
|
||||
color: var(--fg);
|
||||
flex: 1;
|
||||
}
|
||||
.search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface);
|
||||
padding: 0 12px;
|
||||
height: 34px;
|
||||
width: 260px;
|
||||
}
|
||||
.search input {
|
||||
border: 0; outline: none; background: transparent;
|
||||
width: 100%; color: var(--fg); font-size: var(--text-sm);
|
||||
}
|
||||
.search input::placeholder { color: var(--muted); }
|
||||
|
||||
.btn {
|
||||
height: 34px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
padding: 0 14px;
|
||||
background: var(--surface);
|
||||
color: var(--fg);
|
||||
font-size: var(--text-sm);
|
||||
cursor: pointer;
|
||||
transition: background var(--motion-fast), border-color var(--motion-fast);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.btn:hover { border-color: var(--fg); }
|
||||
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
||||
.btn-primary {
|
||||
background: var(--accent); border-color: var(--accent); color: var(--accent-on);
|
||||
}
|
||||
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
|
||||
|
||||
/* ── Page ─────────────────────────────────── */
|
||||
.page {
|
||||
padding: 24px;
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
flex: 1;
|
||||
}
|
||||
.page-head {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
.eyebrow {
|
||||
color: var(--accent);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-xs);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.page-head h1 { font-size: clamp(22px, 3vw, 32px); }
|
||||
.page-head p { color: var(--muted); max-width: 68ch; font-size: var(--text-sm); }
|
||||
|
||||
/* ── Cards ────────────────────────────────── */
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 18px 20px;
|
||||
}
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0,1fr));
|
||||
gap: 14px;
|
||||
}
|
||||
.stat-card .label {
|
||||
color: var(--muted); font-size: var(--text-xs);
|
||||
font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 0.08em;
|
||||
}
|
||||
.stat-card .value {
|
||||
margin-top: 12px; font-size: 32px; line-height: 1;
|
||||
font-family: var(--font-display); font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.stat-card .sub {
|
||||
margin-top: 10px; color: var(--muted); font-size: var(--text-xs); line-height: 1.5;
|
||||
}
|
||||
.panel-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1.4fr 0.9fr;
|
||||
gap: 20px;
|
||||
}
|
||||
.stack { display: grid; gap: 20px; }
|
||||
.section-head {
|
||||
display: flex; align-items: center;
|
||||
justify-content: space-between; gap: 12px; margin-bottom: 14px;
|
||||
}
|
||||
.section-head h2 { font-size: var(--text-lg); }
|
||||
.ghost-link {
|
||||
color: var(--muted); font-size: var(--text-sm);
|
||||
border-radius: var(--radius-sm); padding: 4px 0;
|
||||
transition: color var(--motion-fast);
|
||||
}
|
||||
.ghost-link:hover { color: var(--fg); text-decoration: none; }
|
||||
|
||||
/* ── Data rows ─────────────────────────────── */
|
||||
.task-list, .program-list, .event-list { display: grid; gap: 10px; }
|
||||
.task-row, .program-row, .event-row {
|
||||
display: grid; gap: 10px;
|
||||
border: 1px solid var(--border); border-radius: var(--radius-sm);
|
||||
padding: 12px 14px;
|
||||
background: color-mix(in oklab, var(--surface), var(--bg) 20%);
|
||||
}
|
||||
.task-row { grid-template-columns: 1.5fr 0.9fr 0.9fr 0.7fr; align-items: center; }
|
||||
.program-row { grid-template-columns: 1fr auto; align-items: start; }
|
||||
.event-row { grid-template-columns: 90px 1fr; align-items: start; }
|
||||
|
||||
.kpi-strip { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; margin-top: 14px; }
|
||||
.kpi {
|
||||
border: 1px solid var(--border); border-radius: var(--radius-sm);
|
||||
padding: 12px; background: color-mix(in oklab, var(--surface), var(--bg) 18%);
|
||||
}
|
||||
.kpi strong { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 18px; }
|
||||
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
|
||||
|
||||
.status {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
width: fit-content; padding: 3px 9px; border-radius: var(--radius-pill);
|
||||
font-size: var(--text-xs); border: 1px solid var(--border); font-family: var(--font-mono);
|
||||
}
|
||||
.status::before {
|
||||
content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor;
|
||||
}
|
||||
.status.ok { color: var(--success); }
|
||||
.status.warn { color: var(--warn); }
|
||||
.status.risk { color: var(--danger); }
|
||||
|
||||
.meter {
|
||||
height: 6px; border-radius: 999px;
|
||||
background: color-mix(in oklab, var(--fg), transparent 94%);
|
||||
overflow: hidden; margin-top: 10px;
|
||||
}
|
||||
.meter > span {
|
||||
display: block; height: 100%;
|
||||
background: color-mix(in oklab, var(--accent), white 30%);
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.pill {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
height: 20px; padding: 0 7px; border-radius: var(--radius-pill);
|
||||
border: 1px solid var(--border); color: var(--muted);
|
||||
font-size: var(--text-xs); font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
.footer-note { color: var(--muted); font-size: var(--text-xs); line-height: 1.5; }
|
||||
|
||||
/* ── Footer ───────────────────────────────── */
|
||||
.footer {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
gap: 16px; min-height: 34px; padding: 0 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
color: var(--muted); font-size: var(--text-xs);
|
||||
font-family: var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase;
|
||||
background: color-mix(in oklab, var(--bg), var(--surface) 12%);
|
||||
}
|
||||
.footer-status { display: inline-flex; align-items: center; gap: 8px; }
|
||||
.footer-dot {
|
||||
width: 7px; height: 7px; border-radius: 50%; background: #19d3a2;
|
||||
box-shadow: 0 0 0 3px color-mix(in oklab, #19d3a2, transparent 82%);
|
||||
}
|
||||
|
||||
/* ── Responsive ───────────────────────────── */
|
||||
@media (max-width: 1180px) {
|
||||
.stats-grid, .panel-grid, .kpi-strip { grid-template-columns: 1fr 1fr; }
|
||||
.task-row { grid-template-columns: 1fr; }
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
:root { --sidebar-w: 200px; }
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
.app-shell { grid-template-columns: 1fr; }
|
||||
.sidebar { display: none; }
|
||||
.stats-grid, .panel-grid, .kpi-strip { grid-template-columns: 1fr; }
|
||||
.page-head { flex-direction: column; align-items: start; }
|
||||
.event-row, .program-row { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body data-page="dashboard">
|
||||
<div class="app-shell">
|
||||
|
||||
<!-- ── Sidebar ── -->
|
||||
<aside class="sidebar" aria-label="Primary navigation">
|
||||
<div class="sidebar-brand">
|
||||
<div class="brand-logo">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M2 3.5h12v1.5H2zm5.25 1.5h1.5v8h-1.5zm-3 2h2.25v1.5H4.25zm5.25 0h2.25v1.5H9.5zm-3 3h2.5v1.5H6.5z" fill="currentColor"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sidebar-brand-name">T-Systems</div>
|
||||
<div class="sidebar-brand-sub">Regulation Hub</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="sidebar-nav" aria-label="Primary">
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">主导航</span>
|
||||
<a class="nav-item" href="index.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M2 2h5v5H2zm7 0h5v5H9zM2 9h5v5H2zm7 0h5v5H9z" fill="currentColor" opacity=".7"/>
|
||||
</svg>
|
||||
概览
|
||||
</a>
|
||||
<a class="nav-item active" href="dashboard.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M1.5 2.5h13v1H1.5zm0 3h13v1H1.5zm0 3h8v1h-8zm0 3h6v1h-6zM12 8.5a3.5 3.5 0 110 7 3.5 3.5 0 010-7zm0 1a2.5 2.5 0 100 5 2.5 2.5 0 000-5zm.5 1v2h1.5v1H11v-3h1.5z" fill="currentColor"/>
|
||||
</svg>
|
||||
系统状态
|
||||
<span class="nav-badge">3</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">工作台</span>
|
||||
<a class="nav-item" href="document-management.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M3 1h7l3 3v11H3V1zm1 1v12h8V5h-3V2H4zm5 .5V4h1.5L9 1.5zM6 7h4v1H6zm0 2h4v1H6zm0 2h3v1H6z" fill="currentColor"/>
|
||||
</svg>
|
||||
文档管理
|
||||
</a>
|
||||
<a class="nav-item" href="compliance-analysis.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M8 1l7 3-1 6a7 7 0 01-6 5A7 7 0 011 10L0 4l8-3zm0 1.2L1.3 4.8l.8 5.1A6 6 0 008 14.8a6 6 0 005.9-4.9l.8-5.1L8 2.2zM7.5 5h1v4.5l-1 .5V5zm0 5.5h1v1h-1v-1z" fill="currentColor"/>
|
||||
</svg>
|
||||
合规分析
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">对话</span>
|
||||
<a class="nav-item" href="regulation-chat.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M2 2h12a1 1 0 011 1v8a1 1 0 01-1 1H5l-3 2.5V3a1 1 0 011-1zm0 1v9.5L4.5 11H14V3H2zm2 2h8v1H4zm0 2h6v1H4z" fill="currentColor"/>
|
||||
</svg>
|
||||
法规对话
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="sidebar-footer">
|
||||
<div class="sidebar-user">
|
||||
<div class="avatar">TS</div>
|
||||
<div class="sidebar-user-info">
|
||||
<div class="sidebar-user-name">T-Systems User</div>
|
||||
<div class="sidebar-user-role">Compliance Analyst</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="sidebar-action od-theme-toggle" type="button" data-od-theme aria-label="Toggle theme">
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M8 3a5 5 0 100 10A5 5 0 008 3zM2 8a6 6 0 1112 0A6 6 0 012 8z" fill="currentColor"/>
|
||||
</svg>
|
||||
主题
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- ── Content area ── -->
|
||||
<div class="content-area">
|
||||
<header class="content-topbar">
|
||||
<div class="topbar-title">系统状态</div>
|
||||
<div class="search">
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M6.5 1a5.5 5.5 0 014.23 9.02l3.62 3.62-.7.71-3.63-3.63A5.5 5.5 0 116.5 1zm0 1a4.5 4.5 0 100 9 4.5 4.5 0 000-9z" fill="currentColor" opacity=".5"/>
|
||||
</svg>
|
||||
<input type="search" placeholder="Search regulations, documents…" aria-label="Search" />
|
||||
</div>
|
||||
<button class="btn">Export status</button>
|
||||
<a class="btn btn-primary" href="upload-modal.html">New upload</a>
|
||||
</header>
|
||||
|
||||
<main class="page">
|
||||
<section class="page-head" data-od-id="dashboard-head">
|
||||
<div>
|
||||
<div class="eyebrow">Operations dashboard</div>
|
||||
<h1>Track ingestion health and active compliance work.</h1>
|
||||
<p style="margin-top:6px;">Designed for the team lead who needs to know which documents are blocked, which standards changed, and which program teams need intervention today.</p>
|
||||
</div>
|
||||
<span class="pill">v1.0.0</span>
|
||||
</section>
|
||||
|
||||
<section class="stats-grid" data-od-id="dashboard-stats">
|
||||
<article class="card stat-card">
|
||||
<div class="label">Documents total</div>
|
||||
<div class="value mono">--</div>
|
||||
<div class="sub">Live document totals populate here from the operations snapshot.</div>
|
||||
</article>
|
||||
<article class="card stat-card">
|
||||
<div class="label">Vector chunks</div>
|
||||
<div class="value mono">--</div>
|
||||
<div class="sub">Dense collection `regulations_dense_1024_v2` currently serving retrieval</div>
|
||||
</article>
|
||||
<article class="card stat-card">
|
||||
<div class="label">Analysis backlog</div>
|
||||
<div class="value mono">--</div>
|
||||
<div class="sub">Open investigations, reviewer backlog, and blocked runs roll up here.</div>
|
||||
</article>
|
||||
<article class="card stat-card">
|
||||
<div class="label">Average ingest time</div>
|
||||
<div class="value mono">--</div>
|
||||
<div class="sub">Aliyun parse plus embedding latency trends appear here once runs are active.</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="panel-grid" data-od-id="dashboard-main">
|
||||
<div class="stack">
|
||||
<article class="card">
|
||||
<div class="section-head">
|
||||
<h2>Workflow queue</h2>
|
||||
<a class="ghost-link" href="document-management.html">Open documents →</a>
|
||||
</div>
|
||||
<div class="task-list">
|
||||
<div class="task-row">
|
||||
<div>
|
||||
<strong>GB/T 31484-2015 battery density revision</strong>
|
||||
<div class="footer-note">Uploaded by EV Safety Team · version 2026-04 addendum</div>
|
||||
</div>
|
||||
<span class="status warn">Embedding</span>
|
||||
<span class="mono" style="font-size:12px;">chunk build active</span>
|
||||
<a class="ghost-link" href="document-detail.html">Inspect</a>
|
||||
</div>
|
||||
<div class="task-row">
|
||||
<div>
|
||||
<strong>UNECE R155 annex interpretation note</strong>
|
||||
<div class="footer-note">Parser artifacts ready · waiting for compliance analyst assignment</div>
|
||||
</div>
|
||||
<span class="status ok">Ready</span>
|
||||
<span class="mono" style="font-size:12px;">19 clauses linked</span>
|
||||
<a class="ghost-link" href="compliance-analysis.html">Analyze</a>
|
||||
</div>
|
||||
<div class="task-row">
|
||||
<div>
|
||||
<strong>GB 26112-2010 roof strength scan</strong>
|
||||
<div class="footer-note">OCR confidence dropped below threshold on 6 pages</div>
|
||||
</div>
|
||||
<span class="status risk">Failed</span>
|
||||
<span class="mono" style="font-size:12px;">Retry #2</span>
|
||||
<a class="ghost-link" href="document-management.html">Resolve</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="card">
|
||||
<div class="section-head">
|
||||
<h2>Active compliance programs</h2>
|
||||
<a class="ghost-link" href="compliance-analysis.html">Review findings →</a>
|
||||
</div>
|
||||
<div class="program-list">
|
||||
<div class="program-row">
|
||||
<div>
|
||||
<strong>Intelligent cockpit homologation</strong>
|
||||
<p class="footer-note">42 related standards across driver monitoring, EMC, and child safety. Four findings still open for MY27 platform.</p>
|
||||
</div>
|
||||
<span class="status risk">High risk</span>
|
||||
</div>
|
||||
<div class="program-row">
|
||||
<div>
|
||||
<strong>Battery swap certification dossier</strong>
|
||||
<p class="footer-note">Clause mapping complete. Thermal event test evidence package still awaiting supplier document refresh.</p>
|
||||
</div>
|
||||
<span class="status warn">Pending</span>
|
||||
</div>
|
||||
<div class="program-row">
|
||||
<div>
|
||||
<strong>Connected fleet cybersecurity</strong>
|
||||
<p class="footer-note">RAG checks aligned with UNECE R155. Chat follow-up requested on remote key rotation obligations.</p>
|
||||
</div>
|
||||
<span class="status ok">On track</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="kpi-strip">
|
||||
<div class="kpi">
|
||||
<div class="footer-note">Retrieval hit rate</div>
|
||||
<strong>87%</strong>
|
||||
<div class="meter"><span style="width:87%"></span></div>
|
||||
</div>
|
||||
<div class="kpi">
|
||||
<div class="footer-note">Evidence coverage</div>
|
||||
<strong>72%</strong>
|
||||
<div class="meter"><span style="width:72%"></span></div>
|
||||
</div>
|
||||
<div class="kpi">
|
||||
<div class="footer-note">Reviewer SLA</div>
|
||||
<strong>18h</strong>
|
||||
<div class="meter"><span style="width:64%"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="stack">
|
||||
<article class="card">
|
||||
<div class="section-head">
|
||||
<h2>System health</h2>
|
||||
<a class="ghost-link" href="#">Refresh</a>
|
||||
</div>
|
||||
<div class="task-list">
|
||||
<div class="task-row" style="grid-template-columns: 1fr auto;">
|
||||
<div>
|
||||
<strong>Aliyun parser backend</strong>
|
||||
<div class="footer-note">Poll interval 5 s · timeout 900 s</div>
|
||||
</div>
|
||||
<span class="status warn">Queue depth 7</span>
|
||||
</div>
|
||||
<div class="task-row" style="grid-template-columns: 1fr auto;">
|
||||
<div>
|
||||
<strong>Embedding model</strong>
|
||||
<div class="footer-note">text-embedding-v3 · dimension 1024</div>
|
||||
</div>
|
||||
<span class="status ok">Healthy</span>
|
||||
</div>
|
||||
<div class="task-row" style="grid-template-columns: 1fr auto;">
|
||||
<div>
|
||||
<strong>Vector store</strong>
|
||||
<div class="footer-note">Milvus `regulations_dense_1024_v2`</div>
|
||||
</div>
|
||||
<span class="status ok">Serving</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="card">
|
||||
<div class="section-head">
|
||||
<h2>Regulatory watch</h2>
|
||||
<a class="ghost-link" href="regulation-chat.html">Ask chat →</a>
|
||||
</div>
|
||||
<div class="event-list">
|
||||
<div class="event-row">
|
||||
<span class="mono footer-note">Recent</span>
|
||||
<div>
|
||||
<strong>GB 38031 thermal propagation draft updated</strong>
|
||||
<p class="footer-note">Potential impact on current battery enclosure narrative. Evidence gap flagged in two supplier submissions.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="event-row">
|
||||
<span class="mono footer-note">Recent</span>
|
||||
<div>
|
||||
<strong>UNECE R155 Q&A added note on incident response logs</strong>
|
||||
<p class="footer-note">Connected fleet program must confirm retention windows and ownership controls.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="event-row">
|
||||
<span class="mono footer-note">Recent</span>
|
||||
<div>
|
||||
<strong>GB/T 18487 charging interface interpretation circulated</strong>
|
||||
<p class="footer-note">No blocker yet, but three documents should be re-run against the new clause wording.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<span>T-Systems Regulation Hub</span>
|
||||
<div class="footer-status">
|
||||
<span class="footer-dot" aria-hidden="true"></span>
|
||||
<span>Online</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<script src="ui-preferences.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,623 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>AI + Compliance Hub - Document Detail</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #fafafa;
|
||||
--surface: #ffffff;
|
||||
--surface-warm: var(--surface);
|
||||
--fg: #111111;
|
||||
--fg-2: var(--fg);
|
||||
--muted: #6b6b6b;
|
||||
--meta: var(--muted);
|
||||
--border: #e5e5e5;
|
||||
--border-soft: var(--border);
|
||||
--primary: #e20074;
|
||||
--accent: var(--primary);
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), black 8%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 14%);
|
||||
--success: #17a34a;
|
||||
--warn: #eab308;
|
||||
--danger: #dc2626;
|
||||
--font-display: "TeleNeoWeb-Bold", "TeleNeoWeb-Medium", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-body: "TeleNeoWeb-Regular", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-mono: ui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospace;
|
||||
--text-xs: 12px;
|
||||
--text-sm: 14px;
|
||||
--text-base: 16px;
|
||||
--text-lg: 20px;
|
||||
--text-xl: 24px;
|
||||
--text-2xl: 32px;
|
||||
--text-3xl: 48px;
|
||||
--text-4xl: 64px;
|
||||
--leading-body: 1.5;
|
||||
--leading-tight: 1.2;
|
||||
--tracking-display: -0.01em;
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px;
|
||||
--space-5: 20px;
|
||||
--space-6: 24px;
|
||||
--space-8: 32px;
|
||||
--space-12: 48px;
|
||||
--space-20: 80px;
|
||||
--section-y-desktop: 80px;
|
||||
--section-y-tablet: 48px;
|
||||
--section-y-phone: 32px;
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 16px;
|
||||
--radius-pill: 9999px;
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 2px 8px color-mix(in oklab, var(--fg), transparent 92%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
--container-max: 1440px;
|
||||
--container-gutter-desktop: 24px;
|
||||
--container-gutter-tablet: 16px;
|
||||
--container-gutter-phone: 12px;
|
||||
--sidebar-w: 240px;
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme="light"]) {
|
||||
--bg: #0f1014;
|
||||
--surface: #17181d;
|
||||
--surface-warm: #1d1f26;
|
||||
--fg: #f5f7fb;
|
||||
--fg-2: #e5e8ef;
|
||||
--muted: #a2a9b8;
|
||||
--meta: #858d9c;
|
||||
--border: #2a2d35;
|
||||
--border-soft: #21242c;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), white 12%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 6%);
|
||||
--success: #22c55e;
|
||||
--warn: #facc15;
|
||||
--danger: #f87171;
|
||||
--elev-raised: 0 14px 36px color-mix(in oklab, black, transparent 74%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 56%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
|
||||
:root[data-theme="dark"] {
|
||||
--bg: #0f1014;
|
||||
--surface: #17181d;
|
||||
--surface-warm: #1d1f26;
|
||||
--fg: #f5f7fb;
|
||||
--fg-2: #e5e8ef;
|
||||
--muted: #a2a9b8;
|
||||
--meta: #858d9c;
|
||||
--border: #2a2d35;
|
||||
--border-soft: #21242c;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), white 12%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 6%);
|
||||
--success: #22c55e;
|
||||
--warn: #facc15;
|
||||
--danger: #f87171;
|
||||
--elev-raised: 0 14px 36px color-mix(in oklab, black, transparent 74%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 56%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
:root[data-theme="light"] {
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--leading-body);
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
h1, h2, h3 {
|
||||
margin: 0;
|
||||
font-family: var(--font-display);
|
||||
line-height: var(--leading-tight);
|
||||
letter-spacing: var(--tracking-display);
|
||||
}
|
||||
p { margin: 0; text-wrap: pretty; }
|
||||
a { color: inherit; text-decoration: none; }
|
||||
a:hover { color: var(--fg); text-decoration: underline; }
|
||||
button { font: inherit; }
|
||||
|
||||
/* ── Sidebar shell ── */
|
||||
.app-shell { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }
|
||||
.sidebar { position: sticky; top: 0; height: 100vh; overflow-y: auto; display: flex; flex-direction: column; background: var(--surface); border-right: 1px solid var(--border); z-index: 10; }
|
||||
.sidebar-brand { display: flex; align-items: center; gap: 10px; height: 56px; padding: 0 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
|
||||
.brand-logo { width: 26px; height: 26px; background: var(--accent); border-radius: 6px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||||
.brand-logo svg { color: #fff; }
|
||||
.sidebar-brand-name { font-family: var(--font-display); font-size: 13px; font-weight: 700; line-height: 1.2; }
|
||||
.sidebar-brand-sub { font-size: 10px; color: var(--muted); font-family: var(--font-mono); letter-spacing: 0.04em; }
|
||||
.sidebar-nav { flex: 1; padding: 12px 0; overflow-y: auto; }
|
||||
.nav-group { padding: 0 8px 4px; }
|
||||
.nav-group + .nav-group { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
|
||||
.nav-group-label { font-family: var(--font-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); padding: 0 8px 6px; display: block; }
|
||||
.nav-item { display: flex; align-items: center; gap: 10px; height: 36px; padding: 0 8px; border-radius: 6px; color: var(--muted); font-size: 13px; cursor: pointer; transition: background 140ms, color 140ms; position: relative; }
|
||||
.nav-item:hover { background: color-mix(in oklab, var(--fg), transparent 94%); color: var(--fg); text-decoration: none; }
|
||||
.nav-item.active { background: color-mix(in oklab, var(--accent), transparent 90%); color: var(--accent); font-weight: 600; }
|
||||
.nav-item.active::before { content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 3px; border-radius: 0 3px 3px 0; background: var(--accent); }
|
||||
.nav-icon { width: 16px; height: 16px; flex-shrink: 0; opacity: 0.7; }
|
||||
.nav-item.active .nav-icon { opacity: 1; }
|
||||
.sidebar-footer { border-top: 1px solid var(--border); padding: 10px 8px; flex-shrink: 0; display: flex; flex-direction: column; gap: 4px; }
|
||||
.sidebar-user { display: flex; align-items: center; gap: 10px; padding: 8px; border-radius: 6px; cursor: pointer; }
|
||||
.sidebar-user:hover { background: color-mix(in oklab, var(--fg), transparent 94%); }
|
||||
.avatar { width: 30px; height: 30px; border-radius: 50%; background: var(--accent); color: #fff; font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||||
.sidebar-user-info { min-width: 0; }
|
||||
.sidebar-user-name { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.sidebar-user-role { font-size: 11px; color: var(--muted); font-family: var(--font-mono); }
|
||||
.sidebar-action { display: flex; align-items: center; gap: 10px; height: 34px; padding: 0 8px; border-radius: 6px; color: var(--muted); font-size: 13px; cursor: pointer; border: none; background: transparent; width: 100%; text-align: left; transition: background 140ms, color 140ms; }
|
||||
.sidebar-action:hover { background: color-mix(in oklab, var(--fg), transparent 94%); color: var(--fg); }
|
||||
.content-area { display: flex; flex-direction: column; min-width: 0; min-height: 100vh; }
|
||||
.content-topbar { position: sticky; top: 0; z-index: 5; display: flex; align-items: center; gap: 12px; height: 56px; padding: 0 24px; border-bottom: 1px solid var(--border); background: color-mix(in oklab, var(--bg), transparent 4%); backdrop-filter: blur(10px); }
|
||||
.topbar-title { font-weight: 600; font-size: 15px; color: var(--fg); flex: 1; }
|
||||
.footer-dot { width: 7px; height: 7px; border-radius: 50%; background: #19d3a2; box-shadow: 0 0 0 3px color-mix(in oklab, #19d3a2, transparent 82%); }
|
||||
.footer-status { display: inline-flex; align-items: center; gap: 8px; }
|
||||
@media (max-width: 700px) { .app-shell { grid-template-columns: 1fr; } .sidebar { display: none; } }
|
||||
|
||||
/* ── Page-specific styles ── */
|
||||
.footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
min-height: 36px;
|
||||
padding: 0 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
color: var(--muted);
|
||||
font-size: var(--text-xs);
|
||||
font-family: var(--font-mono);
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
background: color-mix(in oklab, var(--bg), var(--surface) 12%);
|
||||
}
|
||||
.page {
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
padding: 24px;
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
}
|
||||
.topline {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
min-height: 56px;
|
||||
}
|
||||
.back { color: var(--muted); font-size: var(--text-sm); }
|
||||
.eyebrow {
|
||||
color: var(--accent);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-xs);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.hero {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
}
|
||||
.hero h1 { font-size: clamp(30px, 4vw, 46px); }
|
||||
.hero p { max-width: 72ch; color: var(--muted); }
|
||||
.meta-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
.chip,
|
||||
.status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 4px 10px;
|
||||
border-radius: var(--radius-pill);
|
||||
border: 1px solid var(--border);
|
||||
font-size: var(--text-xs);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
.status::before {
|
||||
content: "";
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 999px;
|
||||
background: currentColor;
|
||||
}
|
||||
.status.ok { color: var(--success); }
|
||||
.status.warn { color: color-mix(in oklab, var(--warn), black 24%); }
|
||||
.status.risk { color: var(--danger); }
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 0.9fr 1.4fr;
|
||||
gap: 20px;
|
||||
}
|
||||
.stack {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
}
|
||||
.card {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--surface);
|
||||
padding: 18px;
|
||||
}
|
||||
.section-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.section-head h2 { font-size: var(--text-xl); }
|
||||
.helper {
|
||||
color: var(--muted);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.mono {
|
||||
font-family: var(--font-mono);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.timeline {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
.step {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 14px 14px 14px 16px;
|
||||
background: color-mix(in oklab, var(--surface), var(--bg) 14%);
|
||||
position: relative;
|
||||
}
|
||||
.step::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -1px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 3px;
|
||||
border-radius: 3px 0 0 3px;
|
||||
background: var(--border);
|
||||
}
|
||||
.step.active::before { background: var(--accent); }
|
||||
.step.done::before { background: var(--success); }
|
||||
.step.fail::before { background: var(--danger); }
|
||||
.progress {
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in oklab, var(--fg), transparent 95%);
|
||||
overflow: hidden;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.progress > span {
|
||||
display: block;
|
||||
height: 100%;
|
||||
background: color-mix(in oklab, var(--accent), white 28%);
|
||||
border-radius: inherit;
|
||||
}
|
||||
.artifact-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.artifact {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 14px;
|
||||
background: color-mix(in oklab, var(--surface), var(--bg) 16%);
|
||||
}
|
||||
.artifact strong { display: block; margin-bottom: 6px; }
|
||||
.table {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
.table-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1.1fr 0.9fr 0.9fr 0.9fr;
|
||||
gap: 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 12px 14px;
|
||||
background: color-mix(in oklab, var(--surface), var(--bg) 12%);
|
||||
}
|
||||
.log {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
.log-item {
|
||||
display: grid;
|
||||
grid-template-columns: 90px 1fr;
|
||||
gap: 12px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
@media (max-width: 1100px) {
|
||||
.grid,
|
||||
.artifact-grid,
|
||||
.table-row { grid-template-columns: 1fr; }
|
||||
.hero { flex-direction: column; align-items: start; }
|
||||
}
|
||||
@media (max-width: 760px) {
|
||||
.page { padding: 12px; }
|
||||
.topline { align-items: start; flex-direction: column; }
|
||||
.log-item { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body data-page="detail">
|
||||
<div class="app-shell">
|
||||
<aside class="sidebar" aria-label="Primary navigation">
|
||||
<div class="sidebar-brand">
|
||||
<div class="brand-logo">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M2 3.5h12v1.5H2zm5.25 1.5h1.5v8h-1.5zm-3 2h2.25v1.5H4.25zm5.25 0h2.25v1.5H9.5zm-3 3h2.5v1.5H6.5z" fill="currentColor"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sidebar-brand-name">T-Systems</div>
|
||||
<div class="sidebar-brand-sub">Regulation Hub</div>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="sidebar-nav" aria-label="Primary">
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">主导航</span>
|
||||
<a class="nav-item" href="index.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M2 2h5v5H2zm7 0h5v5H9zM2 9h5v5H2zm7 0h5v5H9z" fill="currentColor" opacity=".7"/></svg>
|
||||
概览
|
||||
</a>
|
||||
<a class="nav-item" href="dashboard.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M1.5 2.5h13v1H1.5zm0 3h13v1H1.5zm0 3h8v1h-8zm0 3h6v1h-6z" fill="currentColor"/></svg>
|
||||
系统状态
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">工作台</span>
|
||||
<a class="nav-item active" href="document-management.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M3 1h7l3 3v11H3V1zm1 1v12h8V5h-3V2H4zm5 .5V4h1.5L9 1.5zM6 7h4v1H6zm0 2h4v1H6zm0 2h3v1H6z" fill="currentColor"/></svg>
|
||||
文档管理
|
||||
</a>
|
||||
<a class="nav-item" href="compliance-analysis.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M8 1l7 3-1 6a7 7 0 01-6 5A7 7 0 011 10L0 4l8-3zm0 1.2L1.3 4.8l.8 5.1A6 6 0 008 14.8a6 6 0 005.9-4.9l.8-5.1L8 2.2zM7.5 5h1v4.5l-1 .5V5zm0 5.5h1v1h-1v-1z" fill="currentColor"/></svg>
|
||||
合规分析
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">对话</span>
|
||||
<a class="nav-item" href="regulation-chat.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M2 2h12a1 1 0 011 1v8a1 1 0 01-1 1H5l-3 2.5V3a1 1 0 011-1zm0 1v9.5L4.5 11H14V3H2zm2 2h8v1H4zm0 2h6v1H4z" fill="currentColor"/></svg>
|
||||
法规对话
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="sidebar-footer">
|
||||
<div class="sidebar-user">
|
||||
<div class="avatar">TS</div>
|
||||
<div class="sidebar-user-info">
|
||||
<div class="sidebar-user-name">T-Systems User</div>
|
||||
<div class="sidebar-user-role">Compliance Analyst</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="sidebar-action od-theme-toggle" type="button" data-od-theme aria-label="Toggle theme">
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M8 3a5 5 0 100 10A5 5 0 008 3zM2 8a6 6 0 1112 0A6 6 0 012 8z" fill="currentColor"/></svg>
|
||||
主题
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div class="content-area">
|
||||
<header class="content-topbar">
|
||||
<span class="topbar-title">文档解析详情</span>
|
||||
</header>
|
||||
|
||||
<main class="page">
|
||||
<section class="topline" data-od-id="detail-top">
|
||||
<a class="back" href="document-management.html">← Back to document management</a>
|
||||
<div class="meta-row">
|
||||
<span class="chip">Battery safety</span>
|
||||
<span class="chip mono">doc_id: GBT-31484-2015-r2</span>
|
||||
<span class="status warn">Embedding in progress</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="hero" data-od-id="detail-hero">
|
||||
<div>
|
||||
<div class="eyebrow">Document detail</div>
|
||||
<h1>Trace parse artifacts from raw upload to vector index.</h1>
|
||||
<p>This view is for operators diagnosing why one document is delayed or degraded. It surfaces parser settings, semantic structure, chunk generation, and Milvus insertion as separate observable stages.</p>
|
||||
</div>
|
||||
<div class="card" style="min-width:280px;">
|
||||
<div class="helper">Current run</div>
|
||||
<h2 style="font-size:24px; margin-top:8px;">Battery density addendum review</h2>
|
||||
<div class="helper" style="margin-top:8px;">Uploaded 09:14 by Battery Safety Team · parser backend `aliyun`</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="grid" data-od-id="detail-main">
|
||||
<div class="stack">
|
||||
<article class="card">
|
||||
<div class="section-head">
|
||||
<h2>Pipeline progression</h2>
|
||||
<span class="helper">Live state</span>
|
||||
</div>
|
||||
<div class="timeline">
|
||||
<div class="step done">
|
||||
<strong>1. Object storage ingestion</strong>
|
||||
<div class="helper">Stored in bucket `upload-files` with artifact prefix `artifacts`</div>
|
||||
<div class="progress"><span style="width:100%"></span></div>
|
||||
</div>
|
||||
<div class="step done">
|
||||
<strong>2. Aliyun parse layout extraction</strong>
|
||||
<div class="helper">Parsed pages, recovered tables, and OCR confidence summarize here once the run completes.</div>
|
||||
<div class="progress"><span style="width:100%"></span></div>
|
||||
</div>
|
||||
<div class="step done">
|
||||
<strong>3. Semantic blocks</strong>
|
||||
<div class="helper">Semantic block persistence is tracked here after parse artifact storage completes.</div>
|
||||
<div class="progress"><span style="width:100%"></span></div>
|
||||
</div>
|
||||
<div class="step active">
|
||||
<strong>4. Vector chunk build</strong>
|
||||
<div class="helper">Using overlap chunking with header-prefixed embedding text</div>
|
||||
<div class="progress"><span style="width:76%"></span></div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<strong>5. Embedding generation</strong>
|
||||
<div class="helper">Target model `text-embedding-v3` · dimension 1024</div>
|
||||
<div class="progress"><span style="width:38%"></span></div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<strong>6. Milvus insertion</strong>
|
||||
<div class="helper">Waiting for chunk vectors before collection sync</div>
|
||||
<div class="progress"><span style="width:8%"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="card">
|
||||
<div class="section-head">
|
||||
<h2>Run log</h2>
|
||||
<span class="helper">Recent events</span>
|
||||
</div>
|
||||
<div class="log">
|
||||
<div class="log-item">
|
||||
<span class="mono helper">09:18:11</span>
|
||||
<div>
|
||||
<strong>Semantic block serialization completed</strong>
|
||||
<div class="helper">Stored block tree and section hierarchy in Postgres parse artifact store.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<span class="mono helper">09:20:44</span>
|
||||
<div>
|
||||
<strong>Chunk builder emitted overlap windows</strong>
|
||||
<div class="helper">Header context is prepended to vector chunks for downstream retrieval quality.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<span class="mono helper">09:22:08</span>
|
||||
<div>
|
||||
<strong>Embedding worker rate-limited temporarily</strong>
|
||||
<div class="helper">Retry budget still healthy. No manual action required unless latency exceeds 15 minutes.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="stack">
|
||||
<article class="card">
|
||||
<div class="section-head">
|
||||
<h2>Artifacts generated</h2>
|
||||
<span class="helper">Output layers</span>
|
||||
</div>
|
||||
<div class="artifact-grid">
|
||||
<div class="artifact">
|
||||
<strong>Layout JSON</strong>
|
||||
<span class="helper">Page, table, and text-span counts populate from the parser artifact output.</span>
|
||||
</div>
|
||||
<div class="artifact">
|
||||
<strong>Semantic blocks</strong>
|
||||
<span class="helper">Semantic nodes are mapped into chapter and clause hierarchy here.</span>
|
||||
</div>
|
||||
<div class="artifact">
|
||||
<strong>Vector chunks</strong>
|
||||
<span class="helper">Overlap windows and embedding text populate after chunk generation.</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="card">
|
||||
<div class="section-head">
|
||||
<h2>Chunk profile</h2>
|
||||
<span class="helper">Top segments</span>
|
||||
</div>
|
||||
<div class="table">
|
||||
<div class="table-row">
|
||||
<div>
|
||||
<strong>4.2 Energy density threshold</strong>
|
||||
<div class="helper">Critical requirement clause</div>
|
||||
</div>
|
||||
<span class="mono">chunk count pending</span>
|
||||
<span class="mono">character count pending</span>
|
||||
<span class="status ok">Linked</span>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div>
|
||||
<strong>5.1 Thermal event test method</strong>
|
||||
<div class="helper">Supplier evidence cross-reference</div>
|
||||
</div>
|
||||
<span class="mono">chunk count pending</span>
|
||||
<span class="mono">character count pending</span>
|
||||
<span class="status warn">Review</span>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div>
|
||||
<strong>Appendix A formulas and tables</strong>
|
||||
<div class="helper">Dense table extraction from scan</div>
|
||||
</div>
|
||||
<span class="mono">chunk count pending</span>
|
||||
<span class="mono">character count pending</span>
|
||||
<span class="status warn">Noisy</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="card">
|
||||
<div class="section-head">
|
||||
<h2>Configuration snapshot</h2>
|
||||
<span class="helper">Runtime values</span>
|
||||
</div>
|
||||
<div class="table">
|
||||
<div class="table-row">
|
||||
<div><strong>Parser backend</strong><div class="helper">Document extraction engine</div></div>
|
||||
<span class="mono">aliyun</span>
|
||||
<span class="mono">5 s poll</span>
|
||||
<span class="mono">900 s timeout</span>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div><strong>Embedding target</strong><div class="helper">Vector generation</div></div>
|
||||
<span class="mono">text-embedding-v3</span>
|
||||
<span class="mono">1024 dim</span>
|
||||
<span class="mono">top_k 10</span>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div><strong>Collection</strong><div class="helper">Milvus destination</div></div>
|
||||
<span class="mono">regulations_dense_1024_v2</span>
|
||||
<span class="mono">dense-only</span>
|
||||
<span class="mono">ready</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<span>T-Systems Regulation</span>
|
||||
<div class="footer-status">
|
||||
<span>Desktop Web</span>
|
||||
<span class="footer-dot" aria-hidden="true"></span>
|
||||
<span>Online</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<script src="ui-preferences.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,693 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>AI + Compliance Hub - Document Management</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #fafafa;
|
||||
--surface: #ffffff;
|
||||
--surface-warm: var(--surface);
|
||||
--fg: #111111;
|
||||
--fg-2: var(--fg);
|
||||
--muted: #6b6b6b;
|
||||
--meta: var(--muted);
|
||||
--border: #e5e5e5;
|
||||
--border-soft: var(--border);
|
||||
--primary: #e20074;
|
||||
--accent: var(--primary);
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), black 8%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 14%);
|
||||
--success: #17a34a;
|
||||
--warn: #eab308;
|
||||
--danger: #dc2626;
|
||||
--font-display: "TeleNeoWeb-Bold", "TeleNeoWeb-Medium", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-body: "TeleNeoWeb-Regular", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-mono: ui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospace;
|
||||
--text-xs: 11px;
|
||||
--text-sm: 13px;
|
||||
--text-base: 15px;
|
||||
--text-lg: 20px;
|
||||
--text-xl: 24px;
|
||||
--text-2xl: 32px;
|
||||
--text-3xl: 48px;
|
||||
--text-4xl: 64px;
|
||||
--leading-body: 1.5;
|
||||
--leading-tight: 1.2;
|
||||
--tracking-display: -0.01em;
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px;
|
||||
--space-5: 20px;
|
||||
--space-6: 24px;
|
||||
--space-8: 32px;
|
||||
--space-12: 48px;
|
||||
--space-20: 80px;
|
||||
--section-y-desktop: 80px;
|
||||
--section-y-tablet: 48px;
|
||||
--section-y-phone: 32px;
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 16px;
|
||||
--radius-pill: 9999px;
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 2px 8px color-mix(in oklab, var(--fg), transparent 92%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
--container-max: 1440px;
|
||||
--container-gutter-desktop: 24px;
|
||||
--container-gutter-tablet: 16px;
|
||||
--container-gutter-phone: 12px;
|
||||
--sidebar-w: 240px;
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme="light"]) {
|
||||
--bg: #0f1014;
|
||||
--surface: #17181d;
|
||||
--surface-warm: #1d1f26;
|
||||
--fg: #f5f7fb;
|
||||
--fg-2: #e5e8ef;
|
||||
--muted: #a2a9b8;
|
||||
--meta: #858d9c;
|
||||
--border: #2a2d35;
|
||||
--border-soft: #21242c;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), white 12%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 6%);
|
||||
--success: #22c55e;
|
||||
--warn: #facc15;
|
||||
--danger: #f87171;
|
||||
--elev-raised: 0 14px 36px color-mix(in oklab, black, transparent 74%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 56%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
|
||||
:root[data-theme="dark"] {
|
||||
--bg: #0f1014;
|
||||
--surface: #17181d;
|
||||
--surface-warm: #1d1f26;
|
||||
--fg: #f5f7fb;
|
||||
--fg-2: #e5e8ef;
|
||||
--muted: #a2a9b8;
|
||||
--meta: #858d9c;
|
||||
--border: #2a2d35;
|
||||
--border-soft: #21242c;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), white 12%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 6%);
|
||||
--success: #22c55e;
|
||||
--warn: #facc15;
|
||||
--danger: #f87171;
|
||||
--elev-raised: 0 14px 36px color-mix(in oklab, black, transparent 74%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 56%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
:root[data-theme="light"] {
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--leading-body);
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
h1, h2, h3 {
|
||||
margin: 0;
|
||||
font-family: var(--font-display);
|
||||
letter-spacing: var(--tracking-display);
|
||||
line-height: var(--leading-tight);
|
||||
}
|
||||
p { margin: 0; text-wrap: pretty; }
|
||||
button, input, select { font: inherit; }
|
||||
a { color: inherit; text-decoration: none; }
|
||||
a:hover { color: var(--fg); text-decoration: underline; }
|
||||
|
||||
/* ── App shell ── */
|
||||
.app-shell { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }
|
||||
|
||||
/* ── Sidebar ── */
|
||||
.sidebar { position: sticky; top: 0; height: 100vh; overflow-y: auto; display: flex; flex-direction: column; background: var(--surface); border-right: 1px solid var(--border); z-index: 10; }
|
||||
.sidebar-brand { display: flex; align-items: center; gap: 10px; height: 56px; padding: 0 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
|
||||
.brand-logo { width: 26px; height: 26px; background: var(--accent); border-radius: 6px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||||
.brand-logo svg { color: #fff; }
|
||||
.sidebar-brand-name { font-family: var(--font-display); font-size: 13px; font-weight: 700; line-height: 1.2; }
|
||||
.sidebar-brand-sub { font-size: 10px; color: var(--muted); font-family: var(--font-mono); letter-spacing: 0.04em; }
|
||||
.sidebar-nav { flex: 1; padding: 12px 0; overflow-y: auto; }
|
||||
.nav-group { padding: 0 8px 4px; }
|
||||
.nav-group + .nav-group { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
|
||||
.nav-group-label { font-family: var(--font-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); padding: 0 8px 6px; display: block; }
|
||||
.nav-item { display: flex; align-items: center; gap: 10px; height: 36px; padding: 0 8px; border-radius: 6px; color: var(--muted); font-size: 13px; cursor: pointer; transition: background 140ms, color 140ms; position: relative; }
|
||||
.nav-item:hover { background: color-mix(in oklab, var(--fg), transparent 94%); color: var(--fg); text-decoration: none; }
|
||||
.nav-item.active { background: color-mix(in oklab, var(--accent), transparent 90%); color: var(--accent); font-weight: 600; }
|
||||
.nav-item.active::before { content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 3px; border-radius: 0 3px 3px 0; background: var(--accent); }
|
||||
.nav-icon { width: 16px; height: 16px; flex-shrink: 0; opacity: 0.7; }
|
||||
.nav-item.active .nav-icon { opacity: 1; }
|
||||
.nav-badge { margin-left: auto; min-width: 20px; height: 18px; padding: 0 6px; border-radius: 9999px; background: color-mix(in oklab, var(--accent), transparent 84%); color: var(--accent); font-size: 10px; font-family: var(--font-mono); display: flex; align-items: center; justify-content: center; font-weight: 700; }
|
||||
.sidebar-footer { border-top: 1px solid var(--border); padding: 10px 8px; flex-shrink: 0; display: flex; flex-direction: column; gap: 4px; }
|
||||
.sidebar-user { display: flex; align-items: center; gap: 10px; padding: 8px; border-radius: 6px; cursor: pointer; }
|
||||
.sidebar-user:hover { background: color-mix(in oklab, var(--fg), transparent 94%); }
|
||||
.avatar { width: 30px; height: 30px; border-radius: 50%; background: var(--accent); color: #fff; font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||||
.sidebar-user-info { min-width: 0; }
|
||||
.sidebar-user-name { font-size: 13px; font-weight: 600; color: var(--fg); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.sidebar-user-role { font-size: 11px; color: var(--muted); font-family: var(--font-mono); }
|
||||
.sidebar-action { display: flex; align-items: center; gap: 10px; height: 34px; padding: 0 8px; border-radius: 6px; color: var(--muted); font-size: 13px; cursor: pointer; border: none; background: transparent; width: 100%; text-align: left; transition: background 140ms, color 140ms; }
|
||||
.sidebar-action:hover { background: color-mix(in oklab, var(--fg), transparent 94%); color: var(--fg); }
|
||||
|
||||
/* ── Content area ── */
|
||||
.content-area { display: flex; flex-direction: column; min-width: 0; min-height: 100vh; }
|
||||
.content-topbar { position: sticky; top: 0; z-index: 5; display: flex; align-items: center; gap: 12px; height: 56px; padding: 0 24px; border-bottom: 1px solid var(--border); background: color-mix(in oklab, var(--bg), transparent 4%); backdrop-filter: blur(10px); }
|
||||
.topbar-title { font-weight: 600; font-size: 15px; color: var(--fg); flex: 1; }
|
||||
.search { display: flex; align-items: center; gap: 8px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); padding: 0 12px; height: 34px; width: 240px; }
|
||||
.search input { border: 0; outline: none; background: transparent; width: 100%; color: var(--fg); font-size: 13px; }
|
||||
.search input::placeholder { color: var(--muted); }
|
||||
|
||||
/* ── Buttons ── */
|
||||
.btn {
|
||||
min-height: 34px;
|
||||
padding: 0 14px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: background var(--motion-fast) var(--ease-standard), border-color var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.btn:hover { border-color: var(--fg); }
|
||||
.btn-primary {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: var(--accent-on);
|
||||
}
|
||||
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
|
||||
|
||||
.btn:focus-visible,
|
||||
.control:focus-visible,
|
||||
.table-row:focus-within {
|
||||
outline: none;
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
|
||||
/* ── Page layout ── */
|
||||
.page {
|
||||
padding: 24px;
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
}
|
||||
.page-head {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
.eyebrow {
|
||||
color: var(--accent);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-xs);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.page-head h1 { font-size: clamp(30px, 4vw, 44px); }
|
||||
.page-head p { max-width: 70ch; color: var(--muted); }
|
||||
.control-bar {
|
||||
display: grid;
|
||||
grid-template-columns: 1.2fr repeat(4, minmax(0, 180px));
|
||||
gap: 12px;
|
||||
}
|
||||
.control {
|
||||
min-height: 44px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface);
|
||||
padding: 0 12px;
|
||||
color: var(--fg);
|
||||
}
|
||||
.control::placeholder { color: var(--muted); }
|
||||
.batch-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: color-mix(in oklab, var(--surface), var(--bg) 16%);
|
||||
padding: 14px 16px;
|
||||
}
|
||||
.batch-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
.card {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--surface);
|
||||
overflow: hidden;
|
||||
}
|
||||
.table-head,
|
||||
.table-row {
|
||||
display: grid;
|
||||
grid-template-columns: 28px 1.4fr 0.8fr 0.85fr 0.85fr 0.75fr 0.75fr 0.6fr;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
.table-head {
|
||||
border-bottom: 1px solid var(--border);
|
||||
color: var(--muted);
|
||||
font-size: var(--text-xs);
|
||||
font-family: var(--font-mono);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
background: color-mix(in oklab, var(--surface), var(--bg) 24%);
|
||||
}
|
||||
.table-row {
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
}
|
||||
.table-row:last-child { border-bottom: 0; }
|
||||
.table-row:hover { background: color-mix(in oklab, var(--fg), transparent 97%); }
|
||||
.footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
min-height: 36px;
|
||||
padding: 0 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
color: var(--muted);
|
||||
font-size: var(--text-xs);
|
||||
font-family: var(--font-mono);
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
background: color-mix(in oklab, var(--bg), var(--surface) 12%);
|
||||
margin-top: auto;
|
||||
}
|
||||
.footer-status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.footer-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #19d3a2;
|
||||
box-shadow: 0 0 0 4px color-mix(in oklab, #19d3a2, transparent 84%);
|
||||
}
|
||||
.doc-title {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
}
|
||||
.doc-title strong { font-size: var(--text-sm); }
|
||||
.doc-title span,
|
||||
.helper {
|
||||
color: var(--muted);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.mono {
|
||||
font-family: var(--font-mono);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: fit-content;
|
||||
padding: 4px 10px;
|
||||
border-radius: var(--radius-pill);
|
||||
border: 1px solid var(--border);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-xs);
|
||||
}
|
||||
.status::before {
|
||||
content: "";
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 999px;
|
||||
background: currentColor;
|
||||
}
|
||||
.status.ok { color: var(--success); }
|
||||
.status.warn { color: color-mix(in oklab, var(--warn), black 24%); }
|
||||
.status.risk { color: var(--danger); }
|
||||
.link-row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.text-link {
|
||||
color: var(--muted);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.text-link:hover { color: var(--accent); }
|
||||
.summary {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.summary-card {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--surface);
|
||||
padding: 16px;
|
||||
}
|
||||
.summary-card strong {
|
||||
display: block;
|
||||
font-size: 28px;
|
||||
line-height: 1;
|
||||
margin: 10px 0 8px;
|
||||
}
|
||||
.summary-card span {
|
||||
color: var(--muted);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 22px;
|
||||
padding: 0 8px;
|
||||
border-radius: var(--radius-pill);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--muted);
|
||||
font-size: var(--text-xs);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
/* ── Responsive ── */
|
||||
@media (max-width: 700px) {
|
||||
.app-shell { grid-template-columns: 1fr; }
|
||||
.sidebar { display: none; }
|
||||
}
|
||||
@media (max-width: 1280px) {
|
||||
.control-bar,
|
||||
.summary { grid-template-columns: 1fr 1fr; }
|
||||
.table-head,
|
||||
.table-row { grid-template-columns: 28px 1.3fr 0.9fr 0.9fr 0.8fr 0.7fr; }
|
||||
.table-head > :nth-child(7),
|
||||
.table-head > :nth-child(8),
|
||||
.table-row > :nth-child(7),
|
||||
.table-row > :nth-child(8) { display: none; }
|
||||
}
|
||||
@media (max-width: 840px) {
|
||||
.content-topbar,
|
||||
.page { padding-inline: 12px; }
|
||||
.control-bar,
|
||||
.summary,
|
||||
.page-head { grid-template-columns: 1fr; }
|
||||
.page-head { display: grid; align-items: start; }
|
||||
.table-head { display: none; }
|
||||
.table-row {
|
||||
grid-template-columns: 28px 1fr;
|
||||
align-items: start;
|
||||
}
|
||||
.table-row > *:nth-child(n+3) {
|
||||
grid-column: 2;
|
||||
}
|
||||
.batch-bar {
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
}
|
||||
.footer {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding-block: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body data-page="documents">
|
||||
<div class="app-shell">
|
||||
<!-- Sidebar -->
|
||||
<aside class="sidebar" aria-label="Primary navigation">
|
||||
<div class="sidebar-brand">
|
||||
<div class="brand-logo">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M2 3.5h12v1.5H2zm5.25 1.5h1.5v8h-1.5zm-3 2h2.25v1.5H4.25zm5.25 0h2.25v1.5H9.5zm-3 3h2.5v1.5H6.5z" fill="currentColor"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sidebar-brand-name">T-Systems</div>
|
||||
<div class="sidebar-brand-sub">Regulation Hub</div>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="sidebar-nav" aria-label="Primary">
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">主导航</span>
|
||||
<a class="nav-item" href="index.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M2 2h5v5H2zm7 0h5v5H9zM2 9h5v5H2zm7 0h5v5H9z" fill="currentColor" opacity=".7"/></svg>
|
||||
概览
|
||||
</a>
|
||||
<a class="nav-item" href="dashboard.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M1.5 2.5h13v1H1.5zm0 3h13v1H1.5zm0 3h8v1h-8zm0 3h6v1h-6zM12 8.5a3.5 3.5 0 110 7 3.5 3.5 0 010-7zm0 1a2.5 2.5 0 100 5 2.5 2.5 0 000-5zm.5 1v2h1.5v1H11v-3h1.5z" fill="currentColor"/></svg>
|
||||
系统状态
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">工作台</span>
|
||||
<a class="nav-item active" href="document-management.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M3 1h7l3 3v11H3V1zm1 1v12h8V5h-3V2H4zm5 .5V4h1.5L9 1.5zM6 7h4v1H6zm0 2h4v1H6zm0 2h3v1H6z" fill="currentColor"/></svg>
|
||||
文档管理
|
||||
</a>
|
||||
<a class="nav-item" href="compliance-analysis.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M8 1l7 3-1 6a7 7 0 01-6 5A7 7 0 011 10L0 4l8-3zm0 1.2L1.3 4.8l.8 5.1A6 6 0 008 14.8a6 6 0 005.9-4.9l.8-5.1L8 2.2zM7.5 5h1v4.5l-1 .5V5zm0 5.5h1v1h-1v-1z" fill="currentColor"/></svg>
|
||||
合规分析
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">对话</span>
|
||||
<a class="nav-item" href="regulation-chat.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M2 2h12a1 1 0 011 1v8a1 1 0 01-1 1H5l-3 2.5V3a1 1 0 011-1zm0 1v9.5L4.5 11H14V3H2zm2 2h8v1H4zm0 2h6v1H4z" fill="currentColor"/></svg>
|
||||
法规对话
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="sidebar-footer">
|
||||
<div class="sidebar-user">
|
||||
<div class="avatar">TS</div>
|
||||
<div class="sidebar-user-info">
|
||||
<div class="sidebar-user-name">T-Systems User</div>
|
||||
<div class="sidebar-user-role">Compliance Analyst</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="sidebar-action od-theme-toggle" type="button" data-od-theme aria-label="Toggle theme">
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M8 3a5 5 0 100 10A5 5 0 008 3zM2 8a6 6 0 1112 0A6 6 0 012 8z" fill="currentColor"/></svg>
|
||||
主题
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Content area -->
|
||||
<div class="content-area">
|
||||
<header class="content-topbar">
|
||||
<div class="topbar-title">文档管理</div>
|
||||
<div class="search">
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M6.5 1a5.5 5.5 0 014.23 9.02l3.62 3.62-.7.71-3.63-3.63A5.5 5.5 0 116.5 1zm0 1a4.5 4.5 0 100 9 4.5 4.5 0 000-9z" fill="currentColor" opacity=".5"/></svg>
|
||||
<input type="search" placeholder="Search documents…" aria-label="Search" />
|
||||
</div>
|
||||
<button class="btn">Import history</button>
|
||||
<a class="btn btn-primary" href="upload-modal.html">Upload documents</a>
|
||||
</header>
|
||||
|
||||
<main class="page">
|
||||
<section class="page-head" data-od-id="docs-head">
|
||||
<div>
|
||||
<div class="eyebrow">Document management</div>
|
||||
<h1>Library control for ingestion and indexing.</h1>
|
||||
</div>
|
||||
<div style="display:grid; gap:12px; justify-items:start;">
|
||||
<p style="margin:0;">Analysts can triage failed jobs, normalize metadata, and move a document from upload to parse-ready without leaving the operations workspace.</p>
|
||||
<div style="display:flex; gap:12px; align-items:center; flex-wrap:wrap;">
|
||||
<span class="pill">Queue</span>
|
||||
<button class="btn">Import history</button>
|
||||
<a class="btn btn-primary" href="upload-modal.html">Upload documents</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="summary" data-od-id="docs-summary">
|
||||
<div class="summary-card">
|
||||
<div class="helper">Total library</div>
|
||||
<strong class="mono">--</strong>
|
||||
<span>Across GB, GB/T, UNECE, ISO, and enterprise interpretation notes</span>
|
||||
</div>
|
||||
<div class="summary-card">
|
||||
<div class="helper">Processing</div>
|
||||
<strong class="mono">--</strong>
|
||||
<span>Waiting on parse, chunking, embedding, or index sync</span>
|
||||
</div>
|
||||
<div class="summary-card">
|
||||
<div class="helper">Failed</div>
|
||||
<strong class="mono">--</strong>
|
||||
<span>Timeout, OCR confidence, duplicate ID, or vector schema mismatch</span>
|
||||
</div>
|
||||
<div class="summary-card">
|
||||
<div class="helper">Avg summary latency</div>
|
||||
<strong class="mono">--</strong>
|
||||
<span>Document summary generation after artifacts complete</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="control-bar" data-od-id="docs-filters">
|
||||
<input class="control" type="text" value="GB/T" aria-label="Keyword filter" />
|
||||
<select class="control" aria-label="Status filter">
|
||||
<option>All statuses</option>
|
||||
<option selected>Processing + failed</option>
|
||||
<option>Indexed</option>
|
||||
</select>
|
||||
<select class="control" aria-label="Regulation type filter">
|
||||
<option selected>Vehicle safety</option>
|
||||
<option>Cybersecurity</option>
|
||||
<option>Battery</option>
|
||||
</select>
|
||||
<select class="control" aria-label="Parser filter">
|
||||
<option selected>Aliyun parser</option>
|
||||
<option>Legacy local parser</option>
|
||||
</select>
|
||||
<select class="control" aria-label="Owner filter">
|
||||
<option selected>All owners</option>
|
||||
<option>Battery Safety Team</option>
|
||||
<option>Connected Vehicle Team</option>
|
||||
</select>
|
||||
</section>
|
||||
|
||||
<section class="batch-bar" data-od-id="docs-batch">
|
||||
<div>
|
||||
<strong>4 selected</strong>
|
||||
<span class="helper">Batch actions apply metadata, retry parse, or archive stale drafts.</span>
|
||||
</div>
|
||||
<div class="batch-actions">
|
||||
<button class="btn">Assign category</button>
|
||||
<button class="btn">Retry parse</button>
|
||||
<button class="btn">Mark superseded</button>
|
||||
<button class="btn">Delete</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card" data-od-id="docs-table">
|
||||
<div class="table-head">
|
||||
<span></span>
|
||||
<span>Document</span>
|
||||
<span>Type</span>
|
||||
<span>Status</span>
|
||||
<span>Artifacts</span>
|
||||
<span>Updated</span>
|
||||
<span>Owner</span>
|
||||
<span>Actions</span>
|
||||
</div>
|
||||
|
||||
<div class="table-row">
|
||||
<input type="checkbox" checked aria-label="Select document" />
|
||||
<div class="doc-title">
|
||||
<strong>GB/T 31484-2015 battery energy density methods</strong>
|
||||
<span class="mono">doc_id: GBT-31484-2015-r2 · version 2026 addendum</span>
|
||||
</div>
|
||||
<span>Battery</span>
|
||||
<span class="status warn">Embedding</span>
|
||||
<span class="mono">chunk build active</span>
|
||||
<span class="mono">09:42</span>
|
||||
<span>Battery Safety</span>
|
||||
<div class="link-row">
|
||||
<a class="text-link" href="document-detail.html">Inspect</a>
|
||||
<a class="text-link" href="#">Retry</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-row">
|
||||
<input type="checkbox" checked aria-label="Select document" />
|
||||
<div class="doc-title">
|
||||
<strong>UNECE R155 cybersecurity management Q&A</strong>
|
||||
<span class="mono">doc_id: UNECE-R155-qa-2026-05 · summary ready</span>
|
||||
</div>
|
||||
<span>Cybersecurity</span>
|
||||
<span class="status ok">Indexed</span>
|
||||
<span class="mono">retrieval-ready</span>
|
||||
<span class="mono">08:18</span>
|
||||
<span>Connected Fleet</span>
|
||||
<div class="link-row">
|
||||
<a class="text-link" href="compliance-analysis.html">Analyze</a>
|
||||
<a class="text-link" href="regulation-chat.html">Chat</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-row">
|
||||
<input type="checkbox" aria-label="Select document" />
|
||||
<div class="doc-title">
|
||||
<strong>GB 26112-2010 roof strength scan</strong>
|
||||
<span class="mono">doc_id: GB-26112-scan-ocr · 6 pages low confidence</span>
|
||||
</div>
|
||||
<span>Vehicle safety</span>
|
||||
<span class="status risk">Failed</span>
|
||||
<span class="mono">OCR blocked</span>
|
||||
<span class="mono">Yesterday</span>
|
||||
<span>Body Structure</span>
|
||||
<div class="link-row">
|
||||
<a class="text-link" href="#">View error</a>
|
||||
<a class="text-link" href="#">Re-upload</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-row">
|
||||
<input type="checkbox" checked aria-label="Select document" />
|
||||
<div class="doc-title">
|
||||
<strong>GB/T 18487 charging interface interpretation</strong>
|
||||
<span class="mono">doc_id: GBT-18487-note-2026 · duplicate metadata candidate</span>
|
||||
</div>
|
||||
<span>Charging</span>
|
||||
<span class="status warn">Review</span>
|
||||
<span class="mono">Summary only</span>
|
||||
<span class="mono">11:06</span>
|
||||
<span>EV Platform</span>
|
||||
<div class="link-row">
|
||||
<a class="text-link" href="#">Normalize</a>
|
||||
<a class="text-link" href="#">Merge</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-row">
|
||||
<input type="checkbox" aria-label="Select document" />
|
||||
<div class="doc-title">
|
||||
<strong>Supplier thermal runaway test report</strong>
|
||||
<span class="mono">doc_id: supplier-pack-773A · confidential appendix attached</span>
|
||||
</div>
|
||||
<span>Evidence</span>
|
||||
<span class="status ok">Indexed</span>
|
||||
<span class="mono">248 chunks</span>
|
||||
<span class="mono">Monday</span>
|
||||
<span>Supplier QA</span>
|
||||
<div class="link-row">
|
||||
<a class="text-link" href="document-detail.html">Inspect</a>
|
||||
<a class="text-link" href="#">Download</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<span>T-Systems Regulation Hub</span>
|
||||
<div class="footer-status">
|
||||
<span class="footer-dot" aria-hidden="true"></span>
|
||||
<span>Online</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<script src="ui-preferences.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
696
Prototype/cc29bcb0-df2d-4d50-9428-7caa406ecb29/index.html
Normal file
696
Prototype/cc29bcb0-df2d-4d50-9428-7caa406ecb29/index.html
Normal file
@@ -0,0 +1,696 @@
|
||||
<!doctype html>
|
||||
<html lang="en"><head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>AI + Compliance Hub Prototype Suite</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #fafafa;
|
||||
--surface: #ffffff;
|
||||
--surface-warm: var(--surface);
|
||||
--fg: #111111;
|
||||
--fg-2: var(--fg);
|
||||
--muted: #6b6b6b;
|
||||
--meta: var(--muted);
|
||||
--border: #e5e5e5;
|
||||
--border-soft: var(--border);
|
||||
--primary: #e20074;
|
||||
--accent: var(--primary);
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), black 8%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 14%);
|
||||
--success: #17a34a;
|
||||
--warn: #eab308;
|
||||
--danger: #dc2626;
|
||||
--font-display: "TeleNeoWeb-Bold", "TeleNeoWeb-Medium", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-body: "TeleNeoWeb-Regular", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-mono: ui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospace;
|
||||
--text-xs: 12px;
|
||||
--text-sm: 14px;
|
||||
--text-base: 16px;
|
||||
--text-lg: 20px;
|
||||
--text-xl: 24px;
|
||||
--text-2xl: 32px;
|
||||
--text-3xl: 48px;
|
||||
--text-4xl: 64px;
|
||||
--leading-body: 1.5;
|
||||
--leading-tight: 1.2;
|
||||
--tracking-display: -0.01em;
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px;
|
||||
--space-5: 20px;
|
||||
--space-6: 24px;
|
||||
--space-8: 32px;
|
||||
--space-12: 48px;
|
||||
--space-20: 80px;
|
||||
--section-y-desktop: 80px;
|
||||
--section-y-tablet: 48px;
|
||||
--section-y-phone: 32px;
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 16px;
|
||||
--radius-pill: 9999px;
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 2px 8px color-mix(in oklab, var(--fg), transparent 92%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
--container-max: 1200px;
|
||||
--container-gutter-desktop: 24px;
|
||||
--container-gutter-tablet: 16px;
|
||||
--container-gutter-phone: 12px;
|
||||
--sidebar-w: 240px;
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme="light"]) {
|
||||
--bg: #0f1014;
|
||||
--surface: #17181d;
|
||||
--surface-warm: #1d1f26;
|
||||
--fg: #f5f7fb;
|
||||
--fg-2: #e5e8ef;
|
||||
--muted: #a2a9b8;
|
||||
--meta: #858d9c;
|
||||
--border: #2a2d35;
|
||||
--border-soft: #21242c;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), white 12%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 6%);
|
||||
--success: #22c55e;
|
||||
--warn: #facc15;
|
||||
--danger: #f87171;
|
||||
--elev-raised: 0 14px 36px color-mix(in oklab, black, transparent 74%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 56%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
|
||||
:root[data-theme="dark"] {
|
||||
--bg: #0f1014;
|
||||
--surface: #17181d;
|
||||
--surface-warm: #1d1f26;
|
||||
--fg: #f5f7fb;
|
||||
--fg-2: #e5e8ef;
|
||||
--muted: #a2a9b8;
|
||||
--meta: #858d9c;
|
||||
--border: #2a2d35;
|
||||
--border-soft: #21242c;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), white 12%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 6%);
|
||||
--success: #22c55e;
|
||||
--warn: #facc15;
|
||||
--danger: #f87171;
|
||||
--elev-raised: 0 14px 36px color-mix(in oklab, black, transparent 74%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 56%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
:root[data-theme="light"] {
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
html { -webkit-text-size-adjust: 100%; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--leading-body);
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
transition: color var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
a:hover { color: var(--fg); text-decoration: underline; }
|
||||
button, input, select { font: inherit; }
|
||||
p { text-wrap: pretty; }
|
||||
h1, h2, h3 { font-family: var(--font-display); line-height: var(--leading-tight); letter-spacing: var(--tracking-display); margin: 0; text-wrap: balance; }
|
||||
|
||||
/* ── Sidebar shell ── */
|
||||
.app-shell { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }
|
||||
.sidebar { position: sticky; top: 0; height: 100vh; overflow-y: auto; display: flex; flex-direction: column; background: var(--surface); border-right: 1px solid var(--border); z-index: 10; }
|
||||
.sidebar-brand { display: flex; align-items: center; gap: 10px; height: 56px; padding: 0 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
|
||||
.brand-logo { width: 26px; height: 26px; background: var(--accent); border-radius: 6px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||||
.brand-logo svg { color: #fff; }
|
||||
.sidebar-brand-name { font-family: var(--font-display); font-size: 13px; font-weight: 700; line-height: 1.2; }
|
||||
.sidebar-brand-sub { font-size: 10px; color: var(--muted); font-family: var(--font-mono); letter-spacing: 0.04em; }
|
||||
.sidebar-nav { flex: 1; padding: 12px 0; overflow-y: auto; }
|
||||
.nav-group { padding: 0 8px 4px; }
|
||||
.nav-group + .nav-group { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
|
||||
.nav-group-label { font-family: var(--font-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); padding: 0 8px 6px; display: block; }
|
||||
.nav-item { display: flex; align-items: center; gap: 10px; height: 36px; padding: 0 8px; border-radius: 6px; color: var(--muted); font-size: 13px; cursor: pointer; transition: background 140ms, color 140ms; position: relative; }
|
||||
.nav-item:hover { background: color-mix(in oklab, var(--fg), transparent 94%); color: var(--fg); text-decoration: none; }
|
||||
.nav-item.active { background: color-mix(in oklab, var(--accent), transparent 90%); color: var(--accent); font-weight: 600; }
|
||||
.nav-item.active::before { content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 3px; border-radius: 0 3px 3px 0; background: var(--accent); }
|
||||
.nav-icon { width: 16px; height: 16px; flex-shrink: 0; opacity: 0.7; }
|
||||
.nav-item.active .nav-icon { opacity: 1; }
|
||||
.sidebar-footer { border-top: 1px solid var(--border); padding: 10px 8px; flex-shrink: 0; display: flex; flex-direction: column; gap: 4px; }
|
||||
.sidebar-user { display: flex; align-items: center; gap: 10px; padding: 8px; border-radius: 6px; cursor: pointer; }
|
||||
.sidebar-user:hover { background: color-mix(in oklab, var(--fg), transparent 94%); }
|
||||
.avatar { width: 30px; height: 30px; border-radius: 50%; background: var(--accent); color: #fff; font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||||
.sidebar-user-info { min-width: 0; }
|
||||
.sidebar-user-name { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.sidebar-user-role { font-size: 11px; color: var(--muted); font-family: var(--font-mono); }
|
||||
.sidebar-action { display: flex; align-items: center; gap: 10px; height: 34px; padding: 0 8px; border-radius: 6px; color: var(--muted); font-size: 13px; cursor: pointer; border: none; background: transparent; width: 100%; text-align: left; transition: background 140ms, color 140ms; }
|
||||
.sidebar-action:hover { background: color-mix(in oklab, var(--fg), transparent 94%); color: var(--fg); }
|
||||
.content-area { display: flex; flex-direction: column; min-width: 0; min-height: 100vh; }
|
||||
.content-topbar { position: sticky; top: 0; z-index: 5; display: flex; align-items: center; gap: 12px; height: 56px; padding: 0 24px; border-bottom: 1px solid var(--border); background: color-mix(in oklab, var(--bg), transparent 4%); backdrop-filter: blur(10px); }
|
||||
.topbar-title { font-weight: 600; font-size: 15px; color: var(--fg); flex: 1; }
|
||||
.footer-dot { width: 7px; height: 7px; border-radius: 50%; background: #19d3a2; box-shadow: 0 0 0 3px color-mix(in oklab, #19d3a2, transparent 82%); }
|
||||
.footer-status { display: inline-flex; align-items: center; gap: 8px; }
|
||||
@media (max-width: 700px) { .app-shell { grid-template-columns: 1fr; } .sidebar { display: none; } }
|
||||
|
||||
/* ── Page-specific styles ── */
|
||||
.container {
|
||||
max-width: var(--container-max);
|
||||
margin: 0 auto;
|
||||
padding: 0 var(--container-gutter-desktop);
|
||||
}
|
||||
.meta {
|
||||
color: var(--muted);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.hero {
|
||||
padding: 72px 0 36px;
|
||||
display: grid;
|
||||
grid-template-columns: 1.3fr 0.9fr;
|
||||
gap: var(--space-12);
|
||||
align-items: start;
|
||||
}
|
||||
.eyebrow {
|
||||
color: var(--accent);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-xs);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
margin: 0 0 var(--space-4);
|
||||
}
|
||||
.hero h1 {
|
||||
font-size: clamp(40px, 6vw, 64px);
|
||||
max-width: 11ch;
|
||||
}
|
||||
.hero-copy {
|
||||
margin-top: var(--space-5);
|
||||
max-width: 58ch;
|
||||
color: var(--muted);
|
||||
font-size: var(--text-lg);
|
||||
}
|
||||
.hero-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-3);
|
||||
margin-top: var(--space-6);
|
||||
}
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--space-2);
|
||||
min-height: 44px;
|
||||
padding: 0 16px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
transition: background var(--motion-fast) var(--ease-standard), border-color var(--motion-fast) var(--ease-standard), color var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.btn:focus-visible,
|
||||
.screen-card a:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
.btn-primary {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: var(--accent-on);
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background: var(--accent-hover);
|
||||
text-decoration: none;
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
border-color: var(--fg);
|
||||
text-decoration: none;
|
||||
}
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-5);
|
||||
box-shadow: var(--elev-flat);
|
||||
}
|
||||
.summary-grid,
|
||||
.screen-grid {
|
||||
display: grid;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
.summary-grid {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
margin-bottom: 44px;
|
||||
}
|
||||
.screen-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
margin-bottom: 64px;
|
||||
}
|
||||
.screen-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
min-height: 280px;
|
||||
}
|
||||
.screen-card a {
|
||||
color: inherit;
|
||||
display: block;
|
||||
border-radius: inherit;
|
||||
}
|
||||
.screen-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
.chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border-radius: var(--radius-pill);
|
||||
border: 1px solid var(--border);
|
||||
padding: 4px 10px;
|
||||
color: var(--muted);
|
||||
font-size: var(--text-xs);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
.mini-shot {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background:
|
||||
linear-gradient(180deg, color-mix(in oklab, var(--accent), white 94%), transparent),
|
||||
var(--surface);
|
||||
padding: var(--space-4);
|
||||
min-height: 148px;
|
||||
}
|
||||
.mini-toolbar {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
.mini-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
background: var(--border);
|
||||
}
|
||||
.mini-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 140px 1fr;
|
||||
gap: var(--space-3);
|
||||
min-height: 94px;
|
||||
}
|
||||
.mini-nav,
|
||||
.mini-body,
|
||||
.mini-row,
|
||||
.mini-block {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: color-mix(in oklab, var(--surface), var(--bg) 24%);
|
||||
}
|
||||
.mini-nav { padding: var(--space-3); }
|
||||
.mini-body { padding: var(--space-3); display: grid; gap: var(--space-2); }
|
||||
.mini-row {
|
||||
height: 12px;
|
||||
background: color-mix(in oklab, var(--fg), transparent 96%);
|
||||
}
|
||||
.mini-row.accent {
|
||||
width: 42%;
|
||||
background: color-mix(in oklab, var(--accent), white 76%);
|
||||
border-color: color-mix(in oklab, var(--accent), white 70%);
|
||||
}
|
||||
.mini-block { height: 56px; }
|
||||
.section-title {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-4);
|
||||
margin-bottom: var(--space-6);
|
||||
}
|
||||
.section-title h2 { font-size: var(--text-2xl); }
|
||||
.section-title p { margin: 0; max-width: 58ch; color: var(--muted); }
|
||||
.flow {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
gap: var(--space-3);
|
||||
margin-bottom: 80px;
|
||||
}
|
||||
.flow-step {
|
||||
position: relative;
|
||||
padding: var(--space-4);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--surface);
|
||||
}
|
||||
.flow-step strong {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.flow-step span {
|
||||
display: block;
|
||||
color: var(--muted);
|
||||
font-size: var(--text-xs);
|
||||
}
|
||||
.flow-step::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: -13px;
|
||||
width: 10px;
|
||||
height: 1px;
|
||||
background: var(--border);
|
||||
}
|
||||
.flow-step:last-child::after { display: none; }
|
||||
.footer {
|
||||
padding: 24px 0 48px;
|
||||
border-top: 1px solid var(--border);
|
||||
color: var(--muted);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
|
||||
@media (max-width: 1023px) {
|
||||
.hero,
|
||||
.summary-grid,
|
||||
.screen-grid,
|
||||
.flow {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.container { padding: 0 var(--container-gutter-tablet); }
|
||||
.flow-step::after { display: none; }
|
||||
}
|
||||
|
||||
@media (max-width: 639px) {
|
||||
.container { padding: 0 var(--container-gutter-phone); }
|
||||
.hero { padding-top: 48px; }
|
||||
.hero-copy { font-size: var(--text-base); }
|
||||
.screen-head { align-items: start; flex-direction: column; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body data-page="index">
|
||||
<div class="app-shell">
|
||||
<aside class="sidebar" aria-label="Primary navigation">
|
||||
<div class="sidebar-brand">
|
||||
<div class="brand-logo">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M2 3.5h12v1.5H2zm5.25 1.5h1.5v8h-1.5zm-3 2h2.25v1.5H4.25zm5.25 0h2.25v1.5H9.5zm-3 3h2.5v1.5H6.5z" fill="currentColor"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sidebar-brand-name">T-Systems</div>
|
||||
<div class="sidebar-brand-sub">Regulation Hub</div>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="sidebar-nav" aria-label="Primary">
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">主导航</span>
|
||||
<a class="nav-item active" href="index.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M2 2h5v5H2zm7 0h5v5H9zM2 9h5v5H2zm7 0h5v5H9z" fill="currentColor" opacity=".7"/></svg>
|
||||
概览
|
||||
</a>
|
||||
<a class="nav-item" href="dashboard.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M1.5 2.5h13v1H1.5zm0 3h13v1H1.5zm0 3h8v1h-8zm0 3h6v1h-6z" fill="currentColor"/></svg>
|
||||
系统状态
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">工作台</span>
|
||||
<a class="nav-item" href="document-management.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M3 1h7l3 3v11H3V1zm1 1v12h8V5h-3V2H4zm5 .5V4h1.5L9 1.5zM6 7h4v1H6zm0 2h4v1H6zm0 2h3v1H6z" fill="currentColor"/></svg>
|
||||
文档管理
|
||||
</a>
|
||||
<a class="nav-item" href="compliance-analysis.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M8 1l7 3-1 6a7 7 0 01-6 5A7 7 0 011 10L0 4l8-3zm0 1.2L1.3 4.8l.8 5.1A6 6 0 008 14.8a6 6 0 005.9-4.9l.8-5.1L8 2.2zM7.5 5h1v4.5l-1 .5V5zm0 5.5h1v1h-1v-1z" fill="currentColor"/></svg>
|
||||
合规分析
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">对话</span>
|
||||
<a class="nav-item" href="regulation-chat.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M2 2h12a1 1 0 011 1v8a1 1 0 01-1 1H5l-3 2.5V3a1 1 0 011-1zm0 1v9.5L4.5 11H14V3H2zm2 2h8v1H4zm0 2h6v1H4z" fill="currentColor"/></svg>
|
||||
法规对话
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="sidebar-footer">
|
||||
<div class="sidebar-user">
|
||||
<div class="avatar">TS</div>
|
||||
<div class="sidebar-user-info">
|
||||
<div class="sidebar-user-name">T-Systems User</div>
|
||||
<div class="sidebar-user-role">Compliance Analyst</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="sidebar-action od-theme-toggle" type="button" data-od-theme aria-label="Toggle theme">
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M8 3a5 5 0 100 10A5 5 0 008 3zM2 8a6 6 0 1112 0A6 6 0 012 8z" fill="currentColor"/></svg>
|
||||
主题
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div class="content-area">
|
||||
<header class="content-topbar">
|
||||
<span class="topbar-title">概览</span>
|
||||
</header>
|
||||
|
||||
<main class="container">
|
||||
<section class="hero" data-od-id="hero">
|
||||
<div>
|
||||
<p class="eyebrow">Prototype suite</p>
|
||||
<h1>Operational screens for AI document compliance work.</h1>
|
||||
<p class="hero-copy">
|
||||
This launcher maps the full desktop workflow: intake, parsing, embeddings, retrieval-led analysis, and citation-backed chat.
|
||||
Each screen is isolated as its own product surface so reviewers can inspect decisions without switching fake demo controls.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a class="btn btn-primary" href="dashboard.html">Open dashboard</a>
|
||||
<a class="btn btn-secondary" href="regulation-chat.html">Jump to regulation chat</a>
|
||||
</div>
|
||||
</div>
|
||||
<aside class="card">
|
||||
<p class="eyebrow">Scope</p>
|
||||
<div class="summary-grid" style="grid-template-columns: 1fr; margin: 0; gap: 14px;">
|
||||
<div>
|
||||
<strong>6 product screens</strong>
|
||||
<div class="meta">Launcher, operations overview, doc management, upload, parse detail, analysis, chat</div>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Backend-aware flows</strong>
|
||||
<div class="meta">Aliyun parsing, chunk generation, text-embedding-v3, dense vector collection, citation retrieval</div>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Review posture</strong>
|
||||
<div class="meta">Quiet utility chrome, clear status states, one accent reserved for action and escalation</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
<section data-od-id="workflow">
|
||||
<div class="section-title">
|
||||
<div>
|
||||
<p class="eyebrow">Interaction rhythm</p>
|
||||
<h2>One sequence, six focused stops.</h2>
|
||||
</div>
|
||||
<p>The product cadence moves from portfolio awareness to precise intervention. Each screen hands off to the next likely action instead of collapsing everything into a single dense page.</p>
|
||||
</div>
|
||||
<div class="flow">
|
||||
<div class="flow-step">
|
||||
<strong>01 Dashboard</strong>
|
||||
<span>Watch ingestion health, queue pressure, policy risk, and active investigations.</span>
|
||||
</div>
|
||||
<div class="flow-step">
|
||||
<strong>02 Library</strong>
|
||||
<span>Filter standards, inspect states, trigger retry, delete, and batch assign metadata.</span>
|
||||
</div>
|
||||
<div class="flow-step">
|
||||
<strong>03 Upload</strong>
|
||||
<span>Stage files, assign regulation type and version, and monitor import queue progress.</span>
|
||||
</div>
|
||||
<div class="flow-step">
|
||||
<strong>04 Parse detail</strong>
|
||||
<span>Follow document parsing, semantic blocks, vector chunks, and embedding/index milestones.</span>
|
||||
</div>
|
||||
<div class="flow-step">
|
||||
<strong>05 Analysis</strong>
|
||||
<span>Compare source passages with retrieved regulations, findings, and conclusion-ready actions.</span>
|
||||
</div>
|
||||
<div class="flow-step">
|
||||
<strong>06 Chat</strong>
|
||||
<span>Interrogate a clause with citations, trace history, and export reasoning with sources.</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-od-id="screens">
|
||||
<div class="section-title">
|
||||
<div>
|
||||
<p class="eyebrow">Screens</p>
|
||||
<h2>Open any surface directly.</h2>
|
||||
</div>
|
||||
<p>Each tile previews its UI structure and primary job. These are entry points into a realistic desktop workflow, not storyboards.</p>
|
||||
</div>
|
||||
<div class="screen-grid">
|
||||
<article class="screen-card card">
|
||||
<div class="screen-head">
|
||||
<div>
|
||||
<h3>Dashboard</h3>
|
||||
<div class="meta">Overview of system health, backlog, and current compliance programs</div>
|
||||
</div>
|
||||
<span class="chip">Operations</span>
|
||||
</div>
|
||||
<a href="dashboard.html">
|
||||
<div class="mini-shot" aria-hidden="true">
|
||||
<div class="mini-toolbar"><span class="mini-dot"></span><span class="mini-dot"></span><span class="mini-dot"></span></div>
|
||||
<div class="mini-layout">
|
||||
<div class="mini-nav"></div>
|
||||
<div class="mini-body">
|
||||
<div class="mini-row accent"></div>
|
||||
<div class="mini-block"></div>
|
||||
<div class="mini-row"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</article>
|
||||
|
||||
<article class="screen-card card">
|
||||
<div class="screen-head">
|
||||
<div>
|
||||
<h3>Document management</h3>
|
||||
<div class="meta">Library, filters, batch actions, and ingestion state control</div>
|
||||
</div>
|
||||
<span class="chip">Library</span>
|
||||
</div>
|
||||
<a href="document-management.html">
|
||||
<div class="mini-shot" aria-hidden="true">
|
||||
<div class="mini-toolbar"><span class="mini-dot"></span><span class="mini-dot"></span><span class="mini-dot"></span></div>
|
||||
<div class="mini-layout">
|
||||
<div class="mini-nav"></div>
|
||||
<div class="mini-body">
|
||||
<div class="mini-row accent"></div>
|
||||
<div class="mini-row"></div>
|
||||
<div class="mini-block"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</article>
|
||||
|
||||
<article class="screen-card card">
|
||||
<div class="screen-head">
|
||||
<div>
|
||||
<h3>Upload modal</h3>
|
||||
<div class="meta">Drag-drop intake, metadata assignment, and import queue feedback</div>
|
||||
</div>
|
||||
<span class="chip">Intake</span>
|
||||
</div>
|
||||
<a href="upload-modal.html">
|
||||
<div class="mini-shot" aria-hidden="true">
|
||||
<div class="mini-toolbar"><span class="mini-dot"></span><span class="mini-dot"></span><span class="mini-dot"></span></div>
|
||||
<div class="mini-layout" style="grid-template-columns: 1fr;">
|
||||
<div class="mini-body">
|
||||
<div class="mini-block"></div>
|
||||
<div class="mini-row accent"></div>
|
||||
<div class="mini-row"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</article>
|
||||
|
||||
<article class="screen-card card">
|
||||
<div class="screen-head">
|
||||
<div>
|
||||
<h3>Document detail</h3>
|
||||
<div class="meta">Parsing, chunking, embedding, and vector store progress by artifact stage</div>
|
||||
</div>
|
||||
<span class="chip">Pipeline</span>
|
||||
</div>
|
||||
<a href="document-detail.html">
|
||||
<div class="mini-shot" aria-hidden="true">
|
||||
<div class="mini-toolbar"><span class="mini-dot"></span><span class="mini-dot"></span><span class="mini-dot"></span></div>
|
||||
<div class="mini-layout">
|
||||
<div class="mini-nav"></div>
|
||||
<div class="mini-body">
|
||||
<div class="mini-row accent"></div>
|
||||
<div class="mini-block"></div>
|
||||
<div class="mini-block"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</article>
|
||||
|
||||
<article class="screen-card card">
|
||||
<div class="screen-head">
|
||||
<div>
|
||||
<h3>Compliance analysis</h3>
|
||||
<div class="meta">Retrieval to reasoning to conclusion workspace with tracked evidence</div>
|
||||
</div>
|
||||
<span class="chip">Analysis</span>
|
||||
</div>
|
||||
<a href="compliance-analysis.html">
|
||||
<div class="mini-shot" aria-hidden="true">
|
||||
<div class="mini-toolbar"><span class="mini-dot"></span><span class="mini-dot"></span><span class="mini-dot"></span></div>
|
||||
<div class="mini-layout">
|
||||
<div class="mini-nav"></div>
|
||||
<div class="mini-body">
|
||||
<div class="mini-row accent"></div>
|
||||
<div class="mini-row"></div>
|
||||
<div class="mini-block"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</article>
|
||||
|
||||
<article class="screen-card card">
|
||||
<div class="screen-head">
|
||||
<div>
|
||||
<h3>Regulation chat</h3>
|
||||
<div class="meta">Source-backed question answering with history, quick prompts, and citation rail</div>
|
||||
</div>
|
||||
<span class="chip">Copilot</span>
|
||||
</div>
|
||||
<a href="regulation-chat.html">
|
||||
<div class="mini-shot" aria-hidden="true">
|
||||
<div class="mini-toolbar"><span class="mini-dot"></span><span class="mini-dot"></span><span class="mini-dot"></span></div>
|
||||
<div class="mini-layout">
|
||||
<div class="mini-nav"></div>
|
||||
<div class="mini-body">
|
||||
<div class="mini-row accent"></div>
|
||||
<div class="mini-row"></div>
|
||||
<div class="mini-row"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container">Prototype prepared for product evaluators reviewing document ingestion, AI parsing, and compliance reasoning workflows.</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<script src="ui-preferences.js"></script>
|
||||
|
||||
|
||||
</body></html>
|
||||
@@ -0,0 +1,608 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>AI + Compliance Hub - Regulation Chat</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #fafafa;
|
||||
--surface: #ffffff;
|
||||
--surface-warm: var(--surface);
|
||||
--fg: #111111;
|
||||
--fg-2: var(--fg);
|
||||
--muted: #6b6b6b;
|
||||
--meta: var(--muted);
|
||||
--border: #e5e5e5;
|
||||
--border-soft: var(--border);
|
||||
--primary: #e20074;
|
||||
--accent: var(--primary);
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), black 8%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 14%);
|
||||
--success: #17a34a;
|
||||
--warn: #eab308;
|
||||
--danger: #dc2626;
|
||||
--font-display: "TeleNeoWeb-Bold", "TeleNeoWeb-Medium", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-body: "TeleNeoWeb-Regular", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-mono: ui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospace;
|
||||
--text-xs: 12px;
|
||||
--text-sm: 14px;
|
||||
--text-base: 16px;
|
||||
--text-lg: 20px;
|
||||
--text-xl: 24px;
|
||||
--text-2xl: 32px;
|
||||
--text-3xl: 48px;
|
||||
--text-4xl: 64px;
|
||||
--leading-body: 1.5;
|
||||
--leading-tight: 1.2;
|
||||
--tracking-display: -0.01em;
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px;
|
||||
--space-5: 20px;
|
||||
--space-6: 24px;
|
||||
--space-8: 32px;
|
||||
--space-12: 48px;
|
||||
--space-20: 80px;
|
||||
--section-y-desktop: 80px;
|
||||
--section-y-tablet: 48px;
|
||||
--section-y-phone: 32px;
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 16px;
|
||||
--radius-pill: 9999px;
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 2px 8px color-mix(in oklab, var(--fg), transparent 92%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
--container-max: 1600px;
|
||||
--container-gutter-desktop: 24px;
|
||||
--container-gutter-tablet: 16px;
|
||||
--container-gutter-phone: 12px;
|
||||
--sidebar-w: 240px;
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme="light"]) {
|
||||
--bg: #0f1014;
|
||||
--surface: #17181d;
|
||||
--surface-warm: #1d1f26;
|
||||
--fg: #f5f7fb;
|
||||
--fg-2: #e5e8ef;
|
||||
--muted: #a2a9b8;
|
||||
--meta: #858d9c;
|
||||
--border: #2a2d35;
|
||||
--border-soft: #21242c;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), white 12%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 6%);
|
||||
--success: #22c55e;
|
||||
--warn: #facc15;
|
||||
--danger: #f87171;
|
||||
--elev-raised: 0 14px 36px color-mix(in oklab, black, transparent 74%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 56%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
|
||||
:root[data-theme="dark"] {
|
||||
--bg: #0f1014;
|
||||
--surface: #17181d;
|
||||
--surface-warm: #1d1f26;
|
||||
--fg: #f5f7fb;
|
||||
--fg-2: #e5e8ef;
|
||||
--muted: #a2a9b8;
|
||||
--meta: #858d9c;
|
||||
--border: #2a2d35;
|
||||
--border-soft: #21242c;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), white 12%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 6%);
|
||||
--success: #22c55e;
|
||||
--warn: #facc15;
|
||||
--danger: #f87171;
|
||||
--elev-raised: 0 14px 36px color-mix(in oklab, black, transparent 74%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 56%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
:root[data-theme="light"] {
|
||||
color-scheme: light;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--leading-body);
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
h1, h2, h3 {
|
||||
margin: 0;
|
||||
font-family: var(--font-display);
|
||||
line-height: var(--leading-tight);
|
||||
letter-spacing: var(--tracking-display);
|
||||
}
|
||||
p { margin: 0; text-wrap: pretty; }
|
||||
button, input { font: inherit; }
|
||||
a { color: inherit; text-decoration: none; }
|
||||
a:hover { color: var(--fg); text-decoration: underline; }
|
||||
|
||||
/* ── Sidebar shell ── */
|
||||
.app-shell { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }
|
||||
.sidebar { position: sticky; top: 0; height: 100vh; overflow-y: auto; display: flex; flex-direction: column; background: var(--surface); border-right: 1px solid var(--border); z-index: 10; }
|
||||
.sidebar-brand { display: flex; align-items: center; gap: 10px; height: 56px; padding: 0 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
|
||||
.brand-logo { width: 26px; height: 26px; background: var(--accent); border-radius: 6px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||||
.brand-logo svg { color: #fff; }
|
||||
.sidebar-brand-name { font-family: var(--font-display); font-size: 13px; font-weight: 700; line-height: 1.2; }
|
||||
.sidebar-brand-sub { font-size: 10px; color: var(--muted); font-family: var(--font-mono); letter-spacing: 0.04em; }
|
||||
.sidebar-nav { flex: 1; padding: 12px 0; overflow-y: auto; }
|
||||
.nav-group { padding: 0 8px 4px; }
|
||||
.nav-group + .nav-group { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
|
||||
.nav-group-label { font-family: var(--font-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); padding: 0 8px 6px; display: block; }
|
||||
.nav-item { display: flex; align-items: center; gap: 10px; height: 36px; padding: 0 8px; border-radius: 6px; color: var(--muted); font-size: 13px; cursor: pointer; transition: background 140ms, color 140ms; position: relative; }
|
||||
.nav-item:hover { background: color-mix(in oklab, var(--fg), transparent 94%); color: var(--fg); text-decoration: none; }
|
||||
.nav-item.active { background: color-mix(in oklab, var(--accent), transparent 90%); color: var(--accent); font-weight: 600; }
|
||||
.nav-item.active::before { content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 3px; border-radius: 0 3px 3px 0; background: var(--accent); }
|
||||
.nav-icon { width: 16px; height: 16px; flex-shrink: 0; opacity: 0.7; }
|
||||
.nav-item.active .nav-icon { opacity: 1; }
|
||||
.sidebar-footer { border-top: 1px solid var(--border); padding: 10px 8px; flex-shrink: 0; display: flex; flex-direction: column; gap: 4px; }
|
||||
.sidebar-user { display: flex; align-items: center; gap: 10px; padding: 8px; border-radius: 6px; cursor: pointer; }
|
||||
.sidebar-user:hover { background: color-mix(in oklab, var(--fg), transparent 94%); }
|
||||
.avatar { width: 30px; height: 30px; border-radius: 50%; background: var(--accent); color: #fff; font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||||
.sidebar-user-info { min-width: 0; }
|
||||
.sidebar-user-name { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.sidebar-user-role { font-size: 11px; color: var(--muted); font-family: var(--font-mono); }
|
||||
.sidebar-action { display: flex; align-items: center; gap: 10px; height: 34px; padding: 0 8px; border-radius: 6px; color: var(--muted); font-size: 13px; cursor: pointer; border: none; background: transparent; width: 100%; text-align: left; transition: background 140ms, color 140ms; }
|
||||
.sidebar-action:hover { background: color-mix(in oklab, var(--fg), transparent 94%); color: var(--fg); }
|
||||
.content-area { display: flex; flex-direction: column; min-width: 0; min-height: 100vh; }
|
||||
.content-topbar { position: sticky; top: 0; z-index: 5; display: flex; align-items: center; gap: 12px; height: 56px; padding: 0 24px; border-bottom: 1px solid var(--border); background: color-mix(in oklab, var(--bg), transparent 4%); backdrop-filter: blur(10px); }
|
||||
.topbar-title { font-weight: 600; font-size: 15px; color: var(--fg); flex: 1; }
|
||||
.footer-status { display: inline-flex; align-items: center; gap: 8px; }
|
||||
.footer-dot { width: 7px; height: 7px; border-radius: 50%; background: #19d3a2; }
|
||||
@media (max-width: 700px) { .app-shell { grid-template-columns: 1fr; } .sidebar { display: none; } }
|
||||
|
||||
/* ── Page content ── */
|
||||
.footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
min-height: 36px;
|
||||
padding: 0 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
color: var(--muted);
|
||||
font-size: var(--text-xs);
|
||||
font-family: var(--font-mono);
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
background: color-mix(in oklab, var(--bg), var(--surface) 12%);
|
||||
}
|
||||
.page {
|
||||
max-width: 1600px;
|
||||
margin: 0 auto;
|
||||
padding: 24px;
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
}
|
||||
.hero {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
}
|
||||
.hero h1 { font-size: clamp(30px, 4vw, 44px); }
|
||||
.hero p { max-width: 72ch; color: var(--muted); }
|
||||
.eyebrow {
|
||||
color: var(--accent);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-xs);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.shell {
|
||||
display: grid;
|
||||
grid-template-columns: 280px minmax(0, 1fr) 320px;
|
||||
gap: 16px;
|
||||
min-height: 760px;
|
||||
}
|
||||
.card {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--surface);
|
||||
padding: 18px;
|
||||
min-width: 0;
|
||||
}
|
||||
.section-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.section-head h2 { font-size: var(--text-xl); }
|
||||
.helper {
|
||||
color: var(--muted);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.history,
|
||||
.quick-list,
|
||||
.sources,
|
||||
.messages {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
.history-item,
|
||||
.quick-item,
|
||||
.source-item,
|
||||
.message {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: color-mix(in oklab, var(--surface), var(--bg) 14%);
|
||||
padding: 14px;
|
||||
}
|
||||
.history-item.active {
|
||||
background: color-mix(in oklab, var(--accent), white 92%);
|
||||
border-color: color-mix(in oklab, var(--accent), white 70%);
|
||||
}
|
||||
.pill,
|
||||
.status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: fit-content;
|
||||
padding: 4px 10px;
|
||||
border-radius: var(--radius-pill);
|
||||
border: 1px solid var(--border);
|
||||
font-size: var(--text-xs);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
.status::before {
|
||||
content: "";
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 999px;
|
||||
background: currentColor;
|
||||
}
|
||||
.status.ok { color: var(--success); }
|
||||
.status.warn { color: color-mix(in oklab, var(--warn), black 24%); }
|
||||
.status.risk { color: var(--danger); }
|
||||
.mono {
|
||||
font-family: var(--font-mono);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.chat-column {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
gap: 16px;
|
||||
min-height: 0;
|
||||
}
|
||||
.messages {
|
||||
align-content: start;
|
||||
overflow: auto;
|
||||
min-height: 0;
|
||||
padding-right: 4px;
|
||||
}
|
||||
.message.assistant {
|
||||
background: color-mix(in oklab, var(--surface), var(--bg) 8%);
|
||||
}
|
||||
.message.user {
|
||||
background: color-mix(in oklab, var(--accent), white 92%);
|
||||
border-color: color-mix(in oklab, var(--accent), white 70%);
|
||||
}
|
||||
.message-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.composer {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--surface);
|
||||
padding: 14px;
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
.input {
|
||||
min-height: 52px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: color-mix(in oklab, var(--surface), var(--bg) 8%);
|
||||
padding: 0 14px;
|
||||
color: var(--fg);
|
||||
width: 100%;
|
||||
}
|
||||
.input:focus-visible,
|
||||
.quick-item:focus-visible,
|
||||
.history-item:focus-visible,
|
||||
.btn:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
.btn-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.btn {
|
||||
min-height: 44px;
|
||||
padding: 0 16px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
}
|
||||
.btn-primary {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: var(--accent-on);
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background: var(--accent-hover);
|
||||
border-color: var(--accent-hover);
|
||||
}
|
||||
.source-item strong,
|
||||
.history-item strong,
|
||||
.quick-item strong {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.citation {
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid var(--border);
|
||||
color: var(--muted);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
@media (max-width: 1320px) {
|
||||
.shell { grid-template-columns: 1fr; }
|
||||
.hero { flex-direction: column; align-items: start; }
|
||||
}
|
||||
@media (max-width: 760px) {
|
||||
.page { padding: 12px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body data-page="chat">
|
||||
<div class="app-shell">
|
||||
<!-- ── Sidebar ── -->
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-brand">
|
||||
<div class="brand-logo">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M3 5.5h18v2H3zm8 2h2v11h-2zm-5 3h3v2H6zm9 0h3v2h-3zm-5 4h2v2h-2z" fill="currentColor" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sidebar-brand-name">T-Systems</div>
|
||||
<div class="sidebar-brand-sub">Regulation Hub</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="sidebar-nav" aria-label="Primary">
|
||||
<!-- 主导航 -->
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">主导航</span>
|
||||
<a class="nav-item" href="index.html">
|
||||
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M3 9.5L12 3l9 6.5V20a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9.5z"/><path d="M9 21V12h6v9"/>
|
||||
</svg>
|
||||
概览
|
||||
</a>
|
||||
<a class="nav-item" href="dashboard.html">
|
||||
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/>
|
||||
</svg>
|
||||
系统状态
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- 工作台 -->
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">工作台</span>
|
||||
<a class="nav-item" href="document-management.html">
|
||||
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="16" y2="17"/>
|
||||
</svg>
|
||||
文档管理
|
||||
</a>
|
||||
<a class="nav-item" href="compliance-analysis.html">
|
||||
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
|
||||
</svg>
|
||||
合规分析
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- 对话 -->
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">对话</span>
|
||||
<a class="nav-item active" href="regulation-chat.html">
|
||||
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
|
||||
</svg>
|
||||
法规对话
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="sidebar-footer">
|
||||
<div class="sidebar-user">
|
||||
<div class="avatar">张</div>
|
||||
<div class="sidebar-user-info">
|
||||
<div class="sidebar-user-name">张工程师</div>
|
||||
<div class="sidebar-user-role">compliance.eng</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="sidebar-action" type="button" data-od-theme aria-label="Toggle color theme">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
|
||||
</svg>
|
||||
主题: 自动
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- ── Content area ── -->
|
||||
<div class="content-area">
|
||||
<header class="content-topbar">
|
||||
<span class="topbar-title">法规对话</span>
|
||||
<div class="footer-status">
|
||||
<span class="footer-dot" aria-hidden="true"></span>
|
||||
<span style="font-size:12px; color:var(--muted); font-family:var(--font-mono);">Session synced</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="page">
|
||||
<section class="hero" data-od-id="chat-hero">
|
||||
<div>
|
||||
<div class="eyebrow">Regulation chat</div>
|
||||
<h1>Ask clause-level questions with citations intact.</h1>
|
||||
<p>The chat surface is tuned for compliance follow-up after analysis. Conversation history stays visible, quick prompts speed common queries, and the right rail keeps cited standards inspectable while the answer streams.</p>
|
||||
</div>
|
||||
<div style="display:flex; gap:12px; flex-wrap:wrap;">
|
||||
<a class="pill" href="compliance-analysis.html">From analysis workspace</a>
|
||||
<span class="status ok">Session synced</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="shell" data-od-id="chat-shell">
|
||||
<aside class="card">
|
||||
<div class="section-head">
|
||||
<h2>History</h2>
|
||||
<span class="helper">3 recent threads</span>
|
||||
</div>
|
||||
<div class="history">
|
||||
<div class="history-item active" tabindex="0">
|
||||
<strong>Roof crush resistance wording</strong>
|
||||
<div class="helper">Started 10:14 · 5 replies · linked to GB 26112-2010</div>
|
||||
</div>
|
||||
<div class="history-item" tabindex="0">
|
||||
<strong>R155 incident log retention</strong>
|
||||
<div class="helper">Started yesterday · 8 replies · cybersecurity watch</div>
|
||||
</div>
|
||||
<div class="history-item" tabindex="0">
|
||||
<strong>Thermal runaway evidence checklist</strong>
|
||||
<div class="helper">Started Monday · 4 replies · supplier dossier</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-head" style="margin-top:20px;">
|
||||
<h2>Quick questions</h2>
|
||||
<span class="helper">Common prompts</span>
|
||||
</div>
|
||||
<div class="quick-list">
|
||||
<button class="quick-item" type="button"><strong>What exact numeric threshold is required here?</strong><span class="helper">Turn vague compliance language into a requirement statement.</span></button>
|
||||
<button class="quick-item" type="button"><strong>Which citations are mandatory versus contextual?</strong><span class="helper">Separate legal requirement from scoring or best practice references.</span></button>
|
||||
<button class="quick-item" type="button"><strong>Draft replacement wording for the dossier.</strong><span class="helper">Generate an auditable sentence that cites the source clause.</span></button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<section class="chat-column">
|
||||
<div class="card">
|
||||
<div class="section-head">
|
||||
<h2>Active thread</h2>
|
||||
<span class="helper">Chunk 148 · GB 26112-2010 context</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="messages">
|
||||
<article class="message user">
|
||||
<div class="message-top">
|
||||
<strong>Reviewer</strong>
|
||||
<span class="helper mono">10:14</span>
|
||||
</div>
|
||||
<p>Does this paragraph need a numeric load statement, or is the current "meets national crush-resistance requirements" wording enough?</p>
|
||||
</article>
|
||||
|
||||
<article class="message assistant">
|
||||
<div class="message-top">
|
||||
<strong>Compliance assistant</strong>
|
||||
<span class="helper mono">10:14</span>
|
||||
</div>
|
||||
<p>The current wording is not sufficient on its own. The retrieved primary citation, <strong>GB 26112-2010 §4.2</strong>, defines a measurable resistance requirement. To make the dossier auditable, the paragraph should state the tested load threshold and link the supporting report.</p>
|
||||
<div class="citation">
|
||||
Primary citation: GB 26112-2010 §4.2 · lead match for the current paragraph · linked body engineering report candidate
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="message user">
|
||||
<div class="message-top">
|
||||
<strong>Reviewer</strong>
|
||||
<span class="helper mono">10:16</span>
|
||||
</div>
|
||||
<p>Draft the exact replacement sentence I can hand back to the body structure team.</p>
|
||||
</article>
|
||||
|
||||
<article class="message assistant">
|
||||
<div class="message-top">
|
||||
<strong>Compliance assistant</strong>
|
||||
<span class="helper mono">10:16</span>
|
||||
</div>
|
||||
<p>Use: "The roof support structure was validated in accordance with <strong>GB 26112-2010 §4.2</strong>; supporting evidence is documented in the linked body engineering report."</p>
|
||||
<div class="citation">
|
||||
Evidence used: GB 26112-2010 §4.2, internal engineering report reference, related C-NCAP context kept as secondary only.
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<form class="composer">
|
||||
<input class="input" type="text" value="Which part of this answer is mandatory compliance language and which part is supporting evidence?" aria-label="Chat input" />
|
||||
<div class="btn-row">
|
||||
<div class="helper">Citations stay attached to each answer and are exportable with the thread.</div>
|
||||
<button class="btn btn-primary" type="submit">Send question</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<aside class="card">
|
||||
<div class="section-head">
|
||||
<h2>Citation rail</h2>
|
||||
<span class="helper">Sources in current answer</span>
|
||||
</div>
|
||||
<div class="sources">
|
||||
<div class="source-item">
|
||||
<strong>GB 26112-2010 §4.2</strong>
|
||||
<div class="helper">Primary mandatory requirement used to justify the numeric threshold.</div>
|
||||
<div class="citation">Reason surfaced: direct match on roof crush resistance wording and the validation requirement.</div>
|
||||
</div>
|
||||
<div class="source-item">
|
||||
<strong>Linked body engineering report</strong>
|
||||
<div class="helper">Internal evidence artifact proposed for linking into the final dossier.</div>
|
||||
<div class="citation">The relevant static load summary should be attached before final sign-off.</div>
|
||||
</div>
|
||||
<div class="source-item">
|
||||
<strong>C-NCAP rulebook §3.1</strong>
|
||||
<div class="helper">Contextual safety framing only, not the basis of the compliance statement.</div>
|
||||
<div class="citation">Kept in thread for reviewer context but not recommended as the lead citation.</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<span>T-Systems Regulation</span>
|
||||
<div class="footer-status">
|
||||
<span>Desktop Web</span>
|
||||
<span class="footer-dot" aria-hidden="true"></span>
|
||||
<span>Online</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<script src="ui-preferences.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
582
Prototype/cc29bcb0-df2d-4d50-9428-7caa406ecb29/upload-modal.html
Normal file
582
Prototype/cc29bcb0-df2d-4d50-9428-7caa406ecb29/upload-modal.html
Normal file
@@ -0,0 +1,582 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>AI + Compliance Hub - Upload Documents</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #fafafa;
|
||||
--surface: #ffffff;
|
||||
--surface-warm: var(--surface);
|
||||
--fg: #111111;
|
||||
--fg-2: var(--fg);
|
||||
--muted: #6b6b6b;
|
||||
--meta: var(--muted);
|
||||
--border: #e5e5e5;
|
||||
--border-soft: var(--border);
|
||||
--primary: #e20074;
|
||||
--accent: var(--primary);
|
||||
--accent-on: #ffffff;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), black 8%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 14%);
|
||||
--success: #17a34a;
|
||||
--warn: #eab308;
|
||||
--danger: #dc2626;
|
||||
--font-display: "TeleNeoWeb-Bold", "TeleNeoWeb-Medium", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-body: "TeleNeoWeb-Regular", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-mono: ui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospace;
|
||||
--text-xs: 12px;
|
||||
--text-sm: 14px;
|
||||
--text-base: 16px;
|
||||
--text-lg: 20px;
|
||||
--text-xl: 24px;
|
||||
--text-2xl: 32px;
|
||||
--text-3xl: 48px;
|
||||
--text-4xl: 64px;
|
||||
--leading-body: 1.5;
|
||||
--leading-tight: 1.2;
|
||||
--tracking-display: -0.01em;
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px;
|
||||
--space-5: 20px;
|
||||
--space-6: 24px;
|
||||
--space-8: 32px;
|
||||
--space-12: 48px;
|
||||
--space-20: 80px;
|
||||
--section-y-desktop: 80px;
|
||||
--section-y-tablet: 48px;
|
||||
--section-y-phone: 32px;
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 16px;
|
||||
--radius-pill: 9999px;
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised: 0 2px 8px color-mix(in oklab, var(--fg), transparent 92%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 70%);
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
--container-max: 1200px;
|
||||
--container-gutter-desktop: 24px;
|
||||
--container-gutter-tablet: 16px;
|
||||
--container-gutter-phone: 12px;
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme="light"]) {
|
||||
--bg: #0f1014;
|
||||
--surface: #17181d;
|
||||
--surface-warm: #1d1f26;
|
||||
--fg: #f5f7fb;
|
||||
--fg-2: #e5e8ef;
|
||||
--muted: #a2a9b8;
|
||||
--meta: #858d9c;
|
||||
--border: #2a2d35;
|
||||
--border-soft: #21242c;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), white 12%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 6%);
|
||||
--success: #22c55e;
|
||||
--warn: #facc15;
|
||||
--danger: #f87171;
|
||||
--elev-raised: 0 14px 36px color-mix(in oklab, black, transparent 74%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 56%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
|
||||
:root[data-theme="dark"] {
|
||||
--bg: #0f1014;
|
||||
--surface: #17181d;
|
||||
--surface-warm: #1d1f26;
|
||||
--fg: #f5f7fb;
|
||||
--fg-2: #e5e8ef;
|
||||
--muted: #a2a9b8;
|
||||
--meta: #858d9c;
|
||||
--border: #2a2d35;
|
||||
--border-soft: #21242c;
|
||||
--accent-hover: color-mix(in oklab, var(--accent), white 12%);
|
||||
--accent-active: color-mix(in oklab, var(--accent), black 6%);
|
||||
--success: #22c55e;
|
||||
--warn: #facc15;
|
||||
--danger: #f87171;
|
||||
--elev-raised: 0 14px 36px color-mix(in oklab, black, transparent 74%);
|
||||
--focus-ring: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 56%);
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
:root[data-theme="light"] {
|
||||
color-scheme: light;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background:
|
||||
linear-gradient(180deg, color-mix(in oklab, var(--bg), var(--fg) 4%), var(--bg)),
|
||||
var(--bg);
|
||||
color: var(--fg);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--leading-body);
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 24px;
|
||||
}
|
||||
h1, h2, h3 {
|
||||
margin: 0;
|
||||
font-family: var(--font-display);
|
||||
line-height: var(--leading-tight);
|
||||
letter-spacing: var(--tracking-display);
|
||||
}
|
||||
p { margin: 0; text-wrap: pretty; }
|
||||
button, input, select, textarea { font: inherit; }
|
||||
a { color: inherit; text-decoration: none; }
|
||||
a:hover { color: var(--fg); text-decoration: underline; }
|
||||
.od-theme-toggle {
|
||||
cursor: pointer;
|
||||
transition: background var(--motion-fast) var(--ease-standard), border-color var(--motion-fast) var(--ease-standard), color var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.od-theme-toggle:hover {
|
||||
color: var(--fg);
|
||||
border-color: var(--fg);
|
||||
text-decoration: none;
|
||||
}
|
||||
.od-theme-toggle:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
.footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
min-height: 36px;
|
||||
padding: 0 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
color: var(--muted);
|
||||
font-size: var(--text-xs);
|
||||
font-family: var(--font-mono);
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
background: color-mix(in oklab, var(--bg), var(--surface) 12%);
|
||||
}
|
||||
.footer-status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.footer-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #19d3a2;
|
||||
box-shadow: 0 0 0 4px color-mix(in oklab, #19d3a2, transparent 84%);
|
||||
}
|
||||
.frame {
|
||||
width: min(1160px, 100%);
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
}
|
||||
.topline {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
.back {
|
||||
color: var(--muted);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.modal {
|
||||
display: grid;
|
||||
grid-template-columns: 1.15fr 0.85fr;
|
||||
gap: 0;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
background: var(--surface);
|
||||
box-shadow: var(--elev-raised);
|
||||
}
|
||||
.panel {
|
||||
padding: 28px;
|
||||
min-width: 0;
|
||||
}
|
||||
.panel + .panel {
|
||||
border-left: 1px solid var(--border);
|
||||
background: color-mix(in oklab, var(--surface), var(--bg) 24%);
|
||||
}
|
||||
.eyebrow {
|
||||
color: var(--accent);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-xs);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.lead {
|
||||
color: var(--muted);
|
||||
margin-top: 12px;
|
||||
max-width: 56ch;
|
||||
}
|
||||
.dropzone {
|
||||
margin-top: 24px;
|
||||
border: 1px dashed color-mix(in oklab, var(--accent), white 54%);
|
||||
border-radius: var(--radius-lg);
|
||||
background: color-mix(in oklab, var(--accent), white 94%);
|
||||
padding: 30px;
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
min-height: 220px;
|
||||
align-content: center;
|
||||
justify-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
.drop-visual {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 18px;
|
||||
border: 1px solid color-mix(in oklab, var(--accent), white 48%);
|
||||
background: var(--surface);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: var(--accent);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.btn-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
.btn {
|
||||
min-height: 44px;
|
||||
padding: 0 16px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
transition: background var(--motion-fast) var(--ease-standard), border-color var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.btn:hover { border-color: var(--fg); }
|
||||
.btn-primary {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: var(--accent-on);
|
||||
}
|
||||
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
|
||||
.theme-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
min-height: 40px;
|
||||
padding: 0 12px;
|
||||
border-radius: var(--radius-pill);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
color: var(--muted);
|
||||
font-size: var(--text-sm);
|
||||
cursor: pointer;
|
||||
transition: background var(--motion-fast) var(--ease-standard), border-color var(--motion-fast) var(--ease-standard), color var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.theme-toggle:hover {
|
||||
color: var(--fg);
|
||||
border-color: var(--fg);
|
||||
text-decoration: none;
|
||||
}
|
||||
.theme-toggle:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
.btn:focus-visible,
|
||||
.field input:focus-visible,
|
||||
.field select:focus-visible,
|
||||
.field textarea:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
.selected-files {
|
||||
margin-top: 22px;
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
.file-row,
|
||||
.queue-row {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface);
|
||||
padding: 14px 16px;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
.file-top,
|
||||
.queue-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
.meta,
|
||||
.hint {
|
||||
color: var(--muted);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.mono {
|
||||
font-family: var(--font-mono);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
margin-top: 22px;
|
||||
}
|
||||
.field {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
.field label {
|
||||
color: var(--muted);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.field input,
|
||||
.field select,
|
||||
.field textarea {
|
||||
min-height: 44px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface);
|
||||
padding: 0 12px;
|
||||
color: var(--fg);
|
||||
}
|
||||
.field textarea {
|
||||
min-height: 104px;
|
||||
padding: 12px;
|
||||
resize: vertical;
|
||||
}
|
||||
.status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: fit-content;
|
||||
padding: 4px 10px;
|
||||
border-radius: var(--radius-pill);
|
||||
border: 1px solid var(--border);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--text-xs);
|
||||
}
|
||||
.status::before {
|
||||
content: "";
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 999px;
|
||||
background: currentColor;
|
||||
}
|
||||
.status.ok { color: var(--success); }
|
||||
.status.warn { color: color-mix(in oklab, var(--warn), black 24%); }
|
||||
.status.risk { color: var(--danger); }
|
||||
.progress {
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in oklab, var(--fg), transparent 95%);
|
||||
overflow: hidden;
|
||||
}
|
||||
.progress > span {
|
||||
display: block;
|
||||
height: 100%;
|
||||
background: color-mix(in oklab, var(--accent), white 26%);
|
||||
border-radius: inherit;
|
||||
}
|
||||
.queue {
|
||||
margin-top: 18px;
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
.summary {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
margin-top: 22px;
|
||||
}
|
||||
.summary-card {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: color-mix(in oklab, var(--surface), var(--bg) 12%);
|
||||
padding: 14px 16px;
|
||||
}
|
||||
@media (max-width: 980px) {
|
||||
.modal,
|
||||
.grid { grid-template-columns: 1fr; }
|
||||
.panel + .panel { border-left: 0; border-top: 1px solid var(--border); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body data-page="upload">
|
||||
<div class="frame">
|
||||
<div class="topline">
|
||||
<a class="back" href="document-management.html">← Back to document management</a>
|
||||
<span class="hint">Modal review surface for intake, metadata, and queue behavior</span>
|
||||
</div>
|
||||
|
||||
<main class="modal">
|
||||
<section class="panel" data-od-id="upload-form">
|
||||
<div class="eyebrow">Upload documents</div>
|
||||
<h1>Stage files for parsing and indexing.</h1>
|
||||
<p class="lead">This intake flow supports PDF, DOCX, and supplier evidence bundles. Metadata is captured up front so the downstream parser and retrieval pipeline stay normalized.</p>
|
||||
|
||||
<div class="dropzone">
|
||||
<div class="drop-visual">PDF</div>
|
||||
<h2 style="font-size:28px;">Drop files here or browse your local archive.</h2>
|
||||
<p class="hint">Recommended per batch: up to 20 files, 200 MB combined, one regulation family per batch.</p>
|
||||
<div class="btn-row">
|
||||
<button class="btn btn-primary">Choose files</button>
|
||||
<button class="btn">Paste object storage link</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="selected-files">
|
||||
<div class="file-row">
|
||||
<div class="file-top">
|
||||
<div>
|
||||
<strong>GBT31484_revision_notes.pdf</strong>
|
||||
<div class="meta mono">size pending validation · scanned appendix included</div>
|
||||
</div>
|
||||
<span class="status ok">Ready</span>
|
||||
</div>
|
||||
<div class="progress"><span style="width:100%"></span></div>
|
||||
</div>
|
||||
<div class="file-row">
|
||||
<div class="file-top">
|
||||
<div>
|
||||
<strong>supplier_thermal_test_report.docx</strong>
|
||||
<div class="meta mono">size pending validation · confidential evidence supplement</div>
|
||||
</div>
|
||||
<span class="status warn">Metadata</span>
|
||||
</div>
|
||||
<div class="progress"><span style="width:58%"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<div class="field">
|
||||
<label for="regulation-type">Regulation type</label>
|
||||
<select id="regulation-type">
|
||||
<option selected>Battery safety</option>
|
||||
<option>Vehicle safety</option>
|
||||
<option>Cybersecurity</option>
|
||||
<option>Charging</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="version">Version / release</label>
|
||||
<input id="version" type="text" value="current revision" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="owner">Owning team</label>
|
||||
<select id="owner">
|
||||
<option selected>Battery Safety Team</option>
|
||||
<option>Connected Fleet</option>
|
||||
<option>Homologation Office</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="parser">Parser backend</label>
|
||||
<select id="parser">
|
||||
<option selected>Aliyun parser</option>
|
||||
<option>Legacy local parser</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field" style="grid-column:1 / -1;">
|
||||
<label for="notes">Reviewer note</label>
|
||||
<textarea id="notes">Re-run this batch against the latest battery energy density requirements and keep supplier evidence attached to the same review thread.</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-row" style="margin-top:24px;">
|
||||
<button class="btn">Save draft batch</button>
|
||||
<button class="btn btn-primary">Start import queue</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<aside class="panel" data-od-id="upload-queue">
|
||||
<div class="eyebrow">Import queue</div>
|
||||
<h2 style="font-size:28px;">Batch progress and validation feedback</h2>
|
||||
<p class="lead" style="font-size:16px;">Reviewers can monitor preflight checks before the parser job is submitted, then watch queue depth and failure mode without leaving the modal.</p>
|
||||
|
||||
<div class="summary">
|
||||
<div class="summary-card">
|
||||
<strong class="mono">2 files validated</strong>
|
||||
<div class="hint">Files already validated for metadata completeness</div>
|
||||
</div>
|
||||
<div class="summary-card">
|
||||
<strong class="mono">live queue</strong>
|
||||
<div class="hint">Current Aliyun parser queue depth populates here</div>
|
||||
</div>
|
||||
<div class="summary-card">
|
||||
<strong class="mono">text-embedding-v3</strong>
|
||||
<div class="hint">Embedding target applied after semantic block extraction</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="queue">
|
||||
<div class="queue-row">
|
||||
<div class="queue-top">
|
||||
<div>
|
||||
<strong>Preflight validation</strong>
|
||||
<div class="meta">Duplicate ID scan, file-type validation, metadata completeness</div>
|
||||
</div>
|
||||
<span class="status ok">Passed</span>
|
||||
</div>
|
||||
<div class="progress"><span style="width:100%"></span></div>
|
||||
</div>
|
||||
|
||||
<div class="queue-row">
|
||||
<div class="queue-top">
|
||||
<div>
|
||||
<strong>Object storage upload</strong>
|
||||
<div class="meta">Bucket `upload-files` · transient object names attached to batch</div>
|
||||
</div>
|
||||
<span class="status ok">Completed</span>
|
||||
</div>
|
||||
<div class="progress"><span style="width:100%"></span></div>
|
||||
</div>
|
||||
|
||||
<div class="queue-row">
|
||||
<div class="queue-top">
|
||||
<div>
|
||||
<strong>Aliyun parse submission</strong>
|
||||
<div class="meta">Polling every <span class="mono">5s</span> · timeout <span class="mono">900s</span></div>
|
||||
</div>
|
||||
<span class="status warn">Queued</span>
|
||||
</div>
|
||||
<div class="progress"><span style="width:42%"></span></div>
|
||||
</div>
|
||||
|
||||
<div class="queue-row">
|
||||
<div class="queue-top">
|
||||
<div>
|
||||
<strong>Chunking + embedding</strong>
|
||||
<div class="meta">Build semantic blocks, overlapping vector chunks, then 1024-d embeddings</div>
|
||||
</div>
|
||||
<span class="status warn">Waiting</span>
|
||||
</div>
|
||||
<div class="progress"><span style="width:12%"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="summary-card" style="margin-top:18px;">
|
||||
<strong>Potential issue to resolve</strong>
|
||||
<p class="hint" style="margin-top:8px;">`supplier_thermal_test_report.docx` lacks a formal standard number. The batch can continue, but retrieval quality improves if you assign an evidence relationship before submit.</p>
|
||||
</div>
|
||||
</aside>
|
||||
</main>
|
||||
</div>
|
||||
<script src="ui-preferences.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
709
Prototype/dashboard-sidebar.html
Normal file
709
Prototype/dashboard-sidebar.html
Normal file
@@ -0,0 +1,709 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>System Status — T-Systems Regulation Hub</title>
|
||||
<style>
|
||||
/* ─── Design tokens ─────────────────────────────────────────────── */
|
||||
:root {
|
||||
/* Sidebar (light rail) */
|
||||
--rail-bg: #ffffff;
|
||||
--rail-surface: #f7f8fa;
|
||||
--rail-fg: #111827;
|
||||
--rail-muted: #8b929e;
|
||||
--rail-border: #e8eaed;
|
||||
--rail-hover: rgba(0,0,0,.04);
|
||||
--rail-active: rgba(226,0,116,.07);
|
||||
|
||||
/* Canvas (content area) */
|
||||
--bg: #f2f4f7;
|
||||
--surface: #ffffff;
|
||||
--fg: #111827;
|
||||
--muted: #6b7280;
|
||||
--border: #e5e7eb;
|
||||
|
||||
/* Brand */
|
||||
--accent: #e20074;
|
||||
--accent-dim: rgba(226,0,116,.10);
|
||||
--accent-hover: #c8006a;
|
||||
--success: #16a34a;
|
||||
--warn: #d97706;
|
||||
--danger: #dc2626;
|
||||
|
||||
/* Type */
|
||||
--font-display: "TeleNeoWeb-Bold","Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
||||
--font-body: "TeleNeoWeb-Regular","Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
||||
--font-mono: ui-monospace,"JetBrains Mono",Menlo,monospace;
|
||||
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 10px;
|
||||
--radius-pill: 9999px;
|
||||
--sidebar-w: 232px;
|
||||
--shadow-card: 0 1px 4px rgba(0,0,0,.06), 0 0 0 1px rgba(0,0,0,.04);
|
||||
--shadow-sm: 0 1px 2px rgba(0,0,0,.05);
|
||||
}
|
||||
|
||||
/* ─── Reset ────────────────────────────────────────────────────── */
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; }
|
||||
html, body { height: 100%; }
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family: var(--font-body);
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
h1,h2,h3,h4 { font-family: var(--font-display); line-height: 1.2; letter-spacing: -0.015em; text-wrap: balance; }
|
||||
a { color: inherit; text-decoration: none; }
|
||||
button, input, select { font: inherit; cursor: pointer; }
|
||||
|
||||
/* ─── App shell ─────────────────────────────────────────────────── */
|
||||
.app-shell { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }
|
||||
|
||||
/* ─── Sidebar ───────────────────────────────────────────────────── */
|
||||
.sidebar {
|
||||
position: sticky; top: 0; height: 100vh;
|
||||
overflow-y: auto; overflow-x: hidden;
|
||||
display: flex; flex-direction: column;
|
||||
background: var(--rail-bg);
|
||||
border-right: 1px solid var(--rail-border);
|
||||
z-index: 20;
|
||||
}
|
||||
.sidebar-brand {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
height: 54px; padding: 0 16px;
|
||||
border-bottom: 1px solid var(--rail-border);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.brand-logo {
|
||||
width: 28px; height: 28px;
|
||||
background: var(--accent);
|
||||
border-radius: 7px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.brand-logo svg { color: #fff; }
|
||||
.brand-name { font-family: var(--font-display); font-size: 13px; color: var(--rail-fg); line-height: 1.2; font-weight: 700; }
|
||||
.brand-sub { font-size: 10px; color: var(--rail-muted); font-family: var(--font-mono); letter-spacing: .04em; margin-top: 2px; }
|
||||
|
||||
.sidebar-nav { flex: 1; padding: 10px 0; }
|
||||
.nav-group { padding: 0 8px 4px; }
|
||||
.nav-group + .nav-group { margin-top: 8px; padding-top: 10px; border-top: 1px solid var(--rail-border); }
|
||||
.nav-group-label {
|
||||
display: block;
|
||||
font-family: var(--font-mono); font-size: 10px;
|
||||
text-transform: uppercase; letter-spacing: .12em;
|
||||
color: var(--rail-muted);
|
||||
padding: 0 8px 6px;
|
||||
}
|
||||
.nav-item {
|
||||
display: flex; align-items: center; gap: 9px;
|
||||
height: 34px; padding: 0 8px;
|
||||
border-radius: var(--radius-sm);
|
||||
color: #4b5563;
|
||||
font-size: 13px;
|
||||
transition: background 120ms, color 120ms;
|
||||
position: relative;
|
||||
}
|
||||
.nav-item:hover { background: var(--rail-hover); color: var(--rail-fg); text-decoration: none; }
|
||||
.nav-item.active {
|
||||
background: var(--rail-active);
|
||||
color: var(--accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
.nav-item.active::before {
|
||||
content: "";
|
||||
position: absolute; left: 0; top: 6px; bottom: 6px;
|
||||
width: 3px; border-radius: 0 3px 3px 0;
|
||||
background: var(--accent);
|
||||
}
|
||||
.nav-icon { width: 15px; height: 15px; flex-shrink: 0; opacity: .55; }
|
||||
.nav-item:hover .nav-icon,
|
||||
.nav-item.active .nav-icon { opacity: 1; }
|
||||
.nav-badge {
|
||||
margin-left: auto;
|
||||
min-width: 18px; height: 17px; padding: 0 5px;
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--accent-dim); color: var(--accent);
|
||||
font-size: 10px; font-family: var(--font-mono); font-weight: 700;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
|
||||
.sidebar-footer {
|
||||
border-top: 1px solid var(--rail-border);
|
||||
padding: 10px 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar-user {
|
||||
display: flex; align-items: center; gap: 9px;
|
||||
padding: 8px; border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
transition: background 120ms;
|
||||
}
|
||||
.sidebar-user:hover { background: var(--rail-hover); }
|
||||
.avatar {
|
||||
width: 28px; height: 28px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent); color: #fff;
|
||||
font-size: 11px; font-weight: 700;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.user-name { font-size: 12px; font-weight: 600; color: var(--rail-fg); }
|
||||
.user-role { font-size: 10px; color: var(--rail-muted); font-family: var(--font-mono); margin-top: 1px; }
|
||||
.sidebar-action {
|
||||
display: flex; align-items: center; gap: 9px;
|
||||
height: 32px; padding: 0 8px;
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--rail-muted); font-size: 12px;
|
||||
border: none; background: transparent; width: 100%;
|
||||
text-align: left;
|
||||
transition: background 120ms, color 120ms;
|
||||
}
|
||||
.sidebar-action:hover { background: var(--rail-hover); color: var(--rail-fg); }
|
||||
|
||||
/* ─── Content area ──────────────────────────────────────────────── */
|
||||
.content-area { display: flex; flex-direction: column; min-width: 0; min-height: 100vh; }
|
||||
|
||||
.topbar {
|
||||
position: sticky; top: 0; z-index: 10;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
height: 54px; padding: 0 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: rgba(242,244,247,.9);
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.topbar-title { font-weight: 600; font-size: 14px; flex: 1; color: var(--fg); }
|
||||
.search {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
border: 1px solid var(--border); border-radius: var(--radius-sm);
|
||||
background: var(--surface);
|
||||
padding: 0 10px; height: 32px; width: 240px;
|
||||
transition: border-color 140ms;
|
||||
}
|
||||
.search:focus-within { border-color: color-mix(in oklab, var(--accent), transparent 60%); }
|
||||
.search input { border: 0; outline: none; background: transparent; width: 100%; color: var(--fg); font-size: 13px; }
|
||||
.search input::placeholder { color: var(--muted); }
|
||||
|
||||
.btn {
|
||||
height: 32px; border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
padding: 0 12px;
|
||||
background: var(--surface); color: var(--fg);
|
||||
font-size: 13px; font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background 120ms, border-color 120ms;
|
||||
white-space: nowrap;
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
}
|
||||
.btn:hover { background: var(--bg); border-color: #b0b7c0; }
|
||||
.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
|
||||
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
|
||||
|
||||
/* ─── Page ──────────────────────────────────────────────────────── */
|
||||
.page { padding: 20px; display: grid; gap: 18px; flex: 1; }
|
||||
|
||||
.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; }
|
||||
.eyebrow {
|
||||
font-family: var(--font-mono); font-size: 10px;
|
||||
text-transform: uppercase; letter-spacing: .1em;
|
||||
color: var(--accent); margin-bottom: 6px;
|
||||
}
|
||||
.page-head h1 { font-size: clamp(20px, 2.4vw, 28px); }
|
||||
.page-head-desc { margin-top: 4px; font-size: 13px; color: var(--muted); max-width: 520px; line-height: 1.55; }
|
||||
|
||||
/* ─── Cards ─────────────────────────────────────────────────────── */
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-card);
|
||||
padding: 16px 18px;
|
||||
}
|
||||
|
||||
/* ─── Stats row ─────────────────────────────────────────────────── */
|
||||
.stats-grid { display: grid; grid-template-columns: repeat(4,1fr); gap: 12px; }
|
||||
.stat-card {
|
||||
border-top: 2px solid var(--border);
|
||||
transition: border-color 200ms;
|
||||
}
|
||||
.stat-card:hover { border-top-color: var(--accent); }
|
||||
.stat-card .s-label {
|
||||
font-family: var(--font-mono); font-size: 10px;
|
||||
text-transform: uppercase; letter-spacing: .1em; color: var(--muted);
|
||||
}
|
||||
.stat-card .s-value {
|
||||
margin-top: 10px;
|
||||
font-size: 32px; line-height: 1;
|
||||
font-family: var(--font-display);
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: -0.02em;
|
||||
color: var(--fg);
|
||||
}
|
||||
.stat-card .s-sub { margin-top: 8px; font-size: 11px; color: var(--muted); line-height: 1.5; }
|
||||
|
||||
/* ─── Two-column panel grid ─────────────────────────────────────── */
|
||||
.panel-grid { display: grid; grid-template-columns: 1.4fr 0.9fr; gap: 18px; }
|
||||
.stack { display: grid; gap: 18px; }
|
||||
|
||||
/* ─── Section heads ─────────────────────────────────────────────── */
|
||||
.section-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
|
||||
.section-head h2 { font-size: 16px; }
|
||||
.ghost-link {
|
||||
color: var(--muted); font-size: 12px;
|
||||
padding: 2px 0; border-radius: 4px;
|
||||
transition: color 120ms;
|
||||
}
|
||||
.ghost-link:hover { color: var(--fg); text-decoration: none; }
|
||||
|
||||
/* ─── Task rows ─────────────────────────────────────────────────── */
|
||||
.task-list, .program-list, .event-list { display: grid; gap: 8px; }
|
||||
.task-row, .program-row, .event-row {
|
||||
display: grid; gap: 10px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 10px 12px;
|
||||
background: var(--surface);
|
||||
transition: border-color 120ms, box-shadow 120ms;
|
||||
}
|
||||
.task-row:hover, .program-row:hover {
|
||||
border-color: #c8cdd8;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
.task-row { grid-template-columns: 1.6fr 0.8fr 0.8fr 0.6fr; align-items: center; }
|
||||
.program-row{ grid-template-columns: 1fr auto; align-items: start; }
|
||||
.event-row { grid-template-columns: 76px 1fr; align-items: start; }
|
||||
|
||||
/* ─── KPI strip ─────────────────────────────────────────────────── */
|
||||
.kpi-strip { display: grid; grid-template-columns: repeat(3,1fr); gap: 8px; margin-top: 12px; }
|
||||
.kpi {
|
||||
border: 1px solid var(--border); border-radius: var(--radius-sm);
|
||||
padding: 10px 12px; background: #f8f9fb;
|
||||
}
|
||||
.kpi strong { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 17px; color: var(--fg); }
|
||||
.kpi-label { font-size: 11px; color: var(--muted); margin-bottom: 4px; }
|
||||
.meter { height: 3px; border-radius: 999px; background: #e5e7eb; overflow: hidden; margin-top: 8px; }
|
||||
.meter > span { display: block; height: 100%; background: var(--accent); border-radius: inherit; }
|
||||
|
||||
/* ─── Dark mode ────────────────────────────────────────────────── */
|
||||
[data-theme="dark"] {
|
||||
--rail-bg: #1a1c22;
|
||||
--rail-surface: #22242c;
|
||||
--rail-fg: #f0f2f5;
|
||||
--rail-muted: #7a8390;
|
||||
--rail-border: #2d3038;
|
||||
--rail-hover: rgba(255,255,255,.05);
|
||||
--rail-active: rgba(226,0,116,.12);
|
||||
--bg: #111318;
|
||||
--surface: #1a1c22;
|
||||
--fg: #f0f2f5;
|
||||
--muted: #7a8390;
|
||||
--border: #2d3038;
|
||||
}
|
||||
[data-theme="dark"] body { color-scheme: dark; }
|
||||
[data-theme="dark"] .topbar { background: rgba(17,19,24,.9); }
|
||||
[data-theme="dark"] .kpi { background: #1e2028; }
|
||||
[data-theme="dark"] .task-row, [data-theme="dark"] .program-row, [data-theme="dark"] .event-row { background: #1e2028; }
|
||||
|
||||
/* ─── Status pills ──────────────────────────────────────────────── */
|
||||
.status {
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
padding: 2px 8px; border-radius: var(--radius-pill);
|
||||
font-size: 11px; font-family: var(--font-mono); font-weight: 600;
|
||||
width: fit-content; white-space: nowrap;
|
||||
}
|
||||
.status::before {
|
||||
content: ""; width: 5px; height: 5px;
|
||||
border-radius: 50%; background: currentColor; flex-shrink: 0;
|
||||
}
|
||||
.status.ok { color: var(--success); background: color-mix(in oklab,var(--success),transparent 90%); }
|
||||
.status.warn { color: var(--warn); background: color-mix(in oklab,var(--warn),transparent 90%); }
|
||||
.status.risk { color: var(--danger); background: color-mix(in oklab,var(--danger),transparent 90%); }
|
||||
|
||||
/* ─── Pill (version tag) ────────────────────────────────────────── */
|
||||
.pill {
|
||||
display: inline-flex; align-items: center;
|
||||
height: 20px; padding: 0 8px;
|
||||
border-radius: var(--radius-pill);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--muted); font-size: 10px;
|
||||
font-family: var(--font-mono); font-weight: 500;
|
||||
}
|
||||
|
||||
/* ─── Task CTA buttons ──────────────────────────────────────────── */
|
||||
.task-cta {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
height: 26px; padding: 0 10px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: transparent; color: var(--muted);
|
||||
font-size: 11px; font-family: var(--font-body);
|
||||
white-space: nowrap; cursor: pointer;
|
||||
transition: border-color 120ms, color 120ms, background 120ms;
|
||||
}
|
||||
.task-cta:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-dim); text-decoration: none; }
|
||||
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 12px; }
|
||||
.note { font-size: 12px; color: var(--muted); line-height: 1.5; }
|
||||
|
||||
/* ─── Footer ────────────────────────────────────────────────────── */
|
||||
.footer {
|
||||
display: flex; align-items: center; justify-content: space-between; gap: 16px;
|
||||
min-height: 34px; padding: 0 20px;
|
||||
border-top: 1px solid var(--border);
|
||||
color: var(--muted);
|
||||
font-size: 10px; font-family: var(--font-mono);
|
||||
letter-spacing: .1em; text-transform: uppercase;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.footer-live { display: inline-flex; align-items: center; gap: 7px; }
|
||||
.footer-dot {
|
||||
width: 6px; height: 6px; border-radius: 50%;
|
||||
background: #22c55e;
|
||||
box-shadow: 0 0 0 3px rgba(34,197,94,.2);
|
||||
}
|
||||
|
||||
/* ─── Responsive ────────────────────────────────────────────────── */
|
||||
@media (max-width: 1200px) {
|
||||
.stats-grid { grid-template-columns: repeat(2,1fr); }
|
||||
.panel-grid { grid-template-columns: 1fr; }
|
||||
.kpi-strip { grid-template-columns: 1fr 1fr; }
|
||||
.task-row { grid-template-columns: 1fr auto; }
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
.app-shell { grid-template-columns: 1fr; }
|
||||
.sidebar { display: none; }
|
||||
.stats-grid, .kpi-strip { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-shell">
|
||||
|
||||
<!-- ─── Sidebar ─────────────────────────────────────────────────────── -->
|
||||
<aside class="sidebar" aria-label="Primary navigation">
|
||||
<div class="sidebar-brand">
|
||||
<div class="brand-logo">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M2 3.5h12v1.5H2zm5.25 1.5h1.5v8h-1.5zm-3 2h2.25v1.5H4.25zm5.25 0h2.25v1.5H9.5zm-3 3h2.5v1.5H6.5z" fill="currentColor"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="brand-name">T-Systems</div>
|
||||
<div class="brand-sub">Regulation Hub</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="sidebar-nav" aria-label="Primary">
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">Main</span>
|
||||
<a class="nav-item" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/index.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M2 2h5v5H2zm7 0h5v5H9zM2 9h5v5H2zm7 0h5v5H9z" fill="currentColor" opacity=".6"/>
|
||||
</svg>
|
||||
Overview
|
||||
</a>
|
||||
<a class="nav-item" href="perception.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none">
|
||||
<circle cx="8" cy="8" r="2.5" fill="currentColor"/>
|
||||
<path d="M8 2.5C4.91 2.5 2.5 5.42 2.5 8S4.91 13.5 8 13.5 13.5 10.58 13.5 8 11.09 2.5 8 2.5zm0 9.5C5.52 12 3.5 10.24 3.5 8S5.52 4 8 4s4.5 1.76 4.5 4-2.02 4-4.5 4z" fill="currentColor" opacity=".45"/>
|
||||
</svg>
|
||||
Regulatory Signals
|
||||
<span class="nav-badge">6</span>
|
||||
</a>
|
||||
<a class="nav-item active" href="dashboard-sidebar.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M1.5 2.5h13v1H1.5zm0 3h13v1H1.5zm0 3h8v1h-8zm0 3h6v1h-6z" fill="currentColor"/>
|
||||
</svg>
|
||||
System Status
|
||||
<span class="nav-badge">3</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">Workbench</span>
|
||||
<a class="nav-item" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/document-management.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M3 1h7l3 3v11H3V1zm1 1v12h8V5h-3V2H4zm5 .5V4h1.5L9 1.5zM6 7h4v1H6zm0 2h4v1H6zm0 2h3v1H6z" fill="currentColor"/>
|
||||
</svg>
|
||||
Documents
|
||||
</a>
|
||||
<a class="nav-item" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/compliance-analysis.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M8 1l7 3-1 6a7 7 0 01-6 5A7 7 0 011 10L0 4l8-3zm0 1.2L1.3 4.8l.8 5.1A6 6 0 008 14.8a6 6 0 005.9-4.9l.8-5.1L8 2.2zM7.5 5h1v4.5h-1V5zm0 5.5h1v1h-1v-1z" fill="currentColor"/>
|
||||
</svg>
|
||||
Compliance Analysis
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">Chat</span>
|
||||
<a class="nav-item" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/regulation-chat.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M2 2h12a1 1 0 011 1v8a1 1 0 01-1 1H5l-3 2.5V3a1 1 0 011-1zm0 1v9.5L4.5 11H14V3H2zm2 2h8v1H4zm0 2h6v1H4z" fill="currentColor"/>
|
||||
</svg>
|
||||
Regulation Q&A
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="sidebar-footer">
|
||||
<div class="sidebar-user">
|
||||
<div class="avatar">TS</div>
|
||||
<div>
|
||||
<div class="user-name">T-Systems User</div>
|
||||
<div class="user-role">Compliance Analyst</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="sidebar-action" type="button" onclick="toggleTheme()">
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M8 3a5 5 0 100 10A5 5 0 008 3zM2 8a6 6 0 1112 0A6 6 0 012 8z" fill="currentColor"/>
|
||||
</svg>
|
||||
<span>Dark mode</span>
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- ─── Content ──────────────────────────────────────────────────────── -->
|
||||
<div class="content-area">
|
||||
<header class="topbar">
|
||||
<span class="topbar-title">System Status</span>
|
||||
<div class="search">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M6.5 1a5.5 5.5 0 014.23 9.02l3.62 3.62-.7.71-3.63-3.63A5.5 5.5 0 116.5 1zm0 1a4.5 4.5 0 100 9 4.5 4.5 0 000-9z" fill="currentColor" opacity=".4"/>
|
||||
</svg>
|
||||
<input type="search" placeholder="Search regulations, documents…" aria-label="Search" />
|
||||
</div>
|
||||
<button class="btn">Export status</button>
|
||||
<a class="btn btn-primary" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/upload-modal.html">New upload</a>
|
||||
</header>
|
||||
|
||||
<main class="page">
|
||||
<!-- Page head -->
|
||||
<section class="page-head">
|
||||
<div>
|
||||
<div class="eyebrow">System Status</div>
|
||||
<h1>System Status</h1>
|
||||
<p class="page-head-desc">Ingestion pipeline, active compliance programs, and regulatory watch — all in one place.</p>
|
||||
</div>
|
||||
<span class="pill">v1.0.0</span>
|
||||
</section>
|
||||
|
||||
<!-- Stats -->
|
||||
<section class="stats-grid">
|
||||
<article class="card stat-card">
|
||||
<div class="s-label">Documents total</div>
|
||||
<div class="s-value mono" id="ds-docs">—</div>
|
||||
<div class="s-sub">Ingested into the knowledge base</div>
|
||||
</article>
|
||||
<article class="card stat-card">
|
||||
<div class="s-label">Vector chunks</div>
|
||||
<div class="s-value mono" id="ds-chunks">—</div>
|
||||
<div class="s-sub">regulations_dense_1024_v2 serving retrieval</div>
|
||||
</article>
|
||||
<article class="card stat-card">
|
||||
<div class="s-label">High-impact signals</div>
|
||||
<div class="s-value mono" id="ds-high">—</div>
|
||||
<div class="s-sub">Regulatory signals requiring immediate review</div>
|
||||
</article>
|
||||
<article class="card stat-card">
|
||||
<div class="s-label">Last 90 days</div>
|
||||
<div class="s-value mono" id="ds-90d">—</div>
|
||||
<div class="s-sub">Recent regulatory publications</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<!-- Two-column panels -->
|
||||
<section class="panel-grid">
|
||||
<!-- Left column -->
|
||||
<div class="stack">
|
||||
<article class="card">
|
||||
<div class="section-head">
|
||||
<h2>Workflow queue</h2>
|
||||
<a class="ghost-link" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/document-management.html">Open documents →</a>
|
||||
</div>
|
||||
<div class="task-list">
|
||||
<div class="task-row">
|
||||
<div>
|
||||
<strong>GB/T 31484-2015 battery density revision</strong>
|
||||
<div class="note">Uploaded by EV Safety Team · version 2026-04 addendum</div>
|
||||
</div>
|
||||
<span class="status warn">Embedding</span>
|
||||
<span class="mono">chunk build active</span>
|
||||
<a class="task-cta" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/document-detail.html">Inspect →</a>
|
||||
</div>
|
||||
<div class="task-row">
|
||||
<div>
|
||||
<strong>UNECE R155 annex interpretation note</strong>
|
||||
<div class="note">Parser artifacts ready · waiting for analyst assignment</div>
|
||||
</div>
|
||||
<span class="status ok">Ready</span>
|
||||
<span class="mono">19 clauses linked</span>
|
||||
<a class="task-cta" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/compliance-analysis.html">Analyze →</a>
|
||||
</div>
|
||||
<div class="task-row">
|
||||
<div>
|
||||
<strong>GB 26112-2010 roof strength scan</strong>
|
||||
<div class="note">OCR confidence dropped below threshold on 6 pages</div>
|
||||
</div>
|
||||
<span class="status risk">Failed</span>
|
||||
<span class="mono">Retry #2</span>
|
||||
<a class="task-cta" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/document-management.html">Resolve →</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="card">
|
||||
<div class="section-head">
|
||||
<h2>Active compliance programs</h2>
|
||||
<a class="ghost-link" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/compliance-analysis.html">Review findings →</a>
|
||||
</div>
|
||||
<div class="program-list">
|
||||
<div class="program-row">
|
||||
<div>
|
||||
<strong>Intelligent cockpit homologation</strong>
|
||||
<p class="note">42 related standards across driver monitoring, EMC, and child safety. Four findings still open for MY27 platform.</p>
|
||||
</div>
|
||||
<span class="status risk">High risk</span>
|
||||
</div>
|
||||
<div class="program-row">
|
||||
<div>
|
||||
<strong>Battery swap certification dossier</strong>
|
||||
<p class="note">Clause mapping complete. Thermal event test evidence package awaiting supplier document refresh.</p>
|
||||
</div>
|
||||
<span class="status warn">Pending</span>
|
||||
</div>
|
||||
<div class="program-row">
|
||||
<div>
|
||||
<strong>Connected fleet cybersecurity</strong>
|
||||
<p class="note">RAG checks aligned with UNECE R155. Chat follow-up requested on remote key rotation obligations.</p>
|
||||
</div>
|
||||
<span class="status ok">On track</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="kpi-strip">
|
||||
<div class="kpi">
|
||||
<div class="kpi-label">Retrieval hit rate</div>
|
||||
<strong>87%</strong>
|
||||
<div class="meter"><span style="width:87%"></span></div>
|
||||
</div>
|
||||
<div class="kpi">
|
||||
<div class="kpi-label">Evidence coverage</div>
|
||||
<strong>72%</strong>
|
||||
<div class="meter"><span style="width:72%"></span></div>
|
||||
</div>
|
||||
<div class="kpi">
|
||||
<div class="kpi-label">Reviewer SLA</div>
|
||||
<strong>18h</strong>
|
||||
<div class="meter"><span style="width:64%"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<!-- Right column -->
|
||||
<div class="stack">
|
||||
<article class="card">
|
||||
<div class="section-head"><h2>System health</h2><a class="ghost-link" href="#">Refresh</a></div>
|
||||
<div class="task-list">
|
||||
<div class="task-row" style="grid-template-columns:1fr auto">
|
||||
<div>
|
||||
<strong>Aliyun parser backend</strong>
|
||||
<div class="note">Poll interval 5 s · timeout 900 s</div>
|
||||
</div>
|
||||
<span class="status warn">Queue depth 7</span>
|
||||
</div>
|
||||
<div class="task-row" style="grid-template-columns:1fr auto">
|
||||
<div>
|
||||
<strong>Embedding model</strong>
|
||||
<div class="note">text-embedding-v3 · dimension 1024</div>
|
||||
</div>
|
||||
<span class="status ok">Healthy</span>
|
||||
</div>
|
||||
<div class="task-row" style="grid-template-columns:1fr auto">
|
||||
<div>
|
||||
<strong>Vector store</strong>
|
||||
<div class="note">Milvus regulations_dense_1024_v2</div>
|
||||
</div>
|
||||
<span class="status ok">Serving</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="card">
|
||||
<div class="section-head">
|
||||
<h2>Regulatory watch</h2>
|
||||
<a class="ghost-link" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/regulation-chat.html">Ask chat →</a>
|
||||
</div>
|
||||
<div class="event-list">
|
||||
<div class="event-row">
|
||||
<span class="mono note">2d ago</span>
|
||||
<div>
|
||||
<strong>GB 38031 thermal propagation draft updated</strong>
|
||||
<p class="note" style="-webkit-line-clamp:2;display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden">Potential impact on current battery enclosure narrative. Evidence gap flagged in two supplier submissions.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="event-row">
|
||||
<span class="mono note">5d ago</span>
|
||||
<div>
|
||||
<strong>UNECE R155 Q&A added note on incident response logs</strong>
|
||||
<p class="note" style="-webkit-line-clamp:2;display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden">Connected fleet program must confirm retention windows and ownership controls.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="event-row">
|
||||
<span class="mono note">12d ago</span>
|
||||
<div>
|
||||
<strong>GB/T 18487 charging interface interpretation circulated</strong>
|
||||
<p class="note" style="-webkit-line-clamp:2;display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden">No blocker yet, but three documents should be re-run against the new clause wording.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<span>T-Systems Regulation Hub</span>
|
||||
<div class="footer-live">
|
||||
<span class="footer-dot" aria-hidden="true"></span>
|
||||
<span>Online</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ─── Theme toggle (P0 fix: reads data-theme attribute) ───────────────
|
||||
function toggleTheme() {
|
||||
const html = document.documentElement;
|
||||
const next = html.dataset.theme === 'dark' ? 'light' : 'dark';
|
||||
html.dataset.theme = next;
|
||||
localStorage.setItem('theme', next);
|
||||
const btn = document.querySelector('.sidebar-action');
|
||||
if (btn) btn.querySelector('span') && (btn.querySelector('span').textContent = next === 'dark' ? 'Light mode' : 'Dark mode');
|
||||
}
|
||||
// Restore saved theme
|
||||
(function() {
|
||||
const saved = localStorage.getItem('theme');
|
||||
if (saved) document.documentElement.dataset.theme = saved;
|
||||
})();
|
||||
|
||||
// ─── Live stats from perception API ──────────────────────────────────
|
||||
async function loadDashboardStats() {
|
||||
try {
|
||||
const r = await fetch('http://6.86.80.9:5173/api/v1/perception/stats');
|
||||
if (!r.ok) return;
|
||||
const s = await r.json();
|
||||
const set = (id, val) => { const el = document.getElementById(id); if (el && val != null) el.textContent = val; };
|
||||
set('ds-high', s.high_impact);
|
||||
set('ds-90d', s.recent_90d);
|
||||
set('ds-docs', s.total);
|
||||
} catch(e) { /* silent — fallback to — */ }
|
||||
}
|
||||
loadDashboardStats();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
11
Prototype/index.html
Normal file
11
Prototype/index.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="refresh" content="0; url=dashboard-sidebar.html" />
|
||||
<title>TSI Regulation Hub</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>location.replace("dashboard-sidebar.html");</script>
|
||||
</body>
|
||||
</html>
|
||||
915
Prototype/perception.html
Normal file
915
Prototype/perception.html
Normal file
@@ -0,0 +1,915 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Regulatory Signals — T-Systems Regulation Hub</title>
|
||||
<style>
|
||||
/* ─── Design tokens (identical to dashboard-sidebar.html) ────────── */
|
||||
:root {
|
||||
--rail-bg: #ffffff;
|
||||
--rail-surface: #f7f8fa;
|
||||
--rail-fg: #111827;
|
||||
--rail-muted: #8b929e;
|
||||
--rail-border: #e8eaed;
|
||||
--rail-hover: rgba(0,0,0,.04);
|
||||
--rail-active: rgba(226,0,116,.07);
|
||||
|
||||
--bg: #f2f4f7;
|
||||
--surface: #ffffff;
|
||||
--fg: #111827;
|
||||
--muted: #6b7280;
|
||||
--border: #e5e7eb;
|
||||
|
||||
--accent: #e20074;
|
||||
--accent-dim: rgba(226,0,116,.10);
|
||||
--accent-hover: #c8006a;
|
||||
--success: #16a34a;
|
||||
--warn: #d97706;
|
||||
--danger: #dc2626;
|
||||
|
||||
--font-display: "TeleNeoWeb-Bold","Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
||||
--font-body: "TeleNeoWeb-Regular","Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
||||
--font-mono: ui-monospace,"JetBrains Mono",Menlo,monospace;
|
||||
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 10px;
|
||||
--radius-pill: 9999px;
|
||||
--sidebar-w: 232px;
|
||||
--shadow-card: 0 1px 4px rgba(0,0,0,.06), 0 0 0 1px rgba(0,0,0,.04);
|
||||
--shadow-sm: 0 1px 2px rgba(0,0,0,.05);
|
||||
}
|
||||
|
||||
/* ─── Reset ─────────────────────────────────────────────────────── */
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; }
|
||||
html, body { height: 100%; }
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family: var(--font-body);
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
h1,h2,h3,h4 { font-family: var(--font-display); line-height: 1.2; letter-spacing: -0.015em; }
|
||||
a { color: inherit; text-decoration: none; }
|
||||
button, input, select { font: inherit; cursor: pointer; }
|
||||
|
||||
/* ─── App shell ──────────────────────────────────────────────────── */
|
||||
.app-shell { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }
|
||||
|
||||
/* ─── Sidebar (light rail — identical to dashboard) ───────────────── */
|
||||
.sidebar {
|
||||
position: sticky; top: 0; height: 100vh;
|
||||
overflow-y: auto; overflow-x: hidden;
|
||||
display: flex; flex-direction: column;
|
||||
background: var(--rail-bg);
|
||||
border-right: 1px solid var(--rail-border);
|
||||
z-index: 20;
|
||||
}
|
||||
.sidebar-brand {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
height: 54px; padding: 0 16px;
|
||||
border-bottom: 1px solid var(--rail-border);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.brand-logo {
|
||||
width: 28px; height: 28px;
|
||||
background: var(--accent);
|
||||
border-radius: 7px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.brand-logo svg { color: #fff; }
|
||||
.brand-name { font-family: var(--font-display); font-size: 13px; color: var(--rail-fg); line-height: 1.2; font-weight: 700; }
|
||||
.brand-sub { font-size: 10px; color: var(--rail-muted); font-family: var(--font-mono); letter-spacing: .04em; margin-top: 2px; }
|
||||
|
||||
.sidebar-nav { flex: 1; padding: 10px 0; }
|
||||
.nav-group { padding: 0 8px 4px; }
|
||||
.nav-group + .nav-group { margin-top: 8px; padding-top: 10px; border-top: 1px solid var(--rail-border); }
|
||||
.nav-group-label {
|
||||
display: block;
|
||||
font-family: var(--font-mono); font-size: 10px;
|
||||
text-transform: uppercase; letter-spacing: .12em;
|
||||
color: var(--rail-muted);
|
||||
padding: 0 8px 6px;
|
||||
}
|
||||
.nav-item {
|
||||
display: flex; align-items: center; gap: 9px;
|
||||
height: 34px; padding: 0 8px;
|
||||
border-radius: var(--radius-sm);
|
||||
color: #4b5563;
|
||||
font-size: 13px;
|
||||
transition: background 120ms, color 120ms;
|
||||
position: relative;
|
||||
}
|
||||
.nav-item:hover { background: var(--rail-hover); color: var(--rail-fg); text-decoration: none; }
|
||||
.nav-item.active { background: var(--rail-active); color: var(--accent); font-weight: 600; }
|
||||
.nav-item.active::before {
|
||||
content: "";
|
||||
position: absolute; left: 0; top: 6px; bottom: 6px;
|
||||
width: 3px; border-radius: 0 3px 3px 0;
|
||||
background: var(--accent);
|
||||
}
|
||||
.nav-icon { width: 15px; height: 15px; flex-shrink: 0; opacity: .55; }
|
||||
.nav-item:hover .nav-icon,
|
||||
.nav-item.active .nav-icon { opacity: 1; }
|
||||
.nav-badge {
|
||||
margin-left: auto;
|
||||
min-width: 18px; height: 17px; padding: 0 5px;
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--accent-dim); color: var(--accent);
|
||||
font-size: 10px; font-family: var(--font-mono); font-weight: 700;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
|
||||
.sidebar-footer {
|
||||
border-top: 1px solid var(--rail-border);
|
||||
padding: 10px 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar-user {
|
||||
display: flex; align-items: center; gap: 9px;
|
||||
padding: 8px; border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
transition: background 120ms;
|
||||
}
|
||||
.sidebar-user:hover { background: var(--rail-hover); }
|
||||
.avatar {
|
||||
width: 28px; height: 28px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent); color: #fff;
|
||||
font-size: 11px; font-weight: 700;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.user-name { font-size: 12px; font-weight: 600; color: var(--rail-fg); }
|
||||
.user-role { font-size: 10px; color: var(--rail-muted); font-family: var(--font-mono); margin-top: 1px; }
|
||||
|
||||
/* ─── Content area ───────────────────────────────────────────────── */
|
||||
.content-area { display: flex; flex-direction: column; min-width: 0; min-height: 100vh; }
|
||||
|
||||
.topbar {
|
||||
position: sticky; top: 0; z-index: 10;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
height: 54px; padding: 0 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: rgba(242,244,247,.9);
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.topbar-title { font-weight: 600; font-size: 14px; }
|
||||
.topbar-sub { color: var(--muted); font-family: var(--font-mono); font-size: 10px; margin-left: 4px; font-weight: 400; }
|
||||
|
||||
.btn {
|
||||
height: 32px; border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
padding: 0 12px;
|
||||
background: var(--surface); color: var(--fg);
|
||||
font-size: 13px; font-weight: 500;
|
||||
transition: background 120ms, border-color 120ms;
|
||||
white-space: nowrap;
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
}
|
||||
.btn:hover { background: var(--bg); border-color: #b0b7c0; }
|
||||
.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
|
||||
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
|
||||
.btn-primary:disabled { opacity: .45; cursor: not-allowed; }
|
||||
.btn-sm { height: 28px; padding: 0 10px; font-size: 12px; }
|
||||
|
||||
.search {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
border: 1px solid var(--border); border-radius: var(--radius-sm);
|
||||
background: var(--surface);
|
||||
padding: 0 10px; height: 32px; width: 220px;
|
||||
transition: border-color 140ms;
|
||||
}
|
||||
.search:focus-within { border-color: color-mix(in oklab, var(--accent), transparent 60%); }
|
||||
.search input { border: 0; outline: none; background: transparent; width: 100%; color: var(--fg); font-size: 13px; }
|
||||
.search input::placeholder { color: var(--muted); }
|
||||
|
||||
/* ─── Stats bar ──────────────────────────────────────────────────── */
|
||||
.stats-bar {
|
||||
display: grid; grid-template-columns: repeat(4,1fr);
|
||||
gap: 1px;
|
||||
background: var(--border);
|
||||
border-bottom: 1px solid var(--border);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.stat-cell {
|
||||
background: var(--surface);
|
||||
padding: 14px 20px;
|
||||
}
|
||||
.stat-cell .s-label {
|
||||
font-family: var(--font-mono); font-size: 10px;
|
||||
text-transform: uppercase; letter-spacing: .1em;
|
||||
color: var(--muted);
|
||||
}
|
||||
.stat-cell .s-value {
|
||||
margin-top: 6px;
|
||||
font-size: 28px; line-height: 1;
|
||||
font-family: var(--font-display);
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
.stat-cell .s-sub { margin-top: 4px; font-size: 11px; color: var(--muted); }
|
||||
|
||||
/* ─── Work area (filter bar + split) ────────────────────────────── */
|
||||
.work-area { flex: 1; display: flex; flex-direction: column; min-height: 0; }
|
||||
|
||||
.filter-bar {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
padding: 10px 20px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
flex-shrink: 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.filter-label {
|
||||
font-family: var(--font-mono); font-size: 10px;
|
||||
text-transform: uppercase; letter-spacing: .08em;
|
||||
color: var(--muted);
|
||||
}
|
||||
.chip {
|
||||
padding: 3px 10px;
|
||||
border-radius: var(--radius-pill);
|
||||
border: 1px solid var(--border);
|
||||
background: transparent; color: var(--muted);
|
||||
font-size: 11px; font-family: var(--font-mono);
|
||||
transition: all 100ms;
|
||||
}
|
||||
.chip:hover { border-color: var(--fg); color: var(--fg); }
|
||||
.chip.on { border-color: var(--accent); color: var(--accent); background: var(--accent-dim); font-weight: 600; }
|
||||
.sep { width: 1px; height: 16px; background: var(--border); flex-shrink: 0; margin: 0 2px; }
|
||||
|
||||
/* ─── Two-pane split ─────────────────────────────────────────────── */
|
||||
.perception-split {
|
||||
flex: 1; display: grid;
|
||||
grid-template-columns: 360px 1fr;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* ─── Feed pane ─────────────────────────────────────────────────── */
|
||||
.feed-pane {
|
||||
display: flex; flex-direction: column;
|
||||
border-right: 1px solid var(--border);
|
||||
min-height: 0;
|
||||
background: var(--bg);
|
||||
}
|
||||
.feed-pane-head {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 12px 16px 10px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.feed-pane-head h2 { font-size: 13px; font-weight: 700; }
|
||||
.feed-count { font-family: var(--font-mono); font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; }
|
||||
.feed-scroll {
|
||||
flex: 1; overflow-y: auto;
|
||||
padding: 10px 10px;
|
||||
display: flex; flex-direction: column; gap: 6px;
|
||||
}
|
||||
.feed-scroll::-webkit-scrollbar { width: 4px; }
|
||||
.feed-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
||||
|
||||
/* Event card */
|
||||
.ev-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 11px 13px;
|
||||
cursor: pointer;
|
||||
transition: border-color 100ms, box-shadow 100ms;
|
||||
}
|
||||
.ev-card:hover { border-color: #c4c9d4; }
|
||||
.ev-card.selected {
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px var(--accent-dim);
|
||||
}
|
||||
.ev-head { display: flex; align-items: center; gap: 6px; margin-bottom: 7px; }
|
||||
.src-tag {
|
||||
font-size: 10px; font-weight: 700; padding: 2px 6px;
|
||||
border-radius: 4px; font-family: var(--font-mono);
|
||||
}
|
||||
.std-code { font-family: var(--font-mono); font-size: 10px; color: var(--muted); }
|
||||
.ev-title { font-weight: 600; font-size: 12px; line-height: 1.4; margin-bottom: 4px; }
|
||||
.ev-summary {
|
||||
font-size: 11px; color: var(--muted); line-height: 1.5;
|
||||
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
|
||||
}
|
||||
.ev-foot { display: flex; align-items: center; gap: 6px; margin-top: 7px; }
|
||||
.ev-date { font-family: var(--font-mono); font-size: 10px; color: var(--muted); }
|
||||
.ev-tag {
|
||||
font-size: 10px; font-family: var(--font-mono);
|
||||
padding: 1px 5px; border-radius: 3px;
|
||||
border: 1px solid var(--border); color: var(--muted);
|
||||
}
|
||||
.imp-dot {
|
||||
margin-left: auto;
|
||||
font-size: 10px; font-family: var(--font-mono);
|
||||
}
|
||||
.loading-msg { font-family: var(--font-mono); font-size: 12px; color: var(--muted); text-align: center; padding: 40px 0; }
|
||||
|
||||
/* ─── Analysis pane ─────────────────────────────────────────────── */
|
||||
.analysis-pane {
|
||||
display: flex; flex-direction: column;
|
||||
min-height: 0; overflow-y: auto;
|
||||
padding: 16px 20px;
|
||||
gap: 12px;
|
||||
}
|
||||
.analysis-pane::-webkit-scrollbar { width: 4px; }
|
||||
.analysis-pane::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
||||
|
||||
.analysis-empty {
|
||||
flex: 1; display: flex; flex-direction: column;
|
||||
align-items: center; justify-content: center;
|
||||
gap: 10px;
|
||||
text-align: center;
|
||||
color: var(--muted);
|
||||
min-height: 300px;
|
||||
}
|
||||
.analysis-empty-ring {
|
||||
width: 40px; height: 40px; border-radius: 50%;
|
||||
border: 1.5px solid var(--border);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.analysis-empty-label { font-size: 13px; }
|
||||
.analysis-empty-hint { font-family: var(--font-mono); font-size: 10px; text-transform: uppercase; letter-spacing: .08em; }
|
||||
|
||||
/* Detail card */
|
||||
.detail-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-card);
|
||||
padding: 14px 16px;
|
||||
}
|
||||
.detail-head { display: flex; align-items: center; gap: 7px; margin-bottom: 8px; }
|
||||
.detail-title { font-weight: 700; font-size: 14px; line-height: 1.3; margin-bottom: 5px; }
|
||||
.detail-summary { font-size: 13px; color: var(--muted); line-height: 1.6; }
|
||||
.detail-meta { display: flex; gap: 14px; margin-top: 10px; flex-wrap: wrap; }
|
||||
.meta-item { font-family: var(--font-mono); font-size: 10px; color: var(--muted); }
|
||||
.meta-item strong { color: var(--fg); }
|
||||
|
||||
.action-row { display: flex; gap: 8px; align-items: center; flex-shrink: 0; }
|
||||
|
||||
/* Output card */
|
||||
.output-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-card);
|
||||
padding: 14px 16px;
|
||||
display: none;
|
||||
}
|
||||
.output-head {
|
||||
font-family: var(--font-mono); font-size: 10px;
|
||||
text-transform: uppercase; letter-spacing: .08em;
|
||||
color: var(--muted); margin-bottom: 10px;
|
||||
display: flex; align-items: center; gap: 7px;
|
||||
}
|
||||
@keyframes blink { 0%,100%{opacity:1} 50%{opacity:0} }
|
||||
.blink { animation: blink 1s step-end infinite; }
|
||||
|
||||
.md-h2 { font-size: 13px; font-weight: 700; color: var(--accent); margin: 14px 0 5px; font-family: var(--font-display); }
|
||||
.md-h3 { font-size: 12px; font-weight: 700; margin: 10px 0 3px; }
|
||||
.md-li { display: flex; gap: 7px; margin-bottom: 3px; padding-left: 3px; font-size: 12px; line-height: 1.6; }
|
||||
.md-li-dot { color: var(--accent); flex-shrink: 0; }
|
||||
.md-p { font-size: 12px; line-height: 1.7; margin-bottom: 3px; }
|
||||
.md-empty { height: 5px; }
|
||||
|
||||
/* Docs card */
|
||||
.docs-card {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-card);
|
||||
padding: 12px 16px;
|
||||
display: none;
|
||||
}
|
||||
.docs-head {
|
||||
font-family: var(--font-mono); font-size: 10px;
|
||||
text-transform: uppercase; letter-spacing: .08em;
|
||||
color: var(--muted); margin-bottom: 8px;
|
||||
}
|
||||
.doc-row { display: flex; align-items: flex-start; gap: 10px; padding: 7px 0; border-top: 1px solid var(--border); }
|
||||
.doc-row:first-of-type { border-top: none; }
|
||||
.doc-score { font-family: var(--font-mono); font-size: 11px; color: var(--accent); font-weight: 700; flex-shrink: 0; width: 32px; }
|
||||
.doc-name { font-size: 12px; font-weight: 600; line-height: 1.4; }
|
||||
.doc-clause { font-family: var(--font-mono); font-size: 10px; color: var(--muted); }
|
||||
.doc-snippet { font-size: 11px; color: var(--muted); line-height: 1.5; margin-top: 2px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
||||
|
||||
/* Status pills */
|
||||
.status {
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
padding: 2px 8px; border-radius: var(--radius-pill);
|
||||
font-size: 10px; font-family: var(--font-mono); font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.status::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
|
||||
.status.ok { color: var(--success); background: color-mix(in oklab,var(--success),transparent 90%); }
|
||||
.status.warn { color: var(--warn); background: color-mix(in oklab,var(--warn),transparent 90%); }
|
||||
.status.risk { color: var(--danger); background: color-mix(in oklab,var(--danger),transparent 90%); }
|
||||
.status.info { color: #3b82f6; background: color-mix(in oklab,#3b82f6,transparent 90%); }
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
display: flex; align-items: center; justify-content: space-between; gap: 16px;
|
||||
min-height: 34px; padding: 0 20px;
|
||||
border-top: 1px solid var(--border);
|
||||
color: var(--muted); font-size: 10px;
|
||||
font-family: var(--font-mono);
|
||||
letter-spacing: .1em; text-transform: uppercase;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.footer-live { display: inline-flex; align-items: center; gap: 7px; }
|
||||
.footer-dot {
|
||||
width: 6px; height: 6px; border-radius: 50%;
|
||||
background: #22c55e;
|
||||
box-shadow: 0 0 0 3px rgba(34,197,94,.2);
|
||||
}
|
||||
|
||||
/* Dark mode */
|
||||
[data-theme="dark"] {
|
||||
--rail-bg: #1a1c22;
|
||||
--rail-surface: #22242c;
|
||||
--rail-fg: #f0f2f5;
|
||||
--rail-muted: #7a8390;
|
||||
--rail-border: #2d3038;
|
||||
--rail-hover: rgba(255,255,255,.05);
|
||||
--rail-active: rgba(226,0,116,.12);
|
||||
--bg: #111318;
|
||||
--surface: #1a1c22;
|
||||
--fg: #f0f2f5;
|
||||
--muted: #7a8390;
|
||||
--border: #2d3038;
|
||||
}
|
||||
[data-theme="dark"] body { color-scheme: dark; }
|
||||
[data-theme="dark"] .topbar { background: rgba(17,19,24,.9); }
|
||||
[data-theme="dark"] .stats-bar { background: var(--border); }
|
||||
[data-theme="dark"] .stat-cell { background: var(--surface); }
|
||||
[data-theme="dark"] .filter-bar { background: var(--surface); }
|
||||
[data-theme="dark"] .ev-card { background: var(--surface); }
|
||||
[data-theme="dark"] .detail-card,
|
||||
[data-theme="dark"] .output-card,
|
||||
[data-theme="dark"] .docs-card { background: var(--surface); }
|
||||
|
||||
/* Sidebar action */
|
||||
.sidebar-action {
|
||||
display: flex; align-items: center; gap: 9px;
|
||||
height: 32px; padding: 0 8px;
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--rail-muted); font-size: 12px;
|
||||
border: none; background: transparent; width: 100%;
|
||||
text-align: left; cursor: pointer;
|
||||
transition: background 120ms, color 120ms;
|
||||
}
|
||||
.sidebar-action:hover { background: var(--rail-hover); color: var(--rail-fg); }
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 1100px) {
|
||||
.stats-bar { grid-template-columns: 1fr 1fr; }
|
||||
.perception-split { grid-template-columns: 300px 1fr; }
|
||||
}
|
||||
@media (max-width: 800px) {
|
||||
.app-shell { grid-template-columns: 1fr; }
|
||||
.sidebar { display: none; }
|
||||
.stats-bar { grid-template-columns: 1fr 1fr; }
|
||||
.perception-split { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-shell">
|
||||
|
||||
<!-- ─── Sidebar (dark rail) ─────────────────────────────────────────── -->
|
||||
<aside class="sidebar" aria-label="Primary navigation">
|
||||
<div class="sidebar-brand">
|
||||
<div class="brand-logo">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M2 3.5h12v1.5H2zm5.25 1.5h1.5v8h-1.5zm-3 2h2.25v1.5H4.25zm5.25 0h2.25v1.5H9.5zm-3 3h2.5v1.5H6.5z" fill="currentColor"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="brand-name">T-Systems</div>
|
||||
<div class="brand-sub">Regulation Hub</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="sidebar-nav" aria-label="Primary">
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">Main</span>
|
||||
<a class="nav-item" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/index.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M2 2h5v5H2zm7 0h5v5H9zM2 9h5v5H2zm7 0h5v5H9z" fill="currentColor" opacity=".6"/></svg>
|
||||
Overview
|
||||
</a>
|
||||
<a class="nav-item active" href="perception.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none">
|
||||
<circle cx="8" cy="8" r="2.5" fill="currentColor"/>
|
||||
<path d="M8 2.5C4.91 2.5 2.5 5.42 2.5 8S4.91 13.5 8 13.5 13.5 10.58 13.5 8 11.09 2.5 8 2.5zm0 9.5C5.52 12 3.5 10.24 3.5 8S5.52 4 8 4s4.5 1.76 4.5 4-2.02 4-4.5 4z" fill="currentColor" opacity=".45"/>
|
||||
</svg>
|
||||
Regulatory Signals
|
||||
<span class="nav-badge" id="badge-high">—</span>
|
||||
</a>
|
||||
<a class="nav-item" href="dashboard-sidebar.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M1.5 2.5h13v1H1.5zm0 3h13v1H1.5zm0 3h8v1h-8zm0 3h6v1h-6z" fill="currentColor"/></svg>
|
||||
System Status
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">Workbench</span>
|
||||
<a class="nav-item" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/document-management.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M3 1h7l3 3v11H3V1zm1 1v12h8V5h-3V2H4zm5 .5V4h1.5L9 1.5zM6 7h4v1H6zm0 2h4v1H6zm0 2h3v1H6z" fill="currentColor"/></svg>
|
||||
Documents
|
||||
</a>
|
||||
<a class="nav-item" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/compliance-analysis.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M8 1l7 3-1 6a7 7 0 01-6 5A7 7 0 011 10L0 4l8-3zm0 1.2L1.3 4.8l.8 5.1A6 6 0 008 14.8a6 6 0 005.9-4.9l.8-5.1L8 2.2zM7.5 5h1v4.5h-1V5zm0 5.5h1v1h-1v-1z" fill="currentColor"/></svg>
|
||||
Compliance Analysis
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-group">
|
||||
<span class="nav-group-label">Chat</span>
|
||||
<a class="nav-item" href="cc29bcb0-df2d-4d50-9428-7caa406ecb29/regulation-chat.html">
|
||||
<svg class="nav-icon" viewBox="0 0 16 16" fill="none"><path d="M2 2h12a1 1 0 011 1v8a1 1 0 01-1 1H5l-3 2.5V3a1 1 0 011-1zm0 1v9.5L4.5 11H14V3H2zm2 2h8v1H4zm0 2h6v1H4z" fill="currentColor"/></svg>
|
||||
Regulation Q&A
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="sidebar-footer">
|
||||
<div class="sidebar-user">
|
||||
<div class="avatar">TS</div>
|
||||
<div>
|
||||
<div class="user-name">T-Systems User</div>
|
||||
<div class="user-role">Compliance Analyst</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="sidebar-action" type="button" onclick="toggleTheme()">
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M8 3a5 5 0 100 10A5 5 0 008 3zM2 8a6 6 0 1112 0A6 6 0 012 8z" fill="currentColor"/>
|
||||
</svg>
|
||||
<span>Dark mode</span>
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- ─── Content ───────────────────────────────────────────────────────── -->
|
||||
<div class="content-area">
|
||||
<!-- Topbar -->
|
||||
<div class="topbar">
|
||||
<span class="topbar-title">
|
||||
Regulatory Signals
|
||||
<span class="topbar-sub">Real-time monitoring · Knowledge-base impact analysis</span>
|
||||
</span>
|
||||
<div class="search">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M6.5 1a5.5 5.5 0 014.23 9.02l3.62 3.62-.7.71-3.63-3.63A5.5 5.5 0 116.5 1zm0 1a4.5 4.5 0 100 9 4.5 4.5 0 000-9z" fill="currentColor" opacity=".4"/>
|
||||
</svg>
|
||||
<input type="search" placeholder="Search signals…" aria-label="Search signals" />
|
||||
</div>
|
||||
<button class="btn btn-sm" onclick="loadFeed()">
|
||||
<svg width="12" height="12" viewBox="0 0 16 16" fill="none"><path d="M13.5 8a5.5 5.5 0 11-1.1-3.3" stroke="currentColor" stroke-width="1.4"/><path d="M10 4.5l2.5.2.3-2.7" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
|
||||
Refresh
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Stats bar — inlined (no card borders, flush panel style) -->
|
||||
<div class="stats-bar">
|
||||
<div class="stat-cell">
|
||||
<div class="s-label">Total signals</div>
|
||||
<div class="s-value" id="stat-total">—</div>
|
||||
<div class="s-sub">All regulatory events in feed</div>
|
||||
</div>
|
||||
<div class="stat-cell">
|
||||
<div class="s-label">High impact</div>
|
||||
<div class="s-value" id="stat-high" style="color:var(--danger)">—</div>
|
||||
<div class="s-sub">Requires immediate review</div>
|
||||
</div>
|
||||
<div class="stat-cell">
|
||||
<div class="s-label">Medium impact</div>
|
||||
<div class="s-value" id="stat-med" style="color:var(--warn)">—</div>
|
||||
<div class="s-sub">Scheduled for assessment</div>
|
||||
</div>
|
||||
<div class="stat-cell">
|
||||
<div class="s-label">Last 90 days</div>
|
||||
<div class="s-value" id="stat-90d" style="color:var(--accent)">—</div>
|
||||
<div class="s-sub">Recent publications</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filter bar -->
|
||||
<div class="filter-bar">
|
||||
<span class="filter-label">Source</span>
|
||||
<button class="chip on" data-src="" onclick="setSource(this,'')">All</button>
|
||||
<button class="chip" data-src="MIIT" onclick="setSource(this,'MIIT')">MIIT</button>
|
||||
<button class="chip" data-src="UN-ECE" onclick="setSource(this,'UN-ECE')">UN-ECE</button>
|
||||
<button class="chip" data-src="ISO" onclick="setSource(this,'ISO')">ISO</button>
|
||||
<button class="chip" data-src="国标委" onclick="setSource(this,'国标委')">GB Comm.</button>
|
||||
<button class="chip" data-src="EUR-Lex" onclick="setSource(this,'EUR-Lex')">EUR-Lex</button>
|
||||
<button class="chip" data-src="IATF" onclick="setSource(this,'IATF')">IATF</button>
|
||||
<div class="sep"></div>
|
||||
<span class="filter-label">Impact</span>
|
||||
<button class="chip on" data-imp="" onclick="setImpact(this,'')">All</button>
|
||||
<button class="chip" data-imp="high" onclick="setImpact(this,'high')">High</button>
|
||||
<button class="chip" data-imp="medium" onclick="setImpact(this,'medium')">Medium</button>
|
||||
<button class="chip" data-imp="low" onclick="setImpact(this,'low')">Low</button>
|
||||
</div>
|
||||
|
||||
<!-- Two-pane split -->
|
||||
<div class="perception-split work-area">
|
||||
<!-- Feed pane -->
|
||||
<div class="feed-pane">
|
||||
<div class="feed-pane-head">
|
||||
<h2>Signal feed</h2>
|
||||
<span class="feed-count" id="feed-count"></span>
|
||||
</div>
|
||||
<div class="feed-scroll" id="feed-scroll">
|
||||
<div class="loading-msg">Loading…</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Analysis pane -->
|
||||
<div class="analysis-pane" id="analysis-pane">
|
||||
<div class="analysis-empty" id="analysis-empty">
|
||||
<div class="analysis-empty-ring">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M8 2a6 6 0 100 12A6 6 0 008 2zm0 2v4l3 1.5-.5 1-3.5-1.75V4H8z" fill="currentColor" opacity=".3"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="analysis-empty-label">Select a signal to view impact analysis</div>
|
||||
<div class="analysis-empty-hint">← Choose from the signal feed</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="footer">
|
||||
<div class="footer-live"><span class="footer-dot"></span><span>Live feed</span></div>
|
||||
<span>T-Systems Regulation Hub</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ─── Theme toggle ────────────────────────────────────────────────────
|
||||
function toggleTheme() {
|
||||
const html = document.documentElement;
|
||||
const next = html.dataset.theme === 'dark' ? 'light' : 'dark';
|
||||
html.dataset.theme = next;
|
||||
localStorage.setItem('theme', next);
|
||||
const spans = document.querySelectorAll('.sidebar-action span');
|
||||
spans.forEach(s => s.textContent = next === 'dark' ? 'Light mode' : 'Dark mode');
|
||||
}
|
||||
(function() {
|
||||
const saved = localStorage.getItem('theme');
|
||||
if (saved) document.documentElement.dataset.theme = saved;
|
||||
})();
|
||||
|
||||
const API = 'http://6.86.80.9:5173/api/v1';
|
||||
|
||||
const SRC_COLOR = {
|
||||
MIIT: '#e20074', 'UN-ECE': '#3b82f6', ISO: '#7c3aed',
|
||||
'国标委': '#059669', 'EUR-Lex': '#d97706', IATF: '#7c3aed'
|
||||
};
|
||||
const IMP_COLOR = { high: 'var(--danger)', medium: 'var(--warn)', low: 'var(--success)' };
|
||||
const IMP_LABEL = { high: 'High', medium: 'Medium', low: 'Low' };
|
||||
const STA_LABEL = { enacted: 'Enacted', draft: 'Draft', consultation: 'Consultation' };
|
||||
const STA_CLASS = { enacted: 'ok', draft: 'warn', consultation: 'info' };
|
||||
|
||||
let currentSource = '', currentImpact = '', selectedId = null, abortCtrl = null;
|
||||
let allEvents = [];
|
||||
|
||||
async function loadStats() {
|
||||
try {
|
||||
const r = await fetch(`${API}/perception/stats`);
|
||||
if (!r.ok) return;
|
||||
const s = await r.json();
|
||||
document.getElementById('stat-total').textContent = s.total ?? '—';
|
||||
document.getElementById('stat-high').textContent = s.high_impact ?? '—';
|
||||
document.getElementById('stat-med').textContent = s.medium_impact ?? '—';
|
||||
document.getElementById('stat-90d').textContent = s.recent_90d ?? '—';
|
||||
const badge = document.getElementById('badge-high');
|
||||
if (badge) badge.textContent = s.high_impact ?? '—';
|
||||
} catch(e) { console.warn('stats:', e); }
|
||||
}
|
||||
|
||||
async function loadFeed() {
|
||||
const scroll = document.getElementById('feed-scroll');
|
||||
scroll.innerHTML = '<div class="loading-msg">Loading…</div>';
|
||||
const params = new URLSearchParams();
|
||||
if (currentSource) params.set('source', currentSource);
|
||||
if (currentImpact) params.set('impact_level', currentImpact);
|
||||
try {
|
||||
const r = await fetch(`${API}/perception/events?${params}`);
|
||||
if (!r.ok) throw new Error(r.status);
|
||||
const data = await r.json();
|
||||
allEvents = data.events || [];
|
||||
const countEl = document.getElementById('feed-count');
|
||||
if (countEl) countEl.textContent = `${allEvents.length} / ${data.total}`;
|
||||
renderFeed(allEvents);
|
||||
} catch(e) {
|
||||
scroll.innerHTML = `<div class="loading-msg" style="color:var(--danger)">Failed to load: ${e.message}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
function renderFeed(events) {
|
||||
const scroll = document.getElementById('feed-scroll');
|
||||
if (!events.length) {
|
||||
scroll.innerHTML = '<div class="loading-msg">No signals match the current filters.</div>';
|
||||
return;
|
||||
}
|
||||
scroll.innerHTML = events.map(ev => {
|
||||
const sc = SRC_COLOR[ev.source] || '#888';
|
||||
const stc = STA_CLASS[ev.status] || 'info';
|
||||
const sel = ev.id === selectedId;
|
||||
return `<div class="ev-card${sel ? ' selected' : ''}" onclick="selectEvent('${ev.id}')" id="card-${ev.id}">
|
||||
<div class="ev-head">
|
||||
<span class="src-tag" style="color:${sc};background:${sc}18">${ev.source}</span>
|
||||
<span class="std-code">${ev.standard_code}</span>
|
||||
<span class="status ${stc}" style="margin-left:auto">${STA_LABEL[ev.status] || ev.status}</span>
|
||||
</div>
|
||||
<div class="ev-title">${ev.title}</div>
|
||||
<div class="ev-summary">${ev.summary}</div>
|
||||
<div class="ev-foot">
|
||||
<span class="ev-date">${ev.published_at}</span>
|
||||
${(ev.tags||[]).slice(0,2).map(t=>`<span class="ev-tag">${t}</span>`).join('')}
|
||||
<span class="imp-dot" style="color:${IMP_COLOR[ev.impact_level]||'var(--muted)'}">● ${IMP_LABEL[ev.impact_level]||ev.impact_level}</span>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function selectEvent(id) {
|
||||
if (id === selectedId) return;
|
||||
selectedId = id;
|
||||
document.querySelectorAll('.ev-card').forEach(c => c.classList.remove('selected'));
|
||||
const card = document.getElementById(`card-${id}`);
|
||||
if (card) card.classList.add('selected');
|
||||
const ev = allEvents.find(e => e.id === id);
|
||||
if (ev) renderDetail(ev);
|
||||
}
|
||||
|
||||
function renderDetail(ev) {
|
||||
const sc = SRC_COLOR[ev.source] || '#888';
|
||||
const stc = STA_CLASS[ev.status] || 'info';
|
||||
const pane = document.getElementById('analysis-pane');
|
||||
pane.innerHTML = `
|
||||
<div class="detail-card">
|
||||
<div class="detail-head">
|
||||
<span class="src-tag" style="color:${sc};background:${sc}18;font-size:10px;padding:2px 7px;border-radius:4px">${ev.source}</span>
|
||||
<span style="font-family:var(--font-mono);font-size:10px;color:var(--muted)">${ev.standard_code}</span>
|
||||
<span class="status ${stc}" style="margin-left:auto">${STA_LABEL[ev.status]||ev.status}</span>
|
||||
</div>
|
||||
<div class="detail-title">${ev.title}</div>
|
||||
<div class="detail-summary">${ev.summary}</div>
|
||||
<div class="detail-meta">
|
||||
<span class="meta-item">Published <strong>${ev.published_at}</strong></span>
|
||||
${ev.effective_at ? `<span class="meta-item">Effective <strong>${ev.effective_at}</strong></span>` : ''}
|
||||
<span class="meta-item">Category <strong>${ev.category}</strong></span>
|
||||
<span class="meta-item">Impact <strong style="color:${IMP_COLOR[ev.impact_level]||'var(--muted)'}">${IMP_LABEL[ev.impact_level]||ev.impact_level}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-row">
|
||||
<button class="btn btn-primary" id="btn-analyze" onclick="startAnalysis('${ev.id}')">
|
||||
<svg width="12" height="12" viewBox="0 0 16 16" fill="none"><path d="M8 1l1.8 5.5H15l-4.8 3.5 1.8 5.5L8 12l-4 3.5 1.8-5.5L1 6.5h5.2z" fill="currentColor"/></svg>
|
||||
Run impact analysis
|
||||
</button>
|
||||
<button class="btn" id="btn-abort" style="display:none" onclick="stopAnalysis()">Stop</button>
|
||||
${ev.source_url ? `<a href="${ev.source_url}" target="_blank" class="btn">
|
||||
<svg width="12" height="12" viewBox="0 0 16 16" fill="none"><path d="M9 2h5v5M9.5 6.5L14 2M3 4h4M3 8h8M3 12h8" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
|
||||
Source
|
||||
</a>` : ''}
|
||||
</div>
|
||||
<div class="docs-card" id="docs-card">
|
||||
<div class="docs-head">Affected documents</div>
|
||||
<div id="docs-list"></div>
|
||||
</div>
|
||||
<div class="output-card" id="output-card">
|
||||
<div class="output-head" id="output-head">
|
||||
<svg width="11" height="11" viewBox="0 0 16 16" fill="none"><path d="M8 1l1.8 5.5H15l-4.8 3.5 1.8 5.5L8 12l-4 3.5 1.8-5.5L1 6.5h5.2z" fill="currentColor"/></svg>
|
||||
AI impact analysis
|
||||
</div>
|
||||
<div id="output-body"></div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
async function startAnalysis(eventId) {
|
||||
if (abortCtrl) abortCtrl.abort();
|
||||
abortCtrl = new AbortController();
|
||||
const btnA = document.getElementById('btn-analyze');
|
||||
const btnX = document.getElementById('btn-abort');
|
||||
btnA.disabled = true;
|
||||
btnX.style.display = '';
|
||||
const outputCard = document.getElementById('output-card');
|
||||
const outputHead = document.getElementById('output-head');
|
||||
const outputBody = document.getElementById('output-body');
|
||||
const docsCard = document.getElementById('docs-card');
|
||||
outputCard.style.display = '';
|
||||
outputBody.innerHTML = '';
|
||||
outputHead.innerHTML = `<svg width="11" height="11" viewBox="0 0 16 16" fill="none"><path d="M8 1l1.8 5.5H15l-4.8 3.5 1.8 5.5L8 12l-4 3.5 1.8-5.5L1 6.5h5.2z" fill="currentColor"/></svg> AI impact analysis <span class="blink" style="color:var(--accent)">▋</span>`;
|
||||
try {
|
||||
const res = await fetch(`${API}/perception/events/${eventId}/analyze`, {
|
||||
method: 'POST', headers: { Accept: 'text/event-stream' }, signal: abortCtrl.signal
|
||||
});
|
||||
if (!res.ok || !res.body) throw new Error(`HTTP ${res.status}`);
|
||||
const reader = res.body.getReader();
|
||||
const dec = new TextDecoder();
|
||||
let buf = '', rawText = '';
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
buf += dec.decode(value, { stream: true });
|
||||
const parts = buf.split('\n\n');
|
||||
buf = parts.pop() ?? '';
|
||||
for (const block of parts) {
|
||||
if (!block.trim()) continue;
|
||||
let evtName = 'message';
|
||||
const dataLines = [];
|
||||
for (const line of block.split('\n')) {
|
||||
if (line.startsWith('event:')) evtName = line.slice(6).trim();
|
||||
else if (line.startsWith('data:')) dataLines.push(line.slice(5).trim());
|
||||
}
|
||||
const payload = dataLines.join('\n');
|
||||
if (!payload) continue;
|
||||
if (evtName === 'sources') {
|
||||
try { renderDocs(JSON.parse(payload), docsCard); } catch {}
|
||||
} else if (evtName === 'content') {
|
||||
rawText += payload;
|
||||
outputBody.innerHTML = renderMarkdown(rawText);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
if (e.name !== 'AbortError') {
|
||||
const ob = document.getElementById('output-body');
|
||||
if (ob) ob.innerHTML += `<div style="color:var(--danger);font-size:12px;margin-top:8px">Analysis error: ${e.message}</div>`;
|
||||
}
|
||||
} finally {
|
||||
const blink = document.querySelector('#output-head .blink');
|
||||
if (blink) blink.remove();
|
||||
const ab = document.getElementById('btn-abort');
|
||||
if (ab) ab.style.display = 'none';
|
||||
const ba = document.getElementById('btn-analyze');
|
||||
if (ba) { ba.disabled = false; ba.innerHTML = `<svg width="12" height="12" viewBox="0 0 16 16" fill="none"><path d="M8 1l1.8 5.5H15l-4.8 3.5 1.8 5.5L8 12l-4 3.5 1.8-5.5L1 6.5h5.2z" fill="currentColor"/></svg> Re-analyse`; }
|
||||
}
|
||||
}
|
||||
|
||||
function stopAnalysis() {
|
||||
if (abortCtrl) { abortCtrl.abort(); abortCtrl = null; }
|
||||
const ab = document.getElementById('btn-abort');
|
||||
if (ab) ab.style.display = 'none';
|
||||
const ba = document.getElementById('btn-analyze');
|
||||
if (ba) ba.disabled = false;
|
||||
const h = document.getElementById('output-head');
|
||||
if (h) { const b = h.querySelector('.blink'); if (b) b.remove(); }
|
||||
}
|
||||
|
||||
function renderDocs(docs, card) {
|
||||
if (!docs || !docs.length) return;
|
||||
card.style.display = '';
|
||||
document.getElementById('docs-list').innerHTML = docs.map(d => `
|
||||
<div class="doc-row">
|
||||
<div class="doc-score">${Math.round(d.score * 100)}%</div>
|
||||
<div>
|
||||
<div class="doc-name">${d.doc_name}</div>
|
||||
<div class="doc-clause">${d.clause || ''}</div>
|
||||
<div class="doc-snippet">${d.snippet || ''}</div>
|
||||
</div>
|
||||
</div>`).join('');
|
||||
}
|
||||
|
||||
function renderMarkdown(text) {
|
||||
return text.split('\n').map(line => {
|
||||
if (line.startsWith('## ')) return `<div class="md-h2">${line.slice(3)}</div>`;
|
||||
if (line.startsWith('### ')) return `<div class="md-h3">${line.slice(4)}</div>`;
|
||||
if (line.startsWith('- ') || line.startsWith('* ')) {
|
||||
const c = line.slice(2).replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>');
|
||||
return `<div class="md-li"><span class="md-li-dot">·</span><span>${c}</span></div>`;
|
||||
}
|
||||
if (/^\d+\./.test(line)) {
|
||||
const c = line.replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>');
|
||||
return `<div class="md-p" style="padding-left:8px">${c}</div>`;
|
||||
}
|
||||
if (!line.trim()) return '<div class="md-empty"></div>';
|
||||
return `<div class="md-p">${line.replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>')}</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function setSource(btn, src) {
|
||||
currentSource = src;
|
||||
document.querySelectorAll('[data-src]').forEach(c => c.classList.toggle('on', c.dataset.src === src));
|
||||
loadFeed();
|
||||
}
|
||||
|
||||
function setImpact(btn, imp) {
|
||||
currentImpact = imp;
|
||||
document.querySelectorAll('[data-imp]').forEach(c => c.classList.toggle('on', c.dataset.imp === imp));
|
||||
loadFeed();
|
||||
}
|
||||
|
||||
// Boot
|
||||
loadStats();
|
||||
loadFeed();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
1059
Prototype/regulation-hub-export.html
Normal file
1059
Prototype/regulation-hub-export.html
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user