Files
worldshine-redesign/client/src/components/resources/ResourcesList.js
Lixian Zhou 3e2ee84225
All checks were successful
Build And Deploy Main / build-and-deploy (push) Successful in 33s
fix
2026-03-12 15:18:31 -04:00

402 lines
15 KiB
JavaScript

import React, {useState, useEffect} from "react";
import { useNavigate } from "react-router-dom";
import { AuthService, ResourceService } from "../../services";
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab, Dropdown } from "react-bootstrap";
import { Columns, Download, Filter, PencilSquare, PersonSquare, Plus } from "react-bootstrap-icons";
import ReactPaginate from 'react-paginate';
import { ManageTable, Export } from "../../shared/components";
import { RESOURCE_TYPE_OPTIONS, RESOURCE_SPECIALTY_OPTIONS } from "../../shared";
const ResourcesList = () => {
const navigate = useNavigate();
const [resources, setResources] = useState([]);
const [keyword, setKeyword] = useState('');
const [currentItems, setCurrentItems] = useState(null);
const [sorting, setSorting] = useState({key: '', order: ''});
const [selectedItems, setSelectedItems] = useState([]);
// Here we use item offsets; we could also use page offsets
// following the API or data you're working with.
const [itemOffset, setItemOffset] = useState(0);
const [pageCount, setPageCount] = useState(0);
const [itemsPerPage, setItemsPerPage] = useState(10);
const [showDeletedItems, setShowDeletedItems] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [showFilterDropdown, setShowFilterDropdown] = useState(false);
const [typeFilter, setTypeFilter] = useState('');
const [specialtyFilter, setSpecialtyFilter] = useState('');
const [columns, setColumns] = useState([
{
key: 'name',
label: 'Provider',
show: true
},
{
key: 'office_name',
label: 'Office Name',
show: true
},
{
key: 'type',
label: 'Type',
show: true
},
{
key: 'specialty',
label: 'Specialty',
show: true
},
{
key: 'phone',
label: 'Office Phone Number',
show: true
},
{
key: 'contact',
label: 'Secondary Phone Number',
show: true
},
{
key: 'fax',
label: 'Fax Number',
show: true
},
{
key: 'email',
label: 'Email',
show: true
},
{
key: 'display_address',
label: 'Provider Address',
show: true
},
{
key: 'note',
label: 'Note',
show: true
}
]);
useEffect(() => {
if (!AuthService.canViewProviderInfo()) {
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`);
}
ResourceService.getAll().then(data => {
setResources(data.data);
})
}, []);
const getFilteredResources = () => {
return (showDeletedItems ? resources.filter(item => item.status !== 'active') : resources.filter(item => item.status === 'active'))
?.filter(resource => (resource?.name?.toLowerCase().includes(keyword.toLowerCase()) || (resource?.specialty?.toLowerCase().includes(keyword.toLowerCase()) ) || (resource?.address?.toLowerCase().includes(keyword.toLowerCase()) )))
?.filter(resource => !typeFilter || (resource?.type || '') === typeFilter)
?.filter(resource => !specialtyFilter || (resource?.specialty || '') === specialtyFilter);
};
useEffect(() => {
const endOffset = itemOffset + parseInt(itemsPerPage);
const newResources = getFilteredResources();
const sortedResources = sorting.key === '' ? newResources : newResources.sort((a, b) => {
return a[sorting.key]?.localeCompare(b[sorting.key]);
});
const results = sorting.order === 'asc' ? sortedResources : sortedResources.reverse();
setCurrentItems(results.slice(itemOffset, endOffset)?.map(item => {
// Format address for display
const line1 = item?.address_line_1 || item?.address || '';
const city = item?.city || '';
const state = item?.state || '';
const zipcode = item?.zipcode || '';
const display_address = [line1, city, state, zipcode].filter(Boolean).join(', ') || '-';
return Object.assign({}, item, {
office_name: item?.office_name || item?.name_original || '-',
display_address
});
}));
setPageCount(Math.ceil((newResources.length || 0) / itemsPerPage));
}, [resources, itemOffset, keyword, itemsPerPage, showDeletedItems, sorting, typeFilter, specialtyFilter]);
// useEffect(() => {
// const newResources = currentItems? [...currentItems] : [];
// console.log('here', sorting);
// setCurrentItems(
// sorting.order === 'asc' ? sortedResources : sortedResources.reverse()
// )
// }, [sorting]);
const handlePageClick = (event) => {
const filteredLength = getFilteredResources().length || 1;
const newOffset = (event.selected * itemsPerPage) % filteredLength;
console.log(
`User requested page number ${event.selected}, which is offset ${newOffset}`
);
setItemOffset(newOffset);
setCurrentPage(event.selected + 1);
};
const handlePageSelect = (pageNo) => {
const filteredLength = getFilteredResources().length || 1;
const newOffset = ((pageNo-1) * itemsPerPage) % filteredLength;
console.log(
`User requested page number ${pageNo}, which is offset ${newOffset}`
);
setItemOffset(newOffset);
};
const getSortingImg = (key) => {
return sorting.key === key ? (sorting.order === 'asc' ? 'up_arrow' : 'down_arrow') : 'default';
}
const sortTableWithField = (key) => {
let newSorting = {
key,
order: 'asc',
}
if (sorting.key === key && sorting.order === 'asc') {
newSorting = {...newSorting, order: 'desc'};
}
setSorting(newSorting);
}
const redirectToAdmin = () => {
navigate(`/medical`)
}
const goToEdit = (id) => {
navigate(`/medical/resources/edit/${id}`)
}
const goToCreateNew = () => {
navigate(`/medical/resources`)
}
const goToView = (id) => {
navigate(`/medical/resources/${id}`)
}
const toggleSelectedAllItems = () => {
if (selectedItems.length !== currentItems?.length || selectedItems?.length === 0) {
const newSelectedItems = [...currentItems].map((provider) => provider.id);
setSelectedItems(newSelectedItems);
} else {
setSelectedItems([]);
}
}
const toggleItem = (id) => {
if (selectedItems.includes(id)) {
const newSelectedItems = [...selectedItems].filter((item) => item !== id);
setSelectedItems(newSelectedItems);
} else {
const newSelectedItems = [...selectedItems, id];
setSelectedItems(newSelectedItems);
}
}
const showArchive = (value) => {
console.log('here', value);
setShowDeletedItems(value === 'archivedProviders');
// Recover all filters
setKeyword('');
setTypeFilter('');
setSpecialtyFilter('');
setSorting({key: '', order: ''});
setSelectedItems([]);
}
const clearAndCloseFilter = () => {
setTypeFilter('');
setSpecialtyFilter('');
setShowFilterDropdown(false);
};
const applyAndCloseFilter = () => {
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 dropdown-container">
<div className="me-4">
<div className="field-label">Type</div>
<select value={typeFilter} onChange={(e) => setTypeFilter(e.currentTarget.value)}>
<option value="">All</option>
{RESOURCE_TYPE_OPTIONS.map((item) => (
<option key={item.value} value={item.value}>{item.label}</option>
))}
</select>
</div>
<div className="me-4">
<div className="field-label">Specialty</div>
<select value={specialtyFilter} onChange={(e) => setSpecialtyFilter(e.currentTarget.value)}>
<option value="">All</option>
{RESOURCE_SPECIALTY_OPTIONS.map((item) => (
<option key={item.value} value={item.value}>{item.label}</option>
))}
</select>
</div>
</div>
<div className="list row">
<div className="col-md-12">
<button className="btn btn-default btn-sm float-right" onClick={clearAndCloseFilter}> Cancel </button>
<button className="btn btn-primary btn-sm float-right" onClick={applyAndCloseFilter}> Filter </button>
</div>
</div>
</div>
)
);
const checkSelectAll = () => {
return selectedItems?.length === currentItems?.length && selectedItems?.length > 0;
}
const deleteResource = (id) => {
const currentResource = resources.find((item) => item.id === id);
ResourceService.disableResource(id, { status: 'inactive', edit_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name,
edit_date: new Date(),
edit_history: currentResource?.edit_history? [...currentResource.edit_history, { employee: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, date: new Date() }] : [{ employee: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, date: new Date() }]}).then(() => {
ResourceService.getAll().then(data => {
setResources(data.data);
})
});
}
const table = <div className="list row mb-4">
<div className="col-md-12">
<table className="personnel-info-table">
<thead>
<tr>
<th className="th-checkbox"><input type="checkbox" checked={checkSelectAll()} onClick={() => toggleSelectedAllItems()}></input></th>
<th className="th-index">No.</th>
{
columns.filter(col => col.show).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>
</th>)
}
</tr>
</thead>
<tbody>
{
currentItems?.map((resource, index) => <tr key={resource?.id}>
<td className="td-checkbox"><input type="checkbox" checked={selectedItems.includes(resource.id)} onClick={()=>toggleItem(resource?.id)}/></td>
<td className="td-index">{itemOffset + index + 1}</td>
{columns.find(col => col.key === 'name')?.show && <td> {AuthService.canEditProviderInfo() && <PencilSquare size={16} className="clickable me-2" onClick={() => goToEdit(resource?.id)}></PencilSquare>} {AuthService.canViewProviderInfo() ? <button className="btn btn-link btn-sm" onClick={() => goToView(resource?.id)}>{resource?.name || '-'}</button> : (resource?.name || '-') } </td>}
{columns.find(col => col.key === 'office_name')?.show && <td>{resource?.office_name || '-'}</td>}
{columns.find(col => col.key === 'type')?.show && <td>{resource?.type || '-'}</td>}
{columns.find(col => col.key === 'specialty')?.show && <td>{resource?.specialty || '-'}</td>}
{columns.find(col => col.key === 'phone')?.show && <td>{resource?.phone || '-'}</td>}
{columns.find(col => col.key === 'contact')?.show && <td>{resource?.contact || '-'}</td>}
{columns.find(col => col.key === 'fax')?.show && <td>{resource?.fax || '-'}</td>}
{columns.find(col => col.key === 'email')?.show && <td>{resource?.email || '-'}</td>}
{columns.find(col => col.key === 'display_address')?.show && <td>{resource?.display_address}</td>}
{columns.find(col => col.key === 'note')?.show && <td>{resource?.note || resource?.description || '-'}</td>}
</tr>)
}
</tbody>
</table>
<div className="pagination-container">
<ReactPaginate
key={itemsPerPage}
className="customers-pagination"
breakLabel="..."
nextLabel="Next"
onPageChange={handlePageClick}
pageRangeDisplayed={5}
pageCount={pageCount}
previousLabel="Prev"
renderOnZeroPageCount={null}
containerClassName="pagination justify-content-center"
pageClassName="page-item"
pageLinkClassName="page-link"
previousClassName="page-prev-item"
previousLinkClassName="page-link"
nextClassName="page-next-item"
nextLinkClassName="page-link"
activeClassName="active"
breakClassName="page-item"
breakLinkClassName="page-link"
/>
<div className="select-page-container">
<input type="number" className="page-picker" max={pageCount} min={1} value={currentPage} onChange={(e) => {handlePageSelect(e.target.value); setCurrentPage(e.target.value)}}/>
<div className="per-page-label"> {` of ${pageCount}`}</div>
</div>
<div className="select-page-container">
<select className="per-page" value={itemsPerPage} onChange={e => setItemsPerPage(e.target.value)}>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select> <span className="per-page-label"> /page</span>
</div>
</div>
</div>
</div>;
return (
<>
<div className="list row mb-4">
<Breadcrumb>
<Breadcrumb.Item href="/medical/index">Medical</Breadcrumb.Item>
<Breadcrumb.Item active>
Provider Information
</Breadcrumb.Item>
</Breadcrumb>
<div className="col-md-12 text-primary">
<h4>
All Providers
</h4>
</div>
</div>
<div className="app-main-content-list-container list-page">
<div className="app-main-content-list-func-container">
<Tabs defaultActiveKey="activeProviders" id="provider-tab" onSelect={(k) => showArchive(k)}>
<Tab eventKey="activeProviders" title="Active Providers">
{table}
</Tab>
<Tab eventKey="archivedProviders" title="Archived Providers">
{table}
</Tab>
</Tabs>
<div className="list-func-panel">
<input className="me-2 with-search-icon" type="text" placeholder="Search" value={keyword} onChange={(e) => setKeyword(e.currentTarget.value)} />
<ManageTable columns={columns} onColumnsChange={setColumns} />
<Dropdown
key={'filter-providers'}
id="filter-providers"
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>
{AuthService.canEditProviderInfo() && <button className="btn btn-primary me-2" onClick={() => goToCreateNew()}><Plus size={16}></Plus>Add New Providers</button>}
<Export
columns={columns}
data={currentItems || []}
filename="resources"
/>
</div>
</div>
</div>
</>
)
};
export default ResourcesList;