← back to Butlr
views/admin/calls.ejs
68 lines
<%- include('../partials/head', { title, meta_desc }) %>
<%- include('../partials/header') %>
<main class="container" style="max-width: 1200px; padding: 24px;">
<p style="margin-bottom: 8px;"><a href="/admin">← Admin</a></p>
<h1 style="margin: 0 0 6px;">Call history</h1>
<p class="muted" style="margin: 0 0 16px;"><%= calls.length %> total · all users · ordered newest first</p>
<div style="display:flex; gap:12px; margin-bottom:16px; flex-wrap:wrap;">
<input id="filter-q" placeholder="filter by business / goal / id" style="flex:1; min-width:240px; padding:8px 12px; border:1px solid var(--line); border-radius:6px;"/>
<select id="filter-status" style="padding:8px 12px; border:1px solid var(--line); border-radius:6px;">
<option value="">all statuses</option>
<option value="queued">queued</option>
<option value="dialing">dialing</option>
<option value="connected">connected</option>
<option value="done">done</option>
<option value="failed">failed</option>
</select>
</div>
<table style="width:100%; border-collapse:collapse; font-size:13px;">
<thead style="background:var(--panel-2);">
<tr style="border-bottom:1px solid var(--line);">
<th style="text-align:left; padding:8px;">When</th>
<th style="text-align:left; padding:8px;">Business</th>
<th style="text-align:left; padding:8px;">Goal</th>
<th style="text-align:left; padding:8px;">Status</th>
<th style="text-align:left; padding:8px;">User</th>
<th style="text-align:left; padding:8px;">Listen</th>
</tr>
</thead>
<tbody id="calls-tbody">
<% for (const c of calls) { %>
<tr class="row" data-status="<%= c.status %>" data-search="<%= ((c.business_name||'') + ' ' + (c.goal||'') + ' ' + c.id).toLowerCase() %>" style="border-bottom:1px solid var(--line);">
<td style="padding:8px; white-space:nowrap; color:var(--ink-dim);"><%= new Date(c.created_at).toLocaleString('en-US',{month:'short',day:'numeric',hour:'numeric',minute:'2-digit'}) %></td>
<td style="padding:8px;"><strong><%= c.business_name %></strong><br><span class="muted" style="font-size:11px;"><%= c.business_phone %></span></td>
<td style="padding:8px; max-width:300px;"><span class="muted"><%= (c.goal||'').slice(0,80) %><%= (c.goal||'').length > 80 ? '…' : '' %></span></td>
<td style="padding:8px;">
<span style="padding:2px 8px; border-radius:4px; font-size:11px; background:<%= c.status === 'done' ? '#dcfce7' : c.status === 'failed' ? '#fee2e2' : c.status === 'connected' ? '#fef3c7' : '#e0e7ff' %>; color:<%= c.status === 'done' ? '#166534' : c.status === 'failed' ? '#991b1b' : c.status === 'connected' ? '#92400e' : '#3730a3' %>;"><%= c.status %></span>
</td>
<td style="padding:8px; font-size:11px;"><%= (c.user_id || 'legacy').slice(0,8) %></td>
<td style="padding:8px;"><a href="/listen/<%= c.id %>">▶</a></td>
</tr>
<% } %>
</tbody>
</table>
<script>
const q = document.getElementById('filter-q');
const s = document.getElementById('filter-status');
function apply() {
const ql = (q.value || '').toLowerCase();
const sv = s.value;
document.querySelectorAll('.row').forEach(r => {
const m1 = !ql || r.dataset.search.includes(ql);
const m2 = !sv || r.dataset.status === sv;
r.style.display = (m1 && m2) ? '' : 'none';
});
}
q.addEventListener('input', apply);
s.addEventListener('change', apply);
</script>
</main>
<%- include('../partials/footer') %>