document.addEventListener('DOMContentLoaded', loadApps); async function loadApps() { const res = await fetch('/api/apps'); if (res.status === 401) { location.href = '/login'; return; } const apps = await res.json(); const grid = document.getElementById('apps'); const empty = document.getElementById('empty'); if (apps.length === 0) { grid.style.display = 'none'; empty.style.display = 'block'; return; } grid.innerHTML = apps.map(app => `
${app.latest_icon ? `${app.name}` : app.name.charAt(0).toUpperCase()}
${esc(app.name)}
${esc(app.bundle_id)}
v${esc(app.latest_version || '?')}${app.latest_build_number ? ` (${esc(app.latest_build_number)})` : ''} · ${timeAgo(app.latest_uploaded_at)}
Install
`).join(''); } async function showApp(appId) { const res = await fetch(`/api/apps/${appId}`); const app = await res.json(); const modal = document.getElementById('modal'); const body = document.getElementById('modal-body'); const icon = app.builds[0]?.icon_filename; body.innerHTML = `

Builds (${app.builds.length})

${app.builds.map(b => `
v${esc(b.version)} (${esc(b.build_number || '?')})
${new Date(b.uploaded_at + 'Z').toLocaleDateString()} · ${formatSize(b.size)}
${b.notes ? `
${esc(b.notes)}
` : ''}
Install
`).join('')}
`; modal.style.display = 'flex'; modal.onclick = (e) => { if (e.target === modal) closeModal(); }; } function closeModal() { document.getElementById('modal').style.display = 'none'; } async function deleteBuild(buildId, appId) { if (!confirm('Delete this build?')) return; await fetch(`/api/builds/${buildId}`, { method: 'DELETE' }); closeModal(); loadApps(); } async function deleteApp(appId) { if (!confirm('Delete this app and all its builds?')) return; await fetch(`/api/apps/${appId}`, { method: 'DELETE' }); closeModal(); loadApps(); } function esc(s) { if (!s) return ''; const d = document.createElement('div'); d.textContent = s; return d.innerHTML; } function formatSize(bytes) { if (!bytes) return ''; if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'; return (bytes / (1024 * 1024)).toFixed(1) + ' MB'; } function timeAgo(dateStr) { if (!dateStr) return ''; const d = new Date(dateStr + 'Z'); const now = new Date(); const diff = (now - d) / 1000; if (diff < 60) return 'just now'; if (diff < 3600) return Math.floor(diff / 60) + 'm ago'; if (diff < 86400) return Math.floor(diff / 3600) + 'h ago'; if (diff < 604800) return Math.floor(diff / 86400) + 'd ago'; return d.toLocaleDateString(); }