// Landing Page function renderLanding() { return `

Never miss downtime again

Monitor your websites, APIs, and services in real-time. Get instant alerts when something goes wrong.

Try Live Demo

Try it now

Enter any URL to check its status instantly

Everything you need

Comprehensive monitoring tools to keep your services running smoothly

${renderFeatureCards()}

Simple, transparent pricing

Start free, scale as you grow

${renderPricingCards()}
`; } function renderFeatureCards() { const features = [ { icon: '⬆️', title: 'Uptime Monitoring', desc: 'Monitor your websites and APIs 24/7 with checks as frequent as every 30 seconds' }, { icon: '⚡', title: 'Response Time Tracking', desc: 'Track response times and identify performance bottlenecks' }, { icon: '🔒', title: 'SSL Monitoring', desc: 'Get alerts before your SSL certificates expire' }, { icon: '📊', title: 'Public Status Pages', desc: 'Beautiful, customizable status pages for your users' }, { icon: '🔔', title: 'Instant Alerts', desc: 'Get notified via email or webhook when things go wrong' }, { icon: '💓', title: 'Heartbeat Monitoring', desc: 'Monitor cron jobs and scheduled tasks' }, ]; return features.map(f => `
${f.icon}

${f.title}

${f.desc}

`).join(''); } function renderPricingCards() { const plans = [ { name: 'Free', price: '$0', period: '/forever', desc: 'Perfect for personal projects', features: ['3 monitors', '5-minute checks', 'Email alerts', '1 status page'], cta: 'Get Started', featured: false }, { name: 'Pro', price: '$10', period: '/month', desc: 'For growing teams', features: ['50 monitors', '1-minute checks', 'Email + Webhook alerts', '5 status pages', 'API access'], cta: 'Start Free Trial', featured: true }, { name: 'Business', price: '$25', period: '/month', desc: 'For demanding applications', features: ['200 monitors', '30-second checks', 'Unlimited alerts', 'Unlimited status pages', 'Full API access'], cta: 'Contact Sales', featured: false } ]; return plans.map(p => `
${p.name}
${p.price}${p.period}
${p.desc}
`).join(''); } async function runDemo() { const url = document.getElementById('demoUrl').value; if (!url) return; const result = document.getElementById('demoResult'); result.style.display = 'block'; document.getElementById('demoStatus').textContent = '...'; document.getElementById('demoTime').textContent = '...'; document.getElementById('demoSsl').textContent = '...'; try { const data = await fetch(`/api/v1/check?url=${encodeURIComponent(url)}`).then(r => r.json()); const statusEl = document.getElementById('demoStatus'); statusEl.textContent = data.status === 'up' ? 'UP' : 'DOWN'; statusEl.className = `demo-result-value ${data.status}`; document.getElementById('demoTime').textContent = safeValue(data.responseTime, 'ms'); document.getElementById('demoSsl').textContent = data.ssl?.valid ? `Valid (${safeValue(data.ssl.daysUntilExpiry, 'd')})` : 'N/A'; } catch (e) { document.getElementById('demoStatus').textContent = 'Error'; document.getElementById('demoStatus').className = 'demo-result-value down'; } } // Dashboard function renderDashboard() { const validMonitors = state.monitors.filter(m => m && m.status); const up = validMonitors.filter(m => m.status === 'up').length; const down = validMonitors.filter(m => m.status === 'down').length; const monitorsWithResponseTime = validMonitors.filter(m => m.responseTime != null && !isNaN(m.responseTime)); const avgTime = monitorsWithResponseTime.length > 0 ? Math.round(monitorsWithResponseTime.reduce((a, m) => a + m.responseTime, 0) / monitorsWithResponseTime.length) : null; return `
${renderSidebar('monitors')}
Total Monitors
${state.monitors.length}
Up
${up}
Down
${down}
Avg Response
${safeValue(avgTime, 'ms')}
${state.loading.monitors ? renderLoading() : (state.monitors.length === 0 ? renderEmptyState('monitors') : state.monitors.map(m => safeRender(() => renderMonitorItem(m), ''))).join(''))}
`; } function renderMonitorItem(monitor) { if (!monitor) return ''; const statusClass = monitor.status === 'up' ? 'status-up' : monitor.status === 'down' ? 'status-down' : 'status-pending'; const uptime = formatNumber(monitor.uptime, 1, '%'); const lastChecked = monitor.lastCheckedAt ? formatRelativeTime(new Date(monitor.lastCheckedAt)) : 'Never'; const responseTime = safeValue(monitor.responseTime, 'ms'); const monitorJson = JSON.stringify(monitor).replace(/"/g, '"').replace(/\x27/g, '''); return `
${escapeHtml(monitor.name || 'Unnamed Monitor')}
${escapeHtml(monitor.url || '')}
Response
${responseTime}
Uptime
${uptime}
Last Check
${lastChecked}
`; } function renderEmptyState(type) { const messages = { monitors: { title: 'No monitors yet', desc: 'Add your first monitor to start tracking uptime', btn: 'Add Monitor', action: 'openMonitorModal()' }, statusPages: { title: 'No status pages', desc: 'Create a status page to share with your users', btn: 'Create Status Page', action: 'openStatusPageModal()' }, alerts: { title: 'No alert channels', desc: 'Add an alert channel to get notified', btn: 'Add Channel', action: 'openAlertModal()' }, heartbeats: { title: 'No heartbeats yet', desc: 'Create a heartbeat to monitor your cron jobs', btn: 'Create Heartbeat', action: 'openHeartbeatModal()' }, maintenance: { title: 'No maintenance scheduled', desc: 'Schedule maintenance windows for your services', btn: 'Schedule Maintenance', action: 'openMaintenanceModal()' }, incidents: { title: 'No incidents', desc: 'All systems operational! No incidents to report.', btn: 'Create Incident', action: 'openIncidentModal()' }, teams: { title: 'No teams yet', desc: 'Create a team to collaborate with others', btn: 'Create Team', action: 'openTeamModal()' } }; const m = messages[type] || { title: 'No data', desc: '', btn: null }; return `
${icons.plus}

