// ============================================ // BRAINSTEEL WEB - STORAGE PANEL // ============================================ let storageState = { clones: [], selectedClone: null, viewMode: 'grid' // 'grid' or 'list' }; let storageApi = { // Fetch all clones from API async fetchClones() { const res = await fetch('/api/clones'); const data = await res.json(); return data.data; }, // Get single clone details async getClone(id) { const res = await fetch(`/api/clones/${id}`); const data = await res.json(); return data.data; }, // Update clone metadata async updateClone(id, updates) { const res = await fetch(`/api/clones/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(updates) }); return res.json(); }, // Delete clone async deleteClone(id) { const res = await fetch(`/api/clones/${id}`, { method: 'DELETE' }); return res.json(); }, // Create version snapshot async createVersion(id) { const res = await fetch(`/api/clones/${id}/versions`, { method: 'POST' }); return res.json(); }, // Restore from version async restoreVersion(id, versionId) { const res = await fetch(`/api/clones/${id}/restore/${versionId}`, { method: 'POST' }); return res.json(); }, // Delete version async deleteVersion(id, versionId) { const res = await fetch(`/api/clones/${id}/versions/${versionId}`, { method: 'DELETE' }); return res.json(); } }; // ============================================ // UI FUNCTIONS // ============================================ async function loadStoragePanel() { const container = document.getElementById('storageContent'); if (!container) return; showStorageLoading(true); try { const data = await storageApi.fetchClones(); storageState.clones = data.clones; renderStorageSummary(data.summary); renderClonesGrid(data.clones); showStorageLoading(false); } catch (err) { console.error('Erro ao carregar storage:', err); container.innerHTML = `

Erro ao carregar clones: ${err.message}

`; showStorageLoading(false); } } function showStorageLoading(show) { const loader = document.getElementById('storageLoader'); if (loader) loader.style.display = show ? 'flex' : 'none'; } function renderStorageSummary(summary) { const el = document.getElementById('storageSummary'); if (!el || !summary) return; el.innerHTML = `

${summary.total}

Total de Clones

${summary.formattedSize || '0 MB'}

Espaço Usado

${summary.byStatus?.active || 0}

Ativos

${summary.byStatus?.archived || 0}

Arquivados

`; } function renderClonesGrid(clones) { const container = document.getElementById('storageClonesGrid'); if (!container) return; if (!clones || clones.length === 0) { container.innerHTML = `

Nenhum clone encontrado

Clone um site para começar a gerenciar seu storage

`; return; } container.innerHTML = clones.map(clone => `

${truncateUrl(clone.baseUrl)}

${clone.baseUrl}

