Improve consistency for server-discovered documents

- Separate "Has Errors" section for erroneous server documents (already
  uploaded but have validation errors)
- "Ready to Upload" section now only shows truly local documents
- Erroneous server docs show only "Delete from server" button (no retry)
- Improved metadata display: show document ID for server docs, hide
  unknown file size
- Clean up verbose debug logging

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-16 22:49:07 +01:00
parent 327943bc18
commit 24daa4bf82
2 changed files with 42 additions and 32 deletions

View File

@@ -316,13 +316,10 @@ async function loadPDFQueue() {
*/ */
async function syncWithServer() { async function syncWithServer() {
if (!currentCredentials) { if (!currentCredentials) {
console.log('[Popup] No credentials, skipping server sync');
return; return;
} }
try { try {
console.log('[Popup] Syncing with Binect server...');
// Get list of documents from server // Get list of documents from server
const result = await chrome.runtime.sendMessage({ const result = await chrome.runtime.sendMessage({
action: 'listServerDocuments', action: 'listServerDocuments',
@@ -330,8 +327,6 @@ async function syncWithServer() {
password: currentCredentials.password password: currentCredentials.password
}); });
console.log('[Popup] listServerDocuments result:', result);
if (!result.success || !result.documents) { if (!result.success || !result.documents) {
console.warn('[Popup] Failed to list server documents:', result.error); console.warn('[Popup] Failed to list server documents:', result.error);
return; return;
@@ -347,15 +342,11 @@ async function syncWithServer() {
errorDetails?: string; errorDetails?: string;
}>; }>;
console.log('[Popup] Found', serverDocs.length, 'documents on server'); console.log('[Popup] Syncing', serverDocs.length, 'server documents');
for (const doc of serverDocs) {
console.log('[Popup] Server doc:', doc.id, doc.filename, 'status:', doc.status, doc.statusText);
}
// Sync each server document to local proxy // Sync each server document to local proxy
for (const doc of serverDocs) { for (const doc of serverDocs) {
console.log('[Popup] Syncing doc', doc.id, 'to local proxy...'); await chrome.runtime.sendMessage({
const syncResult = await chrome.runtime.sendMessage({
action: 'syncFromServer', action: 'syncFromServer',
binectDocumentId: doc.id, binectDocumentId: doc.id,
filename: doc.filename, filename: doc.filename,
@@ -365,10 +356,7 @@ async function syncWithServer() {
recipientAddress: doc.recipientAddress, recipientAddress: doc.recipientAddress,
errorDetails: doc.errorDetails errorDetails: doc.errorDetails
}); });
console.log('[Popup] Sync result for doc', doc.id, ':', syncResult);
} }
console.log('[Popup] Server sync complete');
} catch (error) { } catch (error) {
console.error('[Popup] Server sync error:', error); console.error('[Popup] Server sync error:', error);
} }
@@ -444,13 +432,20 @@ function renderPDFList() {
</div>`; </div>`;
} else { } else {
// Live view - group by Binect status // Live view - group by Binect status
const pendingUpload = pdfQueue.filter(p => p.binectStatus === 'pending' || p.binectStatus === 'uploading' || p.binectStatus === 'failed'); // Separate truly local pending uploads from server documents with errors
const pendingUpload = pdfQueue.filter(p =>
(p.binectStatus === 'pending' || p.binectStatus === 'uploading') ||
(p.binectStatus === 'failed' && !p.binectDocumentId) // Local failed uploads
);
const erroneous = pdfQueue.filter(p =>
p.binectStatus === 'failed' && p.binectDocumentId // Server documents with errors
);
const inBasket = pdfQueue.filter(p => p.binectStatus === 'in_basket' || p.binectStatus === 'ordering'); const inBasket = pdfQueue.filter(p => p.binectStatus === 'in_basket' || p.binectStatus === 'ordering');
const inProduction = pdfQueue.filter(p => p.binectStatus === 'in_production'); const inProduction = pdfQueue.filter(p => p.binectStatus === 'in_production');
const completed = pdfQueue.filter(p => p.binectStatus === 'sent' || p.binectStatus === 'canceled'); const completed = pdfQueue.filter(p => p.binectStatus === 'sent' || p.binectStatus === 'canceled');
// Count actionable items // Count actionable items
const actionableCount = pendingUpload.length + inBasket.length; const actionableCount = pendingUpload.length + erroneous.length + inBasket.length;
// Update count // Update count
if (actionableCount > 0) { if (actionableCount > 0) {
@@ -470,6 +465,13 @@ function renderPDFList() {
</div>`; </div>`;
} }
if (erroneous.length > 0) {
html += `<div class="pdf-section">
<div class="pdf-section-header">Has Errors</div>
${erroneous.map(pdf => renderPDFItem(pdf, 'erroneous')).join('')}
</div>`;
}
if (inBasket.length > 0) { if (inBasket.length > 0) {
html += `<div class="pdf-section"> html += `<div class="pdf-section">
<div class="pdf-section-header">In Basket</div> <div class="pdf-section-header">In Basket</div>
@@ -504,7 +506,7 @@ function renderPDFList() {
/** /**
* Render a single PDF item * Render a single PDF item
*/ */
function renderPDFItem(pdf: DocumentProxy, section: 'pending' | 'basket' | 'production' | 'completed' | 'archived'): string { function renderPDFItem(pdf: DocumentProxy, section: 'pending' | 'erroneous' | 'basket' | 'production' | 'completed' | 'archived'): string {
const statusClass = getStatusClass(pdf.binectStatus); const statusClass = getStatusClass(pdf.binectStatus);
const statusText = getStatusText(pdf); const statusText = getStatusText(pdf);
const priceText = pdf.price ? `${(pdf.price / 100).toFixed(2)}` : ''; const priceText = pdf.price ? `${(pdf.price / 100).toFixed(2)}` : '';
@@ -522,10 +524,13 @@ function renderPDFItem(pdf: DocumentProxy, section: 'pending' | 'basket' | 'prod
<button class="btn-send-item" data-id="${escapeHtml(pdf.id)}" ${pdf.binectStatus === 'uploading' ? 'disabled' : ''}> <button class="btn-send-item" data-id="${escapeHtml(pdf.id)}" ${pdf.binectStatus === 'uploading' ? 'disabled' : ''}>
${pdf.binectStatus === 'uploading' ? 'Uploading...' : (pdf.binectStatus === 'failed' ? 'Retry' : 'Upload')} ${pdf.binectStatus === 'uploading' ? 'Uploading...' : (pdf.binectStatus === 'failed' ? 'Retry' : 'Upload')}
</button> </button>
${canDeleteFromServer <button class="btn-archive" data-id="${escapeHtml(pdf.id)}">Archive</button>
? `<button class="btn-delete-server" data-id="${escapeHtml(pdf.id)}">Delete from server</button>` `;
: `<button class="btn-archive" data-id="${escapeHtml(pdf.id)}">Archive</button>` break;
} case 'erroneous':
// Server documents with errors - can only delete from server
actionsHtml = `
<button class="btn-delete-server" data-id="${escapeHtml(pdf.id)}">Delete from server</button>
`; `;
break; break;
case 'basket': case 'basket':
@@ -563,6 +568,20 @@ function renderPDFItem(pdf: DocumentProxy, section: 'pending' | 'basket' | 'prod
// Show detailed error info for erroneous documents // Show detailed error info for erroneous documents
const hasErrorDetails = pdf.binectStatus === 'failed' && pdf.errorMessage && pdf.binectStatusCode === 7; const hasErrorDetails = pdf.binectStatus === 'failed' && pdf.errorMessage && pdf.binectStatusCode === 7;
// Build metadata parts - only include what we have
const metaParts: string[] = [];
if (pdf.size > 0) {
metaParts.push(formatFileSize(pdf.size));
}
if (pdf.sourceDomain && pdf.sourceDomain !== 'binect.de') {
metaParts.push(escapeHtml(pdf.sourceDomain));
} else if (pdf.binectDocumentId && pdf.sourceDomain === 'binect.de') {
metaParts.push(`ID: ${pdf.binectDocumentId}`);
}
if (priceText) {
metaParts.push(`<span class="pdf-price">${priceText}</span>`);
}
return ` return `
<div class="pdf-list-item ${statusClass}" data-id="${escapeHtml(pdf.id)}"> <div class="pdf-list-item ${statusClass}" data-id="${escapeHtml(pdf.id)}">
<div class="pdf-item-icon">${getStatusIcon(pdf.binectStatus)}</div> <div class="pdf-item-icon">${getStatusIcon(pdf.binectStatus)}</div>
@@ -571,10 +590,7 @@ function renderPDFItem(pdf: DocumentProxy, section: 'pending' | 'basket' | 'prod
${escapeHtml(displayFilename)} ${escapeHtml(displayFilename)}
${isLocalOnly ? '<span class="tag-local">local</span>' : ''} ${isLocalOnly ? '<span class="tag-local">local</span>' : ''}
</div> </div>
<div class="pdf-item-meta"> ${metaParts.length > 0 ? `<div class="pdf-item-meta">${metaParts.join(' · ')}</div>` : ''}
${formatFileSize(pdf.size)} · ${escapeHtml(pdf.sourceDomain)}
${priceText ? ` · <span class="pdf-price">${priceText}</span>` : ''}
</div>
${pdf.recipientAddress ? `<div class="pdf-item-recipient">${escapeHtml(pdf.recipientAddress.split('\n')[0])}</div>` : ''} ${pdf.recipientAddress ? `<div class="pdf-item-recipient">${escapeHtml(pdf.recipientAddress.split('\n')[0])}</div>` : ''}
<div class="pdf-item-status ${statusClass}">${statusText}</div> <div class="pdf-item-status ${statusClass}">${statusText}</div>
${hasErrorDetails ? `<div class="pdf-item-error">${escapeHtml(pdf.errorMessage!)}</div>` : ''} ${hasErrorDetails ? `<div class="pdf-item-error">${escapeHtml(pdf.errorMessage!)}</div>` : ''}

View File

@@ -500,23 +500,17 @@ export async function listServerDocuments(
}); });
// Get shippable documents (status 2) // Get shippable documents (status 2)
console.log('[Binect API] Fetching shippable documents...');
const shippableResponse = await client.documents.list(); const shippableResponse = await client.documents.list();
console.log('[Binect API] Shippable response:', JSON.stringify(shippableResponse));
const shippable = shippableResponse.items || []; const shippable = shippableResponse.items || [];
console.log('[Binect API] Found', shippable.length, 'shippable documents');
// Get erroneous documents (status 7) // Get erroneous documents (status 7)
console.log('[Binect API] Fetching erroneous documents...');
const errorsResponse = await client.documents.listErrors(); const errorsResponse = await client.documents.listErrors();
console.log('[Binect API] Errors response:', JSON.stringify(errorsResponse));
const erroneous = errorsResponse.items || []; const erroneous = errorsResponse.items || [];
console.log('[Binect API] Found', erroneous.length, 'erroneous documents');
// Combine and map to our format // Combine and map to our format
const allDocs = [...shippable, ...erroneous]; const allDocs = [...shippable, ...erroneous];
console.log('[Binect API] Total documents on server:', allDocs.length); console.log('[Binect API] Found', allDocs.length, 'documents on server (', shippable.length, 'shippable,', erroneous.length, 'erroneous)');
return allDocs.map(doc => { return allDocs.map(doc => {
let errorDetails: string | undefined; let errorDetails: string | undefined;