This commit is contained in:
2026-02-12 14:21:23 -05:00
parent 699f5bd562
commit f6c47d8acc
26 changed files with 225 additions and 77 deletions

BIN
.DS_Store vendored

Binary file not shown.

BIN
app/.DS_Store vendored

Binary file not shown.

View File

@@ -1,16 +1,16 @@
{
"files": {
"main.css": "/static/css/main.6616f3cb.css",
"main.js": "/static/js/main.02f98813.js",
"main.css": "/static/css/main.3cda7e3a.css",
"main.js": "/static/js/main.171aba0b.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.6616f3cb.css.map": "/static/css/main.6616f3cb.css.map",
"main.02f98813.js.map": "/static/js/main.02f98813.js.map",
"main.3cda7e3a.css.map": "/static/css/main.3cda7e3a.css.map",
"main.171aba0b.js.map": "/static/js/main.171aba0b.js.map",
"787.c4e7f8f9.chunk.js.map": "/static/js/787.c4e7f8f9.chunk.js.map"
},
"entrypoints": [
"static/css/main.6616f3cb.css",
"static/js/main.02f98813.js"
"static/css/main.3cda7e3a.css",
"static/js/main.171aba0b.js"
]
}

View File

@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script><link rel="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.02f98813.js"></script><link href="/static/css/main.6616f3cb.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.171aba0b.js"></script><link href="/static/css/main.3cda7e3a.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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

Binary file not shown.

BIN
client/build/.DS_Store vendored

Binary file not shown.

View File

