import React, {useEffect, useState, useRef} 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, Breadcrumb, Tabs, Tab } from "react-bootstrap"; import RouteCustomerEditor from "./RouteCustomerEditor"; import { AuthService, TransRoutesService, CustomerService } 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"; import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import { useDrag } from 'react-dnd'; const RouteEdit = () => { const params = useParams(); const allRoutes = useSelector(selectAllRoutes); const tomorrowRoutes = useSelector(selectTomorrowAllRoutes); const historyRoutes = useSelector(selectHistoryRoutes); const drivers = useSelector(selectAllActiveDrivers); const vehicles = useSelector(selectAllActiveVehicles); // const currentRoute = (allRoutes.find(item => item.id === params.id)) || (tomorrowRoutes.find(item => item.id === params.id)) || (historyRoutes.find(item => item.id === params.id)) || {}; const currentVehicle = vehicles.find(item => item.id === currentRoute?.vehicle ) || []; const navigate = useNavigate(); const dispatch = useDispatch(); const { updateRoute} = transRoutesSlice.actions; const { updateVehicle} = vehicleSlice.actions; const [routeName, setRouteName] = useState(''); const [newDriver, setNewDriver] = useState(''); const [newVehicle, setNewVehicle] = useState(''); const [newRouteType, setNewRouteType] = useState(''); const [showAddCheckItem, setShowAddCheckItem] = useState(false); const [showCopyCheckItem, setShowCopyCheckItem] = useState(false); const [newChecklistItems, setNewChecklistItems] = useState([]); const [selectedRouteChecklistToCopy, setSelectedRouteChecklistToCopy] = useState({}); const [newCustomerList, setNewCustomerList] = useState([]); const [errorMessage, setErrorMessage] = useState(undefined); const [estimatedStartTime, setEstimatedStartTime] = useState(undefined); const [currentRoute, setCurrentRoute] = useState(undefined); const [allCustomers, setAllCustomers] = useState([]); const [unassignedCustomers, setUnassignedCustomers] = useState([]); const [addCustomerToRoute, setAddCustomerToRoute] = useState(null); 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}`); } else { navigate(`/trans-routes/${params.id}`); } } const redirectToDashboard = () => { navigate(`/trans-routes/dashboard`); } const softDeleteCurrentRoute = () => { if (!window.confirm('Are you sure you want to archive this route? This action cannot be undone.')) { return; } const data = Object.assign({}, currentRoute, {status: ['disabled']}) dispatch(updateRoute({ id: currentRoute?.id, data, callback: redirectToDashboard })); // redirectToDashboard(); } const validateRoute = () => { const errors = []; // Required fields validation if (!routeName || routeName.trim() === '') { errors.push('Route Name'); } if (!newRouteType || newRouteType === '') { errors.push('Route Type'); } if (!newDriver || newDriver === '') { errors.push('Driver'); } if (!newVehicle || newVehicle === '') { errors.push('Vehicle'); } if (errors.length > 0) { window.alert(`Please fill in the following required fields:\n${errors.join('\n')}`); return false; } return true; }; const updateCurrentRoute = () => { try { if (!validateRoute()) { return; } let data = Object.assign({}, currentRoute, {name: routeName, driver: newDriver, vehicle: newVehicle, type: newRouteType, route_customer_list: newCustomerList}); if (estimatedStartTime && estimatedStartTime !== '') { data = Object.assign({}, data, {estimated_start_time: combineDateAndTime(currentRoute.schedule_date, estimatedStartTime)}) } let payload = { id: currentRoute?.id, data }; if ((historyRoutes.find(item => item.id === params.id)) || (scheduleDate && new Date(data.schedule_date) > new Date())) { payload = Object.assign({}, payload, {dateText: data.schedule_date}); if (scheduleDate && new Date(data.schedule_date) > new Date()) { payload = Object.assign({}, payload, {fromSchedule: true}); } } payload.callback = redirectToView; dispatch(updateRoute(payload)); // TransRoutesService.updateInProgress(data); // setTimeout(() => { // redirectToView(); // }, 5000); } catch(ex) { } } const addItemToArray = () => { const arr = [...newChecklistItems, '']; setNewChecklistItems(arr); } const saveChecklistItems = () => { const data = Object.assign({}, currentVehicle, {checklist: newChecklistItems}); dispatch(updateVehicle({ id: currentVehicle.id, data })); setShowAddCheckItem(false); } const copyChecklistItems = () => { const data = Object.assign({}, currentVehicle, {checklist: vehicles.find(vehicle => vehicle.id === selectedRouteChecklistToCopy.vehicle)?.checklist}); dispatch(updateVehicle({ id: currentVehicle.id, data })); setShowCopyCheckItem(false); } const closeAddCheckItemModal = () => { setNewChecklistItems([]) setShowAddCheckItem(false); } const showAddCheckItemModal = () => { setNewChecklistItems(currentVehicle.checklist || []) setShowAddCheckItem(true); } const closeCopyCheckItemModal = () => { setSelectedRouteChecklistToCopy({}); setShowCopyCheckItem(false); } const showCopyCheckItemModal = () => { setShowCopyCheckItem(true); } 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 calculateUnassignedCustomers = (customers, routes) => { if (!customers || !routes) return []; // Get all customer IDs that are assigned to any route today const assignedCustomerIds = new Set(); routes.forEach(route => { route.route_customer_list?.forEach(customer => { assignedCustomerIds.add(customer.customer_id); }); }); // Filter out customers that are not assigned to any route // Also exclude discharged customers (type is 'discharged' or name contains 'discharged') return customers.filter(customer => { const isDischarged = customer.type === 'discharged' || (customer.name && customer.name.toLowerCase().includes('discharged')) || (customer.status !== 'active'); return customer.status === 'active' && !assignedCustomerIds.has(customer.id) && !isDischarged; }); } useEffect(() => { if (!AuthService.canAddOrEditRoutes()) { window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.') AuthService.logout(); navigate(`/login`); } TransRoutesService.getRoute(params.id).then(data => { setCurrentRoute(data?.data); setRouteName(data?.data?.name); setNewDriver(data?.data?.driver); setNewVehicle(data?.data?.vehicle); setNewRouteType(data?.data?.type); setEstimatedStartTime(data?.data?.estimated_start_time && new Date(data?.data?.estimated_start_time)); setNewCustomerList(data?.data?.route_customer_list); setErrorMessage(undefined); }) // Fetch all customers CustomerService.getAllCustomers().then(data => { setAllCustomers(data?.data || []); }); }, []); // Calculate unassigned customers when allCustomers or routes change useEffect(() => { if (!currentRoute?.schedule_date) return; const routeDate = currentRoute.schedule_date; // Get routes from the same date as the current route (excluding current route which we'll handle separately) const sameDateRoutes = [ ...allRoutes.filter(route => route.schedule_date === routeDate && route.id !== currentRoute.id), ...tomorrowRoutes.filter(route => route.schedule_date === routeDate && route.id !== currentRoute.id), ...historyRoutes.filter(route => route.schedule_date === routeDate && route.id !== currentRoute.id) ]; // Add a virtual route with the current newCustomerList (to include customers being added in the editor) const routesWithCurrentEdits = [ ...sameDateRoutes, { route_customer_list: newCustomerList || [] } ]; const unassigned = calculateUnassignedCustomers(allCustomers, routesWithCurrentEdits); setUnassignedCustomers(unassigned); }, [allCustomers, allRoutes, tomorrowRoutes, historyRoutes, currentRoute, newCustomerList]); // useEffect(() => { // if (currentRoute) { // setRouteName(currentRoute.name); // setNewDriver(currentRoute.driver); // setNewVehicle(currentRoute.vehicle); // setNewRouteType(currentRoute.type); // setEstimatedStartTime(currentRoute.estimated_start_time && new Date(currentRoute.estimated_start_time)); // setNewCustomerList(currentRoute.route_customer_list); // } // setErrorMessage(undefined); // }, [currentRoute]) // Draggable component for unassigned customers const DraggableUnassignedCustomer = ({ customer }) => { const [{ isDragging }, drag] = useDrag({ type: 'UNASSIGNED_CUSTOMER', item: () => customer, collect: (monitor) => ({ isDragging: monitor.isDragging(), }), }); const opacity = isDragging ? 0.5 : 1; return (
{customer.name} {customer.address1} {customer.type}
); }; return ( <>
Transportation Transportation Routes Edit Route

Edit Route Information

{ editSection === 'info' &&
Route Details
Route Name *
setRouteName(e.target.value)}/>
Vechile *
Driver *
Route Type *
{ newRouteType === 'outbound' &&
Estimated Start Time
}
Vehicle Checklist
{ currentVehicle?.checklist?.length > 0 && ( {currentVehicle.checklist.map((item, index) => ())}
{item}
) }
{errorMessage &&
{errorMessage}
}
{ newVehicle && newVehicle !== '' &&
Vehicle Information
Vehicle Number
{vehicles.find(item => item.id === newVehicle)?.vehicle_number}
Seating Capacity
{vehicles.find(item => item.id === newVehicle)?.capacity}
Mileage
{vehicles.find(item => item.id === newVehicle)?.mileage}
Make
{vehicles.find(item => item.id === newVehicle)?.make}
Model
{vehicles.find(item => item.id === newVehicle)?.model}
License Plate
{vehicles.find(item => item.id === newVehicle)?.tag}
Year
{vehicles.find(item => item.id === newVehicle)?.year}
GPS ID
{vehicles.find(item => item.id === newVehicle)?.gps_tag}
EZPass
{vehicles.find(item => item.id === newVehicle)?.ezpass}
Vin
{vehicles.find(item => item.id === newVehicle)?.vin || ''}
} { newDriver && newDriver !== '' &&
Driver Information
Personal Details
Driver Name
{drivers.find(item => item.id === newDriver)?.name}
Preferred Name
{drivers.find(item => item.id === newDriver)?.name_cn}
Job Title
{drivers.find(item => item.id === newDriver)?.title}
Job Type
{drivers.find(item => item.id === newDriver)?.employment_status}
License Type
{drivers.find(item => item.id === newDriver)?.license_type}
Phone Number
{drivers.find(item => item.id === newDriver)?.phone}
Email
{drivers.find(item => item.id === newDriver)?.email}
}
} { editSection === 'assignment' &&
customer?.customer_route_status !== PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT ) || [] } : undefined} setNewCustomerList={setNewCustomerList} onAddCustomer={(addFn) => setAddCustomerToRoute(() => addFn)} />
Scheduled Absences ({currentRoute?.route_customer_list?.filter(item => item?.customer_route_status === PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT)?.length || 0})
{ currentRoute?.route_customer_list.filter(customer => customer?.customer_route_status === PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT)?.map((abItem) => { return
{abItem.customer_name} {abItem.customer_address} {abItem.customer_pickup_status}
}) }
Unassigned Customers ({unassignedCustomers?.length || 0})
{ unassignedCustomers?.map((customer) => { return }) }
}
closeAddCheckItemModal()}> Add New Checklist Item <> {newChecklistItems?.map((item, index) => (
setNewChecklistItems([...newChecklistItems].map((a, index1) => {if (index1 === index) {return e.target.value;} return a;}))}/>
))}
closeCopyCheckItemModal()}> Click on Route to Select <> {[...allRoutes, ...tomorrowRoutes].filter(r => r.id !== currentRoute?.id).map((route) => { return (
setSelectedRouteChecklistToCopy(route)}>
{route.name}
{vehicles.find((a) => a.id === route.vehicle)?.checklist?.map((item, index) => {item})}
); })}
); }; export default RouteEdit;