Spaces:
Sleeping
Sleeping
| // static/js/projects.js v7 | |
| async function loadPublishedProjects() { | |
| if (!currentToken) return; | |
| try { | |
| var res = await fetch('/api/projects', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({token: currentToken}) }); | |
| var data = await res.json(); | |
| var grid = document.getElementById('projects-grid'); | |
| if (!grid) return; | |
| grid.innerHTML = ''; | |
| if (data.projects && data.projects.length) { | |
| data.projects.forEach(function(p) { | |
| var card = document.createElement('div'); | |
| card.style.cssText = 'background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--r-lg);padding:var(--sp-4);display:flex;flex-direction:column;gap:var(--sp-3)'; | |
| var date = new Date(p.created_at).toLocaleDateString(); | |
| var url = location.origin + p.url; | |
| card.innerHTML = '<div style="display:flex;justify-content:space-between;align-items:center"><h3 style="font-size:15px;font-weight:600;color:var(--accent);margin:0">' + p.name + '</h3><span style="font-size:11px;color:var(--text-3)">' + date + '</span></div><div style="font-size:12px;color:var(--text-2);background:rgba(255,255,255,0.03);padding:8px;border-radius:var(--r-sm);word-break:break-all"><a href="' + url + '" target="_blank" style="color:var(--info);text-decoration:none">' + url + '</a></div><div style="display:flex;gap:var(--sp-2)"><button class="ed-btn" onclick="window.open(\'' + url + '\',\'_blank\')" style="flex:1;justify-content:center"><i class="fa-solid fa-external-link"></i> Visit</button><button class="ed-btn success" onclick="publishCurrentWorkspace(\'' + p.name + '\',\'' + p.id + '\')" style="flex:1;justify-content:center"><i class="fa-solid fa-rotate"></i> Update</button></div>'; | |
| grid.appendChild(card); | |
| }); | |
| } else { | |
| grid.innerHTML = '<div style="text-align:center;color:var(--text-3);padding:var(--sp-8);background:var(--bg-elevated);border-radius:var(--r-lg);border:1px solid var(--border)"><i class="fa-solid fa-box-open" style="font-size:2rem;margin-bottom:var(--sp-3);opacity:0.4"></i><p>No projects yet</p><p style="font-size:12px">Tap Publish to deploy your code</p></div>'; | |
| } | |
| } catch (e) { showToast('Error loading projects', 'error'); } | |
| } | |
| var publishingProjectID = null; | |
| async function publishCurrentWorkspace(existingName, existingId) { | |
| publishingProjectID = existingId || null; | |
| var modal = document.getElementById('publish-modal'); | |
| modal.style.display = 'flex'; | |
| document.getElementById('publish-project-name').value = existingName || ''; | |
| var list = document.getElementById('publish-file-list'); | |
| list.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Loading...'; | |
| try { | |
| var wsRes = await fetch('/api/files', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({token: currentToken}) }); | |
| var wsData = await wsRes.json(); | |
| var prev = []; | |
| if (existingId) { | |
| var pubRes = await fetch('/api/project/files', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({token: currentToken, project_id: existingId}) }); | |
| var pubData = await pubRes.json(); | |
| if (pubData.files) prev = pubData.files; | |
| } | |
| list.innerHTML = ''; | |
| if (wsData.files && wsData.files.length) { | |
| wsData.files.forEach(function(f) { | |
| var lbl = document.createElement('label'); | |
| lbl.style.cssText = 'display:flex;align-items:center;gap:8px;color:var(--text-1);cursor:pointer;font-size:13px;font-family:var(--font-mono);min-height:var(--touch-min)'; | |
| var cb = document.createElement('input'); | |
| cb.type = 'checkbox'; cb.value = f; cb.className = 'publish-cb'; | |
| if (!existingId || prev.includes(f)) cb.checked = true; | |
| lbl.appendChild(cb); | |
| lbl.appendChild(document.createTextNode(f)); | |
| list.appendChild(lbl); | |
| }); | |
| } else { list.innerHTML = '<span style="color:var(--error)">Workspace is empty</span>'; } | |
| } catch (e) { list.innerHTML = '<span style="color:var(--error)">Failed to load files</span>'; } | |
| } | |
| function closePublishModal() { | |
| document.getElementById('publish-modal').style.display = 'none'; | |
| } | |
| async function confirmPublish() { | |
| var name = document.getElementById('publish-project-name').value.trim(); | |
| if (!name) { showToast('Enter project name', 'warning'); return; } | |
| var checks = document.querySelectorAll('.publish-cb'); | |
| var files = Array.from(checks).filter(function(c) { return c.checked; }).map(function(c) { return c.value; }); | |
| if (!files.length) { showToast('Select at least one file', 'warning'); return; } | |
| var htmlFiles = files.filter(function(f) { return f.endsWith('.html'); }); | |
| var mainHtml = htmlFiles.includes('index.html') ? 'index.html' : (htmlFiles[0] || 'index.html'); | |
| var btn = document.getElementById('publish-confirm-btn'); | |
| btn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Publishing...'; | |
| btn.disabled = true; | |
| try { | |
| var res = await fetch('/api/publish', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ token: currentToken, project_name: name, project_id: publishingProjectID, files: files, main_html_file: mainHtml }) }); | |
| var data = await res.json(); | |
| if (data.success) { showToast('Published!', 'success'); closePublishModal(); loadPublishedProjects(); } | |
| else showToast(data.error || 'Failed', 'error'); | |
| } catch (e) { showToast('Network error', 'error'); } | |
| btn.innerHTML = '<i class="fa-solid fa-cloud-arrow-up"></i> Publish'; | |
| btn.disabled = false; | |
| } | |