fix
This commit is contained in:
BIN
app/.DS_Store
vendored
BIN
app/.DS_Store
vendored
Binary file not shown.
BIN
app/views/.DS_Store
vendored
BIN
app/views/.DS_Store
vendored
Binary file not shown.
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "/static/css/main.46cc12be.css",
|
||||
"main.js": "/static/js/main.b35b44f5.js",
|
||||
"main.js": "/static/js/main.c97709ee.js",
|
||||
"static/js/787.c4e7f8f9.chunk.js": "/static/js/787.c4e7f8f9.chunk.js",
|
||||
"static/media/landing.png": "/static/media/landing.d4c6072db7a67dff6a78.png",
|
||||
"index.html": "/index.html",
|
||||
"main.46cc12be.css.map": "/static/css/main.46cc12be.css.map",
|
||||
"main.b35b44f5.js.map": "/static/js/main.b35b44f5.js.map",
|
||||
"main.c97709ee.js.map": "/static/js/main.c97709ee.js.map",
|
||||
"787.c4e7f8f9.chunk.js.map": "/static/js/787.c4e7f8f9.chunk.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.46cc12be.css",
|
||||
"static/js/main.b35b44f5.js"
|
||||
"static/js/main.c97709ee.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="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"><link rel="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.b35b44f5.js"></script><link href="/static/css/main.46cc12be.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"><link rel="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.c97709ee.js"></script><link href="/static/css/main.46cc12be.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
File diff suppressed because one or more lines are too long
3
app/views/static/js/main.c97709ee.js
Normal file
3
app/views/static/js/main.c97709ee.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
client/.DS_Store
vendored
BIN
client/.DS_Store
vendored
Binary file not shown.
@@ -301,6 +301,15 @@ const EventsCalendar = () => {
|
||||
eventsDateMap.set(dateString, value);
|
||||
}
|
||||
}
|
||||
// Sort each day by earliest start time first
|
||||
eventsDateMap.forEach((items, key) => {
|
||||
const sortedItems = [...items].sort((a, b) => {
|
||||
const aTime = moment(a?.start_time).valueOf();
|
||||
const bTime = moment(b?.start_time).valueOf();
|
||||
return aTime - bTime;
|
||||
});
|
||||
eventsDateMap.set(key, sortedItems);
|
||||
});
|
||||
return eventsDateMap;
|
||||
};
|
||||
|
||||
|
||||
@@ -94,6 +94,7 @@ const CreateCustomer = () => {
|
||||
const [consentToTextMessages, setConsentToTextMessages] = useState('');
|
||||
const [preferredTextLanguage, setPreferredTextLanguage] = useState('');
|
||||
const [consentToMediaUse, setConsentToMediaUse] = useState('');
|
||||
const selectableCustomerTypes = [CUSTOMER_TYPE.MEMBER, CUSTOMER_TYPE.VISITOR];
|
||||
|
||||
// Medical & Insurance - Providers
|
||||
const [primaryCarePhysician, setPrimaryCarePhysician] = useState('');
|
||||
@@ -595,8 +596,8 @@ const CreateCustomer = () => {
|
||||
<div className="field-label">Customer Type</div>
|
||||
<select value={customerType} onChange={e => setCustomerType(e.target.value)}>
|
||||
<option value="">Select...</option>
|
||||
{Object.keys(CUSTOMER_TYPE).map(key => (
|
||||
<option key={key} value={CUSTOMER_TYPE[key]}>{CUSTOMER_TYPE_TEXT[CUSTOMER_TYPE[key]]}</option>
|
||||
{selectableCustomerTypes.map((typeOption) => (
|
||||
<option key={typeOption} value={typeOption}>{CUSTOMER_TYPE_TEXT[typeOption] || typeOption}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
@@ -937,6 +938,7 @@ const CreateCustomer = () => {
|
||||
onChange={setDietaryRestrictions}
|
||||
options={DIETARY_RESTRICTIONS_GROUPED}
|
||||
placeholder="e.g., No Pork"
|
||||
exclusiveOptionValue="regular"
|
||||
/>
|
||||
</div>
|
||||
<div className="me-4">
|
||||
|
||||
@@ -94,6 +94,7 @@ const UpdateCustomer = () => {
|
||||
const [dischargeConfirmReason, setDischargeConfirmReason] = useState('');
|
||||
const [dischargeConfirmReasonOther, setDischargeConfirmReasonOther] = useState('');
|
||||
const [isDischarging, setIsDischarging] = useState(false);
|
||||
const [isReactivating, setIsReactivating] = useState(false);
|
||||
|
||||
// Care & Services
|
||||
const [dietaryRestrictions, setDietaryRestrictions] = useState([]);
|
||||
@@ -105,6 +106,8 @@ const UpdateCustomer = () => {
|
||||
const [consentToTextMessages, setConsentToTextMessages] = useState('');
|
||||
const [preferredTextLanguage, setPreferredTextLanguage] = useState('');
|
||||
const [consentToMediaUse, setConsentToMediaUse] = useState('');
|
||||
const selectableCustomerTypes = [CUSTOMER_TYPE.MEMBER, CUSTOMER_TYPE.VISITOR];
|
||||
const hasLegacyCustomerType = !!customerType && !selectableCustomerTypes.includes(customerType);
|
||||
|
||||
// Medical & Insurance - Providers
|
||||
const [primaryCarePhysician, setPrimaryCarePhysician] = useState('');
|
||||
@@ -268,6 +271,36 @@ const UpdateCustomer = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleReactivate = async () => {
|
||||
if (!currentCustomer?.id) return;
|
||||
|
||||
setIsReactivating(true);
|
||||
const currentUserName = localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user'))?.name : '';
|
||||
|
||||
const reactivateData = {
|
||||
status: 'active',
|
||||
type: CUSTOMER_TYPE.MEMBER,
|
||||
edit_by: currentUserName,
|
||||
edit_date: new Date()
|
||||
};
|
||||
|
||||
try {
|
||||
await CustomerService.updateCustomer(currentCustomer.id, reactivateData);
|
||||
setCurrentCustomer((prev) => ({
|
||||
...prev,
|
||||
status: 'active',
|
||||
type: CUSTOMER_TYPE.MEMBER
|
||||
}));
|
||||
setCustomerType(CUSTOMER_TYPE.MEMBER);
|
||||
alert('Customer has been reactivated successfully.');
|
||||
} catch (error) {
|
||||
console.error('Error reactivating customer:', error);
|
||||
alert('Error reactivating customer. Please try again.');
|
||||
} finally {
|
||||
setIsReactivating(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onPharmacyChange = (selectedPharmacy) => {
|
||||
setPharmacy(selectedPharmacy);
|
||||
setPharmacyId(selectedPharmacy?.value);
|
||||
@@ -933,8 +966,11 @@ const UpdateCustomer = () => {
|
||||
<div className="field-label">Customer Type</div>
|
||||
<select value={customerType} onChange={e => setCustomerType(e.target.value)}>
|
||||
<option value="">Select...</option>
|
||||
{Object.keys(CUSTOMER_TYPE).map(key => (
|
||||
<option key={key} value={CUSTOMER_TYPE[key]}>{CUSTOMER_TYPE_TEXT[CUSTOMER_TYPE[key]]}</option>
|
||||
{hasLegacyCustomerType && (
|
||||
<option value={customerType}>{CUSTOMER_TYPE_TEXT[customerType] || customerType}</option>
|
||||
)}
|
||||
{selectableCustomerTypes.map((typeOption) => (
|
||||
<option key={typeOption} value={typeOption}>{CUSTOMER_TYPE_TEXT[typeOption] || typeOption}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
@@ -1072,7 +1108,14 @@ const UpdateCustomer = () => {
|
||||
<div className="app-main-content-fields-section">
|
||||
<div className="me-4">
|
||||
<div className="field-label">Address Line 1 {index === 0 && <span className="required">*</span>}</div>
|
||||
<input type="text" placeholder="e.g., 100 Sunshine Lane" className="long" value={address.line1} onChange={e => updateAddress(index, 'line1', e.target.value)}/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="e.g., 100 Sunshine Lane"
|
||||
className="long"
|
||||
value={address.line1}
|
||||
onChange={e => updateAddress(index, 'line1', e.target.value)}
|
||||
required={index === 0}
|
||||
/>
|
||||
</div>
|
||||
<div className="me-4">
|
||||
<div className="field-label">Address Line 2</div>
|
||||
@@ -1269,6 +1312,13 @@ const UpdateCustomer = () => {
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{!isCustomerActive() && (
|
||||
<div style={{ marginTop: '16px' }}>
|
||||
<button className="btn btn-warning btn-sm" onClick={handleReactivate} disabled={isReactivating}>
|
||||
<BoxArrowRight className="me-2" size={16}></BoxArrowRight>{isReactivating ? 'Reactivating...' : 'Reactivate Customer'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="list row mb-5">
|
||||
<div className="col-md-12 col-sm-12 col-xs-12">
|
||||
@@ -1290,6 +1340,7 @@ const UpdateCustomer = () => {
|
||||
onChange={setDietaryRestrictions}
|
||||
options={DIETARY_RESTRICTIONS_GROUPED}
|
||||
placeholder="e.g., No Pork"
|
||||
exclusiveOptionValue="regular"
|
||||
/>
|
||||
</div>
|
||||
<div className="me-4">
|
||||
|
||||
@@ -89,11 +89,17 @@ const Dashboard = () => {
|
||||
?.filter(item => item.status === 'active');
|
||||
}
|
||||
|
||||
setEvents(processedEvents);
|
||||
const sortedEvents = [...(processedEvents || [])].sort((a, b) => {
|
||||
const aTime = moment(a?.start_time).valueOf();
|
||||
const bTime = moment(b?.start_time).valueOf();
|
||||
return aTime - bTime;
|
||||
});
|
||||
|
||||
setEvents(sortedEvents);
|
||||
|
||||
// Group events by date
|
||||
const eventsDateMap = new Map();
|
||||
processedEvents?.forEach(eventItem => {
|
||||
sortedEvents?.forEach(eventItem => {
|
||||
const dateString = moment(eventItem.start_time).format('MMM Do, YYYY');
|
||||
if (eventsDateMap.has(dateString)) {
|
||||
eventsDateMap.set(dateString, [...eventsDateMap.get(dateString), eventItem]);
|
||||
@@ -101,6 +107,16 @@ const Dashboard = () => {
|
||||
eventsDateMap.set(dateString, [eventItem]);
|
||||
}
|
||||
});
|
||||
|
||||
eventsDateMap.forEach((items, key) => {
|
||||
const sortedItems = [...items].sort((a, b) => {
|
||||
const aTime = moment(a?.start_time).valueOf();
|
||||
const bTime = moment(b?.start_time).valueOf();
|
||||
return aTime - bTime;
|
||||
});
|
||||
eventsDateMap.set(key, sortedItems);
|
||||
});
|
||||
|
||||
setGroupedEvents(eventsDateMap);
|
||||
} catch (error) {
|
||||
console.error('Error fetching events:', error);
|
||||
|
||||
@@ -22,9 +22,6 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
|
||||
const [showFilterDropdown, setShowFilterDropdown] = useState(false);
|
||||
const [showManageTableDropdown, setShowManageTableDropdown] = useState(false);
|
||||
const [showExportDropdown, setShowExportDropdown] = useState(false);
|
||||
const [healthConditionFilter, setHealthConditionFilter] = useState('');
|
||||
const [paymentStatusFilter, setPaymentStatusFilter] = useState('');
|
||||
const [serviceRequirementFilter, setServiceRequirementFilter] = useState('');
|
||||
const [tagsFilter, setTagsFilter] = useState([]);
|
||||
const [availableLabels, setAvailableLabels] = useState([]);
|
||||
const [showAvatarModal, setShowAvatarModal] = useState(false);
|
||||
@@ -32,6 +29,7 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
|
||||
const [avatarCustomerName, setAvatarCustomerName] = useState('');
|
||||
const [avatarLoading, setAvatarLoading] = useState(false);
|
||||
const [customerAvatars, setCustomerAvatars] = useState({});
|
||||
const isAllCustomersPage = title === 'All Customers';
|
||||
const [columns, setColumns] = useState([
|
||||
{
|
||||
key: 'name',
|
||||
@@ -98,26 +96,6 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
|
||||
label: 'Fasting',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
key: 'health_condition',
|
||||
label: 'Health Condition',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
key: 'payment_status',
|
||||
label: 'Payment Status',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
key: 'payment_due_date',
|
||||
label: 'Payment Due Date',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
key: 'service_requirement',
|
||||
label: 'Service Requirement',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
key: 'tags',
|
||||
label: 'Tags',
|
||||
@@ -183,21 +161,6 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
|
||||
filtered = filtered.filter(item => item.status === 'active' && item.type !== CUSTOMER_TYPE.TRANSFERRED && item.type !== CUSTOMER_TYPE.DECEASED && item.type !== CUSTOMER_TYPE.DISCHARGED);
|
||||
}
|
||||
|
||||
// Health condition filter
|
||||
if (healthConditionFilter) {
|
||||
filtered = filtered.filter(item => item?.health_condition === healthConditionFilter);
|
||||
}
|
||||
|
||||
// Payment status filter
|
||||
if (paymentStatusFilter) {
|
||||
filtered = filtered.filter(item => item?.payment_status === paymentStatusFilter);
|
||||
}
|
||||
|
||||
// Service requirement filter
|
||||
if (serviceRequirementFilter) {
|
||||
filtered = filtered.filter(item => item?.service_requirement === serviceRequirementFilter);
|
||||
}
|
||||
|
||||
// Tags filter
|
||||
if (tagsFilter.length > 0) {
|
||||
filtered = filtered.filter(item => {
|
||||
@@ -207,7 +170,7 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
|
||||
}
|
||||
|
||||
setFilteredCustomers(filtered);
|
||||
}, [keyword, customers, showInactive, healthConditionFilter, paymentStatusFilter, serviceRequirementFilter, tagsFilter])
|
||||
}, [keyword, customers, showInactive, tagsFilter])
|
||||
|
||||
useEffect(() => {
|
||||
const newCustomers = [...customers];
|
||||
@@ -256,9 +219,6 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
|
||||
}
|
||||
|
||||
const cleanFilterAndClose = () => {
|
||||
setHealthConditionFilter('');
|
||||
setPaymentStatusFilter('');
|
||||
setServiceRequirementFilter('');
|
||||
setTagsFilter([]);
|
||||
setShowFilterDropdown(false);
|
||||
}
|
||||
@@ -360,7 +320,7 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
|
||||
/>
|
||||
)
|
||||
)}
|
||||
{AuthService.canAddOrEditCustomers() && <PencilSquare size={16} className="clickable" onClick={() => goToView(customer?.id)} style={{ flexShrink: 0 }}></PencilSquare>}
|
||||
{AuthService.canAddOrEditCustomers() && <PencilSquare size={16} className="clickable" onClick={() => isAllCustomersPage ? goToEdit(customer?.id) : goToView(customer?.id)} style={{ flexShrink: 0 }}></PencilSquare>}
|
||||
<span className="clickable" style={{ color: '#0066B1', textDecoration: 'underline', cursor: 'pointer' }} onClick={() => goToView(customer?.id)}>{customer?.name}</span>
|
||||
</div>
|
||||
</td>}
|
||||
@@ -376,10 +336,6 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
|
||||
{columns.find(col => col.key === 'address')?.show && <td>{customer?.address1 || customer?.address2 || customer?.address3 || customer?.address4 || customer?.address5}</td>}
|
||||
{columns.find(col => col.key === 'phone')?.show && <td>{customer?.phone || customer?.home_phone || customer?.mobile_phone}</td>}
|
||||
{columns.find(col => col.key === 'emergency_contact')?.show && <td>{customer?.emergency_contact}</td>}
|
||||
{columns.find(col => col.key === 'health_condition')?.show && <td>{customer?.health_condition}</td>}
|
||||
{columns.find(col => col.key === 'payment_status')?.show && <td>{customer?.payment_status}</td>}
|
||||
{columns.find(col => col.key === 'payment_due_date')?.show && <td>{customer?.payment_due_date}</td>}
|
||||
{columns.find(col => col.key === 'service_requirement')?.show && <td>{customer?.service_requirement}</td>}
|
||||
{columns.find(col => col.key === 'tags')?.show && <td>{customer?.tags?.join(', ')}</td>}
|
||||
</tr>)
|
||||
}
|
||||
@@ -398,38 +354,6 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
|
||||
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">Health Condition</div>
|
||||
<select value={healthConditionFilter} onChange={(e) => setHealthConditionFilter(e.target.value)}>
|
||||
<option value=""></option>
|
||||
<option value="diabetes">Diabetes</option>
|
||||
<option value="1-1">1-1</option>
|
||||
<option value="rounding list">Rounding List</option>
|
||||
<option value="MOLST/POA/Advanced Directive">MOLST/POA/Advanced Directive</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="app-main-content-fields-section margin-sm dropdown-container">
|
||||
<div className="me-4">
|
||||
<div className="field-label">Payment Status</div>
|
||||
<select value={paymentStatusFilter} onChange={(e) => setPaymentStatusFilter(e.target.value)}>
|
||||
<option value=""></option>
|
||||
<option value="paid">Paid</option>
|
||||
<option value="overdue">Overdue</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="app-main-content-fields-section margin-sm dropdown-container">
|
||||
<div className="me-4">
|
||||
<div className="field-label">Service Requirement</div>
|
||||
<select value={serviceRequirementFilter} onChange={(e) => setServiceRequirementFilter(e.target.value)}>
|
||||
<option value=""></option>
|
||||
<option value="wheelchair">Wheelchair</option>
|
||||
<option value="special care">Special Care</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="app-main-content-fields-section margin-sm dropdown-container">
|
||||
<div className="me-4">
|
||||
<div className="field-label">Tags</div>
|
||||
|
||||
@@ -145,6 +145,15 @@ const EventsCalendar = () => {
|
||||
eventsDateMap.set(dateString, value);
|
||||
}
|
||||
}
|
||||
// Sort each day by earliest start time first
|
||||
eventsDateMap.forEach((items, key) => {
|
||||
const sortedItems = [...items].sort((a, b) => {
|
||||
const aTime = moment(a?.start_time).valueOf();
|
||||
const bTime = moment(b?.start_time).valueOf();
|
||||
return aTime - bTime;
|
||||
});
|
||||
eventsDateMap.set(key, sortedItems);
|
||||
});
|
||||
return eventsDateMap;
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
|
||||
|
||||
const SideMenu = () => {
|
||||
const SIDEBAR_COLLAPSE_STORAGE_KEY = 'globalSidebarIsCollapsed';
|
||||
const sideNavs = [
|
||||
{
|
||||
icon: <Grid1x2 color="#777" size={14}/>,
|
||||
@@ -230,6 +231,17 @@ const SideMenu = () => {
|
||||
navigate(link);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const savedValue = sessionStorage.getItem(SIDEBAR_COLLAPSE_STORAGE_KEY);
|
||||
if (savedValue === 'true' || savedValue === 'false') {
|
||||
setCollapse(savedValue === 'true');
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
sessionStorage.setItem(SIDEBAR_COLLAPSE_STORAGE_KEY, String(collapse));
|
||||
}, [collapse]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`app-side-bar-container${collapse ? ' collapsed' : ''} noprint`}>
|
||||
|
||||
@@ -61,6 +61,55 @@ const InfoScreen = () => {
|
||||
const [showBackgroundModal, setShowBackgroundModal] = useState(false);
|
||||
const [selectedBackgroundFile, setSelectedBackgroundFile] = useState(null);
|
||||
|
||||
// Expand a recurring rule into instances within a date range
|
||||
const expandRecurrence = (rule, rangeFrom, rangeTo) => {
|
||||
const instances = [];
|
||||
const startDate = new Date(rule.start_repeat_date);
|
||||
const endDate = new Date(rule.end_repeat_date);
|
||||
const from = new Date(rangeFrom);
|
||||
const to = new Date(rangeTo);
|
||||
|
||||
const effectiveStart = from > startDate ? from : startDate;
|
||||
const effectiveEnd = to < endDate ? to : endDate;
|
||||
if (effectiveStart > effectiveEnd) return instances;
|
||||
|
||||
const freq = rule.rrule;
|
||||
let current = new Date(startDate);
|
||||
let count = 0;
|
||||
|
||||
while (current <= effectiveEnd && count < 1000) {
|
||||
if (current >= effectiveStart) {
|
||||
const dateStr = moment(current).format('YYYY-MM-DD');
|
||||
instances.push({
|
||||
...rule,
|
||||
id: `recur-${rule.id}-${dateStr}`,
|
||||
_recur_id: rule.id,
|
||||
start_time: new Date(current),
|
||||
stop_time: new Date(current),
|
||||
});
|
||||
}
|
||||
|
||||
if (freq === 'FREQ=DAILY') {
|
||||
current = new Date(current.getTime() + 24 * 60 * 60 * 1000);
|
||||
} else if (freq === 'FREQ=WEEKLY') {
|
||||
current = new Date(current.getTime() + 7 * 24 * 60 * 60 * 1000);
|
||||
} else if (freq === 'FREQ=MONTHLY') {
|
||||
const next = new Date(current);
|
||||
next.setMonth(next.getMonth() + 1);
|
||||
current = next;
|
||||
} else if (freq === 'FREQ=YEARLY') {
|
||||
const next = new Date(current);
|
||||
next.setFullYear(next.getFullYear() + 1);
|
||||
current = next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
return instances;
|
||||
};
|
||||
|
||||
const redirectTo = () => {
|
||||
navigate('/dashboard/dashboard');
|
||||
}
|
||||
@@ -505,91 +554,88 @@ const InfoScreen = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (customers?.length > 0 && resources?.length > 0) {
|
||||
// Get today's date in the format expected by the API
|
||||
const today = moment().format('YYYY-MM-DD');
|
||||
const now = moment();
|
||||
const fromDate = new Date(now.year(), now.month(), 1);
|
||||
const toDate = new Date(now.year(), now.month() + 1, 0);
|
||||
|
||||
EventsService.getAllEvents({ date: today }).then((data) => {
|
||||
// Filter medical events
|
||||
const medicalEventsData = data.data.filter((item) => {
|
||||
// Filter for medical events that are confirmed and active
|
||||
if (item.type !== 'medical' || !item.confirmed || item.status !== 'active') {
|
||||
return false;
|
||||
}
|
||||
Promise.all([
|
||||
EventsService.getAllEvents({
|
||||
from: EventsService.formatDate(fromDate),
|
||||
to: EventsService.formatDate(toDate)
|
||||
}),
|
||||
EventsService.getAllEventRecurrences()
|
||||
]).then(([eventsRes, recurRes]) => {
|
||||
const allEvents = eventsRes?.data || [];
|
||||
const recurrenceRules = (recurRes?.data || []).filter(
|
||||
rule => ['medical', 'activity', 'meal_plan'].includes(rule.type) && rule.status === 'active'
|
||||
);
|
||||
const recurInstances = recurrenceRules.flatMap(rule => expandRecurrence(rule, fromDate, toDate));
|
||||
const mergedEvents = [...allEvents, ...recurInstances];
|
||||
|
||||
// Add customer name
|
||||
item.customer = item?.data?.customer ?
|
||||
(customers.find(c => c.id === item?.data?.customer)?.name || item?.data?.client_name || '') :
|
||||
(item?.data?.client_name || '');
|
||||
|
||||
// Add driver info (from linked transportation event)
|
||||
item.driverInfo = item?.link_event_name || '';
|
||||
|
||||
// Format start time
|
||||
item.startTime = item?.start_time ?
|
||||
moment(item.start_time).format('MM/DD/YYYY h:mm A') : '';
|
||||
|
||||
return true;
|
||||
const todayEvents = mergedEvents.filter((item) => {
|
||||
if (!item?.start_time) return false;
|
||||
return moment(item.start_time).isSame(now, 'day');
|
||||
});
|
||||
|
||||
// Filter medical events
|
||||
const medicalEventsData = todayEvents
|
||||
.filter((item) => item.type === 'medical' && item.confirmed && item.status === 'active')
|
||||
.map((item) => ({
|
||||
...item,
|
||||
customer: item?.data?.customer
|
||||
? (customers.find(c => c.id === item?.data?.customer)?.name || item?.data?.client_name || '')
|
||||
: (item?.data?.client_name || ''),
|
||||
driverInfo: item?.link_event_name || '',
|
||||
startTime: item?.start_time ? moment(item.start_time).format('MM/DD/YYYY h:mm A') : '',
|
||||
}));
|
||||
|
||||
// Filter activity events
|
||||
const activityEventsData = data.data.filter((item) => {
|
||||
// Filter for activity events that are active
|
||||
if (item.type !== 'activity' || item.status !== 'active') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Format start time
|
||||
item.startTime = item?.start_time ?
|
||||
moment(item.start_time).format('h:mm A') : '';
|
||||
|
||||
return true;
|
||||
});
|
||||
const activityEventsData = todayEvents
|
||||
.filter((item) => item.type === 'activity' && item.status === 'active')
|
||||
.map((item) => ({
|
||||
...item,
|
||||
startTime: item?.start_time ? moment(item.start_time).format('h:mm A') : '',
|
||||
}));
|
||||
|
||||
// Filter meal events and parse meal information
|
||||
const mealEventsData = data.data.filter((item) => {
|
||||
// Filter for meal_plan events that are active
|
||||
if (item.type !== 'meal_plan' || item.status !== 'active') {
|
||||
return false;
|
||||
}
|
||||
const mealEventsData = todayEvents
|
||||
.filter((item) => item.type === 'meal_plan' && item.status === 'active')
|
||||
.map((item) => {
|
||||
const description = item.description || '';
|
||||
|
||||
// Parse meal information from description
|
||||
const description = item.description || '';
|
||||
|
||||
// Parse snack first (last in description)
|
||||
let snack = '';
|
||||
if (description.includes('Snack') || description.includes('snack')) {
|
||||
const snackParts = description.split(/Snack|snack/);
|
||||
if (snackParts.length > 1) {
|
||||
snack = snackParts[1].replace(/:/g, '').trim();
|
||||
let snack = '';
|
||||
if (description.includes('Snack') || description.includes('snack')) {
|
||||
const snackParts = description.split(/Snack|snack/);
|
||||
if (snackParts.length > 1) {
|
||||
snack = snackParts[1].replace(/:/g, '').trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse lunch from the first part (before snack)
|
||||
let lunch = '';
|
||||
const firstPart = description.split(/Snack|snack/)[0];
|
||||
if (firstPart.includes('Lunch') || firstPart.includes('lunch')) {
|
||||
const lunchParts = firstPart.split(/Lunch|lunch/);
|
||||
if (lunchParts.length > 1) {
|
||||
lunch = lunchParts[1].replace(/:/g, '').trim();
|
||||
let lunch = '';
|
||||
const firstPart = description.split(/Snack|snack/)[0];
|
||||
if (firstPart.includes('Lunch') || firstPart.includes('lunch')) {
|
||||
const lunchParts = firstPart.split(/Lunch|lunch/);
|
||||
if (lunchParts.length > 1) {
|
||||
lunch = lunchParts[1].replace(/:/g, '').trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse breakfast from the first part (before lunch)
|
||||
let breakfast = '';
|
||||
const secondPart = firstPart.split(/Lunch|lunch/)[0];
|
||||
if (secondPart.includes('Breakfast') || secondPart.includes('breakfast')) {
|
||||
const breakfastParts = secondPart.split(/Breakfast|breakfast/);
|
||||
if (breakfastParts.length > 1) {
|
||||
breakfast = breakfastParts[1].replace(/:/g, '').trim();
|
||||
let breakfast = '';
|
||||
const secondPart = firstPart.split(/Lunch|lunch/)[0];
|
||||
if (secondPart.includes('Breakfast') || secondPart.includes('breakfast')) {
|
||||
const breakfastParts = secondPart.split(/Breakfast|breakfast/);
|
||||
if (breakfastParts.length > 1) {
|
||||
breakfast = breakfastParts[1].replace(/:/g, '').trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item.breakfast = breakfast;
|
||||
item.lunch = lunch;
|
||||
item.snack = snack;
|
||||
|
||||
return true;
|
||||
});
|
||||
return {
|
||||
...item,
|
||||
breakfast,
|
||||
lunch,
|
||||
snack,
|
||||
};
|
||||
});
|
||||
|
||||
setMedicalEvents(medicalEventsData);
|
||||
setActivityEvents(activityEventsData);
|
||||
|
||||
@@ -779,7 +779,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 className="td-index"> {index}</td>
|
||||
<td className="td-index"> {index + 1}</td>
|
||||
<td>
|
||||
{ customer.customer_name}
|
||||
</td>
|
||||
|
||||
@@ -121,6 +121,42 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList = (a) => {}, view
|
||||
return assignedIds;
|
||||
};
|
||||
|
||||
const formatStructuredAddress = (line1, line2, city, state, zipCode) => {
|
||||
const cityState = [city, state].filter(Boolean).join(', ');
|
||||
return [line1, line2, cityState, zipCode]
|
||||
.filter(item => item && String(item).trim() !== '')
|
||||
.join(' ')
|
||||
.trim();
|
||||
};
|
||||
|
||||
const getCustomerAddressOptions = (customer) => {
|
||||
if (!customer) return [];
|
||||
|
||||
const structuredAddresses = [
|
||||
formatStructuredAddress(customer.address_line_1, customer.address_line_2, customer.city, customer.state, customer.zip_code),
|
||||
formatStructuredAddress(customer.address2_line_1, customer.address2_line_2, customer.city2, customer.state2, customer.zip_code2),
|
||||
formatStructuredAddress(customer.address3_line_1, customer.address3_line_2, customer.city3, customer.state3, customer.zip_code3),
|
||||
formatStructuredAddress(customer.address4_line_1, customer.address4_line_2, customer.city4, customer.state4, customer.zip_code4),
|
||||
formatStructuredAddress(customer.address5_line_1, customer.address5_line_2, customer.city5, customer.state5, customer.zip_code5),
|
||||
];
|
||||
|
||||
const legacyAddresses = [
|
||||
customer.address1,
|
||||
customer.address2,
|
||||
customer.address3,
|
||||
customer.address4,
|
||||
customer.address5,
|
||||
];
|
||||
|
||||
return Array.from(
|
||||
new Set(
|
||||
[...structuredAddresses, ...legacyAddresses]
|
||||
.map(item => (item || '').trim())
|
||||
.filter(item => item !== '')
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Fetch items from another resources.
|
||||
const endOffset = itemOffset + itemsPerPage;
|
||||
@@ -224,10 +260,11 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList = (a) => {}, view
|
||||
|
||||
const toggleItemToRouteList = (customer, value) => {
|
||||
if (value === 'false') {
|
||||
const customerAddresses = getCustomerAddressOptions(customer);
|
||||
setNewRouteCustomerList([].concat(newRouteCustomerList).concat([{
|
||||
customer_id: customer.id,
|
||||
customer_name: `${customer.name} ${customer.name_cn?.length > 0 ? `(${customer.name_cn})` : ``}`,
|
||||
customer_address: customer.address1,
|
||||
customer_address: customerAddresses[0] || '',
|
||||
customer_avatar: customer.avatar,
|
||||
customer_type: customer.type,
|
||||
customer_pickup_status: customer.pickup_status,
|
||||
@@ -246,10 +283,11 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList = (a) => {}, view
|
||||
|
||||
const toggleGroupedItemToRouteList = (customer, value) => {
|
||||
if (value === 'false') {
|
||||
const customerAddresses = getCustomerAddressOptions(customer);
|
||||
setNewRouteGroupedCustomerList([].concat(newRouteGroupedCustomerList).concat([{
|
||||
customer_id: customer.id,
|
||||
customer_name: `${customer.name} ${customer.name_cn?.length > 0 ? `(${customer.name_cn})` : ``}`,
|
||||
customer_address: customer.address1,
|
||||
customer_address: customerAddresses[0] || '',
|
||||
customer_avatar: customer.avatar,
|
||||
customer_group: newGroupName,
|
||||
customer_group_address: newGroupAddress,
|
||||
@@ -344,7 +382,7 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList = (a) => {}, view
|
||||
const newCustomer = {
|
||||
customer_id: customerData.id,
|
||||
customer_name: `${customerData.name} ${customerData.name_cn?.length > 0 ? `(${customerData.name_cn})` : ``}`,
|
||||
customer_address: customerData.address1 || '',
|
||||
customer_address: getCustomerAddressOptions(customerData)[0] || '',
|
||||
customer_avatar: customerData.avatar,
|
||||
customer_type: customerData.type,
|
||||
customer_pickup_status: customerData.pickup_status,
|
||||
@@ -480,38 +518,46 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList = (a) => {}, view
|
||||
|
||||
const Items = ({ currentItems }) => {
|
||||
return currentItems?.map(
|
||||
(customer) => <div key={customer.id} className="option-item">
|
||||
(customer) => {
|
||||
const addressOptions = getCustomerAddressOptions(customer);
|
||||
return <div key={customer.id} className="option-item">
|
||||
<input className="me-4 mt-2" type="checkbox" checked={newRouteCustomerList.find((item) => item.customer_id === customer.id)!==undefined} value={newRouteCustomerList.find((item) => item.customer_id === customer.id)!==undefined} onChange={(e) => toggleItemToRouteList(customer, e.target.value)}/>
|
||||
<div>
|
||||
<div>{`${customer.name}(${customer.name_cn})`}</div>
|
||||
{newRouteCustomerList.find((item) => item.customer_id === customer.id) && (<div>
|
||||
{customer.address1 && customer.address1 !== '' && <div><input className="me-4" name={`${customer.id}-address`} type="radio" onChange={(e) => setCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address1} checked={newRouteCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address1}/><small>{customer.address1}</small></div>}
|
||||
{customer.address2 && customer.address2 !== '' && <div><input className="me-4" name={`${customer.id}-address`} type="radio" onChange={(e) => setCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address2} checked={newRouteCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address2}/><small>{customer.address2}</small></div>}
|
||||
{customer.address3 && customer.address3 !== '' && <div><input className="me-4" name={`${customer.id}-address`} type="radio" onChange={(e) => setCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address3} checked={newRouteCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address3}/><small>{customer.address3}</small></div>}
|
||||
{customer.address4 && customer.address4 !== '' && <div><input className="me-4" name={`${customer.id}-address`} type="radio" onChange={(e) => setCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address4} checked={newRouteCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address4}/><small>{customer.address4}</small></div>}
|
||||
{customer.address5 && customer.address5 !== '' && <div><input className="me-4" name={`${customer.id}-address`} type="radio" onChange={(e) => setCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address5} checked={newRouteCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address5}/><small>{customer.address5}</small></div>}
|
||||
{addressOptions.map((address, idx) => (
|
||||
<div key={`${customer.id}-address-${idx}`}>
|
||||
<input className="me-4" name={`${customer.id}-address`} type="radio" onChange={(e) => setCustomerAddress(customer.id, e.currentTarget.value)} value={address} checked={newRouteCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===address}/>
|
||||
<small>{address}</small>
|
||||
</div>
|
||||
))}
|
||||
</div>)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
const ItemsGroup = ({ currentItems }) => {
|
||||
const assignedIds = getAssignedCustomerIds();
|
||||
return currentItems?.filter(customer => !assignedIds.has(customer.id)).filter((customer) => customer.name.toLowerCase().includes(customerFilter.toLowerCase()) || customer.id.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address1?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address2?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address3?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address4?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address5?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.apartment?.toLowerCase().includes(customerFilter.toLocaleLowerCase()) ).map(
|
||||
(customer) => <div key={customer.id} className="option-item">
|
||||
(customer) => {
|
||||
const addressOptions = getCustomerAddressOptions(customer);
|
||||
return <div key={customer.id} className="option-item">
|
||||
<input className="me-4 mt-2" type="checkbox" checked={newRouteGroupedCustomerList.find((item) => item.customer_id === customer.id)!==undefined} value={newRouteGroupedCustomerList.find((item) => item.customer_id === customer.id)!==undefined} onChange={(e) => toggleGroupedItemToRouteList(customer, e.target.value)}/>
|
||||
<div>
|
||||
<div>{`${customer.name}(${customer.name_cn})`}</div>
|
||||
{newRouteGroupedCustomerList.find((item) => item.customer_id === customer.id) && (<div>
|
||||
{customer.address1 && customer.address1 !== '' && <div><input className="me-4" name={`${customer.id}-address`} type="radio" onChange={(e) => setGroupedCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address1} checked={newRouteGroupedCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address1}/><small>{customer.address1}</small></div>}
|
||||
{customer.address2 && customer.address2 !== '' && <div><input className="me-4" name={`${customer.id}-address`} type="radio" onChange={(e) => setGroupedCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address2} checked={newRouteGroupedCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address2}/><small>{customer.address2}</small></div>}
|
||||
{customer.address3 && customer.address3 !== '' && <div><input className="me-4" name={`${customer.id}-address`} type="radio" onChange={(e) => setGroupedCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address3} checked={newRouteGroupedCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address3}/><small>{customer.address3}</small></div>}
|
||||
{customer.address4 && customer.address4 !== '' && <div><input className="me-4" name={`${customer.id}-address`} type="radio" onChange={(e) => setGroupedCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address4} checked={newRouteGroupedCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address4}/><small>{customer.address4}</small></div>}
|
||||
{customer.address5 && customer.address5 !== '' && <div><input className="me-4" name={`${customer.id}-address`} type="radio" onChange={(e) => setGroupedCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address5} checked={newRouteGroupedCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address5}/><small>{customer.address5}</small></div>}
|
||||
{addressOptions.map((address, idx) => (
|
||||
<div key={`${customer.id}-group-address-${idx}`}>
|
||||
<input className="me-4" name={`${customer.id}-address`} type="radio" onChange={(e) => setGroupedCustomerAddress(customer.id, e.currentTarget.value)} value={address} checked={newRouteGroupedCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===address}/>
|
||||
<small>{address}</small>
|
||||
</div>
|
||||
))}
|
||||
</div>)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
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 { selectAllRoutes, transRoutesSlice, selectTomorrowAllRoutes, selectAllActiveDrivers, selectAllActiveVehicles, selectHistoryRoutes } from "./../../store";
|
||||
import { Breadcrumb, Tabs, Tab } from "react-bootstrap";
|
||||
import RouteCustomerEditor from "./RouteCustomerEditor";
|
||||
import { AuthService, TransRoutesService, CustomerService, EventsService } from "../../services";
|
||||
import TimePicker from 'react-time-picker';
|
||||
@@ -26,15 +26,10 @@ const RouteEdit = () => {
|
||||
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);
|
||||
@@ -166,42 +161,6 @@ const RouteEdit = () => {
|
||||
}
|
||||
}
|
||||
|
||||
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');
|
||||
@@ -489,13 +448,11 @@ const RouteEdit = () => {
|
||||
<div className="me-4">
|
||||
<div className="field-label">Vehicle Checklist
|
||||
</div>
|
||||
{ currentVehicle?.checklist?.length > 0 && (<table className="mb-4">
|
||||
{ vehicles.find(item => item.id === newVehicle)?.checklist?.length > 0 && (<table className="mb-4">
|
||||
<tbody>
|
||||
{currentVehicle.checklist.map((item, index) => (<tr key={index}><td>{item}</td></tr>))}
|
||||
{vehicles.find(item => item.id === newVehicle)?.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">
|
||||
@@ -696,52 +653,6 @@ const RouteEdit = () => {
|
||||
</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;}))}/>
|
||||
<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>
|
||||
</>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={() => closeAddCheckItemModal()}>
|
||||
Close
|
||||
</Button>
|
||||
<Button variant="primary" onClick={() => saveChecklistItems()}>
|
||||
Save Checklist Items
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
<Modal show={showCopyCheckItem} onHide={() => closeCopyCheckItemModal()}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title> Click on Route to Select</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<>
|
||||
{[...allRoutes, ...tomorrowRoutes].filter(r => r.id !== currentRoute?.id).map((route) => {
|
||||
return (<div className={`card-container ${route.id === selectedRouteChecklistToCopy.id ? 'selected': ''}`} key={route.id} onClick={() => setSelectedRouteChecklistToCopy(route)}>
|
||||
<div>{route.name}</div>
|
||||
<div>
|
||||
{vehicles.find((a) => a.id === route.vehicle)?.checklist?.map((item, index) => <small key={index} className="me-2">{item}</small>)}
|
||||
</div>
|
||||
</div>);
|
||||
})}
|
||||
</>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={() => closeCopyCheckItemModal()}>
|
||||
Close
|
||||
</Button>
|
||||
<Button variant="primary" onClick={() => copyChecklistItems()}>
|
||||
Copy Checklist Items
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</>
|
||||
|
||||
);
|
||||
|
||||
@@ -135,10 +135,6 @@ const RouteView = () => {
|
||||
<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>
|
||||
|
||||
@@ -1271,62 +1271,6 @@ const RoutesDashboard = () => {
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<hr></hr>
|
||||
|
||||
<h6 className="text-primary">{moment(dateSelected)?.format('MM/DD/YYYY')}</h6>
|
||||
<div style={{'display': 'flex'}}>
|
||||
<div>
|
||||
<table className="personnel-info-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Index</th>
|
||||
<th>Customer Name</th>
|
||||
<th>Pickup Time</th>
|
||||
<th>Enter Center Time</th>
|
||||
<th>Leave Center Time</th>
|
||||
<th>Drop off TIme</th>
|
||||
<th>MA Number</th>
|
||||
<th>Inbound Name</th>
|
||||
<th>Outbound Name</th>
|
||||
<th>Total Hours</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
getAllUniqueCustomers(routesForSignature
|
||||
?.filter((route) => {
|
||||
if (!selectedDriver) {
|
||||
return route;
|
||||
} else {
|
||||
return route?.driver === selectedDriver;
|
||||
}
|
||||
}))?.map(({customer_name, customer_status_inbound, customer_status_outbound, customer_id, customer_enter_center_time, customer_dropoff_time, customer_leave_center_time, customer_pickup_time, inbound, outbound}, index) => {
|
||||
return (<tr key={index}>
|
||||
<td className="td-index">{index+1}</td>
|
||||
<td>{customer_name}</td>
|
||||
<td> <div style={{'padding': '4px 8px', 'border-radius': '8px', 'backgroundColor': `${customer_status_inbound === PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT || customer_status_inbound === PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT ? 'red':'white'}`}}>{customer_status_inbound !== PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT && customer_status_inbound !== PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT? (customer_pickup_time ? new Date(customer_pickup_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : '') : ''}</div></td>
|
||||
<td> <div style={{'padding': '4px 8px', 'border-radius': '8px', 'backgroundColor': `${customer_status_inbound === PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT || customer_status_inbound === PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT ? 'red':'white'}`}}>{customer_status_inbound !== PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT && customer_status_inbound !== PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT ? (customer_enter_center_time ? new Date(customer_enter_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}): ''): ''}</div></td>
|
||||
<td> <div style={{'padding': '4px 8px', 'border-radius': '8px', 'backgroundColor': `${customer_status_outbound === PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT || customer_status_outbound === PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT ? 'red':'white'}`}}>{customer_status_outbound !== PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT && customer_status_outbound !== PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT ? (customer_leave_center_time ? new Date(customer_leave_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}): ''): ''}</div></td>
|
||||
<td> <div style={{'padding': '4px 8px', 'border-radius': '8px', 'backgroundColor': `${customer_status_outbound === PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT || customer_status_outbound === PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT ? 'red':'white'}`}}>{customer_status_outbound !== PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT && customer_status_outbound !== PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT? (customer_dropoff_time ? new Date(customer_dropoff_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : ''): ''}</div></td>
|
||||
<td>{customers.length > 0 && customers.find(c => c?.id === customer_id || c?.name === customer_name)?.medicaid_number || customers.find(c => c?.id === customer_id)?.medicare_number}</td>
|
||||
<td>{inbound?.name || ''}</td>
|
||||
<td>{outbound?.name || ''}</td>
|
||||
<td>{customer_leave_center_time && customer_enter_center_time && Math.round((new Date(customer_leave_center_time) - new Date(customer_enter_center_time))/1000/3600) || ''}</td>
|
||||
</tr>)
|
||||
})
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="ms-4">
|
||||
<div className="mb-4"><strong>Center Director Signature:</strong></div>
|
||||
{directorSignature && <div className="mb-4"><img width="200px" src={`data:image/jpg;base64, ${directorSignature}`}/></div>}
|
||||
{!directorSignature && <div className="mb-4">No Director Signature Uploaded yet</div>}
|
||||
<div className="mb-4"><strong>Upload Center Director New Signature: </strong></div>
|
||||
<div className="mb-4"><input type="file" onChange={(e) => setSelectedFile(e.target.files[0])} className="form-control-file border"></input></div>
|
||||
<div className="mb-4"><button onClick={() => uploadDirectorSignature()} className="btn btn-sm btn-primary">Submit</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab eventKey="allRoutesStatus" title="All Routes Status">
|
||||
<div className="list row">
|
||||
|
||||
@@ -90,7 +90,7 @@ const RoutesSection = ({transRoutes, copyList, sectionName, drivers, vehicles, c
|
||||
{`${sectionName}: `} <span className="route-stats">{
|
||||
(sectionName.includes('Inbound') ||
|
||||
sectionName.includes('Outbound')) &&
|
||||
(`${seniors?.length} Scheduled (${seniors.filter(item => [CUSTOMER_TYPE.MEMBER, CUSTOMER_TYPE.SELF_PAY].includes(item.customer_type) && ![PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT, PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT].includes(item?.customer_route_status))?.length} Members ${seniors.filter(item=> [CUSTOMER_TYPE.VISITOR].includes(item?.customer_type) && ![PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT, PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT].includes(item?.customer_route_status))?.length} Visitors)`)}</span>
|
||||
(`${seniors?.length} Scheduled (${seniors.filter(item => [CUSTOMER_TYPE.MEMBER, CUSTOMER_TYPE.SELF_PAY].includes(item.customer_type) && ![PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT, PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT].includes(item?.customer_route_status))?.length} Members ${seniors.filter(item=> [CUSTOMER_TYPE.VISITOR].includes(item?.customer_type) && ![PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT, PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT].includes(item?.customer_route_status))?.length} Visitors)${sectionName.includes('Inbound') ? ` ${seniors.filter(item => item?.customer_route_status === PERSONAL_ROUTE_STATUS.IN_CENTER)?.length} checked in` : ''}`)}</span>
|
||||
</h6>
|
||||
{ canAddNew && (
|
||||
<small className="me-4" onClick={() => { if (routeType) {redirect(routeType)} else {redirect()}}}>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, {useState, useEffect} from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { CustomerService, DriverService, EmployeeService, EventsService, TransRoutesService } from "../../services";
|
||||
import { CustomerService, DriverService, EventsService, TransRoutesService } from "../../services";
|
||||
import { selectAllActiveDrivers, selectAllActiveVehicles } from "./../../store";
|
||||
import moment from 'moment';
|
||||
import DatePicker from "react-datepicker";
|
||||
import { PERSONAL_ROUTE_STATUS, ROUTE_STATUS } from "../../shared";
|
||||
import { ROUTE_STATUS } from "../../shared";
|
||||
|
||||
const RouteSignatureList = () => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
@@ -15,91 +15,20 @@ const RouteSignatureList = () => {
|
||||
const navigate = useNavigate();
|
||||
const [dateSelected, setDateSelected] = useState(new Date());
|
||||
const [routes, setRoutes] = useState([]);
|
||||
const [directorSignature, setDirectorSignature] = useState(undefined);
|
||||
const [selectedFile, setSelectedFile] = useState();
|
||||
const [selectedDriver, setSelectedDriver] = useState(undefined);
|
||||
const [driverList, setDriverList] = useState([]);
|
||||
const [customers, setCustomers] = useState([]);
|
||||
|
||||
const redirectToDashboard = () => {
|
||||
navigate(`/trans-routes/dashboard`);
|
||||
}
|
||||
|
||||
const uploadDirectorSignature = () => {
|
||||
const formData = new FormData();
|
||||
const site = EventsService.site;
|
||||
formData.append("file", selectedFile);
|
||||
if (selectedFile) {
|
||||
if (directorSignature) {
|
||||
CustomerService.deleteFile({'name': `center_director_signature_site_${site}`}).then(() => {
|
||||
CustomerService.uploadAvatar(`center_director_signature_site_${site}`, formData).then(() => {
|
||||
CustomerService.getAvatar(`center_director_signature_site_${site}`).then(data => {
|
||||
if (data?.data) {
|
||||
setDirectorSignature(data?.data)
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
} else {
|
||||
CustomerService.uploadAvatar(`center_director_signature_site_${site}`, formData).then(() => {
|
||||
CustomerService.getAvatar(`center_director_signature_site_${site}`).then(data => {
|
||||
if (data?.data) {
|
||||
setDirectorSignature(data?.data)
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const getAllUniqueCustomers = (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, customer_status_inbound: route.type === 'inbound' && item.customer_route_status, customer_status_outbound: route.type === 'outbound' && item.customer_route_status, inbound: route.type === 'inbound' && route, outbound: route.type === 'outbound' && route}))
|
||||
for (const customer of customerList) {
|
||||
const existItem = result.find((item => (item.customer_id === customer.customer_id) || (item?.customer_name?.replaceAll(' ', '')?.toLowerCase() === customer?.customer_name?.replaceAll(' ', '')?.toLowerCase()) ));
|
||||
if (existItem) {
|
||||
result = result.filter(item => item !== existItem);
|
||||
const newItem = Object.assign({}, existItem, {
|
||||
customer_enter_center_time: existItem?.customer_enter_center_time || customer?.customer_enter_center_time,
|
||||
customer_leave_center_time: existItem?.customer_leave_center_time || customer?.customer_leave_center_time,
|
||||
customer_pickup_time: existItem?.customer_pickup_time || customer?.customer_pickup_time,
|
||||
customer_dropoff_time: existItem?.customer_dropoff_time || customer?.customer_dropoff_time,
|
||||
inbound: existItem?.inbound || customer?.inbound,
|
||||
outbound: existItem?.outbound || customer?.outbound,
|
||||
customer_status_inbound: existItem?.customer_status_inbound || customer?.customer_status_inbound,
|
||||
customer_status_outbound: existItem?.customer_status_outbound || customer?.customer_status_outbound
|
||||
})
|
||||
result.push(newItem);
|
||||
} else {
|
||||
result.push(customer);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.sort((a, b) => {
|
||||
if (a.customer_name < b.customer_name) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
DriverService.getAllActiveDrivers('driver', 'active').then((data) => {
|
||||
setDriverList(data.data);
|
||||
});
|
||||
CustomerService.getAllCustomers().then((data) => setCustomers(data?.data));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const site = EventsService.site;
|
||||
CustomerService.getAvatar(`center_director_signature_site_${site}`).then(data => {
|
||||
if (data?.data) {
|
||||
setDirectorSignature(data?.data)
|
||||
}
|
||||
});
|
||||
TransRoutesService.getAll(moment(dateSelected)?.format('MM/DD/YYYY')).then(data => {
|
||||
const routesResults = data.data;
|
||||
const finalRoutes = routesResults.map(async (routeItem) => {
|
||||
@@ -170,63 +99,6 @@ const RouteSignatureList = () => {
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<hr></hr>
|
||||
|
||||
{moment(dateSelected)?.format('MM/DD/YYYY')}
|
||||
<div style={{'display': 'flex'}}>
|
||||
<div>
|
||||
<table className="personnel-info-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Index</th>
|
||||
<th>Customer Name</th>
|
||||
<th>Pickup Time</th>
|
||||
<th>Enter Center Time</th>
|
||||
<th>Leave Center Time</th>
|
||||
<th>Drop off TIme</th>
|
||||
<th>MA Number</th>
|
||||
<th>Inbound Name</th>
|
||||
<th>Outbound Name</th>
|
||||
<th>Total Hours</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
getAllUniqueCustomers(routes?.filter((route) => {
|
||||
if (!selectedDriver) {
|
||||
return route;
|
||||
} else {
|
||||
return route?.driver === selectedDriver;
|
||||
}
|
||||
}))?.map(({customer_name, customer_status_inbound, customer_status_outbound, customer_id, customer_enter_center_time, customer_dropoff_time, customer_leave_center_time, customer_pickup_time, inbound, outbound}, index) => {
|
||||
console.log('customers', customers);
|
||||
return (<tr key={index}>
|
||||
<td>{index+1}</td>
|
||||
<td>{customer_name}</td>
|
||||
<td style={{'backgroundColor': `${customer_status_inbound === PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT || customer_status_inbound === PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT ? 'red':'white'}`}}>{customer_status_inbound !== PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT && customer_status_inbound !== PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT? (customer_pickup_time ? new Date(customer_pickup_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : '') : ''}</td>
|
||||
<td style={{'backgroundColor': `${customer_status_inbound === PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT || customer_status_inbound === PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT ? 'red':'white'}`}}>{customer_status_inbound !== PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT && customer_status_inbound !== PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT ? (customer_enter_center_time ? new Date(customer_enter_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}): ''): ''}</td>
|
||||
<td style={{'backgroundColor': `${customer_status_outbound === PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT || customer_status_outbound === PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT ? 'red':'white'}`}}>{customer_status_outbound !== PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT && customer_status_outbound !== PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT ? (customer_leave_center_time ? new Date(customer_leave_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}): ''): ''}</td>
|
||||
<td style={{'backgroundColor': `${customer_status_outbound === PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT || customer_status_outbound === PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT ? 'red':'white'}`}}>{customer_status_outbound !== PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT && customer_status_outbound !== PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT? (customer_dropoff_time ? new Date(customer_dropoff_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : ''): ''}</td>
|
||||
<td>{customers.length > 0 && customers.find(c => c?.id === customer_id || c?.name === customer_name)?.medicaid_number || customers.find(c => c?.id === customer_id)?.medicare_number}</td>
|
||||
<td>{inbound?.name || ''}</td>
|
||||
<td>{outbound?.name || ''}</td>
|
||||
<td>{customer_leave_center_time && customer_enter_center_time && Math.round((new Date(customer_leave_center_time) - new Date(customer_enter_center_time))/1000/3600) || ''}</td>
|
||||
</tr>)
|
||||
})
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="ms-4">
|
||||
<div className="mb-4"><strong>Center Director Signature:</strong></div>
|
||||
{directorSignature && <div className="mb-4"><img width="200px" src={`data:image/jpg;base64, ${directorSignature}`}/></div>}
|
||||
{!directorSignature && <div className="mb-4">No Director Signature Uploaded yet</div>}
|
||||
<div className="mb-4"><strong>Upload Center Director New Signature: </strong></div>
|
||||
<div className="mb-4"><input type="file" onChange={(e) => setSelectedFile(e.target.files[0])} className="form-control-file border"></input></div>
|
||||
<div className="mb-4"><button onClick={() => uploadDirectorSignature()} className="btn btn-sm btn-primary">Submit</button></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -47,18 +47,25 @@ const AddVehicleInspection = () => {
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
if (!selectedFile || !inspectionDate) {
|
||||
window.alert('Please select a date and a file.');
|
||||
if (!inspectionDate) {
|
||||
window.alert('Please select a date.');
|
||||
return;
|
||||
}
|
||||
const formData = new FormData();
|
||||
formData.append('file', selectedFile);
|
||||
const fileType = isYearly ? 'yearlyInspection' : 'monthlyInspection';
|
||||
VehicleService.uploadVechileFile(formData, currentVehicle.id, currentVehicle.vehicle_number, fileType, inspectionDate).then(() => {
|
||||
setSelectedFile(null);
|
||||
setInspectionDate(null);
|
||||
fetchFiles();
|
||||
});
|
||||
|
||||
// File upload is optional on add page.
|
||||
if (selectedFile) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', selectedFile);
|
||||
const fileType = isYearly ? 'yearlyInspection' : 'monthlyInspection';
|
||||
VehicleService.uploadVechileFile(formData, currentVehicle.id, currentVehicle.vehicle_number, fileType, inspectionDate).then(() => {
|
||||
setSelectedFile(null);
|
||||
setInspectionDate(null);
|
||||
fetchFiles();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
goBack();
|
||||
};
|
||||
|
||||
const goBack = () => {
|
||||
@@ -119,7 +126,7 @@ const AddVehicleInspection = () => {
|
||||
/>
|
||||
</div>
|
||||
<div className="me-4">
|
||||
<div className="field-label">Inspection File</div>
|
||||
<div className="field-label">Inspection File (Optional)</div>
|
||||
<label className="custom-file-upload">
|
||||
<Upload width={20} color={"#fff"} className="me-2" /> Upload
|
||||
<input type="file" onChange={(e) => setSelectedFile(e.target.files[0])} />
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useDispatch } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { AuthService, VehicleService } from "../../services";
|
||||
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab } from "react-bootstrap";
|
||||
import { Columns, Download, Filter, PencilSquare, PersonSquare, Plus } from "react-bootstrap-icons";
|
||||
import { Columns, Download, Filter, PersonSquare, Plus } from "react-bootstrap-icons";
|
||||
import { ManageTable, Export } from "../../shared/components";
|
||||
|
||||
const VehicleList = () => {
|
||||
@@ -183,7 +183,7 @@ const VehicleList = () => {
|
||||
filteredVehicles.map((vehicle, index) => <tr key={vehicle.id}>
|
||||
<td className="td-checkbox"><input type="checkbox" checked={selectedItems.includes(vehicle.id)} onClick={()=>toggleItem(vehicle?.id)}/></td>
|
||||
<td className="td-index">{index + 1}</td>
|
||||
{columns.find(col => col.key === 'vehicle_number')?.show && <td> {AuthService.canAddOrEditVechiles() && <PencilSquare size={16} className="clickable me-2" onClick={() => goToEdit(vehicle?.id)}></PencilSquare>} {AuthService.canViewVechiles() ? <button className="btn btn-link btn-sm" onClick={() => goToView(vehicle?.id)}>{vehicle?.vehicle_number}</button> : vehicle?.vehicle_number } </td>}
|
||||
{columns.find(col => col.key === 'vehicle_number')?.show && <td>{AuthService.canViewVechiles() ? <button className="btn btn-link btn-sm" onClick={() => goToView(vehicle?.id)}>{vehicle?.vehicle_number}</button> : vehicle?.vehicle_number }</td>}
|
||||
{columns.find(col => col.key === 'tag')?.show && <td>{vehicle?.tag}</td>}
|
||||
{columns.find(col => col.key === 'capacity')?.show && <td>{vehicle?.capacity}</td>}
|
||||
{columns.find(col => col.key === 'mileage')?.show && <td>{vehicle?.mileage}</td>}
|
||||
|
||||
@@ -5,6 +5,7 @@ const MultiSelectDropdown = ({
|
||||
onChange,
|
||||
options = [],
|
||||
placeholder = 'Select...',
|
||||
exclusiveOptionValue = null,
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [search, setSearch] = useState('');
|
||||
@@ -30,29 +31,42 @@ const MultiSelectDropdown = ({
|
||||
}, []);
|
||||
|
||||
const selectedValues = (value || []).map(v => v.value);
|
||||
const hasExclusiveSelected = !!exclusiveOptionValue && selectedValues.includes(exclusiveOptionValue);
|
||||
|
||||
const filteredOptions = search
|
||||
? flatOptions.filter(opt => opt.label.toLowerCase().includes(search.toLowerCase()))
|
||||
: flatOptions;
|
||||
|
||||
const allFilteredSelected = filteredOptions.length > 0 && filteredOptions.every(opt => selectedValues.includes(opt.value));
|
||||
const isOptionDisabled = (opt) => (
|
||||
hasExclusiveSelected && opt.value !== exclusiveOptionValue
|
||||
);
|
||||
const selectableFilteredOptions = filteredOptions.filter(opt => !isOptionDisabled(opt));
|
||||
const allFilteredSelected = selectableFilteredOptions.length > 0
|
||||
&& selectableFilteredOptions.every(opt => selectedValues.includes(opt.value));
|
||||
|
||||
const toggleOption = (opt) => {
|
||||
if (isOptionDisabled(opt)) {
|
||||
return;
|
||||
}
|
||||
const exists = selectedValues.includes(opt.value);
|
||||
if (exists) {
|
||||
onChange(value.filter(v => v.value !== opt.value));
|
||||
} else {
|
||||
if (exclusiveOptionValue && opt.value === exclusiveOptionValue) {
|
||||
onChange([opt]);
|
||||
return;
|
||||
}
|
||||
onChange([...value, opt]);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleSelectAll = () => {
|
||||
if (allFilteredSelected) {
|
||||
const filteredValues = filteredOptions.map(o => o.value);
|
||||
const filteredValues = selectableFilteredOptions.map(o => o.value);
|
||||
onChange(value.filter(v => !filteredValues.includes(v.value)));
|
||||
} else {
|
||||
const currentValues = new Set(selectedValues);
|
||||
const newItems = filteredOptions.filter(o => !currentValues.has(o.value));
|
||||
const newItems = selectableFilteredOptions.filter(o => !currentValues.has(o.value));
|
||||
onChange([...value, ...newItems]);
|
||||
}
|
||||
};
|
||||
@@ -122,8 +136,9 @@ const MultiSelectDropdown = ({
|
||||
{filteredOptions.map((opt) => (
|
||||
<div
|
||||
key={opt.value}
|
||||
className={`multi-select-dropdown__option ${selectedValues.includes(opt.value) ? 'multi-select-dropdown__option--selected' : ''}`}
|
||||
className={`multi-select-dropdown__option ${selectedValues.includes(opt.value) ? 'multi-select-dropdown__option--selected' : ''} ${isOptionDisabled(opt) ? 'multi-select-dropdown__option--disabled' : ''}`}
|
||||
onClick={() => toggleOption(opt)}
|
||||
style={isOptionDisabled(opt) ? { opacity: 0.5, cursor: 'not-allowed' } : {}}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -131,6 +146,7 @@ const MultiSelectDropdown = ({
|
||||
onChange={() => toggleOption(opt)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="multi-select-dropdown__checkbox"
|
||||
disabled={isOptionDisabled(opt)}
|
||||
/>
|
||||
<span>{opt.label}</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user