| <!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; |
| } |
| |
| |
| .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> |
|
|
| |
| <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> |
|
|
| |
| <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"> |
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <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()"> |
|
|
| |
| <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> |
|
|
| |
| <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 & 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 & Versioning</button> |
| </div> |
|
|
| |
| <div class="space-y-6" x-show="activeTab === 'queue'"> |
| |
| <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> |
|
|
| |
| <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 & Wales (EN&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> |
|
|
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <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'); |
| } |
| |
| 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> |
|
|