Sync cezen-portal/ from live working copy (V1.0 GA polish + search-icon ordering fix)

Brings the ISO-packaged portal copy current with the live cezen-portal/
working directory, which had drifted across the entire V1.0 GA polish
initiative (Phases 1-10) plus this session's topnav icon-order bugfix.

- 52 existing files updated, 2 new pages added (about.html, health.html)
- cgit/cezen-portal/ is now byte-identical to cezen-portal/
- Backend files (rag_ingest.py, cezen_license.py) already in sync;
  main.py intentionally left as-is (cgit is ahead there with
  workstation-category commercial-fit logic not yet backported to the
  live working copy)

Next: rebuild the ISO (bash autoinstall/build-iso.sh) to bake this in.
This commit is contained in:
Jino Jose committed 2026-07-09 11:11:38 +05:30
1 parent 7540da04a8
commit b3eec34158
57 files changed
+2832 -1354

No files matched your search

+31 -25
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RAG Quality Dashboard — Nexus One AI</title>
<link rel="stylesheet" href="style.css?v=4">
<link rel="stylesheet" href="style.css?v=12">
<style>
.rq-content { max-width:1100px; margin:0 auto; padding:32px 40px 60px; }
@media(max-width:768px){ .rq-content { padding:20px 18px 48px; } }
@@ -128,6 +128,7 @@
<a href="dashboard.html">Dashboard</a>
<a href="analytics.html">Usage Analytics</a>
<a href="audit.html">Governance & Audit</a>
<a href="health.html">Health Center</a>
<a href="feedback.html">Feedback &amp; Ratings</a>
<span class="nav-drop-cat">MANAGE /</span>
<a href="users.html">Users</a>
@@ -167,7 +168,7 @@
</div>
</div>
</nav>
<a href="notifications.html" style="position:relative">🔔</a>
<a href="notifications.html" class="nav-icon-btn" style="position:relative" aria-label="Notifications"><span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M15 17h5l-1.4-1.4A2 2 0 0 1 18 14.2V11a6 6 0 1 0-12 0v3.2a2 2 0 0 1-.6 1.4L4 17h5"/><path d="M10 17a2 2 0 0 0 4 0"/></svg></span></a>
<span class="badge" data-brand="tier">Basic Tier</span>
<div id="nav-org-logo" class="nav-org-logo"></div>
</header>
@@ -192,7 +193,7 @@
<option value="30" selected>Last 30 days</option>
<option value="90">Last 90 days</option>
</select>
<button class="rq-refresh-btn" onclick="refreshAll()">↻ Refresh</button>
<button class="rq-refresh-btn" onclick="refreshAll()">Refresh</button>
</div>
</div>
</div>
@@ -207,7 +208,7 @@
<div style="min-width:0">
<div class="rq-card">
<div class="rq-card-header">
<span style="font-size:18px">📚</span>
<span style="font-size:18px">KB</span>
<div class="rq-card-title">Collection Quality Breakdown</div>
</div>
<div class="rq-card-body">
@@ -225,7 +226,7 @@
<!-- Trend chart -->
<div class="rq-card">
<div class="rq-card-header">
<span style="font-size:18px">📈</span>
<span style="font-size:18px">Trend</span>
<div class="rq-card-title">Hit Rate Trend</div>
</div>
<div class="rq-card-body">
@@ -242,7 +243,7 @@
<!-- Failed questions -->
<div class="rq-card" style="margin-bottom:20px">
<div class="rq-card-header">
<span style="font-size:18px">❌</span>
<span style="font-size:18px">Fail</span>
<div class="rq-card-title">Failed &amp; Unanswered Questions</div>
<span style="font-size:12px;color:var(--lt)" id="fail-count-label"></span>
</div>
@@ -256,7 +257,7 @@
<!-- Document health warnings -->
<div class="rq-card">
<div class="rq-card-header">
<span style="font-size:18px">⚠️</span>
<span style="font-size:18px">Warn</span>
<div class="rq-card-title">Knowledge Base Health Warnings</div>
<span style="font-size:12px;color:var(--lt)" id="warn-count-label"></span>
</div>
@@ -309,12 +310,12 @@ function mockFailed() {
function mockWarnings() {
return [
{ type:'stale', icon:'⏰', title:'IT Procedures — 3 documents older than 30 days', sub:'it-procedures: "VPN Setup Guide v1.2" (47 days), "Email Config 2023" (38 days), "Server Naming Convention" (31 days)', action:'Review' },
{ type:'stale', icon:'⏰', title:'Finance Docs — "GST Circular Q2-2023" may be superseded', sub:'finance-docs: Document dated 14 months ago. A newer version may exist.', action:'Review' },
{ type:'dup', icon:'♊', title:'Duplicate content detected in tender-archive', sub:'3 document pairs share >85% content overlap. This inflates chunk count and confuses retrieval.', action:'Deduplicate' },
{ type:'dup', icon:'♊', title:'HR Policy — "Leave Policy v2" and "Leave Policy v2.1" both indexed', sub:'hr-policy: Both versions are active. Remove older version to avoid conflicting answers.', action:'Remove old' },
{ type:'conflict', icon:'⚡', title:'Conflicting information: Remote Work Policy', sub:'hr-policy contains two documents with contradictory WFH day limits (2 days vs 3 days per week).', action:'Resolve' },
{ type:'stale', icon:'⏰', title:'Product Catalog — 2 items with no update in 60+ days', sub:'product-catalog: Items may reflect discontinued or repriced products.', action:'Review' },
{ type:'stale', title:'IT Procedures — 3 documents older than 30 days', sub:'it-procedures: "VPN Setup Guide v1.2" (47 days), "Email Config 2023" (38 days), "Server Naming Convention" (31 days)', action:'Review' },
{ type:'stale', title:'Finance Docs — "GST Circular Q2-2023" may be superseded', sub:'finance-docs: Document dated 14 months ago. A newer version may exist.', action:'Review' },
{ type:'dup', title:'Duplicate content detected in tender-archive', sub:'3 document pairs share >85% content overlap. This inflates chunk count and confuses retrieval.', action:'Deduplicate' },
{ type:'dup', title:'HR Policy — "Leave Policy v2" and "Leave Policy v2.1" both indexed', sub:'hr-policy: Both versions are active. Remove older version to avoid conflicting answers.', action:'Remove old' },
{ type:'conflict', title:'Conflicting information: Remote Work Policy', sub:'hr-policy contains two documents with contradictory WFH day limits (2 days vs 3 days per week).', action:'Resolve' },
{ type:'stale', title:'Product Catalog — 2 items with no update in 60+ days', sub:'product-catalog: Items may reflect discontinued or repriced products.', action:'Review' },
];
}
@@ -327,10 +328,10 @@ function mockTrend() {
function renderMetrics(m) {
const row = document.getElementById('metrics-row');
row.innerHTML = `
${metricCard('hit','📡','Retrieval Hit Rate', m.hit_rate+'%', 'Queries with at least one relevant chunk returned', m.hit_rate_delta, 'purple')}
${metricCard('ground','✅','Answer Groundedness', m.groundedness+'%', 'Answers traceable to a retrieved source', m.ground_delta, 'green')}
${metricCard('fail','❌','Failed Queries', m.failed_queries, 'No usable chunks retrieved in the period', m.fail_delta, 'amber', true)}
${metricCard('stale','⏰','Stale Documents', m.stale_docs, 'Documents older than 30 days or possibly superseded', m.stale_delta, 'red', true)}
${metricCard('hit','Hit','Retrieval Hit Rate', m.hit_rate+'%', 'Queries with at least one relevant chunk returned', m.hit_rate_delta, 'purple')}
${metricCard('ground','Ground','Answer Groundedness', m.groundedness+'%', 'Answers traceable to a retrieved source', m.ground_delta, 'green')}
${metricCard('fail','Fail','Failed Queries', m.failed_queries, 'No usable chunks retrieved in the period', m.fail_delta, 'amber', true)}
${metricCard('stale','Stale','Stale Documents', m.stale_docs, 'Documents older than 30 days or possibly superseded', m.stale_delta, 'red', true)}
`;
}
@@ -338,7 +339,7 @@ function metricCard(cls, icon, label, val, sub, delta, color, invertDelta = fals
const isUp = delta > 0;
const isGood = invertDelta ? !isUp : isUp;
const trendCls= delta === 0 ? 'flat' : isGood ? 'up' : 'down';
const trendStr= delta === 0 ? '→ no change' : (delta > 0 ? '▲ +' : '▼ ') + Math.abs(delta) + (typeof val === 'string' ? 'pp' : '');
const trendStr= delta === 0 ? 'No change' : (delta > 0 ? '+ ' : '- ') + Math.abs(delta) + (typeof val === 'string' ? 'pp' : '');
return `
<div class="rq-metric ${cls}">
<div class="rq-metric-label">${icon} ${label}</div>
@@ -370,7 +371,7 @@ function renderCollections(cols) {
document.getElementById('coll-table').innerHTML = show.map(c => {
const barCls = c.hit_rate >= 80 ? '' : c.hit_rate >= 65 ? 'amber' : 'red';
const pillCls= c.status === 'good' ? 'good' : c.status === 'warn' ? 'warn' : 'bad';
const pillLbl= c.status === 'good' ? '✓ Healthy' : c.status === 'warn' ? '⚠ Warning' : '✕ Critical';
const pillLbl= c.status === 'good' ? 'Healthy' : c.status === 'warn' ? 'Warning' : 'Critical';
const fresh = c.freshness_days === 0 ? 'Today' : c.freshness_days + 'd ago';
return `
<div class="rq-coll-row">
@@ -440,7 +441,7 @@ function renderTrend(points) {
function renderFailed(fails) {
document.getElementById('fail-count-label').textContent = `${fails.length} queries in period`;
if (!fails.length) {
document.getElementById('fail-table').innerHTML = '<div class="rq-empty">✓ No failed queries in this period</div>';
document.getElementById('fail-table').innerHTML = '<div class="rq-empty">No failed queries in this period</div>';
return;
}
document.getElementById('fail-table').innerHTML = `
@@ -465,12 +466,17 @@ function renderFailed(fails) {
function renderWarnings(warns) {
document.getElementById('warn-count-label').textContent = `${warns.length} issue${warns.length !== 1 ? 's' : ''} detected`;
if (!warns.length) {
document.getElementById('warn-list').innerHTML = '<div class="rq-empty">✓ All documents look healthy</div>';
document.getElementById('warn-list').innerHTML = '<div class="rq-empty">All documents look healthy</div>';
return;
}
const warnIcons = {
stale: '<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>',
dup: '<svg viewBox="0 0 24 24"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>',
conflict: '<svg viewBox="0 0 24 24"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>',
};
document.getElementById('warn-list').innerHTML = warns.map(w => `
<div class="rq-warn-item ${w.type}">
<div class="rq-warn-icon">${w.icon}</div>
<div class="rq-warn-icon"><span class="icon-svg icon-md" aria-hidden="true">${warnIcons[w.type] || warnIcons.stale}</span></div>
<div style="flex:1;min-width:0">
<div class="rq-warn-title">${esc(w.title)}</div>
<div class="rq-warn-sub">${esc(w.sub)}</div>
@@ -484,11 +490,11 @@ function renderWarnings(warns) {
function handleWarnAction(btn, type) {
const item = btn.closest('.rq-warn-item');
if (type === 'stale') {
btn.textContent = 'Flagged ✓';
btn.textContent = 'Flagged';
btn.disabled = true;
} else if (type === 'dup') {
if (confirm('Mark duplicates for review? They will be flagged in the Knowledge Base.')) {
btn.textContent = 'Flagged ✓';
btn.textContent = 'Flagged';
btn.disabled = true;
}
} else if (type === 'conflict') {
@@ -560,6 +566,6 @@ refreshAll();
</script>
<script src="auth.js"></script>
<script src="branding.js"></script>
<script src="branding.js?v=3"></script>
</body>
</html>