Skip to content

Commit 053dda7

Browse files
committed
feat: add initial history entry for non-pending delivery status and update timeline events for better status representation
1 parent 89947c4 commit 053dda7

File tree

4 files changed

+114
-77
lines changed

4 files changed

+114
-77
lines changed

api/controllers/delivery.controller.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,21 @@ export const createDelivery = async (req, res) => {
4747

4848
const delivery = await Delivery.create(value);
4949

50+
// If the delivery is created with a status other than "Pending",
51+
// add an initial history entry to track when this status was set
52+
if (value.status && value.status !== "Pending") {
53+
const initialHistoryEntry = {
54+
description: `Initial status set to ${value.status}`,
55+
status: value.status,
56+
time: new Date(),
57+
updateDate: new Date().toISOString().slice(0, 10),
58+
updateTime: new Date().toTimeString().slice(0, 5),
59+
};
60+
61+
delivery.history.push(initialHistoryEntry);
62+
await delivery.save();
63+
}
64+
5065
// Note: PDF generation is now handled on the frontend with jsPDF
5166
// Invoice URL will be set when PDF is generated and uploaded via the invoice controller
5267
let invoiceUrl = null;
@@ -333,14 +348,14 @@ export const updateStatusAndLocation = async (req, res) => {
333348
updateObj.status = value.status;
334349
}
335350

336-
// Build location update if any location data is provided
351+
// Build location update if status is provided OR any location data is provided
337352
if (
353+
value.status ||
338354
value.description ||
339355
value.city ||
340356
value.country ||
341357
value.lat ||
342-
value.lng ||
343-
value.status
358+
value.lng
344359
) {
345360
locationUpdate.description =
346361
value.description ||
@@ -360,6 +375,7 @@ export const updateStatusAndLocation = async (req, res) => {
360375
lng: value.lng,
361376
};
362377
}
378+
// Always add status to history entry if provided
363379
if (value.status) {
364380
locationUpdate.status = value.status;
365381
}

client/src/Admin/pages/TimelineComponent.tsx

Lines changed: 70 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ const TimelineComponent: React.FC<TimelineComponentProps> = ({ shipment }) => {
7878
return <Pause className="w-4 h-4" />;
7979
case "delivered":
8080
return <CheckCircle className="w-4 h-4" />;
81+
case "pending-delivery":
82+
return <Clock className="w-4 h-4" />;
8183
default:
8284
return <AlertCircle className="w-4 h-4" />;
8385
}
@@ -98,6 +100,8 @@ const TimelineComponent: React.FC<TimelineComponentProps> = ({ shipment }) => {
98100
return "text-orange-400 bg-orange-500";
99101
case "delivered":
100102
return "text-green-400 bg-green-500";
103+
case "pending-delivery":
104+
return "text-gray-400 bg-gray-500";
101105
default:
102106
return "text-gray-400 bg-gray-500";
103107
}
@@ -123,15 +127,6 @@ const TimelineComponent: React.FC<TimelineComponentProps> = ({ shipment }) => {
123127
});
124128
};
125129

