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

+62 -30
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Notifications — Nexus One AI</title>
<link rel="stylesheet" href="style.css?v=4">
<link rel="stylesheet" href="style.css?v=12">
<style>
.nt-layout { display: grid; grid-template-columns: 220px 1fr; min-height: calc(100vh - 64px); }
@media(max-width:700px){ .nt-layout { grid-template-columns: 1fr; } .nt-sidebar { display:none; } }
@@ -23,7 +23,16 @@
}
.nt-filter-btn:hover { background: rgba(124,58,237,.07); color: var(--ink); }
.nt-filter-btn.active { background: rgba(124,58,237,.12); color: var(--purple); font-weight: 700; }
.nt-filter-icon { font-size: 15px; width: 20px; text-align: center; }
.nt-filter-icon {
width: 20px;
height: 20px;
color: currentColor;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.nt-filter-icon svg { width: 16px; height: 16px; }
.nt-filter-count {
margin-left: auto; background: var(--purple); color: white;
font-size: 10px; font-weight: 800; padding: 2px 7px;
@@ -49,7 +58,14 @@
/* List */
.nt-list { display: flex; flex-direction: column; gap: 8px; }
.nt-empty { text-align: center; padding: 60px 20px; color: var(--lt); }
.nt-empty-icon { font-size: 40px; margin-bottom: 12px; opacity: .35; }
.nt-empty-icon {
width: 40px;
height: 40px;
margin: 0 auto 12px;
opacity: .35;
color: var(--text-secondary);
}
.nt-empty-icon svg { width: 100%; height: 100%; }
.nt-item {
display: flex; gap: 14px; align-items: flex-start;
@@ -66,8 +82,9 @@
.nt-item-icon {
width: 38px; height: 38px; border-radius: 10px;
display: flex; align-items: center; justify-content: center;
font-size: 18px; flex-shrink: 0;
color: currentColor; flex-shrink: 0;
}
.nt-item-icon svg { width: 18px; height: 18px; }
.nt-item-icon.info { background: rgba(124,58,237,.12); }
.nt-item-icon.warning { background: rgba(245,158,11,.12); }
.nt-item-icon.error { background: rgba(239,68,68,.12); }
@@ -154,6 +171,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>
@@ -192,8 +210,11 @@
<a href="chatrooms.html">Chat Rooms</a>
</div>
</div>
<a href="notifications.html" class="active" style="position:relative">
🔔 <span id="nav-badge" class="nt-filter-count hidden" style="position:absolute;top:-4px;right:-8px;font-size:9px;padding:1px 5px"></span>
<a href="notifications.html" class="nav-icon-btn active" style="position:relative" title="Notifications">
<span class="icon-svg icon-md" aria-hidden="true">
<svg viewBox="0 0 24 24"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
</span>
<span id="nav-badge" class="nt-filter-count hidden" style="position:absolute;top:-4px;right:-8px;font-size:9px;padding:1px 5px"></span>
</a>
</nav>
<span class="badge" data-brand="tier">Basic Tier</span>
@@ -206,58 +227,58 @@
<div class="nt-sidebar">
<div class="nt-sidebar-title">Filter</div>
<button class="nt-filter-btn active" onclick="setFilter('all', this)">
<span class="nt-filter-icon">📬</span> All
<span class="nt-filter-icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M4 4h16v16H4z"/><path d="m4 7 8 6 8-6"/></svg></span> All
<span class="nt-filter-count hidden" id="cnt-all"></span>
</button>
<button class="nt-filter-btn" onclick="setFilter('unread', this)">
<span class="nt-filter-icon">🔵</span> Unread
<span class="nt-filter-icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="5"/></svg></span> Unread
<span class="nt-filter-count hidden" id="cnt-unread"></span>
</button>
<div style="margin: 14px 8px 6px; font-size:11px;font-weight:700;color:var(--lt);text-transform:uppercase;letter-spacing:.4px">By Source</div>
<button class="nt-filter-btn" onclick="setFilter('guardrail', this)">
<span class="nt-filter-icon">🛡</span> Guardrails
<span class="nt-filter-icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg></span> Guardrails
<span class="nt-filter-count hidden" id="cnt-guardrail"></span>
</button>
<button class="nt-filter-btn" onclick="setFilter('scheduler', this)">
<span class="nt-filter-icon">⏱</span> Scheduler
<span class="nt-filter-icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="12" cy="13" r="8"/><path d="M12 9v4l2 2"/><path d="M9 2h6"/><path d="M12 2v3"/></svg></span> Scheduler
<span class="nt-filter-count hidden" id="cnt-scheduler"></span>
</button>
<button class="nt-filter-btn" onclick="setFilter('agent', this)">
<span class="nt-filter-icon">🤖</span> Agents
<span class="nt-filter-icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><rect x="7" y="8" width="10" height="8" rx="2"/><path d="M12 4v4M9 16v2M15 16v2M6 10H4M20 10h-2"/><circle cx="10" cy="12" r="1"/><circle cx="14" cy="12" r="1"/></svg></span> Agents
<span class="nt-filter-count hidden" id="cnt-agent"></span>
</button>
<button class="nt-filter-btn" onclick="setFilter('rag', this)">
<span class="nt-filter-icon">📚</span> RAG / KB
<span class="nt-filter-icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg></span> RAG / KB
<span class="nt-filter-count hidden" id="cnt-rag"></span>
</button>
<button class="nt-filter-btn" onclick="setFilter('system', this)">
<span class="nt-filter-icon">⚙️</span> System
<span class="nt-filter-icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33h.01A1.65 1.65 0 0 0 10.5 3.1V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51h.01a1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82v.01a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></span> System
<span class="nt-filter-count hidden" id="cnt-system"></span>
</button>
<div style="margin: 14px 8px 6px; font-size:11px;font-weight:700;color:var(--lt);text-transform:uppercase;letter-spacing:.4px">By Severity</div>
<button class="nt-filter-btn" onclick="setFilter('sev-critical', this)">
<span class="nt-filter-icon">🔴</span> Critical
<span class="nt-filter-icon icon-svg" aria-hidden="true"><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></span> Critical
</button>
<button class="nt-filter-btn" onclick="setFilter('sev-error', this)">
<span class="nt-filter-icon">🟠</span> Error
<span class="nt-filter-icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="m15 9-6 6M9 9l6 6"/></svg></span> Error
</button>
<button class="nt-filter-btn" onclick="setFilter('sev-warning', this)">
<span class="nt-filter-icon">🟡</span> Warning
<span class="nt-filter-icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M12 9v4"/><path d="M12 17h.01"/><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"/></svg></span> Warning
</button>
<button class="nt-filter-btn" onclick="setFilter('sev-info', this)">
<span class="nt-filter-icon">🔵</span> Info
<span class="nt-filter-icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 12v4"/><path d="M12 8h.01"/></svg></span> Info
</button>
</div>
<!-- Main -->
<div class="nt-main">
<div class="nt-header">
<div class="nt-title">🔔 Notifications</div>
<div class="nt-title icon-label"><span class="icon-svg icon-lg" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg></span>Notifications</div>
<div class="nt-header-actions">
<button class="nt-btn" onclick="markAllRead()">✓ Mark all read</button>
<button class="nt-btn danger" onclick="clearAll()">🗑 Clear all</button>
<button class="nt-btn" onclick="markAllRead()"><span class="icon-label"><span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="m20 6-11 11-5-5"/></svg></span>Mark all read</span></button>
<button class="nt-btn danger" onclick="clearAll()"><span class="icon-label"><span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M3 6h18"/><path d="M8 6V4h8v2"/><path d="M19 6l-1 14H6L5 6"/></svg></span>Clear all</span></button>
</div>
</div>
@@ -282,14 +303,25 @@
</div>
<div class="nt-list" id="nt-list">
<div class="nt-empty"><div class="nt-empty-icon">🔔</div><p>Loading…</p></div>
<div class="nt-empty"><div class="nt-empty-icon icon-svg"><svg viewBox="0 0 24 24"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg></div><p>Loading…</p></div>
</div>
</div>
</div>
<script>
const SOURCE_ICONS = { guardrail:'🛡', scheduler:'⏱', agent:'🤖', rag:'📚', system:'⚙️' };
const SEV_ICONS = { info:'ℹ️', warning:'⚠️', error:'❌', critical:'🚨' };
const SOURCE_ICONS = {
guardrail:'<span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg></span>',
scheduler:'<span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="12" cy="13" r="8"/><path d="M12 9v4l2 2"/><path d="M9 2h6"/><path d="M12 2v3"/></svg></span>',
agent:'<span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><rect x="7" y="8" width="10" height="8" rx="2"/><path d="M12 4v4M9 16v2M15 16v2M6 10H4M20 10h-2"/><circle cx="10" cy="12" r="1"/><circle cx="14" cy="12" r="1"/></svg></span>',
rag:'<span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg></span>',
system:'<span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33h.01A1.65 1.65 0 0 0 10.5 3.1V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51h.01a1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82v.01a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></span>'
};
const SEV_ICONS = {
info:'<span class="icon-svg icon-md" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 12v4"/><path d="M12 8h.01"/></svg></span>',
warning:'<span class="icon-svg icon-md" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M12 9v4"/><path d="M12 17h.01"/><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"/></svg></span>',
error:'<span class="icon-svg icon-md" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="m15 9-6 6M9 9l6 6"/></svg></span>',
critical:'<span class="icon-svg icon-md" aria-hidden="true"><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></span>'
};
let allNotifications = [];
let currentFilter = 'all';
@@ -373,25 +405,25 @@ function render() {
const list = document.getElementById('nt-list');
const items = getFiltered();
if (!items.length) {
list.innerHTML = `<div class="nt-empty"><div class="nt-empty-icon">✅</div><p>No notifications here.</p></div>`;
list.innerHTML = `<div class="nt-empty"><div class="nt-empty-icon icon-svg"><svg viewBox="0 0 24 24"><path d="m20 6-11 11-5-5"/></svg></div><p>No notifications here.</p></div>`;
return;
}
list.innerHTML = items.map(n => `
<div class="nt-item ${n.is_read ? '' : 'unread'} sev-${n.severity}" onclick="markRead(${n.id})" id="nt-${n.id}">
<div class="nt-item-icon ${n.severity}">${SEV_ICONS[n.severity] || '📌'}</div>
<div class="nt-item-icon ${n.severity}">${SEV_ICONS[n.severity] || ''}</div>
<div class="nt-item-body">
<div class="nt-item-header">
<span class="nt-item-title">${n.title}</span>
<span class="nt-sev-badge ${n.severity}">${n.severity}</span>
<span class="nt-src-badge">${SOURCE_ICONS[n.source]||''} ${n.source}</span>
<span class="nt-src-badge"><span class="icon-label">${SOURCE_ICONS[n.source]||''}<span>${n.source}</span></span></span>
<span class="nt-item-time">${timeAgo(n.created_at)}</span>
</div>
<div class="nt-item-body-text">${n.body}</div>
${n.link ? `<a class="nt-item-link" href="${n.link}" onclick="event.stopPropagation()">→ View details</a>` : ''}
${n.link ? `<a class="nt-item-link" href="${n.link}" onclick="event.stopPropagation()">View details</a>` : ''}
</div>
<div class="nt-item-actions">
<button class="nt-icon-btn" onclick="event.stopPropagation();markRead(${n.id})" title="Mark read">✓</button>
<button class="nt-icon-btn del" onclick="event.stopPropagation();deleteNotif(${n.id})" title="Delete">✕</button>
<button class="nt-icon-btn" onclick="event.stopPropagation();markRead(${n.id})" title="Mark read"><span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="m20 6-11 11-5-5"/></svg></span></button>
<button class="nt-icon-btn del" onclick="event.stopPropagation();deleteNotif(${n.id})" title="Delete"><span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M6 6l12 12M18 6 6 18"/></svg></span></button>
</div>
<div class="nt-unread-dot ${n.is_read ? 'hidden' : ''}"></div>
</div>
@@ -437,6 +469,6 @@ setInterval(loadNotifications, 30000);
</script>
<script src="auth.js"></script>
<script src="branding.js"></script>
<script src="branding.js?v=3"></script>
</body>
</html>