From b0cfcf52c942e1279020d30c70aad2f19534d038 Mon Sep 17 00:00:00 2001 From: Damodar Dahal Date: Fri, 26 Jun 2026 03:37:51 +0000 Subject: [PATCH 1/2] feat: adapt index consumption to NGM Index v2 consolidated manuscripts The backend now emits one consolidated manuscript per logical document with a roled `links` array (RAW/ALTERNATE/SOURCE_PAGE/MARKDOWN), a `document_id`, and a `source_type`; the old `url`/`file_name` remain as a back-compat alias. - Extend the Manuscript type with links/document_id/source_type and add fileLinks/linkByRole/primaryUrl/extFromUrl helpers that fall back to the legacy `url` (so old index snapshots still render during rollout). - Press releases: drop the client-side group-by-press_id (the backend now consolidates); render each release's attachments from `links`, plus a transcript (MARKDOWN) and Source (SOURCE_PAGE) link when present. De-dup by press_id is kept only to tolerate legacy snapshots. - Court orders: one row per case, rendering all its file links. - Kanun Patrika / CIAA reports: read links via primaryUrl. Typecheck + lint clean (only pre-existing exhaustive-deps warnings). Co-Authored-By: Claude Opus 4.8 --- src/components/IndexViewer.tsx | 170 +++++++++++++++++++++++---------- 1 file changed, 117 insertions(+), 53 deletions(-) diff --git a/src/components/IndexViewer.tsx b/src/components/IndexViewer.tsx index 61b00a1..9eb53de 100644 --- a/src/components/IndexViewer.tsx +++ b/src/components/IndexViewer.tsx @@ -32,10 +32,18 @@ const TABLE_CONFIG = { } as const; // NGM Index v2.0 types - Tree-based hierarchical index +type LinkRole = 'RAW' | 'ALTERNATE' | 'SOURCE_PAGE' | 'MARKDOWN' | 'PERMALINK'; +type SourceLink = { link: string; role: LinkRole }; type Manuscript = { - url: string; + url: string; // back-compat alias for the primary RAW file file_name: string; metadata: Record; + // NGM Index v2: one logical document per manuscript, carrying roled links. + // `links`/`document_id`/`source_type` are absent on legacy index snapshots, + // so all readers fall back to `url` (see fileLinks/primaryUrl below). + links?: SourceLink[]; + document_id?: string; + source_type?: string; }; type IndexNodeStub = { @@ -87,6 +95,31 @@ function getProxiedUrl(url: string): string { return url; } +/** Downloadable file links (RAW first, then ALTERNATE). Falls back to the legacy + * single `url` when an older index snapshot has no `links`. */ +function fileLinks(m: Manuscript): SourceLink[] { + const roled = (m.links ?? []).filter((l) => l.role === 'RAW' || l.role === 'ALTERNATE'); + if (roled.length > 0) { + return [...roled].sort((a, b) => Number(b.role === 'RAW') - Number(a.role === 'RAW')); + } + return m.url ? [{ link: m.url, role: 'RAW' }] : []; +} + +/** First link with the given role, if any (e.g. SOURCE_PAGE, MARKDOWN). */ +function linkByRole(m: Manuscript, role: LinkRole): string | undefined { + return m.links?.find((l) => l.role === role)?.link; +} + +/** Primary downloadable URL (RAW), with legacy `url` fallback. */ +function primaryUrl(m: Manuscript): string { + return fileLinks(m)[0]?.link ?? m.url ?? ''; +} + +/** Uppercase file extension parsed from a URL (e.g. "PDF", "DOCX"). */ +function extFromUrl(u: string): string { + return u.match(/\.([A-Za-z0-9]+)(?:[?#]|$)/)?.[1]?.toUpperCase() || 'FILE'; +} + /** Fetch all manuscripts for a node, following pagination via `next` links. */ async function fetchAllManuscripts( ref: string, @@ -620,7 +653,7 @@ export default function IndexViewer() { id: index + 1, fileName: item.file_name.replace('.pdf', ''), year: extractYear(item.file_name) || 'N/A', - url: item.url, + url: primaryUrl(item), })); // Define columns @@ -729,7 +762,7 @@ export default function IndexViewer() { serialNumber: meta?.serial_number || 'N/A', title: meta?.title || item.file_name, date: meta?.date || 'Unknown Date', - url: item.url, + url: primaryUrl(item), }; }); @@ -805,32 +838,37 @@ export default function IndexViewer() { const items = manuscripts.press || []; if (items.length === 0) return

