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

This commit is contained in:
2026-03-17 12:31:02 -04:00
parent c779f9383e
commit 62d9f7d548
5 changed files with 534 additions and 290 deletions

View File

@@ -1,97 +1,26 @@
import React, {useState, useEffect} from "react"; import React from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { AuthService, CustomerService } from "../../services"; import { AuthService } from "../../services";
import { Export } from "../../shared"; import { Export } from "../../shared";
import { Plus } from "react-bootstrap-icons"; import { Plus } from "react-bootstrap-icons";
import DashboardCustomersList from "../dashboard/DashboardCustomersList"; import DashboardCustomersList from "../dashboard/DashboardCustomersList";
const CustomersList = () => { const CustomersList = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [customers, setCustomers] = useState([]);
const CUSTOMER_LIST_COLUMN_TAB_MAP = {
name: 'personalInfo',
chinese_name: 'personalInfo',
email: 'personalInfo',
type: 'careServices',
pickup_status: 'careServices',
birth_date: 'personalInfo',
gender: 'personalInfo',
language: 'personalInfo',
medicare_number: 'medicalInsurance',
medicaid_number: 'medicalInsurance',
address: 'personalInfo',
phone: 'personalInfo',
emergency_contact: 'medicalInsurance',
health_condition: 'medicalInsurance',
payment_status: 'careServices',
payment_due_date: 'careServices',
service_requirement: 'careServices',
tags: 'personalInfo'
};
const getVisibleColumnsByPermission = (columnList = []) => {
return columnList.filter((column) => {
if (column.key === 'name') return true;
const mappedTab = CUSTOMER_LIST_COLUMN_TAB_MAP[column.key];
if (!mappedTab) return true;
return AuthService.canViewCustomerTab(mappedTab);
});
};
const [columns] = useState(getVisibleColumnsByPermission([
{ key: 'name', label:'Name', show: true },
{ key: 'chinese_name', label: 'Preferred Name', show: true },
{ key: 'email', label: 'Email', show: true },
{ key: 'type', label: 'Type', show: true },
{ key: 'pickup_status', label: 'Pickup Status', show: true },
{ key: 'birth_date', label: 'Date of Birth', show: true },
{ key: 'gender', label: 'Gender', show: true },
{ key: 'language', label: 'Language', show: true },
{ key: 'medicare_number', label: 'Medicare Number', show: true },
{ key: 'medicaid_number', label: 'Medicaid Number', show: true },
{ key: 'address', label: 'Address', show: true },
{ key: 'phone', label: 'Phone', show: true },
{ key: 'emergency_contact', 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', show: true }
]));
useEffect(() => {
if (!AuthService.canViewCustomers()) {
window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.')
AuthService.logout();
navigate(`/login`);
return;
}
CustomerService.getAllCustomers().then((data) => {
const customerData = data.data.map((item) => {
item.phone = item?.phone || item?.home_phone || item?.mobile_phone;
item.address = item?.address1 || item?.address2 || item?.address3 || item?.address4 || item?.address5;
return item;
}).sort((a, b) => a.lastname > b.lastname ? 1 : -1);
setCustomers(customerData);
});
}, [navigate]);
const goToCreateNew = () => { const goToCreateNew = () => {
navigate(`/customers`); navigate(`/customers`);
} }
const additionalButtons = ({ showExportDropdown, onExportToggle }) => ( const additionalButtons = ({ showExportDropdown, onExportToggle, exportColumns = [], exportData = [] }) => (
<> <>
{AuthService.canCreateCustomer() && <button className="btn btn-primary me-2" onClick={() => goToCreateNew()}> {AuthService.canCreateCustomer() && <button className="btn btn-primary me-2" onClick={() => goToCreateNew()}>
<Plus size={16}></Plus>Add New Customer <Plus size={16}></Plus>Add New Customer
</button>} </button>}
{AuthService.canExportCustomerReport() && ( {AuthService.canExportCustomerReport() && (
<Export <Export
columns={columns} columns={exportColumns}
data={customers.map((customer) => ({ data={exportData}
...customer,
address: customer?.address1 || customer?.address2 || customer?.address3 || customer?.address4 || customer?.address5,
phone: customer?.phone || customer?.home_phone || customer?.mobile_phone,
tags: customer?.tags?.join(', ')
}))}
filename="customers" filename="customers"
show={showExportDropdown} show={showExportDropdown}
onToggle={onExportToggle} onToggle={onExportToggle}

View File

@@ -3,31 +3,151 @@ import { useDispatch, useSelector } from "react-redux";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { customerSlice } from "./../../store"; import { customerSlice } from "./../../store";
import { AuthService, CustomerService, EventsService } from "../../services"; import { AuthService, CustomerService, EventsService } from "../../services";
import { CUSTOMER_TYPE, CUSTOMER_TYPE_TEXT, PROGRAM_TYPE, PROGRAM_TYPE_TEXT, PAY_SOURCE, PAY_SOURCE_TEXT, YES_NO, YES_NO_TEXT, ManageTable } from "../../shared"; import {
CUSTOMER_TYPE,
CUSTOMER_TYPE_TEXT,
PROGRAM_TYPE,
PROGRAM_TYPE_TEXT,
PAY_SOURCE,
PAY_SOURCE_TEXT,
LEGAL_SEX_TEXT,
MARITAL_STATUS_TEXT,
IMMIGRATION_STATUS_TEXT,
LANGUAGE_OPTIONS,
DAYS_OF_WEEK_OPTIONS,
REFERRAL_SOURCE_TEXT,
CUSTOMER_DISCHARGE_REASON_TEXT,
DIETARY_RESTRICTIONS_OPTIONS,
DIET_TEXTURE_TEXT,
TRANSPORTATION_TYPE_TEXT,
PREFERRED_TEXT_LANGUAGE_TEXT,
YES_NO,
YES_NO_TEXT,
ManageTable
} from "../../shared";
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab, Dropdown, Modal } from "react-bootstrap"; import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab, Dropdown, Modal } 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";
const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, title = null }) => { const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, title = null }) => {
const HIDDEN_CUSTOMER_TYPE_FILTER_VALUES = [CUSTOMER_TYPE.TRANSFERRED, CUSTOMER_TYPE.DECEASED, CUSTOMER_TYPE.DISCHARGED]; const HIDDEN_CUSTOMER_TYPE_FILTER_VALUES = [CUSTOMER_TYPE.TRANSFERRED, CUSTOMER_TYPE.DECEASED, CUSTOMER_TYPE.DISCHARGED];
const DISCHARGE_ONLY_COLUMN_KEYS = new Set(['discharge_date', 'discharge_by', 'discharge_reason']);
const CUSTOMER_COLUMNS = [
{ key: 'customer_name', label: 'Customer Name', show: true },
{ key: 'customer_type', label: 'Customer Type', show: true },
{ key: 'program_type_display', label: 'Program Type', show: true },
{ key: 'pay_source_display', label: 'Pay Source', show: true },
{ key: 'date_of_birth', label: 'Date Of Birth', show: true },
{ key: 'legal_sex_display', label: 'Legal Sex', show: true },
{ key: 'marital_status_display', label: 'Marital Status', show: true },
{ key: 'marriage_date', label: 'Marriage Date', show: true },
{ key: 'immigration_status_display', label: 'Immigration Status', show: true },
{ key: 'language_spoken_display', label: 'Language Spoken', show: true },
{ key: 'phone_number', label: 'Phone Number', show: true },
{ key: 'email', label: 'Email', show: true },
{ key: 'address_display', label: 'Address', show: true },
{ key: 'emergency_contact_display', label: 'Emergency Contact', show: true },
{ key: 'admission_date', label: 'Admission Date', show: true },
{ key: 'days_of_week_display', label: 'Days of Week', show: true },
{ key: 'admission_date_duplicate', label: 'Admission Date', show: true },
{ key: 'enrolled_date', label: 'Enrolled Date', show: true },
{ key: 'created_by', label: 'Created By', show: true },
{ key: 'referral_source_display', label: 'Referral Source', show: true },
{ key: 'discharge_date', label: 'Discharge Date', show: true },
{ key: 'discharge_by', label: 'Discharge By', show: true },
{ key: 'discharge_reason', label: 'Discharge Reason', show: true },
{ key: 'notes_and_attachments', label: 'Notes And Attachments', show: true },
{ key: 'notes_for_driver', label: 'Notes for Driver', show: true },
{ key: 'dietary_restrictions_display', label: 'Dietary Restrictions', show: true },
{ key: 'diet_texture_display', label: 'Diet Texture', show: true },
{ key: 'meal_type', label: 'Meal Type', show: true },
{ key: 'table_number', label: 'Table Number', show: true },
{ key: 'seat_number', label: 'Seat Number', show: true },
{ key: 'transportation_type_display', label: 'Transportation Type', show: true },
{ key: 'consent_to_text_messages_display', label: 'Consent To Text Messages', show: true },
{ key: 'preferred_text_language_display', label: 'Preferred Text Language', show: true },
{ key: 'consent_to_media_use_display', label: 'Consent to Media Use', show: true },
{ key: 'primary_care_physician_display', label: 'Primary Care Physician', show: true },
{ key: 'pharmacy_name', label: 'Pharmacy Name', show: true },
{ key: 'pharmacy_id', label: 'Pharmacy ID', show: true },
{ key: 'diabetes_mellitus_display', label: 'Diabetes Mellitus', show: true },
{ key: 'eyes_on_display', label: 'Eyes-On', show: true },
{ key: 'wheelchair_display', label: 'Wheelchair', show: true },
{ key: 'molst', label: 'MOLST', show: true },
{ key: 'provisions_for_advance_medical', label: 'Provisions for Advance Medical', show: true },
{ key: 'hospice_display', label: 'Hospice', show: true },
{ key: 'burial_arrangements', label: 'Burial Arrangements', show: true },
{ key: 'power_of_attorney', label: 'Power of Attorney', show: true },
{ key: 'requires_rounding_display', label: 'Requires Rounding', show: true },
{ key: 'rounding_notes', label: 'Rounding Notes', show: true },
{ key: 'adcaps_completed_date', label: 'Adcaps Completed Date', show: true },
{ key: 'center_qualification_renew_date', label: 'Center Qualification Renew Date', show: true },
{ key: 'medicaid_renew_date', label: 'Medicaid Renew Date', show: true },
{ key: 'id_expiration_date', label: 'ID Expiration Date', show: true },
{ key: 'medicare_number', label: 'Medicare Number', show: true },
{ key: 'medicaid_number', label: 'Medicaid Number', show: true },
{ key: 'social_security_number', label: 'Social Security Number', show: true },
{ key: 'adcaps_id', label: 'Adcaps ID', show: true }
];
const CUSTOMER_LIST_COLUMN_TAB_MAP = { const CUSTOMER_LIST_COLUMN_TAB_MAP = {
name: 'personalInfo', customer_name: 'personalInfo',
chinese_name: 'personalInfo', customer_type: 'careServices',
program_type_display: 'personalInfo',
pay_source_display: 'personalInfo',
date_of_birth: 'personalInfo',
legal_sex_display: 'personalInfo',
marital_status_display: 'personalInfo',
marriage_date: 'personalInfo',
immigration_status_display: 'personalInfo',
language_spoken_display: 'personalInfo',
phone_number: 'personalInfo',
email: 'personalInfo', email: 'personalInfo',
type: 'careServices', address_display: 'personalInfo',
pickup_status: 'careServices', emergency_contact_display: 'personalInfo',
birth_date: 'personalInfo', admission_date: 'careServices',
gender: 'personalInfo', days_of_week_display: 'careServices',
language: 'personalInfo', admission_date_duplicate: 'careServices',
medicare_number: 'medicalInsurance', enrolled_date: 'careServices',
medicaid_number: 'medicalInsurance', created_by: 'careServices',
address: 'personalInfo', referral_source_display: 'careServices',
phone: 'personalInfo', discharge_date: 'careServices',
emergency_contact: 'medicalInsurance', discharge_by: 'careServices',
tags: 'personalInfo' discharge_reason: 'careServices',
notes_and_attachments: 'careServices',
notes_for_driver: 'careServices',
dietary_restrictions_display: 'careServices',
diet_texture_display: 'careServices',
meal_type: 'careServices',
table_number: 'careServices',
seat_number: 'careServices',
transportation_type_display: 'careServices',
consent_to_text_messages_display: 'careServices',
preferred_text_language_display: 'careServices',
consent_to_media_use_display: 'careServices',
primary_care_physician_display: 'medicalInsurance',
pharmacy_name: 'medicalInsurance',
pharmacy_id: 'medicalInsurance',
diabetes_mellitus_display: 'medicalInsurance',
eyes_on_display: 'medicalInsurance',
wheelchair_display: 'medicalInsurance',
molst: 'medicalInsurance',
provisions_for_advance_medical: 'medicalInsurance',
hospice_display: 'medicalInsurance',
burial_arrangements: 'medicalInsurance',
power_of_attorney: 'medicalInsurance',
requires_rounding_display: 'medicalInsurance',
rounding_notes: 'medicalInsurance',
adcaps_completed_date: 'confidentialDetails',
center_qualification_renew_date: 'confidentialDetails',
medicaid_renew_date: 'confidentialDetails',
id_expiration_date: 'confidentialDetails',
medicare_number: 'confidentialDetails',
medicaid_number: 'confidentialDetails',
social_security_number: 'confidentialDetails',
adcaps_id: 'confidentialDetails'
}; };
const getVisibleColumnsByPermission = (columnList = []) => { const getVisibleColumnsByPermission = (columnList = []) => {
return columnList.filter((column) => { return columnList.filter((column) => {
if (column.key === 'name') return true; if (column.key === 'customer_name') return true;
const mappedTab = CUSTOMER_LIST_COLUMN_TAB_MAP[column.key]; const mappedTab = CUSTOMER_LIST_COLUMN_TAB_MAP[column.key];
if (!mappedTab) return true; if (!mappedTab) return true;
return AuthService.canViewCustomerTab(mappedTab); return AuthService.canViewCustomerTab(mappedTab);
@@ -74,82 +194,115 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
const [customerAvatars, setCustomerAvatars] = useState({}); const [customerAvatars, setCustomerAvatars] = useState({});
const isAllCustomersPage = title === 'All Customers'; const isAllCustomersPage = title === 'All Customers';
const customerTabOrder = ['personalInfo', 'careServices', 'medicalInsurance', 'confidentialDetails', 'formSubmission']; const customerTabOrder = ['personalInfo', 'careServices', 'medicalInsurance', 'confidentialDetails', 'formSubmission'];
const [columns, setColumns] = useState(getVisibleColumnsByPermission([ const [columns, setColumns] = useState(getVisibleColumnsByPermission(CUSTOMER_COLUMNS));
{
key: 'name',
label:'Name',
show: true
},
{
key: 'chinese_name',
label: 'Preferred Name',
show: true
},
{
key: 'email',
label: 'Email',
show: true
},
{
key: 'type',
label: 'Type',
show: true
},
{
key: 'pickup_status',
label: 'Pickup Status',
show: true
},
{
key: 'birth_date',
label: 'Date of Birth',
show: true
},
{
key: 'gender',
label: 'Gender',
show: true
},
{
key: 'language',
label: 'Language',
show: true
},
{
key: 'medicare_number',
label: 'Medicare Number',
show: true
},
{
key: 'medicaid_number',
label: 'Medicaid Number',
show: true
},
{
key: 'address',
label: 'Address',
show: true
},
{
key: 'phone',
label: 'Phone',
show: true
},
{
key: 'emergency_contact',
label: 'Fasting',
show: true
},
{
key: 'tags',
label: 'Tags',
show: true
}
]));
useEffect(() => { useEffect(() => {
setColumns((prevColumns) => getVisibleColumnsByPermission(prevColumns)); setColumns((prevColumns) => getVisibleColumnsByPermission(prevColumns));
}, []); }, []);
const toDisplayText = (value) => {
if (value === undefined || value === null) return '';
const normalized = `${value}`.trim();
if (!normalized) return '';
const lowered = normalized.toLowerCase();
if (lowered === 'yes') return 'Yes';
if (lowered === 'no') return 'No';
return normalized;
};
const getOptionLabel = (options = [], value = '') => {
const normalizedValue = `${value || ''}`.trim().toLowerCase();
const option = options.find((item) => `${item?.value || ''}`.trim().toLowerCase() === normalizedValue);
return option?.label || `${value || ''}`;
};
const formatAddress = (customer) => {
const modernAddress = [
customer?.address_line_1,
customer?.address_line_2,
customer?.city,
customer?.state,
customer?.zip_code
].filter(Boolean).join(', ');
return modernAddress || customer?.address1 || customer?.address2 || customer?.address3 || customer?.address4 || customer?.address5 || '';
};
const formatEmergencyContact = (customer) => {
const modernContact = [customer?.emergency_contact_name, customer?.emergency_contact_phone].filter(Boolean).join(' - ');
return modernContact || customer?.emergency_contact || customer?.emergency_contact2 || '';
};
const formatCustomerRecord = (customer) => {
const paySource = customer?.pay_source || '';
const paySourceLabel = PAY_SOURCE_TEXT[paySource] || paySource;
const paySourceDisplay = paySource === 'other' && customer?.pay_source_other
? `${paySourceLabel} (${customer.pay_source_other})`
: paySourceLabel;
const immigrationStatus = customer?.immigration_status || '';
const immigrationStatusLabel = IMMIGRATION_STATUS_TEXT[immigrationStatus] || immigrationStatus;
const immigrationStatusDisplay = immigrationStatus === 'other' && customer?.immigration_status_other
? `${immigrationStatusLabel} (${customer.immigration_status_other})`
: immigrationStatusLabel;
const referralSource = customer?.referral_source || '';
const referralSourceLabel = REFERRAL_SOURCE_TEXT[referralSource] || referralSource;
const referralSourceDisplay = referralSource === 'other' && customer?.referral_source_other
? `${referralSourceLabel} (${customer.referral_source_other})`
: referralSourceLabel;
const dischargeReason = customer?.discharge_reason || '';
const dischargeReasonLabel = CUSTOMER_DISCHARGE_REASON_TEXT[dischargeReason] || dischargeReason;
const dischargeReasonDisplay = dischargeReason === 'other' && customer?.discharge_reason_other
? `${dischargeReasonLabel} (${customer.discharge_reason_other})`
: dischargeReasonLabel;
return {
...customer,
customer_name: customer?.name || '',
customer_type: CUSTOMER_TYPE_TEXT[customer?.type] || customer?.type || '',
program_type_display: PROGRAM_TYPE_TEXT[customer?.program_type || customer?.program] || customer?.program_type || customer?.program || '',
pay_source_display: paySourceDisplay,
date_of_birth: customer?.birth_date || '',
legal_sex_display: LEGAL_SEX_TEXT[customer?.legal_sex] || customer?.legal_sex || customer?.gender || '',
marital_status_display: MARITAL_STATUS_TEXT[customer?.marital_status] || customer?.marital_status || '',
marriage_date: customer?.marriage_date || '',
immigration_status_display: immigrationStatusDisplay,
language_spoken_display: Array.isArray(customer?.language_spoken) && customer.language_spoken.length > 0
? customer.language_spoken.map((lang) => getOptionLabel(LANGUAGE_OPTIONS, lang)).join(', ')
: (customer?.language || ''),
phone_number: customer?.phone || customer?.home_phone || customer?.mobile_phone || '',
address_display: formatAddress(customer),
emergency_contact_display: formatEmergencyContact(customer),
admission_date: customer?.admission_date || '',
days_of_week_display: Array.isArray(customer?.days_of_week) ? customer.days_of_week.map((day) => getOptionLabel(DAYS_OF_WEEK_OPTIONS, day)).join(', ') : '',
admission_date_duplicate: customer?.admission_date || '',
enrolled_date: customer?.enrolled_date || '',
created_by: customer?.create_by || '',
referral_source_display: referralSourceDisplay,
discharge_reason: dischargeReasonDisplay,
notes_and_attachments: customer?.note || '',
notes_for_driver: customer?.notes_for_driver || '',
dietary_restrictions_display: Array.isArray(customer?.dietary_restrictions)
? customer.dietary_restrictions.map((item) => getOptionLabel(DIETARY_RESTRICTIONS_OPTIONS, item)).join(', ')
: '',
diet_texture_display: DIET_TEXTURE_TEXT[customer?.diet_texture] || customer?.diet_texture || '',
meal_type: customer?.meal_requirement || '',
table_number: customer?.table_id || '',
seat_number: customer?.seat_number || '',
transportation_type_display: TRANSPORTATION_TYPE_TEXT[customer?.transportation_type] || customer?.transportation_type || '',
consent_to_text_messages_display: YES_NO_TEXT[`${customer?.consent_to_text_messages || ''}`.toLowerCase()] || toDisplayText(customer?.consent_to_text_messages || ''),
preferred_text_language_display: PREFERRED_TEXT_LANGUAGE_TEXT[customer?.preferred_text_language] || customer?.preferred_text_language || '',
consent_to_media_use_display: YES_NO_TEXT[`${customer?.consent_to_media_use || ''}`.toLowerCase()] || toDisplayText(customer?.consent_to_media_use || ''),
primary_care_physician_display: customer?.primary_care_physician || customer?.care_provider || '',
pharmacy_name: customer?.pharmacy || '',
diabetes_mellitus_display: toDisplayText(customer?.diabetes_mellitus || ''),
eyes_on_display: YES_NO_TEXT[`${customer?.eyes_on || (customer?.disability ? YES_NO.YES : '')}`.toLowerCase()] || toDisplayText(customer?.eyes_on || (customer?.disability ? YES_NO.YES : '')),
wheelchair_display: toDisplayText(customer?.wheelchair || ''),
hospice_display: toDisplayText(customer?.hospice || ''),
requires_rounding_display: YES_NO_TEXT[`${customer?.requires_rounding || ''}`.toLowerCase()] || toDisplayText(customer?.requires_rounding || '')
};
};
useEffect(() => { useEffect(() => {
if (!AuthService.canViewCustomers()) { if (!AuthService.canViewCustomers()) {
@@ -159,12 +312,11 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
} }
setShowSpinner(true); setShowSpinner(true);
CustomerService.getAllCustomers().then((data) => { CustomerService.getAllCustomers().then((data) => {
setCustomers(data.data.map((item) =>{ setCustomers(
item.phone = item?.phone || item?.home_phone || item?.mobile_phone; data.data
item.address = item?.address1 || item?.address2 || item?.address3 || item?.address4|| item?.address5; .map((item) => formatCustomerRecord(item))
.sort((a, b) => a.lastname > b.lastname ? 1 : -1)
return item; );
}).sort((a, b) => a.lastname > b.lastname ? 1: -1));
}).finally(() => { }).finally(() => {
setShowSpinner(false); setShowSpinner(false);
}); });
@@ -214,7 +366,7 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
} }
if (programTypeFilter && visibleProgramTypeFilter) { if (programTypeFilter && visibleProgramTypeFilter) {
filtered = filtered.filter((item) => (item?.program || '') === programTypeFilter); filtered = filtered.filter((item) => (item?.program_type || item?.program || '') === programTypeFilter);
} }
if (paySourceFilter && visiblePaySourceFilter) { if (paySourceFilter && visiblePaySourceFilter) {
@@ -351,6 +503,10 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
setAvatarCustomerName(''); setAvatarCustomerName('');
} }
const visibleTableColumns = columns.filter((column) => (
column.show && (showInactive || !DISCHARGE_ONLY_COLUMN_KEYS.has(column.key))
));
const table = <div className="list row mb-4"> const table = <div className="list row mb-4">
<div <div
className="col-md-12" className="col-md-12"
@@ -365,7 +521,7 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
<tr> <tr>
<th className="th-index">No.</th> <th className="th-index">No.</th>
{ {
columns.filter(col => col.show).map((column, index) => <th className="sortable-header" key={index}> visibleTableColumns.map((column, index) => <th className="sortable-header" key={index}>
{column.label} <span className="float-right" onClick={() => sortTableWithField(column.key)}><img src={`/images/${getSortingImg(column.key)}.png`}></img></span> {column.label} <span className="float-right" onClick={() => sortTableWithField(column.key)}><img src={`/images/${getSortingImg(column.key)}.png`}></img></span>
</th>) </th>)
} }
@@ -375,49 +531,40 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
{ {
filteredCustomers.map((customer, index) => <tr key={customer.id}> filteredCustomers.map((customer, index) => <tr key={customer.id}>
<td className="td-index">{index + 1}</td> <td className="td-index">{index + 1}</td>
{columns.find(col => col.key === 'name')?.show && <td style={{ verticalAlign: 'middle' }}> {visibleTableColumns.map((column) => (
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}> column.key === 'customer_name' ? (
{AuthService.canViewCustomers() && ( <td key={column.key} style={{ verticalAlign: 'middle' }}>
customerAvatars[customer.id] && customerAvatars[customer.id] !== false ? ( <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<img {AuthService.canViewCustomers() && (
src={`data:image/png;base64, ${customerAvatars[customer.id]}`} customerAvatars[customer.id] && customerAvatars[customer.id] !== false ? (
alt={customer?.name} <img
onClick={() => showProfilePicture(customer)} src={`data:image/png;base64, ${customerAvatars[customer.id]}`}
style={{ alt={customer?.name}
width: '64px', onClick={() => showProfilePicture(customer)}
height: '64px', style={{
borderRadius: '8px', width: '64px',
objectFit: 'cover', height: '64px',
cursor: 'pointer', borderRadius: '8px',
flexShrink: 0 objectFit: 'cover',
}} cursor: 'pointer',
/> flexShrink: 0
) : ( }}
<PersonSquare />
onClick={() => showProfilePicture(customer)} ) : (
size={64} <PersonSquare
className="clickable" onClick={() => showProfilePicture(customer)}
style={{ flexShrink: 0, color: '#ccc' }} size={64}
/> className="clickable"
) style={{ flexShrink: 0, color: '#ccc' }}
)} />
{AuthService.canEditAnyCustomerTab() && <PencilSquare size={16} className="clickable" onClick={() => goToEdit(customer?.id)} style={{ flexShrink: 0 }}></PencilSquare>} )
<span className="clickable" style={{ color: '#0066B1', textDecoration: 'underline', cursor: 'pointer' }} onClick={() => goToView(customer?.id)}>{customer?.name}</span> )}
</div> {AuthService.canEditAnyCustomerTab() && <PencilSquare size={16} className="clickable" onClick={() => goToEdit(customer?.id)} style={{ flexShrink: 0 }}></PencilSquare>}
</td>} <span className="clickable" style={{ color: '#0066B1', textDecoration: 'underline', cursor: 'pointer' }} onClick={() => goToView(customer?.id)}>{customer?.customer_name}</span>
{columns.find(col => col.key === 'chinese_name')?.show && <td>{customer?.name_cn}</td>} </div>
{columns.find(col => col.key === 'email')?.show && <td>{customer?.email}</td>} </td>
{columns.find(col => col.key === 'type')?.show && <td>{customer?.type}</td>} ) : <td key={column.key}>{customer?.[column.key] || ''}</td>
{columns.find(col => col.key === 'pickup_status')?.show && <td>{customer?.pickup_status}</td>} ))}
{columns.find(col => col.key === 'birth_date')?.show && <td>{customer?.birth_date}</td>}
{columns.find(col => col.key === 'gender')?.show && <td>{customer?.gender}</td>}
{columns.find(col => col.key === 'language')?.show && <td>{customer?.language}</td>}
{columns.find(col => col.key === 'medicare_number')?.show && <td>{customer?.medicare_number}</td>}
{columns.find(col => col.key === 'medicaid_number')?.show && <td>{customer?.medicaid_number}</td>}
{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 === 'tags')?.show && <td>{customer?.tags?.join(', ')}</td>}
</tr>) </tr>)
} }
</tbody> </tbody>
@@ -540,7 +687,7 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
<Dropdown.Menu as={customFilterMenu}/> <Dropdown.Menu as={customFilterMenu}/>
</Dropdown>} </Dropdown>}
<ManageTable <ManageTable
columns={isAllCustomersPage ? columns.filter((column) => column.key !== 'pickup_status') : columns} columns={showInactive ? columns : columns.filter((column) => !DISCHARGE_ONLY_COLUMN_KEYS.has(column.key))}
onColumnsChange={handleColumnsChange} onColumnsChange={handleColumnsChange}
show={showManageTableDropdown} show={showManageTableDropdown}
onToggle={(isOpen) => { onToggle={(isOpen) => {
@@ -554,6 +701,14 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
{typeof additionalButtons === 'function' {typeof additionalButtons === 'function'
? additionalButtons({ ? additionalButtons({
showExportDropdown, showExportDropdown,
exportColumns: visibleTableColumns,
exportData: filteredCustomers.map((customer) => {
const exportRecord = {};
visibleTableColumns.forEach((column) => {
exportRecord[column.key] = customer?.[column.key] || '';
});
return exportRecord;
}),
onExportToggle: (isOpen) => { onExportToggle: (isOpen) => {
if (isOpen) { if (isOpen) {
setShowFilterDropdown(false); setShowFilterDropdown(false);

View File

@@ -21,11 +21,6 @@ const EventRequestList = () => {
label:'Customer Name', label:'Customer Name',
show: true show: true
}, },
{
key: 'resource_display',
label: 'Doctor',
show: true
},
{ {
key: 'source', key: 'source',
label: 'Request By', label: 'Request By',
@@ -101,10 +96,26 @@ const EventRequestList = () => {
}, [sorting]); }, [sorting]);
const filterRequestsFun = (item, statusParam, keywordParam) => { const filterRequestsFun = (item, statusParam, keywordParam) => {
const normalizedKeyword = `${keywordParam || ''}`.toLowerCase().trim();
return (item?.customer_display?.toLowerCase()?.includes(keywordParam?.toLowerCase()) || item?.source?.toLowerCase()?.includes(keywordParam?.toLowerCase()) || const statusLabel = getStatusLabel(item?.status || '');
item?.resource_display?.toLowerCase()?.includes(keywordParam?.toLowerCase())) && item?.status === statusParam const completeBy = item?.edit_history?.[item?.edit_history?.length - 1]?.employee || '';
if (!normalizedKeyword) {
return item?.status === statusParam;
}
return (
item?.status === statusParam &&
(
item?.customer_display?.toLowerCase()?.includes(normalizedKeyword) ||
item?.source?.toLowerCase()?.includes(normalizedKeyword) ||
item?.type?.toLowerCase()?.includes(normalizedKeyword) ||
item?.transportation?.toLowerCase()?.includes(normalizedKeyword) ||
item?.symptom?.toLowerCase()?.includes(normalizedKeyword) ||
item?.np?.toLowerCase()?.includes(normalizedKeyword) ||
item?.upload?.toLowerCase()?.includes(normalizedKeyword) ||
statusLabel.toLowerCase().includes(normalizedKeyword) ||
completeBy.toLowerCase().includes(normalizedKeyword)
)
);
} }
const redirectToAdmin = () => { const redirectToAdmin = () => {
@@ -254,6 +265,16 @@ const EventRequestList = () => {
} }
const getStatusLabel = (status) => status === 'active' ? 'In Progress' : (status==='done' ? 'Done' : 'Deleted') const getStatusLabel = (status) => status === 'active' ? 'In Progress' : (status==='done' ? 'Done' : 'Deleted')
const activeTabStatus = showDone ? 'done' : 'active';
const filteredEventRequests = eventRequests.filter((item) => filterRequestsFun(item, activeTabStatus, keyword));
const exportColumns = showDone ? [...columns, { key: 'complete_by', label: 'Complete By', show: true }] : columns;
const exportData = filteredEventRequests.map((eventRequest) => ({
...eventRequest,
create_date: eventRequest?.create_date ? new Date(eventRequest.create_date).toLocaleDateString() : '',
status: getStatusLabel(eventRequest?.status),
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 || ''
}));
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">
@@ -267,7 +288,7 @@ const EventRequestList = () => {
{column.label} <span className="float-right" onClick={() => sortTableWithField(column.key)}><img src={`/images/${getSortingImg(column.key)}.png`}></img></span> {column.label} <span className="float-right" onClick={() => sortTableWithField(column.key)}><img src={`/images/${getSortingImg(column.key)}.png`}></img></span>
</th>) </th>)
} }
<th>Completed By</th> {statusParam === 'done' && <th>Complete By</th>}
<th>Comments</th> <th>Comments</th>
<th>Comment on the record</th> <th>Comment on the record</th>
<th></th> <th></th>
@@ -280,7 +301,6 @@ const EventRequestList = () => {
<td className="td-checkbox"><input type="checkbox" checked={selectedItems.includes(eventRequest?.id)} onClick={()=>toggleItem(eventRequest?.id)}/></td> <td className="td-checkbox"><input type="checkbox" checked={selectedItems.includes(eventRequest?.id)} onClick={()=>toggleItem(eventRequest?.id)}/></td>
<td className="td-index">{index + 1}</td> <td className="td-index">{index + 1}</td>
{columns.find(col => col.key === 'customer_display')?.show && <td>{eventRequest?.customer_display}</td>} {columns.find(col => col.key === 'customer_display')?.show && <td>{eventRequest?.customer_display}</td>}
{columns.find(col => col.key === 'resource_display')?.show && <td>{eventRequest?.resource_display}</td>}
{columns.find(col => col.key === 'source')?.show && <td>{EventRequestsService.sourceList.find((item) => item?.value === eventRequest?.source)?.label || eventRequest?.source}</td>} {columns.find(col => col.key === 'source')?.show && <td>{EventRequestsService.sourceList.find((item) => item?.value === eventRequest?.source)?.label || eventRequest?.source}</td>}
{columns.find(col => col.key === 'type')?.show && <td>{eventRequest?.type}</td>} {columns.find(col => col.key === 'type')?.show && <td>{eventRequest?.type}</td>}
{columns.find(col => col.key === 'transportation')?.show && <td>{eventRequest?.transportation || '-'}</td>} {columns.find(col => col.key === 'transportation')?.show && <td>{eventRequest?.transportation || '-'}</td>}
@@ -289,7 +309,7 @@ const EventRequestList = () => {
{columns.find(col => col.key === 'upload')?.show && <td>{eventRequest?.upload}</td>} {columns.find(col => col.key === 'upload')?.show && <td>{eventRequest?.upload}</td>}
{columns.find(col => col.key === 'status')?.show && <td>{getStatusLabel(eventRequest?.status)}</td>} {columns.find(col => col.key === 'status')?.show && <td>{getStatusLabel(eventRequest?.status)}</td>}
{columns.find(col => col.key === 'create_date')?.show && <td>{new Date(eventRequest?.create_date).toLocaleDateString()}</td>} {columns.find(col => col.key === 'create_date')?.show && <td>{new Date(eventRequest?.create_date).toLocaleDateString()}</td>}
<td>{ eventRequest.status === 'done' && eventRequest?.edit_history && eventRequest?.edit_history[eventRequest?.edit_history?.length - 1]?.employee || ''}</td> {statusParam === 'done' && <td>{eventRequest?.edit_history && eventRequest?.edit_history[eventRequest?.edit_history?.length - 1]?.employee || ''}</td>}
<td>{eventRequest?.notes?.map((note) => { <td>{eventRequest?.notes?.map((note) => {
return <p>{`${note?.author}: ${note?.content}`}</p> return <p>{`${note?.author}: ${note?.content}`}</p>
})}</td> })}</td>
@@ -340,8 +360,8 @@ const EventRequestList = () => {
<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
columns={columns} columns={exportColumns}
data={eventRequests.filter((item) => filterRequestsFun(item, showDone ? 'done': 'active', keyword))} data={exportData}
filename="event-requests" filename="event-requests"
/> />
</div> </div>

View File

@@ -485,6 +485,14 @@ const EventsList = () => {
</div> </div>
</div>; </div>;
const exportColumns = columns.filter((column) => column.show);
const exportData = events
.filter(event => event.status === (showDeletedItems ? 'inactive' : 'active'))
.map((event) => ({
...event,
customer: `${event?.customer || ''}${event?.chinese_name ? ` (${event.chinese_name})` : ''}`
}));
return ( return (
<> <>
@@ -531,8 +539,8 @@ const EventsList = () => {
{AuthService.canEditMedicalEvents() && <button className="btn btn-primary me-2" onClick={() => goToCreateNew()}><Plus size={16}></Plus>Add New Medical Appointment</button>} {AuthService.canEditMedicalEvents() && <button className="btn btn-primary me-2" onClick={() => goToCreateNew()}><Plus size={16}></Plus>Add New Medical Appointment</button>}
{AuthService.canEditDriverAssignment() && ( {AuthService.canEditDriverAssignment() && (
<Export <Export
columns={columns} columns={exportColumns}
data={events.filter(event => event.status === (showDeletedItems ? 'inactive' : 'active'))} data={exportData}
filename="events" filename="events"
/> />
)} )}

View File

@@ -6,6 +6,7 @@ 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";
import { CUSTOMER_TYPE_TEXT, PROGRAM_TYPE_TEXT, PAY_SOURCE_TEXT } from "../../shared/constants/customer.constant";
const EventsMultipleList = () => { const EventsMultipleList = () => {
@@ -24,72 +25,138 @@ const EventsMultipleList = () => {
const [showFilterDropdown, setShowFilterDropdown] = useState(false); const [showFilterDropdown, setShowFilterDropdown] = useState(false);
const [columns, setColumns] = useState([ const [columns, setColumns] = useState([
{ {
key: 'customer', key: 'customer_name',
label:'Customer', label:'Customer Name (Full Name + Preferred Name)',
show: true show: true
}, },
{ {
key: 'chinese_name', key: 'customer_type',
label: 'Preferred Name', label: 'Customer Type',
show: true show: true
}, },
{ {
key: 'member_type', key: 'program_type',
label: 'Member Type', label: 'Program Type',
show: true show: true
}, },
{ {
key: 'eyes_on', key: 'pay_source',
label: 'Eyes-on', label: 'Pay Source',
show: true show: true
}, },
{ {
key: 'doctor', key: 'dob',
label: 'Doctor', label: 'Date Of Birth',
show: true show: true
}, },
{ {
key: 'phone', key: 'language_support',
label: 'Phone', label: 'Language Support',
show: true show: true
}, },
{ {
key: 'address', key: 'transportation_support',
label: 'Address', label: 'Transportation Support',
show: true show: true
}, },
{ {
key: 'translation', key: 'driver',
label: 'Translation',
show: true
},
{
key: 'newPatient',
label: 'New Patient',
show: true
},
{
key: 'needId',
label: 'Need ID',
show: true
},
{
key: 'startTime',
label: 'Start Time',
show: true
},
{
key: 'transportation',
label: 'Driver', label: 'Driver',
show: true show: true
}, },
{ {
key: 'fasting', key: 'appointment_time',
label: 'Fasting', label: 'Appointment Time',
show: true
},
{
key: 'eyes_on',
label: 'Eyes-On',
show: true
},
{
key: 'new_patient',
label: 'New Patient',
show: true
},
{
key: 'doctor_order',
label: "Doctor's Order",
show: true
},
{
key: 'fasting_required',
label: 'Fasting Required',
show: true
},
{
key: 'id_needed',
label: 'ID Needed',
show: true
},
{
key: 'need_medication_list',
label: 'Need Medication List',
show: true
},
{
key: 'provider',
label: 'Provider',
show: true
},
{
key: 'provider_phone_number',
label: 'Provider Phone Number',
show: true
},
{
key: 'provider_address',
label: 'Provider Address',
show: true
},
{
key: 'notes',
label: 'Notes',
show: true
},
{
key: 'reason',
label: 'Reason',
show: true
},
{
key: 'requirement',
label: 'Requirement',
show: true show: true
} }
]); ]);
const toDisplayText = (value) => {
if (value === undefined || value === null) return '';
const normalized = `${value}`.trim();
if (!normalized) return '';
const lowered = normalized.toLowerCase();
if (lowered === 'yes') return 'Yes';
if (lowered === 'no') return 'No';
return normalized;
};
const getOptionLabel = (options = [], value = '') => {
const normalizedValue = `${value || ''}`.trim().toLowerCase();
const option = options.find((item) => `${item?.value || ''}`.trim().toLowerCase() === normalizedValue);
return option?.label || `${value || ''}`;
};
const getCustomerPaySource = (customer) => {
if (!customer) return '';
const paySource = customer?.pay_source || '';
const paySourceLabel = PAY_SOURCE_TEXT[paySource] || paySource;
if (paySource === 'other' && customer?.pay_source_other) {
return `${paySourceLabel} (${customer.pay_source_other})`;
}
return paySourceLabel;
};
const checkDisability = (customers, event) => { const checkDisability = (customers, event) => {
const currentCustomer = customers?.find(c => c?.id === event?.data?.customer || c?.name === event?.data?.client_name || c?.name === event?.target_name); const currentCustomer = customers?.find(c => c?.id === event?.data?.customer || c?.name === event?.data?.client_name || c?.name === event?.target_name);
return currentCustomer?.disability || event?.data?.disability?.toLowerCase() === 'yes' || false; return currentCustomer?.disability || event?.data?.disability?.toLowerCase() === 'yes' || false;
@@ -113,6 +180,31 @@ const EventsMultipleList = () => {
if (fromDate && toDate && customers?.length > 0 && resources?.length > 0) { if (fromDate && toDate && customers?.length > 0 && resources?.length > 0) {
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then((data) => { EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then((data) => {
setEvents(data.data.filter((item) => { setEvents(data.data.filter((item) => {
const currentCustomer = customers.find(c => c.id === item?.data?.customer) || customers?.find(c => c?.name === item?.data?.client_name || c?.name === item?.target_name);
const currentResource = resources.find(r => r.id === item?.data?.resource);
const customerName = item?.data?.customer ? (currentCustomer?.name || item?.data?.client_name || '') : (item?.data?.client_name || '');
const preferredName = item?.data?.customer ? (currentCustomer?.name_cn || '') : (currentCustomer?.name_cn || '');
item.customer_name = preferredName ? `${customerName} (${preferredName})` : customerName;
item.customer_type = CUSTOMER_TYPE_TEXT[currentCustomer?.type] || currentCustomer?.type || '';
item.program_type = PROGRAM_TYPE_TEXT[currentCustomer?.program_type] || currentCustomer?.program_type || '';
item.pay_source = getCustomerPaySource(currentCustomer);
item.dob = item?.data?.customer ? (currentCustomer?.birth_date || '') : (item?.data?.client_birth_date || '');
item.language_support = getOptionLabel(EventsService.interpreterLevelOptions, item?.data?.interpreter || '');
item.transportation_support = getOptionLabel(EventsService.transportationTypeOptions, item?.data?.trans_method || '');
item.driver = item?.link_event_name || '';
item.appointment_time = item?.start_time? `${EventsService.formatDate(new Date(item?.start_time))} ${new Date(item?.start_time).toLocaleTimeString('en-US', { hour: '2-digit', minute: 'numeric', hour12: true })}` : '' ;
item.eyes_on = checkDisability(customers, item) ? 'Yes' : 'No';
item.new_patient = toDisplayText(item?.data?.new_patient || '');
item.doctor_order = toDisplayText(item?.data?.doc_order || '');
item.fasting_required = item?.data?.fasting || '';
item.id_needed = toDisplayText(item?.data?.need_id || '');
item.need_medication_list = toDisplayText(item?.data?.need_med_list || '');
item.provider = item?.data?.resource ? (currentResource?.name || item?.data?.resource_name || '') : (item?.data?.resource_name || '');
item.provider_phone_number = item?.data?.resource ? (currentResource?.phone || item?.data?.resource_phone || '') : (item?.data?.resource_phone || '');
item.provider_address = item?.data?.resource ? (currentResource?.address || item?.data?.resource_address || '') : (item?.data?.resource_address || '');
item.notes = item?.data?.notes || '';
item.reason = item?.data?.reason || '';
item.requirement = item?.data?.other || '';
item.customer = (item?.data?.customer ? (customers.find(c=>c.id === item?.data?.customer)?.name || item?.data?.client_name || '') : (item?.data?.client_name || '')); item.customer = (item?.data?.customer ? (customers.find(c=>c.id === item?.data?.customer)?.name || item?.data?.client_name || '') : (item?.data?.client_name || ''));
item.doctor = item?.data?.resource ? (resources.find(r=> r.id === item?.data?.resource)?.name || item?.data?.resource_name || '') : (item?.data?.resource_name || ''); item.doctor = item?.data?.resource ? (resources.find(r=> r.id === item?.data?.resource)?.name || item?.data?.resource_name || '') : (item?.data?.resource_name || '');
item.phone = item?.data?.resource ? (resources.find(r=> r.id === item?.data?.resource)?.phone || item?.data?.resource_phone || '') : (item?.data?.resource_phone || ''); item.phone = item?.data?.resource ? (resources.find(r=> r.id === item?.data?.resource)?.phone || item?.data?.resource_phone || '') : (item?.data?.resource_phone || '');
@@ -139,8 +231,8 @@ const EventsMultipleList = () => {
useEffect(() => { useEffect(() => {
setFilteredEvents(events?.filter((event) => { setFilteredEvents(events?.filter((event) => {
if (selectedResource && selectedResource.label !== '' && selectedResource.value !== '') { if (selectedResource && selectedResource.label !== '' && selectedResource.value !== '') {
if (selectedResource?.label?.replaceAll(' ', '').replaceAll('', '').replaceAll('*', '').includes(event.doctor?.replaceAll(' ', '').replaceAll('', '').replaceAll('*', '')) if (selectedResource?.label?.replaceAll(' ', '').replaceAll('', '').replaceAll('*', '').includes(event.provider?.replaceAll(' ', '').replaceAll('', '').replaceAll('*', ''))
|| event.doctor?.replaceAll(' ', '').replaceAll('', '').replaceAll('*', '').includes(selectedResource?.label?.replaceAll(' ', '').replaceAll('', '').replaceAll('*', ''))) { || event.provider?.replaceAll(' ', '').replaceAll('', '').replaceAll('*', '').includes(selectedResource?.label?.replaceAll(' ', '').replaceAll('', '').replaceAll('*', ''))) {
return event; return event;
} }
} else { } else {
@@ -148,7 +240,7 @@ const EventsMultipleList = () => {
} }
}).filter((event => { }).filter((event => {
if (selectedCustomer && selectedCustomer !== '' && selectedCustomer.value !== '') { if (selectedCustomer && selectedCustomer !== '' && selectedCustomer.value !== '') {
if (selectedCustomer?.label?.replaceAll(' ', '').replaceAll('(Eyes-on)', '').replaceAll('(discharged)', '').replaceAll('', '').replaceAll('*', '') === (event.customer?.replaceAll(' ', '').replaceAll('(discharged)', '').replaceAll('(Eyes-on)', '').replaceAll('', '').replaceAll('*', ''))) { if ((event.customer_name || '').toLowerCase().includes((selectedCustomer?.label || '').toLowerCase())) {
return event; return event;
} }
} else { } else {
@@ -174,6 +266,31 @@ const EventsMultipleList = () => {
EventsService.updateEvent(id, {confirmed: true}).then(() => { EventsService.updateEvent(id, {confirmed: true}).then(() => {
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then((data) => { EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then((data) => {
setEvents(data.data.filter((item) => { setEvents(data.data.filter((item) => {
const currentCustomer = customers.find(c => c.id === item?.data?.customer) || customers?.find(c => c?.name === item?.data?.client_name || c?.name === item?.target_name);
const currentResource = resources.find(r => r.id === item?.data?.resource);
const customerName = item?.data?.customer ? (currentCustomer?.name || item?.data?.client_name || '') : (item?.data?.client_name || '');
const preferredName = item?.data?.customer ? (currentCustomer?.name_cn || '') : (currentCustomer?.name_cn || '');
item.customer_name = preferredName ? `${customerName} (${preferredName})` : customerName;
item.customer_type = CUSTOMER_TYPE_TEXT[currentCustomer?.type] || currentCustomer?.type || '';
item.program_type = PROGRAM_TYPE_TEXT[currentCustomer?.program_type] || currentCustomer?.program_type || '';
item.pay_source = getCustomerPaySource(currentCustomer);
item.dob = item?.data?.customer ? (currentCustomer?.birth_date || '') : (item?.data?.client_birth_date || '');
item.language_support = getOptionLabel(EventsService.interpreterLevelOptions, item?.data?.interpreter || '');
item.transportation_support = getOptionLabel(EventsService.transportationTypeOptions, item?.data?.trans_method || '');
item.driver = item?.link_event_name || '';
item.appointment_time = item?.start_time? `${EventsService.formatDate(new Date(item?.start_time))} ${new Date(item?.start_time).toLocaleTimeString('en-US', { hour: '2-digit', minute: 'numeric', hour12: true })}` : '' ;
item.eyes_on = checkDisability(customers, item) ? 'Yes' : 'No';
item.new_patient = toDisplayText(item?.data?.new_patient || '');
item.doctor_order = toDisplayText(item?.data?.doc_order || '');
item.fasting_required = item?.data?.fasting || '';
item.id_needed = toDisplayText(item?.data?.need_id || '');
item.need_medication_list = toDisplayText(item?.data?.need_med_list || '');
item.provider = item?.data?.resource ? (currentResource?.name || item?.data?.resource_name || '') : (item?.data?.resource_name || '');
item.provider_phone_number = item?.data?.resource ? (currentResource?.phone || item?.data?.resource_phone || '') : (item?.data?.resource_phone || '');
item.provider_address = item?.data?.resource ? (currentResource?.address || item?.data?.resource_address || '') : (item?.data?.resource_address || '');
item.notes = item?.data?.notes || '';
item.reason = item?.data?.reason || '';
item.requirement = item?.data?.other || '';
item.customer = (item?.data?.customer ? (customers.find(c=>c.id === item?.data?.customer)?.name || item?.data?.client_name || '') : (item?.data?.client_name || '')); item.customer = (item?.data?.customer ? (customers.find(c=>c.id === item?.data?.customer)?.name || item?.data?.client_name || '') : (item?.data?.client_name || ''));
item.doctor = item?.data?.resource ? (resources.find(r=> r.id === item?.data?.resource)?.name || item?.data?.resource_name || '') : (item?.data?.resource_name || ''); item.doctor = item?.data?.resource ? (resources.find(r=> r.id === item?.data?.resource)?.name || item?.data?.resource_name || '') : (item?.data?.resource_name || '');
item.phone = item?.data?.resource ? (resources.find(r=> r.id === item?.data?.resource)?.phone || item?.data?.resource_phone || '') : (item?.data?.resource_phone || ''); item.phone = item?.data?.resource ? (resources.find(r=> r.id === item?.data?.resource)?.phone || item?.data?.resource_phone || '') : (item?.data?.resource_phone || '');
@@ -314,8 +431,6 @@ const EventsMultipleList = () => {
{column.label} <span className="float-right" onClick={() => sortTableWithField(column.key)}><img src={`/images/${getSortingImg(column.key)}.png`}></img></span> {column.label} <span className="float-right" onClick={() => sortTableWithField(column.key)}><img src={`/images/${getSortingImg(column.key)}.png`}></img></span>
</th>) </th>)
} }
<th>Customer Date of Birth</th>
<th>Transportation</th>
<th></th> <th></th>
</tr> </tr>
@@ -325,21 +440,27 @@ const EventsMultipleList = () => {
filteredEvents && filteredEvents.filter(event => event.status === statusParam).map((medicalEvent, index) => <tr key={medicalEvent.id}> filteredEvents && filteredEvents.filter(event => event.status === statusParam).map((medicalEvent, index) => <tr key={medicalEvent.id}>
<td className="td-checkbox"><input type="checkbox" checked={selectedItems.includes(medicalEvent.id)} onClick={()=>toggleItem(medicalEvent?.id)}/></td> <td className="td-checkbox"><input type="checkbox" checked={selectedItems.includes(medicalEvent.id)} onClick={()=>toggleItem(medicalEvent?.id)}/></td>
<td className="td-index">{index + 1}</td> <td className="td-index">{index + 1}</td>
{columns.find(col => col.key === 'customer')?.show && <td>{medicalEvent?.customer}</td>} {columns.find(col => col.key === 'customer_name')?.show && <td>{medicalEvent?.customer_name}</td>}
{columns.find(col => col.key === 'chinese_name')?.show && <td>{medicalEvent?.chinese_name}</td>} {columns.find(col => col.key === 'customer_type')?.show && <td>{medicalEvent?.customer_type}</td>}
{columns.find(col => col.key === 'member_type')?.show && <td>{medicalEvent?.member_type}</td>} {columns.find(col => col.key === 'program_type')?.show && <td>{medicalEvent?.program_type}</td>}
{columns.find(col => col.key === 'pay_source')?.show && <td>{medicalEvent?.pay_source}</td>}
{columns.find(col => col.key === 'dob')?.show && <td>{medicalEvent?.dob}</td>}
{columns.find(col => col.key === 'language_support')?.show && <td>{medicalEvent?.language_support}</td>}
{columns.find(col => col.key === 'transportation_support')?.show && <td>{medicalEvent?.transportation_support}</td>}
{columns.find(col => col.key === 'driver')?.show && <td>{medicalEvent?.driver}</td>}
{columns.find(col => col.key === 'appointment_time')?.show && <td>{medicalEvent?.appointment_time}</td>}
{columns.find(col => col.key === 'eyes_on')?.show && <td>{medicalEvent?.eyes_on}</td>} {columns.find(col => col.key === 'eyes_on')?.show && <td>{medicalEvent?.eyes_on}</td>}
{columns.find(col => col.key === 'doctor')?.show && <td>{medicalEvent?.doctor}</td>} {columns.find(col => col.key === 'new_patient')?.show && <td>{medicalEvent?.new_patient}</td>}
{columns.find(col => col.key === 'phone')?.show && <td>{medicalEvent?.phone}</td>} {columns.find(col => col.key === 'doctor_order')?.show && <td>{medicalEvent?.doctor_order}</td>}
{columns.find(col => col.key === 'address')?.show && <td>{medicalEvent?.address}</td>} {columns.find(col => col.key === 'fasting_required')?.show && <td>{medicalEvent?.fasting_required}</td>}
{columns.find(col => col.key === 'translation')?.show && <td>{medicalEvent?.translation}</td>} {columns.find(col => col.key === 'id_needed')?.show && <td>{medicalEvent?.id_needed}</td>}
{columns.find(col => col.key === 'newPatient')?.show && <td>{medicalEvent?.newPatient}</td>} {columns.find(col => col.key === 'need_medication_list')?.show && <td>{medicalEvent?.need_medication_list}</td>}
{columns.find(col => col.key === 'needId')?.show && <td>{medicalEvent?.needId}</td>} {columns.find(col => col.key === 'provider')?.show && <td>{medicalEvent?.provider}</td>}
{columns.find(col => col.key === 'startTime')?.show && <td>{medicalEvent?.startTime}</td>} {columns.find(col => col.key === 'provider_phone_number')?.show && <td>{medicalEvent?.provider_phone_number}</td>}
{columns.find(col => col.key === 'transportation')?.show && <td>{medicalEvent?.transportation}</td>} {columns.find(col => col.key === 'provider_address')?.show && <td>{medicalEvent?.provider_address}</td>}
{columns.find(col => col.key === 'fasting')?.show && <td>{medicalEvent?.fasting}</td>} {columns.find(col => col.key === 'notes')?.show && <td>{medicalEvent?.notes}</td>}
<td>{medicalEvent?.dob}</td> {columns.find(col => col.key === 'reason')?.show && <td>{medicalEvent?.reason}</td>}
<td>{medicalEvent?.transMethod}</td> {columns.find(col => col.key === 'requirement')?.show && <td>{medicalEvent?.requirement}</td>}
<td>{medicalEvent?.confirmed ? 'Confirmed' : (AuthService.canEditMedicalEvents() ? <button className="btn btn-primary btn-sm me-2 ms-2" onClick={() => confirmEvent(medicalEvent?.id)}>Confirm</button> : 'Pending')}</td> <td>{medicalEvent?.confirmed ? 'Confirmed' : (AuthService.canEditMedicalEvents() ? <button className="btn btn-primary btn-sm me-2 ms-2" onClick={() => confirmEvent(medicalEvent?.id)}>Confirm</button> : 'Pending')}</td>
</tr>) </tr>)
} }
@@ -358,6 +479,17 @@ const EventsMultipleList = () => {
setShowFilterDropdown(false); setShowFilterDropdown(false);
} }
const exportColumns = columns.filter((column) => column.show);
const exportData = filteredEvents
.filter(event => event.status === (showDeletedItems ? 'inactive' : 'active'))
.map((event) => {
const row = {};
exportColumns.forEach((column) => {
row[column.key] = event?.[column.key] || '';
});
return row;
});
const customMenu = React.forwardRef( const customMenu = React.forwardRef(
({ children, style, className, 'aria-labelledby': labeledBy }, ref) => { ({ children, style, className, 'aria-labelledby': labeledBy }, ref) => {
@@ -415,7 +547,7 @@ const EventsMultipleList = () => {
</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">Resource</div> <div className="field-label">Provider</div>
<Select styles={{ <Select styles={{
control: (baseStyles, state) => ({ control: (baseStyles, state) => ({
...baseStyles, ...baseStyles,
@@ -534,8 +666,8 @@ const EventsMultipleList = () => {
<ManageTable columns={columns} onColumnsChange={setColumns} /> <ManageTable columns={columns} onColumnsChange={setColumns} />
{AuthService.canEditMedicalEvents() && <button className="btn btn-primary me-2" onClick={() => goToCreateNew()}><Plus size={16}></Plus>Add New Medical Appointment</button>} {AuthService.canEditMedicalEvents() && <button className="btn btn-primary me-2" onClick={() => goToCreateNew()}><Plus size={16}></Plus>Add New Medical Appointment</button>}
<Export <Export
columns={columns} columns={exportColumns}
data={filteredEvents.filter(event => event.status === (showDeletedItems ? 'inactive' : 'active'))} data={exportData}
filename="events-multiple" filename="events-multiple"
customActions={[ customActions={[
{ label: 'Medical Notifications Doc', onClick: generateMedicalNotificationDocs }, { label: 'Medical Notifications Doc', onClick: generateMedicalNotificationDocs },