${m.title}

${m.desc}

${m.btn ? `` : ''}
`; } // Monitor Detail function renderMonitorDetail() { const m = state.currentMonitor; if (!m) return '

Monitor not found

'; const statusClass = m.status === 'up' ? 'status-up' : m.status === 'down' ? 'status-down' : 'status-pending'; return `
${renderSidebar('monitors')}

${escapeHtml(m.name || 'Unnamed Monitor')}

${escapeHtml(m.url || '')}
Response Time
${safeValue(m.responseTime, 'ms')}
Uptime
${formatNumber(m.uptime, 2, '%')}
Check Interval
${formatInterval(m.checkInterval)}

Response Time

Recent Incidents

${renderLoading()}
`; } // Heartbeats View function renderHeartbeats() { return `
${renderSidebar('heartbeats')}

Monitor your cron jobs, scheduled tasks, and background workers by having them ping a unique URL.

${state.loading.heartbeats ? renderLoading() : (state.heartbeats.length === 0 ? renderEmptyState('heartbeats') : state.heartbeats.map(h => safeRender(() => renderHeartbeatItem(h), ''))).join(''))}
`; } function renderHeartbeatItem(hb) { if (!hb) return ''; const statusClass = hb.status === 'up' ? 'status-up' : hb.status === 'down' ? 'status-down' : 'status-unknown'; const statusText = hb.status === 'up' ? 'Healthy' : hb.status === 'down' ? 'Down' : 'Waiting'; const lastPing = hb.lastPingAt ? formatRelativeTime(new Date(hb.lastPingAt)) : 'Never'; const pingUrl = `https://monitorhq.io/api/v1/heartbeat/${hb.slug}`; return `
${escapeHtml(hb.name)}
${escapeHtml(pingUrl)}
Status
${statusText}
Last Ping
${lastPing}
Interval
${formatInterval(hb.expectedInterval)}
`; } // Incidents View function renderIncidents() { const filtered = filterIncidents(state.incidents); return `
${renderSidebar('incidents')}
${state.loading.incidents ? renderLoading() : (filtered.length === 0 ? renderEmptyState('incidents') : filtered.map(i => safeRender(() => renderIncidentItem(i), ''))).join(''))}
`; } function filterIncidents(incidents) { return incidents.filter(i => { if (state.incidentFilter.status !== 'all' && i.status !== state.incidentFilter.status) return false; if (state.incidentFilter.severity !== 'all' && i.severity !== state.incidentFilter.severity) return false; return true; }); } function renderIncidentItem(incident) { if (!incident) return ''; const severityClass = `severity-${incident.severity}`; const statusClass = `status-badge-${incident.status}`; return `
${incident.status === 'resolved' ? icons.check : icons.incidents}
${escapeHtml(incident.title)}
${incident.monitorName ? `Affects: ${escapeHtml(incident.monitorName)} • ` : ''} Started ${formatRelativeTime(new Date(incident.createdAt))}
${incident.severity} ${incident.status}
`; } async function viewIncident(id) { try { const data = await api(`/incidents/${id}`); state.currentIncident = mapIncident(data.incident || data); navigate('incident-detail'); } catch (e) { toast(e.message, 'error'); } } // Incident Detail View function renderIncidentDetail() { const i = state.currentIncident; if (!i) return '

Incident not found

'; const severityClass = `severity-${i.severity}`; const statusClass = `status-badge-${i.status}`; return `
${renderSidebar('incidents')}

