Routes module

This commit is contained in:
Yang Li 2025-05-09 21:21:30 -04:00
parent 2fc3adfaec
commit 334bd5542b
17 changed files with 1110 additions and 546 deletions

View File

@ -1,16 +1,16 @@
{
"files": {
"main.css": "/static/css/main.bbcd4e86.css",
"main.js": "/static/js/main.8f60a967.js",
"main.css": "/static/css/main.2fa2d232.css",
"main.js": "/static/js/main.77186374.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.bbcd4e86.css.map": "/static/css/main.bbcd4e86.css.map",
"main.8f60a967.js.map": "/static/js/main.8f60a967.js.map",
"main.2fa2d232.css.map": "/static/css/main.2fa2d232.css.map",
"main.77186374.js.map": "/static/js/main.77186374.js.map",
"787.c4e7f8f9.chunk.js.map": "/static/js/787.c4e7f8f9.chunk.js.map"
},
"entrypoints": [
"static/css/main.bbcd4e86.css",
"static/js/main.8f60a967.js"
"static/css/main.2fa2d232.css",
"static/js/main.77186374.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="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.8f60a967.js"></script><link href="/static/css/main.bbcd4e86.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="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.77186374.js"></script><link href="/static/css/main.2fa2d232.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

View File

@ -1,16 +0,0 @@
{
"files": {
"main.css": "/static/css/main.bbcd4e86.css",
"main.js": "/static/js/main.8f60a967.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.bbcd4e86.css.map": "/static/css/main.bbcd4e86.css.map",
"main.8f60a967.js.map": "/static/js/main.8f60a967.js.map",
"787.c4e7f8f9.chunk.js.map": "/static/js/787.c4e7f8f9.chunk.js.map"
},
"entrypoints": [
"static/css/main.bbcd4e86.css",
"static/js/main.8f60a967.js"
]
}

View File

@ -1 +0,0 @@
<!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="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.8f60a967.js"></script><link href="/static/css/main.bbcd4e86.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

View File

@ -1,2 +0,0 @@
"use strict";(self.webpackChunkclient=self.webpackChunkclient||[]).push([[787],{787:function(e,t,n){n.r(t),n.d(t,{getCLS:function(){return y},getFCP:function(){return g},getFID:function(){return C},getLCP:function(){return P},getTTFB:function(){return D}});var i,r,a,o,u=function(e,t){return{name:e,value:void 0===t?-1:t,delta:0,entries:[],id:"v2-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)}},c=function(e,t){try{if(PerformanceObserver.supportedEntryTypes.includes(e)){if("first-input"===e&&!("PerformanceEventTiming"in self))return;var n=new PerformanceObserver((function(e){return e.getEntries().map(t)}));return n.observe({type:e,buffered:!0}),n}}catch(e){}},f=function(e,t){var n=function n(i){"pagehide"!==i.type&&"hidden"!==document.visibilityState||(e(i),t&&(removeEventListener("visibilitychange",n,!0),removeEventListener("pagehide",n,!0)))};addEventListener("visibilitychange",n,!0),addEventListener("pagehide",n,!0)},s=function(e){addEventListener("pageshow",(function(t){t.persisted&&e(t)}),!0)},m=function(e,t,n){var i;return function(r){t.value>=0&&(r||n)&&(t.delta=t.value-(i||0),(t.delta||void 0===i)&&(i=t.value,e(t)))}},v=-1,p=function(){return"hidden"===document.visibilityState?0:1/0},d=function(){f((function(e){var t=e.timeStamp;v=t}),!0)},l=function(){return v<0&&(v=p(),d(),s((function(){setTimeout((function(){v=p(),d()}),0)}))),{get firstHiddenTime(){return v}}},g=function(e,t){var n,i=l(),r=u("FCP"),a=function(e){"first-contentful-paint"===e.name&&(f&&f.disconnect(),e.startTime<i.firstHiddenTime&&(r.value=e.startTime,r.entries.push(e),n(!0)))},o=window.performance&&performance.getEntriesByName&&performance.getEntriesByName("first-contentful-paint")[0],f=o?null:c("paint",a);(o||f)&&(n=m(e,r,t),o&&a(o),s((function(i){r=u("FCP"),n=m(e,r,t),requestAnimationFrame((function(){requestAnimationFrame((function(){r.value=performance.now()-i.timeStamp,n(!0)}))}))})))},h=!1,T=-1,y=function(e,t){h||(g((function(e){T=e.value})),h=!0);var n,i=function(t){T>-1&&e(t)},r=u("CLS",0),a=0,o=[],v=function(e){if(!e.hadRecentInput){var t=o[0],i=o[o.length-1];a&&e.startTime-i.startTime<1e3&&e.startTime-t.startTime<5e3?(a+=e.value,o.push(e)):(a=e.value,o=[e]),a>r.value&&(r.value=a,r.entries=o,n())}},p=c("layout-shift",v);p&&(n=m(i,r,t),f((function(){p.takeRecords().map(v),n(!0)})),s((function(){a=0,T=-1,r=u("CLS",0),n=m(i,r,t)})))},E={passive:!0,capture:!0},w=new Date,L=function(e,t){i||(i=t,r=e,a=new Date,F(removeEventListener),S())},S=function(){if(r>=0&&r<a-w){var e={entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+r};o.forEach((function(t){t(e)})),o=[]}},b=function(e){if(e.cancelable){var t=(e.timeStamp>1e12?new Date:performance.now())-e.timeStamp;"pointerdown"==e.type?function(e,t){var n=function(){L(e,t),r()},i=function(){r()},r=function(){removeEventListener("pointerup",n,E),removeEventListener("pointercancel",i,E)};addEventListener("pointerup",n,E),addEventListener("pointercancel",i,E)}(t,e):L(t,e)}},F=function(e){["mousedown","keydown","touchstart","pointerdown"].forEach((function(t){return e(t,b,E)}))},C=function(e,t){var n,a=l(),v=u("FID"),p=function(e){e.startTime<a.firstHiddenTime&&(v.value=e.processingStart-e.startTime,v.entries.push(e),n(!0))},d=c("first-input",p);n=m(e,v,t),d&&f((function(){d.takeRecords().map(p),d.disconnect()}),!0),d&&s((function(){var a;v=u("FID"),n=m(e,v,t),o=[],r=-1,i=null,F(addEventListener),a=p,o.push(a),S()}))},k={},P=function(e,t){var n,i=l(),r=u("LCP"),a=function(e){var t=e.startTime;t<i.firstHiddenTime&&(r.value=t,r.entries.push(e),n())},o=c("largest-contentful-paint",a);if(o){n=m(e,r,t);var v=function(){k[r.id]||(o.takeRecords().map(a),o.disconnect(),k[r.id]=!0,n(!0))};["keydown","click"].forEach((function(e){addEventListener(e,v,{once:!0,capture:!0})})),f(v,!0),s((function(i){r=u("LCP"),n=m(e,r,t),requestAnimationFrame((function(){requestAnimationFrame((function(){r.value=performance.now()-i.timeStamp,k[r.id]=!0,n(!0)}))}))}))}},D=function(e){var t,n=u("TTFB");t=function(){try{var t=performance.getEntriesByType("navigation")[0]||function(){var e=performance.timing,t={entryType:"navigation",startTime:0};for(var n in e)"navigationStart"!==n&&"toJSON"!==n&&(t[n]=Math.max(e[n]-e.navigationStart,0));return t}();if(n.value=n.delta=t.responseStart,n.value<0||n.value>performance.now())return;n.entries=[t],e(n)}catch(e){}},"complete"===document.readyState?setTimeout(t,0):addEventListener("load",(function(){return setTimeout(t,0)}))}}}]);
//# sourceMappingURL=787.c4e7f8f9.chunk.js.map

File diff suppressed because one or more lines are too long

View File

@ -452,22 +452,31 @@ table .group td {
.multi-columns-container {
display: flex;
background: #D0E1F8;
padding: 24px;
/* background: #D0E1F8; */
padding: 24px 0;
border-radius: 10px;
}
.column-container {
padding: 20px;
padding: 0 20px 0 0;
}
.column-card {
background: white;
padding:24px;
border-radius: 10px;
border: 1px solid #ccc;
margin-bottom: 24px;
}
.react-time-picker__wrapper {
border-radius: 10px;
border-color: #ccc!important;
}
.react-time-picker__wrapper input[type=number] {
border:none;
}
.app-main-content-fields-section .field-value {
font-size: 12px;
@ -485,6 +494,7 @@ table .group td {
.app-main-content-fields-section.short {
margin-bottom: 0;
margin-top: 12px;
justify-content: space-between;
}
.app-main-content-fields-section.with-function .react-datepicker-wrapper {
@ -779,20 +789,54 @@ input[type="checkbox"] {
}
.customers-container {
background: #eee;
padding: 20px;
width: 100%;
position: relative;
}
.customers-dnd-item-container {
border: 1px dotted #777;
padding: 15px;
width: 100%;
margin-bottom: 15px;
border: none;
padding: 0 16px;
margin-bottom: 24px;
display: flex;
align-items: center;
font-size: 13px;
cursor: move;
background: #eee;
margin-left: 60px;
}
.customers-dnd-item-container-absent {
border: none;
padding: 0 16px;
margin-bottom: 24px;
display: flex;
align-items: center;
font-size: 13px;
background: #eee;
}
.new-customers-dnd-item-container {
border: none;
padding: 0 16px;
margin-bottom: 24px;
display: flex;
align-items: center;
font-size: 13px;
margin-left: 44px;
}
.column-card.adjust {
padding-right: 60px;
}
.stop-index {
position: absolute;
left: 0;
}
.customer-dnd-item {
padding: 4px 0;
}
.customer-dnd-img {
@ -1081,6 +1125,10 @@ input[type="checkbox"] {
background: transparent;
}
.modal-dialog {
max-width: 1000px !important;
}
.modal-fullscreen-xxl-down {
height: 100%;
margin: 0;

View File

@ -103,15 +103,14 @@ const Admin = () => {
}, [])
return (
<>
<div className="list row">
<div className="col-md-12">
{/* <div className="list row"> */}
{/* <div className="col-md-12">
<div className="float-end"><button className="btn btn-link btn-sm" onClick={() => AuthService.logout()}>Log Out</button></div>
<h4>Transportation Center </h4>
</div>
<div className="col-md-12">
<Navbar bg="light" expand="lg" className="admin-nav mb-4">
<Container>
{/* <Navbar.Brand>Customers Admin</Navbar.Brand> */}
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
{(AuthService.canAddOrEditEmployees() || AuthService.canViewEmployees()) &&<Nav className="me-auto">
@ -193,7 +192,7 @@ const Admin = () => {
</Container>
</Navbar>
</div>
</div>
</div> */}
<div className="list row">
<div className="col-md-12">
<Outlet />

View File

@ -3,7 +3,8 @@ import { useNavigate } from 'react-router-dom';
import { CustomerService, TransRoutesService, AuthService, ReportService } from "../../services";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { Modal, Button } from "react-bootstrap";
import { CalendarWeek, ClockHistory, Copy, Download, Eraser, Plus, Clock, Send, Filter, CalendarCheck, Check } from "react-bootstrap-icons";
import { Breadcrumb, Tabs, Tab, Dropdown, Spinner, Modal, Button } from "react-bootstrap";
import DateTimePicker from "react-datetime-picker";
import { CSVLink } from "react-csv";
import { CUSTOMER_TYPE, PERSONAL_ROUTE_STATUS, PERSONAL_ROUTE_STATUS_TEXT, REPORT_TYPE } from "../../shared";
@ -29,6 +30,8 @@ const CustomerReport = () => {
const [attendFilter, setAttendFilter] = useState(false);
const [existedReports, setExistedReports] = useState([]);
const [enableSave, setEnableSave] = useState(false);
const [showFilterReportDropdown, setShowFilterReportDropdown] = useState(false);
const [showDateDropdown, setShowDateDropdown] = useState(false);
const [csvData, setCsvData] = useState([]);
const csvInstance = useRef(null);
@ -474,6 +477,103 @@ const CustomerReport = () => {
})
};
const cleanFilterAndClose = () => {
setCallerFilter('');
setNameFilter('');
setAttendFilter(false);
setStatusFilter('')
setShowFilterReportDropdown(false);
}
const cleanDate = () => {
setDatePicked(new Date());
setShowDateDropdown(false);
}
const FilterAndClose = () => {
setShowFilterReportDropdown(false);
}
const customReportFilterMenu = React.forwardRef(
({ children, style, className, 'aria-labelledby': labeledBy }, ref) => {
return (
<div
ref={ref}
style={style}
className={className}
aria-labelledby={labeledBy}
>
<h6>Filter By</h6>
<div className="app-main-content-fields-section margin-sm dropdown-container">
<div className="me-4">
<div className="field-label">User Name</div>
<input type="text" value={nameFilter} onChange={(e) => setNameFilter(e.currentTarget.value)}/>
</div>
</div>
<div className="app-main-content-fields-section margin-sm dropdown-container">
<div className="me-4">
<div className="field-label">Caller</div>
<input type="text" value={callerFilter} onChange={(e) => setCallerFilter(e.currentTarget.value)}/>
</div>
</div>
<div className="app-main-content-fields-section margin-sm dropdown-container">
<div className="me-4">
<div className="field-label">Customer Route Status</div>
<select
value={statusFilter}
onChange={(e) => setStatusFilter(e.target.value)}
>
{
[['', {text: ''}], ...Object.entries(PERSONAL_ROUTE_STATUS_TEXT)].map(([key, {text}]) => (
<option key={key} value={text}>{text}</option>
))
}
</select>
</div>
</div>
<div className="app-main-content-fields-section margin-sm dropdown-container">
<div className="me-4">
<div className="field-label">Include All Customer Attending Today</div>
<input type="checkbox" value={attendFilter} checked={attendFilter === true} onChange={() => setAttendFilter(!attendFilter)}/>
</div>
</div>
<div className="list row">
<div className="col-md-12">
<button className="btn btn-default btn-sm float-right" onClick={() => cleanFilterAndClose()}> Cancel </button>
<button className="btn btn-primary btn-sm float-right" onClick={() => FilterAndClose()}> Filter </button>
</div>
</div>
</div>
);
},
);
const customMenuDate = React.forwardRef(
({ children, style, className, 'aria-labelledby': labeledBy }, ref) => {
return (
<div
ref={ref}
style={style}
className={className}
aria-labelledby={labeledBy}
>
<div className="app-main-content-fields-section margin-sm dropdown-container">
<div className="me-4">
<div className="field-label">Select Date for Customer Report</div>
<DatePicker selected={datePicked} onChange={(v) => setDatePicked(v)} />
</div>
</div>
<div className="list row">
<div className="col-md-12">
<button className="btn btn-default btn-sm float-right" onClick={() => cleanDate()}> Cancel </button>
<button className="btn btn-primary btn-sm float-right" onClick={() => getRoutes()}>Start</button>
</div>
</div>
</div>
);
},
);
useEffect(() => {
CustomerService.getAllActiveCustomers().then((data) => setCustomers(data.data));
}, []);
@ -556,119 +656,119 @@ const CustomerReport = () => {
{
(AuthService.canAddOrEditAttendance() || AuthService.canViewAttendance() ) && (
<>
<div className="list row">
<div className="mb-4">
Select Date for Customer Report:
</div>
<div className="col-md-4 col-sm-4 col-xs-12 mb-4">
<DatePicker selected={datePicked} onChange={(v) => setDatePicked(v)} />
</div>
<div className="col-md-2 col-sm-4 col-xs-12 mb-4">
<button className="btn btn-primary" onClick={() => getRoutes()}>Start</button>
<div className="list row mb-4">
<Breadcrumb>
<Breadcrumb.Item>Transportation</Breadcrumb.Item>
<Breadcrumb.Item active>
Customer Reports
</Breadcrumb.Item>
</Breadcrumb>
<div className="col-md-12 text-primary">
<h4>
Customer Reports
</h4>
</div>
</div>
<div className="list row">
<div className="col-md-12">
<div className="mb-4">
Filter by User Name: <input type="text" value={nameFilter} onChange={(e) => setNameFilter(e.currentTarget.value)}/>
</div>
<div className="mb-4">
Filter by Caller: <input type="text" value={callerFilter} onChange={(e) => setCallerFilter(e.currentTarget.value)}/>
</div>
<div className="col-md-6 col-sm-6 col-xs-12 mb-4">
Filter by Customer Route Status: <select
value={statusFilter}
onChange={(e) => setStatusFilter(e.target.value)}
>
{
[['', {text: ''}], ...Object.entries(PERSONAL_ROUTE_STATUS_TEXT)].map(([key, {text}]) => (
<option key={key} value={text}>{text}</option>
))
}
</select>
</div>
<div className="mb-4">
Include All Customer Attending Today: <input type="checkbox" value={attendFilter} checked={attendFilter === true} onChange={() => setAttendFilter(!attendFilter)}/>
</div>
<div className="list row mb-4">
{/* <CSVLink
className="btn btn-primary btn-sm me-2"
data={getFinalReportCSVData}
filename={`Customer Attendance Report - ${datePicked.toLocaleDateString()}`}
>
Generate Customer Reports
</CSVLink> */}
<button className="col-md-3 col-sm-12 btn btn-primary btn-sm me-2" disabled={!enableSave} onClick={() => generateCustomerReport()}>Save Customer Reports</button>
<button className="col-md-4 col-sm-12 btn btn-primary btn-sm" onClick={() => syncLatestRouteStatus()}>Sync Latest Route Status to Report</button>
<div className="col-md-4 col-sm-12"
onClick={() => {
<div className="app-main-content-list-container">
<div className="app-main-content-list-func-container">
<Tabs defaultActiveKey="customerReport" id="reports-tab">
<Tab eventKey="customerReport" title="Customer Report">
<h6 className="text-primary">Report Details Table</h6>
<div className="app-main-content-fields-section with-function">
<button className="btn btn-primary me-2" disabled={!enableSave} onClick={() => generateCustomerReport()}>Save Customer Reports</button>
<button className="btn btn-primary me-2" onClick={() => syncLatestRouteStatus()}>Sync Latest Route Status to Report</button>
<button onClick={() => {
generateReportData();
}}
>
<Button component='span' color='primary' variant='outlined' className="btn btn-primary btn-sm me-2">Generate Customer Reports</Button>
</div>
</div>
<Fragment>
{csvData.length > 0 ?
}} className="btn btn-primary me-2">Generate Customer Reports</button>
{ csvData.length > 0 &&
<CSVLink
className="btn btn-primary btn-sm btn-no-deco"
data={csvData}
ref={csvInstance}
filename={`Customer Attendance Report - ${datePicked.toLocaleDateString()}`}
/>
: undefined}
</Fragment>
<div className="mt-4"><h6>Report Details Table</h6></div>
<table className="personnel-info-table">
<thead>
<tr>
<th>No.</th>
<th>Name</th>
<th>Customer Route Status</th>
<th>Pick Up Time</th>
<th>Enter Center Time</th>
<th>Leave Center Time</th>
<th>Drop Off Time</th>
<th>Hours Stayed</th>
<th>Cutomer Type</th>
<th>Caller</th>
<th>Seating</th>
<th>Vehicle Number</th>
</tr>
</thead>
<tbody>
{
Array.from(routeCustomerMap.entries()).filter(
(item) => item[1]?.customer_name?.toLowerCase()?.includes(nameFilter?.toLowerCase()) && (
!attendFilter ||
attendFilter && (item[1].customer_enter_center_time || item[1].customer_leave_center_time)
))
.filter(item => item[1]?.customer_caller?.toLowerCase()?.includes(callerFilter?.toLowerCase()))
.filter(item => [CUSTOMER_TYPE.MEMBER, CUSTOMER_TYPE.SELF_PAY].includes(item[1].customer_type))
.filter(item => statusFilter ? (statusFilter !== 'No Status' ? item[1].customer_route_report_status === statusFilter : (!item[1].customer_route_report_status || item[1].customer_route_report_status === statusFilter)):item)
.sort((a, b) => a[1].customer_name > b[1].customer_name ? 1 : -1)
.map(([key, {customer_name, customer_pickup_time, customer_dropoff_time, customer_enter_center_time, customer_leave_center_time, customer_type, customer_caller, customer_route_report_status, customer_vehicle, customer_seating}], index) => {
return (<tr key={index} className={`${AuthService.canAddOrEditAttendance() ? 'clickable': ''} ${isNotFullTime(customer_enter_center_time, customer_leave_center_time)? 'red': ''}`} onClick={() => openForceEditModal(key, customer_enter_center_time, customer_leave_center_time, customer_pickup_time, customer_dropoff_time, customer_route_report_status)}>
<td>{index+1}</td>
<td>{customer_name}</td>
<td>{(customer_route_report_status)}</td>
<td>{customer_pickup_time && new Date(customer_pickup_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}</td>
<td>{customer_enter_center_time && new Date(customer_enter_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}</td>
<td>{customer_leave_center_time && new Date(customer_leave_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}</td>
<td>{customer_dropoff_time && new Date(customer_dropoff_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}</td>
<td>{customer_enter_center_time && customer_leave_center_time && diff_hours(new Date(customer_enter_center_time), new Date(customer_leave_center_time))}</td>
<td>{customer_type}</td>
<td>{customer_caller}</td>
<td>{customer_seating}</td>
<td>{customer_vehicle}</td>
</tr>)
})
}
</tbody>
</table>
</div>
<table className="personnel-info-table">
<thead>
<tr>
<th className="th-index">No.</th>
<th>Name</th>
<th>Customer Route Status</th>
<th>Pick Up Time</th>
<th>Enter Center Time</th>
<th>Leave Center Time</th>
<th>Drop Off Time</th>
<th>Hours Stayed</th>
<th>Cutomer Type</th>
<th>Caller</th>
<th>Seating</th>
<th>Vehicle Number</th>
</tr>
</thead>
<tbody>
{
Array.from(routeCustomerMap.entries()).filter(
(item) => item[1]?.customer_name?.toLowerCase()?.includes(nameFilter?.toLowerCase()) && (
!attendFilter ||
attendFilter && (item[1].customer_enter_center_time || item[1].customer_leave_center_time)
))
.filter(item => item[1]?.customer_caller?.toLowerCase()?.includes(callerFilter?.toLowerCase()))
.filter(item => [CUSTOMER_TYPE.MEMBER, CUSTOMER_TYPE.SELF_PAY].includes(item[1].customer_type))
.filter(item => statusFilter ? (statusFilter !== 'No Status' ? item[1].customer_route_report_status === statusFilter : (!item[1].customer_route_report_status || item[1].customer_route_report_status === statusFilter)):item)
.sort((a, b) => a[1].customer_name > b[1].customer_name ? 1 : -1)
.map(([key, {customer_name, customer_pickup_time, customer_dropoff_time, customer_enter_center_time, customer_leave_center_time, customer_type, customer_caller, customer_route_report_status, customer_vehicle, customer_seating}], index) => {
return (<tr key={index} className={`${AuthService.canAddOrEditAttendance() ? 'clickable': ''} ${isNotFullTime(customer_enter_center_time, customer_leave_center_time)? 'red': ''}`} onClick={() => openForceEditModal(key, customer_enter_center_time, customer_leave_center_time, customer_pickup_time, customer_dropoff_time, customer_route_report_status)}>
<td className="td-index">{index+1}</td>
<td>{customer_name}</td>
<td>{(customer_route_report_status)}</td>
<td>{customer_pickup_time && new Date(customer_pickup_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}</td>
<td>{customer_enter_center_time && new Date(customer_enter_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}</td>
<td>{customer_leave_center_time && new Date(customer_leave_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}</td>
<td>{customer_dropoff_time && new Date(customer_dropoff_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}</td>
<td>{customer_enter_center_time && customer_leave_center_time && diff_hours(new Date(customer_enter_center_time), new Date(customer_leave_center_time))}</td>
<td>{customer_type}</td>
<td>{customer_caller}</td>
<td>{customer_seating}</td>
<td>{customer_vehicle}</td>
</tr>)
})
}
</tbody>
</table>
</Tab>
</Tabs>
<div className="list-func-panel">
<Dropdown
key={'report-date'}
id="report-date"
className="me-2"
show={showDateDropdown}
onToggle={() => setShowDateDropdown(!showDateDropdown)}
autoClose="outside"
>
<Dropdown.Toggle variant="primary">
<CalendarWeek size={16} className="me-2"></CalendarWeek>Select Date to View Report
</Dropdown.Toggle>
<Dropdown.Menu as={customMenuDate}/>
</Dropdown>
<Dropdown
key={'filter-report'}
id="filter-report"
className="me-2"
show={showFilterReportDropdown}
onToggle={() => setShowFilterReportDropdown(!showFilterReportDropdown)}
autoClose="outside"
>
<Dropdown.Toggle variant="primary">
<Filter size={16} className="me-2"></Filter>Filter
</Dropdown.Toggle>
<Dropdown.Menu as={customReportFilterMenu}/>
</Dropdown>
</div>
</div>
</div>
<Modal show={show} onHide={() => closeModal()}>
@ -677,41 +777,52 @@ const CustomerReport = () => {
</Modal.Header>
<Modal.Body>
<>
<div>
Special Checkin: <DateTimePicker onFocus={() => {if (!customerCheckInTime || customerCheckInTime.length === 0) { setCustomerCheckInTime(new Date())}}} format={'MM/dd/y HH:mm'} value={customerCheckInTime} disableClock={true} onChange={setCustomerCheckInTime} />
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Special Checkin
</div>
<DateTimePicker onFocus={() => {if (!customerCheckInTime || customerCheckInTime.length === 0) { setCustomerCheckInTime(new Date())}}} format={'MM/dd/y HH:mm'} value={customerCheckInTime} disableClock={true} onChange={setCustomerCheckInTime} />
</div>
<div className="me-4">
<div className="field-label">Special Checkout
</div>
<DateTimePicker onFocus={() => {if (!customerCheckOutTime || customerCheckOutTime.length === 0) { setCustomerCheckOutTime(new Date())}}} format={'MM/dd/y HH:mm'} value={customerCheckOutTime} disableClock={true} onChange={setCustomerCheckOutTime} />
</div>
</div>
<hr />
<div>
Special Checkout: <DateTimePicker onFocus={() => {if (!customerCheckOutTime || customerCheckOutTime.length === 0) { setCustomerCheckOutTime(new Date())}}} format={'MM/dd/y HH:mm'} value={customerCheckOutTime} disableClock={true} onChange={setCustomerCheckOutTime} />
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Pickip Time
</div>
<DateTimePicker onFocus={() => {if (!customerPickupTime || customerPickupTime.length === 0) { setCustomerPickupTime(new Date())}}} format={'MM/dd/y HH:mm'} value={customerPickupTime} disableClock={true} onChange={setCustomerPickupTime} />
</div>
<div className="me-4">
<div className="field-label">Dropoff Time
</div>
<DateTimePicker onFocus={() => {if (!customerDropoffTime || customerDropoffTime.length === 0) { setCustomerDropoffTime(new Date())}}} format={'MM/dd/y HH:mm'} value={customerDropoffTime} disableClock={true} onChange={setCustomerDropoffTime} />
</div>
</div>
<hr />
<div>
Pickup Time: <DateTimePicker onFocus={() => {if (!customerPickupTime || customerPickupTime.length === 0) { setCustomerPickupTime(new Date())}}} format={'MM/dd/y HH:mm'} value={customerPickupTime} disableClock={true} onChange={setCustomerPickupTime} />
</div>
<hr/>
<div>
Dropoff Time: <DateTimePicker onFocus={() => {if (!customerDropoffTime || customerDropoffTime.length === 0) { setCustomerDropoffTime(new Date())}}} format={'MM/dd/y HH:mm'} value={customerDropoffTime} disableClock={true} onChange={setCustomerDropoffTime} />
</div>
<hr/>
<div>
Special Change Customer Route status:
<select value={customerRouteReportStatus} onChange={e=>setCustomerRouteReportStatus(e.target.value)}>
<option value=''></option>
{
Object.values(PERSONAL_ROUTE_STATUS_TEXT).map((item) => item?.text).map(text => <option key={text} value={text}>{text}</option>)
}
</select>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Special Change Customer Route status
</div>
<select value={customerRouteReportStatus} onChange={e=>setCustomerRouteReportStatus(e.target.value)}>
<option value=''></option>
{
Object.values(PERSONAL_ROUTE_STATUS_TEXT).map((item) => item?.text).map(text => <option key={text} value={text}>{text}</option>)
}
</select>
</div>
</div>
</>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={() => closeModal()}>
<Button variant="link" size="sm" onClick={() => closeModal()}>
Close
</Button>
<Button variant="danger" onClick={() => deleteItem()}>
<Button variant="danger" size="sm" onClick={() => deleteItem()}>
Delete This Item
</Button>
<Button variant="primary" onClick={() => saveReportInfo()}>
<Button variant="primary" size="sm" onClick={() => saveReportInfo()}>
Save Changes
</Button>
</Modal.Footer>

View File

@ -120,10 +120,16 @@ const SideMenu = () => {
roleFunc: AuthService.canViewRoutes
},
{
name: 'Schedule Driver for Appointment',
link: '#',
roleFunc: AuthService.canViewRoutes
name: 'Customer Report',
link: '/admin/customer-report',
category: '/admin',
roleFunc: AuthService.canViewAttendance
}
// {
// name: 'Schedule Driver for Appointment',
// link: '#',
// roleFunc: AuthService.canViewRoutes
// }
]
},
{

View File

@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
import { useSelector,useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
import { selectAllRoutes, transRoutesSlice, vehicleSlice, selectTomorrowAllRoutes, selectAllActiveDrivers, selectAllActiveVehicles, transRouteTemplatesSlice, selectAllActiveRouteTemplates, selectInboundRouteTemplates, selectOutboundRouteTemplates } from "./../../store";
import { Modal, Button } from "react-bootstrap";
import { Modal, Button, Breadcrumb, Tabs, Tab } from "react-bootstrap";
import RouteCustomerEditor from "./RouteCustomerEditor";
import { AuthService } from "../../services";
import moment from 'moment';
@ -71,7 +71,8 @@ const CreateRoute = () => {
}
const redirectToDashboard = () => {
navigate(`/trans-routes/dashboard`);
const date =params.get('date');
navigate(`/trans-routes/dashboard?dateSchedule=${date}`);
}
const goToTemplateList = () => {
@ -173,99 +174,192 @@ const CreateRoute = () => {
return (
<>
<div className="list row mb-4">
<Breadcrumb>
<Breadcrumb.Item>Transportation</Breadcrumb.Item>
<Breadcrumb.Item active>
Transportation Routes
</Breadcrumb.Item>
</Breadcrumb>
<div className="col-md-12 text-primary">
<h5> Create New Route <button className="btn btn-link btn-sm" onClick={() => {redirectToSchedule()}}>Back To Schedule Page</button></h5>
<h4>
Create New Route <button className="btn btn-link btn-sm" onClick={() => {redirectToDashboard()}}>Back</button>
</h4>
</div>
</div>
<div className="list row mb-5">
<div className="col-md-7 col-sm-7 col-xs-12">
<button className="btn btn-primary btn-sm" disabled={disableSave} onClick={() => saveRoute()}> Save </button>
<button className="btn btn-default btn-sm" onClick={() => redirectToSchedule()}> Cancel </button>
</div>
{errorMessage && <div className="col-md-6 col-sm-6 col-xs-12 alert alert-danger mt-4">{errorMessage}</div>}
</div>
<div className="list row mb-4">
<div className="col-md-6 mb-4">
Save As Template: <input type="checkbox" value={saveAsTemplate} checked={saveAsTemplate === true} onChange={e => setSaveAsTemplate(!saveAsTemplate)}/>
<div className="app-main-content-list-container">
<div className="app-main-content-list-func-container">
<Tabs defaultActiveKey="routeOverview" id="route-view-tab">
<Tab eventKey="routeOverview" title="Route Information">
<div className="multi-columns-container">
<div className="column-container">
<div className="column-card">
<h6 className="text-primary">Route Details</h6>
{((params.get('type')==="inbound" && inboundTemplates?.length>0) ||(params.get('type')==="outbound" && outboundTemplates?.length>0)) && <div className="app-main-content-fields-section"><div className="me-4">
<div className="field-label">
Use Existed Template
</div>
<select value={useRouteTemplate} onChange={e => setUseRouteTemplate(e.currentTarget.value)}>
<option value={''}></option>
{
(params.get('type') === 'inbound' && inboundTemplates?.length>0) ? inboundTemplates.map(template => <option value={template.id} key={template.id}>{template.name}</option>) : ((params.get('type') === 'outbound' && outboundTemplates?.length>0) ? outboundTemplates.map(template => <option value={template.id} key={template.id}>{template.name}</option>): <></>)
}
</select>
{/* <button className="btn btn-link btn-sm" onClick={() => goToTemplateList()}>Manage Route Templates</button> */}
</div></div>}
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Route Name
<span className="required">*</span>
</div>
<input type="text" value={routeName || ''} onChange={e => setRouteName(e.target.value)}/>
</div>
<div className="me-4">
<div className="field-label">Vechile
<span className="required">*</span>
</div>
<select value={newVehicle} onChange={e => setNewVehicle(e.target.value)}>
<option value=""></option>
{vehicles.map((vehicle) => (<option key={vehicle.id} value={vehicle.id}>{vehicle.vehicle_number}</option>))}
</select>
</div>
</div>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Driver
<span className="required">*</span>
</div>
<select value={newDriver} onChange={e => setNewDriver(e.target.value)}>
<option value=""></option>
{drivers.map((driver) => <option key={driver.id} value={driver.id}>{driver.name}</option>)}
</select>
</div>
<div className="me-4">
<div className="field-label">Route Type
<span className="required">*</span>
</div>
<select value={newRouteType} onChange={e => setNewRouteType(e.target.value)}>
<option value="inbound">Inbound</option>
<option value="outbound">Outbound</option>
</select>
</div>
</div>
<div className="app-main-content-fields-section">
Save As Template: <input type="checkbox" value={saveAsTemplate} checked={saveAsTemplate === true} onChange={e => setSaveAsTemplate(!saveAsTemplate)}/>
</div>
</div>
<div className="column-card adjust">
<div className="col-md-12 mb-4">
<RouteCustomerEditor currentRoute={currentRoute} setNewCustomerList={setNewCustomerList}></RouteCustomerEditor>
</div>
</div>
<div className="list row mb-5">
<div className="col-md-12 col-sm-12 col-xs-12">
<button className="btn btn-default btn-sm float-right" onClick={() => redirectToDashboard()}> Cancel </button>
<button className="btn btn-primary btn-sm float-right" disabled={disableSave} onClick={() => saveRoute()}> Save </button>
</div>
{errorMessage && <div className="col-md-12 col-sm-12 col-xs-12 alert alert-danger mt-4">{errorMessage}</div>}
</div>
</div>
<div className="column-container">
{ newVehicle && newVehicle !== '' && <div className="column-card mb-4">
<h6 className="text-primary">Vehicle Information</h6>
<div className="app-main-content-fields-section short">
<div className="field-body">
<div className="field-label">Vehicle Number</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.vehicle_number}</div>
</div>
<div className="field-body">
<div className="field-label">Seating Capacity</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.capacity}</div>
</div>
<div className="field-body">
<div className="field-label">Mileage</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.mileage}</div>
</div>
<div className="field-body">
<div className="field-label">Make</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.make}</div>
</div>
<div className="field-body">
<div className="field-label">Model</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.model}</div>
</div>
</div>
<div className="app-main-content-fields-section short">
<div className="field-body">
<div className="field-label">License Plate</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.tag}</div>
</div>
<div className="field-body">
<div className="field-label">Year</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.year}</div>
</div>
<div className="field-body">
<div className="field-label">GPS ID</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.gps_tag}</div>
</div>
<div className="field-body">
<div className="field-label">EZPass</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.ezpass}</div>
</div>
<div className="field-body">
<div className="field-label">Vin</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.vin || ''}</div>
</div>
</div>
</div>}
{
newDriver && newDriver !== '' && <div className="column-card">
<h6 className="text-primary">Driver Information</h6>
<div className="text-primary">Personal Details</div>
<div className="app-main-content-fields-section short">
<div className="field-body">
<div className="field-label">Driver Name</div>
<div className="field-value">{drivers.find(item => item.id === newDriver)?.name}</div>
</div>
<div className="field-body">
<div className="field-label">Preferred Name</div>
<div className="field-value">{drivers.find(item => item.id === newDriver)?.name_cn}</div>
</div>
<div className="field-body">
<div className="field-label">Job Title</div>
<div className="field-value">{drivers.find(item => item.id === newDriver)?.title}</div>
</div>
<div className="field-body">
<div className="field-label">Job Status</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.employment_status}</div>
</div>
</div>
<div className="app-main-content-fields-section short">
<div className="field-body">
<div className="field-label">Driver Capacity</div>
<div className="field-value">{drivers.find(item => item.id === newDriver)?.driver_capacity}</div>
</div>
<div className="field-body">
<div className="field-label">Phone Number</div>
<div className="field-value">{drivers.find(item => item.id === newDriver)?.phone}</div>
</div>
<div className="field-body">
<div className="field-label">Email</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.email}</div>
</div>
</div>
</div>
}
</div>
</div>
</Tab>
</Tabs>
</div>
</div>
{((params.get('type')==="inbound" && inboundTemplates?.length>0) ||(params.get('type')==="outbound" && outboundTemplates?.length>0)) && <div className="list row mb-4">
<div className="col-md-12 mb-4">
Use Existed Template: <select value={useRouteTemplate} onChange={e => setUseRouteTemplate(e.currentTarget.value)}>
<option value={''}></option>
{
(params.get('type') === 'inbound' && inboundTemplates?.length>0) ? inboundTemplates.map(template => <option value={template.id} key={template.id}>{template.name}</option>) : ((params.get('type') === 'outbound' && outboundTemplates?.length>0) ? outboundTemplates.map(template => <option value={template.id} key={template.id}>{template.name}</option>): <></>)
}
</select>
<button className="btn btn-link btn-sm" onClick={() => goToTemplateList()}>Manage Route Templates</button>
</div>
</div>}
<div className="list row mb-4">
<div className="col-md-6 mb-4">
Name(*): <input type="text" value={routeName || ''} onChange={e => setRouteName(e.target.value)}/>
</div>
<div className="col-md-6 mb-4">Vehicle(*): <select value={newVehicle} onChange={e => setNewVehicle(e.target.value)}>
{[{id: ''}, ...vehicles].map((vehicle) => (<option key={vehicle.id} value={vehicle.id}>{vehicle.vehicle_number}</option>))}
</select>
</div>
<div className="col-md-6 mb-4">Driver(*): <select value={newDriver} onChange={e => setNewDriver(e.target.value)}>
{[{id: ''}, ...drivers].map((driver) => <option key={driver.id} value={driver.id}>{driver.name}</option>)}
</select>
</div>
<div className="col-md-6 mb-4">Type(*): <select value={newRouteType} onChange={e => setNewRouteType(e.target.value)}>
<option value=""></option>
<option value="inbound">Inbound</option>
<option value="outbound">Outbound</option>
</select>
</div>
</div>
<div className="list row mb-4">
<div className="col-md-12 mb-4">
<RouteCustomerEditor currentRoute={currentRoute} setNewCustomerList={setNewCustomerList}></RouteCustomerEditor>
</div>
</div>
{ newVehicle && newVehicle !== '' && (<div className="list row mb-4">
<div className="col-md-12 create-route-container">
<h6>Vehicle Info</h6>
<div className="list row">
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Vehicle Number: {vehicles.find(item => item.id === newVehicle)?.vehicle_number}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Tag: {vehicles.find(item => item.id === newVehicle)?.tag}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">EzPass: {vehicles.find(item => item.id === newVehicle)?.ezpass}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">GPS: {vehicles.find(item => item.id === newVehicle)?.gps_tag}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Capacity: {vehicles.find(item => item.id === newVehicle)?.capacity}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Status: {vehicles.find(item => item.id === newVehicle)?.status}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Mileage: {vehicles.find(item => item.id === newVehicle)?.mileage}</div>
</div>
</div>
</div>)}
{ newDriver && newDriver !== '' && (<div className="list row mb-4">
<div className="col-md-12 create-route-container">
<h6>Driver Info</h6>
<div className="list row">
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Name: {drivers.find(item => item.id === newDriver)?.name}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Preferred Name: {drivers.find(item => item.id === newDriver)?.name_cn}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Driver Capacity: {drivers.find(item => item.id === newDriver)?.driver_capacity}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Roles: {drivers.find(item => item.id === newDriver)?.roles}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Phone: {drivers.find(item => item.id === newDriver)?.phone}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Email: {drivers.find(item => item.id === newDriver)?.email}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Employment Status: {drivers.find(item => item.id === newDriver)?.employment_status}</div>
</div>
</div>
</div>)}
{ newVehicle && newVehicle !== '' && (<div className="list row mb-4">
<div className="col-md-6">
<h6>Vehicle Checklist</h6>
{ currentVehicle?.checklist?.length > 0 && (<table className="mb-4">
<tbody>
{currentVehicle.checklist.map((item, index) => (<tr key={index}><td>{item}</td></tr>))}
</tbody>
</table>) }
<div className="mb-4"><button className="btn btn-link btn-sm" onClick={() => showAddCheckItemModal()}>+Add Check Items</button></div>
<div className="mb-4"><button className="btn btn-link btn-sm" onClick={() => showCopyCheckItemModal()}>Copy Checklist From Other Route</button></div>
</div>
</div>)}
<Modal show={showAddCheckItem} onHide={() => closeAddCheckItemModal()}>
<Modal.Header closeButton>
<Modal.Title>Add New Checklist Item</Modal.Title>

View File

@ -482,7 +482,7 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
</div>)}
{showGroupInfo && (<div className="list row mb-4">
<div className="col-md-12">
<CSVLink className="btn btn-primary btn-sm me-2" data={generateRouteReportData()} filename={`Route Report - ${transRoutes[0].name} (定线出车单)`}>
<CSVLink className="btn btn-primary btn-no-deco btn-sm me-2" data={generateRouteReportData()} filename={`Route Report - ${transRoutes[0].name} (定线出车单)`}>
Generate Route Reports
</CSVLink>
{transRoutes[0].type === 'inbound' && <button className="btn btn-primary btn-sm me-2" onClick={() => generateSeniorTimeReport()}>Generate Participants Time Reports</button>}
@ -544,7 +544,7 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
<table className="personnel-info-table">
<thead>
<tr>
<th className="td-index">No.</th>
<th className="th-index">No.</th>
<th>Name</th>
{showCompletedInfo && (<th>Address</th>)}
{showCompletedInfo && (<th>Tel</th>)}
@ -604,7 +604,7 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
.sort((a, b) => a.customer_name.replace(' ', '') > b.customer_name.replace(' ', '') ? 1: -1 )
.map((customer, index) => {
return (<tr key={index}>
<td> {index}</td>
<td className="td-index"> {index}</td>
<td>
{ customer.customer_name}
</td>
@ -656,7 +656,7 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
.map((customerItem, index) => {
if (!customerItem.customer_group) {
return (<tr key={index} >
<td className="children">{customerItem.index}</td>
<td className="td-index">{customerItem.index + 1}</td>
<td>
{ customerItem.customer_name}
</td>
@ -666,8 +666,8 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
{showCompletedInfo && (<td>
{ customerItem.customer_phone }
</td>)}
<td className={getTextAndClassName(customerItem).className}>
{ getTextAndClassName(customerItem).text }
<td> <div className={`${getTextAndClassName(customerItem).className} status-tag`}>
{ getTextAndClassName(customerItem).text } </div>
</td>
<td>
{ CUSTOMER_TYPE_TEXT[customerItem.customer_type]}
@ -704,7 +704,7 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
} else {
return (<React.Fragment key={index}>
<tr className="group">
<td></td>
<td className="td-index"></td>
<td>{customerItem.customer_group}</td>
<td colSpan={showCompletedInfo? 11: 3}>{customerItem.customers[0]?.customer_group_address}</td>
{allowForceEdit && <td>
@ -713,7 +713,7 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
</tr>
{
customerItem.customers?.map((customer) => (<tr key={customer.customer_id} onClick={() => openForceEditModal(customer)}>
<td className="children">{customer.index}</td>
<td className="td-index">{customer.index + 1}</td>
<td className="children">
{ customer.customer_name}
</td>
@ -723,8 +723,8 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
{showCompletedInfo && (<td>
{ customer.customer_phone }
</td>)}
<td className={getTextAndClassName(customer).className}>
{ getTextAndClassName(customer).text }
<td> <div className={`${getTextAndClassName(customerItem).className} status-tag`}>
{ getTextAndClassName(customerItem).text } </div>
</td>
<td>
{ CUSTOMER_TYPE_TEXT[customer.customer_type]}
@ -772,72 +772,90 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
</Modal.Header>
<Modal.Body>
<>
{isInbound && <div>
Estimated Pickup: <TimePicker disableClock={true} format={'HH:mm'} value={customerEstimatedPickUpTime} onChange={setCustomerEstimatedPickUpTime} />
</div>}
<hr />
<div>
Change Address Just For This Trip:
<input value={customerAddressOverride} onChange={(e) => setCustomerAddressOverride((e.currentTarget.value))} />
{/* <select value={customerAddressOverride} onChange={(e) => setCustomerAddressOverride((e.currentTarget.value))}>
<option value=""></option>
{customerAddressesList.map((item) => (<option value={item}>{item}</option>))}
</select> */}
</div>
<hr />
<div>
Special Checkin: <TimePicker disableClock={true} format={'HH:mm'} onFocus={() => {if (!customerCheckInTime || customerCheckInTime.length === 0) { setCustomerCheckInTime(new Date())}}} value={customerCheckInTime} onChange={setCustomerCheckInTime} />
</div>
<hr />
<div>
Special Checkout: <TimePicker disableClock={true} format={'HH:mm'} onFocus={() => {if (!customerCheckOutTime || customerCheckOutTime.length === 0) { setCustomerCheckOutTime(new Date())}}} value={customerCheckOutTime} onChange={setCustomerCheckOutTime} />
</div>
<hr />
<div>
Special Pickup: <TimePicker disableClock={true} format={'HH:mm'} onFocus={() => {if (!customerPickUpTime || customerPickUpTime.length === 0) { setCustomerPickUpTime(new Date())}}} value={customerPickUpTime} onChange={setCustomerPickUpTime} />
</div>
<hr />
<div>
Special Dropoff: <TimePicker disableClock={true} format={'HH:mm'} onFocus={() => {if (!customerDropOffTime || customerDropOffTime.length === 0) { setCustomerDropOffTime(new Date())}}} value={customerDropOffTime} onChange={setCustomerDropOffTime} />
</div>
<hr />
<div>
Special Set User Route Status: <select value={customerStatusInRoute} onChange={(e)=>{setCustomerStatusInRoute(e.currentTarget.value)}}>
<option value=""></option>
{
Object.entries(PERSONAL_ROUTE_STATUS).map((item) => <option key={item[0]} value={item[1]}>
{PERSONAL_ROUTE_STATUS_TEXT[item[1]].text}
</option>)
}
<div className="app-main-content-fields-section">
{isInbound &&<div className="me-4">
<div className="field-label">Estimated Pickup
</div>
<TimePicker disableClock={true} format={'HH:mm'} value={customerEstimatedPickUpTime} onChange={setCustomerEstimatedPickUpTime} />
</div>}
<div className="me-4">
<div className="field-label">Change Address Just For This Trip
</div>
<input type="text" value={customerAddressOverride} onChange={(e) => setCustomerAddressOverride((e.currentTarget.value))} />
</div>
</div>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Special Checkin
</div>
<TimePicker disableClock={true} format={'HH:mm'} onFocus={() => {if (!customerCheckInTime || customerCheckInTime.length === 0) { setCustomerCheckInTime(new Date())}}} value={customerCheckInTime} onChange={setCustomerCheckInTime} />
</div>
<div className="me-4">
<div className="field-label">Special Checkout
</div>
<TimePicker disableClock={true} format={'HH:mm'} onFocus={() => {if (!customerCheckOutTime || customerCheckOutTime.length === 0) { setCustomerCheckOutTime(new Date())}}} value={customerCheckOutTime} onChange={setCustomerCheckOutTime} />
</div>
<div className="me-4">
<div className="field-label">Special Pickup
</div>
<TimePicker disableClock={true} format={'HH:mm'} onFocus={() => {if (!customerPickUpTime || customerPickUpTime.length === 0) { setCustomerPickUpTime(new Date())}}} value={customerPickUpTime} onChange={setCustomerPickUpTime} />
</div>
<div className="me-4">
<div className="field-label">Special Dropoff
</div>
<TimePicker disableClock={true} format={'HH:mm'} onFocus={() => {if (!customerDropOffTime || customerDropOffTime.length === 0) { setCustomerDropOffTime(new Date())}}} value={customerDropOffTime} onChange={setCustomerDropOffTime} />
</div>
</div>
</select>
</div>
<hr/>
<div>
{/* Disable User in This Route: <input type="checkbox" value={disableCustomerInRoute} checked={disableCustomerInRoute} onChange={() => setDisableCustomerInRoute(!disableCustomerInRoute)} /> */}
Special Set User Pickup Status: <select value={customerPickupStatusInRoute} onChange={(e)=>{setCustomerPickupStatusInRoute(e.currentTarget.value)}}>
<option value=""></option>
{
Object.entries(PICKUP_STATUS).map((item) => <option key={item[0]} value={item[1]}>
{PICKUP_STATUS_TEXT[item[1]]}
</option>)
}
</select>
</div>
<div>
Note: <textarea value={customerNote} onChange={(e) => {setCustomerNote(e.target.value)}}></textarea>
</div>
<hr/>
<div>
Transfer To Route: <input type="text" value={customerTransferToRoute} onChange={(e) => {setCustomerTransferToRoute(e.target.value)}}/>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Special Set User Route Status
</div>
<select value={customerStatusInRoute} onChange={(e)=>{setCustomerStatusInRoute(e.currentTarget.value)}}>
<option value=""></option>
{
Object.entries(PERSONAL_ROUTE_STATUS).map((item) => <option key={item[0]} value={item[1]}>
{PERSONAL_ROUTE_STATUS_TEXT[item[1]].text}
</option>)
}
</select>
</div>
<div className="me-4">
<div className="field-label"> Special Set User Pickup Status
</div>
<select value={customerPickupStatusInRoute} onChange={(e)=>{setCustomerPickupStatusInRoute(e.currentTarget.value)}}>
<option value=""></option>
{
Object.entries(PICKUP_STATUS).map((item) => <option key={item[0]} value={item[1]}>
{PICKUP_STATUS_TEXT[item[1]]}
</option>)
}
</select>
</div>
</div>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Transfer To Route
</div>
<input type="text" value={customerTransferToRoute} onChange={(e) => {setCustomerTransferToRoute(e.target.value)}}/>
</div>
</div>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Note
</div>
<textarea value={customerNote} onChange={(e) => {setCustomerNote(e.target.value)}}></textarea>
</div>
</div>
</>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={() => closeModal()}>
<Button variant="link" size="sm" onClick={() => closeModal()}>
Close
</Button>
<Button variant="primary" onClick={() => saveRouteCustomerInfo()}>
<Button variant="primary" size="sm" onClick={() => saveRouteCustomerInfo()}>
Save Changes
</Button>
</Modal.Footer>
@ -848,44 +866,55 @@ const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
</Modal.Header>
<Modal.Body>
<>
{isInbound && <div>
Estimated Pickup: <TimePicker disableClock={true} format={'HH:mm'} value={customerEstimatedPickUpTime} onChange={setCustomerEstimatedPickUpTime} />
</div>}
<hr />
<div>
Special Checkin: <TimePicker disableClock={true} format={'HH:mm'} onFocus={() => {if (!customerCheckInTime || customerCheckInTime.length === 0) { setCustomerCheckInTime(new Date())}}} value={customerCheckInTime} onChange={setCustomerCheckInTime} />
</div>
<hr />
<div>
Special Checkout: <TimePicker disableClock={true} format={'HH:mm'} onFocus={() => {if (!customerCheckOutTime || customerCheckOutTime.length === 0) { setCustomerCheckOutTime(new Date())}}} value={customerCheckOutTime} onChange={setCustomerCheckOutTime} />
</div>
<hr />
<div>
Special Pickup: <TimePicker disableClock={true} format={'HH:mm'} onFocus={() => {if (!customerPickUpTime || customerPickUpTime.length === 0) { setCustomerPickUpTime(new Date())}}} value={customerPickUpTime} onChange={setCustomerPickUpTime} />
</div>
<hr />
<div>
Special Dropoff: <TimePicker disableClock={true} format={'HH:mm'} onFocus={() => {if (!customerDropOffTime || customerDropOffTime.length === 0) { setCustomerDropOffTime(new Date())}}} value={customerDropOffTime} onChange={setCustomerDropOffTime} />
</div>
<hr />
<div>
Special Set Users Route Status: <select value={customerStatusInRoute} onChange={(e)=>{setCustomerStatusInRoute(e.currentTarget.value)}}>
<option value=""></option>
{
Object.entries(PERSONAL_ROUTE_STATUS).map((item) => <option key={item[0]} value={item[1]}>
{PERSONAL_ROUTE_STATUS_TEXT[item[1]].text}
</option>)
}
<div className="app-main-content-fields-section">
{isInbound &&<div className="me-4">
<div className="field-label">Estimated Pickup
</div>
<TimePicker disableClock={true} format={'HH:mm'} value={customerEstimatedPickUpTime} onChange={setCustomerEstimatedPickUpTime} />
</div>}
<div className="me-4">
<div className="field-label">Special Set Users Route Status
</div>
<select value={customerStatusInRoute} onChange={(e)=>{setCustomerStatusInRoute(e.currentTarget.value)}}>
<option value=""></option>
{
Object.entries(PERSONAL_ROUTE_STATUS).map((item) => <option key={item[0]} value={item[1]}>
{PERSONAL_ROUTE_STATUS_TEXT[item[1]].text}
</option>)
}
</select>
</div>
</select>
</div>
</div>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Special Checkin
</div>
<TimePicker disableClock={true} format={'HH:mm'} onFocus={() => {if (!customerCheckInTime || customerCheckInTime.length === 0) { setCustomerCheckInTime(new Date())}}} value={customerCheckInTime} onChange={setCustomerCheckInTime} />
</div>
<div className="me-4">
<div className="field-label">Special Checkout
</div>
<TimePicker disableClock={true} format={'HH:mm'} onFocus={() => {if (!customerCheckOutTime || customerCheckOutTime.length === 0) { setCustomerCheckOutTime(new Date())}}} value={customerCheckOutTime} onChange={setCustomerCheckOutTime} />
</div>
<div className="me-4">
<div className="field-label">Special Pickup
</div>
<TimePicker disableClock={true} format={'HH:mm'} onFocus={() => {if (!customerPickUpTime || customerPickUpTime.length === 0) { setCustomerPickUpTime(new Date())}}} value={customerPickUpTime} onChange={setCustomerPickUpTime} />
</div>
<div className="me-4">
<div className="field-label">Special Dropoff
</div>
<TimePicker disableClock={true} format={'HH:mm'} onFocus={() => {if (!customerDropOffTime || customerDropOffTime.length === 0) { setCustomerDropOffTime(new Date())}}} value={customerDropOffTime} onChange={setCustomerDropOffTime} />
</div>
</div>
</>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={() => closeGroupEditorModal()}>
<Button variant="link" size="sm" onClick={() => closeGroupEditorModal()}>
Close
</Button>
<Button variant="primary" onClick={() => saveRouteGroupCustomerInfo()}>
<Button variant="primary" size="sm" onClick={() => saveRouteGroupCustomerInfo()}>
Save Changes
</Button>
</Modal.Footer>

View File

@ -6,6 +6,7 @@ import { Modal, Button } from "react-bootstrap";
import { CustomerService } from '../../services';
import { PERSONAL_ROUTE_STATUS } from '../../shared';
import ReactPaginate from 'react-paginate';
import { GripVertical, Pencil, RecordCircleFill, XSquare } from 'react-bootstrap-icons';
const ItemTypes = {
@ -78,7 +79,7 @@ const Card = ({ content, index, moveCard }) => {
)
}
const RouteCustomerEditor = ({currentRoute, setNewCustomerList}) => {
const RouteCustomerEditor = ({currentRoute, setNewCustomerList = (a) => {}, viewMode, editFun}) => {
const [customers, setCustomers] = useState([]);
const [showAddPersonnelModal, setShowAddPersonnelModal] = useState(false);
const [showAddAptGroupModal, setShowAddAptGroupModal] = useState(false);
@ -409,6 +410,20 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList}) => {
)
}
const getCurrentAssignedNumber = () => {
let count = 0;
for (const item of customers) {
if (item.customers) {
for (const customer of item.customers) {
count++;
}
} else {
count++;
}
}
return count;
}
useEffect(() => {
const result = [];
for (const item of customers) {
@ -427,59 +442,103 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList}) => {
return (
<DndProvider backend={HTML5Backend}>
<h6>Personnel List</h6>
<div className="row list">
<div className="col-md-6 col-sm-6 col-xs-12">
<button className="btn btn-primary btn-sm me-2 mb-4" onClick={() => openAddPersonnelModal()}> + Add Personnel </button>
<button className="btn btn-primary btn-sm me-2 mb-4" onClick={() => openAddAptGroupModal()}> + Add Apt Group </button>
</div>
</div>
<div className="customers-container mb-4">
{customers.map((item, index) => {
{ !viewMode && <h6 class="text-primary">Customers Assigned ({getCurrentAssignedNumber()})</h6>}
{ viewMode && <h6 class="text-primary">Route Assignment <button className="btn btn-sm btn-primary" onClick={() => editFun('assignment')}><Pencil size={16} className="me-2"></Pencil>Edit </button></h6>}
{!viewMode && <div className="customers-container mb-4">
{customers.map((item, index) => {
if (item?.customers) {
return <Card key={index} index={index} moveCard={reorderItems} content={(<div className="customers-dnd-item-container">
<img className="customer-dnd-img" src="/images/drag-and-drop.svg" />
<div className="stop-index"><span>{`Stop ${index+1}`}</span><RecordCircleFill size={16} color={"#0d6efd"} className="ms-2"></RecordCircleFill> </div>
<GripVertical className="me-4" size={14}></GripVertical>
<div className="customer-dnd-item" onClick={() => openEditAptGroupModal(index, item)}>
<span className="me-2">{item.customer_group} </span> <span>{item.customers[0]?.customer_group_address}</span>
<div className="customer-dnd-item-content">{item.customers.map(customer =>
<div key={customer.customer_id}>
<small className="me-2">{customer.customer_name}</small>
<small className="me-2">{customer.customer_address}</small>
<small className="me-2">{customer.customer_pickup_status}</small>
<small className="me-2">{customer.customer_name}</small>
<small className="me-2">{customer.customer_address}</small>
<small className="me-2">{customer.customer_pickup_status}</small>
</div>)}
</div>
</div>
<div className="customer-delete-btn"><button className="btn btn-default" onClick={()=> deleteGroup(index)}>X</button></div>
<div className="customer-delete-btn"><button className="btn btn-default" onClick={()=> deleteGroup(index)}><XSquare size={14}></XSquare></button></div>
</div>)}></Card>
} else {
return <Card key={index} index={index} moveCard={reorderItems} content={<div className="customers-dnd-item-container">
<img className="customer-dnd-img" src="/images/drag-and-drop.svg" />
<div className="stop-index"><span>{`Stop ${index+1}`}</span><RecordCircleFill size={16} color={"#0d6efd"} className="ms-2"></RecordCircleFill> </div>
<GripVertical className="me-4" size={14}></GripVertical>
<div className="customer-dnd-item">
<span>{item.customer_name} </span>
<small className="me-2">{item.customer_address}</small>
<small className="me-2">{item.customer_pickup_status}</small>
</div>
<div className="customer-delete-btn"><button onClick={() => deleteCustomer(item.customer_id)} className="btn btn-default">X</button></div>
<div className="customer-delete-btn"><button onClick={() => deleteCustomer(item.customer_id)} className="btn btn-default"><XSquare size={14}></XSquare> </button></div>
</div>}>
</Card>
}
})}
</div>
<div className="new-customers-dnd-item-container">
<div className="stop-index"><span>{`Stop ${customers?.length+1}`}</span><RecordCircleFill size={16} color={"#ccc"} className="ms-2"></RecordCircleFill> </div>
<div>
<button className="btn btn-primary btn-sm me-2" onClick={() => openAddPersonnelModal()}> + Add Personnel </button>
<button className="btn btn-primary btn-sm me-2" onClick={() => openAddAptGroupModal()}> + Add Apt Group </button>
</div>
</div>
</div>}
{
viewMode && <div className="customers-container mb-4">
{customers.map((item, index) => {
if (item?.customers) {
return <div className="customers-dnd-item-container">
<div className="stop-index"><span>{`Stop ${index+1}`}</span><RecordCircleFill size={16} color={"#0d6efd"} className="ms-2"></RecordCircleFill> </div>
<div className="customer-dnd-item" onClick={() => openEditAptGroupModal(index, item)}>
<span className="me-2">{item.customer_group} </span> <span>{item.customers[0]?.customer_group_address}</span>
<div className="customer-dnd-item-content">{item.customers.map(customer =>
<div key={customer.customer_id}>
<small className="me-2">{customer.customer_name}</small>
<small className="me-2">{customer.customer_address}</small>
<small className="me-2">{customer.customer_pickup_status}</small>
</div>)}
</div>
</div>
</div>
} else {
return <div className="customers-dnd-item-container">
<div className="stop-index"><span>{`Stop ${index+1}`}</span><RecordCircleFill size={16} color={"#0d6efd"} className="ms-2"></RecordCircleFill> </div>
<div className="customer-dnd-item">
<span>{item.customer_name} </span>
<small className="me-2">{item.customer_address}</small>
<small className="me-2">{item.customer_pickup_status}</small>
</div>
</div>
}
})}
</div>
}
<Modal show={showAddPersonnelModal} onHide={() => closeAddPersonnelModal()}>
<Modal.Header closeButton>
<Modal.Title>Add Personnel</Modal.Title>
</Modal.Header>
<Modal.Body>
<>
<h6>Type in UserId OR Name OR Address to Search:</h6>
<input type="text" className="mb-4" value={customerFilter} onChange={(e) => setCustomerFilter(e.target.value)}/>
<div className="mb-4">
{['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'].map(item => {
return <a key={item} className="me-2" onClick={() => {setLastNameFilter(item?.toLowerCase())} }>{item}</a>
})}
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Type in UserId OR Name OR Address to Search
</div>
<input type="text" className="mb-4" value={customerFilter} onChange={(e) => setCustomerFilter(e.target.value)}/>
</div>
</div>
<div>
<div className="app-main-content-fields-section">
{['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'].map(item => {
return <a key={item} className="me-2" onClick={() => {setLastNameFilter(item?.toLowerCase())} }>{item}</a>
})}
</div>
</div>
<a className="mb-4" onClick={() => setLastNameFilter(undefined)}>Clear All</a>
<div className="customers-container mt-4">
<Items currentItems={currentItems} />
@ -507,10 +566,10 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList}) => {
</>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={() => closeAddPersonnelModal()}>
<Button variant="link" onClick={() => closeAddPersonnelModal()}>
Cancel
</Button>
<Button variant="primary" onClick={() => addPersonnel()}>
<Button variant="primary" size="sm" onClick={() => addPersonnel()}>
Add Personnel
</Button>
</Modal.Footer>
@ -522,16 +581,36 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList}) => {
</Modal.Header>
<Modal.Body>
<>
<div className="mb-4">Group Name(Required): <input type="text" value={newGroupName} onChange={(e) => setNewGroupNameAction(e.target.value)}/></div>
<div className="mb-4">Group Address(Required): <input type="text" value={newGroupAddress} onChange={(e) => setNewGroupAddressAction(e.target.value)}/></div>
<h6>Type in user Id or Name to Search:</h6>
<input type="text" className="mb-4" value={customerFilter} onChange={(e) => setCustomerFilter(e.target.value)}/>
<div className="mb-4">
{['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'].map(item => {
return <a key={item} className="me-2" onClick={() => {setLastNameFilter(item?.toLowerCase())} }>{item}</a>
})}
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Group Name
<span className="required">*</span>
</div>
<input type="text" value={newGroupName} onChange={(e) => setNewGroupNameAction(e.target.value)}/>
</div>
<div className="me-4">
<div className="field-label">Group Address
<span className="required">*</span>
</div>
<input type="text" value={newGroupAddress} onChange={(e) => setNewGroupAddressAction(e.target.value)}/>
</div>
</div>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Type in user Id or Name to Search
</div>
<input type="text" className="mb-4" value={customerFilter} onChange={(e) => setCustomerFilter(e.target.value)}/>
</div>
</div>
<div>
<div className="mb-4">
{['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'].map(item => {
return <a key={item} className="me-2" onClick={() => {setLastNameFilter(item?.toLowerCase())} }>{item}</a>
})}
</div>
</div>
<a className="mb-4" onClick={() => setLastNameFilter(undefined)}>Clear All</a>
<div className="customers-container mt-4">
<ItemsGroup currentItems={currentItems} />
@ -559,10 +638,10 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList}) => {
</>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={() => closeAddAptGroupModal()}>
<Button variant="link" size="sm" onClick={() => closeAddAptGroupModal()}>
Cancel
</Button>
<Button variant="primary" onClick={() => addAptGroup()}>
<Button variant="primary" size="sm" onClick={() => addAptGroup()}>
Add Apt Group
</Button>
</Modal.Footer>
@ -574,16 +653,36 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList}) => {
</Modal.Header>
<Modal.Body>
<>
<div className="mb-4">Group Name(Required): <input type="text" value={newGroupName} onChange={(e) => setNewGroupNameAction(e.target.value)}/></div>
<div className="mb-4">Group Address(Required): <input type="text" value={newGroupAddress} onChange={(e) => setNewGroupAddressAction(e.target.value)}/></div>
<h6>Type in user Id or Name to Search:</h6>
<input type="text" className="mb-4" value={customerFilter} onChange={(e) => setCustomerFilter(e.target.value)}/>
<div className="mb-4">
{['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'].map(item => {
return <a key={item} className="me-2" onClick={() => {setLastNameFilter(item?.toLowerCase())} }>{item}</a>
})}
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Group Name
<span className="required">*</span>
</div>
<input type="text" value={newGroupName} onChange={(e) => setNewGroupNameAction(e.target.value)}/>
</div>
<div className="me-4">
<div className="field-label">Group Address
<span className="required">*</span>
</div>
<input type="text" value={newGroupAddress} onChange={(e) => setNewGroupAddressAction(e.target.value)}/>
</div>
</div>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Type in user Id or Name to Search
</div>
<input type="text" className="mb-4" value={customerFilter} onChange={(e) => setCustomerFilter(e.target.value)}/>
</div>
</div>
<div>
<div className="mb-4">
{['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'].map(item => {
return <a key={item} className="me-2" onClick={() => {setLastNameFilter(item?.toLowerCase())} }>{item}</a>
})}
</div>
</div>
<a className="mb-4" onClick={() => setLastNameFilter(undefined)}>Clear All</a>
<div className="customers-container mt-4">
<ItemsGroup currentItems={currentItems} />
@ -611,10 +710,10 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList}) => {
</>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={() => closeEditAptGroupModal()}>
<Button variant="link" size="sm" onClick={() => closeEditAptGroupModal()}>
Cancel
</Button>
<Button variant="primary" onClick={() => editAptGroup()}>
<Button variant="primary" size="sm" onClick={() => editAptGroup()}>
Update Apt Group
</Button>
</Modal.Footer>

View File

@ -2,12 +2,14 @@ import React, {useEffect, useState} from "react";
import { useSelector,useDispatch } from "react-redux";
import { useParams, useNavigate } from "react-router-dom";
import { selectAllRoutes, transRoutesSlice, vehicleSlice, selectTomorrowAllRoutes, selectAllActiveDrivers, selectAllActiveVehicles, selectHistoryRoutes } from "./../../store";
import { Modal, Button } from "react-bootstrap";
import { Modal, Button, Breadcrumb, Tabs, Tab } from "react-bootstrap";
import RouteCustomerEditor from "./RouteCustomerEditor";
import { AuthService, TransRoutesService } from "../../services";
import TimePicker from 'react-time-picker';
import 'react-time-picker/dist/TimePicker.css';
import moment from 'moment';
import { Archive, GripVertical } from "react-bootstrap-icons";
import { PERSONAL_ROUTE_STATUS } from "../../shared";
const RouteEdit = () => {
const params = useParams();
@ -36,6 +38,7 @@ const RouteEdit = () => {
const [currentRoute, setCurrentRoute] = useState(undefined);
const paramsQuery = new URLSearchParams(window.location.search);
const scheduleDate = paramsQuery.get('dateSchedule');
const editSection = paramsQuery.get('editSection')
const redirectToView = () => {
if (scheduleDate) {
navigate(`/trans-routes/${params.id}?dateSchedule=${scheduleDate}`);
@ -105,7 +108,7 @@ const RouteEdit = () => {
}
const showAddCheckItemModal = () => {
setNewChecklistItems(currentVehicle.checklist)
setNewChecklistItems(currentVehicle.checklist || [])
setShowAddCheckItem(true);
}
@ -162,94 +165,229 @@ const RouteEdit = () => {
return (
<>
<div className="list row mb-4">
<Breadcrumb>
<Breadcrumb.Item>Transportation</Breadcrumb.Item>
<Breadcrumb.Item active>
Transportation Routes
</Breadcrumb.Item>
</Breadcrumb>
<div className="col-md-12 text-primary">
<h5>{currentRoute?.name} <button className="btn btn-link btn-sm" onClick={() => {redirectToView()}}>Back To View</button></h5>
<h4>
Edit Route Information <button className="btn btn-link btn-sm" onClick={() => {redirectToView()}}>Back</button>
</h4>
</div>
</div>
<div className="list row mb-5">
<div className="col-md-7 col-sm-7 col-xs-12">
<button className="btn btn-primary btn-sm me-4" onClick={() => updateCurrentRoute()}> Save Route </button>
<button className="btn btn-default btn-sm" onClick={() => redirectToView()}> Cancel </button>
<button className="btn btn-danger btn-sm float-end" onClick={() => softDeleteCurrentRoute()}>Delete Route</button>
</div>
{errorMessage && <div className="col-md-6 col-sm-6 col-xs-12 alert alert-danger mt-4">{errorMessage}</div>}
</div>
<div className="list row mb-4">
<div className="col-md-6 mb-4">
Name(*): <input type="text" value={routeName || ''} onChange={e => setRouteName(e.target.value)}/>
</div>
<div className="col-md-6 mb-4">Vehicle(*): <select value={newVehicle} onChange={e => setNewVehicle(e.target.value)}>
{vehicles.map((vehicle) => (<option key={vehicle.id} value={vehicle.id}>{vehicle.vehicle_number}</option>))}
</select>
</div>
<div className="col-md-6 mb-4">Driver(*): <select value={newDriver} onChange={e => setNewDriver(e.target.value)}>
{drivers.map((driver) => <option key={driver.id} value={driver.id}>{driver.name}</option>)}
</select>
</div>
<div className="col-md-6 mb-4">Type(*): <select value={newRouteType} onChange={e => setNewRouteType(e.target.value)}>
<option value="inbound">Inbound</option>
<option value="outbound">Outbound</option>
</select>
</div>
{
newRouteType === 'outbound' && (<div className="col-md-6 mb-4">
Estimated Start TIme: <TimePicker disableClock={true} format={'HH:mm'} value={estimatedStartTime} onChange={setEstimatedStartTime} />
</div>)
}
</div>
<div className="list row mb-4">
<div className="col-md-12 mb-4">
<RouteCustomerEditor currentRoute={currentRoute} setNewCustomerList={setNewCustomerList}></RouteCustomerEditor>
</div>
</div>
{ newVehicle && newVehicle !== '' && (<div className="list row mb-4">
<div className="col-md-12 create-route-container">
<h6>Vehicle Info</h6>
<div className="list row">
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Vehicle Number: {vehicles.find(item => item.id === newVehicle)?.vehicle_number}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Tag: {vehicles.find(item => item.id === newVehicle)?.tag}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">EzPass: {vehicles.find(item => item.id === newVehicle)?.ezpass}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">GPS: {vehicles.find(item => item.id === newVehicle)?.gps_tag}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Capacity: {vehicles.find(item => item.id === newVehicle)?.capacity}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Status: {vehicles.find(item => item.id === newVehicle)?.status}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Mileage: {vehicles.find(item => item.id === newVehicle)?.mileage}</div>
</div>
<div className="app-main-content-list-container">
<div className="app-main-content-list-func-container">
<Tabs defaultActiveKey="routeOverview" id="route-view-tab">
<Tab eventKey="routeOverview" title="Route Information">
{ editSection === 'info' && <div className="multi-columns-container">
<div className="column-container">
<div className="column-card">
<h6 className="text-primary">Route Details</h6>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Route Name
<span className="required">*</span>
</div>
<input type="text" value={routeName || ''} onChange={e => setRouteName(e.target.value)}/>
</div>
<div className="me-4">
<div className="field-label">Vechile
<span className="required">*</span>
</div>
<select value={newVehicle} onChange={e => setNewVehicle(e.target.value)}>
{vehicles.map((vehicle) => (<option key={vehicle.id} value={vehicle.id}>{vehicle.vehicle_number}</option>))}
</select>
</div>
</div>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Driver
<span className="required">*</span>
</div>
<select value={newDriver} onChange={e => setNewDriver(e.target.value)}>
{drivers.map((driver) => <option key={driver.id} value={driver.id}>{driver.name}</option>)}
</select>
</div>
<div className="me-4">
<div className="field-label">Route Type
<span className="required">*</span>
</div>
<select value={newRouteType} onChange={e => setNewRouteType(e.target.value)}>
<option value="inbound">Inbound</option>
<option value="outbound">Outbound</option>
</select>
</div>
</div>
{ newRouteType === 'outbound' && <div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Estimated Start Time
</div>
<TimePicker disableClock={true} format={'HH:mm'} value={estimatedStartTime} onChange={setEstimatedStartTime} />
</div>
</div>}
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Vehicle Checklist
</div>
{ currentVehicle?.checklist?.length > 0 && (<table className="mb-4">
<tbody>
{currentVehicle.checklist.map((item, index) => (<tr key={index}><td>{item}</td></tr>))}
</tbody>
</table>) }
<div className="mb-4"><button className="btn btn-link btn-sm" onClick={() => showAddCheckItemModal()}>+Add Check Items</button></div>
<div className="mb-4"><button className="btn btn-link btn-sm" onClick={() => showCopyCheckItemModal()}>Copy Checklist From Other Route</button></div>
</div>
</div>
<div className="list row mb-5">
<div className="col-md-12 col-sm-12 col-xs-12">
<button className="btn btn-default btn-sm float-right" onClick={() => redirectToView()}> Cancel </button>
<button className="btn btn-primary btn-sm float-right" onClick={() => updateCurrentRoute()}> Save </button>
</div>
{errorMessage && <div className="col-md-12 col-sm-12 col-xs-12 alert alert-danger mt-4">{errorMessage}</div>}
</div>
</div>
</div>
<div className="column-container">
{ newVehicle && newVehicle !== '' && <div className="column-card mb-4">
<h6 className="text-primary">Vehicle Information</h6>
<div className="app-main-content-fields-section short">
<div className="field-body">
<div className="field-label">Vehicle Number</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.vehicle_number}</div>
</div>
<div className="field-body">
<div className="field-label">Seating Capacity</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.capacity}</div>
</div>
<div className="field-body">
<div className="field-label">Mileage</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.mileage}</div>
</div>
<div className="field-body">
<div className="field-label">Make</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.make}</div>
</div>
<div className="field-body">
<div className="field-label">Model</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.model}</div>
</div>
</div>
<div className="app-main-content-fields-section short">
<div className="field-body">
<div className="field-label">License Plate</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.tag}</div>
</div>
<div className="field-body">
<div className="field-label">Year</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.year}</div>
</div>
<div className="field-body">
<div className="field-label">GPS ID</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.gps_tag}</div>
</div>
<div className="field-body">
<div className="field-label">EZPass</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.ezpass}</div>
</div>
<div className="field-body">
<div className="field-label">Vin</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.vin || ''}</div>
</div>
</div>
</div>}
{
newDriver && newDriver !== '' && <div className="column-card">
<h6 className="text-primary">Driver Information</h6>
<div className="text-primary">Personal Details</div>
<div className="app-main-content-fields-section short">
<div className="field-body">
<div className="field-label">Driver Name</div>
<div className="field-value">{drivers.find(item => item.id === newDriver)?.name}</div>
</div>
<div className="field-body">
<div className="field-label">Preferred Name</div>
<div className="field-value">{drivers.find(item => item.id === newDriver)?.name_cn}</div>
</div>
<div className="field-body">
<div className="field-label">Job Title</div>
<div className="field-value">{drivers.find(item => item.id === newDriver)?.title}</div>
</div>
<div className="field-body">
<div className="field-label">Job Status</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.employment_status}</div>
</div>
</div>
<div className="app-main-content-fields-section short">
<div className="field-body">
<div className="field-label">Driver Capacity</div>
<div className="field-value">{drivers.find(item => item.id === newDriver)?.driver_capacity}</div>
</div>
<div className="field-body">
<div className="field-label">Phone Number</div>
<div className="field-value">{drivers.find(item => item.id === newDriver)?.phone}</div>
</div>
<div className="field-body">
<div className="field-label">Email</div>
<div className="field-value">{vehicles.find(item => item.id === newVehicle)?.email}</div>
</div>
</div>
</div>
}
</div>
</div> }
{
editSection === 'assignment' && <div className="multi-columns-container">
<div className="column-container">
<div className="column-card adjust">
<div className="col-md-12 mb-4">
<RouteCustomerEditor currentRoute={currentRoute} setNewCustomerList={setNewCustomerList}></RouteCustomerEditor>
</div>
</div>
</div>
<div className="column-container">
<div className="column-card adjust">
<h6 className="text-primary">Scheduled Absences ({currentRoute?.route_customer_list?.filter(item => item?.customer_route_status === PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT)?.length || 0})</h6>
<div className="customers-container mb-4">
{
currentRoute?.route_customer_list.filter(customer => customer?.customer_route_status === PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT)?.map((abItem) => {
return <div className="customers-dnd-item-container-absent">
<GripVertical className="me-4" size={14}></GripVertical>
<div className="customer-dnd-item">
<span>{abItem.customer_name} </span>
<small className="me-2">{abItem.customer_address}</small>
<small className="me-2">{abItem.customer_pickup_status}</small>
</div>
</div>
})
}
</div>
</div>
</div>
</div>
}
</Tab>
</Tabs>
<div className="list-func-panel">
<button className="btn btn-primary" onClick={() => softDeleteCurrentRoute()}><Archive size={16} className="me-2"></Archive>Archive</button>
</div>
</div>)}
{ newDriver && newDriver !== '' && (<div className="list row mb-4">
<div className="col-md-12 create-route-container">
<h6>Driver Info</h6>
<div className="list row">
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Name: {drivers.find(item => item.id === newDriver)?.name}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Preferred Name: {drivers.find(item => item.id === newDriver)?.name_cn}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Driver Capacity: {drivers.find(item => item.id === newDriver)?.driver_capacity}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Roles: {drivers.find(item => item.id === newDriver)?.roles}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Phone: {drivers.find(item => item.id === newDriver)?.phone}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Email: {drivers.find(item => item.id === newDriver)?.email}</div>
<div className="col-md-4 col-sm-6 col-xs-12 mb-2">Employment Status: {drivers.find(item => item.id === newDriver)?.employment_status}</div>
</div>
</div>
</div>)}
<div className="list row mb-4">
<div className="col-md-6">
<h6>Vehicle Checklist</h6>
{ currentVehicle?.checklist?.length > 0 && (<table className="mb-4">
<tbody>
{currentVehicle.checklist.map((item, index) => (<tr key={index}><td>{item}</td></tr>))}
</tbody>
</table>) }
<div className="mb-4"><button className="btn btn-link btn-sm" onClick={() => showAddCheckItemModal()}>+Add Check Items</button></div>
<div className="mb-4"><button className="btn btn-link btn-sm" onClick={() => showCopyCheckItemModal()}>Copy Checklist From Other Route</button></div>
</div>
</div>
<Modal show={showAddCheckItem} onHide={() => closeAddCheckItemModal()}>
<Modal.Header closeButton>
<Modal.Title>Add New Checklist Item</Modal.Title>
</Modal.Header>
<Modal.Body>
<>
{newChecklistItems.map((item, index) => (<div className="mb-4" key={index}><input type="text" value={item} onChange={(e) => setNewChecklistItems([...newChecklistItems].map((a, index1) => {if (index1 === index) {return e.target.value;} return a;}))}/>
{newChecklistItems?.map((item, index) => (<div className="mb-4" key={index}><input type="text" value={item} onChange={(e) => setNewChecklistItems([...newChecklistItems].map((a, index1) => {if (index1 === index) {return e.target.value;} return a;}))}/>
<button className="btn btn-link btn-sm" onClick={(e) => setNewChecklistItems([...newChecklistItems].filter((value, index1) => index1 != index))}>Remove</button>
</div>))}
<button className="btn btn-link" onClick={() => addItemToArray()}>+Add New Item</button>

View File

@ -3,9 +3,11 @@ import { useSelector } from "react-redux";
import { useParams, useNavigate } from "react-router-dom";
import { selectAllRoutes, selectTomorrowAllRoutes, selectAllActiveDrivers, selectAllActiveVehicles, selectHistoryRoutes } from "./../../store";
import PersonnelSection from "./PersonnelSection";
import { Modal, Button } from "react-bootstrap";
import { AuthService, CustomerService, SignatureRequestService } from "../../services";
import moment from 'moment';
import { Breadcrumb, Tabs, Tab, Dropdown, Spinner, Modal, Button } from "react-bootstrap";
import { Download, Pencil } from "react-bootstrap-icons";
import RouteCustomerEditor from "./RouteCustomerEditor";
const RouteView = () => {
const params = useParams();
@ -42,21 +44,13 @@ const RouteView = () => {
}
}
const directToDashboad = () => {
if (tomorrowRoutes.find(item => item.id === params.id)) {
navigate(`/trans-routes/schedule?dateSchedule=${moment(tomorrowRoutes.find(item => item.id === params.id).schedule_date).format('YYYY-MM-DD')}`);
} else {
if (historyRoutes.find(item => item.id === params.id)) {
navigate(`/trans-routes/history`);
} else {
navigate(`/trans-routes/dashboard`);
}
}
navigate(`/trans-routes/dashboard?dateSchedule=${moment(currentRoute?.schedule_date).format('YYYY-MM-DD')}`);
}
const edit = () => {
const edit = (editSection) => {
if (scheduleDate) {
navigate(`/trans-routes/edit/${currentRoute?.id}?dateSchedule=${scheduleDate}`)
navigate(`/trans-routes/edit/${currentRoute?.id}?dateSchedule=${scheduleDate}&editSection=${editSection}`)
} else {
navigate(`/trans-routes/edit/${currentRoute?.id}`)
navigate(`/trans-routes/edit/${currentRoute?.id}?editSection=${editSection}`)
}
}
const deleteFile = () => {
@ -93,51 +87,114 @@ const RouteView = () => {
}, [currentRoute]);
return (
<>
<div className="list row">
<div className="col-md-12 text-primary mb-2">
<h5>{currentRoute?.name} <button className="btn btn-link btn-sm" onClick={() => directToDashboad()}>Back</button> {AuthService.canAddOrEditRoutes() && <button className="btn btn-primary btn-sm" onClick={() => edit()}>Update</button>}</h5>
</div>
</div>
<div className="list row mb-4">
<div className="col-md-4 col-sm-6 col-xs-6">
<div className="mb-2">Vehicle Number: {currentVehicle?.vehicle_number} <button type="button" className="btn btn-info btn-sm" onClick={() => openModal()}>Vehicle Details</button></div>
<div className="mb-2">Total Customers: {currentRoute?.route_customer_list?.length || 0}</div>
<div className="mb-2">Driver Name: {currentDriver?.name}</div>
<div className="mb-2">Route Type: { currentRoute?.type }</div>
{signature && <div className="mb-2">Driver Signature: {signature && <img width="100px" src={`data:image/jpg;base64, ${signature}`}/>}</div>}
{!signature && !signatureRequest && <div className="mb-2"><button className="btn btn-sm btn-primary" onClick={() => generateSignatureRequest()}>Generate Signature Link For Driver</button></div>}
</div>
<div className="col-md-4 col-sm-6 col-xs-6">
<div className="mb-2">Route Start Time: {currentRoute?.start_time && (new Date(currentRoute?.start_time))?.toLocaleTimeString()}</div>
<div className="mb-2">Route End Time: {currentRoute?.end_time && (new Date(currentRoute?.end_time))?.toLocaleTimeString()}</div>
{currentRoute?.type === 'inbound' && <div className="mb-2">Arrive Center Time: {currentRoute?.end_time && (new Date(currentRoute?.end_time))?.toLocaleTimeString()}</div>}
{currentRoute?.type === 'outbound' &&<div className="mb-2">Leave Center Time: {currentRoute?.start_time && (new Date(currentRoute?.start_time))?.toLocaleTimeString()}</div>}
{currentRoute?.type === 'outbound' && <div className="mb-2">Estimated Start Time: {currentRoute?.estimated_start_time && (new Date(currentRoute?.estimated_start_time))?.toLocaleTimeString()}</div>}
</div>
<div className="col-md-4 col-sm-6 col-xs-6">
<div className="mb-2">Start Mileage: { currentRoute?.start_mileage}</div>
<div className="mb-2">End Mileage: { currentRoute?.end_mileage}</div>
</div>
<div className="col-md-12">{!signature && signatureRequest && <div className="alert alert-success fade show mb-2 mt-2" role="alert">
<div>Please send this to the driver to get signature:</div>
<div>{`${window.location.origin}/signature/${signatureRequest?.id}`}</div>
</div>}</div>
</div>
<div className="list row">
<div className="col-md-12 mb-4">
{currentRoute && <PersonnelSection transRoutes={[currentRoute]} showCompletedInfo={true} showGroupInfo={true} isInbound={currentRoute?.type === 'inbound' } allowForceEdit={AuthService.canViewRoutes()} sectionName="Personnel Status (click on each user to edit)" relatedOutbound={getRelatedOutboundRoutesForThisView()} vehicle={currentVehicle} driverName={currentDriver?.name} deleteFile={deleteFile}/>}
<Breadcrumb>
<Breadcrumb.Item>Transportation</Breadcrumb.Item>
<Breadcrumb.Item active>
Transportation Routes
</Breadcrumb.Item>
</Breadcrumb>
<div className="col-md-12 text-primary">
<h4>
View Route Information <button className="btn btn-link btn-sm" onClick={() => {directToDashboad()}}>Back</button>
</h4>
</div>
</div>
<br></br>
<div className="list row">
<div className="col-md-12 mb-4"><h5>Checklist:</h5>
{currentRoute && currentRoute?.checklist_result?.map(item => <div>{`${item?.item}: ${item?.result ? 'Yes': "No"}`}</div>)}
{currentRoute && currentRoute?.checklist_result.length === 0 && <>No Checklist found</>}
<div className="app-main-content-list-container">
<div className="app-main-content-list-func-container">
<Tabs defaultActiveKey="routeOverview" id="route-view-tab">
<Tab eventKey="routeOverview" title="Route Information">
<h6 className="text-primary">Route Details <button className="btn btn-sm btn-primary" onClick={() => edit('info')}><Pencil size={16} className="me-2"></Pencil>Edit </button></h6>
<div className="app-main-content-fields-section">
<div className="field-body">
<div className="field-label">Route Name</div>
<div className="field-value">{currentRoute?.name}</div>
</div>
<div className="field-body">
<div className="field-label">Vehicle</div>
<div className="field-value">{currentVehicle?.vehicle_number}</div>
</div>
<div className="field-body">
<div className="field-label">Driver</div>
<div className="field-value">{currentDriver?.name}</div>
</div>
<div className="field-body">
<div className="field-label">Route Type</div>
<div className="field-value">{currentRoute?.type}</div>
</div>
</div>
<div className="app-main-content-fields-section">
<div className="field-body">
<div className="field-label">Route Start Time</div>
<div className="field-value">{currentRoute?.start_time && (new Date(currentRoute?.start_time))?.toLocaleTimeString()}</div>
</div>
<div className="field-body">
<div className="field-label">Route End Time</div>
<div className="field-value">{currentRoute?.end_time && (new Date(currentRoute?.end_time))?.toLocaleTimeString()}</div>
</div>
{currentRoute?.type === 'inbound' &&<div className="field-body">
<div className="field-label">Arrive Center Time</div>
<div className="field-value">{currentRoute?.end_time && (new Date(currentRoute?.end_time))?.toLocaleTimeString()}</div>
</div>}
{currentRoute?.type === 'outbound' &&<div className="field-body">
<div className="field-label">Leave Center Time</div>
<div className="field-value">{currentRoute?.start_time && (new Date(currentRoute?.start_time))?.toLocaleTimeString()}</div>
</div>}
{currentRoute?.type === 'outbound' &&<div className="field-body">
<div className="field-label">Estimated Start Time</div>
<div className="field-value">{currentRoute?.estimated_start_time && (new Date(currentRoute?.estimated_start_time))?.toLocaleTimeString()}</div>
</div>}
</div>
<div className="app-main-content-fields-section">
<div className="field-body">
<div className="field-label">Start Mileage</div>
<div className="field-value">{ currentRoute?.start_mileage}</div>
</div>
<div className="field-body">
<div className="field-label">End Mileage</div>
<div className="field-value">{ currentRoute?.end_mileage}</div>
</div>
</div>
<div className="app-main-content-fields-section">
{signature &&<div className="field-body">
<div className="field-label">Driver Signature</div>
<div className="field-value">
{signature && <img width="100px" src={`data:image/jpg;base64, ${signature}`}/>}
</div>
</div>}
{!signature && !signatureRequest && <div className="field-body">
<div className="field-label">Signature Request</div>
<div className="field-value"><button className="btn btn-sm btn-primary" onClick={() => generateSignatureRequest()}>Generate Signature Link For Driver</button></div>
</div>}
{!signature && signatureRequest && <div className="alert alert-success fade show mb-2 mt-2" role="alert">
<div>Please send this to the driver to get signature:</div>
<div>{`${window.location.origin}/signature/${signatureRequest?.id}`}</div>
</div>}
</div>
<div className="app-main-content-fields-section">
<div className="field-body">
<div className="field-label">Checklist</div>
<div className="field-value">
{currentRoute && currentRoute?.checklist_result?.map(item => <div>{`${item?.item}: ${item?.result ? 'Yes': "No"}`}</div>)}
{currentRoute && currentRoute?.checklist_result.length === 0 && <>No Checklist found</>}</div>
</div>
</div>
<RouteCustomerEditor currentRoute={currentRoute} viewMode={true} editFun={edit}></RouteCustomerEditor>
</Tab>
<Tab eventKey="routeStatus" title="Route Status">
<div className="list row">
<div className="col-md-12 mb-4">
{currentRoute && <PersonnelSection transRoutes={[currentRoute]} showCompletedInfo={true} showGroupInfo={true} isInbound={currentRoute?.type === 'inbound' } allowForceEdit={AuthService.canViewRoutes()} sectionName="Personnel Status (click on each user to edit)" relatedOutbound={getRelatedOutboundRoutesForThisView()} vehicle={currentVehicle} driverName={currentDriver?.name} deleteFile={deleteFile}/>}
</div>
</div>
</Tab>
</Tabs>
<div className="list-func-panel">
<button className="btn btn-primary"><Download size={16} className="me-2"></Download>Export</button>
</div>
</div>
</div>
<Modal show={showVehicleDetails} onHide={() => closeModal()}>
<Modal show={showVehicleDetails} onHide={() => closeModal()}>
<Modal.Header closeButton>
<Modal.Title>Vehicle Info</Modal.Title>
</Modal.Header>
@ -158,6 +215,9 @@ const RouteView = () => {
</Button>
</Modal.Footer>
</Modal>
</div>
</>
);

View File

@ -1105,7 +1105,7 @@ const RoutesDashboard = () => {
show={showOriginDateDropdown}
disabled
onToggle={() => setShowOriginDateDropdown(!showOriginDateDropdown)}
autoClose="outside"
autoClose={false}
>
<Dropdown.Toggle variant="primary">
<CalendarWeek size={16} className="me-2"></CalendarWeek>Select Date to View & Copy From
@ -1118,7 +1118,7 @@ const RoutesDashboard = () => {
className="me-2"
show={showTargetDateDropdown}
onToggle={() => setShowTargetDateDropdown(!showTargetDateDropdown)}
autoClose="outside"
autoClose={false}
>
<Dropdown.Toggle variant="primary">
<CalendarWeek size={16} className="me-2"></CalendarWeek>Select Date to Copy To
@ -1166,8 +1166,8 @@ const RoutesDashboard = () => {
{ currentTab === 'allRoutesStatus' && <>
{/* <input className="me-2 with-search-icon" type="text" placeholder="Search" value={keyword} onChange={(e) => setKeyword(e.currentTarget.value)} /> */}
<Dropdown
key={'signature-date'}
id="signature-date"
key={'status-date'}
id="status-date"
className="me-2"
show={showDateDropdown}
onToggle={() => setShowDateDropdown(!showDateDropdown)}

View File

@ -121,7 +121,7 @@ const ViewVehicle = () => {
<div className="list-func-panel">
<button className="btn btn-primary me-2" onClick={() => goToEdit(currentVehicle?.id)}><Pencil size={16} className="me-2"></Pencil>Edit</button>
<button className="btn btn-primary me-2" onClick={() => deactivateVehicle()}><Archive size={16} className="me-2"></Archive>Archive</button>
<button className="btn btn-primary"><Download size={16} className="me-2"></Download>Download</button>
<button className="btn btn-primary"><Download size={16} className="me-2"></Download>Export</button>
</div>
</div>
</div>