Routes module
This commit is contained in:
parent
2fc3adfaec
commit
334bd5542b
@ -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"
|
||||
]
|
||||
}
|
||||
@ -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>
|
||||
@ -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"
|
||||
]
|
||||
}
|
||||
@ -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>
|
||||
@ -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
@ -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;
|
||||
|
||||
@ -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 />
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
// }
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
|
||||
</>
|
||||
);
|
||||
|
||||
@ -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)}
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user