Fix demo/showcase toggle: separate buttons, distinct modes

Problem: plain toggle set showcase=true, so demo always became showcase.
No way to enable basic demo mode separately.

Fix:
- Three explicit buttons: [Demo] [Showcase] [Off]
- Demo mode: active=true, showcase=false (team UI only)
- Showcase mode: active=true, showcase=true (full read-only admin)
- Off: both false
- Plain toggle cycles demo on/off without touching showcase
- Clear status text shows which mode and what it means

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
root 2026-03-26 05:26:48 -05:00
parent 732f29d836
commit ffd5e43709

View File

@ -461,7 +461,7 @@ def demo_toggle():
if not session.get("user_id") or not is_admin():
return jsonify({"error": "admin only"}), 403
data = request.json if request.is_json else {}
mode = data.get("mode", "toggle") # toggle, showcase, off
mode = data.get("mode", "toggle") # demo, showcase, off
if mode == "off":
_demo_mode["active"] = False
_demo_mode["showcase"] = False
@ -470,10 +470,20 @@ def demo_toggle():
_demo_mode["active"] = True
_demo_mode["showcase"] = True
_demo_mode["started_by"] = session.get("username")
elif mode == "demo":
_demo_mode["active"] = True
_demo_mode["showcase"] = False
_demo_mode["started_by"] = session.get("username")
else:
_demo_mode["active"] = not _demo_mode["active"]
_demo_mode["showcase"] = _demo_mode["active"]
_demo_mode["started_by"] = session.get("username") if _demo_mode["active"] else None
# plain toggle: cycle off → demo → off
if _demo_mode["active"]:
_demo_mode["active"] = False
_demo_mode["showcase"] = False
_demo_mode["started_by"] = None
else:
_demo_mode["active"] = True
_demo_mode["showcase"] = False
_demo_mode["started_by"] = session.get("username")
return jsonify({"active": _demo_mode["active"], "showcase": _demo_mode["showcase"]})
@ -3000,13 +3010,15 @@ ADMIN_HTML = r"""
<!-- DEMO & SECURITY TAB -->
<div id="tab-security" class="tab-content">
<div class="card" style="border-color:rgba(217,70,239,0.3)">
<h3 style="color:#d946ef">Showcase Mode
<div style="margin-left:auto;display:flex;gap:6px">
<button class="btn" id="admin-showcase-btn" onclick="toggleShowcase()">Enable Showcase</button>
<button class="btn btn-r" id="admin-demo-off" onclick="demoOff()" style="display:none">Turn Off</button>
<h3 style="color:#d946ef">Demo / Showcase
<div style="margin-left:auto;display:flex;gap:6px" id="demo-controls">
<button class="btn btn-g" onclick="setDemoMode('demo')">Demo</button>
<button class="btn" style="border-color:rgba(217,70,239,0.3);color:#d946ef" onclick="setDemoMode('showcase')">Showcase</button>
<button class="btn btn-r" onclick="setDemoMode('off')">Off</button>
</div>
</h3>
<p style="font-size:12px;color:var(--text2);margin-bottom:10px"><strong>Showcase mode</strong> gives visitors full read-only access to everything Admin, Monitor, Logs, Threat Intel, Lab, History. They can run teams, view enrichments, and trigger self-analysis reports. They <strong>cannot</strong> change settings, ban IPs, delete data, or modify configs. Perfect for demos.</p>
<p style="font-size:12px;color:var(--text2);margin-bottom:6px"><strong>Demo</strong> public can use Team UI and run modes. No admin access.</p>
<p style="font-size:12px;color:var(--text2);margin-bottom:10px"><strong>Showcase</strong> full read-only access to everything: Admin, Monitor, Logs, Threat Intel, Lab, History. Cannot change settings or delete data.</p>
<div id="demo-status-admin" style="font-size:13px">Status: <strong style="color:var(--text2)">Off</strong></div>
</div>
<div class="card">
@ -3251,39 +3263,21 @@ function switchTab(name) {
async function loadDemoStatus() {
var r = await fetch('/api/demo/status');
var d = await r.json();
var btn = document.getElementById('admin-showcase-btn');
var offBtn = document.getElementById('admin-demo-off');
var st = document.getElementById('demo-status-admin');
if (d.active && d.showcase) {
btn.textContent = 'Showcase Active';
btn.className = 'btn';
btn.style.cssText = 'margin-left:auto;border-color:#d946ef;color:#d946ef';
offBtn.style.display = '';
st.innerHTML = 'Status: <strong style="color:#d946ef">SHOWCASE MODE</strong> — full read-only access for visitors' + (d.started_by ? ' (by ' + d.started_by + ')' : '');
st.innerHTML = 'Status: <strong style="color:#d946ef">SHOWCASE</strong> — full read-only admin access' + (d.started_by ? ' (by ' + d.started_by + ')' : '');
} else if (d.active) {
btn.textContent = 'Demo Active';
btn.className = 'btn btn-g';
offBtn.style.display = '';
st.innerHTML = 'Status: <strong style="color:var(--green)">DEMO ON</strong>' + (d.started_by ? ' (by ' + d.started_by + ')' : '');
st.innerHTML = 'Status: <strong style="color:var(--green)">DEMO</strong> — team UI only' + (d.started_by ? ' (by ' + d.started_by + ')' : '');
} else {
btn.textContent = 'Enable Showcase';
btn.className = 'btn';
btn.style.cssText = 'border-color:rgba(217,70,239,0.3);color:#d946ef';
offBtn.style.display = 'none';
st.innerHTML = 'Status: <strong style="color:var(--text2)">Off</strong>';
}
}
async function toggleShowcase() {
await fetch('/api/demo/toggle', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({mode:'showcase'})});
async function setDemoMode(mode) {
await fetch('/api/demo/toggle', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({mode:mode})});
loadDemoStatus();
toast('Showcase mode enabled', true);
}
async function demoOff() {
await fetch('/api/demo/toggle', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({mode:'off'})});
loadDemoStatus();
toast('Demo mode disabled', true);
var labels = {demo:'Demo enabled',showcase:'Showcase enabled',off:'Turned off'};
toast(labels[mode] || mode, true);
}
async function loadAllowlist() {