455 lines
21 KiB
HTML
455 lines
21 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Prompt Library — Nexus One AI Portal</title>
|
|
<link rel="stylesheet" href="style.css?v=4">
|
|
<style>
|
|
/* ── Admin bar ── */
|
|
.pl-admin-bar { display:none; align-items:center; gap:10px; margin-bottom:18px; }
|
|
.pl-admin-bar.visible { display:flex; }
|
|
.pl-admin-btn { padding:9px 18px; border-radius:8px; font-size:13px; font-weight:600; cursor:pointer; border:none; font-family:inherit; transition:.15s; }
|
|
.pl-admin-btn.primary { background:var(--teal); color:var(--ink); }
|
|
.pl-admin-btn.primary:hover { background:#0B7A70; }
|
|
|
|
/* ── Prompt card edit overlay ── */
|
|
.pl-card-actions { display:none; gap:6px; margin-top:10px; }
|
|
.pl-card.admin-mode .pl-card-actions { display:flex; }
|
|
.pl-edit-btn, .pl-del-btn { padding:5px 12px; border-radius:6px; font-size:11px; font-weight:700; cursor:pointer; border:none; font-family:inherit; }
|
|
.pl-edit-btn { background:rgba(59,130,246,.1); color:#1D4ED8; }
|
|
.pl-edit-btn:hover { background:#DBEAFE; }
|
|
.pl-del-btn { background:rgba(185,28,28,.08); color:#B91C1C; }
|
|
.pl-del-btn:hover { background:#DC2626; color:var(--ink); }
|
|
|
|
/* ── Modal ── */
|
|
.pl-modal-bg { position:fixed; inset:0; background:rgba(0,0,0,.45); z-index:1000; display:none; align-items:center; justify-content:center; padding:20px; }
|
|
.pl-modal-bg.open { display:flex; }
|
|
.pl-modal { background:var(--navy2); border-radius:16px; padding:32px; width:100%; max-width:600px; box-shadow:0 20px 60px rgba(0,0,0,.25); }
|
|
.pl-modal h3 { margin:0 0 20px; font-size:18px; color:var(--ink); }
|
|
.pl-modal label { display:block; font-size:12px; font-weight:600; color:var(--med); margin-bottom:5px; }
|
|
.pl-modal input, .pl-modal select, .pl-modal textarea {
|
|
width:100%; padding:9px 12px; border:1.5px solid var(--bdr); border-radius:8px;
|
|
font-size:13px; font-family:inherit; outline:none; color:var(--ink); box-sizing:border-box; margin-bottom:14px;
|
|
}
|
|
.pl-modal input:focus, .pl-modal select:focus, .pl-modal textarea:focus { border-color:var(--teal); }
|
|
.pl-modal textarea { min-height:140px; resize:vertical; }
|
|
.pl-modal-row { display:flex; gap:10px; }
|
|
.pl-modal-row > div { flex:1; }
|
|
.pl-modal-footer { display:flex; gap:10px; justify-content:flex-end; margin-top:4px; }
|
|
.pl-modal-footer button { padding:9px 20px; border-radius:8px; font-size:13px; font-weight:600; cursor:pointer; border:none; font-family:inherit; }
|
|
.pl-modal-footer .save { background:var(--teal); color:var(--ink); }
|
|
.pl-modal-footer .save:hover { background:#0B7A70; }
|
|
.pl-modal-footer .cancel { background:rgba(255,255,255,.03); color:var(--med); border:1px solid var(--bdr); }
|
|
.pl-modal-footer .cancel:hover { border-color:var(--teal); color:var(--teal); }
|
|
|
|
/* ── Toast ── */
|
|
#pl-toast { position:fixed; bottom:24px; right:24px; background:#1E3A5F; color:var(--ink); padding:12px 20px; border-radius:10px; font-size:13px; display:none; z-index:9999; box-shadow:0 8px 24px rgba(0,0,0,.2); max-width:340px; }
|
|
#pl-toast.err { background:#7F1D1D; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<header class="topnav">
|
|
<a href="index.html" class="brand">Nexus One <span>AI</span></a>
|
|
<nav>
|
|
<a href="index.html">Home</a>
|
|
<a href="quickstart.html">Quick Start</a>
|
|
<a href="prompts.html" class="active">Prompt Library</a>
|
|
<a href="usecases.html">Use Cases</a>
|
|
<span class="nav-sep"></span>
|
|
<div class="nav-dropdown">
|
|
<button class="nav-drop-btn">Help ▾</button>
|
|
<div class="nav-drop-menu">
|
|
<span class="nav-drop-cat">LEARN /</span>
|
|
<a href="quickstart.html">Quick Start</a>
|
|
<a href="models.html">Models</a>
|
|
<span class="nav-drop-cat">SUPPORT /</span>
|
|
<a href="troubleshooting.html">Troubleshoot</a>
|
|
<a href="faq.html">FAQ</a>
|
|
<span class="nav-drop-cat">MORE /</span>
|
|
<a href="glossary.html">Glossary</a>
|
|
<a href="whats-new.html">What's New</a>
|
|
</div>
|
|
</div>
|
|
<div class="nav-dropdown">
|
|
<button class="nav-drop-btn">Admin ▾</button>
|
|
<div class="nav-drop-menu nav-drop-menu-wide">
|
|
<span class="nav-drop-cat">DOCS /</span>
|
|
<a href="security.html">Security & Privacy</a>
|
|
<a href="admin.html">Admin Guide</a>
|
|
<span class="nav-drop-cat">MONITOR /</span>
|
|
<a href="dashboard.html">Dashboard</a>
|
|
<a href="analytics.html">Usage Analytics</a>
|
|
<a href="audit.html">Audit Log</a>
|
|
<a href="feedback.html">Feedback & Ratings</a>
|
|
<span class="nav-drop-cat">MANAGE /</span>
|
|
<a href="users.html">Users</a>
|
|
<a href="teams.html">Teams</a>
|
|
<a href="models-admin.html">Model Manager</a>
|
|
<a href="training.html">Training</a>
|
|
<a href="knowledge.html">Knowledge Base</a>
|
|
<span class="nav-drop-cat">TOOLS /</span>
|
|
<a href="apikeys.html">API Keys</a>
|
|
<a href="benchmark.html">Benchmarking</a>
|
|
<a href="model-compare.html">Model Compare</a>
|
|
<a href="api-playground.html">API Playground</a>
|
|
<a href="guardrails.html">Guardrails</a>
|
|
<a href="rag-quality.html">RAG Quality</a>
|
|
<a href="router.html">Model Router</a>
|
|
<a href="connectors.html">Connectors</a>
|
|
<span class="nav-drop-cat">SYSTEM /</span>
|
|
<a href="console.html">Console</a>
|
|
<a href="settings.html">Settings</a>
|
|
</div>
|
|
</div>
|
|
<div class="nav-dropdown">
|
|
<button class="nav-drop-btn">AI Tools ▾</button>
|
|
<div class="nav-drop-menu">
|
|
<span class="nav-drop-cat">INTELLIGENCE /</span>
|
|
<a href="documents.html">Document Intelligence</a>
|
|
<a href="chat-multi.html">Multimodal Chat</a>
|
|
<a href="prompt-studio.html">Prompt Studio</a>
|
|
<a href="meeting.html">Meeting Assistant</a>
|
|
<span class="nav-drop-cat">AUTOMATION /</span>
|
|
<a href="agents.html">Agent Builder</a>
|
|
<a href="schedules.html">Scheduled Jobs</a>
|
|
<a href="workflows.html">Workflow Automation</a>
|
|
<span class="nav-drop-cat">QUALITY /</span>
|
|
<a href="evals.html">AI Eval Suite</a>
|
|
<a href="chatrooms.html">Chat Rooms</a>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
<a href="notifications.html" style="position:relative">🔔</a>
|
|
<span class="badge" data-brand="tier">Basic Tier</span>
|
|
<div id="nav-org-logo" class="nav-org-logo"></div>
|
|
</header>
|
|
|
|
<div class="page-hero">
|
|
<div class="label">Prompt Library</div>
|
|
<h1>Ready-to-Use AI Prompts</h1>
|
|
<p>Copy any prompt, paste it into <a href="http://ai.local:3001" target="_blank" style="color:#0F766E">Open WebUI</a>, and get results immediately. Upload the relevant document first where indicated.</p>
|
|
</div>
|
|
|
|
<div class="content">
|
|
|
|
<!-- Admin bar — visible only to admins -->
|
|
<div class="pl-admin-bar" id="pl-admin-bar">
|
|
<button class="pl-admin-btn primary" onclick="openModal()">+ Add Prompt</button>
|
|
<span id="pl-admin-mode-toggle" style="font-size:13px;color:var(--lt);cursor:pointer;" onclick="toggleAdminMode()">⚙ Edit mode: <strong id="admin-mode-label">Off</strong></span>
|
|
</div>
|
|
|
|
<!-- SEARCH + FILTERS -->
|
|
<div class="pl-controls">
|
|
<input type="text" class="ts-search" id="pl-search" placeholder="Search prompts — e.g. 'tender', 'summary', 'complaint'…" oninput="filterCards()">
|
|
<div class="pl-filters" id="pl-filters">
|
|
<button class="pl-filter active" data-cat="all" onclick="setFilter('all',this)">All <span class="pl-count" id="count-all"></span></button>
|
|
<button class="pl-filter" data-cat="procurement" onclick="setFilter('procurement',this)">📋 Procurement</button>
|
|
<button class="pl-filter" data-cat="hr" onclick="setFilter('hr',this)">👥 HR & Admin</button>
|
|
<button class="pl-filter" data-cat="legal" onclick="setFilter('legal',this)">⚖️ Legal & Compliance</button>
|
|
<button class="pl-filter" data-cat="operations" onclick="setFilter('operations',this)">⚙️ Operations</button>
|
|
<button class="pl-filter" data-cat="citizen" onclick="setFilter('citizen',this)">🏛️ Citizen Services</button>
|
|
<button class="pl-filter" data-cat="finance" onclick="setFilter('finance',this)">💰 Finance</button>
|
|
<button class="pl-filter" data-cat="comms" onclick="setFilter('comms',this)">✉️ Communications</button>
|
|
<button class="pl-filter" data-cat="management" onclick="setFilter('management',this)">📊 Management</button>
|
|
<button class="pl-filter" data-cat="general" onclick="setFilter('general',this)">🔧 General</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="pl-tip">
|
|
<span>💡</span>
|
|
<span>Prompts with <span class="upload-badge-inline">📎 Upload doc</span> work best when you first upload the relevant file using the <strong>+</strong> button in Open WebUI. Replace text in <span class="placeholder-eg">[brackets]</span> with your own details.</span>
|
|
</div>
|
|
|
|
<!-- PROMPT CARDS -->
|
|
<div class="pl-grid" id="pl-grid">
|
|
<div class="pl-empty" id="pl-loading" style="padding:48px 0">
|
|
<div style="font-size:32px;margin-bottom:10px">⏳</div>
|
|
<div>Loading prompts…</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="pl-empty" class="pl-empty" style="display:none">
|
|
<div style="font-size:40px;margin-bottom:12px">🔍</div>
|
|
<div>No prompts match your search. Try different keywords.</div>
|
|
</div>
|
|
|
|
<!-- TIPS -->
|
|
<div class="section-title" style="margin-top:48px">Tips for better results</div>
|
|
<div class="info-grid">
|
|
<div class="info-card">
|
|
<h4>Be specific</h4>
|
|
<p>The more context you give, the better the output. Instead of "summarise this document", say "summarise this 60-page infrastructure tender and list the 5 most important technical specifications for our engineering team."</p>
|
|
</div>
|
|
<div class="info-card">
|
|
<h4>Specify the format you want</h4>
|
|
<p>Tell the AI exactly how to format the output: "as a table", "in bullet points", "as a numbered checklist", "in 3 paragraphs", "under 200 words". It will follow your format instructions reliably.</p>
|
|
</div>
|
|
<div class="info-card">
|
|
<h4>Upload the document first</h4>
|
|
<p>For any prompt marked <span class="upload-badge-inline">📎 Upload doc</span>, click the + button in Open WebUI and upload your file before sending the prompt. The AI will read the actual document rather than guessing from general knowledge.</p>
|
|
</div>
|
|
<div class="info-card">
|
|
<h4>Iterate — ask follow-ups</h4>
|
|
<p>The first response is rarely the final version. Follow up: "Make this more formal", "Shorten it to 100 words", "Add a column for priority", "Translate this into Hindi". The AI remembers the full conversation.</p>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- Modal -->
|
|
<div class="pl-modal-bg" id="pl-modal-bg">
|
|
<div class="pl-modal">
|
|
<h3 id="modal-title">Add Prompt</h3>
|
|
<label>Title</label>
|
|
<input type="text" id="m-title" placeholder="e.g. Summarise a tender document">
|
|
<label>Prompt Text</label>
|
|
<textarea id="m-text" placeholder="Enter the full prompt text…"></textarea>
|
|
<div class="pl-modal-row">
|
|
<div>
|
|
<label>Category</label>
|
|
<select id="m-cat">
|
|
<option value="procurement">📋 Procurement</option>
|
|
<option value="hr">👥 HR & Admin</option>
|
|
<option value="legal">⚖️ Legal & Compliance</option>
|
|
<option value="operations">⚙️ Operations</option>
|
|
<option value="citizen">🏛️ Citizen Services</option>
|
|
<option value="finance">💰 Finance</option>
|
|
<option value="comms">✉️ Communications</option>
|
|
<option value="management">📊 Management</option>
|
|
<option value="general">🔧 General</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label>Needs document upload?</label>
|
|
<select id="m-upload">
|
|
<option value="0">No</option>
|
|
<option value="1">Yes — show upload badge</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<label>Tags <span style="font-weight:400;color:var(--lt)">(space-separated, used for search)</span></label>
|
|
<input type="text" id="m-tags" placeholder="e.g. tender summary rfp">
|
|
<div class="pl-modal-footer">
|
|
<button class="cancel" onclick="closeModal()">Cancel</button>
|
|
<button class="save" onclick="savePrompt()">Save</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="pl-toast"></div>
|
|
|
|
<footer>
|
|
<p>Nexus One AI · Powered by Cezen · <span data-brand="tier">Basic Tier</span></p>
|
|
<p>Questions? <a href="mailto:support@cezentech.com">support@cezentech.com</a> · <a href="https://cezentech.com" target="_blank">cezentech.com</a></p>
|
|
</footer>
|
|
|
|
<script>
|
|
const _API = '/api';
|
|
const CAT_META = {
|
|
procurement: { label:'📋 Procurement', cls:'procurement' },
|
|
hr: { label:'👥 HR & Admin', cls:'hr' },
|
|
legal: { label:'⚖️ Legal & Compliance', cls:'legal' },
|
|
operations: { label:'⚙️ Operations', cls:'operations' },
|
|
citizen: { label:'🏛️ Citizen Services', cls:'citizen' },
|
|
finance: { label:'💰 Finance', cls:'finance' },
|
|
comms: { label:'✉️ Communications', cls:'comms' },
|
|
management: { label:'📊 Management', cls:'management' },
|
|
general: { label:'🔧 General', cls:'general' },
|
|
};
|
|
|
|
let allPrompts = [];
|
|
let activeFilter = 'all';
|
|
let adminMode = false;
|
|
let editingId = null;
|
|
let isAdmin = false;
|
|
|
|
// ── Load ──────────────────────────────────────────────────────────────────────
|
|
|
|
async function loadPrompts() {
|
|
try {
|
|
const res = await fetch(`${_API}/prompts`);
|
|
if (!res.ok) throw new Error('Failed to load prompts');
|
|
allPrompts = await res.json();
|
|
renderCards();
|
|
} catch (e) {
|
|
document.getElementById('pl-loading').innerHTML =
|
|
'<div style="color:#B91C1C">Failed to load prompts. Is the backend running?</div>';
|
|
}
|
|
}
|
|
|
|
// ── Render ─────────────────────────────────────────────────────────────────────
|
|
|
|
function renderCards() {
|
|
document.getElementById('pl-loading').remove?.();
|
|
document.getElementById('pl-loading') && (document.getElementById('pl-loading').style.display='none');
|
|
const grid = document.getElementById('pl-grid');
|
|
grid.innerHTML = '';
|
|
const q = document.getElementById('pl-search').value.toLowerCase();
|
|
let visible = 0;
|
|
|
|
allPrompts.forEach(p => {
|
|
const catMatch = activeFilter === 'all' || p.category === activeFilter;
|
|
const searchText = (p.title + ' ' + p.prompt_text + ' ' + p.tags).toLowerCase();
|
|
const textMatch = !q || searchText.includes(q);
|
|
if (!catMatch || !textMatch) return;
|
|
visible++;
|
|
const meta = CAT_META[p.category] || CAT_META.general;
|
|
const card = document.createElement('div');
|
|
card.className = 'pl-card' + (adminMode ? ' admin-mode' : '');
|
|
card.dataset.cat = p.category;
|
|
card.dataset.id = p.id;
|
|
card.innerHTML = `
|
|
<div class="pl-card-header">
|
|
<span class="pl-cat-tag ${meta.cls}">${meta.label}</span>
|
|
${p.needs_upload ? '<span class="upload-badge">📎 Upload doc</span>' : ''}
|
|
</div>
|
|
<div class="pl-title">${escHtml(p.title)}</div>
|
|
<div class="pl-prompt" id="pt-${p.id}">${escHtml(p.prompt_text)}</div>
|
|
<button class="pl-copy-btn" onclick="copyPrompt('pt-${p.id}', this)">Copy Prompt</button>
|
|
<div class="pl-card-actions">
|
|
<button class="pl-edit-btn" onclick="openModal(${p.id})">✏ Edit</button>
|
|
<button class="pl-del-btn" onclick="deletePrompt(${p.id}, '${escHtml(p.title).replace(/'/g,"\\'")}')">🗑 Delete</button>
|
|
</div>
|
|
`;
|
|
grid.appendChild(card);
|
|
});
|
|
|
|
document.getElementById('pl-empty').style.display = visible === 0 ? '' : 'none';
|
|
document.getElementById('count-all').textContent = allPrompts.length;
|
|
}
|
|
|
|
// ── Filter / Search ───────────────────────────────────────────────────────────
|
|
|
|
function setFilter(cat, btn) {
|
|
activeFilter = cat;
|
|
document.querySelectorAll('.pl-filter').forEach(b => b.classList.remove('active'));
|
|
btn.classList.add('active');
|
|
renderCards();
|
|
}
|
|
|
|
function filterCards() { renderCards(); }
|
|
|
|
// ── Admin mode toggle ─────────────────────────────────────────────────────────
|
|
|
|
function toggleAdminMode() {
|
|
adminMode = !adminMode;
|
|
document.getElementById('admin-mode-label').textContent = adminMode ? 'On' : 'Off';
|
|
renderCards();
|
|
}
|
|
|
|
// ── Copy ──────────────────────────────────────────────────────────────────────
|
|
|
|
function copyPrompt(id, btn) {
|
|
const text = document.getElementById(id).textContent.trim();
|
|
navigator.clipboard.writeText(text).then(() => {
|
|
const orig = btn.textContent;
|
|
btn.textContent = '✓ Copied!';
|
|
btn.classList.add('copied');
|
|
setTimeout(() => { btn.textContent = orig; btn.classList.remove('copied'); }, 2000);
|
|
});
|
|
}
|
|
|
|
// ── Modal ─────────────────────────────────────────────────────────────────────
|
|
|
|
function openModal(id) {
|
|
editingId = id || null;
|
|
document.getElementById('modal-title').textContent = id ? 'Edit Prompt' : 'Add Prompt';
|
|
if (id) {
|
|
const p = allPrompts.find(x => x.id === id);
|
|
document.getElementById('m-title').value = p.title;
|
|
document.getElementById('m-text').value = p.prompt_text;
|
|
document.getElementById('m-cat').value = p.category;
|
|
document.getElementById('m-upload').value = p.needs_upload;
|
|
document.getElementById('m-tags').value = p.tags;
|
|
} else {
|
|
document.getElementById('m-title').value = '';
|
|
document.getElementById('m-text').value = '';
|
|
document.getElementById('m-cat').value = 'general';
|
|
document.getElementById('m-upload').value = '0';
|
|
document.getElementById('m-tags').value = '';
|
|
}
|
|
document.getElementById('pl-modal-bg').classList.add('open');
|
|
}
|
|
|
|
function closeModal() {
|
|
document.getElementById('pl-modal-bg').classList.remove('open');
|
|
}
|
|
|
|
async function savePrompt() {
|
|
const body = {
|
|
title: document.getElementById('m-title').value.trim(),
|
|
prompt_text: document.getElementById('m-text').value.trim(),
|
|
category: document.getElementById('m-cat').value,
|
|
tags: document.getElementById('m-tags').value.trim(),
|
|
needs_upload: parseInt(document.getElementById('m-upload').value),
|
|
};
|
|
if (!body.title || !body.prompt_text) { toast('Title and prompt text are required', true); return; }
|
|
|
|
const url = editingId ? `${_API}/prompts/${editingId}` : `${_API}/prompts`;
|
|
const method = editingId ? 'PUT' : 'POST';
|
|
try {
|
|
const res = await fetch(url, { method, headers:{'Content-Type':'application/json'}, body: JSON.stringify(body) });
|
|
if (!res.ok) throw new Error((await res.json()).detail || 'Save failed');
|
|
const saved = await res.json();
|
|
if (editingId) {
|
|
allPrompts = allPrompts.map(p => p.id === editingId ? saved : p);
|
|
} else {
|
|
allPrompts.push(saved);
|
|
}
|
|
closeModal();
|
|
renderCards();
|
|
toast(editingId ? 'Prompt updated' : 'Prompt added');
|
|
} catch(e) { toast(e.message, true); }
|
|
}
|
|
|
|
async function deletePrompt(id, title) {
|
|
if (!confirm(`Delete prompt "${title}"?`)) return;
|
|
try {
|
|
const res = await fetch(`${_API}/prompts/${id}`, { method:'DELETE' });
|
|
if (!res.ok) throw new Error((await res.json()).detail || 'Delete failed');
|
|
allPrompts = allPrompts.filter(p => p.id !== id);
|
|
renderCards();
|
|
toast('Prompt deleted');
|
|
} catch(e) { toast(e.message, true); }
|
|
}
|
|
|
|
// ── Toast ─────────────────────────────────────────────────────────────────────
|
|
|
|
function toast(msg, err) {
|
|
const el = document.getElementById('pl-toast');
|
|
el.textContent = msg;
|
|
el.className = err ? 'err' : '';
|
|
el.style.display = 'block';
|
|
setTimeout(() => el.style.display = 'none', 3000);
|
|
}
|
|
|
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
|
|
function escHtml(s) {
|
|
return (s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
|
}
|
|
|
|
// Close modal on background click
|
|
document.getElementById('pl-modal-bg').addEventListener('click', function(e) {
|
|
if (e.target === this) closeModal();
|
|
});
|
|
|
|
loadPrompts();
|
|
</script>
|
|
|
|
<script src="auth.js"></script>
|
|
<script>
|
|
// Show admin bar after auth resolves
|
|
document.addEventListener('cezenAuthReady', function() {
|
|
if (window.cezenUser && window.cezenUser.role === 'admin') {
|
|
isAdmin = true;
|
|
document.getElementById('pl-admin-bar').classList.add('visible');
|
|
}
|
|
});
|
|
</script>
|
|
<script src="branding.js"></script>
|
|
</body>
|
|
</html>
|