ldv-pilot / ldv-frontend /citations.html
system's picture
system HF Staff
Sync from GitHub master (e9a60c67b365f40e7d83fe11705b5c7d807bccda)
47362a9 verified
Raw
History Blame Contribute Delete
32.2 kB
<!DOCTYPE html>
<html class="dark" lang="en">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>Citation Library — Sydeco CRA</title>
<link rel="icon" type="image/svg+xml" href="images/sydeco_shield.svg">
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.15.12/dist/cdn.min.js" integrity="sha384-pb6hrQvo4s23cEUFtj0CZkzGE3jyK3pj26RIupXXxhSrrcUA/Cn0lZgcCrGH0t6L" crossorigin="anonymous"></script>
<script defer src="i18n.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;600;700;800&family=Plus+Jakarta+Sans:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
<script id="tailwind-config">
tailwind.config = {
darkMode: "class",
theme: {
extend: {
"colors": {
"on-background": "#dee2f4",
"outline-variant": "#4d4635",
"background": "#0e131f",
"on-error": "#690005",
"on-secondary-fixed": "#2d1600",
"secondary-fixed": "#ffdcbf",
"secondary": "#f1bc8c",
"primary": "#d4af37",
"inverse-on-surface": "#2b303d",
"on-primary": "#241a00",
"on-primary-container": "#554300",
"on-secondary-fixed-variant": "#633f19",
"surface-tint": "#d4af37",
"on-tertiary-fixed-variant": "#414754",
"outline": "#99907c",
"inverse-surface": "#dee2f4",
"on-tertiary-container": "#3f4553",
"on-secondary-container": "#e2ae7f",
"primary-container": "#d4af37",
"tertiary": "#c9cedf",
"surface-container-highest": "#303542",
"on-tertiary-fixed": "#161c27",
"on-secondary": "#492905",
"primary-fixed": "#ffe088",
"tertiary-fixed-dim": "#c1c6d7",
"on-surface-variant": "#d0c5af",
"inverse-primary": "#735c00",
"surface-container": "#1b1f2c",
"surface-variant": "#303542",
"surface-container-high": "#252a37",
"surface-container-lowest": "#090e1a",
"on-primary-fixed-variant": "#574500",
"tertiary-container": "#adb3c3",
"tertiary-fixed": "#dde2f3",
"secondary-container": "#66411b",
"on-error-container": "#ffdad6",
"surface-bright": "#343946",
"error": "#ffb4ab",
"surface": "#0e131f",
"surface-container-low": "#161b28",
"on-primary-fixed": "#241a00",
"secondary-fixed-dim": "#f1bc8c",
"surface-dim": "#0e131f",
"on-tertiary": "#2a303d",
"on-surface": "#dee2f4",
"primary-fixed-dim": "#d4af37",
"error-container": "#93000a"
},
"borderRadius": {
"DEFAULT": "4px",
"md": "8px",
"lg": "12px",
"xl": "16px",
"full": "9999px"
},
"spacing": {
"margin-desktop": "64px",
"margin-tablet": "32px",
"margin-mobile": "16px",
"base": "8px",
"gutter": "24px",
"container-max": "1440px"
},
"fontFamily": {
"display": ["Playfair Display", "serif"],
"body": ["Inter", "Plus Jakarta Sans", "sans-serif"],
"display-lg": ["Playfair Display"],
"body-md": ["Inter"],
"headline-lg-mobile": ["Playfair Display"],
"headline-md": ["Playfair Display"],
"label-sm": ["Inter"],
"headline-lg": ["Playfair Display"],
"label-md": ["Inter"],
"body-lg": ["Inter"]
},
"fontSize": {
"display-lg": ["48px", {"lineHeight": "1.1", "letterSpacing": "-0.02em", "fontWeight": "700"}],
"body-md": ["16px", {"lineHeight": "1.5", "fontWeight": "400"}],
"headline-lg-mobile": ["28px", {"lineHeight": "1.2", "fontWeight": "600"}],
"headline-md": ["24px", {"lineHeight": "1.3", "fontWeight": "600"}],
"label-sm": ["12px", {"lineHeight": "1.4", "fontWeight": "500"}],
"headline-lg": ["32px", {"lineHeight": "1.2", "fontWeight": "600"}],
"label-md": ["14px", {"lineHeight": "1.4", "letterSpacing": "0.05em", "fontWeight": "600"}],
"body-lg": ["18px", {"lineHeight": "1.6", "fontWeight": "400"}]
}
},
},
}
</script>
<style>
:root {
--bg-surface: rgba(27,31,44,0.75);
--bg-surface-elevated: rgba(37,42,55,0.85);
--border-color: rgba(212,175,55,0.1);
--color-low: #039855; --color-low-bg: rgba(3,152,85,0.12);
--color-medium: #DC6803; --color-medium-bg: rgba(220,104,3,0.12);
--transition-fast: 0.15s cubic-bezier(0.16,1,0.3,1);
}
body {
background-color: #0e131f;
color: #dee2f4;
font-family: 'Inter', sans-serif;
overflow-x: hidden;
}
.font-editorial {
font-family: 'Playfair Display', serif;
}
.glass-panel {
background: rgba(14, 19, 31, 0.4);
backdrop-filter: blur(20px);
border: 1px solid rgba(212, 175, 55, 0.1);
}
.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
vertical-align: middle;
}
.active-ring {
box-shadow: 0 0 15px rgba(242, 202, 80, 0.2);
}
.legal-table-row:hover {
background: rgba(242, 202, 80, 0.03);
transition: background 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.gold-gradient-text {
background: linear-gradient(135deg, #f2ca50 0%, #e9c349 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.reports-table {
width: 100%;
border-collapse: collapse;
font-size: 0.85rem;
text-align: left;
}
.reports-table th {
background: rgba(48, 53, 66, 0.45);
padding: 12px 16px;
font-weight: 700;
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #9ca3af;
border-bottom: 1px solid rgba(212, 175, 55, 0.1);
}
.reports-table td {
padding: 14px 16px;
border-bottom: 1px solid rgba(212, 175, 55, 0.1);
color: #dee2f4;
vertical-align: middle;
}
.reports-table tbody tr:last-child td {
border-bottom: none;
}
.badge-draft { display:inline-block; padding:4px 10px; border-radius:12px; font-size:0.68rem; font-weight:700; letter-spacing:0.03em; text-transform:uppercase; background:var(--color-medium-bg); color: #DC6803; border:1px solid rgba(180,83,9,0.2); }
.badge-verified { display:inline-block; padding:4px 10px; border-radius:12px; font-size:0.68rem; font-weight:700; letter-spacing:0.03em; text-transform:uppercase; background:var(--color-low-bg); color: #039855; border:1px solid rgba(6,95,70,0.2); }
.juris-badge { font-family:monospace; font-weight:700; padding:2px 6px; border-radius:4px; font-size:0.75rem; background:rgba(48, 53, 66, 0.5); border:1px solid rgba(212, 175, 55, 0.1); }
select, input[type="search"] {
background: rgba(48, 53, 66, 0.45);
border: 1px solid rgba(212, 175, 55, 0.1);
border-radius: 4px;
padding: 10px 12px;
color: #dee2f4;
font-family: 'Inter', sans-serif;
font-size: 0.85rem;
transition: border-color var(--transition-fast);
width: 100%;
}
select:focus, input[type="search"]:focus { outline:none; border-color:#d4af37; }
.btn-verify {
padding: 6px 14px;
font-size: 0.75rem;
text-transform: uppercase;
font-weight: 700;
border-radius: 4px;
background: linear-gradient(135deg,#d4af37 0%,#b8952e 100%);
color: #0e131f;
border: none;
cursor: pointer;
transition: all 0.15s;
}
.btn-verify:hover { transform:translateY(-1px); box-shadow:0 4px 12px rgba(212,175,55,0.25); }
.toast-container { position:fixed; bottom:24px; right:24px; z-index:1000; display:flex; flex-direction:column; gap:8px; }
.toast { padding:12px 20px; border-radius:4px; background:rgba(37,42,55,0.95); border:1px solid #039855; color:#dee2f4; box-shadow:0 16px 40px rgba(0,0,0,0.5); display:flex; align-items:center; gap:10px; font-size:0.85rem; animation:slideIn 0.3s cubic-bezier(0.16,1,0.3,1); }
@keyframes slideIn { from{transform:translateX(100%);opacity:0} to{transform:translateX(0);opacity:1} }
.spinner-ring {
width: 36px;
height: 36px;
border: 3px solid rgba(212,175,55,0.15);
border-top-color:#d4af37;
border-radius:50%;
animation:spin 0.9s linear infinite;
}
@keyframes spin { to{transform:rotate(360deg)} }
.custom-scrollbar::-webkit-scrollbar {
width: 4px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: rgba(31, 41, 55, 0.5);
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #D4AF37;
border-radius: 2px;
}
/* Navigation Link active styling */
.nav-link-active {
color: #d4af37 !important;
background-color: rgba(212, 175, 55, 0.1) !important;
border-left: 3px solid #d4af37;
}
</style>
</head>
<body class="bg-background text-on-background font-body-md overflow-x-hidden">
<button type="button" data-lang-toggle onclick="i18n.toggleLang()"
class="fixed top-3 right-3 z-[60] px-3 py-1.5 rounded-full text-xs font-bold uppercase tracking-wider border border-primary/40 bg-surface-container/90 text-primary hover:bg-primary hover:text-on-primary transition-colors backdrop-blur-sm"
aria-label="Toggle language / Ganti bahasa">ID</button>
<!-- TopNavBar -->
<header class="fixed top-0 left-0 lg:left-64 right-0 z-40 h-16 flex items-center justify-between gap-3 px-4 sm:px-6 border-b border-outline-variant/10 shadow-sm" style="background:rgba(14,19,31,0.9);backdrop-filter:blur(20px);">
<div class="flex items-center gap-2 min-w-0 text-xs font-semibold text-on-surface-variant/60 font-body">
<button type="button" class="lg:hidden flex items-center justify-center w-11 h-11 -ml-2 rounded text-on-surface-variant hover:text-primary transition-colors shrink-0" aria-label="Open navigation menu" onclick="toggleSidebar()">
<span class="material-symbols-outlined">menu</span>
</button>
<a href="/admin" class="hover:text-primary transition-colors text-on-surface-variant/80 hidden sm:inline shrink-0" style="text-decoration:none;" data-i18n="nav.sidebar.admin_portal">Admin Portal</a>
<span class="material-symbols-outlined text-[16px] text-on-surface-variant/30 hidden sm:inline shrink-0">chevron_right</span>
<span class="text-primary font-bold truncate" data-i18n="nav.citation_library">Citation Library</span>
</div>
<a href="/" class="text-sm font-bold font-body uppercase tracking-wider px-3 sm:px-5 py-2 rounded bg-primary text-on-primary hover:brightness-110 transition-all shadow-md shadow-primary/10 whitespace-nowrap shrink-0" style="text-decoration:none;"><span data-i18n="admin.header.new_analysis_prefix">+ New</span><span class="hidden sm:inline" data-i18n="admin.header.new_analysis_suffix"> Analysis</span></a>
</header>
<!-- SideNavBar -->
<div id="sidebar-overlay" class="fixed inset-0 bg-black/60 z-[45] hidden lg:hidden" onclick="toggleSidebar()"></div>
<aside id="sidebar" class="fixed left-0 top-0 bottom-0 w-64 flex flex-col py-6 bg-surface-container/95 lg:bg-surface-container/40 backdrop-blur-2xl border-r border-outline-variant/10 shadow-2xl z-50 -translate-x-full lg:translate-x-0 transition-transform duration-200">
<div class="px-6 mb-8 flex items-center gap-3 pt-4">
<div class="w-10 h-10 rounded bg-primary/20 flex items-center justify-center border border-primary/30 shrink-0">
<svg class="h-6 w-6" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M100 20L40 50V100C40 137.01 65.59 171.41 100 180C134.41 171.41 160 137.01 160 100V50L100 20Z" stroke="#D4AF37" stroke-width="8" stroke-linejoin="round"/>
<path d="M100 60V140" stroke="#D4AF37" stroke-width="8" stroke-linecap="round"/>
<path d="M70 80L130 80" stroke="#D4AF37" stroke-width="8" stroke-linecap="round"/>
<path d="M80 120L120 120" stroke="#D4AF37" stroke-width="8" stroke-linecap="round"/>
</svg>
</div>
<div>
<p class="font-editorial text-primary leading-tight text-[18px]">Sydeco CRA</p>
<p class="font-body text-[10px] text-on-surface-variant uppercase tracking-widest opacity-70 font-semibold">LightML Engine</p>
</div>
</div>
<nav class="flex-grow flex flex-col gap-1 font-body">
<!-- Core Operations -->
<div class="px-6 mb-2 mt-4 text-[10px] font-bold uppercase tracking-widest text-on-surface-variant/40" data-i18n="nav.sidebar.core_operations">Core Operations</div>
<a class="flex items-center gap-3 px-4 py-3 mx-2 text-on-surface-variant hover:bg-surface-variant/30 hover:text-primary rounded-lg transition-all" href="/">
<span class="material-symbols-outlined">upload_file</span>
<span class="text-sm font-semibold" data-i18n="nav.new_analysis">New Analysis</span>
</a>
<a class="flex items-center gap-3 px-4 py-3 mx-2 text-on-surface-variant hover:bg-surface-variant/30 hover:text-primary rounded-lg transition-all" href="/admin#overview">
<span class="material-symbols-outlined">analytics</span>
<span class="text-sm font-semibold" data-i18n="nav.sidebar.overview">Overview</span>
</a>
<!-- Management -->
<div class="px-6 mb-2 mt-4 text-[10px] font-bold uppercase tracking-widest text-on-surface-variant/40" data-i18n="nav.sidebar.management">Management</div>
<a class="flex items-center gap-3 px-4 py-3 mx-2 text-on-surface-variant hover:bg-surface-variant/30 hover:text-primary rounded-lg transition-all" href="/admin#team">
<span class="material-symbols-outlined">group</span>
<span class="text-sm font-semibold" data-i18n="nav.sidebar.team_management">Team Management</span>
</a>
<a class="flex items-center gap-3 px-4 py-3 mx-2 text-on-surface-variant hover:bg-surface-variant/30 hover:text-primary rounded-lg transition-all" href="/admin#orgs">
<span class="material-symbols-outlined">corporate_fare</span>
<span class="text-sm font-semibold" data-i18n="nav.sidebar.organizations">Organizations</span>
</a>
<!-- Compliance & Health -->
<div class="px-6 mb-2 mt-4 text-[10px] font-bold uppercase tracking-widest text-on-surface-variant/40" data-i18n="nav.sidebar.compliance_health">Compliance & Health</div>
<a class="flex items-center gap-3 px-4 py-3 mx-2 nav-link-active rounded-lg transition-all" href="/citations">
<span class="material-symbols-outlined">verified</span>
<span class="text-sm font-semibold" data-i18n="nav.citation_library">Citation Library</span>
</a>
<a class="flex items-center gap-3 px-4 py-3 mx-2 text-on-surface-variant hover:bg-surface-variant/30 hover:text-primary rounded-lg transition-all" href="/admin#audits">
<span class="material-symbols-outlined">receipt_long</span>
<span class="text-sm font-semibold" data-i18n="nav.sidebar.audit_logs">Audit Logs</span>
</a>
<a class="flex items-center gap-3 px-4 py-3 mx-2 text-on-surface-variant hover:bg-surface-variant/30 hover:text-primary rounded-lg transition-all" href="/admin#status">
<span class="material-symbols-outlined">dns</span>
<span class="text-sm font-semibold" data-i18n="nav.sidebar.system_health">System Health</span>
</a>
</nav>
<div class="px-6 mt-auto font-body">
<div class="flex flex-col gap-3 pt-6 border-t border-outline-variant/10">
<a class="flex items-center gap-3 text-on-surface-variant hover:text-primary transition-colors text-xs" href="/logout" style="text-decoration:none;">
<span class="material-symbols-outlined text-[20px]">logout</span>
<span data-i18n="nav.secure_logout">Secure Logout</span>
</a>
</div>
</div>
</aside>
<!-- Main Content Area -->
<div class="lg:ml-64 min-h-screen">
<main class="max-w-[1200px] mx-auto px-4 sm:px-6 pb-12 pt-24" x-data="citationsDashboard()" x-init="load()">
<!-- Breadcrumbs and Header -->
<header class="mb-12">
<div class="flex flex-col md:flex-row md:items-end justify-between gap-6">
<div>
<h1 class="font-editorial text-headline-lg md:text-display-lg text-on-surface mb-2"><span data-i18n="citations.header.title_prefix">Legal Authority</span> <span class="gold-gradient-text" data-i18n="citations.header.title_suffix">Verification</span></h1>
<p class="font-body text-body-lg text-on-surface-variant max-w-2xl" data-i18n="citations.header.subtitle">A secure protocol for reviewing, verifying, and versioning legal citations within the sovereign intelligence architecture.</p>
</div>
</div>
</header>
<!-- Tabs and stats grid -->
<div class="flex flex-col gap-8" x-cloak x-show="!loading" :style="{ display: loading ? 'none' : 'block' }">
<div class="flex border-b border-outline-variant/20 font-body overflow-x-auto">
<button class="px-4 sm:px-8 py-4 font-label-md border-b-2 border-primary text-primary transition-all font-bold whitespace-nowrap" :class="activeTab === 'queue' ? 'border-primary text-primary' : 'border-transparent text-on-surface-variant'" @click="activeTab = 'queue'" data-i18n="citations.tabs.queue">Queue &amp; Verification</button>
<button class="px-4 sm:px-8 py-4 font-label-md border-b-2 transition-all font-bold whitespace-nowrap" :class="activeTab === 'history' ? 'border-primary text-primary' : 'border-transparent text-on-surface-variant'" @click="activeTab = 'history'" data-i18n="citations.tabs.history">History &amp; Versioning</button>
</div>
<!-- Tab Content: Queue -->
<div class="space-y-6" x-show="activeTab === 'queue'">
<!-- Stats Grid -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 font-body">
<div class="glass-panel p-6 rounded-lg flex flex-col justify-between h-32">
<span class="text-xs font-bold text-on-surface-variant uppercase tracking-widest" data-i18n="citations.stats.total_verified">Total Verified</span>
<span class="font-editorial text-headline-lg text-primary" x-text="citations.filter(c => c.status === 'verified').length">0</span>
</div>
<div class="glass-panel p-6 rounded-lg flex flex-col justify-between h-32">
<span class="text-xs font-bold text-on-surface-variant uppercase tracking-widest" data-i18n="citations.stats.awaiting_review">Awaiting Review</span>
<span class="font-editorial text-headline-lg text-secondary" x-text="citations.filter(c => c.status === 'draft').length">0</span>
</div>
<div class="glass-panel p-6 rounded-lg flex flex-col justify-between h-32">
<span class="text-xs font-bold text-on-surface-variant uppercase tracking-widest" data-i18n="citations.stats.superseded">Superseded</span>
<span class="font-editorial text-headline-lg text-on-surface-variant">42</span>
</div>
<div class="glass-panel p-6 rounded-lg flex flex-col justify-between h-32 relative overflow-hidden">
<span class="text-xs font-bold text-on-surface-variant uppercase tracking-widest relative z-10" data-i18n="citations.stats.integrity_score">Integrity Score</span>
<span class="font-editorial text-headline-lg text-primary relative z-10">99.2%</span>
</div>
</div>
<!-- Filters -->
<div class="glass-panel p-5 flex flex-wrap gap-4 items-end font-body">
<div class="flex flex-col gap-1.5 flex-1 min-w-[200px]">
<label class="text-xs font-bold text-on-surface-variant uppercase tracking-widest" for="searchField" data-i18n="citations.filters.search_label">Search Findings</label>
<input type="search" id="searchField" placeholder="Search by ID, article or source…" data-i18n-placeholder="citations.filters.search_placeholder" x-model="filters.search"/>
</div>
<div class="flex flex-col gap-1.5" style="min-width:200px;">
<label class="text-xs font-bold text-on-surface-variant uppercase tracking-widest" for="jurisdictionSelect" data-i18n="citations.filters.jurisdiction_label">Jurisdiction</label>
<select id="jurisdictionSelect" x-model="filters.jurisdiction">
<option value="all" data-i18n="citations.filters.jurisdiction_all">All Jurisdictions</option>
<option value="ID" data-i18n="citations.filters.jurisdiction_id">Indonesia (ID)</option>
<option value="BE" data-i18n="citations.filters.jurisdiction_be">Belgium (BE)</option>
<option value="FR" data-i18n="citations.filters.jurisdiction_fr">France (FR)</option>
<option value="NL" data-i18n="citations.filters.jurisdiction_nl">Netherlands (NL)</option>
<option value="EN&W" data-i18n="citations.filters.jurisdiction_enw">England &amp; Wales (EN&amp;W)</option>
<option value="US" data-i18n="citations.filters.jurisdiction_us">United States (US)</option>
<option value="generic" data-i18n="citations.filters.jurisdiction_generic">Generic (Cross-Border)</option>
</select>
</div>
<div class="flex flex-col gap-1.5" style="min-width:160px;">
<label class="text-xs font-bold text-on-surface-variant uppercase tracking-widest" for="statusSelect" data-i18n="citations.filters.status_label">Status</label>
<select id="statusSelect" x-model="filters.status">
<option value="all" data-i18n="citations.filters.status_all">All Statuses</option>
<option value="draft" data-i18n="citations.filters.status_draft_only">Drafts Only</option>
<option value="verified" data-i18n="citations.filters.status_verified_only">Verified Only</option>
</select>
</div>
</div>
<!-- Main Queue Table -->
<div class="glass-panel rounded-lg overflow-hidden">
<div class="overflow-x-auto custom-scrollbar">
<table class="reports-table">
<thead>
<tr class="font-body text-xs text-on-surface-variant">
<th data-i18n="citations.table.col_finding_id">Finding ID</th>
<th data-i18n="citations.table.col_jurisdiction">Jurisdiction</th>
<th data-i18n="citations.table.col_proposed_article">Proposed Article</th>
<th data-i18n="citations.table.col_legal_source">Legal Source</th>
<th data-i18n="citations.table.col_notes">Notes</th>
<th data-i18n="citations.table.col_status">Status</th>
<th style="text-align:right;" data-i18n="citations.table.col_action">Action</th>
</tr>
</thead>
<tbody class="font-body">
<template x-if="filteredCitations().length === 0">
<tr><td colspan="7" style="text-align:center;color:#9ca3af;font-style:italic;padding:32px;" data-i18n="citations.table.empty">No matching citations found.</td></tr>
</template>
<template x-for="c in filteredCitations()">
<tr class="legal-table-row">
<td style="font-family:monospace;font-size:0.8rem;color:#dee2f4;font-weight:600;" x-text="c.finding_id"></td>
<td><span class="juris-badge" x-text="c.jurisdiction"></span></td>
<td style="font-weight:600;" x-text="c.article || '—'"></td>
<td x-text="c.source || '—'"></td>
<td style="font-size:0.8rem;color:#9ca3af;max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" :title="c.note" x-text="c.note || '—'"></td>
<td>
<span :class="c.status === 'verified' ? 'badge-verified' : 'badge-draft'" x-text="c.status"></span>
</td>
<td style="text-align:right;">
<button class="btn-verify" x-show="c.status === 'draft'" @click="verifyCitation(c)" data-i18n="citations.table.verify_button">Verify</button>
<span x-show="c.status === 'verified'" style="font-size:0.75rem;color:#9ca3af;font-style:italic;padding-right:8px;" data-i18n="citations.table.ready_label">Ready</span>
</td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
</div>
<!-- Tab Content: History (Mockup timeline of versioning history) -->
<div class="space-y-8" x-show="activeTab === 'history'">
<div class="glass-panel p-8 rounded-lg">
<h3 class="font-editorial font-bold text-headline-md text-on-surface mb-8" data-i18n="citations.history.timeline_title">Authority Versioning Timeline</h3>
<div class="relative space-y-12 before:absolute before:left-[11px] before:top-2 before:bottom-2 before:w-[2px] before:bg-outline-variant/20 font-body">
<div class="relative pl-10">
<div class="absolute left-0 top-1 w-6 h-6 rounded-full bg-primary border-4 border-background active-ring"></div>
<div class="flex flex-col md:flex-row justify-between gap-4">
<div>
<p class="font-label-md text-primary mb-1 font-bold">V2.4.1 - Verified Current</p>
<p class="font-editorial text-xl text-on-surface">Delaware Gen. Corp. Law § 102</p>
<p class="text-body-md text-on-surface-variant mt-2">Amended to include liability protections for senior officers as well as directors. Updated via official legislative digest.</p>
</div>
<div class="text-right">
<p class="text-xs text-on-surface-variant uppercase tracking-widest" data-i18n="citations.history.verified_by_label">Verified By</p>
<p class="font-label-md text-on-surface font-semibold">Senior Counsel (ID:772)</p>
<p class="text-xs text-on-surface-variant mt-1">2024-10-15 09:30 UTC</p>
</div>
</div>
</div>
<div class="relative pl-10 opacity-60">
<div class="absolute left-0 top-1 w-6 h-6 rounded-full bg-outline-variant border-4 border-background"></div>
<div class="flex flex-col md:flex-row justify-between gap-4">
<div>
<p class="font-label-md text-on-surface-variant mb-1 font-bold">V2.4.0 - Superseded</p>
<p class="font-editorial text-xl text-on-surface">Delaware Gen. Corp. Law § 102</p>
<p class="text-body-md text-on-surface-variant mt-2">Previous standard liability protections strictly covering corporate board directors. Replaced following new state amendment.</p>
</div>
<div class="text-right">
<p class="text-xs text-on-surface-variant uppercase tracking-widest" data-i18n="citations.history.verified_by_label">Verified By</p>
<p class="font-label-md text-on-surface font-semibold">System Root Admin</p>
<p class="text-xs text-on-surface-variant mt-1">2023-04-10 14:00 UTC</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Loading telemetry spinner -->
<div class="flex flex-col items-center gap-4 py-20 text-on-surface-variant" x-show="loading">
<div class="spinner-ring"></div>
<p class="font-body text-sm" data-i18n="citations.loading.message">Loading citation database…</p>
</div>
<!-- Toasts notification system -->
<div class="toast-container font-body">
<template x-for="t in toasts">
<div class="toast">
<svg viewBox="0 0 24 24" width="16" height="16" stroke="#039855" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"/>
</svg>
<span x-text="t.message"></span>
</div>
</template>
</div>
</main>
</div>
<script>
function citationsDashboard() {
return {
loading: true,
citations: [],
toasts: [],
filters: { search: '', jurisdiction: 'all', status: 'all' },
activeTab: 'queue',
async load() {
try {
const resp = await fetch('/api/v1/citations');
if (resp.status === 401) { window.location.href = '/login'; return; }
this.citations = await resp.json();
this.loading = false;
} catch (e) {
console.error("Failed to load citations:", e);
this.loading = false;
}
},
filteredCitations() {
return this.citations.filter(c => {
const term = this.filters.search.toLowerCase().trim();
const matchesSearch = !term ||
c.finding_id.toLowerCase().includes(term) ||
(c.article || '').toLowerCase().includes(term) ||
(c.source || '').toLowerCase().includes(term) ||
(c.note || '').toLowerCase().includes(term);
const matchesJuris = this.filters.jurisdiction === 'all' || c.jurisdiction === this.filters.jurisdiction;
const matchesStatus = this.filters.status === 'all' || c.status === this.filters.status;
return matchesSearch && matchesJuris && matchesStatus;
});
},
async verifyCitation(citation) {
try {
const resp = await fetch('/api/v1/citations/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ finding_id: citation.finding_id, jurisdiction: citation.jurisdiction }),
});
if (resp.ok) {
citation.status = 'verified';
this.showToast(`Verified: ${citation.finding_id}/${citation.jurisdiction}`);
console.log(`Successfully verified citation: ${citation.finding_id}/${citation.jurisdiction}`);
} else {
const err = await resp.json();
alert(`Error: ${err.error}`);
}
} catch (e) {
console.error("Verification failed:", e);
alert("Network error occurred during verification.");
}
},
showToast(message) {
const id = Date.now();
this.toasts.push({ id, message });
setTimeout(() => { this.toasts = this.toasts.filter(t => t.id !== id); }, 3000);
}
}
}
function toggleSidebar() {
document.getElementById('sidebar').classList.toggle('-translate-x-full');
document.getElementById('sidebar-overlay').classList.toggle('hidden');
}
// Close the mobile drawer after choosing a destination
document.getElementById('sidebar').addEventListener('click', (e) => {
if (window.innerWidth < 1024 && e.target.closest('a,button')
&& !document.getElementById('sidebar').classList.contains('-translate-x-full')) {
toggleSidebar();
}
});
</script>
</body>
</html>