wallapi / public /dashboard.html
Ig0tU
Add Live Site integration, Phone/Subject fields, and origin tracking
42d4b91
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WallAPI - Live Submissions Dashboard</title>
<style>
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background-color: #f0f2f5;
margin: 0;
padding: 40px 20px;
color: #333;
}
.container {
max-width: 800px;
margin: 0 auto;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
}
.header h1 {
margin: 0;
font-size: 28px;
}
.status-badge {
display: inline-flex;
align-items: center;
padding: 6px 12px;
border-radius: 999px;
font-size: 14px;
font-weight: 600;
background: #fee2e2;
color: #b91c1c;
}
.status-badge.connected {
background: #d1fae5;
color: #065f46;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: currentColor;
margin-right: 8px;
}
.submission-list {
display: flex;
flex-direction: column;
gap: 16px;
}
.submission-card {
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
border-left: 4px solid #0066ff;
animation: slideIn 0.3s ease-out forwards;
opacity: 0;
transform: translateY(20px);
}
@keyframes slideIn {
to {
opacity: 1;
transform: translateY(0);
}
}
.submission-header {
display: flex;
justify-content: space-between;
margin-bottom: 12px;
font-size: 14px;
color: #666;
}
.submission-name {
font-weight: 700;
color: #111;
font-size: 18px;
}
.submission-email {
color: #0066ff;
text-decoration: none;
}
.submission-message {
margin-top: 16px;
padding: 16px;
background: #f8fafc;
border-radius: 8px;
border: 1px solid #e2e8f0;
line-height: 1.5;
white-space: pre-wrap;
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: #64748b;
background: white;
border-radius: 12px;
border: 2px dashed #cbd5e1;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Live Form Submissions</h1>
<div id="connection-status" class="status-badge">
<div class="status-dot"></div>
<span id="status-text">Disconnected</span>
</div>
</div>
<div id="submissions" class="submission-list">
<div id="empty-state" class="empty-state">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" style="margin-bottom: 16px; opacity: 0.5;">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
</svg>
<h3>No new submissions yet</h3>
<p>Waiting for real-time updates from your embedded forms...</p>
</div>
</div>
</div>
<!-- Include Socket.IO client from CDN -->
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
<script>
// Connect to the Socket.IO server dynamically based on where we are hosted
const socket = io(window.location.origin);
const statusBadge = document.getElementById('connection-status');
const statusText = document.getElementById('status-text');
const submissionsList = document.getElementById('submissions');
const emptyState = document.getElementById('empty-state');
// Handle Connection
socket.on('connect', () => {
statusBadge.className = 'status-badge connected';
statusText.textContent = 'Live & Connected';
});
socket.on('disconnect', () => {
statusBadge.className = 'status-badge';
statusText.textContent = 'Disconnected';
});
// Listen for new submissions
socket.on('new_submission', (data) => {
// Remove empty state if it exists
if (emptyState && emptyState.parentNode) {
emptyState.parentNode.removeChild(emptyState);
}
// Create a new submission card
const card = document.createElement('div');
card.className = 'submission-card';
const date = new Date(data.timestamp).toLocaleString();
card.innerHTML = `
<div class="submission-header">
<div>
<span class="submission-name">\${data.name}</span> &middot;
<a href="mailto:\${data.email}" class="submission-email">\${data.email}</a>
</div>
<div>
<span style="background:#e0e7ff; color:#3730a3; padding:2px 6px; border-radius:4px; font-size:12px; margin-right:8px;">\${data.origin}</span>
\${date}
</div>
</div>
\${data.phone ? \`<div style="font-size:13px; color:#666; margin-top:4px;">&#128222; \${data.phone}</div>\` : ''}
\${data.subject ? \`<div style="font-weight:600; margin-top:12px; font-size:15px;">Subject: \${data.subject}</div>\` : ''}
<div class="submission-message">\${data.message.replace(/</g, "&lt;").replace(/>/g, "&gt;")}</div>
`;
// Add to the top of the list
submissionsList.insertBefore(card, submissionsList.firstChild);
// Optional: Play a tiny notification sound
// const audio = new Audio('https://assets.mixkit.co/active_storage/sfx/2869/2869-preview.mp3');
// audio.play().catch(e => console.log("Audio play blocked by browser:", e));
});
</script>
</body>
</html>