This commit is contained in:
@@ -8,7 +8,34 @@ import DashboardCustomersList from "../dashboard/DashboardCustomersList";
|
||||
const CustomersList = () => {
|
||||
const navigate = useNavigate();
|
||||
const [customers, setCustomers] = useState([]);
|
||||
const [columns] = 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) => {
|
||||
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 },
|
||||
@@ -27,7 +54,7 @@ const CustomersList = () => {
|
||||
{ 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()) {
|
||||
|
||||
@@ -8,6 +8,29 @@ import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab, Dropdown, Modal } from
|
||||
import { Columns, Download, Filter, PencilSquare, PersonSquare, Plus } from "react-bootstrap-icons";
|
||||
|
||||
const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, title = null }) => {
|
||||
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',
|
||||
tags: 'personalInfo'
|
||||
};
|
||||
const getVisibleColumnsByPermission = (columnList = []) => {
|
||||
return columnList.filter((column) => {
|
||||
const mappedTab = CUSTOMER_LIST_COLUMN_TAB_MAP[column.key];
|
||||
if (!mappedTab) return true;
|
||||
return AuthService.canViewCustomerTab(mappedTab);
|
||||
});
|
||||
};
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const site = EventsService.site;
|
||||
@@ -33,7 +56,7 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
|
||||
const [avatarLoading, setAvatarLoading] = useState(false);
|
||||
const [customerAvatars, setCustomerAvatars] = useState({});
|
||||
const isAllCustomersPage = title === 'All Customers';
|
||||
const [columns, setColumns] = useState([
|
||||
const [columns, setColumns] = useState(getVisibleColumnsByPermission([
|
||||
{
|
||||
key: 'name',
|
||||
label:'Name',
|
||||
@@ -104,7 +127,11 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
|
||||
label: 'Tags',
|
||||
show: true
|
||||
}
|
||||
]);
|
||||
]));
|
||||
useEffect(() => {
|
||||
setColumns((prevColumns) => getVisibleColumnsByPermission(prevColumns));
|
||||
}, []);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (!AuthService.canViewCustomers()) {
|
||||
@@ -257,7 +284,7 @@ const DashboardCustomersList = ({ additionalButtons, showBreadcrumb = false, tit
|
||||
};
|
||||
|
||||
const handleColumnsChange = (newColumns) => {
|
||||
setColumns(newColumns);
|
||||
setColumns(getVisibleColumnsByPermission(newColumns));
|
||||
}
|
||||
|
||||
const goToEdit = (id) => {
|
||||
|
||||
@@ -139,7 +139,7 @@ const SideMenu = () => {
|
||||
name: 'Templates',
|
||||
link: '/trans-routes/daily-templates/list',
|
||||
category: '/trans-routes/daily-templates',
|
||||
roleFunc: AuthService.canViewRoutes
|
||||
roleFunc: () => AuthService.canViewRouteTemplates() || AuthService.canEditRouteTemplates()
|
||||
},
|
||||
{
|
||||
name: 'Appointment One-Day List',
|
||||
@@ -194,7 +194,7 @@ const SideMenu = () => {
|
||||
name: 'Template',
|
||||
link: '/medical/template',
|
||||
category: '/medical/template',
|
||||
roleFunc: AuthService.canViewMedicalSection
|
||||
roleFunc: AuthService.canViewMedicalTemplate
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -22,7 +22,7 @@ const TemplateManager = () => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!AuthService.canViewMedicalSection()) {
|
||||
if (!AuthService.canViewMedicalTemplate()) {
|
||||
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");
|
||||
|
||||
@@ -94,7 +94,7 @@ const Card = ({ content, index, moveCard }) => {
|
||||
)
|
||||
}
|
||||
|
||||
const RouteCustomerEditor = ({currentRoute, setNewCustomerList = (a) => {}, viewMode, editFun, onAddCustomer = null, scheduledAbsentCustomerIds = []}) => {
|
||||
const RouteCustomerEditor = ({currentRoute, setNewCustomerList = (a) => {}, viewMode, editFun = () => {}, canEdit = true, onAddCustomer = null, scheduledAbsentCustomerIds = []}) => {
|
||||
const [customers, setCustomers] = useState([]);
|
||||
const [initializedRouteId, setInitializedRouteId] = useState(null);
|
||||
const [showAddPersonnelModal, setShowAddPersonnelModal] = useState(false);
|
||||
@@ -713,7 +713,7 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList = (a) => {}, view
|
||||
return (
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
{ !viewMode && <h6 class="text-primary">Customers Assigned ({getCurrentAssignedNumber()})</h6>}
|
||||
{ viewMode && <h6 class="text-primary">Route Assignment <button className="btn btn-sm btn-primary" onClick={() => editFun('assignment')}><Pencil size={16} className="me-2"></Pencil>Edit </button></h6>}
|
||||
{ viewMode && <h6 class="text-primary">Route Assignment {canEdit && <button className="btn btn-sm btn-primary" onClick={() => editFun('assignment')}><Pencil size={16} className="me-2"></Pencil>Edit </button>}</h6>}
|
||||
{!viewMode && <CustomersDropZone>
|
||||
{customers.map((item, index) => {
|
||||
if (item?.customers) {
|
||||
@@ -763,7 +763,7 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList = (a) => {}, view
|
||||
if (item?.customers) {
|
||||
return <div className="customers-dnd-item-container">
|
||||
<div className="stop-index"><span>{`Stop ${index+1}`}</span><RecordCircleFill size={16} color={"#0066B1"} className="ms-2"></RecordCircleFill> </div>
|
||||
<div className="customer-dnd-item" onClick={() => openEditAptGroupModal(index, item)}>
|
||||
<div className="customer-dnd-item" onClick={() => canEdit && openEditAptGroupModal(index, item)} style={{ cursor: canEdit ? 'pointer' : 'default' }}>
|
||||
<span className="me-2">{item.customer_group} </span> <span>{item.customers[0]?.customer_group_address}</span>
|
||||
<div className="customer-dnd-item-content">{item.customers.map(customer =>
|
||||
<div key={customer.customer_id}>
|
||||
|
||||
@@ -60,6 +60,7 @@ const RouteView = () => {
|
||||
};
|
||||
const routeForStatusView = mergeRouteCustomerMeta(latestRouteForStatus || routeSnapshot || currentRoute);
|
||||
const routeForAssignmentView = routeSnapshot || currentRoute;
|
||||
const canEditRoutes = AuthService.canAddOrEditRoutes();
|
||||
const closeModal = () => {
|
||||
setShowVehicleDetails(false);
|
||||
}
|
||||
@@ -81,6 +82,7 @@ const RouteView = () => {
|
||||
navigate(`/trans-routes/dashboard?dateSchedule=${moment(currentRoute?.schedule_date).format('YYYY-MM-DD')}`);
|
||||
}
|
||||
const edit = (editSection) => {
|
||||
if (!canEditRoutes) return;
|
||||
if (scheduleDate) {
|
||||
navigate(`/trans-routes/edit/${currentRoute?.id}?dateSchedule=${scheduleDate}&editSection=${editSection}`)
|
||||
} else {
|
||||
@@ -227,7 +229,7 @@ const RouteView = () => {
|
||||
<div className="app-main-content-list-func-container">
|
||||
<Tabs defaultActiveKey="routeOverview" id="route-view-tab">
|
||||
<Tab eventKey="routeOverview" title="Route Information">
|
||||
<h6 className="text-primary">Route Details <button className="btn btn-sm btn-primary" onClick={() => edit('info')}><Pencil size={16} className="me-2"></Pencil>Edit </button></h6>
|
||||
<h6 className="text-primary">Route Details {canEditRoutes && <button className="btn btn-sm btn-primary" onClick={() => edit('info')}><Pencil size={16} className="me-2"></Pencil>Edit </button>}</h6>
|
||||
<div className="app-main-content-fields-section">
|
||||
<div className="field-body">
|
||||
<div className="field-label">Route Name</div>
|
||||
@@ -299,7 +301,7 @@ const RouteView = () => {
|
||||
{currentRoute && currentRoute?.checklist_result.length === 0 && <>No Checklist found</>}</div>
|
||||
</div>
|
||||
</div>
|
||||
<RouteCustomerEditor currentRoute={routeForAssignmentView} viewMode={true} editFun={edit}></RouteCustomerEditor>
|
||||
<RouteCustomerEditor currentRoute={routeForAssignmentView} viewMode={true} editFun={edit} canEdit={canEditRoutes}></RouteCustomerEditor>
|
||||
</Tab>
|
||||
<Tab eventKey="routeStatus" title="Route Status">
|
||||
<div className="list row">
|
||||
@@ -333,7 +335,7 @@ const RouteView = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-12 mb-4">
|
||||
{routeForStatusView && <PersonnelSection transRoutes={[routeForStatusView]} showCompletedInfo={true} showGroupInfo={true} isInbound={routeForStatusView?.type === 'inbound' } allowForceEdit={AuthService.canViewRoutes()} sectionName="Personnel Status (click on each user to edit)" relatedOutbound={getRelatedOutboundRoutesForThisView()} vehicle={currentVehicle} driverName={resolvedDriverName} deleteFile={deleteFile} onRouteUpdated={refreshRouteStatusData}/>}
|
||||
{routeForStatusView && <PersonnelSection transRoutes={[routeForStatusView]} showCompletedInfo={true} showGroupInfo={true} isInbound={routeForStatusView?.type === 'inbound' } allowForceEdit={canEditRoutes} sectionName="Personnel Status (click on each user to edit)" relatedOutbound={getRelatedOutboundRoutesForThisView()} vehicle={currentVehicle} driverName={resolvedDriverName} deleteFile={deleteFile} onRouteUpdated={refreshRouteStatusData}/>}
|
||||
</div>
|
||||
</div>
|
||||
</Tab>
|
||||
|
||||
@@ -66,7 +66,7 @@ const RoutesHistory = () => {
|
||||
</div>
|
||||
<div className="list row">
|
||||
<div className="col-md-12 mb-4">
|
||||
<PersonnelSection transRoutes={allRoutes} showCompletedInfo={false} showGroupInfo={false} allowForceEdit={AuthService.canViewRoutes()} showFilter={true} sectionName="Personnel Status (click on each user to edit)"/>
|
||||
<PersonnelSection transRoutes={allRoutes} showCompletedInfo={false} showGroupInfo={false} allowForceEdit={AuthService.canAddOrEditRoutes()} showFilter={true} sectionName="Personnel Status (click on each user to edit)"/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, {useState, useEffect} from "react";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import { useSelector } from "react-redux";
|
||||
import { DailyRoutesTemplateService } from "../../services";
|
||||
import { AuthService, DailyRoutesTemplateService } from "../../services";
|
||||
import { Breadcrumb, Modal, Button, Spinner } from "react-bootstrap";
|
||||
import { PencilSquare, Trash } from "react-bootstrap-icons";
|
||||
import RoutesSection from "./RoutesSection";
|
||||
@@ -20,6 +20,7 @@ const ViewDailyTemplate = () => {
|
||||
const [newName, setNewName] = useState('');
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
const canEditTemplateRoutes = AuthService.canEditRouteTemplates();
|
||||
|
||||
useEffect(() => {
|
||||
fetchTemplate();
|
||||
@@ -44,6 +45,7 @@ const ViewDailyTemplate = () => {
|
||||
};
|
||||
|
||||
const openEditNameModal = () => {
|
||||
if (!canEditTemplateRoutes) return;
|
||||
setShowEditNameModal(true);
|
||||
};
|
||||
|
||||
@@ -77,6 +79,7 @@ const ViewDailyTemplate = () => {
|
||||
};
|
||||
|
||||
const confirmDelete = () => {
|
||||
if (!canEditTemplateRoutes) return;
|
||||
setShowDeleteModal(true);
|
||||
};
|
||||
|
||||
@@ -93,6 +96,7 @@ const ViewDailyTemplate = () => {
|
||||
};
|
||||
|
||||
const goToCreateRoute = (type) => {
|
||||
if (!canEditTemplateRoutes) return;
|
||||
navigate(`/trans-routes/daily-templates/${params.id}/create-route?type=${type}`);
|
||||
};
|
||||
|
||||
@@ -121,16 +125,16 @@ const ViewDailyTemplate = () => {
|
||||
<div className="col-md-12 text-primary">
|
||||
<h4>
|
||||
{template.name}
|
||||
<PencilSquare
|
||||
{canEditTemplateRoutes && <PencilSquare
|
||||
size={20}
|
||||
className="clickable ms-2"
|
||||
onClick={openEditNameModal}
|
||||
title="Edit template name"
|
||||
/>
|
||||
/>}
|
||||
<button className="btn btn-link btn-sm" onClick={goBack}>Back to List</button>
|
||||
<button className="btn btn-danger btn-sm ms-2" onClick={confirmDelete}>
|
||||
{canEditTemplateRoutes && <button className="btn btn-danger btn-sm ms-2" onClick={confirmDelete}>
|
||||
<Trash size={14} className="me-1" />Delete Template
|
||||
</button>
|
||||
</button>}
|
||||
</h4>
|
||||
<div className="text-muted">
|
||||
<small>Template Date: {template.template_date} | Created by: {template.create_by}</small>
|
||||
@@ -149,7 +153,7 @@ const ViewDailyTemplate = () => {
|
||||
onRouteClick={handleRouteClick}
|
||||
isTemplate={true}
|
||||
templateId={params.id}
|
||||
canAddNew={true}
|
||||
canAddNew={canEditTemplateRoutes}
|
||||
addText="+Add Route"
|
||||
redirect={goToCreateRoute}
|
||||
routeType="inbound"
|
||||
@@ -165,7 +169,7 @@ const ViewDailyTemplate = () => {
|
||||
onRouteClick={handleRouteClick}
|
||||
isTemplate={true}
|
||||
templateId={params.id}
|
||||
canAddNew={true}
|
||||
canAddNew={canEditTemplateRoutes}
|
||||
addText="+Add Route"
|
||||
redirect={goToCreateRoute}
|
||||
routeType="outbound"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, {useState, useEffect} from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import { DailyRoutesTemplateService } from "../../services";
|
||||
import { AuthService, DailyRoutesTemplateService } from "../../services";
|
||||
import { selectAllActiveDrivers, selectAllActiveVehicles } from "./../../store";
|
||||
import { Breadcrumb, Spinner, Modal, Button } from "react-bootstrap";
|
||||
import { Pencil, Trash } from "react-bootstrap-icons";
|
||||
@@ -16,6 +16,7 @@ const ViewDailyTemplateRoute = () => {
|
||||
const [currentRoute, setCurrentRoute] = useState(null);
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
const canEditTemplateRoutes = AuthService.canEditRouteTemplates();
|
||||
|
||||
const currentVehicle = vehicles.find(item => item.id === currentRoute?.vehicle);
|
||||
const currentDriver = drivers.find(item => item.id === currentRoute?.driver);
|
||||
@@ -37,10 +38,12 @@ const ViewDailyTemplateRoute = () => {
|
||||
};
|
||||
|
||||
const goToEdit = () => {
|
||||
if (!canEditTemplateRoutes) return;
|
||||
navigate(`/trans-routes/daily-templates/${params.id}/update-route/${params.routeId}`);
|
||||
};
|
||||
|
||||
const confirmDelete = () => {
|
||||
if (!canEditTemplateRoutes) return;
|
||||
setShowDeleteModal(true);
|
||||
};
|
||||
|
||||
@@ -125,14 +128,14 @@ const ViewDailyTemplateRoute = () => {
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<button className="btn btn-primary me-2" onClick={goToEdit}>
|
||||
{canEditTemplateRoutes && <button className="btn btn-primary me-2" onClick={goToEdit}>
|
||||
<Pencil size={16} className="me-2" />
|
||||
Update Route
|
||||
</button>
|
||||
<button className="btn btn-danger" onClick={confirmDelete}>
|
||||
</button>}
|
||||
{canEditTemplateRoutes && <button className="btn btn-danger" onClick={confirmDelete}>
|
||||
<Trash size={16} className="me-2" />
|
||||
Delete Route
|
||||
</button>
|
||||
</button>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -140,6 +143,7 @@ const ViewDailyTemplateRoute = () => {
|
||||
currentRoute={currentRoute}
|
||||
viewMode={true}
|
||||
editFun={() => goToEdit()}
|
||||
canEdit={canEditTemplateRoutes}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -194,6 +194,10 @@ const canEditAppointmentRequests = () => {
|
||||
return hasPermission('Edit & Create_Appointment Request');
|
||||
}
|
||||
|
||||
const canViewMedicalTemplate = () => {
|
||||
return hasPermission('Medical Template');
|
||||
}
|
||||
|
||||
const canViewMedicalEvents = () => {
|
||||
return hasAnyPermission([
|
||||
'View _Calendar _Medical Appointment',
|
||||
@@ -365,6 +369,7 @@ export const AuthService = {
|
||||
canEditProviderInfo,
|
||||
canViewAppointmentRequests,
|
||||
canEditAppointmentRequests,
|
||||
canViewMedicalTemplate,
|
||||
canViewMedicalEvents,
|
||||
canEditMedicalEvents,
|
||||
canViewMealStatus,
|
||||
|
||||
Reference in New Issue
Block a user