Timeline

${(i.updates || []).map(u => renderTimelineItem(u)).join('') || '

No updates yet

'}
`; } function renderTimelineItem(update) { const statusColors = { investigating: 'red', identified: 'orange', monitoring: 'blue', resolved: 'green' }; const color = statusColors[update.status] || 'blue'; return `
${update.status} ${formatDate(new Date(update.createdAt))}

${escapeHtml(update.message)}

`; } // Maintenance View function renderMaintenance() { const now = new Date(); const active = state.maintenanceWindows.filter(m => new Date(m.startTime) <= now && new Date(m.endTime) >= now); const upcoming = state.maintenanceWindows.filter(m => new Date(m.startTime) > now); const past = state.maintenanceWindows.filter(m => new Date(m.endTime) < now); return `
${renderSidebar('maintenance')}
${active.length > 0 ? `

Active Now

${active.map(m => safeRender(() => renderMaintenanceItem(m, true), ''))).join(''))}
` : ''}

Upcoming

${state.loading.maintenance ? renderLoading() : (upcoming.length === 0 ? renderEmptyState('maintenance') : upcoming.map(m => safeRender(() => renderMaintenanceItem(m), ''))).join(''))}
${past.length > 0 ? `

Past

${past.slice(0, 5).map(m => safeRender(() => renderMaintenanceItem(m, false, true), ''))).join(''))}
` : ''}
`; } function renderMaintenanceItem(maint, isActive = false, isPast = false) { if (!maint) return ''; const startDate = new Date(maint.startTime); const endDate = new Date(maint.endTime); const monitorNames = (maint.monitors || []).map(id => { const mon = state.monitors.find(m => m.id === id); return mon ? mon.name : 'Unknown'; }); return `
${escapeHtml(maint.title)} ${isActive ? 'Active' : ''} ${maint.recurring ? 'Recurring' : ''}
${!isPast ? `
` : ''}
${icons.clock} ${formatDate(startDate)} — ${formatDate(endDate)}
${monitorNames.length > 0 ? `
${monitorNames.map(n => `${escapeHtml(n)}`).join(''}
` : 'All monitors'}
`; } // Status Pages View function renderStatusPages() { return `
${renderSidebar('status-pages')}
${state.loading.statusPages ? renderLoading() : (state.statusPages.length === 0 ? renderEmptyState('statusPages') : state.statusPages.map(p => safeRender(() => renderStatusPageItem(p), ''))).join(''))}
`; } function renderStatusPageItem(page) { if (!page) return ''; return `

${escapeHtml(page.title || 'Untitled')}

/status/${escapeHtml(page.slug || '')}
${icons.link} View
`; } // Alerts View function renderAlerts() { return `
${renderSidebar('alerts')}
${state.loading.alertChannels ? renderLoading() : (state.alertChannels.length === 0 ? renderEmptyState('alerts') : state.alertChannels.map(a => safeRender(() => renderAlertItem(a), ''))).join(''))}
`; } function renderAlertItem(alert) { if (!alert) return ''; return `
${alert.type === 'email' ? icons.email : icons.webhook}
${alert.type === 'email' ? 'Email' : 'Webhook'}
${escapeHtml(alert.target || '')}
`; } // Teams View function renderTeams() { return `
${renderSidebar('teams')}
${state.loading.teams ? renderLoading() : (state.teams.length === 0 ? renderEmptyState('teams') : state.teams.map(t => safeRender(() => renderTeamItem(t), ''))).join(''))}
`; } function renderTeamItem(team) { if (!team) return ''; return `
${escapeHtml(team.name)}
${team.memberCount || 0} member${team.memberCount !== 1 ? 's' : ''}}
${team.role}
`; } async function viewTeam(id) { try { const data = await api(`/teams/${id}`); state.currentTeam = mapTeam(data.team || data); navigate('team-detail'); } catch (e) { toast(e.message, 'error'); } } // Team Detail View function renderTeamDetail() { const t = state.currentTeam; if (!t) return '

Team not found

'; const canManage = t.role === 'owner' || t.role === 'admin'; return `
${renderSidebar('teams')}

Members (${(t.members || []).length})

${(t.members || []).map(m => renderMemberItem(m, t.id, canManage)).join('')}
`; } function renderMemberItem(member, teamId, canManage) { const initial = (member.name || member.email || '?')[0].toUpperCase(); return `
${initial}
${escapeHtml(member.name || 'Unknown')}
${escapeHtml(member.email)}
${member.role} ${canManage && member.role !== 'owner' ? ` ` : ''}
`; }