Corrigir exclusao e salvamento de projetos com dialogos customizados e botao duplo salvar

This commit is contained in:
2026-06-03 20:43:05 +00:00
parent b3191fda47
commit e3c65beb4f
3 changed files with 228 additions and 22 deletions
+114 -21
View File
@@ -11,6 +11,72 @@ function escapeHtml(text) {
.replace(/'/g, "'");
}
// Função auxiliar para exibir alerta customizado e elegante (substitui o alert nativo)
function showCustomAlert(title, message) {
return new Promise((resolve) => {
const overlay = document.createElement('div');
overlay.className = 'custom-dialog-overlay';
overlay.innerHTML = `
<div class="custom-dialog-card">
<div class="custom-dialog-header">
<span>🔔</span>
<span>${escapeHtml(title)}</span>
</div>
<p class="custom-dialog-body">${escapeHtml(message)}</p>
<div class="custom-dialog-footer">
<button class="btn-dialog btn-dialog-confirm">OK</button>
</div>
</div>
`;
const btn = overlay.querySelector('.btn-dialog-confirm');
btn.addEventListener('click', () => {
overlay.remove();
resolve();
});
document.body.appendChild(overlay);
});
}
// Função auxiliar para exibir diálogo de confirmação elegante (substitui o confirm nativo)
function showCustomConfirm(title, message, isDanger = false) {
return new Promise((resolve) => {
const overlay = document.createElement('div');
overlay.className = 'custom-dialog-overlay';
overlay.innerHTML = `
<div class="custom-dialog-card">
<div class="custom-dialog-header">
<span>${isDanger ? '⚠️' : '❓'}</span>
<span>${escapeHtml(title)}</span>
</div>
<p class="custom-dialog-body">${escapeHtml(message)}</p>
<div class="custom-dialog-footer">
<button class="btn-dialog btn-dialog-cancel">Cancelar</button>
<button class="btn-dialog ${isDanger ? 'btn-dialog-danger' : 'btn-dialog-confirm'}">${isDanger ? 'Excluir' : 'Confirmar'}</button>
</div>
</div>
`;
const btnCancel = overlay.querySelector('.btn-dialog-cancel');
const btnConfirm = overlay.querySelector(isDanger ? '.btn-dialog-danger' : '.btn-dialog-confirm');
btnCancel.addEventListener('click', () => {
overlay.remove();
resolve(false);
});
btnConfirm.addEventListener('click', () => {
overlay.remove();
resolve(true);
});
document.body.appendChild(overlay);
});
}
// Função auxiliar para gerar nome de arquivo de download formatado como <nome_data_hora>.<extensao>
function getDownloadFileName(type, ext) {
const now = new Date();
@@ -3504,6 +3570,7 @@ const initApp = () => {
const btnDeleteComicsProject = document.getElementById('btnDeleteComicsProject');
const comicsProjectTitleInput = document.getElementById('comicsProjectTitleInput');
const btnSaveComicsProject = document.getElementById('btnSaveComicsProject');
const btnSaveNewComicsProject = document.getElementById('btnSaveNewComicsProject');
let currentComicsProjectId = null;
let currentComicsCharDescEnglish = '';
@@ -3619,6 +3686,7 @@ const initApp = () => {
if (comicsProjectSelect) comicsProjectSelect.value = '';
if (comicsProjectTitleInput) comicsProjectTitleInput.value = '';
if (btnDeleteComicsProject) btnDeleteComicsProject.style.display = 'none';
if (btnSaveComicsProject) btnSaveComicsProject.style.display = 'none';
comicsTemaInput.value = '';
comicsResultArea.style.display = 'none';
@@ -3707,6 +3775,7 @@ const initApp = () => {
comicsTemaInput.value = project.tema || '';
if (btnDeleteComicsProject) btnDeleteComicsProject.style.display = 'block';
if (btnSaveComicsProject) btnSaveComicsProject.style.display = 'block';
// Carregar proporção
selectedComicsRatio = project.proporcao || '16:9';
@@ -3829,34 +3898,40 @@ const initApp = () => {
} catch (err) {
console.error(err);
alert('Erro ao carregar projeto: ' + err.message);
await showCustomAlert('Erro', 'Erro ao carregar projeto: ' + err.message);
}
};
const saveComicsProject = async () => {
const saveComicsProjectFlow = async (forceNew = false) => {
const titulo = comicsProjectTitleInput ? comicsProjectTitleInput.value.trim() : '';
if (!titulo) {
alert('Por favor, digite um título para salvar seu projeto.');
await showCustomAlert('Campo Obrigatório', 'Por favor, digite um título para salvar seu projeto.');
return;
}
const characters = [];
document.querySelectorAll('.comic-char-row').forEach(row => {
const type = row.querySelector('.char-type-input').value.trim();
const name = row.querySelector('.char-name-input').value.trim();
const isAnimal = row.querySelector('span').textContent === '🐾';
if (type || name) {
characters.push({ type, name, isAnimal });
const typeInput = row.querySelector('.char-type-input');
const nameInput = row.querySelector('.char-name-input');
if (typeInput && nameInput) {
const type = typeInput.value.trim();
const name = nameInput.value.trim();
const isAnimal = row.querySelector('span').textContent === '🐾';
if (type || name) {
characters.push({ type, name, isAnimal });
}
}
});
let cenario = comicsCenarioSelect.value;
if (cenario === 'custom') {
let cenario = comicsCenarioSelect ? comicsCenarioSelect.value : 'no parque de diversões colorido';
if (cenario === 'custom' && comicsCenarioCustomInput) {
cenario = comicsCenarioCustomInput.value.trim();
}
const targetId = forceNew ? null : currentComicsProjectId;
const body = {
id: currentComicsProjectId,
id: targetId,
titulo,
tema: comicsTemaInput.value.trim(),
cenario,
@@ -3885,18 +3960,28 @@ const initApp = () => {
const data = await resp.json();
currentComicsProjectId = data.id;
alert('Projeto salvo com sucesso!');
await showCustomAlert('Sucesso', 'Projeto salvo com sucesso!');
if (btnDeleteComicsProject) btnDeleteComicsProject.style.display = 'block';
if (btnSaveComicsProject) btnSaveComicsProject.style.display = 'block';
await loadComicsProjectsList();
} catch (err) {
console.error(err);
alert('Erro ao salvar projeto: ' + err.message);
await showCustomAlert('Erro', 'Erro ao salvar projeto: ' + err.message);
}
};
const saveComicsProject = () => saveComicsProjectFlow(false);
const saveNewComicsProject = () => saveComicsProjectFlow(true);
const deleteComicsProject = async () => {
if (!currentComicsProjectId) return;
if (!confirm('Tem certeza de que deseja excluir este projeto permanentemente?')) return;
const confirmed = await showCustomConfirm(
'Confirmar Exclusão',
'Tem certeza de que deseja excluir este projeto permanentemente?',
true
);
if (!confirmed) return;
try {
const resp = await fetch(`/api/comics/projects/${currentComicsProjectId}`, {
@@ -3905,12 +3990,12 @@ const initApp = () => {
if (!resp.ok) throw new Error('Falha ao excluir projeto');
alert('Projeto excluído com sucesso!');
await showCustomAlert('Excluído', 'Projeto excluído com sucesso!');
startNewComicsProject();
await loadComicsProjectsList();
} catch (err) {
console.error(err);
alert('Erro ao excluir projeto: ' + err.message);
await showCustomAlert('Erro', 'Erro ao excluir projeto: ' + err.message);
}
};
@@ -3929,8 +4014,12 @@ const initApp = () => {
}
if (btnNewComicsProject) {
btnNewComicsProject.addEventListener('click', () => {
if (confirm('Deseja iniciar um novo projeto? Alterações não salvas serão perdidas.')) {
btnNewComicsProject.addEventListener('click', async () => {
const confirmed = await showCustomConfirm(
'Novo Projeto',
'Deseja iniciar um novo projeto? Alterações não salvas serão perdidas.'
);
if (confirmed) {
startNewComicsProject();
}
});
@@ -3940,6 +4029,10 @@ const initApp = () => {
btnSaveComicsProject.addEventListener('click', saveComicsProject);
}
if (btnSaveNewComicsProject) {
btnSaveNewComicsProject.addEventListener('click', saveNewComicsProject);
}
if (btnDeleteComicsProject) {
btnDeleteComicsProject.addEventListener('click', deleteComicsProject);
}
@@ -3986,7 +4079,7 @@ const initApp = () => {
btnGenerateComics.addEventListener('click', async () => {
const tema = comicsTemaInput.value.trim();
if (!tema) {
alert('Por favor, digite um tema para a história.');
await showCustomAlert('Campo Requerido', 'Por favor, digite um tema para a história.');
return;
}
@@ -4001,7 +4094,7 @@ const initApp = () => {
});
if (personagens.length === 0) {
alert('Adicione e preencha pelo menos um personagem principal.');
await showCustomAlert('Personagens Faltando', 'Adicione e preencha pelo menos um personagem principal.');
return;
}
@@ -4161,7 +4254,7 @@ const initApp = () => {
} catch (err) {
console.error('Erro ao fabricar quadrinhos:', err);
alert('Erro ao fabricar quadrinhos: ' + err.message);
await showCustomAlert('Erro', 'Erro ao fabricar quadrinhos: ' + err.message);
comicsGenerateLoader.style.display = 'none';
btnGenerateComics.disabled = false;
}
+2 -1
View File
@@ -759,7 +759,8 @@
<div style="display: flex; align-items: center; gap: 10px;">
<input type="text" id="comicsProjectTitleInput" class="obs-input" placeholder="Título do Projeto (Ex: Lucas e Mariana no Parque)" style="flex: 1; margin: 0; padding: 8px 12px; font-size: 0.9rem;">
<button type="button" id="btnSaveComicsProject" class="btn-music-option" style="padding: 8px 14px; font-size: 0.82rem; border-color: #10a37f; color: white; background: #10a37f; margin: 0; white-space: nowrap; font-weight: 600;">💾 Salvar</button>
<button type="button" id="btnSaveComicsProject" class="btn-music-option" style="padding: 8px 14px; font-size: 0.82rem; border-color: #10a37f; color: white; background: #10a37f; margin: 0; white-space: nowrap; font-weight: 600; display: none;">💾 Salvar Alterações</button>
<button type="button" id="btnSaveNewComicsProject" class="btn-music-option" style="padding: 8px 14px; font-size: 0.82rem; border-color: var(--brand-pink); color: white; background: var(--brand-pink); margin: 0; white-space: nowrap; font-weight: 600;">💾 Salvar como Novo</button>
</div>
<!-- MODO DE CONTINUIDADE (Apenas quando um projeto é carregado) -->
+112
View File
@@ -3231,5 +3231,117 @@ code {
box-shadow: 0 0 8px rgba(219, 39, 119, 0.2);
}
/* ==========================================================================
CUSTOM MODAL DIALOGS (ALERT & CONFIRM)
========================================================================== */
.custom-dialog-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
display: flex;
justify-content: center;
align-items: center;
z-index: 10000;
animation: dialogFadeIn 0.2s ease-out forwards;
}
.custom-dialog-card {
background: var(--bg-primary, #121212);
border: 1px solid var(--border-light, #262626);
border-radius: 16px;
width: 400px;
max-width: 90%;
padding: 24px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
display: flex;
flex-direction: column;
gap: 16px;
transform: scale(0.9);
opacity: 0;
animation: dialogScaleUp 0.25s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}
.custom-dialog-header {
font-size: 1.1rem;
font-weight: 700;
color: var(--text-primary, #ffffff);
display: flex;
align-items: center;
gap: 8px;
}
.custom-dialog-body {
font-size: 0.92rem;
color: var(--text-secondary, #a3a3a3);
line-height: 1.5;
margin: 0;
}
.custom-dialog-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 8px;
}
.btn-dialog {
padding: 8px 18px;
font-size: 0.85rem;
font-weight: 600;
border-radius: 8px;
cursor: pointer;
border: 1px solid transparent;
transition: all 0.2s ease;
}
.btn-dialog-cancel {
background: transparent;
border-color: var(--border-light, #262626);
color: var(--text-secondary, #a3a3a3);
}
.btn-dialog-cancel:hover {
background: rgba(255, 255, 255, 0.05);
color: var(--text-primary, #ffffff);
}
.btn-dialog-confirm {
background: linear-gradient(135deg, var(--brand-green, #10a37f) 0%, #0a5f49 100%);
color: white;
box-shadow: 0 4px 12px rgba(16, 163, 127, 0.25);
}
.btn-dialog-confirm:hover {
opacity: 0.9;
transform: translateY(-1px);
}
.btn-dialog-danger {
background: linear-gradient(135deg, #ef4444 0%, #b91c1c 100%);
color: white;
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.25);
}
.btn-dialog-danger:hover {
opacity: 0.9;
transform: translateY(-1px);
}
@keyframes dialogFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes dialogScaleUp {
from { transform: scale(0.95); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}