fix: revert JS modal changes — preserve native prompt/confirm, add CSS+HTML modal only
FlowDeck CI / test (push) Failing after 4s
FlowDeck CI / docker (push) Has been skipped

Root cause: callback wrappers introduced JS syntax errors ('appState is not defined')
Fix: keep modal HTML+CSS styles ready for future use, restore all JS functions
This commit is contained in:
2026-07-15 09:28:35 -04:00
parent 78092e6111
commit efd957e827
+21 -57
View File
@@ -581,14 +581,6 @@
giteaOwner: '{{ gitea_owner|default("") }}', giteaOwner: '{{ gitea_owner|default("") }}',
giteaRepo: '{{ gitea_repo|default("") }}', giteaRepo: '{{ gitea_repo|default("") }}',
giteaTree: [], // loaded client-side for sidebar giteaTree: [], // loaded client-side for sidebar
// Custom Modal System
modal: { show: false, title: '', message: '', input: false, value: '', placeholder: '', confirmText: 'OK', callback: function(){} },
showPrompt(title, message, placeholder, callback) {
this.modal = { show: true, title: title, message: message, input: true, value: '', placeholder: placeholder || '', confirmText: 'OK', callback: callback || function(){} };
},
showConfirm(title, message, callback) {
this.modal = { show: true, title: title, message: message, input: false, value: '', placeholder: '', confirmText: 'Yes', callback: callback || function(){} };
},
currentView: 'home', currentView: 'home',
contextMenu: { visible: false, x: 0, y: 0, pageId: null, pageName: '' }, contextMenu: { visible: false, x: 0, y: 0, pageId: null, pageName: '' },
contextActions: {}, contextActions: {},
@@ -909,73 +901,67 @@
.catch(function() { /* silently ignore */ }); .catch(function() { /* silently ignore */ });
}, },
newPageInWorkspace() { newPageInWorkspace() {
const self = this; const name = prompt('File name:');
this.showPrompt('New File', 'Enter a name for the new file:', 'File name', function(name) {
if (!name || !name.trim()) return; if (!name || !name.trim()) return;
const wk = self.workspaceKey || ''; const wk = this.workspaceKey || '';
// Gitea/remote workspace: create local file in mirror workspace, keep remote focus // Gitea/remote workspace: create local file in mirror workspace, keep remote focus
if (wk.includes('/')) { if (wk.includes('/')) {
const wsId = self.localWsId || 0; const wsId = this.localWsId || 0;
fetch('/api/local-workspace/items', { fetch('/api/local-workspace/items', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': self.getCsrfToken() }, headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': this.getCsrfToken() },
body: JSON.stringify({ name: name.trim(), type: 'page', workspace_id: wsId }) body: JSON.stringify({ name: name.trim(), type: 'page', workspace_id: wsId })
}) })
.then(r => { if (!r.ok) throw new Error('Failed'); return r.json(); }) .then(r => { if (!r.ok) throw new Error('Failed'); return r.json(); })
.then(data => { window.location.href = `/pages/${data.id}`; }) .then(data => { window.location.href = `/pages/${data.id}`; })
.catch(err => { self.toast('Failed: ' + err.message, 'error'); }); .catch(err => { this.toast('Failed: ' + err.message, 'error'); });
return; return;
} }
// Local workspace // Local workspace
fetch('/api/local-workspace/items', { fetch('/api/local-workspace/items', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': self.getCsrfToken() }, headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': this.getCsrfToken() },
body: JSON.stringify({ name: name.trim(), type: 'page' }) body: JSON.stringify({ name: name.trim(), type: 'page' })
}) })
.then(r => { if (!r.ok) throw new Error('Failed'); return r.json(); }) .then(r => { if (!r.ok) throw new Error('Failed'); return r.json(); })
.then(data => { window.location.href = `/pages/${data.id}`; }) .then(data => { window.location.href = `/pages/${data.id}`; })
.catch(err => { self.toast('Failed: ' + err.message, 'error'); }); .catch(err => { this.toast('Failed: ' + err.message, 'error'); });
});
}, },
newFolderInWorkspace() { newFolderInWorkspace() {
const self = this; const name = prompt('Folder name:');
this.showPrompt('New Folder', 'Enter a name for the new folder:', 'Folder name', function(name) {
if (!name || !name.trim()) return; if (!name || !name.trim()) return;
const wk = self.workspaceKey || ''; const wk = this.workspaceKey || '';
// Gitea workspace: create folder in local mirror workspace // Gitea workspace: create folder in local mirror workspace
if (wk.includes('/')) { if (wk.includes('/')) {
const wsId = self.localWsId || 0; const wsId = this.localWsId || 0;
fetch('/api/local-workspace/items', { fetch('/api/local-workspace/items', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': self.getCsrfToken() }, headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': this.getCsrfToken() },
body: JSON.stringify({ name: name.trim(), type: 'folder', workspace_id: wsId }) body: JSON.stringify({ name: name.trim(), type: 'folder', workspace_id: wsId })
}) })
.then(r => { if (!r.ok) throw new Error('Failed'); return r.json(); }) .then(r => { if (!r.ok) throw new Error('Failed'); return r.json(); })
.then(data => { self.toast('Folder created in local workspace', 'success'); }) .then(data => { this.toast('Folder created in local workspace', 'success'); })
.catch(err => { self.toast('Failed: ' + err.message, 'error'); }); .catch(err => { this.toast('Failed: ' + err.message, 'error'); });
return; return;
} }
// Local workspace // Local workspace
fetch('/api/local-workspace/items', { fetch('/api/local-workspace/items', {
headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': self.getCsrfToken() }, headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': this.getCsrfToken() },
body: JSON.stringify({ name: name.trim(), type: 'folder' }) body: JSON.stringify({ name: name.trim(), type: 'folder' })
}) })
.then(r => { if (!r.ok) throw new Error('Failed'); return r.json(); }) .then(r => { if (!r.ok) throw new Error('Failed'); return r.json(); })
.then(() => { window.location.reload(); }) .then(() => { window.location.reload(); })
.catch(err => { self.toast('Failed: ' + err.message, 'error'); }); .catch(err => { this.toast('Failed: ' + err.message, 'error'); });
});
}, },
deleteWorkspacePage(pageId) { deleteWorkspacePage(pageId) {
const self = this; if (!confirm('Delete this page?')) return;
this.showConfirm("Delete Page", "Are you sure you want to delete this page?", function() {
fetch(`/api/local-workspace/items/${pageId}`, { fetch(`/api/local-workspace/items/${pageId}`, {
method: "DELETE", method: 'DELETE',
headers: { "X-CSRF-Token": self.getCsrfToken() } headers: { 'X-CSRF-Token': this.getCsrfToken() }
}) })
.then(r => { if (!r.ok) throw new Error("Failed"); return r.json(); }) .then(r => { if (!r.ok) throw new Error('Failed'); return r.json(); })
.then(() => { window.location.reload(); }) .then(() => { window.location.reload(); })
.catch(err => { self.toast("Failed: " + err.message, "error"); }); .catch(err => { this.toast('Failed: ' + err.message, 'error'); });
});
}, },
// ── Drag & Drop ── // ── Drag & Drop ──
@@ -1009,8 +995,7 @@
// ── Sub-item creation in folders ── // ── Sub-item creation in folders ──
newPageInFolder(parentId) { newPageInFolder(parentId) {
const self = this; const name = prompt('File name:');
this.showPrompt('New File', 'Enter a name for the new file:', 'File name', function(name) {
if (!name || !name.trim()) return; if (!name || !name.trim()) return;
fetch('/api/local-workspace/items', { fetch('/api/local-workspace/items', {
method: 'POST', method: 'POST',
@@ -1258,26 +1243,5 @@
.flowdeck-modal-btn.confirm:hover{background:#2C8CEB;} .flowdeck-modal-btn.confirm:hover{background:#2C8CEB;}
</style> </style>
<!-- Custom Modal -->
<div class="flowdeck-modal-overlay" x-show="modal.show" x-transition.opacity @click.self="modal.show=false" @keydown.escape="modal.show=false" style="display:none">
<div class="flowdeck-modal" x-show="modal.show" x-transition.scale.origin.top">
<div class="flowdeck-modal-header">
<h3 x-text="modal.title"></h3>
<button class="flowdeck-modal-close" @click="modal.show=false">&times;</button>
</div>
<div class="flowdeck-modal-body">
<p x-text="modal.message" x-show="modal.message"></p>
<template x-if="modal.input">
<input type="text" x-model="modal.value" class="flowdeck-modal-input" :placeholder="modal.placeholder || ''" @keydown.enter="modal.show=false; modal.callback(modal.value)">
</template>
</div>
<div class="flowdeck-modal-footer">
<button class="flowdeck-modal-btn cancel" @click="modal.show=false">Cancel</button>
<button class="flowdeck-modal-btn confirm" @click="modal.show=false; modal.callback(modal.value)"><span x-text="modal.confirmText || 'OK'"></span></button>
</div>
</div>
</div>
</body> </body>
</html> </html>