generated from coulomb/repo-seed
Move refresh to global header button
- Add refresh button in header (before help button) - Refresh updates all uploaded documents at once (in_basket, in_production) - Remove individual refresh buttons from production items - Add spinning animation while refresh is in progress - Show count of refreshed documents in status message Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -93,6 +93,30 @@ body {
|
|||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-btn svg {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-btn.refreshing svg {
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header actions */
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-sm);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
/* Views */
|
/* Views */
|
||||||
.view {
|
.view {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|||||||
@@ -10,7 +10,16 @@
|
|||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h1>BinectChrome</h1>
|
<h1>BinectChrome</h1>
|
||||||
<button id="helpBtn" class="icon-btn" aria-label="Help and tracking info" title="Help & Info">?</button>
|
<div class="header-actions">
|
||||||
|
<button id="refreshBtn" class="icon-btn" aria-label="Refresh status from server" title="Refresh status">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<polyline points="23 4 23 10 17 10"></polyline>
|
||||||
|
<polyline points="1 20 1 14 7 14"></polyline>
|
||||||
|
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button id="helpBtn" class="icon-btn" aria-label="Help and tracking info" title="Help & Info">?</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Authentication View -->
|
<!-- Authentication View -->
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ const loginBtn = document.getElementById('loginBtn') as HTMLButtonElement;
|
|||||||
const authError = document.getElementById('authError')!;
|
const authError = document.getElementById('authError')!;
|
||||||
|
|
||||||
const logoutBtn = document.getElementById('logoutBtn')!;
|
const logoutBtn = document.getElementById('logoutBtn')!;
|
||||||
|
const refreshBtn = document.getElementById('refreshBtn')!;
|
||||||
const helpBtn = document.getElementById('helpBtn')!;
|
const helpBtn = document.getElementById('helpBtn')!;
|
||||||
const togglePasswordBtn = document.getElementById('togglePassword') as HTMLButtonElement;
|
const togglePasswordBtn = document.getElementById('togglePassword') as HTMLButtonElement;
|
||||||
const eyeIcon = document.getElementById('eyeIcon')!;
|
const eyeIcon = document.getElementById('eyeIcon')!;
|
||||||
@@ -75,6 +76,7 @@ async function init() {
|
|||||||
function setupEventListeners() {
|
function setupEventListeners() {
|
||||||
loginForm.addEventListener('submit', handleLogin);
|
loginForm.addEventListener('submit', handleLogin);
|
||||||
logoutBtn.addEventListener('click', handleLogout);
|
logoutBtn.addEventListener('click', handleLogout);
|
||||||
|
refreshBtn.addEventListener('click', handleRefreshAll);
|
||||||
helpBtn.addEventListener('click', handleHelp);
|
helpBtn.addEventListener('click', handleHelp);
|
||||||
togglePasswordBtn.addEventListener('click', handleTogglePassword);
|
togglePasswordBtn.addEventListener('click', handleTogglePassword);
|
||||||
}
|
}
|
||||||
@@ -336,9 +338,8 @@ function renderPDFItem(pdf: PDFQueueEntry, section: 'pending' | 'basket' | 'prod
|
|||||||
`;
|
`;
|
||||||
break;
|
break;
|
||||||
case 'production':
|
case 'production':
|
||||||
actionsHtml = `
|
// No individual actions - use global refresh button in header
|
||||||
<button class="btn-refresh-item" data-id="${escapeHtml(pdf.id)}">Refresh</button>
|
actionsHtml = '';
|
||||||
`;
|
|
||||||
break;
|
break;
|
||||||
case 'completed':
|
case 'completed':
|
||||||
actionsHtml = `
|
actionsHtml = `
|
||||||
@@ -386,14 +387,6 @@ function setupPDFListEventListeners() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Refresh buttons
|
|
||||||
pdfList.querySelectorAll('.btn-refresh-item').forEach(btn => {
|
|
||||||
btn.addEventListener('click', (e) => {
|
|
||||||
const id = (e.target as HTMLElement).dataset.id;
|
|
||||||
if (id) handleRefreshStatus(id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Dismiss/Cancel/Remove buttons
|
// Dismiss/Cancel/Remove buttons
|
||||||
pdfList.querySelectorAll('.btn-dismiss').forEach(btn => {
|
pdfList.querySelectorAll('.btn-dismiss').forEach(btn => {
|
||||||
btn.addEventListener('click', (e) => {
|
btn.addEventListener('click', (e) => {
|
||||||
@@ -685,6 +678,53 @@ async function handleOrderPDF(id: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle refresh all documents that have been uploaded to server
|
||||||
|
*/
|
||||||
|
async function handleRefreshAll() {
|
||||||
|
if (!currentCredentials) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all documents that have been uploaded and are still visible
|
||||||
|
const uploadedDocs = pdfQueue.filter(p =>
|
||||||
|
p.binectDocumentId &&
|
||||||
|
(p.status === 'in_basket' || p.status === 'in_production')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (uploadedDocs.length === 0) {
|
||||||
|
showStatus('No documents to refresh', 'success');
|
||||||
|
setTimeout(() => hideStatus(), 2000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable refresh button during refresh
|
||||||
|
refreshBtn.classList.add('refreshing');
|
||||||
|
showStatus(`Refreshing ${uploadedDocs.length} document${uploadedDocs.length > 1 ? 's' : ''}...`, 'uploading');
|
||||||
|
|
||||||
|
let successCount = 0;
|
||||||
|
let errorCount = 0;
|
||||||
|
|
||||||
|
for (const pdf of uploadedDocs) {
|
||||||
|
try {
|
||||||
|
await handleRefreshStatus(pdf.id);
|
||||||
|
successCount++;
|
||||||
|
} catch {
|
||||||
|
errorCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshBtn.classList.remove('refreshing');
|
||||||
|
|
||||||
|
if (errorCount === 0) {
|
||||||
|
showStatus(`Refreshed ${successCount} document${successCount > 1 ? 's' : ''}`, 'success');
|
||||||
|
} else {
|
||||||
|
showStatus(`Refreshed ${successCount}, ${errorCount} failed`, 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => hideStatus(), 3000);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle refresh document status
|
* Handle refresh document status
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user