@@ -557,13 +557,22 @@ table .group td {
border-color: #ccc!important;
}
.react-time-picker__wrapper input[type=number] {
border:none;
.react-time-picker__wrapper input[type=number],
.react-datetime-picker__wrapper input[type=number] {
border: none;
padding-left: 2px !important;
padding-right: 2px !important;
height: auto !important;
}
.app-main-content-fields-section .field-value {
font-size: 12px;
color: #555;
min-height: 1.2em;
}
.app-main-content-fields-section .field-value:empty::before {
content: '\00a0';
}
.app-main-content-fields-section .field-body {
@@ -748,29 +757,46 @@ input[type="number"] {
/* box-sizing: border-box; */
}
/* Exclude react-time-picker inputs from the global input styles above */
.react-time-picker .react-time-picker__inputGroup__input {
/* Exclude react-time-picker and react-datetime-picker inputs from global input styles */
.react-time-picker .react-time-picker__inputGroup__input,
.react-datetime-picker .react-datetime-picker__inputGroup__input {
height: auto !important;
padding-left: 1px !important;
padding-right: 1px !important;
padding-left: 2px !important;
padding-right: 2px !important;
border: none !important;
border-radius: 0 !important;
box-sizing: content-box !important;
min-width: 0;
min-width: 0.54em !important;
width: auto !important;
font-variant-numeric: tabular-nums;
letter-spacing: normal;
text-align: center;
appearance: textfield;
-moz-appearance: textfield;
}
/* Hide number input spinners inside pickers */
.react-time-picker input[type=number]::-webkit-inner-spin-button,
.react-time-picker input[type=number]::-webkit-outer-spin-button,
.react-datetime-picker input[type=number]::-webkit-inner-spin-button,
.react-datetime-picker input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Add spacing between the ":" divider and the minute input */
.react-time-picker .react-time-picker__inputGroup__divider {
.react-time-picker .react-time-picker__inputGroup__divider,
.react-datetime-picker .react-datetime-picker__inputGroup__divider {
padding: 0 3px;
}
/* Ensure the leading zero and minute digit inputs have no extra spacing */
.react-time-picker .react-time-picker__inputGroup__leadingZero {
/* Ensure the leading zero and digit inputs have no extra spacing */
.react-time-picker .react-time-picker__inputGroup__leadingZero,
.react-datetime-picker .react-datetime-picker__inputGroup__leadingZero {
padding: 0;
margin: 0;
display: inline;
font-variant-numeric: tabular-nums;
}
input[type="email"] {
@@ -1712,6 +1738,8 @@ input[type="checkbox"] {
display: flex;
justify-content: center;
align-items: center;
gap: 16px;
flex-wrap: wrap;
}
.stage {

View File

@@ -243,11 +243,12 @@ const EventsCalendar = () => {
setToDate(new Date(endDate.getFullYear(), endDate.getMonth() + 1, 0));
},
onClickDate(date) {
if (currentTab === 'medicalCalendar') return;
// Parse as local date to avoid UTC timezone offset shifting the day
const [y, m, d] = date.split('-').map(Number);
const localDate = new Date(y, m - 1, d);
// Default to 10:00 AM for Medical Appointments and Activities tabs
if (currentTab === 'medicalCalendar' || currentTab === 'activitiesCalendar') {
// Default to 10:00 AM for Activities tab
if (currentTab === 'activitiesCalendar') {
localDate.setHours(10, 0, 0, 0);
}
setNewEventStartDateTime(localDate);
@@ -255,6 +256,7 @@ const EventsCalendar = () => {
setShowCreationModal(true);
},
onClickDateTime(dateTime) {
if (currentTab === 'medicalCalendar') return;
setNewEventStartDateTime(new Date(dateTime.replace(' ', 'T')));
setNewEventEndDateTime(new Date(dateTime.replace(' ', 'T')));
setShowCreationModal(true);
@@ -821,12 +823,12 @@ const EventsCalendar = () => {
style={{ cursor: 'pointer' }}
title="Edit"
/>
<Archive
{currentTab !== 'medicalCalendar' && <Archive
size={16}
onClick={() => { setDeleteTargetId(calendarEvent?.id); setShowDeleteConfirm(true); }}
style={{ cursor: 'pointer' }}
title="Delete"
/>
/>}
</div>
</>
}
@@ -842,13 +844,6 @@ const EventsCalendar = () => {
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">Show Deleted Events</div>
<input type="checkbox" value={showDeletedItems} checked={showDeletedItems === true} onClick={() => setShowDeletedItems(!showDeletedItems)} />
</div>
</div>
<hr style={{ margin: '8px 0' }} />
<div style={{ display: 'flex', flexDirection: 'column' }}>
<div className="field-label" style={{ marginBottom: '8px' }}>Filter by Type</div>
{currentTab === 'reminderDatesCalendar' ? (

View File

@@ -436,7 +436,18 @@ const CreateCustomer = () => {
// Additional Information
note,
edit_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name || '',
status: 'active'
status: 'active',
// Form Submission flags
hipaa_authorization_form: !!hipaaAuthorizationForm,
medication_management_consent_form: !!medicationManagementConsentForm,
freedom_of_choice_form: !!freedomOfChoiceForm,
meal_benefit_application_form: !!mealBenefitApplicationForm,
photo_video_release_form: !!photoVideoReleaseForm,
security_deposit_agreement_form: !!securityDepositAgreementForm,
recreational_program_contract_form: !!recreationalProgramContractForm,
tb_form: !!tbForm,
pre_screening_form: !!preScreeningForm,
};
const formData = new FormData();
@@ -445,6 +456,24 @@ const CreateCustomer = () => {
if (selectedFile) {
payload = Object.assign({}, payload, {avatar: formData})
}
// Collect form files for upload after customer creation
const formFiles = [
{ file: hipaaAuthorizationForm, fileType: 'hipaa_authorization_form' },
{ file: medicationManagementConsentForm, fileType: 'medication_management_consent_form' },
{ file: freedomOfChoiceForm, fileType: 'freedom_of_choice_form' },
{ file: mealBenefitApplicationForm, fileType: 'meal_benefit_application_form' },
{ file: photoVideoReleaseForm, fileType: 'photo_video_release_form' },
{ file: securityDepositAgreementForm, fileType: 'security_deposit_agreement_form' },
{ file: recreationalProgramContractForm, fileType: 'recreational_program_contract_form' },
{ file: tbForm, fileType: 'tb_form' },
{ file: preScreeningForm, fileType: 'pre_screening_form' },
].filter(f => f.file);
if (formFiles.length > 0) {
payload = Object.assign({}, payload, { formFiles });
}
dispatch(createCustomer(payload));
redirectTo();
};

View File

@@ -29,7 +29,7 @@ import {
YES_NO, YES_NO_TEXT,
PREFERRED_TEXT_LANGUAGE, PREFERRED_TEXT_LANGUAGE_TEXT
} from "../../shared";
import { Upload, BoxArrowRight } from "react-bootstrap-icons";
import { Upload, BoxArrowRight, CheckCircleFill } from "react-bootstrap-icons";
const UpdateCustomer = () => {
const navigate = useNavigate();
@@ -733,7 +733,18 @@ const UpdateCustomer = () => {
// Additional Information
note,
edit_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name || '',
status: 'active'
status: 'active',
// Form Submission flags
hipaa_authorization_form: hipaaAuthorizationForm ? true : (currentCustomer?.hipaa_authorization_form || false),
medication_management_consent_form: medicationManagementConsentForm ? true : (currentCustomer?.medication_management_consent_form || false),
freedom_of_choice_form: freedomOfChoiceForm ? true : (currentCustomer?.freedom_of_choice_form || false),
meal_benefit_application_form: mealBenefitApplicationForm ? true : (currentCustomer?.meal_benefit_application_form || false),
photo_video_release_form: photoVideoReleaseForm ? true : (currentCustomer?.photo_video_release_form || false),
security_deposit_agreement_form: securityDepositAgreementForm ? true : (currentCustomer?.security_deposit_agreement_form || false),
recreational_program_contract_form: recreationalProgramContractForm ? true : (currentCustomer?.recreational_program_contract_form || false),
tb_form: tbForm ? true : (currentCustomer?.tb_form || false),
pre_screening_form: preScreeningForm ? true : (currentCustomer?.pre_screening_form || false),
};
};
@@ -764,6 +775,13 @@ const UpdateCustomer = () => {
return true;
};
const uploadFormFile = (file, fileType) => {
if (!file) return;
const fd = new FormData();
fd.append('file', file);
CustomerService.uploadCustomerFile(fd, urlParams.id, currentCustomer?.name || '', fileType);
};
const saveCustomer = () => {
if (!validateCustomer()) {
return;
@@ -778,6 +796,18 @@ const UpdateCustomer = () => {
payload = Object.assign({}, payload, {avatar: formData})
}
dispatch(updateCustomer(payload));
// Upload form files
uploadFormFile(hipaaAuthorizationForm, 'hipaa_authorization_form');
uploadFormFile(medicationManagementConsentForm, 'medication_management_consent_form');
uploadFormFile(freedomOfChoiceForm, 'freedom_of_choice_form');
uploadFormFile(mealBenefitApplicationForm, 'meal_benefit_application_form');
uploadFormFile(photoVideoReleaseForm, 'photo_video_release_form');
uploadFormFile(securityDepositAgreementForm, 'security_deposit_agreement_form');
uploadFormFile(recreationalProgramContractForm, 'recreational_program_contract_form');
uploadFormFile(tbForm, 'tb_form');
uploadFormFile(preScreeningForm, 'pre_screening_form');
setTimeout(() => {
redirectToView();
}, 3000);
@@ -1562,24 +1592,27 @@ const UpdateCustomer = () => {
<div className="app-main-content-fields-section base-line">
<div className="me-4">
<div className="field-label">Maryland HIPAA Authorization</div>
{currentCustomer?.hipaa_authorization_form && !hipaaAuthorizationForm && <div style={{color: '#28a745', fontSize: '12px', marginBottom: '4px'}}><CheckCircleFill size={12} className="me-1"/>Uploaded</div>}
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload
<Upload width={20} color={"#fff"} className="me-2"></Upload> {currentCustomer?.hipaa_authorization_form ? 'Replace' : 'Upload'}
<input type="file" onChange={(e) => setHipaaAuthorizationForm(e.target.files[0])}/>
</label>
<div className="file-name">{hipaaAuthorizationForm?.name}</div>
</div>
<div className="me-4">
<div className="field-label">Medication Management Consent Form</div>
{currentCustomer?.medication_management_consent_form && !medicationManagementConsentForm && <div style={{color: '#28a745', fontSize: '12px', marginBottom: '4px'}}><CheckCircleFill size={12} className="me-1"/>Uploaded</div>}
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload
<Upload width={20} color={"#fff"} className="me-2"></Upload> {currentCustomer?.medication_management_consent_form ? 'Replace' : 'Upload'}
<input type="file" onChange={(e) => setMedicationManagementConsentForm(e.target.files[0])}/>
</label>
<div className="file-name">{medicationManagementConsentForm?.name}</div>
</div>
<div className="me-4">
<div className="field-label">Freedom Of Choice (Medicaid only)</div>
{currentCustomer?.freedom_of_choice_form && !freedomOfChoiceForm && <div style={{color: '#28a745', fontSize: '12px', marginBottom: '4px'}}><CheckCircleFill size={12} className="me-1"/>Uploaded</div>}
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload
<Upload width={20} color={"#fff"} className="me-2"></Upload> {currentCustomer?.freedom_of_choice_form ? 'Replace' : 'Upload'}
<input type="file" onChange={(e) => setFreedomOfChoiceForm(e.target.files[0])}/>
</label>
<div className="file-name">{freedomOfChoiceForm?.name}</div>
@@ -1588,24 +1621,27 @@ const UpdateCustomer = () => {
<div className="app-main-content-fields-section base-line">
<div className="me-4">
<div className="field-label">Meal Benefit Application</div>
{currentCustomer?.meal_benefit_application_form && !mealBenefitApplicationForm && <div style={{color: '#28a745', fontSize: '12px', marginBottom: '4px'}}><CheckCircleFill size={12} className="me-1"/>Uploaded</div>}
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload
<Upload width={20} color={"#fff"} className="me-2"></Upload> {currentCustomer?.meal_benefit_application_form ? 'Replace' : 'Upload'}
<input type="file" onChange={(e) => setMealBenefitApplicationForm(e.target.files[0])}/>
</label>
<div className="file-name">{mealBenefitApplicationForm?.name}</div>
</div>
<div className="me-4">
<div className="field-label">Photo-Video Release Waiver Form</div>
{currentCustomer?.photo_video_release_form && !photoVideoReleaseForm && <div style={{color: '#28a745', fontSize: '12px', marginBottom: '4px'}}><CheckCircleFill size={12} className="me-1"/>Uploaded</div>}
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload
<Upload width={20} color={"#fff"} className="me-2"></Upload> {currentCustomer?.photo_video_release_form ? 'Replace' : 'Upload'}
<input type="file" onChange={(e) => setPhotoVideoReleaseForm(e.target.files[0])}/>
</label>
<div className="file-name">{photoVideoReleaseForm?.name}</div>
</div>
<div className="me-4">
<div className="field-label">Security Deposit Payment Agreement (Self Pay only)</div>
{currentCustomer?.security_deposit_agreement_form && !securityDepositAgreementForm && <div style={{color: '#28a745', fontSize: '12px', marginBottom: '4px'}}><CheckCircleFill size={12} className="me-1"/>Uploaded</div>}
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload
<Upload width={20} color={"#fff"} className="me-2"></Upload> {currentCustomer?.security_deposit_agreement_form ? 'Replace' : 'Upload'}
<input type="file" onChange={(e) => setSecurityDepositAgreementForm(e.target.files[0])}/>
</label>
<div className="file-name">{securityDepositAgreementForm?.name}</div>
@@ -1614,8 +1650,9 @@ const UpdateCustomer = () => {
<div className="app-main-content-fields-section base-line">
<div className="me-4">
<div className="field-label">Recreational Program Contract (Senior Plus only)</div>
{currentCustomer?.recreational_program_contract_form && !recreationalProgramContractForm && <div style={{color: '#28a745', fontSize: '12px', marginBottom: '4px'}}><CheckCircleFill size={12} className="me-1"/>Uploaded</div>}
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload
<Upload width={20} color={"#fff"} className="me-2"></Upload> {currentCustomer?.recreational_program_contract_form ? 'Replace' : 'Upload'}
<input type="file" onChange={(e) => setRecreationalProgramContractForm(e.target.files[0])}/>
</label>
<div className="file-name">{recreationalProgramContractForm?.name}</div>
@@ -1627,16 +1664,18 @@ const UpdateCustomer = () => {
<div className="app-main-content-fields-section base-line">
<div className="me-4">
<div className="field-label">TB Form</div>
{currentCustomer?.tb_form && !tbForm && <div style={{color: '#28a745', fontSize: '12px', marginBottom: '4px'}}><CheckCircleFill size={12} className="me-1"/>Uploaded</div>}
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload
<Upload width={20} color={"#fff"} className="me-2"></Upload> {currentCustomer?.tb_form ? 'Replace' : 'Upload'}
<input type="file" onChange={(e) => setTbForm(e.target.files[0])}/>
</label>
<div className="file-name">{tbForm?.name}</div>
</div>
<div className="me-4">
<div className="field-label">Pre-Screening Form</div>
{currentCustomer?.pre_screening_form && !preScreeningForm && <div style={{color: '#28a745', fontSize: '12px', marginBottom: '4px'}}><CheckCircleFill size={12} className="me-1"/>Uploaded</div>}
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload
<Upload width={20} color={"#fff"} className="me-2"></Upload> {currentCustomer?.pre_screening_form ? 'Replace' : 'Upload'}
<input type="file" onChange={(e) => setPreScreeningForm(e.target.files[0])}/>
</label>
<div className="file-name">{preScreeningForm?.name}</div>

View File

@@ -100,6 +100,7 @@ const Seating = () => {
const [deletedItems, setDeletedItems] = useState([]);
const [editingLabelId, setEditingLabelId] = useState(null);
const [editingLabelName, setEditingLabelName] = useState('');
const [editingLabelColorId, setEditingLabelColorId] = useState(null);
const [selectedCustomer, setSelectedCustomer] = useState(undefined);
const [selectedCustomerLabel, setSelectedCustomerLabel] = useState(undefined);
@@ -646,12 +647,11 @@ const Seating = () => {
setStartAddLabel(false);
setEditingLabelId(null);
setEditingLabelName('');
setEditingLabelColorId(null);
}
const onDeleteLabel = (item) => {
setCurrentLabels(prevLabels => ({
...prevLabels.filter(lb => lb.id !== item.id)
}))
setCurrentLabels(prevLabels => prevLabels.filter(lb => lb.id !== item.id))
setDeletedItems(prevLabels => ([
...prevLabels,
item
@@ -680,6 +680,16 @@ const Seating = () => {
setEditingLabelName('');
}
const saveEditingLabelColor = (labelId, newColor) => {
LabelService.updateLabel(labelId, { label_color: newColor }).then(() => {
LabelService.getAll('active').then(data => {
setCurrentLabels(data?.data);
setOriginalLabelsList(data?.data);
});
});
setEditingLabelColorId(null);
}
const handleFullScreen = () => {
setIsFullScreen(true);
}
@@ -1134,7 +1144,11 @@ const Seating = () => {
<div className="mb-4">
{
currentLabels.map((item) => <div className="label-delete-item" key={item.id}>
<span style={{width: '16px', height: '16px', borderRadius: '16px', background: item.label_color, flexShrink: 0 }}></span>
<span
style={{width: '16px', height: '16px', borderRadius: '16px', background: item.label_color, flexShrink: 0, cursor: 'pointer', border: editingLabelColorId === item.id ? '2px solid #333' : 'none' }}
onClick={() => setEditingLabelColorId(editingLabelColorId === item.id ? null : item.id)}
title="Click to change color"
></span>
{editingLabelId === item.id ? (
<div style={{ display: 'flex', alignItems: 'center', gap: '4px', flex: 1, marginLeft: '8px' }}>
<input
@@ -1155,6 +1169,21 @@ const Seating = () => {
<FileX size={16} style={{ cursor: 'pointer' }} onClick={() => onDeleteLabel(item)} />
</>
)}
{editingLabelColorId === item.id && (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '6px', marginTop: '6px', padding: '6px', background: '#f8f8f8', borderRadius: '6px', width: '100%' }}>
{colorsList.map(color => (
<span
key={color}
onClick={() => saveEditingLabelColor(item.id, color)}
style={{
width: '20px', height: '20px', borderRadius: '50%', background: color, cursor: 'pointer',
border: item.label_color === color ? '2px solid #333' : '2px solid transparent',
display: 'inline-block'
}}
></span>
))}
</div>
)}
</div>)
}
</div>
@@ -1233,7 +1262,7 @@ const Seating = () => {
}
</div>
{
currentLabels.map((item) => <div className="mb-4" style={{fontSize: '12px'}} key={item.id}><span style={{width: '16px', height: '16px', borderRadius: '16px', background: item.label_color, display: 'inline-block', marginRight: '8px' }}></span>{item.label_name}</div>)
currentLabels.map((item) => <div className="mb-4" style={{fontSize: '12px', display: 'inline-flex', alignItems: 'center', marginRight: '16px'}} key={item.id}><span style={{width: '16px', height: '16px', borderRadius: '16px', background: item.label_color, display: 'inline-block', marginRight: '8px' }}></span>{item.label_name}</div>)
}
</div>
</div>

View File

@@ -8,7 +8,8 @@ const BreakfastSection = ({transRoutes, breakfastRecords, sectionName, confimHas
const createBreakfastForAll = async () => {
const dateStr = moment(selectedDate || new Date()).format('MM/DD/YYYY');
for (const c of customers) {
const unconfirmed = customers.filter(c => !c.has_breakfast);
for (const c of unconfirmed) {
await TransRoutesService.createBreakfastRecords({
customer_id: c?.customer_id,
customer_name: c?.customer_name,
@@ -39,7 +40,7 @@ const BreakfastSection = ({transRoutes, breakfastRecords, sectionName, confimHas
return (
<>
<div className="text-primary mb-4"><h5>{sectionName} {` (${customers?.length})`}</h5></div>
{breakfastRecords?.length <= 0 && (<div className="mb-2 mt-2"><button onClick={() => createBreakfastForAll()} className="btn btn-primary btn-sm">Confirm All Breakfast</button></div>)}
{customers?.some(c => !c.has_breakfast) && (<div className="mb-2 mt-2"><button onClick={() => createBreakfastForAll()} className="btn btn-primary btn-sm">Confirm All Breakfast</button></div>)}
<div className="list row">
<div className="col-md-12">
<table className="personnel-info-table">

View File

@@ -2,7 +2,7 @@ import React, {useState, useEffect} from "react";
import { useNavigate } from "react-router-dom";
import { AuthService, DailyRoutesTemplateService } from "../../services";
import { Breadcrumb, Modal, Button, Tabs, Tab } from "react-bootstrap";
import { PencilSquare, Eye } from "react-bootstrap-icons";
import { PencilSquare } from "react-bootstrap-icons";
import ReactPaginate from 'react-paginate';
const DailyTemplatesList = () => {
@@ -173,7 +173,6 @@ const DailyTemplatesList = () => {
{column.label} <span className="float-right" onClick={() => sortTableWithField(column.key)}><img src={`/images/${getSortingImg(column.key)}.png`} alt="sort"></img></span>
</th>)
}
<th>Actions</th>
</tr>
</thead>
<tbody>
@@ -188,9 +187,6 @@ const DailyTemplatesList = () => {
{columns.find(col => col.key === 'routes_count')?.show && <td>{template?.routes_count}</td>}
{columns.find(col => col.key === 'create_by')?.show && <td>{template?.create_by}</td>}
{columns.find(col => col.key === 'create_date')?.show && <td>{template?.create_date}</td>}
<td>
<Eye size={16} className="clickable me-2" onClick={() => goToView(template?.id)} title="View" />
</td>
</tr>)
}
</tbody>

View File

@@ -8,7 +8,8 @@ const LunchSection = ({transRoutes, lunchRecords, sectionName, confirmHasLunch,
const createLunchForAll = async () => {
const dateStr = moment(selectedDate || new Date()).format('MM/DD/YYYY');
for (const c of customers) {
const unconfirmed = customers.filter(c => !c.has_lunch);
for (const c of unconfirmed) {
await TransRoutesService.createLunchRecords({
customer_id: c?.customer_id,
customer_name: c?.customer_name,
@@ -39,7 +40,7 @@ const LunchSection = ({transRoutes, lunchRecords, sectionName, confirmHasLunch,
return (
<>
<div className="text-primary mb-4"><h5>{sectionName} {` (${customers?.length})`}</h5></div>
{lunchRecords?.length <= 0 && (<div className="mb-2 mt-2"><button onClick={() => createLunchForAll()} className="btn btn-primary btn-sm">Confirm All Lunch</button></div>)}
{customers?.some(c => !c.has_lunch) && (<div className="mb-2 mt-2"><button onClick={() => createLunchForAll()} className="btn btn-primary btn-sm">Confirm All Lunch</button></div>)}
<div className="list row">
<div className="col-md-12">
<table className="personnel-info-table">

View File

@@ -593,7 +593,7 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList = (a) => {}, view
if (item?.customers) {
return <Card key={index} index={index} moveCard={reorderItems} content={(<div className="customers-dnd-item-container">
<div className="stop-index"><span>{`Stop ${index+1}`}</span><RecordCircleFill size={16} color={"#0d6efd"} className="ms-2"></RecordCircleFill> </div>
<GripVertical className="me-4" size={14}></GripVertical>
<GripVertical className="me-4" size={20}></GripVertical>
<div className="customer-dnd-item" onClick={() => openEditAptGroupModal(index, item)}>
<span className="me-2">{item.customer_group} </span> <span>{item.customers[0]?.customer_group_address}</span>
<div className="customer-dnd-item-content">{item.customers.map(customer =>
@@ -610,7 +610,7 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList = (a) => {}, view
} else {
return <Card key={index} index={index} moveCard={reorderItems} content={<div className="customers-dnd-item-container">
<div className="stop-index"><span>{`Stop ${index+1}`}</span><RecordCircleFill size={16} color={"#0d6efd"} className="ms-2"></RecordCircleFill> </div>
<GripVertical className="me-4" size={14}></GripVertical>
<GripVertical className="me-4" size={20}></GripVertical>
<div className="customer-dnd-item">
<span>{item.customer_name} </span>
<small className="me-2">{item.customer_address}</small>

View File

@@ -366,7 +366,7 @@ const RouteEdit = () => {
return (
<div ref={drag} style={{ opacity }} className="customers-dnd-item-container-absent">
<GripVertical className="me-4" size={14}></GripVertical>
<GripVertical className="me-4" size={20}></GripVertical>
<div className="customer-dnd-item">
<span>{customer.name} </span>
<small className="me-2">{customer.address1}</small>
@@ -592,7 +592,7 @@ const RouteEdit = () => {
// Existing scheduled absences from route data
currentRoute?.route_customer_list?.filter(customer => customer?.customer_route_status === PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT)?.map((abItem) => {
return <div key={abItem.customer_id} className="customers-dnd-item-container-absent">
<GripVertical className="me-4" size={14}></GripVertical>
<GripVertical className="me-4" size={20}></GripVertical>
<div className="customer-dnd-item">
<span>{abItem.customer_name} </span>
<small className="me-2">{abItem.customer_address}</small>
@@ -612,7 +612,7 @@ const RouteEdit = () => {
.filter(a => !existingIds.has(a.customer_id))
.map((abItem) => (
<div key={`att-${abItem.customer_id}`} className="customers-dnd-item-container-absent" style={{ opacity: 0.85 }}>
<GripVertical className="me-4" size={14}></GripVertical>
<GripVertical className="me-4" size={20}></GripVertical>
<div className="customer-dnd-item">
<span>{abItem.customer_name} </span>
<small className="me-2">{abItem.customer_address}</small>

View File

@@ -8,7 +8,8 @@ const SnackSection = ({transRoutes, snackRecords, sectionName, confirmHasSnack,
const createSnackForAll = async () => {
const dateStr = moment(selectedDate || new Date()).format('MM/DD/YYYY');
for (const c of customers) {
const unconfirmed = customers.filter(c => !c.has_snack);
for (const c of unconfirmed) {
await TransRoutesService.createSnackRecords({
customer_id: c?.customer_id,
customer_name: c?.customer_name,
@@ -39,7 +40,7 @@ const SnackSection = ({transRoutes, snackRecords, sectionName, confirmHasSnack,
return (
<>
<div className="text-primary mb-4"><h5>{sectionName} {` (${customers?.length})`}</h5></div>
{snackRecords?.length <= 0 && (<div className="mb-2 mt-2"><button onClick={() => createSnackForAll()} className="btn btn-primary btn-sm">Confirm All Snack</button></div>)}
{customers?.some(c => !c.has_snack) && (<div className="mb-2 mt-2"><button onClick={() => createSnackForAll()} className="btn btn-primary btn-sm">Confirm All Snack</button></div>)}
<div className="list row">
<div className="col-md-12">
<table className="personnel-info-table">

View File

@@ -97,20 +97,20 @@ const UpdateVehicle = () => {
useEffect(() => {
if (currentVehicle) {
// Basic Information
setVehicleNumber(currentVehicle.vehicle_number || '');
setVehicleNumber(String(currentVehicle.vehicle_number ?? ''));
setResponsibleDriver(currentVehicle.responsible_driver ? {
value: currentVehicle.responsible_driver_id || '',
label: currentVehicle.responsible_driver || ''
} : null);
setCapacity(currentVehicle.capacity?.toString() || '');
setMileage(currentVehicle.mileage?.toString() || '');
setMake(currentVehicle.make || '');
setVehicleModel(currentVehicle.vehicle_model || '');
setYear(currentVehicle.year || '');
setVin(currentVehicle.vin || '');
setTag(currentVehicle.tag || '');
setGps(currentVehicle.gps_tag || '');
setEzpass(currentVehicle.ezpass || '');
setMake(String(currentVehicle.make ?? ''));
setVehicleModel(String(currentVehicle.vehicle_model ?? ''));
setYear(String(currentVehicle.year ?? ''));
setVin(String(currentVehicle.vin ?? ''));
setTag(String(currentVehicle.tag ?? ''));
setGps(String(currentVehicle.gps_tag ?? ''));
setEzpass(String(currentVehicle.ezpass ?? ''));
setHasLiftEquip(currentVehicle.has_lift_equip === true ? 'true' : (currentVehicle.has_lift_equip === false ? 'false' : ''));
setFuelType(currentVehicle.fuel_type || '');
setTitle(currentVehicle.title || '');

View File

@@ -6,6 +6,21 @@ import moment from 'moment';
const { createCustomer, createCustomerFailure, updateCustomer, updateCustomerFailure, deleteCustomer, deleteCustomerFailure } = customerSlice.actions;
const { fetchAllRoutesSuccess, fetchAllTomorrowRoutesSuccess, fetchAllHisotryRoutesSuccess } = transRoutesSlice.actions;
function* uploadFormFiles(customerId, customerName, formFiles) {
if (!formFiles || !customerId) return;
for (const { file, fileType } of formFiles) {
if (file) {
try {
const fd = new FormData();
fd.append('file', file);
yield call(CustomerService.uploadCustomerFile, fd, customerId, customerName || '', fileType);
} catch (err) {
console.error(`Error uploading ${fileType}:`, err);
}
}
}
}
function* createCustomerSaga(action) {
try {
const customer = yield call(CustomerService.createNewCustomer, action.payload.data);
@@ -15,6 +30,10 @@ function* createCustomerSaga(action) {
CustomerService.uploadAvatar(customer.data.id, action.payload.avatar)
}
}
// Upload form files after customer is created
if (customer?.data?.id && action.payload.formFiles) {
yield* uploadFormFiles(customer.data.id, action.payload.data?.name || '', action.payload.formFiles);
}
} catch(ex) {
yield put(createCustomerFailure(ex));
}

View File

@@ -68,6 +68,14 @@ const getClient = (id) => {
return http.get(`clients/${id}`);
}
const uploadCustomerFile = (data, customerId, name, fileType) => {
return http.post(`/files/upload-physical?objectId=${customerId}&name=${name}&fileType=${fileType}&model=customer`, data);
}
const getAllCustomerFiles = (customerId, name, fileType) => {
return http.get(`/files/list?objectId=${customerId}&name=${name}&fileType=${fileType}&model=customer`);
}
export const CustomerService = {
getAllActiveCustomers,
@@ -85,5 +93,7 @@ export const CustomerService = {
updateClient,
deleteClient,
getClientsByNameOrEmail,
getClient
getClient,
uploadCustomerFile,
getAllCustomerFiles
};