From 74e44c034e5a06d43774dcfdd375facbbb4fe395 Mon Sep 17 00:00:00 2001 From: vickytechkey Date: Fri, 18 Sep 2026 19:16:12 +0530 Subject: [PATCH] feat: add order views for rescheduling pickups, handover manifests, and damage reporting --- .../orders/HandoverManifestView.tsx | 233 ++++++++++++ src/components/orders/OrderDetailView.tsx | 353 +++++++++--------- src/components/orders/ReportDamageView.tsx | 334 +++++++++++++++++ .../orders/ReschedulePickupView.tsx | 303 +++++++++++++++ src/pages/dashboard/OrdersPage.tsx | 94 ++++- 5 files changed, 1144 insertions(+), 173 deletions(-) create mode 100644 src/components/orders/HandoverManifestView.tsx create mode 100644 src/components/orders/ReportDamageView.tsx create mode 100644 src/components/orders/ReschedulePickupView.tsx diff --git a/src/components/orders/HandoverManifestView.tsx b/src/components/orders/HandoverManifestView.tsx new file mode 100644 index 0000000..374d7a8 --- /dev/null +++ b/src/components/orders/HandoverManifestView.tsx @@ -0,0 +1,233 @@ +import React, { useState } from 'react'; +import { + ChevronRight, + PackageCheck, + CheckCircle2, + Truck, + Calendar, + Check, + CheckCheck +} from 'lucide-react'; + +interface HandoverManifestViewProps { + onBack: () => void; + onTabChange: (tab: string) => void; +} + +export const HandoverManifestView: React.FC = ({ onBack, onTabChange }) => { + const [allSelected, setAllSelected] = useState(true); + const [isConfirming, setIsConfirming] = useState(false); + const [isConfirmed, setIsConfirmed] = useState(false); + + const mockPackages = [ + { awb: 'DLV-88491024-IN', item: 'Jaipur Cobalt Ceramic Plates (Set of 4)', orderId: 'TRD-9031', dest: 'Bengaluru, Karnataka', pin: '560001', weight: '2.4 kg' }, + { awb: 'DLV-88491025-IN', item: 'Bidriware Silver Inlay Fluted Vase', orderId: 'TRD-9038', dest: 'New Delhi, Delhi', pin: '110001', weight: '1.8 kg' }, + { awb: 'DLV-88491026-IN', item: 'Banarasi Katan Silk Saree (Heirloom)', orderId: 'TRD-9042', dest: 'Mumbai, Maharashtra', pin: '400050', weight: '0.9 kg' }, + { awb: 'DLV-88491027-IN', item: 'Gorakhpur Terracotta Planters (Pair)', orderId: 'TRD-9049', dest: 'Hyderabad, Telangana', pin: '500034', weight: '4.6 kg' }, + { awb: 'DLV-88491028-IN', item: 'Saharanpur Sheesham Carved Bowls (Trio)', orderId: 'TRD-9055', dest: 'Chennai, Tamil Nadu', pin: '600028', weight: '1.7 kg' }, + ]; + + const handleConfirm = () => { + setIsConfirming(true); + setTimeout(() => { + setIsConfirming(false); + setIsConfirmed(true); + }, 1200); + }; + + return ( +
+ {/* Breadcrumb & Top Bar */} +
+
+
+ Orders & Dispatch + + Manifest #TRD-2025-MNF-0841 +
+

3PL Courier Handover Manifest

+
+ Delhivery Express + • + Today, Oct 24 • Window: 2:30 PM – 3:30 PM + • + Surface & Air Sweep +
+
+
+ + {/* Header Tabs */} +
+ + + + + +
+ + {/* Manifest Summary Bento Cards */} +
+
+
+ Total Parcels +
+
14
+
All packed & labeled
+
+
+
+
+
+ Combined Weight +
+
+ 18.2 kg +
+
Volumetric verified
+
+
+
+
+
+ Assigned Carrier +
+
Delhivery Express
+
Priority Pickup Hub #IND-45
+
+
+
+
+ + {/* Manifest Table Card */} +
+
+
+

Package List

+ 14 items +
+
+ Mark items verified upon physical sweep + +
+
+ +
+ + + + + + + + + + + + + {mockPackages.map((pkg, idx) => ( + + + + + + + + + ))} + +
+ setAllSelected(!allSelected)} style={{ width: '16px', height: '16px', accentColor: '#5a0118', cursor: 'pointer' }} /> + AWB NumberCraft ItemDestinationWeightDriver Check
+ + +
{pkg.awb}
+
+
{pkg.item}
+
Order #{pkg.orderId}
+
+
{pkg.dest}
+
{pkg.pin}
+
+
{pkg.weight}
+
+
+ Verified +
+
+
+ +
+ Showing 5 of 14 verified packages. Batch validated for courier pickup. + All 14 parcels scanned +
+
+ + {/* Primary Confirmation Footer & Floating Action */} +
+
+
+ +
+
+
Ready for Dispatch
+
Both signatures recorded. Completing handover sends instant delivery updates.
+
+
+
+ + +
+
+ + +
+ ); +}; diff --git a/src/components/orders/OrderDetailView.tsx b/src/components/orders/OrderDetailView.tsx index 6929d3c..2d52fbb 100644 --- a/src/components/orders/OrderDetailView.tsx +++ b/src/components/orders/OrderDetailView.tsx @@ -3,13 +3,19 @@ import { OrderItem } from '../../types/seller'; import { ChevronRight, Printer, - Calendar, - Truck, CheckCircle2, Check, Award, - PackageCheck, - Gift + Gift, + Grid, + RefreshCw, + Clock, + AlertTriangle, + Star, + MapPin, + Box, + Bike, + Truck } from 'lucide-react'; interface OrderDetailViewProps { @@ -17,116 +23,115 @@ interface OrderDetailViewProps { onBack: () => void; onPrintLabel: (order: OrderItem) => void; onReschedule: (order: OrderItem) => void; + onTabChange?: (tab: string) => void; } -export const OrderDetailView: React.FC = ({ order, onBack, onPrintLabel, onReschedule }) => { +export const OrderDetailView: React.FC = ({ order, onBack, onPrintLabel, onReschedule, onTabChange }) => { return (
+ {/* Top Navigation & Breadcrumbs */} -
-
- Orders - - Order {order.orderNumber} +
+
+ Orders + + Order #{order.orderNumber}
- - + + {order.courierPartner || 'Delhivery Priority Air'} - AWB: {order.awbNumber} + AWB: {order.awbNumber}
{/* Header Tabs */} -
- - - - +
{/* Header Hero Card */} -
+

Order {order.orderNumber}

- - - Ready for Pickup + + Ready for Pickup - B2C Retail + B2C Retail
-
+
Placed yesterday at 4:15 PM - Courier pickup window: {order.pickupSlot || 'Today, 2:30 – 3:30 PM'} + Courier pickup window: {order.pickupSlot || 'Today, 2:30 – 3:30 PM'}
- -
{/* Minimal 3-Step Timeline */} -
-
+
+
-
+
1. Order Placed
-
Yesterday, 4:15 PM
-
- Payment in Escrow +
Yesterday, 4:15 PM
+
+ Payment in Escrow
-
+
2. Packed & Verified
-
Today, 1:10 PM
-
- Box Barcode #90814 +
Today, 1:10 PM
+
+ Box Barcode #90814
-
-
+
+
3. Out for Pickup
Arriving by 3:30 PM
-
- pin OTP: 7491 +
+ OTP: 7491
@@ -135,172 +140,186 @@ export const OrderDetailView: React.FC = ({ order, onBack,
{/* 2-Column Split: Item & Payout + Delivery & Packing Details */} -
- {/* Left Col: Handcrafted Item & Checklist */} -
+
+ {/* Left Col: Handcrafted Item & Payout */} +
+ {/* Product Card */} -
-
-

Consignment Item

- 1 Box (2.4 kg) +
+
+

Consignment Item

+ 1 Box (2.4 kg)
-
-
- Craft Product - Set of 4 +
+
+ {order.productName} + Set of 4
-
- GI-RAJ-0042 - SKU: JPR-BLU-084 + GI-RAJ-0042 + SKU: JPR-BLU-084
-

- {order.productTitle || 'Jaipur Royal Cobalt Ceramic Dinner Plates'} -

-

High-fire quartz terracotta blend with lead-free traditional indigo glaze.

-
- Quantity: {order.quantity} set +

{order.productName}

+

High-fire quartz terracotta blend with lead-free traditional indigo glaze.

+
+ Quantity: 1 set • - Unit Price: ₹{(order.amount / (order.quantity || 1)).toLocaleString()} + Unit Price: {order.orderValue}
{/* Clean Financial Summary */} -
-
-
-
Order Value
-
₹{order.amount.toLocaleString()}.00
+
+
+
+ Order Value + {order.orderValue}
-
-
-
TradHox Commission
-
₹0.00 (0%)
+
+
+ TradHox Commission + ₹0.00 (0%)
-
-
-
Net Maker Payout
-
₹{order.amount.toLocaleString()}.00
+
+
+ Net Maker Payout + {order.orderValue}
{/* Simplified 3-Item Packing Checklist */} -
-
-
-

Pre-Dispatch Checklist

-

Artisanal packing safety verified for transit.

+
+
+
+

Pre-Dispatch Checklist

+

Artisanal packing safety verified for transit.

- 3 of 3 Verified + 3 of 3 Verified
-
-
-
- - Acoustic tap test passed (zero micro-cracks) +
+
+
+ + Acoustic tap test passed (zero micro-cracks)
- Glaze OK -
-
-
- - Double honeycomb craft cushioning applied -
- Protected + Glaze OK
-
- - GI authenticity hologram and fragile sticker attached +
+ + Double honeycomb craft cushioning applied
- Holo #9912 + Protected +
+
+
+ + GI authenticity hologram and fragile sticker attached +
+ Holo #9912
+
{/* Right Col: Customer, Gift Note & Courier Handshake */} -
-
- {/* Delivery & Patron */} -
-
- Recipient - - 5th Order Patron - -
-
{order.customerName}
-
- #402, Samriddhi Enclave, 14th Main, 4th Block, Koramangala, {order.customerCity}, {order.customerState} – 560034 -
-
- +91 98450 XXXXX - Masked -
+
+ + {/* Delivery & Patron */} +
+
+ Recipient + + 5th Order Patron +
- - {/* Customer Gift Note Card */} -
-
- - Customer Gift Note -
-

- “Please handle with care, this is an anniversary present!” -

-
- Gift note printed on dispatch card -
+
{order.customerName}
+
+ {order.shippingAddress || '#402, Samriddhi Enclave, 14th Main, 4th Block, Koramangala, Bengaluru, Karnataka – 560034'}
- - {/* Courier Handshake */} -
-
Courier Handover
-
-
-
Ramesh Kumar
-
Delhivery Express • KA-01-MJ-4192
-
-
- -
-
- -
- Verification OTP: - 7491 -
-

Share OTP only after parcel barcode is scanned

+
+ +91 98450 XXXXX + Masked
+ + {/* Customer Gift Note Card */} +
+
+ + Customer Gift Note +
+

+ “Please handle with care, this is an anniversary present!” +

+
+ Gift note printed on dispatch card +
+
+ + {/* Courier Handshake */} +
+
Courier Handover
+
+
+
Ramesh Kumar
+
Delhivery Express • KA-01-MJ-4192
+
+
+ +
+
+
+ Verification OTP: + 7491 +
+

Share OTP only after parcel barcode is scanned

+
+
- + +
+ ); +}; diff --git a/src/components/orders/ReschedulePickupView.tsx b/src/components/orders/ReschedulePickupView.tsx new file mode 100644 index 0000000..bc2752e --- /dev/null +++ b/src/components/orders/ReschedulePickupView.tsx @@ -0,0 +1,303 @@ +import React, { useState } from 'react'; +import { + ArrowLeft, + Grid, + RefreshCw, + Clock, + AlertTriangle, + Package, + CheckCircle2, + Calendar, + Truck, + Edit +} from 'lucide-react'; +import { OrderItem } from '../../types/seller'; + +interface ReschedulePickupViewProps { + onBack: () => void; + onTabChange?: (tab: string) => void; + order?: OrderItem | null; // Optional: if provided, reschedule for single order, else batch +} + +export const ReschedulePickupView: React.FC = ({ onBack, onTabChange, order }) => { + const [reason, setReason] = useState('curing'); + const [slot, setSlot] = useState('morning'); + const [dateType, setDateType] = useState('tomorrow'); + + const reasons = [ + { id: 'curing', label: 'Handcrafts Still Drying / Finishing', desc: 'Extra time needed for kiln curing or natural dye drying.' }, + { id: 'adding_orders', label: 'Adding More Orders to Batch', desc: 'Packing more finished crafts to ship together.' }, + { id: 'closed', label: 'Workshop Temporarily Closed', desc: 'Artisan master or team unavailable today.' }, + { id: 'weather', label: 'Weather / Heavy Rain in Cluster', desc: 'Protecting consignments from humidity or rain.' }, + { id: 'inaccessible', label: 'Workshop Inaccessible', desc: 'Local craft corridor or market temporarily blocked.' }, + { id: 'other', label: 'Other Reason', desc: 'Briefly let the logistics team know what changed.' } + ]; + + const slots = [ + { id: 'morning', label: 'Morning (10:00 AM – 1:00 PM)', desc: 'Delhivery Express & Air Linehaul', tag: 'Fastest Delivery' }, + { id: 'afternoon', label: 'Afternoon (2:00 PM – 5:00 PM)', desc: 'Standard ground carrier sweep', tag: 'Standard', tagBg: 'transparent' }, + { id: 'evening', label: 'Evening (5:00 PM – 7:00 PM)', desc: 'Regional depot consolidation run', tag: 'Evening Run', tagBg: 'transparent' } + ]; + + return ( +
+ + {/* Top Context Navigation Strip */} +
+
+
+ + Back to Orders & Shipping + + / + Jaipur Pottery & Handloom Cluster +
+ +
+
+

Reschedule Courier Pickup

+

Choose a new pickup date and time that fits your workshop schedule.

+
+
+ + Batch of 14 Orders ready +
+
+ + +
+
+ + {/* Main Workspace: 2-Column Desktop Grid */} +
+
+ +
+ + {/* Current Booking */} +
+
+
+ +
+
+ Current Booking + Today, 2:30 PM – 3:30 PM + 14 orders ready for Delhivery collection +
+
+ + + Active Pickup + +
+ + {/* Step 1 */} +
+
+ Step 1 +

1. Why do you need to reschedule?

+
+ +
+ {reasons.map((r) => { + const isSelected = reason === r.id; + return ( +
+ +
+ ); + })} +
+
+ + {/* Step 2 */} +
+
+
+ Step 2 +

2. When should the courier come?

+
+ Free Doorstep Pickup +
+ +
+ + + +
+ +
+ {slots.map((s) => { + const isSelected = slot === s.id; + return ( + + ); + })} +
+ +
+ Free doorstep pickup · No penalty or cancellation fees +
+
+ + {/* Step 3 */} +
+
+ Step 3 +

3. Instructions for Driver (Optional)

+
+
+ + +
+
+ +
+ +
+
+
+
+ Reschedule Summary + +
+ +
+
+ Consignment + {order ? '1 Package' : '14 Packages'} +
+
+ Pickup Location + Jaipur Pottery Hub +
+
+ Selected New Slot + + {dateType === 'tomorrow' ? 'Tomorrow' : 'Custom'}, {slot === 'morning' ? '10:00 AM – 1:00 PM' : slot === 'afternoon' ? '2:00 PM – 5:00 PM' : '5:00 PM – 7:00 PM'} + +
+
+ +
+ +
+ Tradhox Artisan Promise +

100% protection on courier adjustments. No penalties for handmade delays.

+
+
+ +
+ + +
+ +

+ Buyer notifications will be automatically updated with the revised dispatch schedule. +

+
+
+
+ +
+
+ + +
+ ); +}; diff --git a/src/pages/dashboard/OrdersPage.tsx b/src/pages/dashboard/OrdersPage.tsx index bfa0355..33ede92 100644 --- a/src/pages/dashboard/OrdersPage.tsx +++ b/src/pages/dashboard/OrdersPage.tsx @@ -1,7 +1,10 @@ +import { ReschedulePickupView } from "../../components/orders/ReschedulePickupView"; +import { ReportDamageView } from "../../components/orders/ReportDamageView"; import React, { useState } from 'react'; import { useSeller } from '../../context/SellerContext'; import { OrderItem } from '../../types/seller'; import { OrderDetailView } from '../../components/orders/OrderDetailView'; +import { HandoverManifestView } from '../../components/orders/HandoverManifestView'; import { Printer, Calendar, @@ -19,6 +22,9 @@ export const OrdersPage: React.FC = () => { const { orders, reschedulePickup } = useSeller(); const [activeTab, setActiveTab] = useState<'all' | OrderItem['status']>('all'); const [activeOrder, setActiveOrder] = useState(null); + const [showManifest, setShowManifest] = useState(false); + const [showReschedule, setShowReschedule] = useState(false); + const [showReportDamage, setShowReportDamage] = useState(false); // Modals state const [selectedOrderForLabel, setSelectedOrderForLabel] = useState(null); @@ -43,13 +49,89 @@ export const OrdersPage: React.FC = () => { alert('Generating combined PDF batch shipping manifest for 14 ready orders.'); }; + if (showManifest) { + return ( + setShowManifest(false)} + onTabChange={(tab) => { + if (tab === 'overall') { + setShowManifest(false); + setActiveTab('all'); + } else if (tab === 'ready_for_pickup') { + setShowManifest(false); + setActiveTab('ready_to_ship'); + } + }} + /> + ); + } + + if (showReschedule) { + return ( + setShowReschedule(false)} + onTabChange={(tab) => { + if (tab === 'overall') { + setShowReschedule(false); + setActiveTab('all'); + } else if (tab === 'ready_for_pickup') { + setShowReschedule(false); + setActiveTab('ready_to_ship'); + } else if (tab === 'handover') { + setShowReschedule(false); + setShowManifest(true); + } + }} + /> + ); + } + + if (showReportDamage) { + return ( + setShowReportDamage(false)} + onTabChange={(tab) => { + if (tab === 'overall') { + setShowReportDamage(false); + setActiveTab('all'); + } else if (tab === 'ready_for_pickup') { + setShowReportDamage(false); + setActiveTab('ready_to_ship'); + } else if (tab === 'handover') { + setShowReportDamage(false); + setShowManifest(true); + } else if (tab === 'reschedule') { + setShowReportDamage(false); + setShowReschedule(true); + } + }} + /> + ); + } + if (activeOrder) { return ( setActiveOrder(null)} onPrintLabel={setSelectedOrderForLabel} - onReschedule={setRescheduleOrder} + onReschedule={() => { + setActiveOrder(null); + setShowReschedule(true); + }} + onTabChange={(tab) => { + if (tab === 'handover') { + setActiveOrder(null); + setShowManifest(true); + } else if (tab === 'overall') { + setActiveOrder(null); + setActiveTab('all'); + } else if (tab === 'report_damage') { + setActiveOrder(null); + setShowReportDamage(true); + } + }} /> ); } @@ -70,7 +152,7 @@ export const OrdersPage: React.FC = () => {

- @@ -91,15 +173,15 @@ export const OrdersPage: React.FC = () => { Ready for pickup - - - @@ -126,7 +208,7 @@ export const OrdersPage: React.FC = () => {
- + Call Driver