2026-05-24 19:51:29 +00:00
|
|
|
'use client';
|
|
|
|
|
|
2026-07-07 18:43:17 +00:00
|
|
|
import { useState, useRef } from 'react';
|
2026-05-24 22:47:47 +00:00
|
|
|
import { OrderItem } from '@/lib/types';
|
2026-05-24 19:51:29 +00:00
|
|
|
import { cn, formatCurrency } from '@/lib/utils';
|
2026-07-07 15:15:42 +00:00
|
|
|
import { getItemDisplayDescription, getItemDisplayQty } from '@/lib/pocketbase';
|
2026-08-02 20:58:08 +00:00
|
|
|
import { X, Minus, Plus, Check, Camera, ImagePlus, Trash2, AlertCircle } from 'lucide-react';
|
2026-05-24 19:51:29 +00:00
|
|
|
|
|
|
|
|
interface CollectModalProps {
|
2026-05-24 22:47:47 +00:00
|
|
|
item: OrderItem | null;
|
2026-05-24 19:51:29 +00:00
|
|
|
isOpen: boolean;
|
|
|
|
|
onClose: () => void;
|
2026-07-07 18:43:17 +00:00
|
|
|
onConfirm: (item: OrderItem, receivedQty: number, notes: string, photo: File | null) => void;
|
2026-05-24 19:51:29 +00:00
|
|
|
}
|
|
|
|
|
|
2026-10-14 14:12:37 +00:00
|
|
|
// Reject absurd files at the UI so the driver sees the constraint
|
|
|
|
|
// immediately rather than after a long stalled upload. This is NOT the
|
|
|
|
|
// size that actually goes over the wire — `compressPhoto` (lib/image.ts)
|
|
|
|
|
// re-encodes to 1200px/q70 before upload, which lands in the low
|
|
|
|
|
// hundreds of KB. Raw camera files were multi-megabyte and every one of
|
|
|
|
|
// them was truncated in transit on 2026-10-12; the cap here just stops
|
|
|
|
|
// someone picking a 50MB file off a camera roll.
|
2026-07-07 18:43:17 +00:00
|
|
|
const MAX_PHOTO_BYTES = 10 * 1024 * 1024;
|
|
|
|
|
const ALLOWED_PHOTO_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/heic', 'image/heif'];
|
|
|
|
|
|
2026-05-24 19:51:29 +00:00
|
|
|
export default function CollectModal({
|
2026-05-24 22:47:47 +00:00
|
|
|
item,
|
2026-05-24 19:51:29 +00:00
|
|
|
isOpen,
|
|
|
|
|
onClose,
|
|
|
|
|
onConfirm,
|
|
|
|
|
}: CollectModalProps) {
|
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 [receivedQty, setReceivedQty] = useState('');
|
2026-05-24 19:51:29 +00:00
|
|
|
const [notes, setNotes] = useState('');
|
|
|
|
|
const [error, setError] = useState('');
|
2026-07-07 18:43:17 +00:00
|
|
|
const [photo, setPhoto] = useState<File | null>(null);
|
|
|
|
|
const [photoPreviewUrl, setPhotoPreviewUrl] = useState<string | null>(null);
|
2026-08-02 20:58:08 +00:00
|
|
|
// Reason the driver is collecting less than the expected qty. We
|
|
|
|
|
// require *something* on a partial — either a picked reason or a
|
|
|
|
|
// photo — so a partial collection can't be silently submitted with
|
|
|
|
|
// no audit trail. Over-collections still pass through with just
|
|
|
|
|
// notes (the driver may genuinely have nothing visual to add).
|
|
|
|
|
const [partialReason, setPartialReason] = useState('');
|
2026-07-07 18:43:17 +00:00
|
|
|
const cameraInputRef = useRef<HTMLInputElement>(null);
|
|
|
|
|
const libraryInputRef = useRef<HTMLInputElement>(null);
|
2026-05-24 19:51:29 +00:00
|
|
|
|
2026-05-24 22:47:47 +00:00
|
|
|
if (!isOpen || !item) return null;
|
2026-05-24 19:51:29 +00:00
|
|
|
|
2026-07-07 15:15:42 +00:00
|
|
|
const expectedQty = getItemDisplayQty(item);
|
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 parsedQty = parseFloat(receivedQty) || 0;
|
|
|
|
|
const isPartial = parsedQty > 0 && parsedQty < expectedQty;
|
|
|
|
|
const isOverQty = parsedQty > expectedQty;
|
2026-08-02 20:58:08 +00:00
|
|
|
// True once the driver has justified the partial one way or another.
|
|
|
|
|
const partialJustified = partialReason.trim() !== '' || photo !== null;
|
2026-07-07 15:15:42 +00:00
|
|
|
const displayDescription = getItemDisplayDescription(item);
|
2026-05-24 19:51:29 +00:00
|
|
|
|
2026-07-07 18:43:17 +00:00
|
|
|
const handlePhotoSelected = (file: File | undefined | null) => {
|
|
|
|
|
if (!file) return;
|
|
|
|
|
if (!ALLOWED_PHOTO_TYPES.includes(file.type)) {
|
|
|
|
|
setError(`Photo must be JPEG, PNG, WebP, or HEIC (got ${file.type || 'unknown'})`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (file.size > MAX_PHOTO_BYTES) {
|
|
|
|
|
setError(`Photo is too large (${(file.size / 1024 / 1024).toFixed(1)}MB). Max is 10MB.`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
setError('');
|
|
|
|
|
if (photoPreviewUrl) URL.revokeObjectURL(photoPreviewUrl);
|
|
|
|
|
setPhotoPreviewUrl(URL.createObjectURL(file));
|
|
|
|
|
setPhoto(file);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleRemovePhoto = () => {
|
|
|
|
|
if (photoPreviewUrl) URL.revokeObjectURL(photoPreviewUrl);
|
|
|
|
|
setPhoto(null);
|
|
|
|
|
setPhotoPreviewUrl(null);
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-24 19:51:29 +00:00
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
setError('');
|
|
|
|
|
|
|
|
|
|
const qty = parseFloat(receivedQty);
|
|
|
|
|
if (isNaN(qty) || qty <= 0) {
|
|
|
|
|
setError('Please enter a valid quantity');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-02 20:58:08 +00:00
|
|
|
// Partial collections must be justified — either a picked reason
|
|
|
|
|
// or a photo. Without one we refuse to submit so the operator
|
|
|
|
|
// always has audit context for a short pickup.
|
|
|
|
|
if (isPartial && !partialJustified) {
|
|
|
|
|
setError('Please tell us why the quantity is short, or take a photo to continue');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Prefix the driver's free-text notes with the structured reason
|
|
|
|
|
// when provided, so the operator sees both in the problem note
|
|
|
|
|
// without losing the original wording. The reason goes first
|
|
|
|
|
// because that's the most actionable piece.
|
|
|
|
|
const finalNotes = isPartial && partialReason
|
|
|
|
|
? `[${partialReason}] ${notes}`.trim()
|
|
|
|
|
: notes;
|
|
|
|
|
|
|
|
|
|
onConfirm(item, qty, finalNotes, photo);
|
2026-05-24 19:51:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const adjustQty = (delta: number) => {
|
|
|
|
|
const current = parseFloat(receivedQty) || 0;
|
|
|
|
|
const newQty = Math.max(0, current + delta);
|
|
|
|
|
setReceivedQty(newQty.toString());
|
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('');
|
2026-05-24 19:51:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center">
|
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="absolute inset-0 bg-black/50 modal-overlay" onClick={onClose} />
|
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="relative bg-[#e6e5e0] bottom-sheet w-full max-w-md flex flex-col max-h-[calc(100dvh-1rem)] sm:max-h-[85vh] border border-[rgba(38,37,30,0.1)] animate-slide-up">
|
|
|
|
|
<form
|
|
|
|
|
onSubmit={handleSubmit}
|
|
|
|
|
className="flex flex-col min-h-0 flex-1"
|
|
|
|
|
>
|
|
|
|
|
{/* Drag indicator */}
|
|
|
|
|
<div className="sm:hidden flex justify-center pt-2 pb-1">
|
|
|
|
|
<div className="w-10 h-1 bg-[rgba(38,37,30,0.2)] rounded-full" />
|
|
|
|
|
</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
|
|
|
|
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 */}
|
|
|
|
|
<div className="flex items-center justify-between px-6 pt-2 pb-5">
|
|
|
|
|
<div className="flex items-center gap-2.5">
|
|
|
|
|
<div className="w-8 h-8 bg-[#e6f1ec] rounded-xl flex items-center justify-center">
|
|
|
|
|
<Check size={18} className="text-[#18714f]" />
|
|
|
|
|
</div>
|
|
|
|
|
<h2 className="text-lg font-bold text-[#26251e]">Collect Item</h2>
|
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
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={onClose}
|
|
|
|
|
aria-label="Close"
|
|
|
|
|
className="p-2 rounded-xl hover:bg-[rgba(38,37,30,0.06)] active:bg-[rgba(38,37,30,0.12)] transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<X size={18} className="text-[rgba(38,37,30,0.55)]" />
|
|
|
|
|
</button>
|
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
|
|
|
|
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
|
|
|
{/* Scrollable body — overflow lives on a child so the form
|
|
|
|
|
can still submit via Enter / native submit semantics. */}
|
|
|
|
|
<div className="flex-1 min-h-0 overflow-y-auto custom-scrollbar px-6 pb-6 space-y-5">
|
|
|
|
|
{/* Item info */}
|
|
|
|
|
<div className="p-4 bg-[#ebeae5] rounded-xl">
|
2026-07-07 15:15:42 +00:00
|
|
|
<p className="text-sm font-semibold text-[#26251e] truncate">{displayDescription}</p>
|
|
|
|
|
{item.sku && (
|
|
|
|
|
<p className="text-[10px] text-[rgba(38,37,30,0.5)] font-mono mt-0.5">#{item.sku}</p>
|
|
|
|
|
)}
|
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="flex items-center gap-3 mt-1.5 text-xs text-[rgba(38,37,30,0.55)]">
|
|
|
|
|
<span className="font-medium text-[#26251e]">
|
|
|
|
|
{item.expand?.supplier?.suppliername || 'Unknown'}
|
|
|
|
|
</span>
|
|
|
|
|
<span>Expected: {expectedQty} units</span>
|
|
|
|
|
</div>
|
|
|
|
|
{item.selling_price && (
|
|
|
|
|
<p className="text-xs text-[#18714f] font-medium mt-1">
|
|
|
|
|
Unit price: {formatCurrency(item.selling_price)}
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2026-05-24 19:51:29 +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
|
|
|
{/* Quantity */}
|
|
|
|
|
<div>
|
|
|
|
|
<label htmlFor="received-qty" className="block text-sm font-semibold text-[#26251e] mb-2">
|
|
|
|
|
Received Quantity *
|
|
|
|
|
</label>
|
|
|
|
|
<div className="flex items-center gap-3 flex-wrap">
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={() => adjustQty(-1)}
|
|
|
|
|
aria-label="Decrease quantity"
|
|
|
|
|
className="w-12 h-12 flex items-center justify-center rounded-xl bg-[#ebeae5] active:bg-[#e1e0db] transition-colors touch-manipulation press-feedback-sm"
|
|
|
|
|
>
|
|
|
|
|
<Minus size={18} className="text-[#26251e]" />
|
|
|
|
|
</button>
|
|
|
|
|
<input
|
|
|
|
|
id="received-qty"
|
|
|
|
|
type="number"
|
|
|
|
|
step="0.01"
|
|
|
|
|
inputMode="decimal"
|
|
|
|
|
value={receivedQty}
|
|
|
|
|
onChange={(e) => { setReceivedQty(e.target.value); setError(''); }}
|
|
|
|
|
className="w-20 text-center text-2xl font-bold py-3 border-2 border-[rgba(38,37,30,0.1)] rounded-xl focus:ring-2 focus:ring-[#f54e00]/30 focus:border-[#f54e00] transition-all bg-[#f2f1ed] text-[#26251e]"
|
|
|
|
|
placeholder="0"
|
|
|
|
|
autoFocus
|
|
|
|
|
/>
|
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
|
|
|
|
|
type="button"
|
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
|
|
|
onClick={() => adjustQty(1)}
|
|
|
|
|
aria-label="Increase quantity"
|
|
|
|
|
className="w-12 h-12 flex items-center justify-center rounded-xl bg-[#ebeae5] active:bg-[#e1e0db] transition-colors touch-manipulation press-feedback-sm"
|
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
|
|
|
<Plus size={18} className="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
|
|
|
</button>
|
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
|
|
|
{expectedQty > 0 && (
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={() => { setReceivedQty(expectedQty.toString()); setError(''); }}
|
|
|
|
|
className="px-3 py-2 text-xs font-medium text-[#125a3e] bg-[#e6f1ec] rounded-lg active:bg-[#c2dfd0] transition-colors touch-manipulation"
|
|
|
|
|
>
|
|
|
|
|
Full ({expectedQty})
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
{isPartial && (
|
|
|
|
|
<p className="text-xs text-[#8a5a02] mt-2 font-medium">
|
|
|
|
|
Partial collection: {parsedQty} of {expectedQty} units
|
|
|
|
|
</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
|
|
|
)}
|
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
|
|
|
{isOverQty && (
|
|
|
|
|
<p className="text-xs text-[#26251e] mt-2 font-medium">
|
|
|
|
|
Over collection: {parsedQty} exceeds expected {expectedQty}
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
2026-05-24 19:51:29 +00:00
|
|
|
</div>
|
|
|
|
|
|
2026-08-02 20:58:08 +00:00
|
|
|
{/* Partial-collection justification. Only shown when the
|
|
|
|
|
driver enters a qty less than expected. We require EITHER
|
|
|
|
|
a picked reason OR a photo so the operator always has
|
|
|
|
|
some audit context for the short pickup. The reason
|
|
|
|
|
flows through into the auto-generated problem note. */}
|
|
|
|
|
{isPartial && (
|
|
|
|
|
<div
|
|
|
|
|
className="p-4 bg-[#fcf3d8] border border-[#f6e3a4] rounded-xl space-y-2"
|
|
|
|
|
role="group"
|
|
|
|
|
aria-label="Partial collection justification"
|
|
|
|
|
>
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<AlertCircle size={14} className="text-[#8a5a02] flex-shrink-0" aria-hidden="true" />
|
|
|
|
|
<p className="text-xs font-semibold text-[#8a5a02]">
|
|
|
|
|
Why are you short? Pick one or take a photo below.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<label htmlFor="partial-reason" className="sr-only">Reason for short collection</label>
|
|
|
|
|
<select
|
|
|
|
|
id="partial-reason"
|
|
|
|
|
value={partialReason}
|
|
|
|
|
onChange={(e) => { setPartialReason(e.target.value); setError(''); }}
|
|
|
|
|
className={cn(
|
|
|
|
|
'w-full p-2.5 text-sm bg-[#f2f1ed] border rounded-lg text-[#26251e] focus:outline-none focus:ring-2 focus:ring-[#f54e00]/30 focus:border-[#f54e00] transition-all',
|
|
|
|
|
partialReason ? 'border-[rgba(38,37,30,0.2)]' : 'border-[#cf2d56]/40'
|
|
|
|
|
)}
|
|
|
|
|
aria-required="true"
|
|
|
|
|
>
|
|
|
|
|
<option value="">Select a reason…</option>
|
|
|
|
|
<option value="Out of stock">Supplier out of stock</option>
|
|
|
|
|
<option value="Wrong item">Wrong item received</option>
|
|
|
|
|
<option value="Quality issue">Quality issue</option>
|
|
|
|
|
<option value="Damaged">Damaged goods</option>
|
|
|
|
|
<option value="Wrong qty packed">Wrong qty packed</option>
|
|
|
|
|
<option value="Other">Other (specify in notes)</option>
|
|
|
|
|
</select>
|
|
|
|
|
{!partialReason && !photo && (
|
|
|
|
|
<p className="text-[10px] text-[#8a5a02]">
|
|
|
|
|
Required: pick a reason or attach a photo below to continue.
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
</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
|
|
|
{/* Notes */}
|
|
|
|
|
<div>
|
|
|
|
|
<label htmlFor="collect-notes" className="block text-sm font-semibold text-[#26251e] mb-2">
|
|
|
|
|
Notes (optional)
|
|
|
|
|
</label>
|
|
|
|
|
<textarea
|
|
|
|
|
id="collect-notes"
|
|
|
|
|
value={notes}
|
|
|
|
|
onChange={(e) => setNotes(e.target.value)}
|
|
|
|
|
className="w-full p-3 text-sm bg-[#f2f1ed] border border-[rgba(38,37,30,0.1)] rounded-xl text-[#26251e] placeholder:text-[rgba(38,37,30,0.4)] focus:ring-2 focus:ring-[#f54e00]/30 focus:border-[#f54e00] focus:bg-[#f7f7f4] transition-all resize-none selectable"
|
|
|
|
|
rows={2}
|
|
|
|
|
placeholder="Add any notes about this collection..."
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2026-07-07 18:43:17 +00:00
|
|
|
|
|
|
|
|
{/* Photo (optional) — proof-of-pickup. Driver takes a
|
|
|
|
|
snapshot of the goods so operators have a visual
|
|
|
|
|
record when a quantity mismatch gets reported later. */}
|
|
|
|
|
<div>
|
|
|
|
|
<div className="flex items-center justify-between mb-2">
|
|
|
|
|
<label className="text-sm font-semibold text-[#26251e]">
|
|
|
|
|
Photo (optional)
|
|
|
|
|
</label>
|
|
|
|
|
<span className="text-[10px] text-[rgba(38,37,30,0.5)] uppercase tracking-wider">
|
|
|
|
|
Proof of pickup
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{photoPreviewUrl ? (
|
|
|
|
|
<div className="relative rounded-xl overflow-hidden border border-[rgba(38,37,30,0.1)] bg-[#f2f1ed]">
|
|
|
|
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
|
|
|
<img
|
|
|
|
|
src={photoPreviewUrl}
|
|
|
|
|
alt="Pickup photo preview"
|
|
|
|
|
className="w-full h-48 object-cover"
|
|
|
|
|
/>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={handleRemovePhoto}
|
|
|
|
|
aria-label="Remove photo"
|
|
|
|
|
className="absolute top-2 right-2 w-9 h-9 rounded-full bg-[rgba(38,37,30,0.85)] text-white flex items-center justify-center active:bg-[rgba(38,37,30,1)] transition-colors touch-manipulation"
|
|
|
|
|
>
|
|
|
|
|
<Trash2 size={16} aria-hidden="true" />
|
|
|
|
|
</button>
|
|
|
|
|
<div className="px-3 py-2 text-[11px] text-[rgba(38,37,30,0.55)] flex items-center justify-between">
|
|
|
|
|
<span className="truncate">{photo?.name}</span>
|
|
|
|
|
<span className="flex-shrink-0 ml-2 font-mono">
|
|
|
|
|
{photo ? `${(photo.size / 1024).toFixed(0)}KB` : ''}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="grid grid-cols-2 gap-2">
|
|
|
|
|
{/* capture="environment" hints the camera (rear-facing)
|
|
|
|
|
on mobile. Desktop browsers ignore the attribute
|
|
|
|
|
and show the standard file picker. */}
|
|
|
|
|
<input
|
|
|
|
|
ref={cameraInputRef}
|
|
|
|
|
type="file"
|
|
|
|
|
accept="image/*"
|
|
|
|
|
capture="environment"
|
|
|
|
|
className="sr-only"
|
|
|
|
|
onChange={(e) => handlePhotoSelected(e.target.files?.[0])}
|
|
|
|
|
/>
|
|
|
|
|
<input
|
|
|
|
|
ref={libraryInputRef}
|
|
|
|
|
type="file"
|
|
|
|
|
accept="image/*"
|
|
|
|
|
className="sr-only"
|
|
|
|
|
onChange={(e) => handlePhotoSelected(e.target.files?.[0])}
|
|
|
|
|
/>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={() => cameraInputRef.current?.click()}
|
|
|
|
|
className="flex flex-col items-center justify-center gap-1.5 py-4 rounded-xl border-2 border-dashed border-[rgba(38,37,30,0.18)] bg-[#ebeae5] active:bg-[#e1e0db] transition-colors touch-manipulation press-feedback-sm min-h-[88px]"
|
|
|
|
|
>
|
|
|
|
|
<Camera size={20} className="text-[#26251e]" aria-hidden="true" />
|
|
|
|
|
<span className="text-xs font-semibold text-[#26251e]">Take Photo</span>
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={() => libraryInputRef.current?.click()}
|
|
|
|
|
className="flex flex-col items-center justify-center gap-1.5 py-4 rounded-xl border-2 border-dashed border-[rgba(38,37,30,0.18)] bg-[#ebeae5] active:bg-[#e1e0db] transition-colors touch-manipulation press-feedback-sm min-h-[88px]"
|
|
|
|
|
>
|
|
|
|
|
<ImagePlus size={20} className="text-[#26251e]" aria-hidden="true" />
|
|
|
|
|
<span className="text-xs font-semibold text-[#26251e]">From Library</span>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{error && <p className="text-xs text-[#b3224a]" role="alert">{error}</p>}
|
2026-05-24 19:51:29 +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
|
|
|
{/* Pinned footer — sits above BottomNav (z-50). pb accounts for
|
|
|
|
|
the 4rem nav height + safe-area inset + visual gap so the
|
|
|
|
|
button never hides under the tab bar. */}
|
|
|
|
|
<div
|
|
|
|
|
className="px-6 pt-3 pb-[calc(4rem+env(safe-area-inset-bottom,0px)+0.75rem)] border-t border-[rgba(38,37,30,0.08)] bg-[#e6e5e0] flex-shrink-0"
|
|
|
|
|
style={{ boxShadow: '0 -8px 24px rgba(0,0,0,0.06)' }}
|
2026-05-24 19:51:29 +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
|
|
|
<button
|
|
|
|
|
type="submit"
|
2026-08-02 20:58:08 +00:00
|
|
|
disabled={!parsedQty || parsedQty < 0 || (isPartial && !partialJustified)}
|
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={cn(
|
|
|
|
|
'w-full py-4 px-4 rounded-xl text-white font-semibold text-[15px]',
|
|
|
|
|
'bg-[#1f8a65] active:bg-[#125a3e] transition-all',
|
|
|
|
|
'touch-manipulation press-feedback min-h-[56px]',
|
2026-08-02 20:58:08 +00:00
|
|
|
(!parsedQty || parsedQty < 0 || (isPartial && !partialJustified)) && 'opacity-50'
|
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
|
|
|
)}
|
|
|
|
|
style={{ boxShadow: '0 4px 12px rgba(31,138,101,0.3)' }}
|
|
|
|
|
>
|
|
|
|
|
Confirm Collection
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2026-05-24 19:51:29 +00:00
|
|
|
</form>
|
|
|
|
|
</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
|
|
|
}
|