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
+40
-26
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Scheduled Jobs — Nexus One AI</title>
|
||||
<link rel="stylesheet" href="style.css?v=4">
|
||||
<link rel="stylesheet" href="style.css?v=12">
|
||||
<style>
|
||||
/* ── Layout ── */
|
||||
.sj-layout { display:grid; grid-template-columns:280px 1fr; min-height:calc(100vh - 64px); }
|
||||
@@ -34,7 +34,8 @@
|
||||
|
||||
/* ── Empty state ── */
|
||||
.sj-empty { text-align:center; padding:80px 20px; color:var(--lt); }
|
||||
.sj-empty-icon { font-size:56px; margin-bottom:16px; }
|
||||
.sj-empty-icon { width:56px; height:56px; margin:0 auto 16px; color:var(--teal); }
|
||||
.sj-empty-icon svg { width:100%; height:100%; }
|
||||
.sj-empty-title { font-size:20px; font-weight:700; color:var(--ink); margin-bottom:8px; }
|
||||
.sj-empty-sub { font-size:14px; max-width:420px; margin:0 auto; line-height:1.6; }
|
||||
|
||||
@@ -102,7 +103,8 @@
|
||||
.sj-modal { background:var(--navy2); border-radius:14px; padding:28px; max-width:680px; width:90%; max-height:80vh; display:flex; flex-direction:column; }
|
||||
.sj-modal-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:16px; }
|
||||
.sj-modal-title { font-size:15px; font-weight:700; color:var(--ink); }
|
||||
.sj-modal-close { background:none; border:none; font-size:20px; cursor:pointer; color:var(--lt); }
|
||||
.sj-modal-close { background:none; border:none; cursor:pointer; color:var(--lt); width:20px; height:20px; padding:0; }
|
||||
.sj-modal-close svg { width:100%; height:100%; }
|
||||
.sj-modal-body { overflow-y:auto; flex:1; font-size:13px; color:var(--ink); line-height:1.7; white-space:pre-wrap; word-break:break-word; }
|
||||
</style>
|
||||
</head>
|
||||
@@ -140,6 +142,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>
|
||||
@@ -179,7 +182,9 @@
|
||||
</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-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>
|
||||
</a>
|
||||
<span class="badge" data-brand="tier">Basic Tier</span>
|
||||
<div id="nav-org-logo" class="nav-org-logo"></div>
|
||||
</header>
|
||||
@@ -190,10 +195,10 @@
|
||||
<aside class="sj-sidebar">
|
||||
<div class="sj-sidebar-header">
|
||||
<h3>Scheduled Jobs</h3>
|
||||
<button class="sj-new-btn" onclick="newJob()">+ New Job</button>
|
||||
<button class="sj-new-btn" onclick="newJob()"><span class="icon-label"><span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></svg></span><span>New Job</span></span></button>
|
||||
</div>
|
||||
<div class="sj-job-list" id="job-list">
|
||||
<div style="padding:16px;font-size:12px;color:var(--lt);text-align:center">No jobs yet</div>
|
||||
<div class="cezen-list-empty">No jobs yet — <a onclick="newJob()">create your first one</a>.</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
@@ -202,15 +207,15 @@
|
||||
|
||||
<!-- Empty state -->
|
||||
<div class="sj-empty" id="sj-empty">
|
||||
<div class="sj-empty-icon">⏰</div>
|
||||
<div class="sj-empty-icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="12" cy="13" r="8"/><path d="M12 9v4l3 2"/><path d="M8 3h8"/></svg></div>
|
||||
<div class="sj-empty-title">Scheduled AI Jobs</div>
|
||||
<div class="sj-empty-sub">
|
||||
Run prompts or agent pipelines automatically on a schedule — every hour, every morning, or on a custom cron. Results are logged and viewable here.
|
||||
</div>
|
||||
<div style="margin-top:24px;display:flex;gap:12px;justify-content:center;flex-wrap:wrap">
|
||||
<button class="btn btn-primary" onclick="newJob('daily')">📅 Daily Summary</button>
|
||||
<button class="btn btn-ghost" onclick="newJob('hourly')">⏱ Hourly Monitor</button>
|
||||
<button class="btn btn-ghost" onclick="newJob('weekly')">📊 Weekly Report</button>
|
||||
<button class="btn btn-primary" onclick="newJob('daily')"><span class="icon-label"><span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg></span><span>Daily Summary</span></span></button>
|
||||
<button class="btn btn-ghost" onclick="newJob('hourly')"><span class="icon-label"><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 9v4l3 2"/><path d="M8 3h8"/></svg></span><span>Hourly Monitor</span></span></button>
|
||||
<button class="btn btn-ghost" onclick="newJob('weekly')"><span class="icon-label"><span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M4 19h16"/><path d="M7 16V8"/><path d="M12 16V5"/><path d="M17 16v-3"/></svg></span><span>Weekly Report</span></span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -227,7 +232,7 @@
|
||||
|
||||
<!-- Job config card -->
|
||||
<div class="sj-card">
|
||||
<div class="sj-card-title">⚙️ Job Configuration</div>
|
||||
<div class="sj-card-title"><span class="icon-svg icon-md" 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>Job Configuration</div>
|
||||
|
||||
<div class="sj-toggle-row">
|
||||
<label class="sj-toggle">
|
||||
@@ -245,8 +250,8 @@
|
||||
<div class="sj-field">
|
||||
<label>Job Type</label>
|
||||
<select id="job-type" onchange="onTypeChange()">
|
||||
<option value="prompt">💬 Run a Prompt</option>
|
||||
<option value="agent">🤖 Run an Agent</option>
|
||||
<option value="prompt">Run a Prompt</option>
|
||||
<option value="agent">Run an Agent</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -260,8 +265,8 @@
|
||||
<div class="sj-field">
|
||||
<label>Schedule</label>
|
||||
<div class="sj-sched-tabs">
|
||||
<button class="sj-sched-tab active" id="tab-interval" onclick="setSchedType('interval')">⏱ Interval</button>
|
||||
<button class="sj-sched-tab" id="tab-cron" onclick="setSchedType('cron')">🗓 Cron</button>
|
||||
<button class="sj-sched-tab active" id="tab-interval" onclick="setSchedType('interval')">Interval</button>
|
||||
<button class="sj-sched-tab" id="tab-cron" onclick="setSchedType('cron')">Cron</button>
|
||||
</div>
|
||||
|
||||
<!-- Interval -->
|
||||
@@ -328,15 +333,15 @@
|
||||
</div>
|
||||
|
||||
<div class="sj-actions">
|
||||
<button class="btn btn-primary" onclick="saveJob()">💾 Save Job</button>
|
||||
<button class="btn btn-ghost" onclick="triggerNow()" id="trigger-btn" style="display:none">▶ Run Now</button>
|
||||
<button class="btn btn-ghost" onclick="deleteJob()" id="delete-btn" style="display:none;color:#B91C1C;border-color:#FCA5A5">🗑 Delete</button>
|
||||
<button class="btn btn-primary" onclick="saveJob()"><span class="icon-label"><span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M5 21h14"/><path d="M19 21V7l-3-3H5v17"/><path d="M9 10h6"/><path d="M9 14h6"/></svg></span><span>Save Job</span></span></button>
|
||||
<button class="btn btn-ghost" onclick="triggerNow()" id="trigger-btn" style="display:none"><span class="icon-label"><span class="icon-svg icon-sm" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="m8 5 11 7-11 7z"/></svg></span><span>Run Now</span></span></button>
|
||||
<button class="btn btn-ghost" onclick="deleteJob()" id="delete-btn" style="display:none;color:#B91C1C;border-color:#FCA5A5"><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><span>Delete</span></span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Run history -->
|
||||
<div class="sj-card" id="hist-card" style="display:none">
|
||||
<div class="sj-card-title">📋 Run History <span id="hist-count" style="font-size:12px;font-weight:400;color:var(--lt)"></span></div>
|
||||
<div class="sj-card-title"><span class="icon-svg icon-md" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M9 3h6"/><path d="M10 7h4"/><rect x="5" y="5" width="14" height="16" rx="2"/></svg></span>Run History <span id="hist-count" style="font-size:12px;font-weight:400;color:var(--lt)"></span></div>
|
||||
<div id="hist-body"><div class="sj-empty-hist">No runs yet</div></div>
|
||||
</div>
|
||||
|
||||
@@ -349,7 +354,7 @@
|
||||
<div class="sj-modal">
|
||||
<div class="sj-modal-header">
|
||||
<span class="sj-modal-title">Run Output</span>
|
||||
<button class="sj-modal-close" onclick="document.getElementById('output-modal').classList.remove('open')">✕</button>
|
||||
<button class="sj-modal-close" onclick="document.getElementById('output-modal').classList.remove('open')" aria-label="Close"><svg viewBox="0 0 24 24"><path d="M6 6l12 12M18 6 6 18"/></svg></button>
|
||||
</div>
|
||||
<div class="sj-modal-body" id="modal-content"></div>
|
||||
</div>
|
||||
@@ -363,6 +368,15 @@ let models = [];
|
||||
let currentJobId = null;
|
||||
let schedType = 'interval';
|
||||
let pollTimer = null;
|
||||
function sjIconSvg(name, sizeClass = 'icon-sm') {
|
||||
const icons = {
|
||||
robot:'<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>',
|
||||
chat:'<svg viewBox="0 0 24 24"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>',
|
||||
play:'<svg viewBox="0 0 24 24"><path d="m8 5 11 7-11 7z"/></svg>',
|
||||
spinner:'<svg viewBox="0 0 24 24"><path d="M21 12a9 9 0 1 1-2.64-6.36"/></svg>'
|
||||
};
|
||||
return `<span class="icon-svg ${sizeClass}" aria-hidden="true">${icons[name] || icons.chat}</span>`;
|
||||
}
|
||||
|
||||
// ── Sidebar ───────────────────────────────────────────────────────────────────
|
||||
async function loadJobList() {
|
||||
@@ -376,7 +390,7 @@ async function loadJobList() {
|
||||
function renderSidebar() {
|
||||
const el = document.getElementById('job-list');
|
||||
if (!jobs.length) {
|
||||
el.innerHTML = '<div style="padding:16px;font-size:12px;color:var(--lt);text-align:center">No jobs yet</div>';
|
||||
el.innerHTML = '<div class="cezen-list-empty">No jobs yet — <a onclick="newJob()">create your first one</a>.</div>';
|
||||
return;
|
||||
}
|
||||
el.innerHTML = jobs.map(j => {
|
||||
@@ -387,7 +401,7 @@ function renderSidebar() {
|
||||
<span class="sj-dot ${active}"></span>
|
||||
${esc(j.name)}
|
||||
</div>
|
||||
<div class="sj-job-meta">${j.job_type === 'agent' ? '🤖 Agent' : '💬 Prompt'} · ${fmtSched(j)}</div>
|
||||
<div class="sj-job-meta">${j.job_type === 'agent' ? 'Agent' : 'Prompt'} · ${fmtSched(j)}</div>
|
||||
${nextTxt ? `<div class="sj-job-next">${nextTxt}</div>` : ''}
|
||||
</div>`;
|
||||
}).join('');
|
||||
@@ -585,7 +599,7 @@ async function triggerNow() {
|
||||
if (!currentJobId) return;
|
||||
const btn = document.getElementById('trigger-btn');
|
||||
btn.disabled = true;
|
||||
btn.textContent = '⏳ Running…';
|
||||
btn.innerHTML = `<span class="icon-label">${sjIconSvg('spinner')}<span>Running...</span></span>`;
|
||||
try {
|
||||
const res = await fetch(`${_API}/schedules/${currentJobId}/run`, {
|
||||
method:'POST', credentials:'include'
|
||||
@@ -599,7 +613,7 @@ async function triggerNow() {
|
||||
alert('Trigger failed: ' + e.message);
|
||||
}
|
||||
btn.disabled = false;
|
||||
btn.textContent = '▶ Run Now';
|
||||
btn.innerHTML = `<span class="icon-label">${sjIconSvg('play')}<span>Run Now</span></span>`;
|
||||
}
|
||||
|
||||
// ── Delete ────────────────────────────────────────────────────────────────────
|
||||
@@ -630,7 +644,7 @@ async function loadHistory(jobId) {
|
||||
countEl.textContent = runs.length ? `(${runs.length} runs)` : '';
|
||||
|
||||
if (!runs.length) {
|
||||
el.innerHTML = '<div class="sj-empty-hist">No runs yet — save the job and click ▶ Run Now to test it</div>';
|
||||
el.innerHTML = '<div class="sj-empty-hist">No runs yet. Save the job and click Run Now to test it.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -717,6 +731,6 @@ async function loadAgents() {
|
||||
</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