405 lines
20 KiB
HTML
405 lines
20 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Feedback & Ratings — Nexus One AI</title>
|
|
<link rel="stylesheet" href="style.css?v=4">
|
|
<style>
|
|
.fb-wrap { max-width: 1100px; margin: 0 auto; padding: 28px 28px 60px; }
|
|
.fb-header { display: flex; align-items: center; gap: 14px; margin-bottom: 24px; }
|
|
.fb-title { font-size: 22px; font-weight: 800; color: var(--ink); flex: 1; }
|
|
.fb-export-btn {
|
|
padding: 8px 16px; border-radius: 9px; border: 1.5px solid var(--bdr);
|
|
background: none; color: var(--med); font-family: inherit; font-size: 13px;
|
|
font-weight: 600; cursor: pointer; transition: .15s;
|
|
}
|
|
.fb-export-btn:hover { border-color: var(--purple); color: var(--purple); }
|
|
|
|
/* Overview cards */
|
|
.fb-overview { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 14px; margin-bottom: 28px; }
|
|
.fb-overview-card {
|
|
background: var(--navy2); border: 1.5px solid var(--bdr);
|
|
border-radius: 12px; padding: 16px 18px;
|
|
}
|
|
.fb-ov-label { font-size: 11px; font-weight: 700; color: var(--lt); text-transform: uppercase; letter-spacing: .4px; margin-bottom: 6px; }
|
|
.fb-ov-val { font-size: 28px; font-weight: 800; color: var(--purple); line-height: 1; }
|
|
.fb-ov-sub { font-size: 12px; color: var(--lt); margin-top: 5px; }
|
|
|
|
/* Star display */
|
|
.fb-stars { color: #f59e0b; letter-spacing: 2px; }
|
|
.fb-stars.lg { font-size: 28px; }
|
|
|
|
/* Grid row */
|
|
.fb-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 28px; }
|
|
@media(max-width:780px){ .fb-grid { grid-template-columns: 1fr; } }
|
|
|
|
.fb-panel {
|
|
background: var(--navy2); border: 1.5px solid var(--bdr);
|
|
border-radius: 14px; padding: 18px 20px;
|
|
}
|
|
.fb-panel-title { font-size: 14px; font-weight: 800; color: var(--ink); margin-bottom: 16px; }
|
|
|
|
/* Rating distribution bar */
|
|
.fb-dist-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
|
|
.fb-dist-label { font-size: 12px; color: var(--med); width: 40px; flex-shrink: 0; }
|
|
.fb-dist-bar-wrap { flex: 1; background: var(--bdr); border-radius: 6px; height: 10px; overflow: hidden; }
|
|
.fb-dist-bar { height: 100%; border-radius: 6px; background: var(--purple); transition: width .4s; }
|
|
.fb-dist-bar.green { background: #10b981; }
|
|
.fb-dist-bar.lime { background: #84cc16; }
|
|
.fb-dist-bar.yellow { background: #f59e0b; }
|
|
.fb-dist-bar.orange { background: #f97316; }
|
|
.fb-dist-bar.red { background: #ef4444; }
|
|
.fb-dist-n { font-size: 12px; color: var(--lt); width: 28px; text-align: right; }
|
|
|
|
/* Model table */
|
|
.fb-model-table { width: 100%; border-collapse: collapse; }
|
|
.fb-model-table th { font-size: 11px; font-weight: 700; color: var(--lt); text-transform: uppercase; letter-spacing: .4px; padding: 0 0 8px; text-align: left; border-bottom: 2px solid var(--bdr); }
|
|
.fb-model-table td { padding: 9px 0; border-bottom: 1px solid var(--bdr); font-size: 13px; color: var(--med); vertical-align: middle; }
|
|
.fb-model-table td:first-child { font-weight: 700; color: var(--ink); }
|
|
.fb-score-bar-wrap { width: 80px; background: var(--bdr); border-radius: 6px; height: 8px; display: inline-block; overflow: hidden; margin-right: 8px; vertical-align: middle; }
|
|
.fb-score-bar { height: 100%; border-radius: 6px; background: var(--purple); }
|
|
|
|
/* Feed */
|
|
.fb-feed-filters { display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
|
|
.fb-feed-filter {
|
|
padding: 5px 14px; border-radius: 20px; font-size: 12px; font-weight: 600;
|
|
cursor: pointer; border: 1.5px solid var(--bdr); background: none; color: var(--med);
|
|
font-family: inherit; transition: .12s;
|
|
}
|
|
.fb-feed-filter.active { border-color: var(--purple); background: rgba(124,58,237,.1); color: var(--purple); }
|
|
|
|
.fb-feed { display: flex; flex-direction: column; gap: 10px; }
|
|
.fb-feedback-card {
|
|
background: var(--bg); border: 1.5px solid var(--bdr);
|
|
border-radius: 11px; padding: 14px 16px;
|
|
transition: .12s;
|
|
}
|
|
.fb-feedback-card:hover { border-color: rgba(124,58,237,.3); }
|
|
.fb-feedback-card.low { border-left: 3px solid #ef4444; }
|
|
.fb-card-header { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; flex-wrap: wrap; }
|
|
.fb-card-user { font-size: 13px; font-weight: 700; color: var(--ink); }
|
|
.fb-card-model { font-size: 11px; font-weight: 700; padding: 2px 9px; border-radius: 20px; background: rgba(124,58,237,.1); color: var(--purple); }
|
|
.fb-card-page { font-size: 11px; color: var(--lt); padding: 2px 8px; border-radius: 20px; background: var(--bdr); }
|
|
.fb-card-time { font-size: 11px; color: var(--lt); margin-left: auto; }
|
|
.fb-card-snippet { font-size: 12px; color: var(--lt); font-style: italic; margin-bottom: 7px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
.fb-card-comment { font-size: 13px; color: var(--med); line-height: 1.5; }
|
|
.fb-card-footer { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
|
|
.fb-card-del {
|
|
margin-left: auto; background: none; border: none; cursor: pointer;
|
|
font-size: 12px; color: var(--lt); padding: 3px 8px; border-radius: 6px;
|
|
transition: .12s; font-family: inherit;
|
|
}
|
|
.fb-card-del:hover { color: #ef4444; }
|
|
|
|
/* Section divider */
|
|
.fb-section-header { font-size: 16px; font-weight: 800; color: var(--ink); margin: 28px 0 16px; display: flex; align-items: center; gap: 10px; }
|
|
|
|
/* Toast */
|
|
#fb-toast { position:fixed;bottom:24px;right:24px;z-index:9999;background:var(--ink);color:white;padding:10px 18px;border-radius:10px;font-size:13px;font-weight:600;opacity:0;transform:translateY(8px);transition:.25s;pointer-events:none; }
|
|
#fb-toast.show { opacity:1;transform:translateY(0); }
|
|
</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">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 active">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" class="active">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>
|
|
<a href="notifications.html" style="position:relative">🔔</a>
|
|
</nav>
|
|
<span class="badge" data-brand="tier">Basic Tier</span>
|
|
<div id="nav-org-logo" class="nav-org-logo"></div>
|
|
</header>
|
|
|
|
<div class="fb-wrap">
|
|
<div class="fb-header">
|
|
<div class="fb-title">⭐ Feedback & Ratings</div>
|
|
<button class="fb-export-btn" onclick="exportCSV()">📥 Export CSV</button>
|
|
</div>
|
|
|
|
<!-- Overview -->
|
|
<div class="fb-overview" id="fb-overview">
|
|
<div class="fb-overview-card">
|
|
<div class="fb-ov-label">Total Ratings</div>
|
|
<div class="fb-ov-val" id="ov-total">—</div>
|
|
<div class="fb-ov-sub">from all users</div>
|
|
</div>
|
|
<div class="fb-overview-card">
|
|
<div class="fb-ov-label">Average Rating</div>
|
|
<div class="fb-ov-val" id="ov-avg">—</div>
|
|
<div class="fb-stars" id="ov-stars">—</div>
|
|
</div>
|
|
<div class="fb-overview-card">
|
|
<div class="fb-ov-label">5-Star Responses</div>
|
|
<div class="fb-ov-val" id="ov-five">—</div>
|
|
<div class="fb-ov-sub" id="ov-five-pct">—</div>
|
|
</div>
|
|
<div class="fb-overview-card">
|
|
<div class="fb-ov-label">Low Ratings (1-2★)</div>
|
|
<div class="fb-ov-val" id="ov-low" style="color:#ef4444">—</div>
|
|
<div class="fb-ov-sub" id="ov-low-pct">need attention</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Charts row -->
|
|
<div class="fb-grid">
|
|
<!-- Distribution -->
|
|
<div class="fb-panel">
|
|
<div class="fb-panel-title">Rating Distribution</div>
|
|
<div id="fb-dist">Loading…</div>
|
|
</div>
|
|
<!-- By model -->
|
|
<div class="fb-panel">
|
|
<div class="fb-panel-title">Average Score by Model</div>
|
|
<table class="fb-model-table" id="fb-model-table">
|
|
<thead><tr><th>Model</th><th>Avg</th><th>Count</th></tr></thead>
|
|
<tbody id="fb-model-tbody"><tr><td colspan="3" style="color:var(--lt)">Loading…</td></tr></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Feed -->
|
|
<div class="fb-section-header">Recent Feedback</div>
|
|
<div class="fb-feed-filters">
|
|
<button class="fb-feed-filter active" onclick="setFeedFilter('all', this)">All</button>
|
|
<button class="fb-feed-filter" onclick="setFeedFilter('low', this)">⚠️ Low (1-2★)</button>
|
|
<button class="fb-feed-filter" onclick="setFeedFilter('high', this)">✅ High (4-5★)</button>
|
|
<button class="fb-feed-filter" onclick="setFeedFilter('comment', this)">💬 With comments</button>
|
|
</div>
|
|
<div class="fb-feed" id="fb-feed">
|
|
<p style="color:var(--lt)">Loading…</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="fb-toast"></div>
|
|
|
|
<script>
|
|
const MOCK_SUMMARY = {
|
|
total: 142, avg_rating: 3.8,
|
|
by_rating: [{rating:1,n:6},{rating:2,n:11},{rating:3,n:28},{rating:4,n:54},{rating:5,n:43}],
|
|
by_model: [
|
|
{model:'mistral:7b',n:38,avg:4.2,min:2},{model:'llama3.1:70b',n:45,avg:4.0,min:1},
|
|
{model:'llama3.1:8b',n:32,avg:3.5,min:1},{model:'codellama:34b',n:18,avg:3.8,min:2},
|
|
{model:'gemma2:9b',n:9,avg:3.2,min:1},
|
|
],
|
|
low_rated: [
|
|
{id:12,username:'ravi.kumar',model:'llama3.1:8b',rating:1,comment:'Completely wrong answer on GST calculation. Very misleading.',prompt_snippet:'What is the GST on advisory services?',page:'chat-multi',created_at:new Date(Date.now()-1*24*3600000).toISOString()},
|
|
{id:18,username:'priya.nair',model:'llama3.1:8b',rating:2,comment:'Response was too generic. Did not use the uploaded document at all.',prompt_snippet:'Summarise the attached tender document',page:'documents',created_at:new Date(Date.now()-2*24*3600000).toISOString()},
|
|
{id:23,username:'anand.m',model:'gemma2:9b',rating:2,comment:'Hallucinated a policy that does not exist.',prompt_snippet:'What is our leave encashment policy?',page:'chat-multi',created_at:new Date(Date.now()-3*24*3600000).toISOString()},
|
|
],
|
|
recent: [
|
|
{id:55,username:'deepa.v',model:'mistral:7b',rating:5,comment:'Saved me hours on the vendor comparison!',prompt_snippet:'Compare these two proposals for the ERP project',page:'documents',created_at:new Date(Date.now()-30*60000).toISOString()},
|
|
{id:54,username:'kiran.rao',model:'llama3.1:70b',rating:4,comment:'Great structured output. Minor hallucination on one clause.',prompt_snippet:'Extract penalty clauses from the contract',page:'documents',created_at:new Date(Date.now()-2*3600000).toISOString()},
|
|
{id:53,username:'ravi.kumar',model:'llama3.1:70b',rating:5,comment:'',prompt_snippet:'Write a response to the vendor about the delay',page:'chat-multi',created_at:new Date(Date.now()-5*3600000).toISOString()},
|
|
{id:52,username:'priya.nair',model:'mistral:7b',rating:4,comment:'Accurate and concise.',prompt_snippet:'List mandatory bid documents for this RFP',page:'documents',created_at:new Date(Date.now()-8*3600000).toISOString()},
|
|
{id:51,username:'anand.m',model:'codellama:34b',rating:3,comment:'Code was correct but missing error handling.',prompt_snippet:'Write a Python script to parse the audit CSV',page:'prompt-studio',created_at:new Date(Date.now()-1*24*3600000).toISOString()},
|
|
{id:12,username:'ravi.kumar',model:'llama3.1:8b',rating:1,comment:'Completely wrong answer on GST calculation.',prompt_snippet:'What is the GST on advisory services?',page:'chat-multi',created_at:new Date(Date.now()-2*24*3600000).toISOString()},
|
|
{id:18,username:'priya.nair',model:'llama3.1:8b',rating:2,comment:'Response was too generic.',prompt_snippet:'Summarise the attached tender document',page:'documents',created_at:new Date(Date.now()-3*24*3600000).toISOString()},
|
|
]
|
|
};
|
|
|
|
let feedFilter = 'all';
|
|
let feedData = [];
|
|
|
|
function stars(n, max=5) {
|
|
return '★'.repeat(Math.round(n)) + '☆'.repeat(max - Math.round(n));
|
|
}
|
|
|
|
function timeAgo(iso) {
|
|
const d = (Date.now() - new Date(iso)) / 1000;
|
|
if (d < 60) return `${Math.floor(d)}s ago`;
|
|
if (d < 3600) return `${Math.floor(d/60)}m ago`;
|
|
if (d < 86400) return `${Math.floor(d/3600)}h ago`;
|
|
return `${Math.floor(d/86400)}d ago`;
|
|
}
|
|
|
|
async function loadSummary() {
|
|
let data;
|
|
try {
|
|
const r = await fetch('/api/feedback/summary');
|
|
if (!r.ok) throw new Error();
|
|
data = await r.json();
|
|
} catch { data = MOCK_SUMMARY; }
|
|
|
|
// Overview
|
|
document.getElementById('ov-total').textContent = data.total;
|
|
document.getElementById('ov-avg').textContent = data.avg_rating.toFixed(1);
|
|
document.getElementById('ov-stars').textContent = stars(data.avg_rating);
|
|
|
|
const fiveObj = data.by_rating.find(r => r.rating === 5) || {n:0};
|
|
const lowObj = data.by_rating.filter(r => r.rating <= 2).reduce((s,r) => ({n: s.n + r.n}), {n:0});
|
|
document.getElementById('ov-five').textContent = fiveObj.n;
|
|
document.getElementById('ov-five-pct').textContent = `${data.total ? Math.round(fiveObj.n/data.total*100) : 0}% of total`;
|
|
document.getElementById('ov-low').textContent = lowObj.n;
|
|
|
|
// Distribution
|
|
const distEl = document.getElementById('fb-dist');
|
|
const distColors = ['red','orange','yellow','lime','green'];
|
|
const maxN = Math.max(...data.by_rating.map(r => r.n), 1);
|
|
distEl.innerHTML = [5,4,3,2,1].map(r => {
|
|
const obj = data.by_rating.find(x => x.rating === r) || {n:0};
|
|
const pct = Math.round(obj.n / maxN * 100);
|
|
return `<div class="fb-dist-row">
|
|
<div class="fb-dist-label"><span class="fb-stars">${'★'.repeat(r)}</span></div>
|
|
<div class="fb-dist-bar-wrap"><div class="fb-dist-bar ${distColors[r-1]}" style="width:${pct}%"></div></div>
|
|
<div class="fb-dist-n">${obj.n}</div>
|
|
</div>`;
|
|
}).join('');
|
|
|
|
// Model table
|
|
const tbody = document.getElementById('fb-model-tbody');
|
|
tbody.innerHTML = data.by_model.sort((a,b)=>b.avg-a.avg).map(m => {
|
|
const pct = (m.avg / 5 * 100).toFixed(0);
|
|
return `<tr>
|
|
<td>${m.model.split(':')[0]}<span style="font-size:10px;color:var(--lt)">:${m.model.split(':')[1]||''}</span></td>
|
|
<td>
|
|
<div class="fb-score-bar-wrap"><div class="fb-score-bar" style="width:${pct}%"></div></div>
|
|
<strong style="color:var(--purple)">${m.avg.toFixed(1)}</strong>
|
|
<span class="fb-stars" style="font-size:11px;color:#f59e0b">${stars(m.avg)}</span>
|
|
</td>
|
|
<td>${m.n}</td>
|
|
</tr>`;
|
|
}).join('');
|
|
|
|
// Feed
|
|
feedData = data.recent;
|
|
renderFeed();
|
|
}
|
|
|
|
function setFeedFilter(f, btn) {
|
|
feedFilter = f;
|
|
document.querySelectorAll('.fb-feed-filter').forEach(b => b.classList.remove('active'));
|
|
btn.classList.add('active');
|
|
renderFeed();
|
|
}
|
|
|
|
function getFilteredFeed() {
|
|
if (feedFilter === 'low') return feedData.filter(f => f.rating <= 2);
|
|
if (feedFilter === 'high') return feedData.filter(f => f.rating >= 4);
|
|
if (feedFilter === 'comment') return feedData.filter(f => f.comment.trim());
|
|
return feedData;
|
|
}
|
|
|
|
function renderFeed() {
|
|
const feed = document.getElementById('fb-feed');
|
|
const items = getFilteredFeed();
|
|
if (!items.length) {
|
|
feed.innerHTML = `<p style="color:var(--lt);padding:20px 0">No feedback matching this filter.</p>`;
|
|
return;
|
|
}
|
|
feed.innerHTML = items.map(f => `
|
|
<div class="fb-feedback-card ${f.rating <= 2 ? 'low' : ''}">
|
|
<div class="fb-card-header">
|
|
<span class="fb-card-user">👤 ${f.username}</span>
|
|
<span class="fb-stars">${stars(f.rating)}</span>
|
|
<span class="fb-card-model">${f.model}</span>
|
|
<span class="fb-card-page">${f.page || 'portal'}</span>
|
|
<span class="fb-card-time">${timeAgo(f.created_at)}</span>
|
|
</div>
|
|
${f.prompt_snippet ? `<div class="fb-card-snippet">Prompt: "${f.prompt_snippet}"</div>` : ''}
|
|
${f.comment ? `<div class="fb-card-comment">"${f.comment}"</div>` : '<div class="fb-card-comment" style="color:var(--lt);font-style:italic">No comment left.</div>'}
|
|
<div class="fb-card-footer">
|
|
${f.rating <= 2 ? `<a href="evals.html" style="font-size:12px;color:var(--purple)">→ Add to Eval Suite</a>` : ''}
|
|
<button class="fb-card-del" onclick="deleteFeedback(${f.id})">🗑 Delete</button>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
async function deleteFeedback(id) {
|
|
feedData = feedData.filter(f => f.id !== id);
|
|
try { await fetch(`/api/feedback/${id}`, { method:'DELETE' }); } catch {}
|
|
renderFeed();
|
|
showToast('Feedback deleted');
|
|
}
|
|
|
|
function exportCSV() {
|
|
const rows = [['ID','User','Model','Rating','Comment','Prompt','Page','Date']];
|
|
feedData.forEach(f => rows.push([f.id,f.username,f.model,f.rating,`"${(f.comment||'').replace(/"/g,'""')}"`,`"${(f.prompt_snippet||'').replace(/"/g,'""')}"`,f.page,f.created_at]));
|
|
const csv = rows.map(r => r.join(',')).join('\n');
|
|
const blob = new Blob([csv], {type:'text/csv'});
|
|
const a = document.createElement('a'); a.href = URL.createObjectURL(blob);
|
|
a.download = 'cezen_feedback.csv'; a.click();
|
|
}
|
|
|
|
function showToast(msg) {
|
|
const t = document.getElementById('fb-toast');
|
|
t.textContent = msg; t.classList.add('show');
|
|
setTimeout(() => t.classList.remove('show'), 2200);
|
|
}
|
|
|
|
loadSummary();
|
|
</script>
|
|
|
|
<script src="auth.js"></script>
|
|
<script src="branding.js"></script>
|
|
</body>
|
|
</html>
|