No records found for CIAA Press Releases.

; - // Group manuscripts by press_id - const grouped = new Map; files: Manuscript[] }>(); + // NGM Index v2 emits ONE consolidated manuscript per press release (its + // attachments are in `links`), so no client-side grouping is needed. + // De-duplicate by press_id anyway to tolerate legacy snapshots that still + // emit one manuscript per attachment, merging their links. + const byPressId = new Map(); for (const item of items) { const parsed = Number(item.metadata?.press_id); const hasValidPressId = Number.isFinite(parsed) && parsed > 0; - const groupKey = hasValidPressId ? `press:${parsed}` : `file:${item.url}`; - if (!grouped.has(groupKey)) { - grouped.set(groupKey, { - pressId: hasValidPressId ? parsed : null, - meta: item.metadata, - files: [], - }); + const key = hasValidPressId ? `press:${parsed}` : `file:${item.url}`; + const existing = byPressId.get(key); + if (!existing) { + byPressId.set(key, { ...item }); + } else { + existing.links = [...fileLinks(existing), ...fileLinks(item)]; } - grouped.get(groupKey)!.files.push(item); } - // Transform data for table - const tableData = [...grouped.entries()] - .sort(([, a], [, b]) => (b.pressId ?? -Infinity) - (a.pressId ?? -Infinity)) - .map(([, { pressId, meta, files }]) => ({ - pressId: pressId ?? 0, - title: String(meta?.title || `Press Release ${pressId ? `#${pressId}` : '(Unknown)'}`), - date: String(meta?.publication_date || 'N/A'), - fileCount: files.length, - files: files, - })); + // Transform data for table — one row per release. + const tableData = [...byPressId.values()] + .map((item) => { + const meta = item.metadata as Record; + const parsed = Number(meta?.press_id); + const pressId = Number.isFinite(parsed) && parsed > 0 ? parsed : 0; + return { + pressId, + title: String(meta?.title || `Press Release ${pressId ? `#${pressId}` : '(Unknown)'}`), + date: String(meta?.publication_date || 'N/A'), + item, + }; + }) + .sort((a, b) => b.pressId - a.pressId); // Define columns const columns: ColumnDef[] = [ @@ -855,25 +893,43 @@ export default function IndexViewer() { size: TABLE_CONFIG.COLUMN_SIZE_XXXLARGE, enableColumnFilter: false, enableSorting: false, - cell: (info) => ( - + ); + }, }, ]; @@ -1167,7 +1223,7 @@ export default function IndexViewer() { caseNumber, year, fileName: item.file_name, - url: item.url, + item, }; }); @@ -1184,7 +1240,7 @@ export default function IndexViewer() { header: 'Case Number', size: TABLE_CONFIG.COLUMN_SIZE_XXLARGE, cell: (info) => ( - + {info.getValue() as string} ), @@ -1213,17 +1269,25 @@ export default function IndexViewer() { enableColumnFilter: false, enableSorting: false, cell: (info) => { - const ext = info.row.original.fileName.match(/\.([A-Za-z0-9]+)$/)?.[1]?.toUpperCase() || 'FILE'; + const files = fileLinks(info.row.original.item); return ( - - View {ext} - +
+ {files.map((f, i) => { + const ext = extFromUrl(f.link); + return ( + + {ext}{files.length > 1 ? ` ${i + 1}` : ''} + + ); + })} +
); }, }, From 75e8534d4f3f6c6687a49f92340fe9524d481129 Mon Sep 17 00:00:00 2001 From: Damodar Dahal Date: Fri, 26 Jun 2026 03:51:29 +0000 Subject: [PATCH 2/2] feat(brand): rebrand NGM to the Jawafdehi identity + simplify IA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Align ngm.jawafdehi.org with the Jawafdehi brand and streamline the experience. Brand design system: - index.css tokens recolored to brand navy #0E1F3A / crimson #B5242C / warm white, brand font stack (Helvetica/Arial/Noto Sans Devanagari), navy-tinted shadows. - Replace every generic-blue literal (App.css + inline styles across the pages and components) with brand tokens; hero gradient is navy -> crimson. Identity: - Navy header with the Jawafdehi wordmark (logo-dark.svg) + an "NGM · Nepal Governance Modernization" tag, crimson active-nav underline, and a brand footer ("A project of Jawafdehi", CC BY-NC 4.0). - Landing rebuilt: brand hero + eyebrow, FAQ-aligned About copy, clear entry cards. IA simplification: - Drop the Archive's redundant "CIAA Cases Dataset" and "Court Cases" tabs (they duplicated the dedicated /dataset and /search pages); the Archive is now the document datasets only. Nav: Home · Court Cases · Archive · CIAA Cases · Status. Typecheck + production build clean; lint 0 errors (only pre-existing deps warnings). Co-Authored-By: Claude Opus 4.8 --- src/App.css | 206 +++++++++++++++++---------- src/components/CIAADatasetViewer.tsx | 6 +- src/components/CourtCaseSearch.tsx | 26 ++-- src/components/IndexViewer.tsx | 57 ++------ src/components/Layout.tsx | 76 ++++++---- src/index.css | 46 ++++-- src/pages/CaseDetailPage.tsx | 12 +- src/pages/LandingPage.tsx | 121 ++++++++-------- 8 files changed, 302 insertions(+), 248 deletions(-) diff --git a/src/App.css b/src/App.css index 8fe096b..106307f 100644 --- a/src/App.css +++ b/src/App.css @@ -9,62 +9,75 @@ position: sticky; top: 0; z-index: 50; - background: var(--bg-card); - border-bottom: 1px solid var(--border); - backdrop-filter: blur(8px); - -webkit-backdrop-filter: blur(8px); -} - -.app-header.glass { - background: rgba(var(--bg-card), 0.8); + background: var(--navy); + border-bottom: 3px solid var(--crimson); } .header-content { max-width: 1200px; margin: 0 auto; - padding: 1rem 1.5rem; + padding: 0.8rem 1.5rem; display: flex; justify-content: space-between; align-items: center; + gap: 1rem; } -.logo-group { +/* Brand lockup: Jawafdehi wordmark + NGM tag */ +.brand { display: flex; align-items: center; - gap: 1rem; + gap: 0.75rem; + text-decoration: none; + flex-shrink: 0; } -.logo-icon { - width: 36px; - height: 36px; - flex-shrink: 0; +.brand-wordmark { + height: 28px; + width: auto; + display: block; } -.logo { - font-size: 1.25rem; +.brand-sep { + width: 1px; + height: 26px; + background: rgba(255, 253, 247, 0.25); +} + +.brand-sub { + display: flex; + flex-direction: column; + line-height: 1.15; +} + +.brand-tag { + font-size: 0.95rem; font-weight: 700; - letter-spacing: -0.025em; - color: var(--text-primary); + letter-spacing: 0.05em; + color: var(--warm-white); } -.version-pill { - font-size: 0.75rem; - font-weight: 600; - padding: 0.125rem 0.5rem; - background: var(--border); - color: var(--text-secondary); - border-radius: 9999px; +.brand-tagline { + font-size: 0.6rem; + letter-spacing: 0.07em; + text-transform: uppercase; + color: rgba(255, 253, 247, 0.6); } .nav-link { - margin-left: 1.5rem; + margin-left: 1.25rem; font-size: 0.875rem; font-weight: 500; - color: var(--text-secondary); + color: rgba(255, 253, 247, 0.78); + white-space: nowrap; } .nav-link:hover { - color: var(--text-primary); + color: var(--warm-white); +} + +.nav-parent { + color: rgba(255, 253, 247, 0.55); } /* Main Content */ @@ -84,12 +97,21 @@ padding: 3rem 0; } +.hero-eyebrow { + text-transform: uppercase; + letter-spacing: 0.12em; + font-size: 0.72rem; + font-weight: 700; + color: var(--crimson); + margin-bottom: 0.85rem; +} + .hero h2 { font-size: 2.5rem; font-weight: 800; letter-spacing: -0.025em; margin-bottom: 1rem; - background: linear-gradient(135deg, #1d4ed8 0%, #dc2626 100%); + background: linear-gradient(135deg, var(--navy) 0%, var(--crimson) 100%); background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; @@ -119,7 +141,7 @@ .tabs { display: flex; border-bottom: 1px solid var(--border); - background: #dbeafe; + background: var(--bg-tint); padding: 0 1rem; } @@ -128,7 +150,7 @@ background: transparent; border: none; border-bottom: 2px solid transparent; - color: #475569; + color: var(--text-secondary); font-size: 0.875rem; font-weight: 600; cursor: pointer; @@ -136,8 +158,8 @@ } .tab-btn:hover { - color: #1e293b; - background: #bfdbfe; + color: var(--text-primary); + background: var(--border-cool); } .tab-btn.active { @@ -247,9 +269,9 @@ } .badge.info { - background: #dbeafe; - color: #1e40af; - border-color: #bfdbfe; + background: var(--bg-tint); + color: var(--accent); + border-color: var(--border-cool); } @@ -355,12 +377,38 @@ /* Footer */ .app-footer { + margin-top: 2rem; + padding: 2.5rem 1.5rem; + background: var(--navy); + color: rgba(255, 253, 247, 0.7); + border-top: 3px solid var(--crimson); +} + +.footer-inner { + max-width: 1200px; + margin: 0 auto; text-align: center; - padding: 2rem; - color: var(--text-secondary); + display: flex; + flex-direction: column; + gap: 0.4rem; font-size: 0.875rem; - border-top: 1px solid var(--border); - background: var(--bg-main); +} + +.footer-brand { + font-weight: 700; + font-size: 1rem; + color: var(--warm-white); + letter-spacing: 0.03em; +} + +.footer-meta { + font-size: 0.78rem; + color: rgba(255, 253, 247, 0.5); +} + +.app-footer a { + color: var(--warm-white); + text-decoration: underline; } /* Mobile Responsive Styles */ @@ -682,7 +730,7 @@ .filter-select:focus { outline: none; border-color: var(--accent); - box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1); + box-shadow: 0 0 0 3px var(--accent-soft); } .filter-clear { @@ -740,9 +788,9 @@ } .file-chip.doc { - background: #eff6ff; - color: #1e40af; - border-color: #bfdbfe; + background: var(--bg-tint); + color: var(--accent); + border-color: var(--border-cool); } .file-chip.default { @@ -768,8 +816,8 @@ align-items: center; gap: 1rem; padding: 0.75rem 1rem; - background: #e0e7ff; - border-bottom: 1px solid #bfdbfe; + background: var(--bg-tint); + border-bottom: 1px solid var(--border-cool); border-radius: 12px 12px 0 0; } @@ -794,8 +842,8 @@ flex: 1; padding: 0.75rem 2.5rem 0.75rem 2.5rem; background: #ffffff; - color: #1e293b; - border: 1px solid #bfdbfe; + color: var(--text-primary); + border: 1px solid var(--border-cool); border-radius: 0.5rem; font-size: 0.875rem; transition: all 0.2s; @@ -804,7 +852,7 @@ .table-search:focus { outline: none; border-color: var(--accent); - box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1); + box-shadow: 0 0 0 3px var(--accent-soft); } .clear-search { @@ -828,7 +876,7 @@ .table-info { font-size: 0.875rem; - color: #1e293b; + color: var(--text-primary); white-space: nowrap; font-weight: 600; } @@ -853,7 +901,7 @@ } .data-table thead { - background: #e0e7ff; + background: var(--bg-tint); position: sticky; top: 0; z-index: 10; @@ -864,8 +912,8 @@ text-align: left; font-size: 0.8rem; font-weight: 600; - color: #1e293b; - border-bottom: 2px solid #bfdbfe; + color: var(--text-primary); + border-bottom: 2px solid var(--border-cool); white-space: nowrap; } @@ -873,8 +921,8 @@ padding: 0.625rem 0.75rem; font-size: 0.8rem; font-weight: 600; - color: #1e293b; - border-bottom: 1px solid #cbd5e1; + color: var(--text-primary); + border-bottom: 1px solid var(--border-cool); } .data-table tbody tr { @@ -883,7 +931,7 @@ } .data-table tbody tr:hover { - background: #e0e7ff; + background: var(--bg-tint); } .data-table tbody tr:last-child td { @@ -936,7 +984,7 @@ .filter-input:focus { outline: none; border-color: var(--accent); - box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1); + box-shadow: 0 0 0 2px var(--accent-soft); } /* Table Pagination - Compact */ @@ -945,8 +993,8 @@ justify-content: space-between; align-items: center; padding: 0.75rem 1rem; - background: #e0e7ff; - border-top: 1px solid #bfdbfe; + background: var(--bg-tint); + border-top: 1px solid var(--border-cool); border-radius: 0 0 12px 12px; gap: 1rem; } @@ -960,8 +1008,8 @@ .table-pagination .pagination-btn { padding: 0.375rem 0.75rem; background: #ffffff; - color: #1e293b; - border: 1px solid #bfdbfe; + color: var(--text-primary); + border: 1px solid var(--border-cool); border-radius: var(--radius-md); font-size: 0.8rem; font-weight: 500; @@ -985,7 +1033,7 @@ .table-pagination .pagination-info { font-size: 0.8rem; font-weight: 600; - color: #1e293b; + color: var(--text-primary); padding: 0 0.375rem; white-space: nowrap; } @@ -1001,7 +1049,7 @@ align-items: center; gap: 0.375rem; font-size: 0.8rem; - color: #1e293b; + color: var(--text-primary); font-weight: 600; white-space: nowrap; } @@ -1009,8 +1057,8 @@ .page-size-select { padding: 0.25rem 0.375rem; background: #ffffff; - color: #1e293b; - border: 1px solid #bfdbfe; + color: var(--text-primary); + border: 1px solid var(--border-cool); border-radius: var(--radius-md); font-size: 0.8rem; cursor: pointer; @@ -1024,7 +1072,7 @@ .page-size-select:focus { outline: none; border-color: var(--accent); - box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1); + box-shadow: 0 0 0 3px var(--accent-soft); } /* Link Styling in Table */ @@ -1171,7 +1219,7 @@ padding: 0.625rem 2.5rem 0.625rem 0.75rem; background: #ffffff; color: var(--text-primary); - border: 1px solid #bfdbfe; + border: 1px solid var(--border-cool); border-radius: var(--radius-md); font-size: 0.875rem; transition: all 0.2s; @@ -1180,7 +1228,7 @@ .advanced-search-input:focus { outline: none; border-color: var(--accent); - box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1); + box-shadow: 0 0 0 3px var(--accent-soft); } .advanced-search-field .clear-search { @@ -1211,8 +1259,10 @@ /* === Navigation active state === */ .nav-link.active { - color: var(--accent); + color: var(--warm-white); font-weight: 700; + border-bottom: 2px solid var(--crimson); + padding-bottom: 3px; } /* === Header nav container === */ @@ -1382,9 +1432,9 @@ } .status-badge.info { - background: #dbeafe; - color: #1e40af; - border: 1px solid #bfdbfe; + background: var(--bg-tint); + color: var(--accent); + border: 1px solid var(--border-cool); } .status-card h4 { @@ -1428,16 +1478,16 @@ font-weight: 600; padding: 0.15rem 0.5rem; border-radius: 4px; - background: #eff6ff; - color: #1e40af; - border: 1px solid #bfdbfe; - font-family: 'Inter', monospace; + background: var(--bg-tint); + color: var(--accent); + border: 1px solid var(--border-cool); + font-family: ui-monospace, 'SF Mono', monospace; } /* === Status Timestamps Note === */ .status-timestamps-note { background: #f8fafc; - border: 1px dashed #cbd5e1; + border: 1px dashed var(--border-cool); border-radius: var(--radius-md); padding: 1.25rem; } diff --git a/src/components/CIAADatasetViewer.tsx b/src/components/CIAADatasetViewer.tsx index fbcbff9..23673c8 100644 --- a/src/components/CIAADatasetViewer.tsx +++ b/src/components/CIAADatasetViewer.tsx @@ -257,7 +257,7 @@ export default function CIAADatasetViewer() { target="_blank" rel="noopener noreferrer" onClick={(e) => e.stopPropagation()} - style={{ color: '#2563eb', textDecoration: 'none', fontWeight: 500 }} + style={{ color: 'var(--navy)', textDecoration: 'none', fontWeight: 500 }} > {info.getValue() as string} @@ -420,7 +420,7 @@ export default function CIAADatasetViewer() {
⚖️ Plaintiffs ({row.plaintiffs.length})
{row.plaintiffs.map((p, idx) => ( - {p.name} + {p.name} ))}
@@ -461,7 +461,7 @@ export default function CIAADatasetViewer() {
⚖️ Plaintiffs ({row.appealed_case.plaintiffs.length})
{row.appealed_case.plaintiffs.map((p, idx) => ( - {p.name} + {p.name} ))}
diff --git a/src/components/CourtCaseSearch.tsx b/src/components/CourtCaseSearch.tsx index bd7ab28..60c98db 100644 --- a/src/components/CourtCaseSearch.tsx +++ b/src/components/CourtCaseSearch.tsx @@ -226,9 +226,9 @@ export default function CourtCaseSearch() { background: '#f0f4ff', borderRadius: '12px', marginBottom: '1.5rem', - border: '2px solid #bfdbfe' + border: '2px solid var(--border-cool)' }}> -

