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
+90
-53
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Workflow Designer — Nexus One AI</title>
|
||||
<link rel="stylesheet" href="style.css?v=4">
|
||||
<link rel="stylesheet" href="style.css?v=12">
|
||||
<style>
|
||||
/* ── Layout ── */
|
||||
.wf-layout { display:grid; grid-template-columns:270px 1fr; min-height:calc(100vh - 64px); }
|
||||
@@ -33,14 +33,16 @@
|
||||
|
||||
/* ── Empty state ── */
|
||||
.wf-empty { text-align:center; padding:80px 20px; }
|
||||
.wf-empty-icon { font-size:52px; margin-bottom:16px; }
|
||||
.wf-empty-icon { width:52px; height:52px; margin:0 auto 16px; color:var(--purple); }
|
||||
.wf-empty-icon svg { width:100%; height:100%; }
|
||||
.wf-empty-title { font-size:20px; font-weight:700; color:var(--ink); margin-bottom:8px; }
|
||||
.wf-empty-sub { font-size:14px; color:var(--lt); max-width:420px; margin:0 auto; line-height:1.6; }
|
||||
.wf-templates { display:grid; grid-template-columns:repeat(3,1fr); gap:12px; max-width:660px; margin:28px auto 0; }
|
||||
@media(max-width:600px){ .wf-templates { grid-template-columns:1fr; } }
|
||||
.wf-tpl { padding:16px; border:1.5px solid var(--bdr); border-radius:12px; background:var(--navy2); cursor:pointer; text-align:left; transition:.15s; font-family:inherit; }
|
||||
.wf-tpl:hover { border-color:var(--purple); background:rgba(124,58,237,.04); }
|
||||
.wf-tpl-icon { font-size:24px; margin-bottom:8px; display:block; }
|
||||
.wf-tpl-icon { width:24px; height:24px; margin-bottom:8px; display:block; color:var(--text-secondary); }
|
||||
.wf-tpl-icon svg { width:100%; height:100%; }
|
||||
.wf-tpl-name { font-size:13px; font-weight:700; color:var(--ink); }
|
||||
.wf-tpl-desc { font-size:11px; color:var(--lt); margin-top:3px; }
|
||||
|
||||
@@ -88,7 +90,8 @@
|
||||
.wf-step-actions { display:flex; gap:4px; }
|
||||
.wf-step-btn { background:none; border:none; cursor:pointer; font-size:14px; padding:3px 7px; color:var(--lt); border-radius:4px; }
|
||||
.wf-step-btn:hover { color:var(--ink); background:rgba(124,58,237,.06); }
|
||||
.wf-step-chevron { font-size:11px; color:var(--lt); transition:.2s; }
|
||||
.wf-step-chevron { width:14px; height:14px; color:var(--lt); transition:.2s; }
|
||||
.wf-step-chevron svg { width:100%; height:100%; }
|
||||
.wf-step.open .wf-step-chevron { transform:rotate(90deg); }
|
||||
.wf-step-body { padding:16px 18px; display:none; }
|
||||
.wf-step.open .wf-step-body { display:block; }
|
||||
@@ -123,7 +126,8 @@
|
||||
.wf-step-menu-cat { font-size:10px; font-weight:700; color:var(--lt); text-transform:uppercase; letter-spacing:.6px; padding:10px 14px 4px; }
|
||||
.wf-step-menu-opt { display:flex; align-items:center; gap:10px; padding:10px 14px; cursor:pointer; font-size:13px; color:var(--ink); transition:.1s; }
|
||||
.wf-step-menu-opt:hover { background:rgba(124,58,237,.05); }
|
||||
.wf-step-menu-opt .icon { font-size:18px; width:24px; text-align:center; }
|
||||
.wf-step-menu-opt .icon { width:24px; height:24px; color:var(--text-secondary); flex-shrink:0; }
|
||||
.wf-step-menu-opt .icon svg { width:100%; height:100%; }
|
||||
.wf-step-menu-opt .lbl { font-weight:600; }
|
||||
.wf-step-menu-opt .sub { font-size:11px; color:var(--lt); margin-top:1px; }
|
||||
|
||||
@@ -138,7 +142,8 @@
|
||||
.wf-run-log.show { display:block; }
|
||||
.wf-log-step { display:flex; align-items:flex-start; gap:12px; padding:10px 0; border-bottom:1px solid var(--bdr); }
|
||||
.wf-log-step:last-child { border-bottom:none; }
|
||||
.wf-log-icon { font-size:18px; flex-shrink:0; margin-top:1px; }
|
||||
.wf-log-icon { width:18px; height:18px; flex-shrink:0; margin-top:1px; color:var(--text-secondary); }
|
||||
.wf-log-icon svg { width:100%; height:100%; }
|
||||
.wf-log-body { flex:1; }
|
||||
.wf-log-label { font-size:13px; font-weight:600; color:var(--ink); }
|
||||
.wf-log-detail { font-size:12px; color:var(--lt); margin-top:2px; }
|
||||
@@ -192,6 +197,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>
|
||||
@@ -231,7 +237,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>
|
||||
@@ -242,10 +250,11 @@
|
||||
<aside class="wf-sidebar">
|
||||
<div class="wf-sidebar-header">
|
||||
<h3>Workflows</h3>
|
||||
<button class="wf-new-btn" onclick="newWorkflow()">+ New Workflow</button>
|
||||
<button class="wf-new-btn" onclick="newWorkflow()"><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 Workflow</span></span></button>
|
||||
</div>
|
||||
<input type="text" id="wf-filter" placeholder="Filter workflows…" oninput="renderSidebar()" style="width:100%;box-sizing:border-box;margin:0 0 10px;padding:8px 10px;border:1px solid var(--bdr);border-radius:7px;font-size:12.5px;font-family:inherit">
|
||||
<div class="wf-list" id="wf-list">
|
||||
<div style="padding:16px;font-size:12px;color:var(--lt);text-align:center">No workflows yet</div>
|
||||
<div class="cezen-list-empty">No workflows yet — <a onclick="newWorkflow()">create your first one</a>.</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
@@ -254,39 +263,39 @@
|
||||
|
||||
<!-- Empty state -->
|
||||
<div class="wf-empty" id="wf-empty">
|
||||
<div class="wf-empty-icon">⚡</div>
|
||||
<div class="wf-empty-icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M13 2 3 14h7l-1 8 10-12h-7z"/></svg></div>
|
||||
<div class="wf-empty-title">Workflow Designer</div>
|
||||
<div class="wf-empty-sub">
|
||||
Connect triggers and AI steps into automated pipelines. Process documents the moment they arrive, route questions to the right model, or generate daily summaries — all without code.
|
||||
</div>
|
||||
<div class="wf-templates">
|
||||
<button class="wf-tpl" onclick="loadTemplate('doc_inbox')">
|
||||
<span class="wf-tpl-icon">📥</span>
|
||||
<span class="wf-tpl-icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M12 3v12"/><path d="m7 10 5 5 5-5"/><path d="M5 21h14"/></svg></span>
|
||||
<div class="wf-tpl-name">Document Inbox</div>
|
||||
<div class="wf-tpl-desc">Summarise every uploaded document and save to KB</div>
|
||||
</button>
|
||||
<button class="wf-tpl" onclick="loadTemplate('daily_digest')">
|
||||
<span class="wf-tpl-icon">📊</span>
|
||||
<span class="wf-tpl-icon icon-svg" 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>
|
||||
<div class="wf-tpl-name">Daily Digest</div>
|
||||
<div class="wf-tpl-desc">Pull metrics each morning and email a summary</div>
|
||||
</button>
|
||||
<button class="wf-tpl" onclick="loadTemplate('query_router')">
|
||||
<span class="wf-tpl-icon">🔀</span>
|
||||
<span class="wf-tpl-icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M16 3h5v5"/><path d="M4 20 21 3"/><path d="M21 16v5h-5"/><path d="M15 15 21 21"/><path d="M4 4h5l6 6"/></svg></span>
|
||||
<div class="wf-tpl-name">Query Router</div>
|
||||
<div class="wf-tpl-desc">Classify incoming queries and route to the right agent</div>
|
||||
</button>
|
||||
<button class="wf-tpl" onclick="loadTemplate('contract_review')">
|
||||
<span class="wf-tpl-icon">📋</span>
|
||||
<span class="wf-tpl-icon icon-svg" 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>
|
||||
<div class="wf-tpl-name">Contract Review</div>
|
||||
<div class="wf-tpl-desc">Extract clauses and flag risks from legal documents</div>
|
||||
</button>
|
||||
<button class="wf-tpl" onclick="loadTemplate('meeting_notes')">
|
||||
<span class="wf-tpl-icon">🎙️</span>
|
||||
<span class="wf-tpl-icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><rect x="9" y="3" width="6" height="11" rx="3"/><path d="M5 10a7 7 0 0 0 14 0"/><path d="M12 17v4"/><path d="M8 21h8"/></svg></span>
|
||||
<div class="wf-tpl-name">Meeting Notes</div>
|
||||
<div class="wf-tpl-desc">Transcribe recordings and post action items to Slack</div>
|
||||
</button>
|
||||
<button class="wf-tpl" onclick="newWorkflow()">
|
||||
<span class="wf-tpl-icon">✏️</span>
|
||||
<span class="wf-tpl-icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg></span>
|
||||
<div class="wf-tpl-name">Blank Workflow</div>
|
||||
<div class="wf-tpl-desc">Start from scratch with your own trigger and steps</div>
|
||||
</button>
|
||||
@@ -311,7 +320,7 @@
|
||||
<span class="wf-dot"></span> <span id="wf-status-label">Draft</span>
|
||||
</button>
|
||||
<button class="wf-save-btn" onclick="saveWorkflow()">Save</button>
|
||||
<button class="wf-delete-btn" onclick="deleteWorkflow()" title="Delete workflow">🗑</button>
|
||||
<button class="wf-delete-btn" onclick="deleteWorkflow()" title="Delete workflow"><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></button>
|
||||
</div>
|
||||
|
||||
<textarea class="wf-desc-input" id="wf-desc" rows="2" placeholder="Description — what does this workflow do?" oninput="markDirty()"></textarea>
|
||||
@@ -327,15 +336,15 @@
|
||||
</div>
|
||||
<div class="wf-trigger-body">
|
||||
<div class="wf-field">
|
||||
<label>Trigger Type</label>
|
||||
<label>Trigger Type <span class="cezen-help" tabindex="0" data-tip="What starts this workflow: a new file landing in a watched folder, a cron schedule, an incoming webhook call, a chat message, a form submission, or a manual click of Run. Pick one, then fill in its settings below.">?</span></label>
|
||||
<select id="trigger-type" onchange="onTriggerChange()">
|
||||
<option value="">— select —</option>
|
||||
<option value="file_upload">📁 File Uploaded</option>
|
||||
<option value="schedule">⏰ Schedule (Cron)</option>
|
||||
<option value="api_webhook">🌐 API Webhook</option>
|
||||
<option value="chat_message">💬 Chat Message</option>
|
||||
<option value="form_submit">📝 Form Submitted</option>
|
||||
<option value="manual">▶ Manual (Run button)</option>
|
||||
<option value="file_upload">File Uploaded</option>
|
||||
<option value="schedule">Schedule (Cron)</option>
|
||||
<option value="api_webhook">API Webhook</option>
|
||||
<option value="chat_message">Chat Message</option>
|
||||
<option value="form_submit">Form Submitted</option>
|
||||
<option value="manual">Manual (Run button)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="trigger-extra"></div>
|
||||
@@ -350,45 +359,45 @@
|
||||
|
||||
<!-- Add step -->
|
||||
<div class="wf-add-step" id="add-step-wrap">
|
||||
<button class="wf-add-step-btn" onclick="toggleStepMenu()">+ Add Step</button>
|
||||
<button class="wf-add-step-btn" onclick="toggleStepMenu()"><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>Add Step</span></span></button>
|
||||
<div class="wf-step-menu" id="step-menu">
|
||||
<div class="wf-step-menu-cat">AI</div>
|
||||
<div class="wf-step-menu-opt" onclick="addStep('prompt')">
|
||||
<span class="icon">🤖</span>
|
||||
<span class="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>
|
||||
<div><div class="lbl">AI Prompt</div><div class="sub">Send a prompt to a model and capture output</div></div>
|
||||
</div>
|
||||
<div class="wf-step-menu-opt" onclick="addStep('classify')">
|
||||
<span class="icon">🏷️</span>
|
||||
<span class="icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="m20 7-8.5 8.5a2.12 2.12 0 0 1-3 0L4 11V4h7l9 9a2.12 2.12 0 0 1 0 3z"/><circle cx="7.5" cy="7.5" r=".5"/></svg></span>
|
||||
<div><div class="lbl">Classify</div><div class="sub">Label input into one of your defined categories</div></div>
|
||||
</div>
|
||||
<div class="wf-step-menu-opt" onclick="addStep('extract')">
|
||||
<span class="icon">🔍</span>
|
||||
<span class="icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="6"/><path d="m20 20-4.35-4.35"/></svg></span>
|
||||
<div><div class="lbl">Extract Fields</div><div class="sub">Pull structured data from text (JSON output)</div></div>
|
||||
</div>
|
||||
<div class="wf-step-menu-opt" onclick="addStep('summarise')">
|
||||
<span class="icon">📝</span>
|
||||
<span class="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"/><path d="M8 7h8M8 11h8M8 15h5"/></svg></span>
|
||||
<div><div class="lbl">Summarise</div><div class="sub">Generate a concise summary of input text</div></div>
|
||||
</div>
|
||||
<div class="wf-step-menu-cat">DATA</div>
|
||||
<div class="wf-step-menu-opt" onclick="addStep('rag_search')">
|
||||
<span class="icon">📚</span>
|
||||
<span class="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>
|
||||
<div><div class="lbl">Knowledge Search</div><div class="sub">Query your RAG collections for relevant context</div></div>
|
||||
</div>
|
||||
<div class="wf-step-menu-opt" onclick="addStep('save_kb')">
|
||||
<span class="icon">💾</span>
|
||||
<span class="icon icon-svg" 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>
|
||||
<div><div class="lbl">Save to Knowledge Base</div><div class="sub">Ingest output into a RAG collection</div></div>
|
||||
</div>
|
||||
<div class="wf-step-menu-opt" onclick="addStep('filter')">
|
||||
<span class="icon">🔀</span>
|
||||
<span class="icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M16 3h5v5"/><path d="M4 20 21 3"/><path d="M21 16v5h-5"/><path d="M15 15 21 21"/><path d="M4 4h5l6 6"/></svg></span>
|
||||
<div><div class="lbl">Filter / Condition</div><div class="sub">Branch or stop based on a condition</div></div>
|
||||
</div>
|
||||
<div class="wf-step-menu-cat">NOTIFY</div>
|
||||
<div class="wf-step-menu-opt" onclick="addStep('email')">
|
||||
<span class="icon">📧</span>
|
||||
<span class="icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M4 6h16v12H4z"/><path d="m4 7 8 6 8-6"/></svg></span>
|
||||
<div><div class="lbl">Send Email</div><div class="sub">Send workflow output via email</div></div>
|
||||
</div>
|
||||
<div class="wf-step-menu-opt" onclick="addStep('http')">
|
||||
<span class="icon">🌐</span>
|
||||
<span class="icon icon-svg" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a15 15 0 0 1 0 18M12 3a15 15 0 0 0 0 18"/></svg></span>
|
||||
<div><div class="lbl">HTTP Request</div><div class="sub">POST output to an external endpoint</div></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -403,7 +412,7 @@
|
||||
<label>Test Input</label>
|
||||
<textarea id="run-input" rows="3" placeholder="Paste sample text or leave empty for manual trigger…"></textarea>
|
||||
</div>
|
||||
<button class="wf-run-btn" id="run-btn" onclick="runWorkflow()">▶ Run Now</button>
|
||||
<button class="wf-run-btn" id="run-btn" onclick="runWorkflow()"><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>
|
||||
<div class="wf-run-log" id="run-log"></div>
|
||||
</div>
|
||||
|
||||
@@ -421,6 +430,28 @@ let workflows = [];
|
||||
let currentWf = null;
|
||||
let steps = [];
|
||||
let dirty = false;
|
||||
function wfIconSvg(name, sizeClass = 'icon-md') {
|
||||
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>',
|
||||
tag:'<svg viewBox="0 0 24 24"><path d="m20 7-8.5 8.5a2.12 2.12 0 0 1-3 0L4 11V4h7l9 9a2.12 2.12 0 0 1 0 3z"/><circle cx="7.5" cy="7.5" r=".5"/></svg>',
|
||||
search:'<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="6"/><path d="m20 20-4.35-4.35"/></svg>',
|
||||
book:'<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"/><path d="M8 7h8M8 11h8M8 15h5"/></svg>',
|
||||
kb:'<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>',
|
||||
save:'<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>',
|
||||
route:'<svg viewBox="0 0 24 24"><path d="M16 3h5v5"/><path d="M4 20 21 3"/><path d="M21 16v5h-5"/><path d="M15 15 21 21"/><path d="M4 4h5l6 6"/></svg>',
|
||||
email:'<svg viewBox="0 0 24 24"><path d="M4 6h16v12H4z"/><path d="m4 7 8 6 8-6"/></svg>',
|
||||
globe:'<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a15 15 0 0 1 0 18M12 3a15 15 0 0 0 0 18"/></svg>',
|
||||
plus:'<svg viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></svg>',
|
||||
play:'<svg viewBox="0 0 24 24"><path d="m8 5 11 7-11 7z"/></svg>',
|
||||
trash:'<svg viewBox="0 0 24 24"><path d="M3 6h18"/><path d="M8 6V4h8v2"/><path d="M19 6l-1 14H6L5 6"/></svg>',
|
||||
up:'<svg viewBox="0 0 24 24"><path d="m18 15-6-6-6 6"/></svg>',
|
||||
down:'<svg viewBox="0 0 24 24"><path d="m6 9 6 6 6-6"/></svg>',
|
||||
chevronRight:'<svg viewBox="0 0 24 24"><path d="m9 6 6 6-6 6"/></svg>',
|
||||
bolt:'<svg viewBox="0 0 24 24"><path d="M13 2 3 14h7l-1 8 10-12h-7z"/></svg>',
|
||||
settings:'<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>'
|
||||
};
|
||||
return `<span class="icon-svg ${sizeClass}" aria-hidden="true">${icons[name] || icons.settings}</span>`;
|
||||
}
|
||||
|
||||
// ── Templates ─────────────────────────────────────────────────────────────────
|
||||
const TEMPLATES = {
|
||||
@@ -465,7 +496,7 @@ const TEMPLATES = {
|
||||
steps: [
|
||||
{ type:'extract', name:'Extract Clauses', config:{ model:'llama3', schema:'{"clauses": [{"type": "string", "text": "string", "risk": "high|medium|low"}]}', outputVar:'clauses' } },
|
||||
{ type:'filter', name:'Check for High Risk', config:{ condition:'{{clauses.clauses | filter: risk == "high" | count}} > 0', trueAction:'continue', falseAction:'stop' } },
|
||||
{ type:'email', name:'Alert Legal Team', config:{ to:'legal@cezentech.com', subject:'⚠️ High-Risk Contract: {{filename}}', bodyTemplate:'High-risk clauses found in {{filename}}:\n\n{{clauses.clauses | filter: risk == "high" | format}}' } }
|
||||
{ type:'email', name:'Alert Legal Team', config:{ to:'legal@cezentech.com', subject:'High-Risk Contract: {{filename}}', bodyTemplate:'High-risk clauses found in {{filename}}:\n\n{{clauses.clauses | filter: risk == "high" | format}}' } }
|
||||
]
|
||||
},
|
||||
meeting_notes: {
|
||||
@@ -483,8 +514,8 @@ const TEMPLATES = {
|
||||
};
|
||||
|
||||
const STEP_ICONS = {
|
||||
prompt:'🤖', classify:'🏷️', extract:'🔍', summarise:'📝',
|
||||
rag_search:'📚', save_kb:'💾', filter:'🔀', email:'📧', http:'🌐'
|
||||
prompt:wfIconSvg('robot'), classify:wfIconSvg('tag'), extract:wfIconSvg('search'), summarise:wfIconSvg('book'),
|
||||
rag_search:wfIconSvg('kb'), save_kb:wfIconSvg('save'), filter:wfIconSvg('route'), email:wfIconSvg('email'), http:wfIconSvg('globe')
|
||||
};
|
||||
const STEP_LABELS = {
|
||||
prompt:'AI Prompt', classify:'Classify', extract:'Extract Fields', summarise:'Summarise',
|
||||
@@ -508,11 +539,17 @@ function saveWorkflows() {
|
||||
function renderSidebar() {
|
||||
const el = document.getElementById('wf-list');
|
||||
if (!workflows.length) {
|
||||
el.innerHTML = '<div style="padding:16px;font-size:12px;color:var(--lt);text-align:center">No workflows yet</div>';
|
||||
el.innerHTML = '<div class="cezen-list-empty">No workflows yet — <a onclick="newWorkflow()">create your first one</a>.</div>';
|
||||
return;
|
||||
}
|
||||
el.innerHTML = workflows.map(wf => {
|
||||
const statusLabel = wf.status === 'active' ? '● Active' : wf.status === 'paused' ? '⏸ Paused' : '◌ Draft';
|
||||
const q = (document.getElementById('wf-filter')?.value || '').trim().toLowerCase();
|
||||
const filtered = q ? workflows.filter(wf => (wf.name || '').toLowerCase().includes(q)) : workflows;
|
||||
if (!filtered.length) {
|
||||
el.innerHTML = '<div class="cezen-list-empty">No workflows match your filter.</div>';
|
||||
return;
|
||||
}
|
||||
el.innerHTML = filtered.map(wf => {
|
||||
const statusLabel = wf.status === 'active' ? 'Active' : wf.status === 'paused' ? 'Paused' : 'Draft';
|
||||
const statusCls = wf.status || 'draft';
|
||||
return `<div class="wf-item${currentWf && currentWf.id === wf.id ? ' active' : ''}" onclick="openWorkflow('${wf.id}')">
|
||||
<div class="wf-item-name">${escHtml(wf.name)}</div>
|
||||
@@ -629,7 +666,7 @@ function saveWorkflow() {
|
||||
|
||||
dirty = false;
|
||||
renderSidebar();
|
||||
toast('✓ Workflow saved');
|
||||
toast('Workflow saved');
|
||||
}
|
||||
|
||||
function markDirty() { dirty = true; }
|
||||
@@ -659,8 +696,8 @@ function onTriggerChange() {
|
||||
|
||||
function updateTriggerDisplay() {
|
||||
const labels = {
|
||||
file_upload:'📁 File Uploaded', schedule:'⏰ Schedule', api_webhook:'🌐 API Webhook',
|
||||
chat_message:'💬 Chat Message', form_submit:'📝 Form Submitted', manual:'▶ Manual'
|
||||
file_upload:'File Uploaded', schedule:'Schedule', api_webhook:'API Webhook',
|
||||
chat_message:'Chat Message', form_submit:'Form Submitted', manual:'Manual'
|
||||
};
|
||||
const type = document.getElementById('trigger-type').value;
|
||||
document.getElementById('trigger-display').textContent = labels[type] || 'Choose a trigger…';
|
||||
@@ -709,20 +746,20 @@ function renderSteps() {
|
||||
}
|
||||
|
||||
function buildStepHtml(s, i) {
|
||||
const icon = STEP_ICONS[s.type] || '⚙️';
|
||||
const icon = STEP_ICONS[s.type] || wfIconSvg('settings');
|
||||
const label = STEP_LABELS[s.type] || s.type;
|
||||
return `<div class="wf-step" id="step-${s.id}">
|
||||
<div class="wf-step-head" onclick="toggleStep('${s.id}')">
|
||||
<div class="wf-step-num">${i+1}</div>
|
||||
<span style="font-size:18px">${icon}</span>
|
||||
<span>${icon}</span>
|
||||
<div class="wf-step-name">${escHtml(s.name||label)}</div>
|
||||
<span class="wf-step-type-chip">${label}</span>
|
||||
<div class="wf-step-actions" onclick="event.stopPropagation()">
|
||||
${i > 0 ? `<button class="wf-step-btn" title="Move up" onclick="moveStep('${s.id}',-1)">↑</button>` : ''}
|
||||
${i < steps.length-1 ? `<button class="wf-step-btn" title="Move down" onclick="moveStep('${s.id}',1)">↓</button>` : ''}
|
||||
<button class="wf-step-btn" title="Remove" onclick="removeStep('${s.id}')">✕</button>
|
||||
${i > 0 ? `<button class="wf-step-btn" title="Move up" onclick="moveStep('${s.id}',-1)">${wfIconSvg('up', 'icon-sm')}</button>` : ''}
|
||||
${i < steps.length-1 ? `<button class="wf-step-btn" title="Move down" onclick="moveStep('${s.id}',1)">${wfIconSvg('down', 'icon-sm')}</button>` : ''}
|
||||
<button class="wf-step-btn" title="Remove" onclick="removeStep('${s.id}')">${wfIconSvg('trash', 'icon-sm')}</button>
|
||||
</div>
|
||||
<span class="wf-step-chevron">▶</span>
|
||||
<span class="wf-step-chevron">${wfIconSvg('chevronRight', 'icon-sm')}</span>
|
||||
</div>
|
||||
<div class="wf-step-body">
|
||||
${buildStepFields(s)}
|
||||
@@ -893,7 +930,7 @@ async function runWorkflow() {
|
||||
const allSteps = [{type:'trigger',name:'Trigger'},...steps];
|
||||
logEl.innerHTML = allSteps.map((s,i) => `
|
||||
<div class="wf-log-step" id="log-${i}">
|
||||
<span class="wf-log-icon">${i===0 ? '⚡' : (STEP_ICONS[s.type]||'⚙️')}</span>
|
||||
<span class="wf-log-icon">${i===0 ? wfIconSvg('bolt', 'icon-sm') : (STEP_ICONS[s.type]||wfIconSvg('settings', 'icon-sm'))}</span>
|
||||
<div class="wf-log-body">
|
||||
<div class="wf-log-label">${escHtml(s.name||STEP_LABELS[s.type]||s.type)}</div>
|
||||
<div class="wf-log-detail" id="log-detail-${i}">Waiting…</div>
|
||||
@@ -929,7 +966,7 @@ async function runWorkflow() {
|
||||
summarise: 'Summary generated (312 words). Key topics: service delivery, payment terms, SLA.',
|
||||
rag_search: 'Found 5 relevant passages across 3 documents.',
|
||||
save_kb: 'Document ingested into knowledge base (embedding: 1,536 dimensions).',
|
||||
filter: 'Condition evaluated → true → continuing workflow.',
|
||||
filter: 'Condition evaluated as true. Continuing workflow.',
|
||||
email: 'Email queued for delivery to admin@cezentech.com.',
|
||||
http: 'POST 200 OK — {"status":"received","id":"evt_abc123"}'
|
||||
};
|
||||
@@ -972,6 +1009,6 @@ loadWorkflows();
|
||||
</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