2026-05-24 19:51:29 +00:00
|
|
|
'use client';
|
|
|
|
|
|
2026-09-08 20:15:31 +00:00
|
|
|
import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
2026-05-24 19:51:29 +00:00
|
|
|
import { useRouter } from 'next/navigation';
|
2026-05-24 22:47:47 +00:00
|
|
|
import { OrderItem } from '@/lib/types';
|
2026-05-24 19:51:29 +00:00
|
|
|
import { useAuth } from '@/components/AuthProvider';
|
|
|
|
|
import BottomNav from '@/components/BottomNav';
|
|
|
|
|
import CollectionItemCard from '@/components/CollectionItemCard';
|
2026-08-12 06:04:26 +00:00
|
|
|
import SupplierGroup, { POSubGroup } from '@/components/SupplierGroup';
|
2026-07-07 15:15:42 +00:00
|
|
|
import AreaGroup from '@/components/AreaGroup';
|
2026-05-24 19:51:29 +00:00
|
|
|
import CollectModal from '@/components/CollectModal';
|
|
|
|
|
import ProblemModal from '@/components/ProblemModal';
|
2026-08-02 19:38:38 +00:00
|
|
|
import ItemDetailModal from '@/components/ItemDetailModal';
|
2026-10-21 18:58:33 +00:00
|
|
|
import InvoiceMatchHost from '@/components/InvoiceMatchHost';
|
2026-10-26 08:54:09 +00:00
|
|
|
import CollectPrompt from '@/components/CollectPrompt';
|
|
|
|
|
import { invoiceCheckBase } from '@/lib/invoiceCheck';
|
2026-05-24 19:51:29 +00:00
|
|
|
import {
|
2026-07-07 15:15:42 +00:00
|
|
|
getMyAssignedItems,
|
2026-08-17 20:33:10 +00:00
|
|
|
getMyAssignedStockItems,
|
2026-07-07 15:15:42 +00:00
|
|
|
getPoolItems,
|
|
|
|
|
getProblemItems,
|
2026-05-24 19:51:29 +00:00
|
|
|
updateCollectionStatus,
|
2026-08-17 20:33:10 +00:00
|
|
|
updateStockCollectionStatus,
|
2026-07-07 23:35:26 +00:00
|
|
|
uploadOrderItemAttachment,
|
2026-05-24 19:51:29 +00:00
|
|
|
reportProblem,
|
2026-08-17 20:33:10 +00:00
|
|
|
reportStockProblem,
|
2026-05-24 19:51:29 +00:00
|
|
|
isAuthenticated,
|
|
|
|
|
logout,
|
2026-07-07 18:43:17 +00:00
|
|
|
getItemDisplayQty,
|
2026-08-12 06:04:26 +00:00
|
|
|
bulkCollect,
|
|
|
|
|
bulkProblem,
|
2026-08-17 20:33:10 +00:00
|
|
|
bulkCollectStock,
|
|
|
|
|
bulkProblemStock,
|
2026-05-24 19:51:29 +00:00
|
|
|
} from '@/lib/pocketbase';
|
2026-07-07 18:43:17 +00:00
|
|
|
import { getCached, setCached } from '@/lib/itemsCache';
|
2026-10-19 15:02:06 +00:00
|
|
|
import {
|
|
|
|
|
countInvoicePages,
|
|
|
|
|
invoicePhotoTarget,
|
|
|
|
|
uploadInvoicePage,
|
|
|
|
|
uploadStockPickupPhoto,
|
|
|
|
|
type PhotoTarget,
|
|
|
|
|
} from '@/lib/poDocs';
|
|
|
|
|
|
|
|
|
|
/** Items with the target's new file list applied (a line, or every pickup on a stock order). */
|
|
|
|
|
function withTargetFiles(items: OrderItem[], target: PhotoTarget, files: string[]): OrderItem[] {
|
|
|
|
|
return items.map((i) => {
|
|
|
|
|
if (target.collection === 'order_items') {
|
|
|
|
|
return i.id === target.id ? { ...i, order_item_attachements: files } : i;
|
|
|
|
|
}
|
|
|
|
|
const po = i.expand?.purchase_order;
|
|
|
|
|
return i.kind === 'pickup' && po?.id === target.id
|
|
|
|
|
? { ...i, expand: { ...i.expand, purchase_order: { ...po, additional_attachments: files } } }
|
|
|
|
|
: i;
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-07-07 15:15:42 +00:00
|
|
|
import { cn, clusterByProximity } from '@/lib/utils';
|
2026-05-24 19:51:29 +00:00
|
|
|
import {
|
|
|
|
|
RefreshCw,
|
|
|
|
|
LogOut,
|
|
|
|
|
MapPin,
|
|
|
|
|
AlertCircle,
|
|
|
|
|
CheckCircle2,
|
|
|
|
|
Search,
|
|
|
|
|
X,
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
Package,
|
2026-08-12 06:04:26 +00:00
|
|
|
CheckCheck,
|
|
|
|
|
AlertTriangle,
|
2026-05-24 19:51:29 +00:00
|
|
|
} from 'lucide-react';
|
|
|
|
|
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
// Cursor-harmonized supplier color palette: same warm cream surface
|
|
|
|
|
// for every group, with the *name* color living in a single muted
|
|
|
|
|
// tile (not a saturated left-border stripe). Each group gets a
|
|
|
|
|
// distinct hue but all read as part of the same family.
|
2026-05-26 22:55:21 +00:00
|
|
|
const SUPPLIER_COLORS = [
|
2026-08-02 19:38:38 +00:00
|
|
|
{ dot: 'bg-[#f54e00]', border: 'border-l-[#f54e00]', light: 'bg-[#ebeae5]', text: 'text-[#26251e]', ring: 'ring-[#f54e00]/30' }, // orange (signature)
|
|
|
|
|
{ dot: 'bg-[#18714f]', border: 'border-l-[#18714f]', light: 'bg-[#e6f1ec]', text: 'text-[#125a3e]', ring: 'ring-[#18714f]/30' }, // emerald
|
|
|
|
|
{ dot: 'bg-[#6c5a99]', border: 'border-l-[#6c5a99]', light: 'bg-[#ece8f3]', text: 'text-[#4a3d6b]', ring: 'ring-[#6c5a99]/30' }, // violet
|
|
|
|
|
{ dot: 'bg-[#cf2d56]', border: 'border-l-[#cf2d56]', light: 'bg-[#fbeaef]', text: 'text-[#8a1b3a]', ring: 'ring-[#cf2d56]/30' }, // rose
|
|
|
|
|
{ dot: 'bg-[#8a5a02]', border: 'border-l-[#8a5a02]', light: 'bg-[#fcf3d8]', text: 'text-[#8a5a02]', ring: 'ring-[#d49103]/30' }, // amber
|
|
|
|
|
{ dot: 'bg-[#26251e]', border: 'border-l-[#26251e]', light: 'bg-[#ebeae5]', text: 'text-[#26251e]', ring: 'ring-[#26251e]/30' }, // ink
|
|
|
|
|
{ dot: 'bg-[#6b6760]', border: 'border-l-[#6b6760]', light: 'bg-[#ebeae5]', text: 'text-[#4a4742]', ring: 'ring-[#6b6760]/30' }, // warm gray
|
|
|
|
|
{ dot: 'bg-[#0d9488]', border: 'border-l-[#0d9488]', light: 'bg-[#e6f5f3]', text: 'text-[#0a7a70]', ring: 'ring-[#0d9488]/30' }, // teal
|
|
|
|
|
{ dot: 'bg-[#8a1b3a]', border: 'border-l-[#8a1b3a]', light: 'bg-[#fbeaef]', text: 'text-[#8a1b3a]', ring: 'ring-[#cf2d56]/30' }, // crimson
|
|
|
|
|
{ dot: 'bg-[#4a3d6b]', border: 'border-l-[#4a3d6b]', light: 'bg-[#ece8f3]', text: 'text-[#4a3d6b]', ring: 'ring-[#6c5a99]/30' }, // indigo
|
2026-05-26 22:55:21 +00:00
|
|
|
];
|
|
|
|
|
|
2026-05-24 19:51:29 +00:00
|
|
|
export default function CollectionsPage() {
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
const { isLoggedIn, user, isLoading: authLoading } = useAuth();
|
2026-07-07 18:43:17 +00:00
|
|
|
// Hydrate initial state from the in-memory cache so tab switching
|
|
|
|
|
// shows last-known data instantly (no loading skeleton) while the
|
|
|
|
|
// background re-fetch lands. Without this, every bottom-nav click
|
|
|
|
|
// shows a blank-then-skeleton-then-content flash, which is the
|
|
|
|
|
// "feels sluggish on PC" symptom drivers reported.
|
|
|
|
|
const [items, setItems] = useState<OrderItem[]>(() => {
|
|
|
|
|
if (typeof window === 'undefined') return [];
|
|
|
|
|
return getCached('assigned') ?? [];
|
|
|
|
|
});
|
|
|
|
|
const [filteredItems, setFilteredItems] = useState<OrderItem[]>(items);
|
|
|
|
|
// Only show the loading skeleton on first mount when we have
|
|
|
|
|
// absolutely nothing to show. Subsequent loads (returning to
|
|
|
|
|
// the tab, switching tabs) get the cached items immediately
|
|
|
|
|
// and the re-fetch happens silently in the background.
|
|
|
|
|
const [isLoading, setIsLoading] = useState<boolean>(items.length === 0);
|
2026-05-24 19:51:29 +00:00
|
|
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
|
|
|
|
const [error, setError] = useState('');
|
|
|
|
|
const [searchQuery, setSearchQuery] = useState('');
|
2026-05-24 22:47:47 +00:00
|
|
|
const [selectedItem, setSelectedItem] = useState<OrderItem | null>(null);
|
2026-05-24 19:51:29 +00:00
|
|
|
const [showCollectModal, setShowCollectModal] = useState(false);
|
2026-08-02 19:38:38 +00:00
|
|
|
const [showDetailModal, setShowDetailModal] = useState(false);
|
2026-05-24 19:51:29 +00:00
|
|
|
const [showProblemModal, setShowProblemModal] = useState(false);
|
|
|
|
|
const [toast, setToast] = useState<{ message: string; type: 'success' | 'error' | 'warning' } | null>(null);
|
2026-10-26 08:54:09 +00:00
|
|
|
// "Collect these items?" after an invoice photo. With the AI match on it
|
|
|
|
|
// waits in pendingPrompt until the match sheet closes, so it only lists
|
|
|
|
|
// what the match left behind.
|
|
|
|
|
type PromptState = { poLabel: string; ids: string[] };
|
|
|
|
|
const [collectPrompt, setCollectPrompt] = useState<PromptState | null>(null);
|
|
|
|
|
const pendingPrompt = useRef<PromptState | null>(null);
|
2026-10-18 08:31:41 +00:00
|
|
|
// One layout for every driver: compact rows, grouped supplier → PO.
|
|
|
|
|
// The supplier/invoice/area and compact/detailed switches were removed
|
|
|
|
|
// on 2026-10-18 — six layouts behind small icons just confused drivers.
|
|
|
|
|
const [viewMode] = useState<'compact' | 'detailed'>('compact');
|
|
|
|
|
const [groupMode] = useState<'supplier' | 'po' | 'area'>('po');
|
2026-07-07 15:15:42 +00:00
|
|
|
const [statusFilter, setStatusFilter] = useState<'assigned' | 'collection_pool' | 'problem'>('assigned');
|
2026-05-26 22:55:21 +00:00
|
|
|
|
|
|
|
|
// Track which supplier groups are expanded
|
|
|
|
|
const [expandedSuppliers, setExpandedSuppliers] = useState<Set<string>>(new Set());
|
2026-07-07 15:15:42 +00:00
|
|
|
const [expandedAreas, setExpandedAreas] = useState<Set<string>>(new Set());
|
2026-10-18 08:31:41 +00:00
|
|
|
// POs the driver has folded away. POs start open, so opening a
|
|
|
|
|
// supplier shows its items straight away instead of one more tap per PO.
|
|
|
|
|
const [collapsedPOs, setCollapsedPOs] = useState<Set<string>>(new Set());
|
2026-05-24 19:51:29 +00:00
|
|
|
|
2026-09-08 20:15:31 +00:00
|
|
|
// Mirror items.length into a ref so loadItems can decide whether to
|
|
|
|
|
// flash the loading skeleton WITHOUT making items.length a callback
|
|
|
|
|
// dependency. If items.length WERE a dep, every successful collect
|
|
|
|
|
// (which filters the collected item out of state) would rebuild
|
|
|
|
|
// loadItems, the auto-fire useEffect below would re-run loadItems,
|
|
|
|
|
// and the setExpandedSuppliers(new Set()) call inside it would
|
|
|
|
|
// collapse the supplier the driver is currently working through —
|
|
|
|
|
// forcing them to re-expand for every single item. The ref lets us
|
|
|
|
|
// keep the "don't flash a skeleton when items are already on screen"
|
|
|
|
|
// behavior while breaking the collapse loop.
|
|
|
|
|
const itemsLengthRef = useRef(0);
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
itemsLengthRef.current = items.length;
|
|
|
|
|
}, [items.length]);
|
|
|
|
|
|
2026-05-24 19:51:29 +00:00
|
|
|
useEffect(() => {
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
if (!authLoading && !isLoggedIn) router.push('/login/');
|
2026-05-24 19:51:29 +00:00
|
|
|
}, [isLoggedIn, authLoading, router]);
|
|
|
|
|
|
2026-07-07 18:43:17 +00:00
|
|
|
// Each tab maps to a different PocketBase fetch — pool and problems
|
|
|
|
|
// are SYSTEM-WIDE (any driver can see them), assigned is the current
|
|
|
|
|
// driver's personal list only. This is the contract the brief asked
|
|
|
|
|
// for: "pool must show all items in collection_pool, same with
|
|
|
|
|
// problems; default when you open the app should be assigned".
|
|
|
|
|
//
|
|
|
|
|
// The cache write on success is what makes subsequent tab switches
|
|
|
|
|
// instant — the next page mount reads the cache and renders the
|
|
|
|
|
// last-known list with no skeleton flash.
|
|
|
|
|
const loadItems = useCallback(async (showLoading = true) => {
|
2026-05-24 19:51:29 +00:00
|
|
|
if (!isAuthenticated()) return;
|
2026-07-07 18:43:17 +00:00
|
|
|
// Only show the loading skeleton when we genuinely have nothing
|
2026-09-08 20:15:31 +00:00
|
|
|
// to render. Read items.length from the ref so this stays a
|
|
|
|
|
// current-value check, not a subscription — see the comment on
|
|
|
|
|
// itemsLengthRef above for why.
|
|
|
|
|
if (showLoading && itemsLengthRef.current === 0) setIsLoading(true);
|
2026-05-24 19:51:29 +00:00
|
|
|
setError('');
|
|
|
|
|
try {
|
2026-07-07 15:15:42 +00:00
|
|
|
let data: OrderItem[] = [];
|
|
|
|
|
if (statusFilter === 'assigned') {
|
2026-08-17 20:33:10 +00:00
|
|
|
// Merge order_items (the original Assigned scope) with
|
|
|
|
|
// stock_purchase_items (status="assigned_to_driver"). Both
|
|
|
|
|
// get reshaped onto the same OrderItem shape with a `kind`
|
|
|
|
|
// discriminator so the existing card / modal / map code
|
|
|
|
|
// renders them identically — only the WRITE side branches
|
|
|
|
|
// on kind to hit the right PB collection.
|
|
|
|
|
//
|
|
|
|
|
// Pool + Problems stay unchanged: pool is system-wide open
|
|
|
|
|
// claims, problems are operator-side audit flags. Stock
|
|
|
|
|
// pickups don't have an open-claim state.
|
|
|
|
|
const [orders, stock] = await Promise.all([
|
|
|
|
|
getMyAssignedItems(),
|
|
|
|
|
getMyAssignedStockItems(),
|
|
|
|
|
]);
|
|
|
|
|
data = [...orders, ...stock];
|
2026-07-07 15:15:42 +00:00
|
|
|
} else if (statusFilter === 'collection_pool') {
|
|
|
|
|
data = await getPoolItems();
|
|
|
|
|
} else if (statusFilter === 'problem') {
|
|
|
|
|
data = await getProblemItems();
|
|
|
|
|
}
|
2026-05-24 22:47:47 +00:00
|
|
|
setItems(data);
|
|
|
|
|
setFilteredItems(data);
|
2026-07-07 18:43:17 +00:00
|
|
|
setCached(statusFilter, data);
|
2026-09-08 16:41:14 +00:00
|
|
|
// Default to ALL groups collapsed. Drivers see a flat list of
|
|
|
|
|
// stops (suppliers / area clusters / POs) on first load and tap
|
|
|
|
|
// one to drill into its items — matches the "open the app,
|
|
|
|
|
// see your day" mental model. The "Show all" toolbar action
|
|
|
|
|
// (`expandAll`) is still available for the rare case where a
|
|
|
|
|
// driver wants everything expanded at once.
|
|
|
|
|
setExpandedSuppliers(new Set());
|
|
|
|
|
setExpandedAreas(new Set());
|
2026-10-18 08:31:41 +00:00
|
|
|
setCollapsedPOs(new Set());
|
2026-05-24 19:51:29 +00:00
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Failed to load collections:', err);
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
setError('Failed to load collections. Pull down to retry.');
|
2026-05-24 19:51:29 +00:00
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
setIsRefreshing(false);
|
|
|
|
|
}
|
2026-09-08 20:15:31 +00:00
|
|
|
}, [statusFilter]);
|
2026-05-24 19:51:29 +00:00
|
|
|
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
useEffect(() => { loadItems(); }, [loadItems]);
|
2026-05-24 19:51:29 +00:00
|
|
|
|
2026-07-07 18:43:17 +00:00
|
|
|
// Seed items from cache the moment the filter changes, BEFORE the
|
|
|
|
|
// network re-fetch resolves. This is what kills the
|
|
|
|
|
// skeleton-flash on tab/filter switches — driver taps Pool, the
|
|
|
|
|
// page immediately renders whatever was last cached for pool, and
|
|
|
|
|
// the re-fetch swaps in fresh data when it lands.
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const cached = getCached(statusFilter);
|
|
|
|
|
if (cached) {
|
|
|
|
|
setItems(cached);
|
|
|
|
|
setFilteredItems(cached);
|
|
|
|
|
}
|
|
|
|
|
// intentionally not depending on `items` — we only want this to
|
|
|
|
|
// fire when statusFilter changes (or on mount).
|
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
|
}, [statusFilter]);
|
|
|
|
|
|
2026-05-24 19:51:29 +00:00
|
|
|
useEffect(() => {
|
2026-05-24 22:47:47 +00:00
|
|
|
let filtered = items;
|
2026-05-24 19:51:29 +00:00
|
|
|
if (searchQuery) {
|
|
|
|
|
const query = searchQuery.toLowerCase();
|
2026-05-24 22:47:47 +00:00
|
|
|
filtered = filtered.filter((i) => {
|
|
|
|
|
const desc = i.description || '';
|
|
|
|
|
const supplier = i.expand?.supplier?.suppliername || '';
|
|
|
|
|
const orderNum = i.expand?.order?.order_number || '';
|
2026-05-24 19:51:29 +00:00
|
|
|
return (
|
|
|
|
|
desc.toLowerCase().includes(query) ||
|
|
|
|
|
supplier.toLowerCase().includes(query) ||
|
|
|
|
|
orderNum.toLowerCase().includes(query)
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-05-24 22:47:47 +00:00
|
|
|
setFilteredItems(filtered);
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
}, [items, searchQuery, statusFilter]);
|
|
|
|
|
|
|
|
|
|
// Counts for the status chip strip — derived from the unfiltered
|
|
|
|
|
// list so chips stay stable as the user types in the search field.
|
2026-07-07 15:15:42 +00:00
|
|
|
// Each tab pulls from a different PB query (driver-scoped vs system-wide),
|
|
|
|
|
// so these counts aren't comparable across tabs; they each represent
|
|
|
|
|
// the size of the most recent fetch for THAT tab.
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
const statusCounts = useMemo(() => ({
|
|
|
|
|
assigned: items.filter((i) => i.item_status === 'assigned').length,
|
2026-07-07 15:15:42 +00:00
|
|
|
collection_pool: items.filter((i) => i.item_status === 'collection_pool').length,
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
problem: items.filter((i) => i.problem_flag).length,
|
|
|
|
|
}), [items]);
|
2026-05-24 19:51:29 +00:00
|
|
|
|
2026-07-07 15:15:42 +00:00
|
|
|
// Mirror the currently selected tab to localStorage so the Map page
|
|
|
|
|
// can pick it up on its next visit and show the same dataset the
|
|
|
|
|
// driver was last working with.
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (typeof window !== 'undefined') {
|
|
|
|
|
window.localStorage.setItem('1driver_map_tab', statusFilter);
|
|
|
|
|
}
|
|
|
|
|
}, [statusFilter]);
|
|
|
|
|
|
2026-05-24 19:51:29 +00:00
|
|
|
const handleRefresh = async () => {
|
|
|
|
|
setIsRefreshing(true);
|
2026-05-24 22:47:47 +00:00
|
|
|
await loadItems(false);
|
2026-05-24 19:51:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const showToast = (message: string, type: 'success' | 'error' | 'warning' = 'success') => {
|
|
|
|
|
setToast({ message, type });
|
|
|
|
|
setTimeout(() => setToast(null), 3000);
|
|
|
|
|
};
|
|
|
|
|
|
2026-10-17 15:43:35 +00:00
|
|
|
// Item ids whose collect is still in flight. The modal disables its
|
|
|
|
|
// button while saving, but this is the backstop: a second call for
|
|
|
|
|
// the same item is dropped instead of re-saving and re-uploading.
|
|
|
|
|
const collectingRef = useRef<Set<string>>(new Set());
|
|
|
|
|
|
2026-07-07 18:43:17 +00:00
|
|
|
const handleCollect = async (item: OrderItem, receivedQty: number, notes: string, photo: File | null) => {
|
2026-10-17 15:43:35 +00:00
|
|
|
if (collectingRef.current.has(item.id)) return;
|
|
|
|
|
collectingRef.current.add(item.id);
|
2026-05-24 19:51:29 +00:00
|
|
|
try {
|
2026-07-07 18:43:17 +00:00
|
|
|
// expectedQty is computed from the same getItemDisplayQty the
|
|
|
|
|
// modal uses, so the comparison happens against the same number
|
|
|
|
|
// the driver just saw on screen.
|
|
|
|
|
const expectedQty = getItemDisplayQty(item);
|
2026-08-17 20:33:10 +00:00
|
|
|
|
|
|
|
|
if (item.kind === 'pickup') {
|
2026-10-19 15:02:06 +00:00
|
|
|
// Stock-pickup stop — PATCH stock_purchase_items. The photo goes
|
|
|
|
|
// on the stock order (stock_purchases.additional_attachments):
|
|
|
|
|
// the lines have no file field. It used to be dropped.
|
2026-08-17 20:33:10 +00:00
|
|
|
await updateStockCollectionStatus(item.id, {
|
|
|
|
|
qty_collected: receivedQty,
|
|
|
|
|
notes,
|
|
|
|
|
expectedQty,
|
|
|
|
|
});
|
2026-10-19 15:02:06 +00:00
|
|
|
let stockPhotoError = '';
|
|
|
|
|
if (photo) {
|
|
|
|
|
try {
|
|
|
|
|
await uploadStockPickupPhoto(item, photo);
|
|
|
|
|
} catch (uploadErr) {
|
|
|
|
|
stockPhotoError = uploadErr instanceof Error ? uploadErr.message : 'unknown error';
|
|
|
|
|
console.error('Stock pickup photo upload failed:', uploadErr);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-08-17 20:33:10 +00:00
|
|
|
setItems((prev) => prev.filter((i) => i.id !== item.id));
|
|
|
|
|
setShowCollectModal(false);
|
2026-10-19 15:02:06 +00:00
|
|
|
if (stockPhotoError) {
|
|
|
|
|
showToast(`Collected, but the photo didn't upload (${stockPhotoError}) — add it from the office.`, 'warning');
|
|
|
|
|
} else {
|
|
|
|
|
showToast(photo ? 'Stock pickup collected · photo uploaded' : 'Stock pickup collected successfully');
|
|
|
|
|
}
|
2026-08-17 20:33:10 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-07 23:35:26 +00:00
|
|
|
const updated = await updateCollectionStatus(item.id, 'collected', {
|
2026-05-24 22:47:47 +00:00
|
|
|
qty_collected: receivedQty,
|
2026-05-24 19:51:29 +00:00
|
|
|
notes,
|
2026-07-07 23:35:26 +00:00
|
|
|
photo: null, // legacy collection_photo is no longer the home
|
|
|
|
|
// for the new photo — see uploadOrderItemAttachment
|
|
|
|
|
// below. Keeping the param `null` avoids clearing
|
|
|
|
|
// any previously-stored photo on the row.
|
2026-07-07 18:43:17 +00:00
|
|
|
expectedQty,
|
2026-05-24 22:47:47 +00:00
|
|
|
collected_date: new Date().toISOString(),
|
2026-05-24 19:51:29 +00:00
|
|
|
});
|
2026-07-07 23:35:26 +00:00
|
|
|
|
|
|
|
|
// Upload the photo, if any, into the order_item_attachments
|
|
|
|
|
// collection so the operator can audit the pickup with a
|
|
|
|
|
// 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.
|
2026-10-14 14:12:41 +00:00
|
|
|
//
|
|
|
|
|
// 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 = '';
|
2026-07-07 23:35:26 +00:00
|
|
|
if (photo) {
|
|
|
|
|
try {
|
|
|
|
|
await uploadOrderItemAttachment({
|
|
|
|
|
orderItemId: updated.id,
|
|
|
|
|
file: photo,
|
|
|
|
|
source: 'driver_proof',
|
|
|
|
|
notes: notes || undefined,
|
|
|
|
|
});
|
|
|
|
|
} catch (uploadErr) {
|
2026-10-14 14:12:41 +00:00
|
|
|
// 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';
|
2026-07-07 23:35:26 +00:00
|
|
|
console.error('Photo upload failed:', uploadErr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-24 22:47:47 +00:00
|
|
|
setItems((prev) => prev.filter((i) => i.id !== item.id));
|
2026-05-24 19:51:29 +00:00
|
|
|
setShowCollectModal(false);
|
2026-10-14 14:12:41 +00:00
|
|
|
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',
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-05-24 19:51:29 +00:00
|
|
|
} catch (err) {
|
|
|
|
|
showToast('Failed to update collection', 'error');
|
2026-10-17 15:43:35 +00:00
|
|
|
} finally {
|
|
|
|
|
collectingRef.current.delete(item.id);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// One invoice photo per PO, taken from the PO header. Page N goes on
|
|
|
|
|
// the same line as page 1 so the office sees the pages together.
|
|
|
|
|
const handleAddInvoicePage = async (poItems: OrderItem[], file: File) => {
|
|
|
|
|
const target = invoicePhotoTarget(poItems);
|
|
|
|
|
if (!target) return;
|
2026-10-19 15:02:06 +00:00
|
|
|
const poNumber = poItems[0]?.expand?.purchase_order?.po_number ?? '';
|
2026-10-17 15:43:35 +00:00
|
|
|
const page = countInvoicePages(poItems) + 1;
|
|
|
|
|
try {
|
|
|
|
|
const files = await uploadInvoicePage({ target, poNumber, page, file });
|
2026-10-19 15:02:06 +00:00
|
|
|
setItems((prev) => withTargetFiles(prev, target, files));
|
2026-10-17 15:43:35 +00:00
|
|
|
showToast(`Invoice page ${page} uploaded${poNumber ? ` · PO ${poNumber}` : ''}`);
|
2026-10-26 08:54:09 +00:00
|
|
|
// The photo collects nothing by itself: ask, so the lines reach
|
|
|
|
|
// Collection Confirm whether or not the AI match runs.
|
|
|
|
|
if (statusFilter === 'assigned') {
|
|
|
|
|
const prompt = { poLabel: poNumber ? `PO ${poNumber}` : 'this PO', ids: poItems.map((i) => i.id) };
|
|
|
|
|
if (poNumber && invoiceCheckBase()) pendingPrompt.current = prompt;
|
|
|
|
|
else setCollectPrompt(prompt);
|
|
|
|
|
}
|
2026-10-17 15:43:35 +00:00
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Invoice photo upload failed:', err);
|
|
|
|
|
const reason = err instanceof Error ? err.message : 'unknown error';
|
|
|
|
|
showToast(`Invoice photo didn't upload (${reason}) — try again.`, 'error');
|
|
|
|
|
throw err;
|
2026-05-24 19:51:29 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
const handleProblem = async (item: OrderItem, problemType: string, description: string) => {
|
2026-05-24 19:51:29 +00:00
|
|
|
try {
|
2026-08-17 20:33:10 +00:00
|
|
|
if (item.kind === 'pickup') {
|
|
|
|
|
await reportStockProblem(item.id, problemType, description);
|
|
|
|
|
} else {
|
|
|
|
|
await reportProblem(item.id, problemType, description);
|
|
|
|
|
}
|
2026-05-24 22:47:47 +00:00
|
|
|
setItems((prev) => prev.filter((i) => i.id !== item.id));
|
2026-05-24 19:51:29 +00:00
|
|
|
setShowProblemModal(false);
|
2026-09-08 20:04:37 +00:00
|
|
|
// Item is now in status='collection_pool' on PB, so it dropped
|
|
|
|
|
// off the Assigned tab server-side as well. Toast signposts the
|
|
|
|
|
// pool handoff so the driver knows the item will be re-attempted
|
|
|
|
|
// rather than just disappearing.
|
|
|
|
|
showToast('Problem reported — item sent back to the pool', 'warning');
|
2026-05-24 19:51:29 +00:00
|
|
|
} catch (err) {
|
2026-10-14 14:12:41 +00:00
|
|
|
// 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,
|
|
|
|
|
);
|
2026-05-24 19:51:29 +00:00
|
|
|
showToast('Failed to report problem', 'error');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-08-12 06:04:26 +00:00
|
|
|
// Bulk actions — used by the per-PO "Mark all" buttons in the Invoice
|
|
|
|
|
// group mode. Skips the per-item modal flow and stamps every row in
|
|
|
|
|
// the list straight to its terminal status. The driver is presumed
|
|
|
|
|
// to have visually confirmed the items under the PO sub-header.
|
|
|
|
|
//
|
|
|
|
|
// On success the affected rows are removed from local state so the
|
|
|
|
|
// Assigned-tab list shrinks immediately without waiting for the next
|
|
|
|
|
// refetch — drivers want instant feedback that the action landed.
|
|
|
|
|
const handleBulkCollect = async (poLabel: string, itemsToCollect: OrderItem[]) => {
|
|
|
|
|
if (itemsToCollect.length === 0) return;
|
|
|
|
|
try {
|
2026-08-17 20:33:10 +00:00
|
|
|
// Split the input by kind so order rows and stock rows go to
|
|
|
|
|
// their respective PB collections in one round-trip each.
|
|
|
|
|
const [orderResult, stockResult] = await Promise.all([
|
|
|
|
|
bulkCollect(itemsToCollect.filter((i) => i.kind !== 'pickup')),
|
|
|
|
|
bulkCollectStock(itemsToCollect),
|
|
|
|
|
]);
|
|
|
|
|
const ok = orderResult.ok + stockResult.ok;
|
|
|
|
|
const failed = orderResult.failed + stockResult.failed;
|
2026-08-12 06:04:26 +00:00
|
|
|
const ids = new Set(itemsToCollect.map((i) => i.id));
|
|
|
|
|
setItems((prev) => prev.filter((i) => !ids.has(i.id)));
|
2026-08-17 20:33:10 +00:00
|
|
|
if (failed === 0) {
|
|
|
|
|
showToast(`Collected ${ok} item${ok === 1 ? '' : 's'} · ${poLabel}`, 'success');
|
2026-08-12 06:04:26 +00:00
|
|
|
} else {
|
|
|
|
|
showToast(
|
2026-08-17 20:33:10 +00:00
|
|
|
`Collected ${ok}, ${failed} failed · ${poLabel}`,
|
2026-08-12 06:04:26 +00:00
|
|
|
'warning'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
showToast(`Bulk collect failed · ${poLabel}`, 'error');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleBulkProblem = async (poLabel: string, itemsToFlag: OrderItem[]) => {
|
|
|
|
|
if (itemsToFlag.length === 0) return;
|
|
|
|
|
const description = `Bulk-flagged from ${poLabel}`;
|
|
|
|
|
try {
|
2026-08-17 20:33:10 +00:00
|
|
|
const [orderResult, stockResult] = await Promise.all([
|
|
|
|
|
bulkProblem(itemsToFlag.filter((i) => i.kind !== 'pickup'), description),
|
|
|
|
|
bulkProblemStock(itemsToFlag, description),
|
|
|
|
|
]);
|
|
|
|
|
const ok = orderResult.ok + stockResult.ok;
|
|
|
|
|
const failed = orderResult.failed + stockResult.failed;
|
2026-08-12 06:04:26 +00:00
|
|
|
const ids = new Set(itemsToFlag.map((i) => i.id));
|
|
|
|
|
setItems((prev) => prev.filter((i) => !ids.has(i.id)));
|
2026-08-17 20:33:10 +00:00
|
|
|
if (failed === 0) {
|
2026-09-08 20:04:37 +00:00
|
|
|
showToast(
|
|
|
|
|
`Flagged ${ok} item${ok === 1 ? '' : 's'} · sent back to the pool · ${poLabel}`,
|
|
|
|
|
'warning'
|
|
|
|
|
);
|
2026-08-12 06:04:26 +00:00
|
|
|
} else {
|
|
|
|
|
showToast(
|
2026-08-17 20:33:10 +00:00
|
|
|
`Flagged ${ok}, ${failed} failed · ${poLabel}`,
|
2026-08-12 06:04:26 +00:00
|
|
|
'error'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
showToast(`Bulk flag failed · ${poLabel}`, 'error');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-24 19:51:29 +00:00
|
|
|
const handleNavigate = (lat: number, lng: number, name: string) => {
|
|
|
|
|
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
|
2026-05-24 22:47:47 +00:00
|
|
|
const url = isIOS
|
|
|
|
|
? `maps://maps.apple.com/?q=${encodeURIComponent(name)}&ll=${lat},${lng}`
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
: `https://www.google.com/maps/dir/?api=1&destination=${lat},${lng}`;
|
2026-05-24 19:51:29 +00:00
|
|
|
window.open(url, '_blank');
|
|
|
|
|
};
|
|
|
|
|
|
2026-08-02 19:38:38 +00:00
|
|
|
const handleShowDetails = (item: OrderItem) => {
|
|
|
|
|
setSelectedItem(item);
|
|
|
|
|
setShowDetailModal(true);
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-26 22:55:21 +00:00
|
|
|
const toggleSupplier = (supplierId: string) => {
|
|
|
|
|
setExpandedSuppliers(prev => {
|
|
|
|
|
const next = new Set(prev);
|
|
|
|
|
if (next.has(supplierId)) next.delete(supplierId);
|
|
|
|
|
else next.add(supplierId);
|
|
|
|
|
return next;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-07 15:15:42 +00:00
|
|
|
const toggleArea = (clusterId: string) => {
|
|
|
|
|
setExpandedAreas(prev => {
|
|
|
|
|
const next = new Set(prev);
|
|
|
|
|
if (next.has(clusterId)) next.delete(clusterId);
|
|
|
|
|
else next.add(clusterId);
|
|
|
|
|
return next;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2026-08-12 06:04:26 +00:00
|
|
|
const togglePO = (poId: string) => {
|
2026-10-18 08:31:41 +00:00
|
|
|
setCollapsedPOs(prev => {
|
2026-08-12 06:04:26 +00:00
|
|
|
const next = new Set(prev);
|
|
|
|
|
if (next.has(poId)) next.delete(poId);
|
|
|
|
|
else next.add(poId);
|
|
|
|
|
return next;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-26 22:55:21 +00:00
|
|
|
const expandAll = () => {
|
2026-08-12 06:04:26 +00:00
|
|
|
if (groupMode === 'supplier' || groupMode === 'po') {
|
2026-07-07 15:15:42 +00:00
|
|
|
const allIds = new Set<string>();
|
|
|
|
|
filteredItems.forEach(i => {
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
if (i.expand?.supplier?.id) allIds.add(i.expand.supplier.id);
|
2026-07-07 15:15:42 +00:00
|
|
|
});
|
|
|
|
|
setExpandedSuppliers(allIds);
|
|
|
|
|
} else {
|
|
|
|
|
const allIds = new Set<string>();
|
|
|
|
|
areaClusters.forEach(c => allIds.add(c.id));
|
|
|
|
|
setExpandedAreas(allIds);
|
|
|
|
|
}
|
2026-08-12 06:04:26 +00:00
|
|
|
// For PO mode, expandAll also opens every PO sub-header so the
|
|
|
|
|
// driver sees the full breakdown without an extra tap.
|
2026-10-18 08:31:41 +00:00
|
|
|
if (groupMode === 'po') setCollapsedPOs(new Set());
|
2026-05-26 22:55:21 +00:00
|
|
|
};
|
|
|
|
|
|
2026-07-10 00:01:57 +00:00
|
|
|
const collapseAll = () => {
|
2026-08-12 06:04:26 +00:00
|
|
|
if (groupMode === 'supplier' || groupMode === 'po') {
|
2026-07-10 00:01:57 +00:00
|
|
|
setExpandedSuppliers(new Set());
|
|
|
|
|
} else {
|
|
|
|
|
setExpandedAreas(new Set());
|
|
|
|
|
}
|
2026-08-12 06:04:26 +00:00
|
|
|
if (groupMode === 'po') {
|
2026-10-18 08:31:41 +00:00
|
|
|
const allPoIds = new Set<string>();
|
|
|
|
|
filteredItems.forEach(i => allPoIds.add(i.expand?.purchase_order?.id || '__no_po__'));
|
|
|
|
|
setCollapsedPOs(allPoIds);
|
2026-08-12 06:04:26 +00:00
|
|
|
}
|
2026-07-10 00:01:57 +00:00
|
|
|
};
|
2026-05-26 22:55:21 +00:00
|
|
|
|
2026-06-09 22:05:06 +00:00
|
|
|
// Group by supplier - items without supplier are grouped under "Unknown"
|
2026-05-26 22:55:21 +00:00
|
|
|
const groupedArray = useMemo(() => {
|
|
|
|
|
const grouped = filteredItems.reduce((acc, item) => {
|
|
|
|
|
const supplier = item.expand?.supplier;
|
2026-06-09 22:05:06 +00:00
|
|
|
const key = supplier?.id || '__unknown__';
|
2026-05-26 22:55:21 +00:00
|
|
|
if (!acc[key]) acc[key] = { supplier, items: [] };
|
|
|
|
|
acc[key].items.push(item);
|
|
|
|
|
return acc;
|
2026-06-09 22:05:06 +00:00
|
|
|
}, {} as Record<string, { supplier: NonNullable<OrderItem['expand']>['supplier'] | null; items: OrderItem[] }>);
|
2026-05-24 19:51:29 +00:00
|
|
|
|
2026-05-26 22:55:21 +00:00
|
|
|
return Object.values(grouped).sort((a, b) =>
|
2026-06-09 22:05:06 +00:00
|
|
|
(a.supplier?.suppliername || 'zzz').localeCompare(b.supplier?.suppliername || 'zzz')
|
2026-05-26 22:55:21 +00:00
|
|
|
);
|
|
|
|
|
}, [filteredItems]);
|
2026-05-24 19:51:29 +00:00
|
|
|
|
2026-08-12 06:04:26 +00:00
|
|
|
// Group by supplier, then by PO within each supplier. Used by the
|
|
|
|
|
// 'po' groupMode so the driver can see (and bulk-collect) one PO at
|
|
|
|
|
// a time even when a single supplier has multiple invoices.
|
|
|
|
|
//
|
|
|
|
|
// Shape:
|
|
|
|
|
// [{ supplier, pos: [{ poId, poNumber, items: [...] }, ...] }, ...]
|
|
|
|
|
//
|
|
|
|
|
// Items without a PO are bucketed under '__no_po__' so they still
|
|
|
|
|
// surface — better to render "No PO" than to silently drop them.
|
|
|
|
|
const groupedByPO = useMemo(() => {
|
|
|
|
|
const supplierBuckets: Record<string, {
|
|
|
|
|
supplier: NonNullable<OrderItem['expand']>['supplier'] | null;
|
|
|
|
|
pos: Record<string, { poId: string; poNumber: string; items: OrderItem[] }>;
|
|
|
|
|
}> = {};
|
|
|
|
|
|
|
|
|
|
filteredItems.forEach((item) => {
|
|
|
|
|
const supplier = item.expand?.supplier ?? null;
|
|
|
|
|
const supplierKey = supplier?.id || '__unknown__';
|
|
|
|
|
if (!supplierBuckets[supplierKey]) {
|
|
|
|
|
supplierBuckets[supplierKey] = { supplier, pos: {} };
|
|
|
|
|
}
|
|
|
|
|
const po = item.expand?.purchase_order;
|
|
|
|
|
const poKey = po?.id || '__no_po__';
|
|
|
|
|
if (!supplierBuckets[supplierKey].pos[poKey]) {
|
|
|
|
|
supplierBuckets[supplierKey].pos[poKey] = {
|
|
|
|
|
poId: poKey,
|
|
|
|
|
poNumber: po?.po_number || 'No PO',
|
|
|
|
|
items: [],
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
supplierBuckets[supplierKey].pos[poKey].items.push(item);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return Object.values(supplierBuckets)
|
|
|
|
|
.sort((a, b) =>
|
|
|
|
|
(a.supplier?.suppliername || 'zzz').localeCompare(b.supplier?.suppliername || 'zzz')
|
|
|
|
|
)
|
|
|
|
|
.map((bucket) => ({
|
|
|
|
|
supplier: bucket.supplier,
|
|
|
|
|
pos: Object.values(bucket.pos).sort((a, b) =>
|
|
|
|
|
a.poNumber.localeCompare(b.poNumber, undefined, { numeric: true })
|
|
|
|
|
),
|
|
|
|
|
}));
|
|
|
|
|
}, [filteredItems]);
|
|
|
|
|
|
2026-07-07 15:15:42 +00:00
|
|
|
// Group by geographic proximity — items within 2km share a cluster color
|
|
|
|
|
const areaClusters = useMemo(() => {
|
|
|
|
|
return clusterByProximity(filteredItems, 2);
|
|
|
|
|
}, [filteredItems]);
|
|
|
|
|
|
2026-05-24 19:51:29 +00:00
|
|
|
if (authLoading) {
|
|
|
|
|
return (
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<div className="min-h-screen flex items-center justify-center bg-[#f2f1ed]">
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
<div className="flex flex-col items-center gap-3">
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<div className="w-10 h-10 border-[3px] border-[#f54e00] border-t-transparent rounded-full animate-spin" />
|
|
|
|
|
<p className="text-sm text-[rgba(38,37,30,0.55)]">Loading...</p>
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
</div>
|
2026-05-24 19:51:29 +00:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-24 22:47:47 +00:00
|
|
|
if (!isLoggedIn) return null;
|
2026-05-24 19:51:29 +00:00
|
|
|
|
|
|
|
|
return (
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<div className="min-h-screen bg-[#f2f1ed] pb-nav">
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
{/* Header */}
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<header
|
|
|
|
|
className="bg-[#e6e5e0] text-[#26251e] safe-top sticky top-0 z-40 border-b border-[rgba(38,37,30,0.1)] backdrop-blur-md"
|
|
|
|
|
role="banner"
|
|
|
|
|
style={{ boxShadow: '0 1px 0 rgba(38,37,30,0.06)' }}
|
|
|
|
|
>
|
2026-07-07 16:28:19 +00:00
|
|
|
<div className="brand-accent-top" aria-hidden="true" />
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
<div className="px-4 pt-2 pb-3">
|
2026-05-24 19:51:29 +00:00
|
|
|
<div className="flex items-center justify-between">
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
<div className="flex-1 min-w-0">
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<h1 className="text-xl font-bold tracking-tight text-[#26251e]">Collections</h1>
|
|
|
|
|
<p className="text-[13px] text-[rgba(38,37,30,0.55)] truncate">{user?.name || user?.email}</p>
|
2026-05-24 19:51:29 +00:00
|
|
|
</div>
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
<div className="flex items-center gap-1.5">
|
|
|
|
|
<div className="relative">
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<span className="absolute -top-0.5 -right-0.5 w-2.5 h-2.5 bg-[#f54e00] rounded-full border-2 border-[#e6e5e0] animate-pulse" aria-hidden="true" />
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
<button
|
|
|
|
|
onClick={handleRefresh}
|
|
|
|
|
disabled={isRefreshing}
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
aria-label={isRefreshing ? 'Refreshing collections' : 'Refresh collections'}
|
|
|
|
|
className="p-2.5 rounded-xl bg-[#ebeae5] active:bg-[#e1e0db] transition-all touch-manipulation"
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
>
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<RefreshCw size={18} className={cn('text-[#26251e]', isRefreshing && 'animate-spin')} aria-hidden="true" />
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => logout()}
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
aria-label="Sign out"
|
|
|
|
|
className="p-2.5 rounded-xl bg-[#ebeae5] active:bg-[#e1e0db] transition-all touch-manipulation"
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
>
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<LogOut size={18} className="text-[#26251e]" aria-hidden="true" />
|
2026-05-24 19:51:29 +00:00
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
{/* Search bar */}
|
2026-05-26 22:55:21 +00:00
|
|
|
<div className="px-4 pb-2">
|
2026-05-24 19:51:29 +00:00
|
|
|
<div className="relative">
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<Search size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-[rgba(38,37,30,0.55)] pointer-events-none" aria-hidden="true" />
|
|
|
|
|
<label htmlFor="collections-search" className="sr-only">Search collections</label>
|
2026-05-24 19:51:29 +00:00
|
|
|
<input
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
id="collections-search"
|
2026-05-24 19:51:29 +00:00
|
|
|
type="text"
|
|
|
|
|
value={searchQuery}
|
|
|
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
placeholder="Search by item, supplier, order..."
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
className="w-full pl-9 pr-9 py-2.5 bg-[#f2f1ed] border border-[rgba(38,37,30,0.1)] rounded-xl text-sm text-[#26251e] placeholder:text-[rgba(38,37,30,0.4)] focus:outline-none focus:ring-2 focus:ring-[#f54e00]/30 focus:border-[#f54e00] focus:bg-[#f7f7f4] transition-all"
|
2026-05-24 19:51:29 +00:00
|
|
|
/>
|
|
|
|
|
{searchQuery && (
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
<button
|
|
|
|
|
onClick={() => setSearchQuery('')}
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
aria-label="Clear search"
|
|
|
|
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-[rgba(38,37,30,0.55)] active:text-[#26251e]"
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
>
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<X size={16} aria-hidden="true" />
|
2026-05-24 19:51:29 +00:00
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-08-02 20:42:40 +00:00
|
|
|
{/* Stats + Controls bar — wraps onto two lines on narrow viewports
|
|
|
|
|
so the stat chips don't collide with the toggle groups. On
|
|
|
|
|
≥640px everything fits on one line again. */}
|
|
|
|
|
<div className="px-4 pb-2 flex flex-wrap items-center justify-between gap-x-2 gap-y-2">
|
|
|
|
|
<div className="flex items-center gap-1.5 min-w-0 flex-wrap">
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<span className="inline-flex items-center gap-1 px-2.5 py-1 rounded-full bg-[#ebeae5] text-[11px] font-medium text-[#26251e]">
|
|
|
|
|
<Package size={12} aria-hidden="true" />
|
2026-05-26 22:55:21 +00:00
|
|
|
{items.length} item{items.length !== 1 ? 's' : ''}
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
</span>
|
2026-05-26 22:55:21 +00:00
|
|
|
{groupedArray.length > 0 && (
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<span className="inline-flex items-center gap-1 px-2.5 py-1 rounded-full bg-[#ebeae5] text-[11px] font-medium text-[#26251e]">
|
|
|
|
|
<MapPin size={12} aria-hidden="true" />
|
2026-05-26 22:55:21 +00:00
|
|
|
{groupedArray.length} stop{groupedArray.length !== 1 ? 's' : ''}
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-07-07 15:15:42 +00:00
|
|
|
</div>
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
|
|
|
|
|
{/* Status filter chips — show counts of each state so drivers
|
|
|
|
|
can see at a glance what's pending vs flagged. */}
|
|
|
|
|
<div
|
|
|
|
|
className="px-4 pb-3 flex items-center gap-1.5 overflow-x-auto no-scrollbar -mx-1"
|
|
|
|
|
role="tablist"
|
|
|
|
|
aria-label="Filter by status"
|
|
|
|
|
>
|
|
|
|
|
{([
|
|
|
|
|
{ key: 'assigned', label: 'Assigned' },
|
2026-07-07 15:15:42 +00:00
|
|
|
{ key: 'collection_pool', label: 'Pool' },
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
{ key: 'problem', label: 'Problems' },
|
|
|
|
|
] as const).map(({ key, label }) => {
|
|
|
|
|
const count = statusCounts[key];
|
|
|
|
|
const active = statusFilter === key;
|
|
|
|
|
return (
|
|
|
|
|
<button
|
|
|
|
|
key={key}
|
|
|
|
|
role="tab"
|
|
|
|
|
aria-selected={active}
|
|
|
|
|
onClick={() => setStatusFilter(key)}
|
|
|
|
|
className={cn(
|
|
|
|
|
'inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-[11px] font-semibold transition-all touch-manipulation flex-shrink-0',
|
|
|
|
|
active
|
|
|
|
|
? 'bg-[#26251e] text-[#f2f1ed]'
|
|
|
|
|
: 'bg-[rgba(38,37,30,0.06)] text-[rgba(38,37,30,0.7)] active:bg-[rgba(38,37,30,0.12)]'
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
{label}
|
|
|
|
|
<span
|
|
|
|
|
className={cn(
|
|
|
|
|
'inline-flex items-center justify-center min-w-[18px] h-[18px] px-1 rounded-full text-[10px] font-bold',
|
|
|
|
|
active ? 'bg-[#f54e00] text-white' : 'bg-[rgba(38,37,30,0.08)] text-[rgba(38,37,30,0.6)]'
|
|
|
|
|
)}
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
>
|
|
|
|
|
{count}
|
|
|
|
|
</span>
|
|
|
|
|
</button>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
2026-05-24 19:51:29 +00:00
|
|
|
</header>
|
|
|
|
|
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
{/* Content */}
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<main className="p-4 custom-scrollbar" role="main" aria-live="polite">
|
2026-05-24 19:51:29 +00:00
|
|
|
{isLoading ? (
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<div className="space-y-3" aria-label="Loading your collections" aria-busy="true">
|
2026-05-26 22:55:21 +00:00
|
|
|
{[...Array(3)].map((_, i) => (
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<div key={i} className="bg-[#e6e5e0] rounded-2xl p-4 border border-[rgba(38,37,30,0.1)]">
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
<div className="skeleton h-4 w-3/4 rounded-lg mb-3" />
|
|
|
|
|
<div className="skeleton h-3 w-1/2 rounded-lg mb-2" />
|
|
|
|
|
<div className="skeleton h-3 w-2/3 rounded-lg" />
|
2026-05-24 19:51:29 +00:00
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
) : error ? (
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<div className="text-center py-16 px-4" role="alert">
|
|
|
|
|
<div className="w-16 h-16 bg-[#fbeaef] rounded-2xl flex items-center justify-center mx-auto mb-4">
|
|
|
|
|
<AlertCircle size={32} className="text-[#b3224a]" aria-hidden="true" />
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
</div>
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<p className="text-[#26251e] font-semibold mb-1">{error}</p>
|
|
|
|
|
<p className="text-[rgba(38,37,30,0.55)] text-sm mb-6">Check your connection and try again</p>
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
<button
|
|
|
|
|
onClick={handleRefresh}
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
className="inline-flex items-center gap-2 px-6 py-3 bg-[#f54e00] text-white rounded-xl text-sm font-semibold active:bg-[#d44200] transition-colors touch-manipulation press-feedback min-h-[44px]"
|
|
|
|
|
style={{ boxShadow: '0 4px 12px rgba(245,78,0,0.3)' }}
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
>
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<RefreshCw size={16} aria-hidden="true" />
|
2026-05-24 19:51:29 +00:00
|
|
|
Try Again
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2026-07-07 15:15:42 +00:00
|
|
|
) : filteredItems.length === 0 ? (
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
<div className="text-center py-16 px-4">
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<div className="w-16 h-16 bg-[#e6f1ec] rounded-2xl flex items-center justify-center mx-auto mb-4">
|
|
|
|
|
<CheckCircle2 size={32} className="text-[#18714f]" aria-hidden="true" />
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
</div>
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<p className="text-[#26251e] font-semibold text-lg mb-1">
|
2026-07-07 15:15:42 +00:00
|
|
|
{searchQuery ? 'Nothing matches' : 'All caught up'}
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
</p>
|
|
|
|
|
<p className="text-[rgba(38,37,30,0.55)] text-sm">
|
|
|
|
|
{searchQuery
|
|
|
|
|
? 'No collections match your search.'
|
2026-07-07 15:15:42 +00:00
|
|
|
: statusFilter === 'assigned'
|
2026-08-18 13:46:37 +00:00
|
|
|
? 'No pickups for today or yesterday. New ones appear here as the operator schedules them.'
|
2026-07-07 15:15:42 +00:00
|
|
|
: statusFilter === 'collection_pool'
|
|
|
|
|
? 'Collection pool is empty — nothing to claim right now.'
|
|
|
|
|
: 'No problems reported. Nice work.'}
|
2026-05-24 19:51:29 +00:00
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
2026-05-26 22:55:21 +00:00
|
|
|
<div className="space-y-4">
|
2026-08-12 06:04:26 +00:00
|
|
|
{groupMode === 'area' ? (
|
2026-07-07 15:15:42 +00:00
|
|
|
areaClusters.map((cluster, clusterIdx) => {
|
|
|
|
|
const color = SUPPLIER_COLORS[clusterIdx % SUPPLIER_COLORS.length];
|
|
|
|
|
const isExpanded = expandedAreas.has(cluster.id);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<AreaGroup
|
|
|
|
|
key={cluster.id}
|
|
|
|
|
clusterId={cluster.id}
|
|
|
|
|
label={cluster.label}
|
|
|
|
|
items={cluster.items}
|
|
|
|
|
center={cluster.center}
|
|
|
|
|
color={color}
|
|
|
|
|
isExpanded={isExpanded}
|
|
|
|
|
viewMode={viewMode}
|
|
|
|
|
onToggle={() => toggleArea(cluster.id)}
|
|
|
|
|
onCollect={(item) => { setSelectedItem(item); setShowCollectModal(true); }}
|
|
|
|
|
onProblem={(item) => { setSelectedItem(item); setShowProblemModal(true); }}
|
|
|
|
|
onNavigate={handleNavigate}
|
2026-08-02 19:38:38 +00:00
|
|
|
onShowDetails={handleShowDetails}
|
2026-07-07 15:15:42 +00:00
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
})
|
2026-08-12 06:04:26 +00:00
|
|
|
) : (
|
|
|
|
|
// Supplier + Invoice group modes share the SupplierGroup
|
|
|
|
|
// component; the PO sub-group render only kicks in when
|
|
|
|
|
// poGroups is non-empty (i.e. when groupMode === 'po').
|
|
|
|
|
// For 'supplier' mode the group carries items; for 'po'
|
|
|
|
|
// mode items is empty and pos carries the sub-buckets.
|
|
|
|
|
(() => {
|
|
|
|
|
const supplierGroups = groupMode === 'po'
|
|
|
|
|
? groupedByPO.map((g) => ({ supplier: g.supplier, items: [] as OrderItem[], pos: g.pos }))
|
|
|
|
|
: groupedArray.map((g) => ({ supplier: g.supplier, items: g.items, pos: [] as POSubGroup[] }));
|
|
|
|
|
return supplierGroups.map((group, groupIdx) => {
|
|
|
|
|
const color = SUPPLIER_COLORS[groupIdx % SUPPLIER_COLORS.length];
|
|
|
|
|
const isExpanded = expandedSuppliers.has(group.supplier!.id);
|
|
|
|
|
const isPoMode = groupMode === 'po';
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<SupplierGroup
|
|
|
|
|
key={group.supplier!.id}
|
|
|
|
|
supplier={group.supplier!}
|
|
|
|
|
items={group.items}
|
|
|
|
|
color={color}
|
|
|
|
|
isExpanded={isExpanded}
|
|
|
|
|
viewMode={viewMode}
|
|
|
|
|
onToggle={() => toggleSupplier(group.supplier!.id)}
|
|
|
|
|
onCollect={(item) => { setSelectedItem(item); setShowCollectModal(true); }}
|
|
|
|
|
onProblem={(item) => { setSelectedItem(item); setShowProblemModal(true); }}
|
|
|
|
|
onNavigate={handleNavigate}
|
|
|
|
|
onShowDetails={handleShowDetails}
|
|
|
|
|
poGroups={isPoMode ? group.pos : undefined}
|
2026-10-18 08:31:41 +00:00
|
|
|
collapsedPOs={isPoMode ? collapsedPOs : undefined}
|
2026-08-12 06:04:26 +00:00
|
|
|
onTogglePO={isPoMode ? togglePO : undefined}
|
|
|
|
|
onBulkCollect={isPoMode ? handleBulkCollect : undefined}
|
|
|
|
|
onBulkProblem={isPoMode ? handleBulkProblem : undefined}
|
2026-10-17 15:43:35 +00:00
|
|
|
onAddInvoicePage={handleAddInvoicePage}
|
2026-08-12 06:04:26 +00:00
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
})()
|
2026-07-07 15:15:42 +00:00
|
|
|
)}
|
2026-05-24 19:51:29 +00:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</main>
|
|
|
|
|
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
{/* Modals */}
|
2026-05-24 19:51:29 +00:00
|
|
|
<CollectModal
|
2026-05-24 22:47:47 +00:00
|
|
|
item={selectedItem}
|
2026-05-24 19:51:29 +00:00
|
|
|
isOpen={showCollectModal}
|
2026-05-24 22:47:47 +00:00
|
|
|
onClose={() => { setShowCollectModal(false); setSelectedItem(null); }}
|
2026-05-24 19:51:29 +00:00
|
|
|
onConfirm={handleCollect}
|
2026-10-17 15:43:35 +00:00
|
|
|
invoicePages={
|
|
|
|
|
selectedItem?.expand?.purchase_order
|
|
|
|
|
? countInvoicePages(
|
|
|
|
|
items.filter((i) => i.expand?.purchase_order?.id === selectedItem.expand?.purchase_order?.id),
|
|
|
|
|
)
|
|
|
|
|
: 0
|
|
|
|
|
}
|
2026-05-24 19:51:29 +00:00
|
|
|
/>
|
|
|
|
|
<ProblemModal
|
2026-05-24 22:47:47 +00:00
|
|
|
item={selectedItem}
|
2026-05-24 19:51:29 +00:00
|
|
|
isOpen={showProblemModal}
|
2026-05-24 22:47:47 +00:00
|
|
|
onClose={() => { setShowProblemModal(false); setSelectedItem(null); }}
|
2026-05-24 19:51:29 +00:00
|
|
|
onConfirm={handleProblem}
|
|
|
|
|
/>
|
2026-08-02 19:38:38 +00:00
|
|
|
<ItemDetailModal
|
|
|
|
|
item={selectedItem!}
|
|
|
|
|
purchaseOrder={selectedItem?.expand?.purchase_order ?? null}
|
|
|
|
|
isOpen={showDetailModal}
|
|
|
|
|
onClose={() => { setShowDetailModal(false); setSelectedItem(null); }}
|
|
|
|
|
/>
|
2026-05-24 19:51:29 +00:00
|
|
|
|
2026-10-21 18:58:33 +00:00
|
|
|
{/* Beta: invoice match helper (renders nothing unless it is run) */}
|
|
|
|
|
<InvoiceMatchHost
|
|
|
|
|
onCollected={(ids) => {
|
|
|
|
|
setItems((prev) => prev.filter((i) => !ids.includes(i.id)));
|
|
|
|
|
showToast(`Invoice matched · ${ids.length} item${ids.length === 1 ? '' : 's'} marked collected`);
|
|
|
|
|
}}
|
|
|
|
|
onRestored={() => { void loadItems(false); }}
|
2026-10-26 08:54:09 +00:00
|
|
|
onClosed={() => {
|
|
|
|
|
if (pendingPrompt.current) setCollectPrompt(pendingPrompt.current);
|
|
|
|
|
pendingPrompt.current = null;
|
|
|
|
|
}}
|
2026-10-21 18:58:33 +00:00
|
|
|
/>
|
|
|
|
|
|
2026-10-26 08:54:09 +00:00
|
|
|
{collectPrompt && (() => {
|
|
|
|
|
const left = items.filter((i) => collectPrompt.ids.includes(i.id));
|
|
|
|
|
if (left.length === 0) return null;
|
|
|
|
|
return (
|
|
|
|
|
<CollectPrompt
|
|
|
|
|
key={collectPrompt.ids.join(',')}
|
|
|
|
|
poLabel={collectPrompt.poLabel}
|
|
|
|
|
items={left}
|
|
|
|
|
onClose={() => setCollectPrompt(null)}
|
|
|
|
|
onCollect={(chosen) => {
|
|
|
|
|
setCollectPrompt(null);
|
|
|
|
|
void handleBulkCollect(collectPrompt.poLabel, chosen);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
})()}
|
|
|
|
|
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
{/* Toast */}
|
2026-05-24 19:51:29 +00:00
|
|
|
{toast && (
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
<div
|
|
|
|
|
className="fixed top-4 left-4 right-4 z-[70] animate-slide-down safe-top pointer-events-none"
|
|
|
|
|
role="status"
|
|
|
|
|
aria-live="polite"
|
|
|
|
|
>
|
2026-05-24 22:47:47 +00:00
|
|
|
<div className={cn(
|
refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 20:32:20 +00:00
|
|
|
'px-4 py-3 rounded-2xl text-sm font-medium text-white text-center pointer-events-auto',
|
|
|
|
|
toast.type === 'success' && 'bg-[#18714f]',
|
|
|
|
|
toast.type === 'error' && 'bg-[#cf2d56]',
|
|
|
|
|
toast.type === 'warning' && 'bg-[#d49103]'
|
|
|
|
|
)}
|
|
|
|
|
style={{ boxShadow: '0 14px 32px rgba(0,0,0,0.18)' }}
|
|
|
|
|
>
|
2026-05-24 19:51:29 +00:00
|
|
|
{toast.message}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<BottomNav />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
Add PWA support and UI redesign
Add full PWA support (icons, manifest, service worker, pwa lib and bridge) and introduce install/offline UI. Generate app icons via a new scripts/generate-icons.js and include multiple icon sizes in public/icons; update manifest and layout head meta/preconnect. Revamp global styles (globals.css) with new utilities, animations, spacing for bottom nav, card shadows, skeletons and accessibility tweaks. Polish major screens (login, collections, map, settings) with improved headers, loading states, touch/press feedback, and layout adjustments; add new components (InstallPrompt, OfflineIndicator, PWABridge) and update various components to match new visual/UX patterns. Update Dockerfile to generate icons and use Next.js static export output (/out) for nginx, and bump package setup to support icon generation.
2026-05-26 22:34:50 +00:00
|
|
|
}
|