generated from coulomb/repo-seed
UI refinements: Send button, server deletion detection, remove manual refresh
- Rename "Order" button to "Send" throughout UI - Detect server-side document deletions (404) and auto-archive - Remove manual refresh button (auto-refresh handles this) - Fix password toggle button with preventDefault/stopPropagation - Make auto-refresh silent (no status messages) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -256,9 +256,12 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|||||||
sendResponse({ success: true, ...result });
|
sendResponse({ success: true, ...result });
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
// Include error code for 404 detection
|
||||||
|
const errorCode = (error as { statusCode?: number }).statusCode;
|
||||||
sendResponse({
|
sendResponse({
|
||||||
success: false,
|
success: false,
|
||||||
error: error instanceof Error ? error.message : 'Failed to get status'
|
error: error instanceof Error ? error.message : 'Failed to get status',
|
||||||
|
errorCode
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -22,13 +22,6 @@
|
|||||||
<polyline points="14 2 14 8 20 8"></polyline>
|
<polyline points="14 2 14 8 20 8"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<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>
|
<button id="helpBtn" class="icon-btn" aria-label="Help and tracking info" title="Help & Info">?</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ const logoutBtn = document.getElementById('logoutBtn')!;
|
|||||||
const archiveToggleBtn = document.getElementById('archiveToggleBtn')!;
|
const archiveToggleBtn = document.getElementById('archiveToggleBtn')!;
|
||||||
const archiveIcon = document.getElementById('archiveIcon')!;
|
const archiveIcon = document.getElementById('archiveIcon')!;
|
||||||
const liveIcon = document.getElementById('liveIcon')!;
|
const liveIcon = document.getElementById('liveIcon')!;
|
||||||
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')!;
|
||||||
@@ -87,9 +86,16 @@ function setupEventListeners() {
|
|||||||
loginForm.addEventListener('submit', handleLogin);
|
loginForm.addEventListener('submit', handleLogin);
|
||||||
logoutBtn.addEventListener('click', handleLogout);
|
logoutBtn.addEventListener('click', handleLogout);
|
||||||
archiveToggleBtn.addEventListener('click', handleToggleArchiveView);
|
archiveToggleBtn.addEventListener('click', handleToggleArchiveView);
|
||||||
refreshBtn.addEventListener('click', handleRefreshAll);
|
|
||||||
helpBtn.addEventListener('click', handleHelp);
|
helpBtn.addEventListener('click', handleHelp);
|
||||||
togglePasswordBtn.addEventListener('click', handleTogglePassword);
|
|
||||||
|
// Password toggle - ensure button exists and attach handler
|
||||||
|
if (togglePasswordBtn) {
|
||||||
|
togglePasswordBtn.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
handleTogglePassword();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -434,7 +440,7 @@ function renderPDFItem(pdf: DocumentProxy, section: 'pending' | 'basket' | 'prod
|
|||||||
case 'basket':
|
case 'basket':
|
||||||
actionsHtml = `
|
actionsHtml = `
|
||||||
<button class="btn-order-item" data-id="${escapeHtml(pdf.id)}" ${pdf.binectStatus === 'ordering' ? 'disabled' : ''}>
|
<button class="btn-order-item" data-id="${escapeHtml(pdf.id)}" ${pdf.binectStatus === 'ordering' ? 'disabled' : ''}>
|
||||||
${pdf.binectStatus === 'ordering' ? 'Ordering...' : 'Order'}
|
${pdf.binectStatus === 'ordering' ? 'Sending...' : 'Send'}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn-archive" data-id="${escapeHtml(pdf.id)}">Archive</button>
|
<button class="btn-archive" data-id="${escapeHtml(pdf.id)}">Archive</button>
|
||||||
`;
|
`;
|
||||||
@@ -539,7 +545,7 @@ function getStatusText(pdf: DocumentProxy): string {
|
|||||||
case 'in_basket':
|
case 'in_basket':
|
||||||
return pdf.binectStatusText || 'Ready to order';
|
return pdf.binectStatusText || 'Ready to order';
|
||||||
case 'ordering':
|
case 'ordering':
|
||||||
return 'Ordering...';
|
return 'Sending...';
|
||||||
case 'in_production':
|
case 'in_production':
|
||||||
return pdf.binectStatusText || 'In production';
|
return pdf.binectStatusText || 'In production';
|
||||||
case 'sent':
|
case 'sent':
|
||||||
@@ -788,7 +794,7 @@ async function handleOrderPDF(id: string) {
|
|||||||
pdf.binectStatusText = result.statusText;
|
pdf.binectStatusText = result.statusText;
|
||||||
renderPDFList();
|
renderPDFList();
|
||||||
|
|
||||||
showStatus('Order placed! Document is in production.', 'success');
|
showStatus('Sent! Document is in production.', 'success');
|
||||||
|
|
||||||
// Start auto-refresh sequence
|
// Start auto-refresh sequence
|
||||||
startAutoRefresh();
|
startAutoRefresh();
|
||||||
@@ -799,7 +805,7 @@ async function handleOrderPDF(id: string) {
|
|||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : 'Order failed';
|
const errorMessage = error instanceof Error ? error.message : 'Send failed';
|
||||||
|
|
||||||
// Revert to in_basket status
|
// Revert to in_basket status
|
||||||
await chrome.runtime.sendMessage({
|
await chrome.runtime.sendMessage({
|
||||||
@@ -833,14 +839,10 @@ async function handleRefreshAll() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (uploadedDocs.length === 0) {
|
if (uploadedDocs.length === 0) {
|
||||||
showStatus('No documents to refresh', 'success');
|
|
||||||
setTimeout(() => hideStatus(), 2000);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable refresh button during refresh
|
console.log(`[Popup] Refreshing ${uploadedDocs.length} document(s)...`);
|
||||||
refreshBtn.classList.add('refreshing');
|
|
||||||
showStatus(`Refreshing ${uploadedDocs.length} document${uploadedDocs.length > 1 ? 's' : ''}...`, 'uploading');
|
|
||||||
|
|
||||||
let successCount = 0;
|
let successCount = 0;
|
||||||
let errorCount = 0;
|
let errorCount = 0;
|
||||||
@@ -854,15 +856,9 @@ async function handleRefreshAll() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshBtn.classList.remove('refreshing');
|
if (errorCount > 0) {
|
||||||
|
console.log(`[Popup] Refresh complete: ${successCount} ok, ${errorCount} failed`);
|
||||||
if (errorCount === 0) {
|
|
||||||
showStatus(`Refreshed ${successCount} document${successCount > 1 ? 's' : ''}`, 'success');
|
|
||||||
} else {
|
|
||||||
showStatus(`Refreshed ${successCount}, ${errorCount} failed`, 'error');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => hideStatus(), 3000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -884,6 +880,13 @@ async function handleRefreshStatus(id: string) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
|
// Check if document was deleted on server (404)
|
||||||
|
if (result.errorCode === 404) {
|
||||||
|
console.log('[Popup] Document deleted on server, archiving:', id);
|
||||||
|
await handleArchivePDF(id);
|
||||||
|
showStatus('Document no longer exists on server', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
throw new Error(result.error || 'Failed to get status');
|
throw new Error(result.error || 'Failed to get status');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -922,15 +925,9 @@ async function handleRefreshStatus(id: string) {
|
|||||||
if (result.recipientAddress) pdf.recipientAddress = result.recipientAddress;
|
if (result.recipientAddress) pdf.recipientAddress = result.recipientAddress;
|
||||||
renderPDFList();
|
renderPDFList();
|
||||||
|
|
||||||
showStatus(`Status: ${result.statusText}`, 'success');
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
hideStatus();
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : 'Failed to refresh';
|
console.error('[Popup] Refresh error:', error);
|
||||||
showStatus(errorMessage, 'error');
|
// Only show errors that aren't already handled (like 404)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -445,6 +445,10 @@ export async function getDocumentStatus(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (error instanceof BinectApiError) {
|
if (error instanceof BinectApiError) {
|
||||||
|
// Check for 404 - document not found (deleted on server)
|
||||||
|
if (error.status === 404) {
|
||||||
|
throw new BinectAPIError('Document not found on server', 404);
|
||||||
|
}
|
||||||
throw BinectAPIError.fromBinectError(error);
|
throw BinectAPIError.fromBinectError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user