This commit is contained in:
2026-03-09 12:02:24 -04:00
parent 3339160d96
commit 63ee52adc4
16 changed files with 93 additions and 34 deletions

BIN
.DS_Store vendored

Binary file not shown.

BIN
app/.DS_Store vendored

Binary file not shown.

View File

@@ -130,13 +130,40 @@ exports.updateRoute = (req, res) => {
});
}
const id = req.params.id;
RoutePath.findByIdAndUpdate(id, req.body, { useFindAndModify: false })
.then(data => {
if (!data) {
res.status(404).send({
RoutePath.findById(id)
.then((currentRoute) => {
if (!currentRoute) {
return res.status(404).send({
message: `Cannot update Route with id=${id}. Maybe Route was not found!`
});
} else res.send({ success: true, message: "Route was updated successfully." });
}
const updatePayload = Object.assign({}, req.body);
delete updatePayload.id;
// Safety: only allow customer_route_status updates for customers that
// already belong to this specific route id.
if (Array.isArray(updatePayload.route_customer_list) && Array.isArray(currentRoute.route_customer_list)) {
const customerIdsInCurrentRoute = new Set(
currentRoute.route_customer_list.map((c) => `${c.customer_id || c._id || ''}`)
);
updatePayload.route_customer_list = updatePayload.route_customer_list.map((customer) => {
const customerId = `${customer.customer_id || customer._id || ''}`;
if (customer.customer_route_status && customerId && !customerIdsInCurrentRoute.has(customerId)) {
const sanitizedCustomer = Object.assign({}, customer);
delete sanitizedCustomer.customer_route_status;
return sanitizedCustomer;
}
return customer;
});
}
return RoutePath.findByIdAndUpdate(id, updatePayload, { useFindAndModify: false });
})
.then((data) => {
if (!data) return;
return res.send({ success: true, message: "Route was updated successfully." });
})
.catch(err => {
res.status(500).send({

View File

@@ -1,16 +1,16 @@
{
"files": {
"main.css": "/static/css/main.46cc12be.css",
"main.js": "/static/js/main.3066488d.js",
"main.js": "/static/js/main.c9552441.js",
"static/js/787.c4e7f8f9.chunk.js": "/static/js/787.c4e7f8f9.chunk.js",
"static/media/landing.png": "/static/media/landing.d4c6072db7a67dff6a78.png",
"index.html": "/index.html",
"main.46cc12be.css.map": "/static/css/main.46cc12be.css.map",
"main.3066488d.js.map": "/static/js/main.3066488d.js.map",
"main.c9552441.js.map": "/static/js/main.c9552441.js.map",
"787.c4e7f8f9.chunk.js.map": "/static/js/787.c4e7f8f9.chunk.js.map"
},
"entrypoints": [
"static/css/main.46cc12be.css",
"static/js/main.3066488d.js"
"static/js/main.c9552441.js"
]
}

View File

@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"><link rel="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.3066488d.js"></script><link href="/static/css/main.46cc12be.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"><link rel="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.c9552441.js"></script><link href="/static/css/main.46cc12be.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
client/.DS_Store vendored

Binary file not shown.

View File

@@ -217,6 +217,7 @@ const DailyTemplatesList = () => {
customer_leave_center_time: null,
customer_pickup_time: null,
customer_dropoff_time: null,
customer_route_status: null,
})) || [];
return {

View File

@@ -394,6 +394,17 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
return PERSONAL_ROUTE_STATUS_TEXT[PERSONAL_ROUTE_STATUS.NO_STATUS];
}
const getDisplayPickupOrder = (pickupOrder) => {
if (pickupOrder === undefined || pickupOrder === null || pickupOrder === '') {
return '';
}
const numericValue = Number(pickupOrder);
if (Number.isNaN(numericValue)) {
return pickupOrder;
}
return numericValue + 1;
}
const generateRouteReportData = () => {
const title = ['', '', `Route(路线): ${transRoutes[0].name} Driver(司机): ${driverName} Vehicle(车号): ${vehicle?.vehicle_number} Date(日期): ${transRoutes[0]?.schedule_date}`]
const signature = ['', '', `Driver's Signature(司机签字): ________________________ Manager's Signature(经理签字): ______________________`]
@@ -812,7 +823,7 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
{ customer.customer_special_needs }
</td>)}
{showCompletedInfo && (<td>
{ customer.customer_pickup_order }
{ getDisplayPickupOrder(customer.customer_pickup_order) }
</td>)}
{showCompletedInfo && (<td>
{ customer.customer_estimated_pickup_time ? new Date(customer.customer_estimated_pickup_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : '' }
@@ -827,11 +838,14 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
</tr>)
})}
{showGroupInfo && getSortedFormItems()
{showGroupInfo && (() => {
let stopNo = 0;
return getSortedFormItems()
.map((customerItem, index) => {
if (!customerItem.customer_group) {
stopNo += 1;
return (<tr key={index} >
<td className="td-index">{customerItem.index + 1}</td>
<td className="td-index">{stopNo}</td>
<td>
{ customerItem.customer_name}
</td>
@@ -864,7 +878,7 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
{ customerItem.customer_special_needs }
</td>)}
{showCompletedInfo && (<td>
{ customerItem.customer_pickup_order }
{ getDisplayPickupOrder(customerItem.customer_pickup_order) }
</td>)}
{showCompletedInfo && (<td>
{ customerItem.customer_estimated_pickup_time ? new Date(customerItem.customer_estimated_pickup_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : '' }
@@ -877,10 +891,11 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
</td>}
</tr>);
} else {
const groupedChildrenRowStyle = { backgroundColor: '#fff' };
stopNo += 1;
const groupedRowsStyle = { backgroundColor: '#f3f3f3' };
return (<React.Fragment key={index}>
<tr className="group">
<td className="td-index"></td>
<tr className="group" style={groupedRowsStyle}>
<td className="td-index">{stopNo}</td>
<td>{customerItem.customer_group}</td>
<td colSpan={showCompletedInfo? 11: 3}>{customerItem.customers[0]?.customer_group_address}</td>
{allowForceEdit && <td>
@@ -888,8 +903,8 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
</td>}
</tr>
{
customerItem.customers?.map((customer) => (<tr key={customer.customer_id} style={groupedChildrenRowStyle} onClick={() => openForceEditModal(customer)}>
<td className="td-index">{customer.index + 1}</td>
customerItem.customers?.map((customer) => (<tr key={customer.customer_id} style={groupedRowsStyle} onClick={() => openForceEditModal(customer)}>
<td className="td-index"></td>
<td className="children">
{ customer.customer_name}
</td>
@@ -922,7 +937,7 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
{ customer.customer_special_needs }
</td>)}
{showCompletedInfo && (<td>
{ customer.customer_pickup_order }
{ getDisplayPickupOrder(customer.customer_pickup_order) }
</td>)}
{showCompletedInfo && (<td>
{ customer.customer_estimated_pickup_time ? new Date(customer.customer_estimated_pickup_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : '' }
@@ -937,7 +952,8 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
}
</React.Fragment>)
}
})}
});
})()}
</tbody>
</table>
</div>

View File

@@ -103,6 +103,14 @@ const RouteReportWithSignature = () => {
return (item?.result === true || item?.checked === true) ? '✓' : '';
};
const sortedRouteCustomers = [...(currentRoute?.route_customer_list || [])].sort((a, b) => {
const aOrder = Number(a?.customer_pickup_order);
const bOrder = Number(b?.customer_pickup_order);
const safeA = Number.isNaN(aOrder) ? Number.MAX_SAFE_INTEGER : aOrder;
const safeB = Number.isNaN(bOrder) ? Number.MAX_SAFE_INTEGER : bOrder;
return safeA - safeB;
});
return (
<>
<style>
@@ -121,9 +129,9 @@ const RouteReportWithSignature = () => {
border-collapse: collapse;
}
.route-report-table thead th {
background-color: #f5f5f5;
background-color: #f5f5f5 !important;
text-align: center;
color: #000;
color: #000 !important;
}
.route-report-header {
border: 1px solid #333;
@@ -155,7 +163,7 @@ const RouteReportWithSignature = () => {
.bilingual-label {
display: block;
font-size: 10px;
color: #666;
color: #000 !important;
}
`}
</style>
@@ -223,7 +231,7 @@ const RouteReportWithSignature = () => {
</thead>
<tbody>
{
currentRoute?.route_customer_list?.map((customer, index) => {
sortedRouteCustomers?.map((customer, index) => {
const relativeRouteCustomer = getInboundOrOutboundRouteCustomer(customer?.customer_id);
const otherRouteWithThisCustomer = getOtherRouteWithThisCustomer(customer?.customer_id);
const customerInOtherRoute = otherRouteWithThisCustomer?.route_customer_list?.find(cust => cust?.customer_id === customer?.customer_id);

View File

@@ -312,7 +312,7 @@ const RoutesDashboard = () => {
customer_dropoff_time: null,
customer_estimated_pickup_time: null,
customer_estimated_dropoff_time: null,
customer_route_status: PERSONAL_ROUTE_STATUS.NO_STATUS,
customer_route_status: null,
customer_address_override: null
}))
};
@@ -529,7 +529,7 @@ const RoutesDashboard = () => {
customer_dropoff_time: null,
customer_estimated_pickup_time: null,
customer_estimated_dropoff_time: null,
customer_route_status: PERSONAL_ROUTE_STATUS.NO_STATUS,
customer_route_status: null,
customer_address_override: null
})
})
@@ -585,7 +585,7 @@ const RoutesDashboard = () => {
customer_dropoff_time: null,
customer_estimated_pickup_time: null,
customer_estimated_dropoff_time: null,
customer_route_status: PERSONAL_ROUTE_STATUS.NO_STATUS,
customer_route_status: null,
customer_address_override: null
})
})
@@ -700,7 +700,7 @@ const RoutesDashboard = () => {
customer_dropoff_time: null,
customer_estimated_pickup_time: null,
customer_estimated_dropoff_time: null,
customer_route_status: PERSONAL_ROUTE_STATUS.NO_STATUS
customer_route_status: null
})
})
});
@@ -1189,6 +1189,7 @@ const RoutesDashboard = () => {
customer_leave_center_time: null,
customer_pickup_time: null,
customer_dropoff_time: null,
customer_route_status: null,
// customer_estimated_pickup_time: undefined,
// customer_estimated_dropoff_time: undefined,
// customer_pickup_status: undefined,
@@ -1266,7 +1267,11 @@ const RoutesDashboard = () => {
const createPromises = selectedTemplate.routes.map(route => {
const routeData = {
...route,
schedule_date: targetDate
schedule_date: targetDate,
route_customer_list: (route.route_customer_list || []).map((customer) => ({
...customer,
customer_route_status: null
}))
};
return TransRoutesService.createNewRoute(routeData);
});

View File

@@ -93,7 +93,7 @@ const RoutesSection = ({transRoutes, copyList, sectionName, drivers, vehicles, c
{`${sectionName}: `} <span className="route-stats">{
(sectionName.includes('Inbound') ||
sectionName.includes('Outbound')) &&
(`${seniors?.length} Scheduled (${seniors.filter(item => [CUSTOMER_TYPE.MEMBER, CUSTOMER_TYPE.SELF_PAY].includes(item.customer_type) && ![PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT, PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT].includes(item?.customer_route_status))?.length} Members ${seniors.filter(item=> [CUSTOMER_TYPE.VISITOR].includes(item?.customer_type) && ![PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT, PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT].includes(item?.customer_route_status))?.length} Visitors)${sectionName.includes('Inbound') && showCheckedInText ? ` ${seniors.filter(item => item?.customer_route_status === PERSONAL_ROUTE_STATUS.IN_CENTER)?.length} checked in` : ''}`)}</span>
(`${seniors.filter(item => [CUSTOMER_TYPE.MEMBER, CUSTOMER_TYPE.SELF_PAY].includes(item.customer_type) && ![PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT, PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT].includes(item?.customer_route_status))?.length} Members ${seniors.filter(item=> [CUSTOMER_TYPE.VISITOR].includes(item?.customer_type) && ![PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT, PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT].includes(item?.customer_route_status))?.length} Visitors${sectionName.includes('Inbound') && showCheckedInText ? ` ${seniors.filter(item => item?.customer_route_status === PERSONAL_ROUTE_STATUS.IN_CENTER)?.length} checked in` : ''}`)}</span>
</h6>
<div className="d-flex align-items-center" style={{ gap: '10px' }}>
{canAddNew && (

View File

@@ -153,6 +153,7 @@ const ViewDailyTemplate = () => {
addText="+Add Route"
redirect={goToCreateRoute}
routeType="inbound"
showCheckedInText={false}
/>
</div>
<div className="col-md-12 mb-4">
@@ -168,6 +169,7 @@ const ViewDailyTemplate = () => {
addText="+Add Route"
redirect={goToCreateRoute}
routeType="outbound"
showCheckedInText={false}
/>
</div>
</div>