126-
const STATUS_ORDER = [
127-
"pending",
128-
"processing",
129-
"shipped",
130-
"in transit",
131-
"on hold",
132-
"delivered",
133-
];
134-
135130
const STATUS_LABELS: Record<string, string> = {
136131
pending: "Order Confirmed & Pending",
137132
processing: "Processing",
@@ -141,26 +136,16 @@ const TimelineComponent: React.FC<TimelineComponentProps> = ({ shipment }) => {
141136
delivered: "Delivered",
142137
};
143138

144-
// Build timeline events
139+
// Build timeline events - chronological order based on actual history
145140
const buildTimeline = (): TimelineEvent[] => {
146141
const events: TimelineEvent[] = [];
147142
const sentDate = new Date(shipment.dateSent);
148143
const deliveryDate = new Date(shipment.deliveryDate);
149144

150-
// Group history by status (case-insensitive)
151-
const historyByStatus: Record<string, LocationUpdate[]> = {};
152-
(Array.isArray(shipment.history) ? shipment.history : []).forEach(
153-
(entry) => {
154-
const status = entry.status?.toLowerCase() || "pending";
155-
if (!historyByStatus[status]) historyByStatus[status] = [];
156-
historyByStatus[status].push(entry);
157-
}
158-
);
159-
160-
// Always add Pending (first)
145+
// Always start with creation/pending event
161146
events.push({
162-
id: "pending",
163-
title: STATUS_LABELS["pending"],
147+
id: "created",
148+
title: "Order Created & Confirmed",
164149
location: `${shipment.sender?.city || "Unknown"}, ${
165150
shipment.sender?.country || "Unknown"
166151
}`,
@@ -171,49 +156,66 @@ const TimelineComponent: React.FC<TimelineComponentProps> = ({ shipment }) => {
171156
isFixed: true,
172157
});
173158

174-
// Add only statuses that exist in history (except pending/delivered)
175-
STATUS_ORDER.slice(1, -1).forEach((status) => {
176-
const entries = historyByStatus[status] || [];
177-
if (entries.length > 0) {
178-
// Use the latest entry for this status
179-
const latest = entries.reduce((a, b) =>
180-
new Date(a.time).getTime() > new Date(b.time).getTime() ? a : b
181-
);
182-
events.push({
183-
id: status,
184-
title:
185-
STATUS_LABELS[status] +
186-
(latest.description ? ` - ${latest.description}` : ""),
187-
location:
188-
latest.city && latest.country
189-
? `${latest.city}, ${latest.country}`
190-
: latest.location
191-
? `${latest.location.lat?.toFixed(2) ?? ""}, ${
192-
latest.location.lng?.toFixed(2) ?? ""
193-
}`
194-
: "Unknown",
195-
date: latest.time ? formatDate(latest.time) : "",
196-
time: latest.time ? formatTime(latest.time) : "",
197-
status,
198-
completed: true,
199-
isFixed: false,
200-
});
201-
}
202-
});
159+
// Add all history entries in chronological order
160+
if (Array.isArray(shipment.history) && shipment.history.length > 0) {
161+
// Sort history by time (chronological order)
162+
const sortedHistory = [...shipment.history].sort(
163+
(a, b) => new Date(a.time).getTime() - new Date(b.time).getTime()
164+
);
203165

204-
// Always add Delivered (last)
205-
events.push({
206-
id: "delivered",
207-
title: STATUS_LABELS["delivered"],
208-
location: `${shipment.receiver?.city || "Unknown"}, ${
209-
shipment.receiver?.country || "Unknown"
210-
}`,
211-
date: formatDate(deliveryDate),
212-
time: formatTime(deliveryDate),
213-
status: "delivered",
214-
completed: shipment.status?.toLowerCase() === "delivered",
215-
isFixed: true,
216-
});
166+
sortedHistory.forEach((entry, index) => {
167+
if (entry.status) {
168+
const entryTime = new Date(entry.time);
169+
events.push({
170+
id: `history-${index}`,
171+
title: STATUS_LABELS[entry.status.toLowerCase()] || entry.status,
172+
location:
173+
entry.city && entry.country
174+
? `${entry.city}, ${entry.country}`
175+
: entry.location
176+
? `${entry.location.lat?.toFixed(2) ?? ""}, ${
177+
entry.location.lng?.toFixed(2) ?? ""
178+
}`
179+
: entry.description || "Unknown",
180+
date: formatDate(entryTime),
181+
time: formatTime(entryTime),
182+
status: entry.status.toLowerCase(),
183+
completed: true,
184+
isFixed: false,
185+
});
186+
}
187+
});
188+
}
189+
190+
// Add delivery event (only if status is delivered)
191+
if (shipment.status?.toLowerCase() === "delivered") {
192+
events.push({
193+
id: "delivered",
194+
title: STATUS_LABELS["delivered"],
195+
location: `${shipment.receiver?.city || "Unknown"}, ${
196+
shipment.receiver?.country || "Unknown"
197+
}`,
198+
date: formatDate(deliveryDate),
199+
time: formatTime(deliveryDate),
200+
status: "delivered",
201+
completed: true,
202+
isFixed: true,
203+
});
204+
} else {
205+
// Add expected delivery as pending event
206+
events.push({
207+
id: "expected-delivery",
208+
title: "Expected Delivery",
209+
location: `${shipment.receiver?.city || "Unknown"}, ${
210+
shipment.receiver?.country || "Unknown"
211+
}`,
212+
date: formatDate(deliveryDate),
213+
time: formatTime(deliveryDate),
214+
status: "pending-delivery",
215+
completed: false,
216+
isFixed: true,
217+
});
218+
}
217219

218220
return events;
219221
};
@@ -334,11 +336,11 @@ const TimelineComponent: React.FC<TimelineComponentProps> = ({ shipment }) => {
334336
<div className="mt-8 p-4 bg-gray-50 dark:bg-zinc-800/30 border border-gray-200 dark:border-zinc-700 rounded-lg">
335337
<div className="flex justify-between items-center text-sm">
336338
<span className="text-gray-600 dark:text-zinc-400">
337-
Total Updates: {timeline.length - 2}{" "}
338-
{/* Exclude fixed pending/delivery */}
339+
Total Updates: {timeline.filter((event) => !event.isFixed).length}{" "}
340+
{/* Exclude fixed creation/delivery events */}
339341
</span>
340342
<span className="text-gray-600 dark:text-zinc-400">
341-
Status:{" "}
343+
Current Status:{" "}
342344
<span className="text-gray-900 dark:text-white font-medium capitalize">
343345
{shipment.status}
344346
</span>

client/src/components/EditShipmentForm.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ const EditShipmentForm: React.FC<ShipmentFormProps> = ({
130130

131131
// Check dates
132132
if (!formData.pickupDate) {
133-
errors.push("Pickup date is required");
133+
// Auto-set pickup date to today if not provided
134+
const today = new Date().toISOString();
135+
setFormData((prev) => ({ ...prev, pickupDate: today }));
134136
}
135137
if (!formData.deliveryDate) {
136138
errors.push("Delivery date is required");
@@ -225,12 +227,21 @@ const EditShipmentForm: React.FC<ShipmentFormProps> = ({
225227
...formData.receiver,
226228
...parseLocation(formData.receiver.location),
227229
};
228-
const payload = {
230+
231+
// Transform dates to ISO strings, use today for pickup if not provided
232+
const transformedData = {
229233
...formData,
230234
sender,
231235
receiver,
236+
dateSent: formData.pickupDate
237+
? new Date(formData.pickupDate).toISOString()
238+
: new Date().toISOString(), // Use today's date as fallback
239+
deliveryDate: formData.deliveryDate
240+
? new Date(formData.deliveryDate).toISOString()
241+
: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(), // 7 days from now as fallback
232242
};
233-
onSubmit?.(payload);
243+
244+
onSubmit?.(transformedData);
234245
};
235246

236247
const steps = [
@@ -347,6 +358,9 @@ const EditShipmentForm: React.FC<ShipmentFormProps> = ({
347358
<div>
348359
<label className="block text-zinc-900 dark:text-white text-sm font-medium mb-2">
349360
Pickup Date
361+
<span className="text-zinc-500 dark:text-zinc-400 text-xs ml-2">
362+
(Today's date will be used if not selected)
363+
</span>
350364
</label>
351365
<DatePickerDemo
352366
value={formData.pickupDate}
@@ -830,7 +844,7 @@ const EditShipmentForm: React.FC<ShipmentFormProps> = ({
830844
Pickup Date:
831845
</span>
832846
<span className="text-zinc-900 dark:text-white">
833-
{formData.pickupDate || "Not selected"}
847+
{formData.pickupDate || "Today's date"}
834848
</span>
835849
</div>
836850
<div className="flex justify-between">

client/src/components/ShipmentForm.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
136136

137137
// Check dates
138138
if (!formData.pickupDate) {
139-
errors.push("Pickup date is required");
139+
// Auto-set pickup date to today if not provided
140+
const today = new Date().toISOString();
141+
setFormData((prev) => ({ ...prev, pickupDate: today }));
140142
}
141143
if (!formData.deliveryDate) {
142144
errors.push("Delivery date is required");
@@ -359,6 +361,9 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
359361
<div>
360362
<label className="block text-zinc-900 dark:text-white text-sm font-medium mb-2">
361363
Pickup Date
364+
<span className="text-zinc-500 dark:text-zinc-400 text-xs ml-2">
365+
(Today's date will be used if not selected)
366+
</span>
362367
</label>
363368
<DatePickerDemo
364369
value={formData.pickupDate}
@@ -827,7 +832,7 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({
827832
<div className="flex justify-between">
828833
<span className="text-zinc-400">Pickup Date:</span>
829834
<span className="text-white">
830-
{formData.pickupDate || "Not selected"}
835+
{formData.pickupDate || "Today's date"}
831836
</span>
832837
</div>
833838
<div className="flex justify-between">

0 commit comments

Comments
 (0)