+

🔍 Search Court Cases

@@ -239,7 +239,7 @@ export default function CourtCaseSearch() { htmlFor="court-select" style={{ display: 'block', - color: '#1e40af', + color: 'var(--navy)', marginBottom: '0.5rem', fontSize: '0.9rem', fontWeight: 600 @@ -262,7 +262,7 @@ export default function CourtCaseSearch() { width: '100%', padding: '0.6rem', borderRadius: '6px', - border: '2px solid #bfdbfe', + border: '2px solid var(--border-cool)', background: '#ffffff', fontSize: '0.95rem', cursor: 'pointer' @@ -286,7 +286,7 @@ export default function CourtCaseSearch() { htmlFor="case-number" style={{ display: 'block', - color: '#1e40af', + color: 'var(--navy)', marginBottom: '0.5rem', fontSize: '0.9rem', fontWeight: 600 @@ -304,7 +304,7 @@ export default function CourtCaseSearch() { width: '100%', padding: '0.6rem', borderRadius: '6px', - border: '2px solid #bfdbfe', + border: '2px solid var(--border-cool)', background: '#ffffff', fontSize: '0.95rem' }} @@ -319,7 +319,7 @@ export default function CourtCaseSearch() { padding: '0.6rem 1.5rem', borderRadius: '6px', border: 'none', - background: !loading && caseNumber.trim() ? '#3b82f6' : '#cbd5e1', + background: !loading && caseNumber.trim() ? 'var(--navy)' : 'var(--border-cool)', color: 'white', fontWeight: 600, cursor: !loading && caseNumber.trim() ? 'pointer' : 'not-allowed', @@ -396,7 +396,7 @@ export default function CourtCaseSearch() { {/* Entities (Parties) */} {caseData.entities && caseData.entities.length > 0 && (
-

+

👥 Parties Involved

@@ -416,19 +416,19 @@ export default function CourtCaseSearch() { {/* Hearings */} {caseData.hearings && caseData.hearings.length > 0 && (
-

+

🗓 Hearing History ({caseData.hearings.length} hearings)

{caseData.hearings.map((hearing) => ( -
+
- {hearing.hearing_date_bs} + {hearing.hearing_date_bs} {hearing.hearing_date_ad && ({hearing.hearing_date_ad})}
{hearing.case_status && ( - + {hearing.case_status} )} @@ -458,7 +458,7 @@ export default function CourtCaseSearch() {
diff --git a/src/components/IndexViewer.tsx b/src/components/IndexViewer.tsx index 9eb53de..58513bf 100644 --- a/src/components/IndexViewer.tsx +++ b/src/components/IndexViewer.tsx @@ -1,8 +1,6 @@ import { useState, useEffect, useCallback, useRef } from 'react'; import type { ColumnDef } from '@tanstack/react-table'; import { DataTable } from './DataTable'; -import CIAADatasetViewer from './CIAADatasetViewer'; -import CourtCaseSearch from './CourtCaseSearch'; // TODO: Replace with backend metadata extraction when persons data is available import { containsPersonName } from '../data/casesData'; @@ -312,7 +310,6 @@ export default function IndexViewer() { const [rootError, setRootError] = useState(null); const [tabErrors, setTabErrors] = useState>({ kanun: null, ciaa: null, press: null, court: null, dataset: null, courtcases: null }); const [activeTab, setActiveTab] = useState('kanun'); - const [hasVisitedDataset, setHasVisitedDataset] = useState(false); const loadingRef = useRef>(new Set()); const abortControllersRef = useRef>(new Map()); const hasAttemptedLoadRef = useRef>(new Set()); @@ -1064,12 +1061,12 @@ export default function IndexViewer() { background: '#f0f4ff', borderRadius: '12px', marginBottom: '1.5rem', - border: '2px solid #bfdbfe' + border: '2px solid var(--border-cool)' }}> -

🔍 Filter Court Records

+

🔍 Filter Court Records

-
-