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:
1 parent
7540da04a8
commit
b3eec34158
57 files changed
+2832
-1354
No files matched your search
@@ -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 & 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 & 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>
|
||||
Reference in new issue
Block a user