${formatDate(clone.cloneInfo?.clonedAt)} ${clone.stats?.fileCount || 0} arquivos ${clone.stats?.formattedSize || '0 B'}
`).join(''); } function showCloneDetails(cloneId) { const clone = storageState.clones.find(c => c.id === cloneId); if (!clone) return; storageState.selectedClone = clone; const modal = createModal('cloneDetailsModal', ` `); document.body.appendChild(modal); showModal('cloneDetailsModal'); } function renderVersionsList(versions) { if (!versions || versions.length === 0) { return '

Nenhuma versão salva. Clique em "Criar Snapshot" para salvar o estado atual.

'; } return versions.map(v => `
${v.id} ${formatDate(v.createdAt)} ${formatBytes(v.size)}
`).join(''); } function showCloneVersions(cloneId) { const clone = storageState.clones.find(c => c.id === cloneId); if (clone) { storageState.selectedClone = clone; showCloneDetails(cloneId); } } // ============================================ // ACTIONS // ============================================ async function updateCloneStatus(cloneId, status) { try { await storageApi.updateClone(cloneId, { status }); showToast('Status atualizado', 'success'); loadStoragePanel(); } catch (err) { showToast('Erro ao atualizar status', 'error'); } } async function saveCloneTags(cloneId, tagsString) { const tags = tagsString.split(',').map(t => t.trim()).filter(t => t); try { await storageApi.updateClone(cloneId, { tags }); showToast('Tags salvas', 'success'); } catch (err) { showToast('Erro ao salvar tags', 'error'); } } async function createNewVersion(cloneId) { try { showToast('Criando snapshot...', 'info'); const result = await storageApi.createVersion(cloneId); if (result.success) { showToast('Snapshot criado com sucesso', 'success'); // Refresh the details modal showCloneDetails(cloneId); loadStoragePanel(); } } catch (err) { showToast('Erro ao criar snapshot: ' + err.message, 'error'); } } async function restoreCloneVersion(cloneId, versionId) { if (!confirm('Restaurar esta versão? O estado atual será salvo como backup.')) return; try { showToast('Restaurando versão...', 'info'); const result = await storageApi.restoreVersion(cloneId, versionId); if (result.success) { showToast('Versão restaurada com sucesso', 'success'); closeModal('cloneDetailsModal'); loadStoragePanel(); } } catch (err) { showToast('Erro ao restaurar: ' + err.message, 'error'); } } async function deleteCloneVersion(cloneId, versionId) { if (!confirm('Excluir esta versão?')) return; try { const result = await storageApi.deleteVersion(cloneId, versionId); if (result.success) { showToast('Versão removida', 'success'); showCloneDetails(cloneId); loadStoragePanel(); } } catch (err) { showToast('Erro ao remover versão', 'error'); } } function confirmDeleteClone(cloneId) { if (!confirm('Tem certeza que deseja excluir este clone permanentemente?')) return; storageApi.deleteClone(cloneId) .then(result => { if (result.success) { showToast('Clone excluído', 'success'); closeModal('cloneDetailsModal'); loadStoragePanel(); } }) .catch(err => showToast('Erro ao excluir: ' + err.message, 'error')); } function openClone(cloneId) { window.open(`/api/preview/${cloneId}`, '_blank'); } function cloneSiteAgain(baseUrl) { // Navigate to new clone page with URL pre-filled switchAgentTab('designer'); const urlInput = document.getElementById('designUrl'); if (urlInput) { urlInput.value = baseUrl.startsWith('http') ? baseUrl : 'https://' + baseUrl; } showToast(`URL carregada: ${baseUrl}`, 'info'); } function showCloneMenu(cloneId) { // Simple context menu const clone = storageState.clones.find(c => c.id === cloneId); if (!clone) return; const action = prompt(`Ações para ${clone.baseUrl}: 1. Abrir 2. Detalhes 3. Versões 4. Reclonar 5. Excluir Digite o número:`); if (action === '1') openClone(cloneId); else if (action === '2') showCloneDetails(cloneId); else if (action === '3') showCloneVersions(cloneId); else if (action === '4') cloneSiteAgain(clone.baseUrl); else if (action === '5') confirmDeleteClone(cloneId); } // ============================================ // MODAL HELPERS // ============================================ function createModal(id, content) { const existing = document.getElementById(id); if (existing) existing.remove(); const modal = document.createElement('div'); modal.id = id; modal.className = 'modal'; modal.innerHTML = ``; modal.addEventListener('click', (e) => { if (e.target.classList.contains('modal')) closeModal(id); }); return modal; } function showModal(id) { const modal = document.getElementById(id); if (modal) { modal.style.display = 'flex'; setTimeout(() => modal.classList.add('active'), 10); } } function closeModal(id) { const modal = document.getElementById(id); if (modal) { modal.classList.remove('active'); setTimeout(() => modal.remove(), 300); } } // ============================================ // UTILITIES // ============================================ function truncateUrl(url, maxLen = 30) { if (!url) return ''; if (url.length <= maxLen) return url; return url.substring(0, maxLen) + '...'; } function formatDate(dateStr) { if (!dateStr) return 'N/A'; const date = new Date(dateStr); return date.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); } function formatBytes(bytes) { if (!bytes || bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } function showToast(message, type = 'info') { // Simple alert for now - could be replaced with a proper toast system console.log(`[${type}] ${message}`); // Create a simple toast element const toast = document.createElement('div'); toast.className = `toast toast-${type}`; toast.textContent = message; toast.style.cssText = ` position: fixed; bottom: 20px; right: 20px; padding: 12px 24px; background: ${type === 'success' ? '#43e97b' : type === 'error' ? '#f5576c' : '#667eea'}; color: white; border-radius: 8px; z-index: 10000; animation: slideIn 0.3s ease; `; document.body.appendChild(toast); setTimeout(() => { toast.style.opacity = '0'; setTimeout(() => toast.remove(), 300); }, 3000); } // ============================================ // INIT // ============================================ document.addEventListener('DOMContentLoaded', function() { // If storage page element exists, load the panel const storageContent = document.getElementById('storageContent'); if (storageContent) { loadStoragePanel(); } // Add storage nav click handler const storageNav = document.querySelector('[data-page="storage"]'); if (storageNav) { storageNav.addEventListener('click', loadStoragePanel); } console.log('✅ Storage Panel initialized'); });