1155 lines
58 KiB
JavaScript
1155 lines
58 KiB
JavaScript
import React, {useState} from "react";
|
|
import { useDispatch } from "react-redux";
|
|
import { CUSTOMER_TYPE_TEXT, PERSONAL_ROUTE_STATUS, PERSONAL_ROUTE_STATUS_TEXT, PICKUP_STATUS, PICKUP_STATUS_TEXT, REPORT_TYPE } from "../../shared";
|
|
import { Modal, Button } from "react-bootstrap";
|
|
import { transRoutesSlice } from "./../../store";
|
|
import { CSVLink } from "react-csv";
|
|
import { ReportService, CustomerService } from "../../services";
|
|
import TimePicker from 'react-time-picker';
|
|
import 'react-time-picker/dist/TimePicker.css';
|
|
import moment from 'moment';
|
|
import { useNavigate } from "react-router-dom";
|
|
|
|
const PersonnelInfoTable = ({transRoutes, showCompletedInfo,
|
|
showGroupInfo, allowForceEdit, showFilter,
|
|
driverName, vehicle, relatedOutbound,
|
|
vehicles, isInbound, deleteFile,
|
|
keyword, statusFilter, customerNameFilter,
|
|
customerTableId, routeTypeFilter, customerTypeFilter
|
|
}) => {
|
|
const [show, setShow] = useState(false);
|
|
const [showGroupEditor, setShowGroupEditor] = useState(false);
|
|
const [showBulkUpdateModal, setShowBulkUpdateModal] = useState(false);
|
|
const [customerInEdit, setCustomerInEdit] = useState(undefined);
|
|
const [customersInEdit, setCustomersInEdit] = useState([]);
|
|
// const [statusFilter, setStatusFilter] = useState('');
|
|
// const [customerTypeFilter, setCustomerTypeFilter] = useState('');
|
|
// const [customerNameFilter, setCustomerNameFilter] = useState('');
|
|
// const [customerTableId, setCustomerTableId] = useState('');
|
|
// const [routeTypeFilter, setRouteTypeFilter] = useState('');
|
|
const [customerCheckInTime, setCustomerCheckInTime] = useState('');
|
|
const [customerCheckOutTime, setCustomerCheckOutTime] = useState('');
|
|
const [customerPickUpTime, setCustomerPickUpTime] = useState('');
|
|
const [customerDropOffTime, setCustomerDropOffTime] = useState('');
|
|
const [customerEstimatedPickUpTime, setCustomerEstimatedPickUpTime] = useState('');
|
|
const [customerEstimatedDropOffTime, setCustomerEstimatedDropOffTime] = useState('');
|
|
const [customerStatusInRoute, setCustomerStatusInRoute] = useState(false);
|
|
const [customerPickupStatusInRoute, setCustomerPickupStatusInRoute] = useState('');
|
|
const [customerTransferToRoute, setCustomerTransferToRoute] = useState('');
|
|
const [customerAddressOverride, setCustomerAddressOverride] = useState('');
|
|
const [customerAddressesList, setCustomerAddressesList] = useState([]);
|
|
const [customerNote, setCustomerNote] = useState('');
|
|
const [bulkEnterCenterTime, setBulkEnterCenterTime] = useState('');
|
|
const [bulkLeaveCenterTime, setBulkLeaveCenterTime] = useState('');
|
|
const [bulkCustomerRouteStatus, setBulkCustomerRouteStatus] = useState('');
|
|
const [bulkCustomerPickupStatus, setBulkCustomerPickupStatus] = useState('');
|
|
const dispatch = useDispatch();
|
|
const navigate = useNavigate();
|
|
const { updateRoute } = transRoutesSlice.actions;
|
|
const params = new URLSearchParams(window.location.search);
|
|
const scheduleDate = params.get('dateSchedule');
|
|
// const clearFilters = () => {
|
|
// setStatusFilter('');
|
|
// setCustomerTypeFilter('');
|
|
// setRouteTypeFilter('');
|
|
// setCustomerNameFilter('');
|
|
// setCustomerTableId('');
|
|
// }
|
|
|
|
const openForceEditModal = (customer) => {
|
|
if (allowForceEdit) {
|
|
// CustomerService.getCustomer(customer?.customer_id).then(data => {
|
|
// const fullCustomer = data?.data;
|
|
// const result = [];
|
|
// if (fullCustomer?.address1 && fullCustomer?.address1 !== '') {
|
|
// result.push(fullCustomer?.address1);
|
|
// }
|
|
// if (fullCustomer?.address2 && fullCustomer?.address2 !== '') {
|
|
// result.push(fullCustomer?.address2);
|
|
// }
|
|
// if (fullCustomer?.address3 && fullCustomer?.address3 !== '') {
|
|
// result.push(fullCustomer?.address3);
|
|
// }
|
|
// if (fullCustomer?.address4 && fullCustomer?.address4 !== '') {
|
|
// result.push(fullCustomer?.address4);
|
|
// }
|
|
// if (fullCustomer?.address5 && fullCustomer?.address5 !== '') {
|
|
// result.push(fullCustomer?.address5);
|
|
// }
|
|
// setCustomerAddressesList(result);
|
|
// })
|
|
setShow(true);
|
|
setCustomerInEdit(customer);
|
|
setCustomerCheckInTime(customer.customer_enter_center_time ? new Date(customer.customer_enter_center_time) : '');
|
|
setCustomerCheckOutTime(customer.customer_leave_center_time ? new Date(customer.customer_leave_center_time) : '');
|
|
setCustomerPickUpTime(customer.customer_pickup_time ? new Date(customer.customer_pickup_time) : '');
|
|
setCustomerDropOffTime(customer.customer_dropoff_time ? new Date(customer.customer_dropoff_time) : '');
|
|
setCustomerEstimatedPickUpTime(customer.customer_estimated_pickup_time ? new Date(customer.customer_estimated_pickup_time) : '');
|
|
setCustomerEstimatedDropOffTime(customer.customer_estimated_dropoff_time ? new Date(customer.customer_estimated_dropoff_time) : '');
|
|
setCustomerStatusInRoute(customer.customer_route_status);
|
|
setCustomerPickupStatusInRoute(customer.customer_pickup_status);
|
|
setCustomerNote(customer?.customer_note);
|
|
}
|
|
}
|
|
|
|
const goToReportWithSignature = () => {
|
|
navigate(`/trans-routes/route-report-with-signature/${transRoutes[0]?.id}`)
|
|
}
|
|
|
|
const openForceEditGroupModal = (customers) => {
|
|
if (allowForceEdit) {
|
|
setShowGroupEditor(true);
|
|
setCustomersInEdit(customers);
|
|
setCustomerCheckInTime('');
|
|
setCustomerCheckOutTime('');
|
|
setCustomerDropOffTime('');
|
|
setCustomerPickUpTime('');
|
|
setCustomerStatusInRoute('');
|
|
setCustomerEstimatedDropOffTime('');
|
|
setCustomerEstimatedPickUpTime('');
|
|
setCustomerAddressOverride('');
|
|
setCustomerNote('');
|
|
}
|
|
}
|
|
|
|
const closeGroupEditorModal = () => {
|
|
setCustomersInEdit([]);
|
|
setCustomerCheckInTime('');
|
|
setCustomerCheckOutTime('');
|
|
setCustomerStatusInRoute('');
|
|
setCustomerDropOffTime('');
|
|
setCustomerPickUpTime('');
|
|
setCustomerEstimatedDropOffTime('');
|
|
setCustomerEstimatedPickUpTime('');
|
|
setCustomerNote('');
|
|
setShowGroupEditor(false);
|
|
|
|
}
|
|
|
|
const closeModal = () => {
|
|
setCustomerCheckInTime('');
|
|
setCustomerCheckOutTime('');
|
|
setCustomerDropOffTime('');
|
|
setCustomerPickUpTime('');
|
|
setCustomerStatusInRoute(false);
|
|
setCustomerPickupStatusInRoute('');
|
|
setCustomerAddressesList([]);
|
|
setCustomerEstimatedDropOffTime('');
|
|
setCustomerEstimatedPickUpTime('');
|
|
setCustomerAddressOverride('');
|
|
setCustomerNote('');
|
|
setShow(false);
|
|
}
|
|
|
|
const combineDateAndTime = (date, time) => {
|
|
const dateObj = moment(date);
|
|
const timeObj = moment(time, 'HH:mm');
|
|
dateObj.set({
|
|
hour: timeObj.get('hour'),
|
|
minute: timeObj.get('minute'),
|
|
second: timeObj.get('second')
|
|
})
|
|
return dateObj;
|
|
}
|
|
|
|
const saveRouteCustomerInfo = () => {
|
|
const routeId = customerInEdit.routeId;
|
|
let removeSignature = false
|
|
if (routeId) {
|
|
let requestBody = transRoutes.find((route) => route.id === routeId);
|
|
const dateStr = requestBody?.schedule_date || '';
|
|
const newCustomerList = requestBody.route_customer_list.map((item) => {
|
|
let addedFields = {};
|
|
if (item.customer_id === customerInEdit.customer_id) {
|
|
if (customerCheckInTime && customerCheckInTime !== '') {
|
|
addedFields.customer_enter_center_time = combineDateAndTime(dateStr, customerCheckInTime);
|
|
addedFields.customer_route_status = PERSONAL_ROUTE_STATUS.IN_CENTER;
|
|
} else {
|
|
addedFields.customer_enter_center_time = null;
|
|
}
|
|
if (customerCheckOutTime && customerCheckOutTime !=='') {
|
|
addedFields.customer_leave_center_time = combineDateAndTime(dateStr, customerCheckOutTime);
|
|
addedFields.customer_route_status = PERSONAL_ROUTE_STATUS.LEFT_CENTER;
|
|
} else {
|
|
addedFields.customer_leave_center_time = null;
|
|
}
|
|
if (customerPickUpTime && customerPickUpTime !=='') {
|
|
addedFields.customer_pickup_time = combineDateAndTime(dateStr, customerPickUpTime);
|
|
addedFields.customer_route_status = PERSONAL_ROUTE_STATUS.PICKED;
|
|
removeSignature = true;
|
|
} else {
|
|
addedFields.customer_pickup_time = null;
|
|
}
|
|
if (customerDropOffTime && customerDropOffTime !=='') {
|
|
addedFields.customer_dropoff_time = combineDateAndTime(dateStr, customerDropOffTime);
|
|
addedFields.customer_route_status = PERSONAL_ROUTE_STATUS.DROPPED_OFF;
|
|
removeSignature = true;
|
|
} else {
|
|
addedFields.customer_dropoff_time = null;
|
|
}
|
|
if (customerEstimatedPickUpTime && customerEstimatedPickUpTime !=='') {
|
|
addedFields.customer_estimated_pickup_time = combineDateAndTime(dateStr, customerEstimatedPickUpTime);
|
|
} else {
|
|
addedFields.customer_estimated_pickup_time = null;
|
|
}
|
|
// if (customerEstimatedDropOffTime && customerEstimatedDropOffTime !=='') {
|
|
// addedFields.customer_estimated_dropoff_time = customerEstimatedDropOffTime;
|
|
// }
|
|
if (customerAddressOverride && customerAddressOverride !=='') {
|
|
addedFields.customer_address_override = customerAddressOverride;
|
|
}
|
|
if (customerStatusInRoute) {
|
|
addedFields.customer_route_status = customerStatusInRoute;
|
|
} else {
|
|
addedFields.customer_route_status = PERSONAL_ROUTE_STATUS.NO_STATUS;
|
|
}
|
|
if (customerPickupStatusInRoute && customerPickupStatusInRoute !=='') {
|
|
addedFields.customer_pickup_status = customerPickupStatusInRoute;
|
|
}
|
|
if (customerTransferToRoute && customerTransferToRoute !=='') {
|
|
addedFields.customer_transfer_to_route = customerTransferToRoute;
|
|
}
|
|
if (customerNote && customerNote !== '') {
|
|
addedFields.customer_note = customerNote;
|
|
}
|
|
}
|
|
return Object.assign({}, item, addedFields);;
|
|
})
|
|
requestBody = Object.assign({}, requestBody, {route_customer_list: newCustomerList, updatedAt: new Date(), updatedBy: 'admin'});
|
|
let finalParams = { id: routeId, data: requestBody };
|
|
if (scheduleDate) {
|
|
finalParams = Object.assign({}, finalParams, {dateText: moment(scheduleDate).format('MM/DD/YYYY'), fromSchedule: true})
|
|
}
|
|
if (dateStr !== '' && dateStr !== moment().format('MM/DD/YYYY')) {
|
|
finalParams = Object.assign({}, finalParams, {dateText: dateStr})
|
|
}
|
|
dispatch(updateRoute(finalParams));
|
|
// if (removeSignature && deleteFile) {
|
|
// deleteFile();
|
|
// }
|
|
} else {
|
|
window.alert('Fail to update Route: no route Id is attached to this customer.')
|
|
}
|
|
setShow(false);
|
|
}
|
|
|
|
const saveRouteGroupCustomerInfo = () => {
|
|
const routeId = customersInEdit[0]?.routeId;
|
|
let removeSignature = false
|
|
if (routeId) {
|
|
let requestBody = transRoutes.find((route) => route.id === routeId);
|
|
const dateStr = requestBody?.schedule_date || '';
|
|
const newCustomerList = requestBody.route_customer_list.map((item) => {
|
|
let addedFields = {};
|
|
if (customersInEdit?.find((customerItem) => customerItem.customer_id === item.customer_id)) {
|
|
if (customerCheckInTime && customerCheckInTime !== '') {
|
|
addedFields.customer_enter_center_time = combineDateAndTime(dateStr, customerCheckInTime);
|
|
addedFields.customer_route_status = PERSONAL_ROUTE_STATUS.IN_CENTER;
|
|
}
|
|
if (customerCheckOutTime && customerCheckOutTime !=='') {
|
|
addedFields.customer_leave_center_time = combineDateAndTime(dateStr, customerCheckOutTime);
|
|
addedFields.customer_route_status = PERSONAL_ROUTE_STATUS.LEFT_CENTER;
|
|
}
|
|
if (customerPickUpTime && customerPickUpTime !=='') {
|
|
addedFields.customer_pickup_time = combineDateAndTime(dateStr, customerPickUpTime);
|
|
addedFields.customer_route_status = PERSONAL_ROUTE_STATUS.PICKED;
|
|
removeSignature = true;
|
|
}
|
|
if (customerDropOffTime && customerDropOffTime !=='') {
|
|
addedFields.customer_dropoff_time = combineDateAndTime(dateStr, customerDropOffTime);
|
|
addedFields.customer_route_status = PERSONAL_ROUTE_STATUS.DROPPED_OFF;
|
|
removeSignature = true;
|
|
}
|
|
if (customerStatusInRoute) {
|
|
addedFields.customer_route_status = customerStatusInRoute;
|
|
} else {
|
|
addedFields.customer_route_status = PERSONAL_ROUTE_STATUS.NO_STATUS;
|
|
}
|
|
}
|
|
return Object.assign({}, item, addedFields);;
|
|
})
|
|
requestBody = Object.assign({}, requestBody, {route_customer_list: newCustomerList, updatedAt: new Date(), updatedBy: 'admin'});
|
|
let finalParams = { id: routeId, data: requestBody };
|
|
if (scheduleDate) {
|
|
finalParams = Object.assign({}, finalParams, {dateText: moment(scheduleDate).format('MM/DD/YYYY'), fromSchedule: true})
|
|
}
|
|
if (dateStr !== '' && dateStr !== moment().format('MM/DD/YYYY')) {
|
|
finalParams = Object.assign({}, finalParams, {dateText: dateStr})
|
|
}
|
|
dispatch(updateRoute(finalParams));
|
|
// if (removeSignature && deleteFile) {
|
|
// deleteFile();
|
|
// }
|
|
// dispatch(updateRoute({ id: routeId, data: requestBody }))
|
|
} else {
|
|
window.alert('Fail to update Route: no route Id is attached to this customer.')
|
|
}
|
|
setShowGroupEditor(false);
|
|
}
|
|
|
|
// const getAllRoutesCustomersStatus = (routes) => {
|
|
// const result = {};
|
|
// for (const route of routes) {
|
|
// for (const customer of route.route_customer_list) {
|
|
// if ((!showGroupInfo && customer.customer_route_status !== PERSONAL_ROUTE_STATUS.DISABLED) || showGroupInfo) {
|
|
// if (result.hasOwnProperty(customer.customer_id)) {
|
|
// result[customer.customer_id] = Object.assign(
|
|
// {},
|
|
// result[customer.customer_id],
|
|
// customer,
|
|
// route.type === 'inbound' ? { inboundStatus: customer.customer_route_status, inboundRoute: route.id} : {},
|
|
// route.type === 'outbound' ? { outboundStatus: customer.customer_route_status, outboundRoute: route.id} : {},
|
|
// customer.customer_pickup_status === PICKUP_STATUS.SCHEDULE_ABSENT ? { inboundStatus: PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT, outboundStatus: PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT} : {});
|
|
|
|
// } else {
|
|
// result[customer.customer_id] = Object.assign(
|
|
// {},
|
|
// customer,
|
|
// route.type === 'inbound' ? { inboundStatus: customer.customer_route_status, inboundRoute: route.id, outboundStatus: null, outboundRoute: null} : {},
|
|
// route.type === 'outbound' ? { outboundStatus: customer.customer_route_status, outboundRoute: route.id, inboundRoute: null, inboundStatus: null} : {},
|
|
// customer.customer_pickup_status === PICKUP_STATUS.SCHEDULE_ABSENT ? { inboundStatus: PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT, outboundStatus: PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT} : {});
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// console.log(result);
|
|
// return result;
|
|
// };
|
|
|
|
// const getTextAndClassName = (customer) => {
|
|
// if (customer.outboundStatus) {
|
|
// return PERSONAL_ROUTE_STATUS_TEXT[customer.outboundStatus];
|
|
// } else {
|
|
// if (customer.inboundStatus) {
|
|
// return PERSONAL_ROUTE_STATUS_TEXT[customer.inboundStatus];
|
|
// }
|
|
// }
|
|
// return PERSONAL_ROUTE_STATUS_TEXT[PERSONAL_ROUTE_STATUS.NO_STATUS];
|
|
// };
|
|
|
|
const getAllCustomers = (routes) => {
|
|
let result = [];
|
|
for (const route of routes) {
|
|
const customerList = route.route_customer_list.map(item => Object.assign({}, item, {routeType: route.type, routeId: route.id, route: route}))
|
|
result = result.concat(customerList);
|
|
}
|
|
return result.sort((a, b) => {
|
|
if (a.customer_id < b.customer_id) {
|
|
return -1;
|
|
} else {
|
|
return 1;
|
|
}
|
|
});
|
|
}
|
|
|
|
const getRouteCustomersWithGroups = () => {
|
|
const customerList = transRoutes[0]?.route_customer_list.map((item, index )=> Object.assign({}, item, {routeType: transRoutes[0].type, routeId: transRoutes[0].id}, {index: index+1}));
|
|
const result = {};
|
|
if (customerList) {
|
|
for (const customer of customerList) {
|
|
if (customer.customer_group) {
|
|
if (result[customer.customer_group]) {
|
|
result[customer.customer_group].push(customer);
|
|
} else {
|
|
result[customer.customer_group] = [];
|
|
result[customer.customer_group].push(customer);
|
|
}
|
|
} else {
|
|
if (result.no_group) {
|
|
result.no_group.push(customer);
|
|
} else {
|
|
result.no_group = [];
|
|
result.no_group.push(customer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
const getSortedFormItems = () => {
|
|
const result = getRouteCustomersWithGroups();
|
|
let finalResult = [];
|
|
for (const key of Object.keys(result)) {
|
|
if (key === 'no_group') {
|
|
finalResult = finalResult.concat(result[key]);
|
|
} else {
|
|
finalResult.push({
|
|
customer_pickup_order: result[key][0].customer_pickup_order,
|
|
customer_group: key,
|
|
customers: result[key]
|
|
})
|
|
}
|
|
}
|
|
return finalResult.sort((a, b) => a.customer_pickup_order - b.customer_pickup_order);
|
|
}
|
|
|
|
const getTextAndClassName = (customer) => {
|
|
if (customer.customer_pickup_status === PICKUP_STATUS.SCHEDULE_ABSENT) {
|
|
return PERSONAL_ROUTE_STATUS_TEXT[PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT];
|
|
}
|
|
if (customer.customer_route_status) {
|
|
return PERSONAL_ROUTE_STATUS_TEXT[customer.customer_route_status];
|
|
}
|
|
return PERSONAL_ROUTE_STATUS_TEXT[PERSONAL_ROUTE_STATUS.NO_STATUS];
|
|
}
|
|
|
|
const generateRouteReportData = () => {
|
|
const title = ['', '', `Route(路线): ${transRoutes[0].name} Driver(司机): ${driverName} Vehicle(车号): ${vehicle?.vehicle_number} Date(日期): ${transRoutes[0]?.schedule_date}`]
|
|
const signature = ['', '', `Driver's Signature(司机签字): ________________________ Manager's Signature(经理签字): ______________________`]
|
|
const head = ['No.', 'Name', 'Address', 'Phone', 'Show-Up', 'Pick-Up', 'Arrival', 'Departure', 'Drop-Off', 'Notice', 'Member Type', 'Vehicle Number'];
|
|
const chineseHead=['序号', '姓名', '地址', '联系电话', '出勤', '接到时间', '抵达中心', '离开中心', '送达时间', '备注', '用户类别', '车号', ];
|
|
const content = [];
|
|
const customersList = getSortedFormItems();
|
|
let index = 1;
|
|
for (let i=0; i<customersList.length; i++) {
|
|
if (!customersList[i].customers) {
|
|
content.push([index, customersList[i].customer_name, customersList[i].customer_address_override || customersList[i].customer_address, customersList[i].customer_phone, customersList[i].customer_enter_center_time? 'Y': 'N', customersList[i].customer_pickup_time, customersList[i].customer_enter_center_time, customersList[i].customer_leave_center_time, customersList[i].customer_dropoff_time, customersList[i].customer_note, customersList[i].customer_type, vehicle?.vehicle_number]);
|
|
index++;
|
|
} else {
|
|
content.push(['', customersList[i].customer_group, customersList[i].customers[0].customer_group_address]);
|
|
for (const customer of customersList[i].customers) {
|
|
content.push([index, customer.customer_name, customer.customer_address_override || customer.customer_address, customer.customer_phone, customer.customer_enter_center_time? 'Y': 'N', customer.customer_pickup_time, customer.customer_enter_center_time, customer.customer_leave_center_time, customer.customer_dropoff_time, customer.customer_note,customer.customer_type, vehicle?.vehicle_number]);
|
|
index++;
|
|
}
|
|
}
|
|
}
|
|
const itemsTitle = ['', 'Items', 'Inspected Result'];
|
|
const checklist = [];
|
|
transRoutes[0]?.checklist_result?.forEach((item) => {
|
|
checklist.push(['', item.item, item.result ? 'Y' : 'N']);
|
|
})
|
|
return [title, signature, head, chineseHead, ...content, itemsTitle, ...checklist];
|
|
}
|
|
|
|
const generateInboundSeniorsReportData = () => {
|
|
const head = ['No.', 'Name', 'Show-Up', 'Vehicle Number'];
|
|
const chineseHead=['序号', '姓名', '出勤', '车号'];
|
|
const content = [];
|
|
let index = 1;
|
|
const customersList = getAllCustomers(transRoutes.filter(route => route.type === 'inbound'))?.filter(customer => (customer.customer_pickup_status !== PICKUP_STATUS.SCHEDULE_ABSENT) && (![PERSONAL_ROUTE_STATUS.NO_STATUS, PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT, PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT].includes(customer.customer_route_status)));
|
|
for (let i=0; i<customersList.length; i++) {
|
|
content.push([index, customersList[i].customer_name, customersList[i].customer_route_status !== PERSONAL_ROUTE_STATUS.DISABLED ? 'Y': 'N', vehicles?.find(item => item?.id === customersList[i]?.route?.vehicle)?.vehicle_number]);
|
|
index++;
|
|
}
|
|
const finalNumber = ['Participants Arrived:', customersList.filter(customer => customer.customer_route_status!== PERSONAL_ROUTE_STATUS.DISABLED)?.length];
|
|
return [head, chineseHead, ...content, finalNumber ];
|
|
}
|
|
|
|
const generateSeniorTimeReport = () => {
|
|
const customersList = getAllCustomers(transRoutes.filter(route => route.type === 'inbound'));
|
|
const content = [];
|
|
let outboundCustomerList = [];
|
|
for (const outboundRoute of relatedOutbound) {
|
|
outboundCustomerList = outboundCustomerList.concat(outboundRoute.route_customer_list)
|
|
}
|
|
const outboundMap = new Map();
|
|
for (const outboundCustomer of outboundCustomerList) {
|
|
outboundMap.set(outboundCustomer.customer_id, outboundCustomer);
|
|
}
|
|
for (const customer of customersList) {
|
|
const element = {
|
|
index: customersList.indexOf(customer)+1,
|
|
customer_name: customer.customer_name,
|
|
customer_enter_center_time: customer.customer_enter_center_time,
|
|
customer_pickup_time: customer.customer_pickup_time,
|
|
customer_leave_center_time: outboundMap.get(customer.customer_id)?.customer_leave_center_time,
|
|
customer_dropoff_time: outboundMap.get(customer.customer_id)?.customer_dropoff_time,
|
|
customer_address: customer.customer_address_override || customer.customer_address,
|
|
customer_phone: customer.customer_phone,
|
|
customer_note: customer.customer_note,
|
|
customer_type: customer.customer_type,
|
|
vehicle_number: vehicle?.vehicle_number
|
|
}
|
|
content.push(element);
|
|
}
|
|
ReportService.getReportsByRouteIdAndType(transRoutes[0].id, REPORT_TYPE.SENIOR_CONSOLIDATE_REPORT).then(data=> {
|
|
if (data.data && data.data.length > 0) {
|
|
ReportService.updateReport(data.data[0].id, {type: REPORT_TYPE.SENIOR_CONSOLIDATE_REPORT, driver_name: driverName, vehicle_number: vehicle?.vehicle_number, checklist_result: transRoutes[0]?.checklist_result, route_id: transRoutes[0].id, route_name: transRoutes[0].name, date: transRoutes[0].schedule_date, data: content, head: ['No.', 'Name', 'Phone', 'Address', 'Pickup Time', 'Enter Center Time', 'Leave Center Time', 'Drop Off Time', 'Note', 'Member Type', 'Vehicle Number'], chinese_head: ['序号', '姓名', '电话', '地址', '接到时间', '进入中心时间', '离开中心时间', '送达时间', '备注', '用户类别', '车号']}).then(() => {
|
|
window.alert('The report is saved to Database. To get a PDF version, please run your PYTHON EXE Script.')
|
|
});
|
|
} else {
|
|
ReportService.createReport({type: REPORT_TYPE.SENIOR_CONSOLIDATE_REPORT, driver_name: driverName, route_id: transRoutes[0].id, vehicle_number: vehicle?.vehicle_number, checklist_result: transRoutes[0]?.checklist_result, route_name: transRoutes[0].name, date: transRoutes[0].schedule_date, data: content, head: ['No.', 'Name', 'Phone', 'Address', 'Pickup Time', 'Enter Center Time', 'Leave Center Time', 'Drop Off Time', 'Note', 'Member Type', 'Vehicle Number' ], chinese_head: ['序号', '姓名', '电话', '地址', '接到时间', '进入中心时间', '离开中心时间', '送达时间', '备注', '用户类别', '车号' ]}).then(() => {
|
|
window.alert('The report is saved to Database. To get a PDF version, please run your PYTHON EXE Script.')
|
|
});
|
|
}
|
|
})
|
|
}
|
|
|
|
const openBulkUpdateModal = () => {
|
|
setShowBulkUpdateModal(true);
|
|
setBulkEnterCenterTime('');
|
|
setBulkLeaveCenterTime('');
|
|
setBulkCustomerRouteStatus('');
|
|
setBulkCustomerPickupStatus('');
|
|
}
|
|
|
|
const closeBulkUpdateModal = () => {
|
|
setShowBulkUpdateModal(false);
|
|
setBulkEnterCenterTime('');
|
|
setBulkLeaveCenterTime('');
|
|
setBulkCustomerRouteStatus('');
|
|
setBulkCustomerPickupStatus('');
|
|
}
|
|
|
|
const saveBulkUpdate = () => {
|
|
const routeId = transRoutes[0]?.id;
|
|
|
|
// Debug logging
|
|
console.log('=== saveBulkUpdate Debug ===');
|
|
console.log('bulkEnterCenterTime:', bulkEnterCenterTime);
|
|
console.log('bulkEnterCenterTime type:', typeof bulkEnterCenterTime);
|
|
console.log('bulkEnterCenterTime value:', JSON.stringify(bulkEnterCenterTime));
|
|
console.log('bulkLeaveCenterTime:', bulkLeaveCenterTime);
|
|
console.log('bulkLeaveCenterTime type:', typeof bulkLeaveCenterTime);
|
|
console.log('bulkLeaveCenterTime value:', JSON.stringify(bulkLeaveCenterTime));
|
|
|
|
if (routeId) {
|
|
let requestBody = transRoutes.find((route) => route.id === routeId);
|
|
const dateStr = requestBody?.schedule_date || '';
|
|
|
|
console.log('dateStr:', dateStr);
|
|
console.log('Number of customers to update:', requestBody.route_customer_list.length);
|
|
console.log('Original customer list IDs:', requestBody.route_customer_list.map(c => ({ id: c.customer_id, name: c.customer_name, pickup_status: c.customer_pickup_status })));
|
|
|
|
const updatedCustomerList = requestBody.route_customer_list.map((item) => {
|
|
// Skip customers who are Unscheduled Absent or Scheduled Absent
|
|
// Only skip if status is explicitly set to one of these values (not undefined)
|
|
if (item.customer_pickup_status &&
|
|
(item.customer_pickup_status === PICKUP_STATUS.UNSCHEDULE_ABSENT ||
|
|
item.customer_pickup_status === PICKUP_STATUS.SCHEDULE_ABSENT)) {
|
|
console.log(`Skipping customer ${item.customer_id} (${item.customer_name}) - status: ${item.customer_pickup_status}`);
|
|
return item;
|
|
}
|
|
|
|
let updatedItem = { ...item };
|
|
|
|
// Debug logging for each customer
|
|
console.log(`Processing customer ${item.customer_id} (${item.customer_name})`);
|
|
|
|
// Check if bulkEnterCenterTime is a valid time string (HH:mm format)
|
|
console.log('Checking bulkEnterCenterTime:', {
|
|
value: bulkEnterCenterTime,
|
|
type: typeof bulkEnterCenterTime,
|
|
isTruthy: !!bulkEnterCenterTime,
|
|
isString: typeof bulkEnterCenterTime === 'string',
|
|
trimmed: typeof bulkEnterCenterTime === 'string' ? bulkEnterCenterTime.trim() : 'N/A',
|
|
trimmedLength: typeof bulkEnterCenterTime === 'string' ? bulkEnterCenterTime.trim().length : 0
|
|
});
|
|
|
|
if (bulkEnterCenterTime && typeof bulkEnterCenterTime === 'string' && bulkEnterCenterTime.trim() !== '') {
|
|
try {
|
|
const combinedTime = combineDateAndTime(dateStr, bulkEnterCenterTime);
|
|
const dateValue = combinedTime.toDate();
|
|
console.log('Combined enter center time (moment):', combinedTime);
|
|
console.log('Combined enter center time (Date):', dateValue);
|
|
updatedItem.customer_enter_center_time = dateValue;
|
|
// Don't automatically update status when setting time
|
|
console.log(`Set customer_enter_center_time for ${item.customer_name} to:`, updatedItem.customer_enter_center_time);
|
|
} catch (e) {
|
|
console.error('Error combining date and enter center time:', e);
|
|
}
|
|
} else {
|
|
console.log('Skipping bulkEnterCenterTime - condition not met');
|
|
}
|
|
|
|
// Check if bulkLeaveCenterTime is a valid time string (HH:mm format)
|
|
console.log('Checking bulkLeaveCenterTime:', {
|
|
value: bulkLeaveCenterTime,
|
|
type: typeof bulkLeaveCenterTime,
|
|
isTruthy: !!bulkLeaveCenterTime,
|
|
isString: typeof bulkLeaveCenterTime === 'string',
|
|
trimmed: typeof bulkLeaveCenterTime === 'string' ? bulkLeaveCenterTime.trim() : 'N/A',
|
|
trimmedLength: typeof bulkLeaveCenterTime === 'string' ? bulkLeaveCenterTime.trim().length : 0
|
|
});
|
|
|
|
if (bulkLeaveCenterTime && typeof bulkLeaveCenterTime === 'string' && bulkLeaveCenterTime.trim() !== '') {
|
|
try {
|
|
const combinedTime = combineDateAndTime(dateStr, bulkLeaveCenterTime);
|
|
const dateValue = combinedTime.toDate();
|
|
console.log('Combined leave center time (moment):', combinedTime);
|
|
console.log('Combined leave center time (Date):', dateValue);
|
|
updatedItem.customer_leave_center_time = dateValue;
|
|
// Don't automatically update status when setting time
|
|
console.log(`Set customer_leave_center_time for ${item.customer_name} to:`, updatedItem.customer_leave_center_time);
|
|
} catch (e) {
|
|
console.error('Error combining date and leave center time:', e);
|
|
}
|
|
} else {
|
|
console.log('Skipping bulkLeaveCenterTime - condition not met');
|
|
}
|
|
|
|
console.log(`Final updatedItem for ${item.customer_name}:`, {
|
|
customer_id: updatedItem.customer_id,
|
|
customer_enter_center_time: updatedItem.customer_enter_center_time,
|
|
customer_leave_center_time: updatedItem.customer_leave_center_time
|
|
});
|
|
|
|
if (bulkCustomerRouteStatus && bulkCustomerRouteStatus !== '') {
|
|
updatedItem.customer_route_status = bulkCustomerRouteStatus;
|
|
}
|
|
|
|
if (bulkCustomerPickupStatus && bulkCustomerPickupStatus !== '') {
|
|
updatedItem.customer_pickup_status = bulkCustomerPickupStatus;
|
|
}
|
|
|
|
return updatedItem;
|
|
});
|
|
|
|
requestBody = Object.assign({}, requestBody, {route_customer_list: updatedCustomerList, updatedAt: new Date(), updatedBy: 'admin'});
|
|
|
|
// Debug: Log all customers from updated list
|
|
console.log('=== All Updated Customers ===');
|
|
updatedCustomerList.forEach((customer, index) => {
|
|
console.log(`Customer ${index + 1}:`, {
|
|
customer_id: customer.customer_id,
|
|
customer_name: customer.customer_name,
|
|
pickup_status: customer.customer_pickup_status,
|
|
customer_enter_center_time: customer.customer_enter_center_time,
|
|
customer_leave_center_time: customer.customer_leave_center_time
|
|
});
|
|
});
|
|
|
|
let finalParams = { id: routeId, data: requestBody };
|
|
if (scheduleDate) {
|
|
finalParams = Object.assign({}, finalParams, {dateText: moment(scheduleDate).format('MM/DD/YYYY'), fromSchedule: true})
|
|
}
|
|
if (dateStr !== '' && dateStr !== moment().format('MM/DD/YYYY')) {
|
|
finalParams = Object.assign({}, finalParams, {dateText: dateStr})
|
|
}
|
|
|
|
// Log final payload before dispatch, especially for Wang,Huanran
|
|
const wangCustomer = finalParams.data.route_customer_list?.find(c =>
|
|
c.customer_id === '63657acff745ffd72affb8d8' || c.customer_name?.includes('Wang,Huanran')
|
|
);
|
|
console.log('Wang,Huanran customer in final payload:', wangCustomer);
|
|
console.log('Final params being dispatched:', {
|
|
id: finalParams.id,
|
|
data_keys: Object.keys(finalParams.data),
|
|
customer_count: finalParams.data.route_customer_list?.length
|
|
});
|
|
|
|
// Stringify to see what would actually be sent (Date serialization)
|
|
try {
|
|
const stringified = JSON.stringify(finalParams.data.route_customer_list?.slice(0, 2));
|
|
console.log('Sample serialized customer list (first 2):', stringified);
|
|
} catch (e) {
|
|
console.error('Error stringifying:', e);
|
|
}
|
|
|
|
console.log('=== End saveBulkUpdate Debug ===');
|
|
|
|
dispatch(updateRoute(finalParams));
|
|
} else {
|
|
window.alert('Fail to update Route: no route Id found.')
|
|
}
|
|
closeBulkUpdateModal();
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{!showGroupInfo && (<div className="list row mb-4">
|
|
<div className="col-md-12">
|
|
<CSVLink className="btn btn-primary btn-sm btn-no-deco" data={generateInboundSeniorsReportData()} filename={`Route Inbound Customers Show-up Report`}>
|
|
Generate Inbound Participants Report
|
|
</CSVLink>
|
|
</div>
|
|
</div>)}
|
|
{showGroupInfo && (<div className="list row mb-4">
|
|
<div className="col-md-12">
|
|
<CSVLink className="btn btn-primary btn-no-deco btn-sm me-2" data={generateRouteReportData()} filename={`Route Report - ${transRoutes[0].name} (定线出车单)`}>
|
|
Generate Route Reports
|
|
</CSVLink>
|
|
<button className="btn btn-primary btn-sm me-2" onClick={() => goToReportWithSignature()}>Get Route Report With Signature</button>
|
|
<button className="btn btn-primary btn-sm" onClick={() => openBulkUpdateModal()}>Bulk Update Route Customer Time</button>
|
|
</div>
|
|
|
|
</div>)}
|
|
|
|
{/* {showFilter && (<div>
|
|
<h6>Filter:</h6>
|
|
<div className="list row">
|
|
<div className="col-md-6 col-sm-6 col-xs-12 mb-4">
|
|
Participant Status: <select
|
|
value={statusFilter}
|
|
onChange={(e) => setStatusFilter(e.target.value)}
|
|
>
|
|
{
|
|
[['', {text: ''}], ...Object.entries(PERSONAL_ROUTE_STATUS_TEXT)].map(([key, {text}]) => (
|
|
<option key={key} value={key}>{text}</option>
|
|
))
|
|
}
|
|
</select>
|
|
</div>
|
|
<div className="col-md-6 col-sm-6 col-xs-12 mb-4 ml-4">
|
|
Participant Type: <select
|
|
value={customerTypeFilter}
|
|
onChange={(e) => setCustomerTypeFilter(e.target.value)}
|
|
>
|
|
{
|
|
[['', ''], ...Object.entries(CUSTOMER_TYPE_TEXT)].map(([key, text]) => (
|
|
<option key={key} value={key}>{text}</option>
|
|
))
|
|
}
|
|
</select>
|
|
</div>
|
|
<div className="col-md-6 col-sm-6 col-xs-12 mb-4">
|
|
Routes Type: <select
|
|
value={routeTypeFilter}
|
|
onChange={(e) => setRouteTypeFilter(e.target.value)}
|
|
>
|
|
<option value=""></option>
|
|
<option value="inbound">inbound</option>
|
|
<option value="outbound">outbound</option>
|
|
</select>
|
|
</div>
|
|
<div className="col-md-6 col-sm-6 col-xs-12 mb-4">
|
|
Participant Name: <input type="text" value={customerNameFilter} onChange={(e) => setCustomerNameFilter(e.target.value)} />
|
|
</div>
|
|
<div className="col-md-6 col-sm-6 col-xs-12 mb-4">
|
|
Table Id: <input type="text" value={customerTableId} onChange={(e) => setCustomerTableId(e.target.value)} />
|
|
</div>
|
|
<div className="col-md-12 col-sm-12 col-xs-12 mb-4">
|
|
<button className="btn btn-primary" onClick={() => clearFilters()}>Clear Filters</button>
|
|
</div>
|
|
</div>
|
|
</div>)} */}
|
|
<div className="list row">
|
|
<div className="col-md-12 overflow-auto">
|
|
<table className="personnel-info-table">
|
|
<thead>
|
|
<tr>
|
|
<th className="th-index">No.</th>
|
|
<th>Name</th>
|
|
{showCompletedInfo && (<th>Address</th>)}
|
|
{showCompletedInfo && (<th>Tel</th>)}
|
|
<th>Status</th>
|
|
<th>Type</th>
|
|
{!showCompletedInfo &&<th>Route Type</th>}
|
|
<th>Pick Up Time</th>
|
|
<th>Enter Center Time</th>
|
|
<th>Leave Center Time</th>
|
|
<th>Drop Off Time</th>
|
|
{showCompletedInfo && (<th>Schedule Absent</th>)}
|
|
{showCompletedInfo && (<th>Schedule Absent Note</th>)}
|
|
{showCompletedInfo && (<th>Special Needs</th>)}
|
|
{showCompletedInfo && (<th>Pickup Order</th>)}
|
|
{showCompletedInfo && (<th>Estimated Pickup Time</th>)}
|
|
{!showCompletedInfo && (<th>Vehicle Number</th>)}
|
|
{allowForceEdit && <th>Edit</th>}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{!showGroupInfo && getAllCustomers(transRoutes)
|
|
.filter((item) => {
|
|
const filterHasValue = (filterField) => filterField && filterField.length > 0
|
|
let result1 = true;
|
|
let result2 = true;
|
|
let result3 = true;
|
|
let result4 = true;
|
|
let result5 = true;
|
|
if (filterHasValue(statusFilter) && getTextAndClassName(item)?.text !== PERSONAL_ROUTE_STATUS_TEXT[statusFilter]?.text) {
|
|
result1 = false;
|
|
} else {
|
|
result1 = true;
|
|
}
|
|
if ((filterHasValue(customerTypeFilter) && item.customer_type !== customerTypeFilter)) {
|
|
result2 = false;
|
|
} else {
|
|
result2 = true;
|
|
}
|
|
if (filterHasValue(routeTypeFilter) && item.routeType !== routeTypeFilter) {
|
|
result3 = false;
|
|
} else {
|
|
result3 = true;
|
|
}
|
|
if (filterHasValue(customerNameFilter) && !item.customer_name?.toLowerCase().includes(customerNameFilter.toLowerCase()) ) {
|
|
result4 = false;
|
|
} else {
|
|
result4 = true;
|
|
}
|
|
if (filterHasValue(customerTableId) && item.customer_table_id !== customerTableId) {
|
|
result5 = false;
|
|
} else {
|
|
result5 = true;
|
|
}
|
|
return result1&&result2&&result3&&result4&&result5;
|
|
})
|
|
//.filter((item) => item?.customer_name?.includes(keyword) || getTextAndClassName(item)?.text?.includes(keyword) || item?.customer_type?.includes(keyword) || item?.table_id?.includes(keyword))
|
|
.sort((a, b) => a.customer_name.replace(' ', '') > b.customer_name.replace(' ', '') ? 1: -1 )
|
|
.map((customer, index) => {
|
|
return (<tr key={index}>
|
|
<td className="td-index"> {index + 1}</td>
|
|
<td>
|
|
{ customer.customer_name}
|
|
</td>
|
|
{showCompletedInfo && (<td>
|
|
{ customer.customer_address_override || customer.customer_address }
|
|
</td>)}
|
|
{showCompletedInfo && (<td>
|
|
{ customer.customer_phone }
|
|
</td>)}
|
|
<td>
|
|
<div className={`${getTextAndClassName(customer).className} status-tag`}>{ getTextAndClassName(customer).text } </div>
|
|
</td>
|
|
<td>
|
|
{ CUSTOMER_TYPE_TEXT[customer.customer_type]}
|
|
</td>
|
|
{!showCompletedInfo && <td>
|
|
{ customer.routeType}
|
|
</td>}
|
|
<td>{customer.customer_pickup_time && new Date(customer.customer_pickup_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}</td>
|
|
<td>{customer.customer_enter_center_time && new Date(customer.customer_enter_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}</td>
|
|
<td>{customer.customer_leave_center_time && new Date(customer.customer_leave_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}</td>
|
|
<td>{customer.customer_dropoff_time && new Date(customer.customer_dropoff_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}</td>
|
|
{showCompletedInfo && (<td>
|
|
{ customer.customer_pickup_status === PICKUP_STATUS.SCHEDULE_ABSENT ? 'Yes' : "No" }
|
|
</td>)}
|
|
{showCompletedInfo && (<td>
|
|
{ customer.customer_note }
|
|
</td>)}
|
|
{showCompletedInfo && (<td>
|
|
{ customer.customer_special_needs }
|
|
</td>)}
|
|
{showCompletedInfo && (<td>
|
|
{ customer.customer_pickup_order }
|
|
</td>)}
|
|
{showCompletedInfo && (<td>
|
|
{ customer.customer_estimated_pickup_time ? new Date(customer.customer_estimated_pickup_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : '' }
|
|
</td>)}
|
|
{!showCompletedInfo && (<td>
|
|
{ vehicle?.vehicle_number || vehicles?.find((item) => item?.id === customer?.route?.vehicle)?.vehicle_number }
|
|
</td>)}
|
|
{allowForceEdit && (<td>
|
|
<button className="btn btn-link btn-sm" onClick={() => openForceEditModal(customer)}>Edit</button>
|
|
</td>)}
|
|
|
|
</tr>)
|
|
})}
|
|
|
|
{showGroupInfo && getSortedFormItems()
|
|
.map((customerItem, index) => {
|
|
if (!customerItem.customer_group) {
|
|
return (<tr key={index} >
|
|
<td className="td-index">{customerItem.index + 1}</td>
|
|
<td>
|
|
{ customerItem.customer_name}
|
|
</td>
|
|
{showCompletedInfo && (<td>
|
|
{ customerItem.customer_address_override || customerItem.customer_address }
|
|
</td>)}
|
|
{showCompletedInfo && (<td>
|
|
{ customerItem.customer_phone }
|
|
</td>)}
|
|
<td> <div className={`${getTextAndClassName(customerItem).className} status-tag`}>
|
|
{ getTextAndClassName(customerItem).text } </div>
|
|
</td>
|
|
<td>
|
|
{ CUSTOMER_TYPE_TEXT[customerItem.customer_type]}
|
|
</td>
|
|
{!showCompletedInfo && <td>
|
|
{ customerItem.routeType}
|
|
</td>}
|
|
<td>{customerItem.customer_pickup_time && new Date(customerItem.customer_pickup_time).toLocaleTimeString('en-US', {hour12: false})}</td>
|
|
<td>{customerItem.customer_enter_center_time && new Date(customerItem.customer_enter_center_time).toLocaleTimeString('en-US', {hour12: false})}</td>
|
|
<td>{customerItem.customer_leave_center_time && new Date(customerItem.customer_leave_center_time).toLocaleTimeString('en-US', {hour12: false})}</td>
|
|
<td>{customerItem.customer_dropoff_time && new Date(customerItem.customer_dropoff_time).toLocaleTimeString('en-US', {hour12: false})}</td>
|
|
{showCompletedInfo && (<td>
|
|
{ customerItem.customer_pickup_status === PICKUP_STATUS.SCHEDULE_ABSENT ? 'Yes' : "No" }
|
|
</td>)}
|
|
{showCompletedInfo && (<td>
|
|
{ customerItem.customer_note }
|
|
</td>)}
|
|
{showCompletedInfo && (<td>
|
|
{ customerItem.customer_special_needs }
|
|
</td>)}
|
|
{showCompletedInfo && (<td>
|
|
{ customerItem.customer_pickup_order }
|
|
</td>)}
|
|
{showCompletedInfo && (<td>
|
|
{ customerItem.customer_estimated_pickup_time ? new Date(customerItem.customer_estimated_pickup_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : '' }
|
|
</td>)}
|
|
{!showCompletedInfo && (<td>
|
|
{ vehicle?.vehicle_number }
|
|
</td>)}
|
|
{allowForceEdit && <td>
|
|
<button className="btn btn-link btn-sm" onClick={() => openForceEditModal(customerItem)}>Edit</button>
|
|
</td>}
|
|
</tr>);
|
|
} else {
|
|
return (<React.Fragment key={index}>
|
|
<tr className="group">
|
|
<td className="td-index"></td>
|
|
<td>{customerItem.customer_group}</td>
|
|
<td colSpan={showCompletedInfo? 11: 3}>{customerItem.customers[0]?.customer_group_address}</td>
|
|
{allowForceEdit && <td>
|
|
<button className="btn btn-link btn-sm" onClick={() => openForceEditGroupModal(customerItem.customers)}>Edit Group</button>
|
|
</td>}
|
|
</tr>
|
|
{
|
|
customerItem.customers?.map((customer) => (<tr key={customer.customer_id} onClick={() => openForceEditModal(customer)}>
|
|
<td className="td-index">{customer.index + 1}</td>
|
|
<td className="children">
|
|
{ customer.customer_name}
|
|
</td>
|
|
{showCompletedInfo && (<td>
|
|
{ customer.customer_address_override || customer.customer_address }
|
|
</td>)}
|
|
{showCompletedInfo && (<td>
|
|
{ customer.customer_phone }
|
|
</td>)}
|
|
<td> <div className={`${getTextAndClassName(customerItem).className} status-tag`}>
|
|
{ getTextAndClassName(customerItem).text } </div>
|
|
</td>
|
|
<td>
|
|
{ CUSTOMER_TYPE_TEXT[customer.customer_type]}
|
|
</td>
|
|
{!showCompletedInfo && <td>
|
|
{ customer.routeType}
|
|
</td>}
|
|
<td>{customer.customer_pickup_time && new Date(customer.customer_pickup_time).toLocaleTimeString('en-US', {hour12: false})}</td>
|
|
<td>{customer.customer_enter_center_time && new Date(customer.customer_enter_center_time).toLocaleTimeString('en-US', {hour12: false})}</td>
|
|
<td>{customer.customer_leave_center_time && new Date(customer.customer_leave_center_time).toLocaleTimeString('en-US', {hour12: false})}</td>
|
|
<td>{customer.customer_dropoff_time && new Date(customer.customer_dropoff_time).toLocaleTimeString('en-US', {hour12: false})}</td>
|
|
{showCompletedInfo && (<td>
|
|
{ customer.customer_pickup_status === PICKUP_STATUS.SCHEDULE_ABSENT ? 'Yes' : "No" }
|
|
</td>)}
|
|
{showCompletedInfo && (<td>
|
|
{ customer.customer_note }
|
|
</td>)}
|
|
{showCompletedInfo && (<td>
|
|
{ customer.customer_special_needs }
|
|
</td>)}
|
|
{showCompletedInfo && (<td>
|
|
{ customer.customer_pickup_order }
|
|
</td>)}
|
|
{showCompletedInfo && (<td>
|
|
{ customer.customer_estimated_pickup_time ? new Date(customer.customer_estimated_pickup_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : '' }
|
|
</td>)}
|
|
{!showCompletedInfo && (<td>
|
|
{ vehicle?.vehicle_number }
|
|
</td>)}
|
|
{allowForceEdit && <td>
|
|
<button className="btn btn-link btn-sm" onClick={() => openForceEditModal(customer)}>Edit</button>
|
|
</td>}
|
|
</tr>))
|
|
}
|
|
</React.Fragment>)
|
|
}
|
|
})}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<Modal show={show} onHide={() => closeModal()}>
|
|
<Modal.Header closeButton>
|
|
<Modal.Title>Special Edit Customer</Modal.Title>
|
|
</Modal.Header>
|
|
<Modal.Body>
|
|
<>
|
|
<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>
|
|
|
|
<div className="app-main-content-fields-section">
|
|
<div className="me-4">
|
|
<div className="field-label">Special Set Customer 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 Customer 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="link" size="sm" onClick={() => closeModal()}>
|
|
Close
|
|
</Button>
|
|
<Button variant="primary" size="sm" onClick={() => saveRouteCustomerInfo()}>
|
|
Save Changes
|
|
</Button>
|
|
</Modal.Footer>
|
|
</Modal>
|
|
<Modal show={showGroupEditor} onHide={() => closeGroupEditorModal()}>
|
|
<Modal.Header closeButton>
|
|
<Modal.Title>Special Edit Group Participants</Modal.Title>
|
|
</Modal.Header>
|
|
<Modal.Body>
|
|
<>
|
|
<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>
|
|
</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="link" size="sm" onClick={() => closeGroupEditorModal()}>
|
|
Close
|
|
</Button>
|
|
<Button variant="primary" size="sm" onClick={() => saveRouteGroupCustomerInfo()}>
|
|
Save Changes
|
|
</Button>
|
|
</Modal.Footer>
|
|
</Modal>
|
|
<Modal show={showBulkUpdateModal} onHide={() => closeBulkUpdateModal()}>
|
|
<Modal.Header closeButton>
|
|
<Modal.Title>Bulk Update Route Customer Time</Modal.Title>
|
|
</Modal.Header>
|
|
<Modal.Body>
|
|
<>
|
|
<div className="app-main-content-fields-section">
|
|
<div className="me-4">
|
|
<div className="field-label">Customer Enter Center Time</div>
|
|
<TimePicker disableClock={true} format={'HH:mm'} value={bulkEnterCenterTime} onChange={setBulkEnterCenterTime} />
|
|
</div>
|
|
<div className="me-4">
|
|
<div className="field-label">Customer Leave Center Time</div>
|
|
<TimePicker disableClock={true} format={'HH:mm'} value={bulkLeaveCenterTime} onChange={setBulkLeaveCenterTime} />
|
|
</div>
|
|
</div>
|
|
<div className="app-main-content-fields-section">
|
|
<div className="me-4">
|
|
<div className="field-label">Customer Route Status</div>
|
|
<select value={bulkCustomerRouteStatus} onChange={(e) => setBulkCustomerRouteStatus(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">Customer Pickup Status</div>
|
|
<select value={bulkCustomerPickupStatus} onChange={(e) => setBulkCustomerPickupStatus(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="alert alert-info">
|
|
<strong>Note:</strong> This will update all customers in this route who are not Unscheduled Absent or Scheduled Absent.
|
|
</div>
|
|
</>
|
|
</Modal.Body>
|
|
<Modal.Footer>
|
|
<Button variant="link" size="sm" onClick={() => closeBulkUpdateModal()}>
|
|
Cancel
|
|
</Button>
|
|
<Button variant="primary" size="sm" onClick={() => saveBulkUpdate()}>
|
|
Save
|
|
</Button>
|
|
</Modal.Footer>
|
|
</Modal>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default PersonnelInfoTable; |