fix
All checks were successful
Build And Deploy Main / build-and-deploy (push) Successful in 30s

This commit is contained in:
2026-03-17 12:35:19 -04:00
parent 62d9f7d548
commit ba7aa58103
2 changed files with 239 additions and 104 deletions

View File

@@ -1,7 +1,7 @@
import React, {useState, useEffect} from "react"; import React, {useState, useEffect} from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { AuthService, EventsService, CustomerService, ResourceService, EventRequestsService } from "../../services"; import { AuthService, EventsService, CustomerService, ResourceService, EventRequestsService } from "../../services";
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab } from "react-bootstrap"; import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab, Dropdown } from "react-bootstrap";
import { Columns, Download, Filter, PencilSquare, PersonSquare, Plus } from "react-bootstrap-icons"; import { Columns, Download, Filter, PencilSquare, PersonSquare, Plus } from "react-bootstrap-icons";
import { ManageTable, Export } from "../../shared/components"; import { ManageTable, Export } from "../../shared/components";
@@ -15,6 +15,10 @@ const EventRequestList = () => {
const [comments, setComments] = useState({}); const [comments, setComments] = useState({});
const [keyword, setKeyword] = useState(''); const [keyword, setKeyword] = useState('');
const [selectedItems, setSelectedItems] = useState([]); const [selectedItems, setSelectedItems] = useState([]);
const [showFilterDropdown, setShowFilterDropdown] = useState(false);
const [requestTypeFilter, setRequestTypeFilter] = useState('');
const [transportationSupportFilter, setTransportationSupportFilter] = useState('');
const [newPatientFilter, setNewPatientFilter] = useState('');
const [columns, setColumns] = useState([ const [columns, setColumns] = useState([
{ {
key: 'customer_display', key: 'customer_display',
@@ -97,13 +101,26 @@ const EventRequestList = () => {
const filterRequestsFun = (item, statusParam, keywordParam) => { const filterRequestsFun = (item, statusParam, keywordParam) => {
const normalizedKeyword = `${keywordParam || ''}`.toLowerCase().trim(); const normalizedKeyword = `${keywordParam || ''}`.toLowerCase().trim();
const normalizedRequestTypeFilter = `${requestTypeFilter || ''}`.toLowerCase().trim();
const normalizedTransportationFilter = `${transportationSupportFilter || ''}`.toLowerCase().trim();
const normalizedNewPatientFilter = `${newPatientFilter || ''}`.toLowerCase().trim();
const statusLabel = getStatusLabel(item?.status || ''); const statusLabel = getStatusLabel(item?.status || '');
const completeBy = item?.edit_history?.[item?.edit_history?.length - 1]?.employee || ''; const completeBy = item?.edit_history?.[item?.edit_history?.length - 1]?.employee || '';
const itemRequestType = `${item?.type || ''}`.toLowerCase().trim();
const itemTransportation = `${item?.transportation || ''}`.toLowerCase().trim();
const itemNewPatient = `${item?.np || ''}`.toLowerCase().trim();
const isMatchingStructuredFilters =
(!normalizedRequestTypeFilter || itemRequestType === normalizedRequestTypeFilter) &&
(!normalizedTransportationFilter || itemTransportation === normalizedTransportationFilter) &&
(!normalizedNewPatientFilter || itemNewPatient === normalizedNewPatientFilter);
if (!normalizedKeyword) { if (!normalizedKeyword) {
return item?.status === statusParam; return item?.status === statusParam && isMatchingStructuredFilters;
} }
return ( return (
item?.status === statusParam && item?.status === statusParam &&
isMatchingStructuredFilters &&
( (
item?.customer_display?.toLowerCase()?.includes(normalizedKeyword) || item?.customer_display?.toLowerCase()?.includes(normalizedKeyword) ||
item?.source?.toLowerCase()?.includes(normalizedKeyword) || item?.source?.toLowerCase()?.includes(normalizedKeyword) ||
@@ -275,6 +292,66 @@ const EventRequestList = () => {
source: EventRequestsService.sourceList.find((item) => item?.value === eventRequest?.source)?.label || eventRequest?.source, source: EventRequestsService.sourceList.find((item) => item?.value === eventRequest?.source)?.label || eventRequest?.source,
complete_by: eventRequest?.edit_history && eventRequest?.edit_history[eventRequest?.edit_history?.length - 1]?.employee || '' complete_by: eventRequest?.edit_history && eventRequest?.edit_history[eventRequest?.edit_history?.length - 1]?.employee || ''
})); }));
const requestTypeOptions = [...new Set(eventRequests.map((item) => `${item?.type || ''}`.trim()).filter(Boolean))];
const transportationSupportOptions = [...new Set(eventRequests.map((item) => `${item?.transportation || ''}`.trim()).filter(Boolean))];
const newPatientOptions = [...new Set(eventRequests.map((item) => `${item?.np || ''}`.trim()).filter(Boolean))];
const cleanFilterAndClose = () => {
setRequestTypeFilter('');
setTransportationSupportFilter('');
setNewPatientFilter('');
setShowFilterDropdown(false);
};
const applyFilterAndClose = () => {
setShowFilterDropdown(false);
};
const customFilterMenu = React.forwardRef(
({ children, style, className, 'aria-labelledby': labeledBy }, ref) => (
<div
ref={ref}
style={style}
className={className}
aria-labelledby={labeledBy}
>
<h6>Filter By</h6>
<div className="app-main-content-fields-section margin-sm">
<div className="me-4">
<div className="field-label">Request Type</div>
<select value={requestTypeFilter} onChange={(e) => setRequestTypeFilter(e.currentTarget.value)}>
<option value="">All</option>
{requestTypeOptions.map((option) => <option key={option} value={option}>{option}</option>)}
</select>
</div>
</div>
<div className="app-main-content-fields-section margin-sm">
<div className="me-4">
<div className="field-label">Transportation Support</div>
<select value={transportationSupportFilter} onChange={(e) => setTransportationSupportFilter(e.currentTarget.value)}>
<option value="">All</option>
{transportationSupportOptions.map((option) => <option key={option} value={option}>{option}</option>)}
</select>
</div>
</div>
<div className="app-main-content-fields-section margin-sm">
<div className="me-4">
<div className="field-label">New Patient</div>
<select value={newPatientFilter} onChange={(e) => setNewPatientFilter(e.currentTarget.value)}>
<option value="">All</option>
{newPatientOptions.map((option) => <option key={option} value={option}>{option}</option>)}
</select>
</div>
</div>
<div className="list row">
<div className="col-md-12">
<button className="btn btn-default btn-sm float-right" onClick={() => cleanFilterAndClose()}> Cancel </button>
<button className="btn btn-primary btn-sm float-right" onClick={() => applyFilterAndClose()}> Filter </button>
</div>
</div>
</div>
)
);
const table = (statusParam, keywordParam) => <div className="list row mb-4"> const table = (statusParam, keywordParam) => <div className="list row mb-4">
<div className="col-md-12"> <div className="col-md-12">
@@ -356,7 +433,19 @@ const EventRequestList = () => {
</Tabs> </Tabs>
<div className="list-func-panel"> <div className="list-func-panel">
<input className="me-2 with-search-icon" type="text" placeholder="Search" value={keyword} onChange={(e) => setKeyword(e.currentTarget.value)} /> <input className="me-2 with-search-icon" type="text" placeholder="Search" value={keyword} onChange={(e) => setKeyword(e.currentTarget.value)} />
<button className="btn btn-primary me-2"><Filter size={16} className="me-2"></Filter>Filter</button> <Dropdown
key={'filter-event-request-list'}
id="filter-event-request-list"
className="me-2"
show={showFilterDropdown}
onToggle={(isOpen) => setShowFilterDropdown(isOpen)}
autoClose={false}
>
<Dropdown.Toggle variant="primary">
<Filter size={16} className="me-2"></Filter>Filter
</Dropdown.Toggle>
<Dropdown.Menu as={customFilterMenu}/>
</Dropdown>
<ManageTable columns={columns} onColumnsChange={setColumns} /> <ManageTable columns={columns} onColumnsChange={setColumns} />
{AuthService.canEditAppointmentRequests() && <button className="btn btn-primary me-2" onClick={() => goToCreateNew()}><Plus size={16}></Plus>Add New Appointment Request</button>} {AuthService.canEditAppointmentRequests() && <button className="btn btn-primary me-2" onClick={() => goToCreateNew()}><Plus size={16}></Plus>Add New Appointment Request</button>}
<Export <Export

View File

@@ -2,7 +2,6 @@ import React, {useState, useEffect} from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { AuthService, EventsService, CustomerService, ResourceService } from "../../services"; import { AuthService, EventsService, CustomerService, ResourceService } from "../../services";
import DatePicker from "react-datepicker"; import DatePicker from "react-datepicker";
import Select from 'react-select';
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab, Button, Modal, DropdownButton, Dropdown } from "react-bootstrap"; import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab, Button, Modal, DropdownButton, Dropdown } from "react-bootstrap";
import { Columns, Download, Filter, PencilSquare, PersonSquare, Plus } from "react-bootstrap-icons"; import { Columns, Download, Filter, PencilSquare, PersonSquare, Plus } from "react-bootstrap-icons";
import { ManageTable, Export } from "../../shared/components"; import { ManageTable, Export } from "../../shared/components";
@@ -18,11 +17,20 @@ const EventsMultipleList = () => {
const [resources, setResources] = useState([]); const [resources, setResources] = useState([]);
const [fromDate, setFromDate] = useState(new Date()); const [fromDate, setFromDate] = useState(new Date());
const [toDate, setToDate] = useState(new Date(new Date().setMonth(new Date().getMonth() + 6))); const [toDate, setToDate] = useState(new Date(new Date().setMonth(new Date().getMonth() + 6)));
const [selectedResource, setSelectedResource] = useState(null);
const [selectedCustomer, setSelectedCustomer] = useState(null);
const [filteredEvents, setFilteredEvents] = useState(events); const [filteredEvents, setFilteredEvents] = useState(events);
const [showDeletedItems, setShowDeletedItems] = useState(false); const [showDeletedItems, setShowDeletedItems] = useState(false);
const [showFilterDropdown, setShowFilterDropdown] = useState(false); const [showFilterDropdown, setShowFilterDropdown] = useState(false);
const [customerTypeFilter, setCustomerTypeFilter] = useState('');
const [programTypeFilter, setProgramTypeFilter] = useState('');
const [paySourceFilter, setPaySourceFilter] = useState('');
const [languageSupportFilter, setLanguageSupportFilter] = useState('');
const [transportationSupportFilter, setTransportationSupportFilter] = useState('');
const [eyesOnFilter, setEyesOnFilter] = useState('');
const [newPatientFilter, setNewPatientFilter] = useState('');
const [doctorOrderFilter, setDoctorOrderFilter] = useState('');
const [fastingRequiredFilter, setFastingRequiredFilter] = useState('');
const [idNeededFilter, setIdNeededFilter] = useState('');
const [needMedicationListFilter, setNeedMedicationListFilter] = useState('');
const [columns, setColumns] = useState([ const [columns, setColumns] = useState([
{ {
key: 'customer_name', key: 'customer_name',
@@ -230,24 +238,34 @@ const EventsMultipleList = () => {
useEffect(() => { useEffect(() => {
setFilteredEvents(events?.filter((event) => { setFilteredEvents(events?.filter((event) => {
if (selectedResource && selectedResource.label !== '' && selectedResource.value !== '') { return (
if (selectedResource?.label?.replaceAll(' ', '').replaceAll('', '').replaceAll('*', '').includes(event.provider?.replaceAll(' ', '').replaceAll('', '').replaceAll('*', '')) (!customerTypeFilter || event?.customer_type === customerTypeFilter) &&
|| event.provider?.replaceAll(' ', '').replaceAll('', '').replaceAll('*', '').includes(selectedResource?.label?.replaceAll(' ', '').replaceAll('', '').replaceAll('*', ''))) { (!programTypeFilter || event?.program_type === programTypeFilter) &&
return event; (!paySourceFilter || event?.pay_source === paySourceFilter) &&
} (!languageSupportFilter || event?.language_support === languageSupportFilter) &&
} else { (!transportationSupportFilter || event?.transportation_support === transportationSupportFilter) &&
return event; (!eyesOnFilter || event?.eyes_on === eyesOnFilter) &&
} (!newPatientFilter || event?.new_patient === newPatientFilter) &&
}).filter((event => { (!doctorOrderFilter || event?.doctor_order === doctorOrderFilter) &&
if (selectedCustomer && selectedCustomer !== '' && selectedCustomer.value !== '') { (!fastingRequiredFilter || event?.fasting_required === fastingRequiredFilter) &&
if ((event.customer_name || '').toLowerCase().includes((selectedCustomer?.label || '').toLowerCase())) { (!idNeededFilter || event?.id_needed === idNeededFilter) &&
return event; (!needMedicationListFilter || event?.need_medication_list === needMedicationListFilter)
} );
} else { }));
return event; }, [
} events,
}))); customerTypeFilter,
}, [selectedResource, selectedCustomer, events]); programTypeFilter,
paySourceFilter,
languageSupportFilter,
transportationSupportFilter,
eyesOnFilter,
newPatientFilter,
doctorOrderFilter,
fastingRequiredFilter,
idNeededFilter,
needMedicationListFilter
]);
@@ -470,8 +488,17 @@ const EventsMultipleList = () => {
</div>; </div>;
const cleanFilterAndClose = () => { const cleanFilterAndClose = () => {
setSelectedCustomer(null); setCustomerTypeFilter('');
setSelectedResource(null); setProgramTypeFilter('');
setPaySourceFilter('');
setLanguageSupportFilter('');
setTransportationSupportFilter('');
setEyesOnFilter('');
setNewPatientFilter('');
setDoctorOrderFilter('');
setFastingRequiredFilter('');
setIdNeededFilter('');
setNeedMedicationListFilter('');
setShowFilterDropdown(false); setShowFilterDropdown(false);
} }
@@ -490,11 +517,25 @@ const EventsMultipleList = () => {
return row; return row;
}); });
const getUniqueOptions = (key) => {
return [...new Set(events.map((event) => `${event?.[key] || ''}`.trim()).filter(Boolean))];
};
const customerTypeOptions = getUniqueOptions('customer_type');
const programTypeOptions = getUniqueOptions('program_type');
const paySourceOptions = getUniqueOptions('pay_source');
const languageSupportOptions = getUniqueOptions('language_support');
const transportationSupportOptions = getUniqueOptions('transportation_support');
const eyesOnOptions = getUniqueOptions('eyes_on');
const newPatientOptions = getUniqueOptions('new_patient');
const doctorOrderOptions = getUniqueOptions('doctor_order');
const fastingRequiredOptions = getUniqueOptions('fasting_required');
const idNeededOptions = getUniqueOptions('id_needed');
const needMedicationListOptions = getUniqueOptions('need_medication_list');
const customMenu = React.forwardRef( const customMenu = React.forwardRef(
({ children, style, className, 'aria-labelledby': labeledBy }, ref) => { ({ children, style, className, 'aria-labelledby': labeledBy }, ref) => {
const [value, setValue] = useState('');
return ( return (
<div <div
ref={ref} ref={ref}
@@ -505,86 +546,91 @@ const EventsMultipleList = () => {
<h6>Filter By</h6> <h6>Filter By</h6>
<div className="app-main-content-fields-section margin-sm"> <div className="app-main-content-fields-section margin-sm">
<div className="me-4"> <div className="me-4">
<div className="field-label">Customer</div> <div className="field-label">Customer Type</div>
<Select styles={{ <select value={customerTypeFilter} onChange={(e) => setCustomerTypeFilter(e.currentTarget.value)}>
control: (baseStyles, state) => ({ <option value="">All</option>
...baseStyles, {customerTypeOptions.map((option) => <option key={option} value={option}>{option}</option>)}
width: '210px', </select>
height: '45px', </div>
minHeight: '45px', <div className="me-4">
'padding-top': 0, <div className="field-label">Program Type</div>
'padding-bottom': 0, <select value={programTypeFilter} onChange={(e) => setProgramTypeFilter(e.currentTarget.value)}>
'margin-top': 0, <option value="">All</option>
'margin-bottom': 0 {programTypeOptions.map((option) => <option key={option} value={option}>{option}</option>)}
}), </select>
indicatorSeparator: (baseStyles, state) => ({
...baseStyles,
display: 'none'
}),
dropdownIndicator: (baseStyles) => ({
...baseStyles,
paddingRight: '12px'
}),
indicatorsContainer: (baseStyles) => ({
...baseStyles,
'margin-top': '-5px'
}),
placeholder: (baseStyles) => ({
...baseStyles,
'margin-top': '-5px',
'font-size': '13px'
}),
singleValue: (baseStyles, state) => ({
...baseStyles,
'margin-top': '-5px',
'font-size': '13px'
})
}} value={selectedCustomer || ''} onChange={selectedData => {setSelectedCustomer(selectedData); setSelectedItems([]);}} options={[{value: '', label: ''}, ...customers.filter(customer => customer?.status === 'active' && customer?.type !== 'discharged').map(resource => ({
value: resource?.id || '',
label: resource?.name || '',
}))]}></Select>
</div> </div>
</div> </div>
<div className="app-main-content-fields-section margin-sm"> <div className="app-main-content-fields-section margin-sm">
<div className="me-4"> <div className="me-4">
<div className="field-label">Provider</div> <div className="field-label">Pay Source</div>
<Select styles={{ <select value={paySourceFilter} onChange={(e) => setPaySourceFilter(e.currentTarget.value)}>
control: (baseStyles, state) => ({ <option value="">All</option>
...baseStyles, {paySourceOptions.map((option) => <option key={option} value={option}>{option}</option>)}
width: '210px', </select>
height: '45px', </div>
minHeight: '45px', <div className="me-4">
'padding-top': 0, <div className="field-label">Language Support</div>
'padding-bottom': 0, <select value={languageSupportFilter} onChange={(e) => setLanguageSupportFilter(e.currentTarget.value)}>
'margin-top': 0, <option value="">All</option>
'margin-bottom': 0 {languageSupportOptions.map((option) => <option key={option} value={option}>{option}</option>)}
}), </select>
indicatorSeparator: (baseStyles, state) => ({ </div>
...baseStyles, </div>
display: 'none' <div className="app-main-content-fields-section margin-sm">
}), <div className="me-4">
dropdownIndicator: (baseStyles) => ({ <div className="field-label">Transportation Support</div>
...baseStyles, <select value={transportationSupportFilter} onChange={(e) => setTransportationSupportFilter(e.currentTarget.value)}>
paddingRight: '12px' <option value="">All</option>
}), {transportationSupportOptions.map((option) => <option key={option} value={option}>{option}</option>)}
indicatorsContainer: (baseStyles) => ({ </select>
...baseStyles, </div>
'margin-top': '-5px' <div className="me-4">
}), <div className="field-label">Eyes-On</div>
placeholder: (baseStyles) => ({ <select value={eyesOnFilter} onChange={(e) => setEyesOnFilter(e.currentTarget.value)}>
...baseStyles, <option value="">All</option>
'margin-top': '-20px', {eyesOnOptions.map((option) => <option key={option} value={option}>{option}</option>)}
'font-size': '13px' </select>
}), </div>
singleValue: (baseStyles, state) => ({ </div>
...baseStyles, <div className="app-main-content-fields-section margin-sm">
'margin-top': '-20px', <div className="me-4">
'font-size': '13px' <div className="field-label">New Patient</div>
}) <select value={newPatientFilter} onChange={(e) => setNewPatientFilter(e.currentTarget.value)}>
}} value={selectedResource || ''} onChange={selectedData => {setSelectedResource(selectedData); setSelectedItems([]);}} options={[{value: '', label: ''}, ...resources?.filter(r => r.status === 'active').map(resource => ({ <option value="">All</option>
value: resource?.id || '', {newPatientOptions.map((option) => <option key={option} value={option}>{option}</option>)}
label: resource?.name || '', </select>
}))]}></Select> </div>
<div className="me-4">
<div className="field-label">Doctor's Order</div>
<select value={doctorOrderFilter} onChange={(e) => setDoctorOrderFilter(e.currentTarget.value)}>
<option value="">All</option>
{doctorOrderOptions.map((option) => <option key={option} value={option}>{option}</option>)}
</select>
</div>
</div>
<div className="app-main-content-fields-section margin-sm">
<div className="me-4">
<div className="field-label">Fasting Required</div>
<select value={fastingRequiredFilter} onChange={(e) => setFastingRequiredFilter(e.currentTarget.value)}>
<option value="">All</option>
{fastingRequiredOptions.map((option) => <option key={option} value={option}>{option}</option>)}
</select>
</div>
<div className="me-4">
<div className="field-label">ID Needed</div>
<select value={idNeededFilter} onChange={(e) => setIdNeededFilter(e.currentTarget.value)}>
<option value="">All</option>
{idNeededOptions.map((option) => <option key={option} value={option}>{option}</option>)}
</select>
</div>
</div>
<div className="app-main-content-fields-section margin-sm">
<div className="me-4">
<div className="field-label">Need Medication List</div>
<select value={needMedicationListFilter} onChange={(e) => setNeedMedicationListFilter(e.currentTarget.value)}>
<option value="">All</option>
{needMedicationListOptions.map((option) => <option key={option} value={option}>{option}</option>)}
</select>
</div> </div>
</div> </div>
<div className="list row"> <div className="list row">