feat(assigned): window by yesterday+today (driver-local timezone)
Assigned tab was previously unfiltered by date — every assigned row in the system was shown, including items scheduled for the future (e.g. operator pre-assigns for tomorrow, driver sees them today). Live PB probe (2026-08-11) shows `assigned_at` is set to the SCHEDULED pickup date, not the moment of assignment: 15 currently assigned rows split evenly across Aug 18/19/20. New filter: `assigned_at` must fall within a rolling 2-day window in driver-local time (yesterday 00:00 → today 23:59:59.999). This covers today's work plus any carryover from yesterday that didn't get completed. Items scheduled for tomorrow or later stay hidden until their day rolls around. Window boundaries are computed in the driver's local timezone (this is a browser app, so `new Date()` is local) then converted to UTC ISO for the PB filter — works as long as driver + operator share a timezone, which is the case for the current single-region deployment. Empty-state copy updated so drivers understand why nothing might show even when they know there are upcoming pickups. check-pb-schema probe updated to match the new clauses so future regressions fail the build instead of silently showing everything.
This commit is contained in:
parent
22bf5dab81
commit
4efce9290c
3 changed files with 49 additions and 8 deletions
|
|
@ -780,7 +780,7 @@ const loadItems = useCallback(async (showLoading = true) => {
|
|||
{searchQuery
|
||||
? 'No collections match your search.'
|
||||
: statusFilter === 'assigned'
|
||||
? 'No assigned collections right now. New pickups will appear here.'
|
||||
? 'No pickups for today or yesterday. New ones appear here as the operator schedules them.'
|
||||
: statusFilter === 'collection_pool'
|
||||
? 'Collection pool is empty — nothing to claim right now.'
|
||||
: 'No problems reported. Nice work.'}
|
||||
|
|
|
|||
|
|
@ -112,24 +112,61 @@ const hydrateSuppliers = async (items: OrderItem[]): Promise<OrderItem[]> => {
|
|||
// Referencing either in a filter hard-400s the whole query, so any
|
||||
// OR-fallback against them would *break* the Assigned tab rather
|
||||
// than rescue it.
|
||||
// - item_status = "assigned" is the live, working status. Currently
|
||||
// 3 rows; the others have moved on in the workflow (e.g. invoiced).
|
||||
// - item_status = "assigned" is the live, working status.
|
||||
// - `assigned_at` is set by the operator app to the SCHEDULED pickup
|
||||
// date (not the moment of assignment). Items assigned "today for
|
||||
// tomorrow" carry assigned_at = tomorrow's date — the driver app
|
||||
// surfaces them tomorrow, not today. Live probe shows current
|
||||
// assigned rows split evenly across Aug 18/19/20 as expected.
|
||||
//
|
||||
// If the operator app needs items to land here, it must write
|
||||
// `drv_id = user.id` and `item_status = "assigned"` on the row.
|
||||
// Assigned-day window:
|
||||
// The driver app shows items whose `assigned_at` falls within a
|
||||
// rolling 2-day window: yesterday + today (driver-local time). This
|
||||
// covers:
|
||||
// - items scheduled for today (the day's work), and
|
||||
// - items scheduled for yesterday (carryover that wasn't completed
|
||||
// — still actionable today).
|
||||
// Items scheduled for tomorrow or later stay hidden until their day
|
||||
// arrives, so the driver isn't tempted to "get ahead" of the schedule
|
||||
// and pickup a PO whose supplier hasn't been told it'll be picked up.
|
||||
//
|
||||
// Timezone: the window boundaries are computed in the driver's local
|
||||
// timezone (this is a browser app, so `new Date()` is local) and then
|
||||
// converted to UTC ISO strings for the PB filter. This works as long
|
||||
// as the driver and operator share a timezone — if they diverge, the
|
||||
// window rolls over at midnight in the driver's locale, not the
|
||||
// operator's. Acceptable for the current single-region deployment.
|
||||
const getAssignedDayWindow = (): { fromIso: string; toIso: string } => {
|
||||
const now = new Date();
|
||||
// Today at 00:00 in the driver's local timezone.
|
||||
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
// Last millisecond of today (23:59:59.999) — inclusive upper bound.
|
||||
const todayEnd = new Date(todayStart.getTime() + 24 * 60 * 60 * 1000 - 1);
|
||||
// Yesterday at 00:00 — inclusive lower bound.
|
||||
const yesterdayStart = new Date(todayStart.getTime() - 24 * 60 * 60 * 1000);
|
||||
return {
|
||||
fromIso: yesterdayStart.toISOString(),
|
||||
toIso: todayEnd.toISOString(),
|
||||
};
|
||||
};
|
||||
|
||||
export const getMyAssignedItems = async (): Promise<OrderItem[]> => {
|
||||
const user = getCurrentUser();
|
||||
if (!user) return [];
|
||||
const { fromIso, toIso } = getAssignedDayWindow();
|
||||
const filter =
|
||||
`drv_id = "${user.id}" && item_status = "assigned" && ` +
|
||||
`assigned_at >= "${fromIso}" && assigned_at <= "${toIso}"`;
|
||||
try {
|
||||
const items = await pb.collection('order_items').getFullList<OrderItem>({
|
||||
filter: `drv_id = "${user.id}" && item_status = "assigned"`,
|
||||
filter,
|
||||
expand: 'supplier,order,purchase_order',
|
||||
sort: 'supplier.suppliername',
|
||||
});
|
||||
return items;
|
||||
} catch {
|
||||
const items = await pb.collection('order_items').getFullList<OrderItem>({
|
||||
filter: `drv_id = "${user.id}" && item_status = "assigned"`,
|
||||
filter,
|
||||
});
|
||||
return hydrateSuppliers(items);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,11 @@ const PASSWORD = process.env.PB_USER_PASSWORD || '1234567890';
|
|||
// PB validates identifier names at the parser level and 400s the moment
|
||||
// it hits one it doesn't know, regardless of the value.
|
||||
const FILTERS = [
|
||||
{ collection: 'order_items', filter: 'drv_id = "placeholder" && item_status = "assigned"' },
|
||||
// Assigned-tab filter with the yesterday+today window. The ISO
|
||||
// timestamps here are placeholders — PB only validates field names
|
||||
// and operators at parse time, so any well-formed ISO string works.
|
||||
// Keep in sync with getAssignedDayWindow in lib/pocketbase.ts.
|
||||
{ collection: 'order_items', filter: 'drv_id = "placeholder" && item_status = "assigned" && assigned_at >= "2026-01-01T00:00:00.000Z" && assigned_at <= "2026-12-31T23:59:59.999Z"' },
|
||||
{ collection: 'order_items', filter: 'item_status = "collection_pool"' },
|
||||
{ collection: 'order_items', filter: 'problem_flag = true || item_status = "problem"' },
|
||||
// Stock pickups live in their own collection — see
|
||||
|
|
|
|||
Loading…
Reference in a new issue