página de usuário

This commit is contained in:
JotaChina committed 2025-12-10 21:52:33 -03:00
1 parent 0a8538e5f2
commit 00f001aa6d
3 files changed
+211 -113

No files matched your search

+103 -85
View File
@@ -91,15 +91,20 @@ permalink: /perfil/
<div class="table-container">
<table class="table is-fullwidth is-hoverable is-striped">
<thead><tr><th>Nome do Projeto</th><th>Data</th><th class="has-text-right">Ações</th></tr></thead>
<tbody id="projects-list">
</tbody>
<tbody id="projects-list"></tbody>
</table>
<p id="no-projects" class="has-text-grey is-hidden has-text-centered p-4">Nenhum projeto encontrado.</p>
</div>
</div>
<div id="samples-tab" class="content-tab-content is-hidden">
<p class="has-text-centered has-text-grey p-5">Em breve.</p>
<div class="table-container">
<table class="table is-fullwidth is-hoverable is-striped">
<thead><tr><th>Nome do Sample</th><th>Categoria</th><th>Data</th><th class="has-text-right">Ações</th></tr></thead>
<tbody id="samples-list"></tbody>
</table>
<p id="no-samples" class="has-text-grey is-hidden has-text-centered p-4">Nenhum sample enviado.</p>
</div>
</div>
</div>
</div>
@@ -108,18 +113,30 @@ permalink: /perfil/
<div id="preview-modal" class="modal">
<div class="modal-background"></div>
<div class="modal-card" style="width: 90%; max-width: 900px; height: 80vh;">
<div class="modal-card" style="width: 90%; max-width: 900px;">
<header class="modal-card-head" style="background-color: #f0f8ff; border-bottom: 1px solid #cfe8fc;">
<p class="modal-card-title" id="modal-title" style="color: #205081; font-weight: bold;">Preview</p>
<button class="delete" aria-label="close"></button>
</header>
<section class="modal-card-body p-0" style="background-color: #fff; overflow: hidden;">
<iframe id="preview-iframe" src="" style="width: 100%; height: 100%; border: none;"></iframe>
<section class="modal-card-body p-0" style="background-color: #fff; min-height: 200px; display: flex; align-items: center; justify-content: center;">
<iframe id="preview-iframe" class="is-hidden" src="" style="width: 100%; height: 60vh; border: none;"></iframe>
<div id="audio-player-container" class="is-hidden p-5 has-text-centered" style="width: 100%;">
<div class="icon is-large has-text-info mb-4">
<i class="fa-solid fa-music fa-3x"></i>
</div>
<h4 id="audio-name" class="title is-5 mb-2"></h4>
<audio id="preview-audio" controls style="width: 80%;"></audio>
</div>
</section>
<footer class="modal-card-foot" style="justify-content: flex-end; background-color: #fff; border-top: 1px solid #cfe8fc;">
<button class="button" id="close-modal-btn">Fechar</button>
<a href="#" id="full-edit-btn" target="_blank" class="button is-info">
<span>Abrir em Nova Aba</span>
<span>Abrir / Baixar</span>
<span class="icon is-small ml-1"><i class="fa-solid fa-up-right-from-square"></i></span>
</a>
</footer>
@@ -128,70 +145,85 @@ permalink: /perfil/
<script>
document.addEventListener('DOMContentLoaded', () => {
// --- Lógica do Perfil ---
// Referências DOM
const els = {
username: document.getElementById('display-username'),
bio: document.getElementById('display-bio'),
tags: document.getElementById('display-tags'),
projectList: document.getElementById('projects-list'),
// ... (referências do form de edição iguais ao anterior)
sampleList: document.getElementById('samples-list'),
formBox: document.getElementById('edit-form-box'),
form: document.getElementById('profile-form'),
inpUser: document.getElementById('input-username'),
inpBio: document.getElementById('input-bio'),
inpTags: document.getElementById('input-tags'),
msg: document.getElementById('update-msg'),
btnEdit: document.getElementById('btn-edit-profile'),
btnCancel: document.getElementById('btn-cancel-edit'),
msg: document.getElementById('update-msg')
btnCancel: document.getElementById('btn-cancel-edit')
};
// --- Lógica do Modal ---
// Referências Modal
const modal = document.getElementById('preview-modal');
const iframe = document.getElementById('preview-iframe');
const audioContainer = document.getElementById('audio-player-container');
const audioPlayer = document.getElementById('preview-audio');
const audioName = document.getElementById('audio-name');
const modalTitle = document.getElementById('modal-title');
const fullEditBtn = document.getElementById('full-edit-btn');
function openModal(url, title, fullLink) {
// --- FUNÇÃO DE MODAL INTELIGENTE ---
window.openModal = (type, url, title, fullLink) => {
modalTitle.textContent = title;
iframe.src = url;
fullEditBtn.href = fullLink || url;
// Resetar
iframe.classList.add('is-hidden');
audioContainer.classList.add('is-hidden');
iframe.src = "";
audioPlayer.pause();
if (type === 'project') {
iframe.classList.remove('is-hidden');
iframe.src = url;
// Tenta esconder menu do iframe
iframe.onload = () => {
try {
const doc = iframe.contentDocument || iframe.contentWindow.document;
const style = doc.createElement('style');
style.textContent = ".tabs, .navbar, footer { display: none !important; } body { overflow: auto !important; }";
doc.head.appendChild(style);
} catch(e){}
};
} else if (type === 'audio') {
audioContainer.classList.remove('is-hidden');
audioName.textContent = title.replace('Ouvir: ', '');
audioPlayer.src = url;
audioPlayer.play();
}
modal.classList.add('is-active');
document.documentElement.classList.add('is-clipped');
}
};
function closeModal() {
modal.classList.remove('is-active');
document.documentElement.classList.remove('is-clipped');
iframe.src = "";
audioPlayer.pause();
}
document.querySelectorAll('.modal-background, .modal-card-head .delete, #close-modal-btn').forEach(el => el.onclick = closeModal);
// Fecha modal nos botões X e Background
document.querySelectorAll('.modal-background, .modal-card-head .delete, #close-modal-btn').forEach(el => {
el.addEventListener('click', closeModal);
});
// Limpa visual dentro do iframe para parecer um "modal nativo"
iframe.addEventListener('load', () => {
try {
const doc = iframe.contentDocument || iframe.contentWindow.document;
const style = doc.createElement('style');
style.textContent = `
.tabs, .navbar, .sidebar-wrapper, .main-header, footer { display: none !important; }
.publication { padding-top: 0 !important; }
body { background-color: #fff !important; overflow: auto !important; }
`;
doc.head.appendChild(style);
} catch(e) {}
});
// --- Carregar Dados ---
// --- CARREGAR DADOS ---
async function loadProfile() {
try {
const res = await fetch('/api/user/profile');
if (res.status === 401) { window.location.href = '/login/'; return; }
const data = await res.json();
// Preenche Header
// Header
els.username.textContent = data.username;
els.bio.textContent = data.bio || "Sem bio definida.";
els.tags.innerHTML = '';
@@ -199,92 +231,78 @@ document.addEventListener('DOMContentLoaded', () => {
if(t.trim()) els.tags.innerHTML += `<span class="tag is-info is-light mr-1">${t.trim()}</span>`;
});
// Preenche Forms
// Forms Values
els.inpUser.value = data.username;
els.inpBio.value = data.bio || "";
els.inpTags.value = data.tags || "";
// Preenche Lista de Projetos com Lógica de Link e Modal
// 1. Projetos
els.projectList.innerHTML = '';
if (data.projects && data.projects.length > 0) {
data.projects.forEach(p => {
// Prepara URLs
// Assume que o arquivo mmp gera uma pagina em /projetos/NOME_SEM_EXTENSAO/
const slug = p.filename.replace('.mmp', '').replace('.mmpz', '');
// URL da página pública do projeto
const pageUrl = `/mmpSearch/projetos/${slug}.html`;
// URL do editor embedado
const editorUrl = `/mmpSearch/creation.html?project=${p.filename}&embed=true`;
els.projectList.innerHTML += `
<tr>
<td style="vertical-align: middle;">
<a href="${pageUrl}" class="has-text-weight-bold" style="color: #205081; text-decoration: none;">
<span class="icon is-small mr-1"><i class="fa-solid fa-music"></i></span>
${p.display_name}
<a href="${pageUrl}" class="has-text-weight-bold" style="color: #205081;">
<span class="icon is-small mr-1"><i class="fa-solid fa-music"></i></span>${p.display_name}
</a>
</td>
<td style="vertical-align: middle;">${p.created_at}</td>
<td class="has-text-right">
<div class="buttons is-right">
<button class="button is-small is-info is-light"
onclick="openModal('${pageUrl}', 'Detalhes: ${p.display_name}', '${pageUrl}')"
title="Ver detalhes">
<i class="fa-solid fa-eye"></i>
</button>
<button class="button is-small is-warning is-light"
onclick="openModal('${editorUrl}', 'Editor: ${p.display_name}', '${editorUrl}')"
title="Editar no Navegador">
<i class="fa-solid fa-sliders"></i>
</button>
<a href="${p.download_link}" class="button is-small is-primary is-light" title="Baixar MMP">
<i class="fa-solid fa-download"></i>
</a>
<button class="button is-small is-danger is-light" onclick="deleteProject(${p.id})" title="Excluir">
<i class="fa-solid fa-trash"></i>
</button>
</div>
<button class="button is-small is-info is-light" onclick="openModal('project', '${pageUrl}', 'Detalhes: ${p.display_name}', '${pageUrl}')"><i class="fa-solid fa-eye"></i></button>
<button class="button is-small is-warning is-light" onclick="openModal('project', '${editorUrl}', 'Editor: ${p.display_name}', '${editorUrl}')"><i class="fa-solid fa-sliders"></i></button>
<a href="${p.download_link}" class="button is-small is-primary is-light"><i class="fa-solid fa-download"></i></a>
<button class="button is-small is-danger is-light" onclick="deleteItem('project', ${p.id})"><i class="fa-solid fa-trash"></i></button>
</td>
</tr>
`;
</tr>`;
});
} else {
document.getElementById('no-projects').classList.remove('is-hidden');
}
// 2. Samples
els.sampleList.innerHTML = '';
if (data.samples && data.samples.length > 0) {
data.samples.forEach(s => {
els.sampleList.innerHTML += `
<tr>
<td style="vertical-align: middle;"><strong>${s.display_name}</strong></td>
<td style="vertical-align: middle;"><span class="tag is-light">${s.category}</span></td>
<td style="vertical-align: middle;">${s.created_at}</td>
<td class="has-text-right">
<button class="button is-small is-success is-light" onclick="openModal('audio', '${s.download_link}', 'Ouvir: ${s.display_name}', '${s.download_link}')"><i class="fa-solid fa-play"></i></button>
<a href="${s.download_link}" download class="button is-small is-primary is-light"><i class="fa-solid fa-download"></i></a>
<button class="button is-small is-danger is-light" onclick="deleteItem('sample', ${s.id})"><i class="fa-solid fa-trash"></i></button>
</td>
</tr>`;
});
} else {
document.getElementById('no-samples').classList.remove('is-hidden');
}
} catch(e) { console.error(e); }
}
// --- Outros Handlers ---
// Handlers de Edição
els.btnEdit.onclick = () => els.formBox.classList.remove('is-hidden');
els.btnCancel.onclick = () => els.formBox.classList.add('is-hidden');
// Handler de update (mesmo do anterior)
els.form.onsubmit = async (e) => {
e.preventDefault();
// ... (Código de fetch /api/user/update igual ao anterior) ...
// Para economizar espaço, mantenha a lógica de POST aqui
// No sucesso, chame loadProfile() e esconda o form
// Simulação rápida para o exemplo:
const payload = { username: els.inpUser.value, bio: els.inpBio.value, tags: els.inpTags.value };
const res = await fetch('/api/user/update', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify(payload)});
if(res.ok) { els.formBox.classList.add('is-hidden'); loadProfile(); }
};
// Disponibiliza função global de delete
window.deleteProject = async (id) => {
if(!confirm("Apagar projeto permanentemente?")) return;
const res = await fetch(`/api/project/${id}`, { method: 'DELETE' });
// Função Delete Genérica
window.deleteItem = async (type, id) => {
if(!confirm("Tem certeza que deseja apagar?")) return;
const res = await fetch(`/api/${type}/${id}`, { method: 'DELETE' });
if(res.ok) loadProfile();
};
// Disponibiliza função global para modal (usada no onclick inline)
window.openModal = openModal;
// Inicializa
loadProfile();