// ============================================
// 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', `
${formatDate(clone.cloneInfo?.clonedAt)}
${clone.stats?.formattedSize || '0 B'}
${clone.stats?.fileCount || 0}
Versões Salvas
${renderVersionsList(clone.versions)}
Arquivos HTML
${(clone.htmlFiles || []).map(f => `- ${f}
`).join('')}
`);
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 = `${content}
`;
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');
});