All checks were successful
Build And Deploy Main / build-and-deploy (push) Successful in 33s
402 lines
15 KiB
JavaScript
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; |