This commit is contained in:
Neha Shivankar 2026-04-29 05:52:37 +00:00
parent 017ddc0848
commit a179edede7
7 changed files with 10 additions and 114 deletions

0
.gitignore vendored Normal file → Executable file
View File

0
boltic.yaml Normal file → Executable file
View File

0
package.json Normal file → Executable file
View File

50
public/app.js Normal file → Executable file
View File

@ -1,5 +1,7 @@
/* ── Socket ──────────────────────────────────────────────────────────────── */ /* ── Socket ──────────────────────────────────────────────────────────────── */
const socket = io(); const socket = io({
path: window.location.pathname.replace(/\/$/, '') + '/socket.io'
});
/* ── State ───────────────────────────────────────────────────────────────── */ /* ── State ───────────────────────────────────────────────────────────────── */
let state = { let state = {
@ -14,13 +16,6 @@ let state = {
/* ── DOM Refs ─────────────────────────────────────────────────────────────── */ /* ── DOM Refs ─────────────────────────────────────────────────────────────── */
const $ = id => document.getElementById(id); const $ = id => document.getElementById(id);
const loadingScreen = $('loading-screen');
const qrScreen = $('qr-screen');
const degradedScreen = $('degraded-screen');
const dashboard = $('dashboard');
const qrImg = $('qr-img');
const qrOverlay = $('qr-overlay');
const qrConnecting = $('qr-connecting');
const inboxList = $('inbox-list'); const inboxList = $('inbox-list');
const convPlaceholder= $('conv-placeholder'); const convPlaceholder= $('conv-placeholder');
const convView = $('conv-view'); const convView = $('conv-view');
@ -78,36 +73,7 @@ function setBadge(el, sessionStr) {
} }
/* ── Screen Router ────────────────────────────────────────────────────────── */ /* ── Screen Router ────────────────────────────────────────────────────────── */
function route(sessionStr, qrDataUrl) { function route() {}
loadingScreen.classList.add('hidden');
qrScreen.classList.add('hidden');
degradedScreen.classList.add('hidden');
dashboard.classList.add('hidden');
qrConnecting.classList.add('hidden');
if (sessionStr === 'awaiting_scan' || sessionStr === 'connecting') {
qrScreen.classList.remove('hidden');
if (qrDataUrl) {
qrImg.src = qrDataUrl;
qrOverlay.classList.add('hidden');
} else {
qrOverlay.classList.remove('hidden');
}
if (sessionStr === 'connecting') {
qrConnecting.classList.remove('hidden');
}
setBadge($('qr-status-badge'), sessionStr);
} else if (sessionStr === 'degraded') {
degradedScreen.classList.remove('hidden');
} else if (sessionStr === 'connected') {
dashboard.classList.remove('hidden');
} else {
// disconnected / reconnecting — show QR screen with no QR
qrScreen.classList.remove('hidden');
qrOverlay.classList.remove('hidden');
setBadge($('qr-status-badge'), sessionStr);
}
}
/* ── Stats Update ─────────────────────────────────────────────────────────── */ /* ── Stats Update ─────────────────────────────────────────────────────────── */
function updateStats(stats) { function updateStats(stats) {
@ -259,7 +225,7 @@ async function doSend() {
const idempotencyKey = `${state.selectedChatId}-${Date.now()}-${Math.random().toString(36).slice(2)}`; const idempotencyKey = `${state.selectedChatId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
try { try {
const res = await fetch('/api/send', { const res = await fetch(window.location.pathname + 'api/send', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ chatId: state.selectedChatId, body, idempotencyKey }) body: JSON.stringify({ chatId: state.selectedChatId, body, idempotencyKey })
@ -309,7 +275,7 @@ async function doAiDraft() {
$('ops-ai-badge').innerHTML = '<span class="dot"></span>Generating'; $('ops-ai-badge').innerHTML = '<span class="dot"></span>Generating';
try { try {
const res = await fetch('/api/ai-draft', { const res = await fetch(window.location.pathname + 'api/ai-draft', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ chatId: state.selectedChatId }) body: JSON.stringify({ chatId: state.selectedChatId })
@ -418,8 +384,8 @@ window.addEventListener('resize', () => {
(async () => { (async () => {
try { try {
const [sessionRes, inboxRes] = await Promise.all([ const [sessionRes, inboxRes] = await Promise.all([
fetch('/api/session'), fetch(window.location.pathname + 'api/session'),
fetch('/api/inbox') fetch(window.location.pathname + 'api/inbox')
]); ]);
const sessionData = await sessionRes.json(); const sessionData = await sessionRes.json();
const inbox = await inboxRes.json(); const inbox = await inboxRes.json();

74
public/index.html Normal file → Executable file
View File

@ -10,78 +10,8 @@
</head> </head>
<body> <body>
<!-- ── Loading Screen ──────────────────────────────────────────────────── -->
<div id="loading-screen" class="loading-screen">
<div class="loading-logo">
<div class="logo-icon">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2C6.477 2 2 6.477 2 12c0 1.89.525 3.66 1.438 5.168L2 22l4.832-1.438A9.956 9.956 0 0012 22c5.523 0 10-4.477 10-10S17.523 2 12 2z" fill="currentColor"/>
</svg>
</div>
<span>WhatsApp <strong>AI Agent</strong></span>
</div>
<div class="loading-spinner"></div>
</div>
<!-- ── QR Screen ───────────────────────────────────────────────────────── -->
<div id="qr-screen" class="qr-screen hidden">
<div class="qr-card">
<div class="qr-header">
<div class="logo-lockup">
<div class="logo-icon">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2C6.477 2 2 6.477 2 12c0 1.89.525 3.66 1.438 5.168L2 22l4.832-1.438A9.956 9.956 0 0012 22c5.523 0 10-4.477 10-10S17.523 2 12 2z" fill="currentColor"/>
</svg>
</div>
<span>WhatsApp <strong>AI Agent</strong></span>
</div>
</div>
<div id="qr-status-badge" class="status-badge status-awaiting">
<span class="dot"></span>Awaiting Scan
</div>
<div class="qr-body">
<div id="qr-image-wrap" class="qr-image-wrap">
<img id="qr-img" src="" alt="QR Code" />
<div id="qr-overlay" class="qr-overlay hidden">
<div class="qr-refresh-msg">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 4v6h6M23 20v-6h-6M20.49 9A9 9 0 005.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 013.51 15"/></svg>
QR Expired
</div>
</div>
</div>
<p class="qr-hint">Open <strong>WhatsApp</strong> on your phone → <strong>Linked Devices</strong> → Scan this QR</p>
<div class="qr-steps">
<div class="step"><span>1</span>Open WhatsApp on mobile</div>
<div class="step"><span>2</span>Tap the three dots → Linked Devices</div>
<div class="step"><span>3</span>Point camera at this screen</div>
</div>
</div>
<div id="qr-connecting" class="qr-connecting hidden">
<div class="loading-spinner small"></div>
<span>Authenticating…</span>
</div>
</div>
</div>
<!-- ── Degraded Screen ─────────────────────────────────────────────────── -->
<div id="degraded-screen" class="qr-screen hidden">
<div class="qr-card" style="max-width:420px">
<div class="qr-header">
<div class="logo-lockup">
<div class="logo-icon"><svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 2C6.477 2 2 6.477 2 12c0 1.89.525 3.66 1.438 5.168L2 22l4.832-1.438A9.956 9.956 0 0012 22c5.523 0 10-4.477 10-10S17.523 2 12 2z" fill="currentColor"/></svg></div>
<span>WhatsApp <strong>AI Agent</strong></span>
</div>
</div>
<div class="status-badge status-failed"><span class="dot"></span>Degraded</div>
<div class="qr-body" style="text-align:center;padding:2rem 1rem">
<p style="color:var(--text-secondary);margin-bottom:1rem">The WhatsApp session could not be started. This is usually because Chromium is not available in this environment.</p>
<p style="color:var(--text-muted);font-size:.85rem">Set <code>DEMO_MODE=true</code> in your environment to explore the dashboard with sample data.</p>
</div>
</div>
</div>
<!-- ── Main Dashboard ──────────────────────────────────────────────────── --> <!-- ── Main Dashboard ──────────────────────────────────────────────────── -->
<div id="dashboard" class="dashboard hidden"> <div id="dashboard" class="dashboard">
<!-- Sidebar --> <!-- Sidebar -->
<aside class="sidebar"> <aside class="sidebar">
@ -256,7 +186,7 @@
<!-- Toast container --> <!-- Toast container -->
<div id="toasts" class="toast-container"></div> <div id="toasts" class="toast-container"></div>
<script src="/socket.io/socket.io.js"></script> <script src="socket.io/socket.io.js"></script>
<script src="app.js"></script> <script src="app.js"></script>
</body> </body>
</html> </html>

0
public/style.css Normal file → Executable file
View File

0
server.js Normal file → Executable file
View File