Add menu and list page style

This commit is contained in:
Yang Li 2025-04-23 16:26:33 -04:00
parent 1d1a5a09bb
commit fb3bd27a15
19 changed files with 629 additions and 257 deletions

BIN
app/.DS_Store vendored

Binary file not shown.

View File

@ -1,16 +1,16 @@
{
"files": {
"main.css": "/static/css/main.380e9f2d.css",
"main.js": "/static/js/main.c721f88d.js",
"main.css": "/static/css/main.3602a440.css",
"main.js": "/static/js/main.d36451b8.js",
"static/js/787.c4e7f8f9.chunk.js": "/static/js/787.c4e7f8f9.chunk.js",
"static/media/landing.png": "/static/media/landing.d4c6072db7a67dff6a78.png",
"index.html": "/index.html",
"main.380e9f2d.css.map": "/static/css/main.380e9f2d.css.map",
"main.c721f88d.js.map": "/static/js/main.c721f88d.js.map",
"main.3602a440.css.map": "/static/css/main.3602a440.css.map",
"main.d36451b8.js.map": "/static/js/main.d36451b8.js.map",
"787.c4e7f8f9.chunk.js.map": "/static/js/787.c4e7f8f9.chunk.js.map"
},
"entrypoints": [
"static/css/main.380e9f2d.css",
"static/js/main.c721f88d.js"
"static/css/main.3602a440.css",
"static/js/main.d36451b8.js"
]
}

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script><link rel="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.c721f88d.js"></script><link href="/static/css/main.380e9f2d.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script><link rel="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.d36451b8.js"></script><link href="/static/css/main.3602a440.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,136 +0,0 @@
/*!
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/
/*!
* Signature Pad v2.3.2
* https://github.com/szimek/signature_pad
*
* Copyright 2017 Szymon Nowak
* Released under the MIT license
*
* The main idea and some parts of the code (e.g. drawing variable width Bézier curve) are taken from:
* http://corner.squareup.com/2012/07/smoother-signatures.html
*
* Implementation of interpolation using cubic Bézier curves is taken from:
* http://benknowscode.wordpress.com/2012/09/14/path-interpolation-using-cubic-bezier-and-control-point-estimation-in-javascript
*
* Algorithm for approximated length of a Bézier curve is taken from:
* http://www.lemoda.net/maths/bezier-length/index.html
*
*/
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/**
* @license
* Lodash <https://lodash.com/>
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* use-sync-external-store-shim.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* use-sync-external-store-shim/with-selector.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* React Router v6.3.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
/** @license React v16.13.1
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
//! Copyright (c) JS Foundation and other contributors
//! github.com/moment/moment-timezone
//! license : MIT
//! moment-timezone.js
//! moment.js
//! version : 0.5.45

File diff suppressed because one or more lines are too long

View File

@ -1,16 +1,16 @@
{
"files": {
"main.css": "/static/css/main.380e9f2d.css",
"main.js": "/static/js/main.c721f88d.js",
"main.css": "/static/css/main.3602a440.css",
"main.js": "/static/js/main.d36451b8.js",
"static/js/787.c4e7f8f9.chunk.js": "/static/js/787.c4e7f8f9.chunk.js",
"static/media/landing.png": "/static/media/landing.d4c6072db7a67dff6a78.png",
"index.html": "/index.html",
"main.380e9f2d.css.map": "/static/css/main.380e9f2d.css.map",
"main.c721f88d.js.map": "/static/js/main.c721f88d.js.map",
"main.3602a440.css.map": "/static/css/main.3602a440.css.map",
"main.d36451b8.js.map": "/static/js/main.d36451b8.js.map",
"787.c4e7f8f9.chunk.js.map": "/static/js/787.c4e7f8f9.chunk.js.map"
},
"entrypoints": [
"static/css/main.380e9f2d.css",
"static/js/main.c721f88d.js"
"static/css/main.3602a440.css",
"static/js/main.d36451b8.js"
]
}

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script><link rel="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.c721f88d.js"></script><link href="/static/css/main.380e9f2d.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script><link rel="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.d36451b8.js"></script><link href="/static/css/main.3602a440.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

View File

@ -9,10 +9,37 @@ body {
font-size: 13px;
}
.text-primary {
color: #0066B1;
}
.text-primary h4 {
color: #0066B1;
}
.btn-link {
color: #0066B1;
}
.breadcrumb {
align-items: baseline;
}
.breadcrumb-item+.breadcrumb-item:before {
content: ">" !important;
}
.breadcrumb-item a {
color: #aaa;
font-size: 13px;
text-decoration: none !important;
}
.breadcrumb-item.active {
color: #555;
font-size: 13px;
}
label {
font-size: 12px;
}
@ -35,6 +62,37 @@ input {
background: white;
}
.app-menu-user-profile-container {
position: absolute;
right: 40px;
display: flex;
justify-content: center;
align-items: center;
}
.app-menu-user-profile {
border: 1px solid #ccc;
border-radius: 8px;
padding: 1px 4px;
display: flex;
justify-content: center;
align-items: center;
}
.user-info-container {
margin-left: 4px;
padding: 1px 4px;
}
.user-name {
font-size: 12px;
}
.user-role {
font-size: 12px;
color: #aaa;
}
.app-side-bar-container {
width: 300px;
border-right: 1px solid #ccc;
@ -44,11 +102,50 @@ input {
.app-side-bar-collapse {
padding: 16px;
border-top: 1px solid #ccc;
border-right: 1px solid #ccc;
width: 300px;
position: absolute;
bottom: 0;
left: 0;
cursor: pointer;
background: white;
}
.app-side-bar-list-item-container.collapsed {
text-align: center
}
.app-side-bar-list-item {
padding: 8px 0 4px 16px;
margin-right: 16px;
cursor: pointer;
}
.app-side-bar-list-item-name {
font-size: 12px;
}
.app-side-bar-menu-container {
height: calc(100vh - 120px);
overflow: auto;
}
.float-icon {
float: right;
}
.with-icon {
margin-left: 8px;
}
.sub-nav-item {
padding-top: 2px !important;
padding-bottom: 2px !important;
}
.app-side-bar-list-item-active {
background: #EAF3FF;
border-left: 2px solid #0066B1;
}
.collapse-label {
@ -85,6 +182,55 @@ input {
margin-right: 8px;
}
.app-main-content-list-container {
border: 1px solid #ccc;
border-radius: 8px;
padding: 4px 24px;
}
.app-main-content-list-func-container {
position: relative;
}
.app-main-content-list-func-container .nav-tabs {
border-bottom: none !important;
}
.app-main-content-list-func-container .nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link {
color: #777;
padding-bottom: 16px;
font-size: 14px;
}
.app-main-content-list-func-container .nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link:hover {
border: none;
border-bottom-color: #0066B1 !important;
}
.app-main-content-list-func-container .nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active:hover {
border-color: transparent;
}
.app-main-content-list-func-container .nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active {
border-color: transparent;
border-bottom-color: #0066B1;
color: #0066B1 !important;
}
.app-main-content-list-func-container .tab-pane {
border-top: 1px solid #ccc;
padding-top: 24px;
overflow: auto;
}
.app-main-content-list-func-container .list-func-panel {
position: absolute;
right: 0;
top: 4px;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
@ -190,23 +336,48 @@ legend {
margin-right: 5px;
}
table {
/* table {
border: 1px solid #0dcaf0;
}
} */
table tr {
border: 1px solid #0dcaf0;
border: 1px solid white;
}
table tr th, table tr td {
border: 1px solid #0dcaf0;
padding: 5px 15px;
font-size: 11px;
color: #0dcaf0;
table tr th {
border: 1px solid white;
padding: 8px 16px;
font-size: 12px;
background-color: #0066B1;
color: white;
min-width: 250px;
}
table tr td {
color: #000;
border: 1px solid white;
padding-left: 16px;
font-size: 12px;
color: #333;
min-width: 250px;
}
.clickable {
cursor: pointer;
}
.td-checkbox, .th-checkbox {
width: 30px !important;
padding-left: 8px !important;
min-width: 5px !important;
border-right: none;
}
.float-right {
float: right;
}
tbody tr:nth-child(even) {
background-color: #eee;
}
table .children {

View File

@ -162,8 +162,8 @@ function App() {
<Route path="events/:id" element={<ViewEvent /> } />
<Route path="events/edit/:id" element={<UpdateEvent /> } />
<Route path="events/create-from-request" element={<UpdateEvent /> } />
<Route path="events/request" element={<CreateEventRequest />} />
<Route path="events/request/list" element={<EventRequestList />} />
<Route path="event-request" element={<CreateEventRequest />} />
<Route path="event-request/list" element={<EventRequestList />} />
</Route>
</Route>
</Routes>

View File

@ -4,7 +4,8 @@ import { useNavigate, useParams } from "react-router-dom";
import { customerSlice } from "./../../store";
import { AuthService, CustomerService, EventsService } from "../../services";
import { CUSTOMER_TYPE } from "../../shared";
import { Spinner } from "react-bootstrap";
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab } from "react-bootstrap";
import { Columns, Download, Filter, PencilSquare, PersonSquare, Plus } from "react-bootstrap-icons";
const CustomersList = () => {
@ -16,6 +17,9 @@ const CustomersList = () => {
const [transferMap, setTransferMap] = useState({});
// const [events, setEvents] = useState([]);
const [showSpinner, setShowSpinner] = useState(false);
const [sorting, setSorting] = useState({key: '', order: ''});
const [selectedItems, setSelectedItems] = useState([]);
const [filteredCustomers, setFilteredCustomers] = useState(customers);
useEffect(() => {
if (!AuthService.canViewCustomers()) {
@ -24,7 +28,12 @@ const CustomersList = () => {
navigate(`/login`);
}
CustomerService.getAllCustomers().then((data) => {
setCustomers(data.data.sort((a, b) => a.lastname > b.lastname ? 1: -1));
setCustomers(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));
})
// EventsService.getAllEvents({ from: EventsService.formatDate(new Date()), to: '9999-12-31'}).then((data) => {
// console.log('events', data.data)
@ -32,6 +41,135 @@ const CustomersList = () => {
// })
}, []);
useEffect(() => {
if (showInactive) {
setFilteredCustomers(customers && customers.filter((item) => item?.name.toLowerCase().includes(keyword.toLowerCase())).filter(item => (item.type === CUSTOMER_TYPE.TRANSFERRED || item.type === CUSTOMER_TYPE.DECEASED || item.type === CUSTOMER_TYPE.DISCHARED) && item.status !== 'active'));
} else {
setFilteredCustomers(customers && customers.filter((item) => item?.name.toLowerCase().includes(keyword.toLowerCase())).filter(item => (item.type !== CUSTOMER_TYPE.TRANSFERRED && item.type!=CUSTOMER_TYPE.DECEASED && item.type!=CUSTOMER_TYPE.DISCHARED) && item.status === 'active'));
}
}, [keyword, customers])
useEffect(() => {
const newCustomers = [...customers];
const sortedCustomers = sorting.key === '' ? newCustomers : newCustomers.sort((a, b) => {
return a[sorting.key]?.localeCompare(b[sorting.key]);
});
setCustomers(
sorting.order === 'asc' ? sortedCustomers : sortedCustomers.reverse()
)
}, [sorting]);
const columns = [
{
key: 'name',
label:'Name'
},
{
key: 'chinese_name',
label: 'Preferred Name'
},
{
key: 'email',
label: 'Email'
},
{
key: 'type',
label: 'Type'
},
{
key: 'pickup_status',
label: 'Pickup Status'
},
{
key: 'birth_date',
label: 'Date of Birth'
},
{
key: 'gender',
label: 'Gender'
},
{
key: 'language',
label: 'Language'
},
{
key: 'medicare_number',
label: 'Medicare Number',
},
{
key: 'medicaid_number',
label: 'Medicaid Number'
},
{
key: 'address',
label: 'Address'
},
{
key: 'phone',
label: 'Phone'
},
{
key: 'emergency_contact',
label: 'Fasting'
}
];
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 toggleSelectedAllItems = () => {
if (selectedItems.length !== filteredCustomers.length || selectedItems.length === 0) {
const newSelectedItems = [...filteredCustomers].filter(ev=> ev.status === 'active').map((customer) => customer.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);
setShowInactive(value === 'archivedCustomers');
// Recover all filters
setKeyword('');
setSorting({key: '', order: ''});
setSelectedItems([]);
}
const checkSelectAll = () => {
return selectedItems.length === filteredCustomers.filter(ev=> ev.status === 'active').length && selectedItems.length > 0;
}
const redirectToAdmin = () => {
navigate(`/admin/customer-report`)
}
@ -63,14 +201,12 @@ const CustomersList = () => {
const site = EventsService.site;
const transferCustomer = (customerId) => {
console.log('transfer To', transferMap[customerId]);
if (site !== undefined && site !== null && site !== '' && site !== 0) {
setShowSpinner(true);
const currentCustomer = customers.find((c) => c.id === customerId);
if (currentCustomer) {
EventsService.getByCustomer({name: currentCustomer?.name, id: currentCustomer?.id, namecn: currentCustomer?.name_cn}).then((eventsData) => {
const events = eventsData?.data;
console.log('eventsToUpdate', events);
CustomerService.updateCustomer(customerId, { ...currentCustomer, site: transferMap[customerId] }).then(() => {
// const eventsWithCustomer = events.filter(ev => ev?.data?.customer === customerId || ev?.data?.client_name === currentCustomer?.name || ev?.target_name === currentCustomer?.name);
if (events?.length > 0) {
@ -148,6 +284,63 @@ const CustomersList = () => {
})
}
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>
{
columns.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>)
}
<th></th>
<th>Transfer To</th>
</tr>
</thead>
<tbody>
{
filteredCustomers.map(customer => <tr key={customer.id}>
<td className="td-checkbox"><input type="checkbox" checked={selectedItems.includes(customer.id)} onClick={()=>toggleItem(customer?.id)}/></td>
<td> {AuthService.canAddOrEditCustomers() && <PencilSquare size={16} className="clickable me-2" onClick={() => goToEdit(customer?.id)}></PencilSquare>} {AuthService.canViewCustomers() && <PersonSquare onClick={() => goToView(customer?.id)} size={16} className="clickable me-2" />} {customer?.name}</td>
<td>{customer?.name_cn}</td>
<td>{customer?.email}</td>
<td>{customer?.type}</td>
<td>{customer?.pickup_status}</td>
<td>{customer?.birth_date}</td>
<td>{customer?.gender}</td>
<td>{customer?.language}</td>
<td>{customer?.medicare_number}</td>
<td>{customer?.medicaid_number}</td>
<td>{customer?.address1 || customer?.address2 || customer?.address3 || customer?.address4 || customer?.address5}</td>
<td>{customer?.phone || customer?.home_phone || customer?.mobile_phone}</td>
<td>{customer?.emergency_contact}</td>
<td>
{AuthService.canViewCustomers() && <button className="btn btn-link btn-sm me-2" onClick={() => exportCSV(customer)}>Export Medical Events</button>}
</td>
<td>
{AuthService.canAddOrEditCustomers() &&
<div>
<select className="transfer-select" value={transferMap[customer?.id]} onChange={(e) => setTransferValue(customer?.id, e.target.value)}>
<option value=""></option>
{ site !== 1 && <option value="1">Gaithersburg - 1</option>}
{ site !== 2 && <option value="2">Beltsville - 2</option>}
{ site !== 3 && <option value="3">Frederick - 3</option>}
</select>
{ transferMap[customer?.id] && <button className="btn btn-primary btn-sm me-2" onClick={() => transferCustomer(customer?.id)}>Confirm</button> }
</div>
}
</td>
</tr>)
}
</tbody>
</table>
</div>
</div>;
return (
<>
@ -157,81 +350,38 @@ const CustomersList = () => {
</Spinner>
</div>}
<div className="list row mb-4">
<Breadcrumb>
<Breadcrumb.Item>General</Breadcrumb.Item>
<Breadcrumb.Item active>
Customer Information
</Breadcrumb.Item>
</Breadcrumb>
<div className="col-md-12 text-primary">
<h5>All Customers <button className="btn btn-primary btn-sm" onClick={() => {goToCreateNew()}}>Create New Customer</button> <button className="btn btn-link btn-sm" onClick={() => {redirectToAdmin()}}>Back</button></h5>
<h4>
All Customers
{/* <button className="btn btn-primary btn-sm" onClick={() => {goToCreateNew()}}>Create New Customer</button>
<button className="btn btn-link btn-sm" onClick={() => {redirectToAdmin()}}>Back</button> */}
</h4>
</div>
</div>
<div className="list row mb-4">
<div className="col-md-12 mb-4">
Filter By Name: <input className="me-2" type="text" value={keyword} onChange={(e) => setKeyword(e.currentTarget.value)} />
</div>
<div className="col-md-12 mb-4">
<input className="me-2" type="checkbox" value={showInactive} checked={showInactive === true} onChange={() => setShowInactive(!showInactive)} />
Show Transferred / Deactivated Customers
</div>
<div className="col-md-12">
<table className="personnel-info-table">
<thead>
<tr>
<th>Name</th>
<th>Preferred Name</th>
<th>Email</th>
<th>Type</th>
<th>Pickup Status</th>
<th>Date of Birth</th>
<th>Gender</th>
<th>Language</th>
<th>Medicare Number</th>
<th>Medicaid Number</th>
<th>Address</th>
<th>Phone</th>
<th>Emergency Contact</th>
<th></th>
<th>Transfer To</th>
</tr>
</thead>
<tbody>
{
customers && customers.filter((item) => item?.name.toLowerCase().includes(keyword.toLowerCase())).filter(item => showInactive ? item : item.type !== CUSTOMER_TYPE.TRANSFERRED && item.type!=CUSTOMER_TYPE.DECEASED && item.type!=CUSTOMER_TYPE.DISCHARED && item.status === 'active').map(customer => <tr key={customer.id}>
<td>{customer?.name}</td>
<td>{customer?.name_cn}</td>
<td>{customer?.email}</td>
<td>{customer?.type}</td>
<td>{customer?.pickup_status}</td>
<td>{customer?.birth_date}</td>
<td>{customer?.gender}</td>
<td>{customer?.language}</td>
<td>{customer?.medicare_number}</td>
<td>{customer?.medicaid_number}</td>
<td>{customer?.address1 || customer?.address2 || customer?.address3 || customer?.address4 || customer?.address5}</td>
<td>{customer?.phone || customer?.home_phone || customer?.mobile_phone}</td>
<td>{customer?.emergency_contact}</td>
<td>
{AuthService.canAddOrEditCustomers() && <button className="btn btn-primary btn-sm me-2" onClick={() => goToEdit(customer?.id)}>Edit</button> }
{AuthService.canViewCustomers() && <button className="btn btn-default btn-sm me-2" onClick={() => goToView(customer?.id)}>View</button>}
{AuthService.canViewCustomers() && <button className="btn btn-primary btn-sm me-2" onClick={() => exportCSV(customer)}>Export</button>}
</td>
<td>
{AuthService.canAddOrEditCustomers() &&
<div>
<select className="transfer-select" value={transferMap[customer?.id]} onChange={(e) => setTransferValue(customer?.id, e.target.value)}>
<option value=""></option>
{ site !== 1 && <option value="1">Gaithersburg - 1</option>}
{ site !== 2 && <option value="2">Beltsville - 2</option>}
{ site !== 3 && <option value="3">Frederick - 3</option>}
</select>
{ transferMap[customer?.id] && <button className="btn btn-primary btn-sm me-2" onClick={() => transferCustomer(customer?.id)}>Confirm</button> }
</div>
}
</td>
</tr>)
}
</tbody>
</table>
</div>
<div className="app-main-content-list-container">
<div className="app-main-content-list-func-container">
<Tabs defaultActiveKey="activeCustomers" id="customers-tab" onSelect={(k) => showArchive(k)}>
<Tab eventKey="activeCustomers" title="Active Customers">
{table}
</Tab>
<Tab eventKey="archivedCustomers" title="Archived Customers">
{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)} />
<button className="btn btn-primary me-2"><Filter size={16} className="me-2"></Filter>Filter</button>
<button className="btn btn-primary me-2"><Columns size={16} className="me-2"></Columns>Manage Table</button>
<button className="btn btn-primary me-2" onClick={() => goToCreateNew()}><Plus size={16}></Plus>Add New Customer</button>
<button className="btn btn-primary"><Download size={16} className="me-2"></Download>Export</button>
</div>
</div>
</div>
</>
)

View File

@ -29,7 +29,7 @@ const CreateEvent = () => {
// const params = new URLSearchParams(window.location.search);
const goToRequestList = () => {
navigate(`/medical/events/request/list`);
navigate(`/medical/event-request/list`);
}
const redirectTo = () => {

View File

@ -108,7 +108,7 @@ const columns = [
const goToCreateNew = () => {
navigate(`/medical/events/request`)
navigate(`/medical/event-request`)
}
const deleteItem = (id) => {

View File

@ -1,3 +1,4 @@
import { Bell, ChevronDown, PersonCircle } from 'react-bootstrap-icons';
import { Outlet, useLocation, Navigate } from 'react-router-dom';
import { AuthService } from '../../services';
import SideMenu from './menu';
@ -5,6 +6,7 @@ import SideMenu from './menu';
function Layout() {
const location = useLocation();
const showMenu = location.pathname !== '/login' && location.pathname !== '/landing'; // Example: Hide menu on login page
const user = localStorage.getItem('user') || {};
const getLogoSuffix = () => {
return (window.location.hostname.includes('worldshine2.mayo.llc') || window.location.hostname.includes('site2') || window.location.host.includes('ws2') ||window.location.hostname.includes('localhost')) ? "Care LLC" : ((window.location.hostname.includes('worldshine3.mayo.llc') ||window.location.hostname.includes('site3') || window.location.hostname.includes('ws3')) ? "Cloverleaf LLC" : "International LLC");
}
@ -15,6 +17,18 @@ function Layout() {
showMenu && <SideMenu />
}
<div className="app-main-container">
<div className="app-menu-user-profile-container">
<Bell size={16} color="#0066B1"/>
<div className="app-menu-user-profile ms-2">
<PersonCircle size={24}/>
<div className="user-info-container me-2">
<div className="user-name">{JSON.parse(user).username}</div>
<div className="user-role">{JSON.parse(user).roles[0]}</div>
</div>
<ChevronDown size={12} color="#555"></ChevronDown>
</div>
</div>
{localStorage.getItem('user') && localStorage.getItem('token') ? <Outlet /> : <Navigate to="/login" replace />}
</div>
</div>

View File

@ -1,16 +1,181 @@
import { Outlet, useLocation, Navigate } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { ArrowBarLeft, ArrowBarRight} from 'react-bootstrap-icons'
import { ArrowBarLeft, ArrowBarRight, Grid1x2, Display, Person, BusFront, BagPlus, Tools, ChevronDown, ChevronUp} from 'react-bootstrap-icons'
import { AuthService } from '../../services';
import { useNavigate } from 'react-router-dom';
const sideNavs = [
{
icon: <Grid1x2 color="#777" size={14}/>,
name: 'Dashboard',
link: '#',
roleFunc: undefined
},
{
icon: <Display color="#777" size={14}/>,
name: 'Info Screen',
link: '#',
roleFunc: undefined
},
{
icon: <Person color="#777" size={14}/>,
name: 'General',
collapsed: false,
subNavs: [
{
name: 'Customer Information',
//collapsed: true,
link: '/customers/list',
category: '/customers'
// subNavs: [
// {
// name: 'Customer List',
// link: '/customers/list'
// },
// {
// name: 'Create New Customer',
// link: '/customers'
// }
// ]
},
{
name: 'Calendar',
link: '#'
},
{
name: 'Messaging',
//collapsed: true,
link: '/messages/list',
category: '/messages'
// subNavs: [
// {
// name: 'Messages List',
// link: '/messages/list'
// },
// {
// name: 'Create New Message',
// link: '/messages'
// },
// {
// name: 'Send Message',
// link: '/messages/send-message'
// },
// {
// name: 'Sent Messages List',
// link: '/messages/sent-messages/list'
// }
// ]
},
// {
// name: 'Center Phones',
// //collapsed: true,
// link: '/center-phones/list'
// // subNavs: [
// // {
// // name: 'Center Phones List',
// // link: '/center-phones/list'
// // },
// // {
// // name: 'Create New Center Phone',
// // link: '/center-phones'
// // }
// // ]
// }
]
},
{
name: 'Transportation',
icon: <BusFront color="#777" size={14}/>,
collapsed: false,
subNavs: [
{
name: 'Vehicle Information',
link: '/vehicles/list',
category: '/vehicles'
// subNavs: [
// {
// name: 'Vehicles List',
// link: '/vehicles/list'
// },
// {
// name: 'Create New Vehicle',
// link: '/vehicles'
// }
// ]
},
{
name: 'Transportation Schedule',
link: '/trans-routes/schedule',
category: '/trans-routes'
},
{
name: 'Schedule Driver for Appointment',
link: '#'
}
]
},
{
name: 'Medical',
icon: <BagPlus color="#777" size={14}/>,
collapsed: false,
subNavs: [
{
name: 'Provider Information',
link: '/medical/resources/list',
category: '/resources'
},
{
name: 'Appointment Requests',
link: '/medical/event-request/list',
category: '/event-request'
},
{
name: 'Appointment Calendar',
link: '/medical/events/calendar',
category: '/events'
}
]
},
{
name: 'Lobby',
icon: <Tools color="#777" size={14}/>,
collapsed: false,
subNavs: [
{
name: 'Meal Status',
link: '#'
},
{
name: 'Seating Chart',
link: '#'
}
]
}
]
const SideMenu = () => {
const [collapse, setCollapse] = useState(false)
const [collapse, setCollapse] = useState(false);
const [navs, setNavs] = useState(sideNavs)
const location = useLocation();
const navigate = useNavigate();
const showMenu = location.pathname !== '/login' && location.pathname !== '/landing'; // Example: Hide menu on login page
const getLogoSuffix = () => {
return (window.location.hostname.includes('worldshine2.mayo.llc') || window.location.hostname.includes('site2') || window.location.host.includes('ws2') ||window.location.hostname.includes('localhost')) ? "Care LLC" : ((window.location.hostname.includes('worldshine3.mayo.llc') ||window.location.hostname.includes('site3') || window.location.hostname.includes('ws3')) ? "Cloverleaf LLC" : "International LLC");
}
const toggleMenu = (item) => {
item.collapsed = !item.collapsed
const navsCopy = [...sideNavs];
setNavs(navsCopy.map((nav) => item.name === nav.name ? item : nav));
}
const goToLink = (link) => {
console.log('this is lik', link);
navigate(link);
}
return (
<>
<div className={`app-side-bar-container${collapse ? ' collapsed' : ''}`}>
@ -19,6 +184,26 @@ const SideMenu = () => {
{!collapse && <strong className="logo-worldshine">Worldshine</strong>}
{!collapse && <span className="logo-suffix">{getLogoSuffix()}</span>}
</div>
<div className="app-side-bar-menu-container">
{
sideNavs.map((sideNav) => {
return <div className={`app-side-bar-list-item-container${collapse ? ' collapsed' : ''}`}>
<div onClick={() => sideNav.link && goToLink(sideNav.link)} className={`app-side-bar-list-item main-nav-item${sideNav.category && location?.pathname?.includes(sideNav.category) ? ' app-side-bar-list-item-active' : '' }`}>
{ sideNav?.icon ? <>{sideNav.icon}</> : <></>}
{ !collapse && <span className={`app-side-bar-list-item-name${sideNav?.icon? ' with-icon' : ''}`}>{sideNav.name}</span> }
{ !collapse && sideNav?.subNavs?.length > 0 && <span onClick={() => toggleMenu(sideNav)} className="float-icon">{sideNav?.collapsed ? <ChevronDown color="#777" size={12}/> : <ChevronUp color="#777" size={12}/>}</span> }
</div>
{
!collapse && !sideNav?.collapsed && sideNav?.subNavs?.map((subNav) => <div onClick={() => subNav.link && goToLink(subNav.link)} className={`app-side-bar-list-item sub-nav-item${subNav?.category && location?.pathname?.includes(subNav?.category) ? ' app-side-bar-list-item-active' : '' }`}>
{ subNav?.icon ? <>{subNav.icon}</> : <></>}
{ !collapse && <span className={`app-side-bar-list-item-name${subNav?.icon? ' with-icon' : ''}`}>{subNav.name}</span> }
{ subNav?.subNavs?.length > 0 && <span onClick={() => toggleMenu(subNav)} className="float-icon">{sideNav?.collapsed ? <ChevronDown color="#777" size={12}/> : <ChevronUp color="#777" size={12}/>}</span> }
</div>)
}
</div>
})
}
</div>
<div className={`app-side-bar-collapse ${collapse ? ' collapsed' : ''}`} onClick={() => setCollapse(!collapse)}>
{collapse ? <ArrowBarRight color="#777" size={20} /> : <><ArrowBarLeft color="#777" size={20}/> <span className='collapse-label'>{` Collapse`}</span></>}

View File

@ -36,11 +36,11 @@ const MedicalIndex = () => {
}
const goToRequestList = () => {
navigate(`/medical/events/request/list`);
navigate(`/medical/event-request/list`);
}
const goToCreateRequest = () => {
navigate(`/medical/events/request`)
navigate(`/medical/event-request`)
}

View File

@ -151,10 +151,10 @@ app.get('/medical/events/list', function (req,res) {
app.get('/medical/events', function (req,res) {
res.sendFile(path + "index.html");
});
app.get('/medical/events/request', function (req,res) {
app.get('/medical/event-request', function (req,res) {
res.sendFile(path + "index.html");
});
app.get('/medical/events/request/list', function (req,res) {
app.get('/medical/event-request/list', function (req,res) {
res.sendFile(path + "index.html");
});
app.get('/medical/events/edit/:id', function (req,res) {