Add global loader overlay and progress feedback for imports
This commit is contained in:
parent
fa4423acfe
commit
4ff7708f76
|
|
@ -540,6 +540,44 @@ main.container {
|
||||||
color: #ff8a8a;
|
color: #ff8a8a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.global-loader {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.55);
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.global-loader .loader-content {
|
||||||
|
background: var(--color-bg-card);
|
||||||
|
padding: 1.5rem 2rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35);
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
min-width: 280px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.global-loader .spinner {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border: 3px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-top-color: var(--color-brand);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
/* Detail views */
|
/* Detail views */
|
||||||
.breadcrumb {
|
.breadcrumb {
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,9 @@ function displayGlobalSearchResults(data) {
|
||||||
|
|
||||||
// Bulk Import Functions
|
// Bulk Import Functions
|
||||||
async function bulkImportAll() {
|
async function bulkImportAll() {
|
||||||
|
showLoader('Importing all data from TPDB...');
|
||||||
if (!confirm('This will import ALL data from TPDB. This may take several hours. Continue?')) {
|
if (!confirm('This will import ALL data from TPDB. This may take several hours. Continue?')) {
|
||||||
|
hideLoader();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,11 +129,15 @@ async function bulkImportAll() {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setImportStatus('import-all', 'Error: ' + error.message, false);
|
setImportStatus('import-all', 'Error: ' + error.message, false);
|
||||||
|
} finally {
|
||||||
|
hideLoader();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bulkImportPerformers() {
|
async function bulkImportPerformers() {
|
||||||
|
showLoader('Importing performers from TPDB...');
|
||||||
if (!confirm('This will import ALL performers from TPDB. Continue?')) {
|
if (!confirm('This will import ALL performers from TPDB. Continue?')) {
|
||||||
|
hideLoader();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,11 +171,14 @@ async function bulkImportPerformers() {
|
||||||
eventSource.onerror = function() {
|
eventSource.onerror = function() {
|
||||||
updateProgress('performers', 0, 0, 'Connection error', true);
|
updateProgress('performers', 0, 0, 'Connection error', true);
|
||||||
eventSource.close();
|
eventSource.close();
|
||||||
|
hideLoader();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bulkImportStudios() {
|
async function bulkImportStudios() {
|
||||||
|
showLoader('Importing studios from TPDB...');
|
||||||
if (!confirm('This will import ALL studios from TPDB. Continue?')) {
|
if (!confirm('This will import ALL studios from TPDB. Continue?')) {
|
||||||
|
hideLoader();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -203,11 +212,14 @@ async function bulkImportStudios() {
|
||||||
eventSource.onerror = function() {
|
eventSource.onerror = function() {
|
||||||
updateProgress('studios', 0, 0, 'Connection error', true);
|
updateProgress('studios', 0, 0, 'Connection error', true);
|
||||||
eventSource.close();
|
eventSource.close();
|
||||||
|
hideLoader();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bulkImportScenes() {
|
async function bulkImportScenes() {
|
||||||
|
showLoader('Importing scenes from TPDB...');
|
||||||
if (!confirm('This will import ALL scenes from TPDB. Continue?')) {
|
if (!confirm('This will import ALL scenes from TPDB. Continue?')) {
|
||||||
|
hideLoader();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -241,6 +253,7 @@ async function bulkImportScenes() {
|
||||||
eventSource.onerror = function() {
|
eventSource.onerror = function() {
|
||||||
updateProgress('scenes', 0, 0, 'Connection error', true);
|
updateProgress('scenes', 0, 0, 'Connection error', true);
|
||||||
eventSource.close();
|
eventSource.close();
|
||||||
|
hideLoader();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -291,6 +304,7 @@ async function aeImportPerformerByName() {
|
||||||
const name = prompt('Import performer by name (Adult Empire):');
|
const name = prompt('Import performer by name (Adult Empire):');
|
||||||
if (!name) return;
|
if (!name) return;
|
||||||
setAEStatus(`Searching Adult Empire for "${name}"...`);
|
setAEStatus(`Searching Adult Empire for "${name}"...`);
|
||||||
|
showLoader(`Importing performer "${name}" from Adult Empire...`);
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/ae/import/performer', {
|
const res = await fetch('/api/ae/import/performer', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
@ -306,6 +320,8 @@ async function aeImportPerformerByName() {
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setAEStatus(`Error: ${err.message}`, true);
|
setAEStatus(`Error: ${err.message}`, true);
|
||||||
|
} finally {
|
||||||
|
hideLoader();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -313,6 +329,7 @@ async function aeImportPerformerByURL() {
|
||||||
const url = prompt('Paste Adult Empire performer URL:');
|
const url = prompt('Paste Adult Empire performer URL:');
|
||||||
if (!url) return;
|
if (!url) return;
|
||||||
setAEStatus('Importing performer from Adult Empire URL...');
|
setAEStatus('Importing performer from Adult Empire URL...');
|
||||||
|
showLoader('Importing performer from Adult Empire URL...');
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/ae/import/performer-by-url', {
|
const res = await fetch('/api/ae/import/performer-by-url', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
@ -328,6 +345,8 @@ async function aeImportPerformerByURL() {
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setAEStatus(`Error: ${err.message}`, true);
|
setAEStatus(`Error: ${err.message}`, true);
|
||||||
|
} finally {
|
||||||
|
hideLoader();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -335,6 +354,7 @@ async function aeImportSceneByName() {
|
||||||
const title = prompt('Import scene by title (Adult Empire):');
|
const title = prompt('Import scene by title (Adult Empire):');
|
||||||
if (!title) return;
|
if (!title) return;
|
||||||
setAEStatus(`Searching Adult Empire for "${title}"...`);
|
setAEStatus(`Searching Adult Empire for "${title}"...`);
|
||||||
|
showLoader(`Importing scene "${title}" from Adult Empire...`);
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/ae/import/scene', {
|
const res = await fetch('/api/ae/import/scene', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
@ -350,6 +370,8 @@ async function aeImportSceneByName() {
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setAEStatus(`Error: ${err.message}`, true);
|
setAEStatus(`Error: ${err.message}`, true);
|
||||||
|
} finally {
|
||||||
|
hideLoader();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -357,6 +379,7 @@ async function aeImportSceneByURL() {
|
||||||
const url = prompt('Paste Adult Empire scene URL:');
|
const url = prompt('Paste Adult Empire scene URL:');
|
||||||
if (!url) return;
|
if (!url) return;
|
||||||
setAEStatus('Importing scene from Adult Empire URL...');
|
setAEStatus('Importing scene from Adult Empire URL...');
|
||||||
|
showLoader('Importing scene from Adult Empire URL...');
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/ae/import/scene-by-url', {
|
const res = await fetch('/api/ae/import/scene-by-url', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
@ -372,6 +395,8 @@ async function aeImportSceneByURL() {
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setAEStatus(`Error: ${err.message}`, true);
|
setAEStatus(`Error: ${err.message}`, true);
|
||||||
|
} finally {
|
||||||
|
hideLoader();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -539,6 +564,23 @@ function setImportStatus(type, message, success) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close modals when clicking outside
|
// Close modals when clicking outside
|
||||||
|
|
||||||
|
// Global loader helpers
|
||||||
|
function showLoader(msg) {
|
||||||
|
const overlay = document.getElementById('global-loader');
|
||||||
|
const text = document.getElementById('global-loader-text');
|
||||||
|
if (overlay) {
|
||||||
|
overlay.style.display = 'flex';
|
||||||
|
}
|
||||||
|
if (text && msg) {
|
||||||
|
text.textContent = msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideLoader() {
|
||||||
|
const overlay = document.getElementById('global-loader');
|
||||||
|
if (overlay) overlay.style.display = 'none';
|
||||||
|
}
|
||||||
window.onclick = function(event) {
|
window.onclick = function(event) {
|
||||||
if (event.target.classList.contains('modal')) {
|
if (event.target.classList.contains('modal')) {
|
||||||
event.target.classList.remove('active');
|
event.target.classList.remove('active');
|
||||||
|
|
|
||||||
|
|
@ -57,4 +57,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
<div id="global-loader" class="global-loader" style="display:none;">
|
||||||
|
<div class="loader-content">
|
||||||
|
<div class="spinner"></div>
|
||||||
|
<div id="global-loader-text">Working...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user