From bb5a585af67aa4f59f0d0f1518eefee4ef7ddfbe Mon Sep 17 00:00:00 2001 From: Dave Date: Wed, 14 Oct 2026 16:12:41 +0200 Subject: [PATCH] fix(reporting): never strand a collected item, and log real PB errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two failure paths were swallowing their errors. 1. handleCollect returned early when the photo upload failed. Status was already flipped to `collected` at that point, but the early return skipped `setItems`, leaving a successfully-collected item on screen still looking un-collected. The driver re-collected it and re-attempted the same oversized upload — one record picked up 29 failed attempts in a single morning. The item now clears off the list either way, and a photo failure is a warning toast telling the driver to add it from the office, not a silent stall. 2. handleProblem logged nothing. PocketBase puts the real reason in `response.data` keyed by field name; the top-level message is always the useless "Something went wrong while processing your request." That is exactly what hid the problem_type mismatch fixed separately in PB today: the select was missing out_of_stock/partial_stock/quality_issue/wrong_item, so four of the five Report Problem buttons 400'd with no visible reason. --- app/page.tsx | 66 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 71005da..24c38d8 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -284,6 +284,15 @@ const loadItems = useCallback(async (showLoading = true) => { // visual record. We do this AFTER the status flip so a // failure here doesn't strand the item in `assigned` — // the collection is the more important state change. + // + // The item is cleared off the list either way. An earlier + // version returned early on upload failure, which left a + // successfully-collected item sitting on screen still looking + // un-collected; the driver re-collected it, re-attempted the + // same oversized upload, and one record picked up 29 failed + // attempts in a single morning on 2026-10-12. + let photoFailed = false; + let photoFailure = ''; if (photo) { try { await uploadOrderItemAttachment({ @@ -293,29 +302,37 @@ const loadItems = useCallback(async (showLoading = true) => { notes: notes || undefined, }); } catch (uploadErr) { - // Status already changed. Surface the failure so the - // driver knows the photo did NOT persist (a success - // toast here would be a lie). Known PB bug: image/* mime - // detection rejects camera JPEGs — until that server - // side detection is fixed, photos will keep failing. + // The collection itself succeeded, so this is a warning, + // not a failure — but the driver must still know the photo + // did NOT persist. A success toast here would be a lie. + // + // (The "Known PB bug: image/* mime detection rejects camera + // JPEGs" note that used to live here was wrong: PB's own + // logs show every attempt failing as `400 unexpected EOF` + // — the multipart body truncated in transit, not a format + // rejection. `order_item_attachements` has mimeTypes=null, + // so nothing was checking the type at all. Fixed by + // compressing the photo client-side in lib/image.ts.) + photoFailed = true; + photoFailure = uploadErr instanceof Error ? uploadErr.message : 'unknown error'; console.error('Photo upload failed:', uploadErr); - const reason = - uploadErr instanceof Error ? uploadErr.message : 'unknown error'; - showToast( - `Item collected — but photo upload failed (${reason})`, - 'warning' - ); - return; // skip the success toast that follows } } setItems((prev) => prev.filter((i) => i.id !== item.id)); setShowCollectModal(false); - showToast( - photo - ? 'Collection completed · photo uploaded' - : 'Collection completed successfully' - ); + if (photoFailed) { + showToast( + `Collected, but the photo didn't upload (${photoFailure}) — add it from the office.`, + 'warning', + ); + } else { + showToast( + photo + ? 'Collection completed · photo uploaded' + : 'Collection completed successfully', + ); + } } catch (err) { showToast('Failed to update collection', 'error'); } @@ -336,6 +353,21 @@ const loadItems = useCallback(async (showLoading = true) => { // rather than just disappearing. showToast('Problem reported — item sent back to the pool', 'warning'); } catch (err) { + // PB puts the actual reason in `response.data` keyed by field + // name — the top-level `message` is always the useless + // "Something went wrong while processing your request." A 400 here + // is almost always a select-field value the schema doesn't allow + // (this cost us every problem report on 2026-10-14: problem_type + // was missing out_of_stock/partial_stock/quality_issue/wrong_item). + // Logging only the message hid that for months. + const detail = (err as { response?: { data?: Record } }) + ?.response?.data; + console.error( + 'Failed to report problem:', + item.id, + problemType, + detail ? JSON.stringify(detail) : err, + ); showToast('Failed to report problem', 'error'); } };