fix(reporting): never strand a collected item, and log real PB errors

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.
This commit is contained in:
Dave 2026-10-14 16:12:41 +02:00
parent efba1bbf2f
commit bb5a585af6

View file

@ -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);
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'
: '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<string, { message?: string }> } })
?.response?.data;
console.error(
'Failed to report problem:',
item.id,
problemType,
detail ? JSON.stringify(detail) : err,
);
showToast('Failed to report problem', 'error');
}
};