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

This commit is contained in:
2026-03-10 16:21:27 -04:00
parent 27ba6b89d7
commit e7f47e664c
5 changed files with 261 additions and 15 deletions

View File

@@ -3,6 +3,73 @@ const Employee = db.employee;
var bcrypt = require("bcryptjs");
const { splitSite } = require("../middlewares");
const ALL_PERMISSIONS = [
'Dashboard',
'Admin View',
'View_Info Screen',
'Edit_Info Screen',
'View_Customer Info _Personal Info',
'View_Customer Info _Care & Services',
'View_Customer Info _Medical & Insurance',
'View_Customer Info _Confidential Details',
'View_Customer Info _Form Submission',
'Edit_Customer Info _ Personal Info',
'Edit_Customer Info _ Care & Services',
'Edit_Customer Info _ Medical & Insurance',
'Edit_Customer Info _ Confidential Details',
'Edit_Customer Info _ Form Submission',
'Discharge_Customer',
'Reactivate_Customer',
'Create_Customer',
'Export_Customer Report',
'View _Calendar _Medical Appointment',
'View _Calendar _Activities',
'View _Calendar _Attendance Notes',
'View _Calendar _Meal Plan',
'View _Calendar _Important Dates',
'Edit&Create _Calendar _Medical Appointment',
'Edit&Create _Calendar _Activities',
'Edit&Create _Calendar _Attendance Notes',
'Edit&Create _Calendar _Meal Plan',
'Edit&Create _Calendar _Important Dates',
'View_Messaging',
'Sent_Messaging',
'View_Messaging Template',
'Create&Edit_Messaging Template',
'View_Vehicle info_Basic Info',
'View_Vehicle info_Documents',
'View_Vehicle info_Repair Records',
'Edit_Vehicle info_Basic Info',
'Edit_Vehicle info_Documents',
'Edit_Vehicle info_Repair Records',
'Add_New Vehicle',
'Archive_Vehicle',
'Delete_Vehicle',
'Export_Vehicle Report',
'View_Transportation Schedule_Route Overview',
'Create&Edit_Transportation Schedule',
'Export_Transportation Schedule Report',
'View_Route Template',
'Create&Edit_Route Template',
'View_Driver Assignment for Appointment',
'Edit_Driver Assignment for Appointment',
'View_Provider Info',
'Create & Edit _Provider Info',
'View_Appointment Request',
'Edit & Create_Appointment Request',
'View_Appointment Calendar',
'Edit & Create_Appointment Calendar',
'Medical Template',
'View_Meal Status',
'Edit_Meal Status',
'View_Seating Chart',
'Edit_Seating Chart',
'Employee page',
'Set Permission for Employee'
];
const hasAllPermissionsByUsername = (username) => (username || '').toString().trim().toLowerCase() === 'testadmin03';
// Create and Save a new Employee (driver, distributor, admin)
exports.createEmployee = (req, res) => {
// Validate request
@@ -11,13 +78,16 @@ exports.createEmployee = (req, res) => {
return;
}
const site = splitSite.findSiteNumber(req);
const normalizedUsername = req.body.username || req.body.email || '';
const requestPermissions = Array.isArray(req.body.permissions) ? req.body.permissions : [];
// Create a Employee
const employee = new Employee({
username: req.body.username || req.body.email || '',
username: normalizedUsername,
name_cn: req.body.name_cn || '',
email: req.body.email || '',
password: req.body.password ? bcrypt.hashSync(req.body.password, 8) : '',
roles: req.body.roles || [],
permissions: hasAllPermissionsByUsername(normalizedUsername) ? ALL_PERMISSIONS : requestPermissions,
mobile_phone: req.body.mobile_phone || '',
home_phone: req.body.home_phone || '',
language: req.body.language || '',
@@ -204,13 +274,24 @@ exports.updateEmployee = (req, res) => {
if (req.body.password) {
req.body.password = bcrypt.hashSync(req.body.password, 8);
}
Employee.findByIdAndUpdate(id, req.body, { useFindAndModify: false })
.then(data => {
if (!data) {
Employee.findById(id)
.then((existingEmployee) => {
if (!existingEmployee) {
res.status(404).send({
message: `Cannot update employee with id=${id}. Maybe Employee was not found!`
});
} else res.send({ success: true, message: "Employee was updated successfully." });
return null;
}
const nextData = Object.assign({}, req.body);
const effectiveUsername = nextData.username || existingEmployee.username;
if (hasAllPermissionsByUsername(effectiveUsername)) {
nextData.permissions = ALL_PERMISSIONS;
}
return Employee.findByIdAndUpdate(id, nextData, { useFindAndModify: false });
})
.then((data) => {
if (!data) return;
res.send({ success: true, message: "Employee was updated successfully." });
})
.catch(err => {
res.status(500).send({

View File

@@ -8,6 +8,9 @@ module.exports = mongoose => {
roles: [{
type: String
}],
permissions: [{
type: String
}],
mobile_phone: String,
home_phone: String,
language: String,

View File

@@ -4,7 +4,7 @@ import { useNavigate, useParams } from "react-router-dom";
import { driverSlice } from "./../../store";
import { employeeSlice } from "../../store/employees";
import { AuthService, EmployeeService } from "../../services";
import { EMPLOYEE_TITLE, EMPLOYEE_TITLE_CN, EMPLOYEE_TITLE_ROLES_MAP } from "../../shared";
import { EMPLOYEE_TITLE, EMPLOYEE_TITLE_CN, EMPLOYEE_PERMISSION_GROUPS, EMPLOYEE_ALL_PERMISSIONS } from "../../shared";
import { Modal, Button } from "react-bootstrap";
const UpdateEmployee = () => {
@@ -21,7 +21,7 @@ const UpdateEmployee = () => {
const [lastname, setLastname] = useState('');
const [nameCN, setNameCN] = useState('');
const [birthDate, setBirthDate] = useState('');
const [roles, setRoles] = useState('');
const [permissions, setPermissions] = useState([]);
const [email, setEmail] = useState('');
const [driverCapacity, setDriverCapacity] = useState();
const [mobilePhone, setMobilePhone] = useState('');
@@ -39,6 +39,8 @@ const UpdateEmployee = () => {
const [tags, setTags] = useState('');
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [selectedFile, setSelectedFile] = useState();
const isAlwaysAllPermissionsUser = (value) => (value || '').toString().trim().toLowerCase() === 'testadmin03';
const isSuperPermissionLocked = isAlwaysAllPermissionsUser(username || currentEmployee?.username);
const params = new URLSearchParams(window.location.search);
const redirectTo = () => {
const redirect = params.get('redirect');
@@ -74,7 +76,12 @@ const UpdateEmployee = () => {
setLastname(currentEmployee.lastname);
setNameCN(currentEmployee.name_cn);
setBirthDate(currentEmployee.birth_date);
setRoles(currentEmployee.roles.join(','));
const currentPermissions = Array.isArray(currentEmployee.permissions) ? currentEmployee.permissions : [];
setPermissions(
isAlwaysAllPermissionsUser(currentEmployee.username)
? EMPLOYEE_ALL_PERMISSIONS
: currentPermissions
);
setEmail(currentEmployee.email);
setDriverCapacity(currentEmployee.driver_capacity);
setMobilePhone(currentEmployee.mobile_phone);
@@ -98,14 +105,22 @@ const UpdateEmployee = () => {
if (value) {
setTitle(value);
setTitleCN(EMPLOYEE_TITLE_CN[value]);
setRoles(EMPLOYEE_TITLE_ROLES_MAP[value]?.join(','));
} else {
setTitle('');
setTitleCN('');
setRoles('');
}
}
const togglePermission = (permissionKey) => {
if (isSuperPermissionLocked) return;
setPermissions((prevPermissions) => {
if (prevPermissions.includes(permissionKey)) {
return prevPermissions.filter((permission) => permission !== permissionKey);
}
return [...prevPermissions, permissionKey];
});
};
const triggerShowDeleteModal = () => {
setShowDeleteModal(true);
}
@@ -135,7 +150,7 @@ const UpdateEmployee = () => {
edit_by: 'admin',
note,
tags: tags.replace(' ', '').split(','),
roles: roles && roles.replace(' ', '').split(',')
permissions: isAlwaysAllPermissionsUser(username) ? EMPLOYEE_ALL_PERMISSIONS : permissions
};
if (password && password.length > 0) {
data = Object.assign({}, data, {password});
@@ -174,7 +189,7 @@ const UpdateEmployee = () => {
edit_by: 'admin',
note,
tags: tags.replace(' ', '').split(','),
roles: roles && roles.replace(' ', '').split(','),
permissions: isAlwaysAllPermissionsUser(username) ? EMPLOYEE_ALL_PERMISSIONS : permissions,
status
};
if (password && password.length > 0) {
@@ -255,9 +270,6 @@ const UpdateEmployee = () => {
<div className="col-md-4 mb-4">
<div>Title CN(中文称谓):</div> <input type="text" value={titleCN || ''} onChange={e => setTitleCN(e.target.value)}/>
</div>
<div className="col-md-4 mb-4">
<div>Roles:(*)</div> <input type="text" value={roles || ''} onChange={e => setRoles(e.target.value)}/>
</div>
<div className="col-md-4 mb-4">
<div>Email:(*)</div> <input type="email" value={email || ''} onChange={e => setEmail(e.target.value)}/>
</div>
@@ -306,6 +318,34 @@ const UpdateEmployee = () => {
onChange={(e) => setSelectedFile(e.target.files[0])}
/>
</div>
<div className="col-md-12 mb-4">
<div className="fw-bold mb-2">Permissions</div>
{isSuperPermissionLocked && (
<div className="text-muted mb-2">
testAdmin03 always has all permissions.
</div>
)}
{Object.entries(EMPLOYEE_PERMISSION_GROUPS).map(([groupName, permissionItems]) => (
<div key={groupName} className="mb-3">
<div className="text-primary mb-1">{groupName}</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px 18px' }}>
{permissionItems.map((permissionKey) => (
<label key={permissionKey} style={{ minWidth: '280px' }}>
<input
type="checkbox"
className="me-2"
checked={isSuperPermissionLocked || permissions.includes(permissionKey)}
onChange={() => togglePermission(permissionKey)}
disabled={isSuperPermissionLocked}
/>
{permissionKey}
</label>
))}
</div>
</div>
))}
</div>
</div>
<div className="list row mb-5">
<div className="col-md-6 col-sm-6 col-xs-12">

View File

@@ -393,6 +393,10 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList = (a) => {}, view
}))
}
const removeGroupedCustomerFromSelection = (id) => {
setNewRouteGroupedCustomerList((prevList) => prevList.filter((item) => item.customer_id !== id));
}
const setNewGroupNameAction = (value) => {
setNewGroupName(value);
for (const item of newRouteGroupedCustomerList) {
@@ -937,6 +941,36 @@ const RouteCustomerEditor = ({currentRoute, setNewCustomerList = (a) => {}, view
<input type="text" value={newGroupAddress} onChange={(e) => setNewGroupAddressAction(e.target.value)}/>
</div>
</div>
<div className="app-main-content-fields-section">
<div className="me-4" style={{ width: '100%' }}>
<div className="field-label">Selected Customers ({newRouteGroupedCustomerList.length})</div>
{newRouteGroupedCustomerList.length === 0 ? (
<small className="text-muted">No customer selected yet.</small>
) : (
<div className="customers-container" style={{ maxHeight: '180px', overflowY: 'auto', padding: '8px' }}>
{newRouteGroupedCustomerList.map((customer) => (
<div
key={`selected-group-customer-${customer.customer_id}`}
className="d-flex align-items-center justify-content-between mb-2"
style={{ gap: '12px' }}
>
<div>
<div><small>{customer.customer_name}</small></div>
<div><small className="text-muted">{customer.customer_address}</small></div>
</div>
<Button
variant="outline-danger"
size="sm"
onClick={() => removeGroupedCustomerFromSelection(customer.customer_id)}
>
Delete
</Button>
</div>
))}
</div>
)}
</div>
</div>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Type in user Id or Name to Search

View File

@@ -125,6 +125,94 @@ export const EMPLOYEE_TITLE_ROLES_MAP = {
export const INVITATION_CODE = ['ws5801care', 'world911street'];
export const EMPLOYEE_PERMISSION_GROUPS = {
Dashboard: [
'Dashboard',
'Admin View'
],
InfoScreen: [
'View_Info Screen',
'Edit_Info Screen'
],
CustomerInfo: [
'View_Customer Info _Personal Info',
'View_Customer Info _Care & Services',
'View_Customer Info _Medical & Insurance',
'View_Customer Info _Confidential Details',
'View_Customer Info _Form Submission',
'Edit_Customer Info _ Personal Info',
'Edit_Customer Info _ Care & Services',
'Edit_Customer Info _ Medical & Insurance',
'Edit_Customer Info _ Confidential Details',
'Edit_Customer Info _ Form Submission',
'Discharge_Customer',
'Reactivate_Customer',
'Create_Customer',
'Export_Customer Report'
],
Calendar: [
'View _Calendar _Medical Appointment',
'View _Calendar _Activities',
'View _Calendar _Attendance Notes',
'View _Calendar _Meal Plan',
'View _Calendar _Important Dates',
'Edit&Create _Calendar _Medical Appointment',
'Edit&Create _Calendar _Activities',
'Edit&Create _Calendar _Attendance Notes',
'Edit&Create _Calendar _Meal Plan',
'Edit&Create _Calendar _Important Dates'
],
Messaging: [
'View_Messaging',
'Sent_Messaging',
'View_Messaging Template',
'Create&Edit_Messaging Template'
],
Vehicle: [
'View_Vehicle info_Basic Info',
'View_Vehicle info_Documents',
'View_Vehicle info_Repair Records',
'Edit_Vehicle info_Basic Info',
'Edit_Vehicle info_Documents',
'Edit_Vehicle info_Repair Records',
'Add_New Vehicle',
'Archive_Vehicle',
'Delete_Vehicle',
'Export_Vehicle Report'
],
Transportation: [
'View_Transportation Schedule_Route Overview',
'Create&Edit_Transportation Schedule',
'Export_Transportation Schedule Report',
'View_Route Template',
'Create&Edit_Route Template',
'View_Driver Assignment for Appointment',
'Edit_Driver Assignment for Appointment'
],
Medical: [
'View_Provider Info',
'Create & Edit _Provider Info',
'View_Appointment Request',
'Edit & Create_Appointment Request',
'View_Appointment Calendar',
'Edit & Create_Appointment Calendar',
'Medical Template'
],
Lobby: [
'View_Meal Status',
'Edit_Meal Status',
'View_Seating Chart',
'Edit_Seating Chart'
],
EmployeeManagement: [
'Employee page',
'Set Permission for Employee'
]
};
export const EMPLOYEE_ALL_PERMISSIONS = Object.values(EMPLOYEE_PERMISSION_GROUPS)
.reduce((allPermissions, groupPermissions) => allPermissions.concat(groupPermissions), []);
// // Test Site 01
// export const LEGACY_LINK = (window.location.hostname.includes('worldshine2.mayo.llc') || window.location.hostname.includes('site2')) ? 'http://worldshineretro2.mayo.llc/staff/login?user=bxia' : ((window.location.hostname.includes('worldshine3.mayo.llc') || window.location.hostname.includes('site3')) ? 'http://worldshineretro3.mayo.llc/staff/login?user=bxia': 'http://worldshineretro.mayo.llc/staff/login?user=leapon');
// export const LEGACY_LINK = (window.location.hostname.includes('ws2') || window.location.hostname.includes('site2')) ? 'http://wsretro2.mayosolution.com/staff/login?user=bxia' : ((window.location.hostname.includes('ws3') || window.location.hostname.includes('site3')) ? 'http://wsretro3.mayosolution.com/staff/login?user=bxia': 'http://wsretro1.mayosolution.com/staff/login?user=leapon');