Overview
SmartShelf implements a comprehensive user management system with role-based access control (RBAC). Administrators can create, update, and manage user accounts with three distinct roles, each having specific permissions and access levels.
Role-Based Access Control
The system supports three user roles with hierarchical permissions:
Admin Full System Access
All Manager permissions
User management (CRUD)
System configuration
Complete data access
Manager Operational Management
Inventory management
Task creation/assignment
Analytics & reports
FEFO & forecasting
Worker Task Execution
View inventory
View assigned tasks
Update task status
Limited dashboard
Permission Matrix
Detailed permission breakdown from README.md:
Feature Admin Manager Worker Dashboard Dashboard Overview ✅ ✅ ✅ Inventory View Inventory ✅ ✅ ✅ Add/Edit Inventory ✅ ✅ ❌ Delete Inventory ✅ ✅ ❌ User Management User Management ✅ ❌ ❌ Tasks Create Tasks ✅ ✅ ❌ View All Tasks ✅ ✅ ❌ View My Tasks - - ✅ Update Task Status ✅ ✅ ✅ (own) Analytics View Analytics ✅ ✅ ❌ FEFO Ordering ✅ ✅ ❌ Demand Forecast ✅ ✅ ❌
User CRUD Operations
Get All Users
Retrieve all users with filtering (userController.js:7-57):
GET / api / users ? role = Worker & isActive = true & search = john & page = 1 & limit = 10
Query Parameters:
role - Filter by role (Admin, Manager, Worker)
isActive - Filter by active status (true/false)
search - Search by name or email
page - Page number (default: 1)
limit - Items per page (default: 10)
Implementation:
exports . getAllUsers = async ( req , res ) => {
const query = {};
// Filter by role
if ( req . query . role ) {
query . role = req . query . role ;
}
// Filter by active status
if ( req . query . isActive !== undefined ) {
query . isActive = req . query . isActive === 'true' ;
}
// Search by name or email
if ( req . query . search ) {
query . $or = [
{ name: { $regex: req . query . search , $options: 'i' } },
{ email: { $regex: req . query . search , $options: 'i' } }
];
}
const users = await User . find ( query )
. select ( '-password' ) // Exclude password from response
. sort ({ createdAt: - 1 });
};
Response:
{
"users" : [
{
"id" : "673ab12c5f8e9a001234abcd" ,
"name" : "John Doe" ,
"email" : "[email protected] " ,
"role" : "Worker" ,
"isActive" : true ,
"createdAt" : "2025-11-15T10:30:00.000Z"
}
],
"pagination" : {
"currentPage" : 1 ,
"totalPages" : 3 ,
"totalUsers" : 25 ,
"limit" : 10
}
}
Create User
Admins can create new user accounts (userController.js:86-127):
Request Body:
{
"name" : "Jane Smith" ,
"email" : "[email protected] " ,
"password" : "SecurePass123!" ,
"role" : "Manager"
}
Implementation:
exports . createUser = async ( req , res ) => {
const { name , email , password , role } = req . body ;
// Validation
if ( ! name || ! email || ! password || ! role ) {
return sendErrorResponse ( res , 'Please provide all required fields' , 400 );
}
// Check if user already exists
const existingUser = await User . findOne ({ email: email . toLowerCase () });
if ( existingUser ) {
return sendErrorResponse ( res , 'User with this email already exists' , 400 );
}
// Create user
const user = await User . create ({
name ,
email: email . toLowerCase (),
password , // Automatically hashed by User model pre-save hook
role
});
// Return user without password
sendSuccessResponse ( res , 'User created successfully' , {
user: user . toSafeObject ()
}, 201 );
};
Validations:
All fields required (name, email, password, role)
Email must be unique
Email automatically converted to lowercase
Password automatically hashed using bcrypt
Role must be: Admin, Manager, or Worker
Passwords are automatically hashed using bcrypt before storage. Never store plain-text passwords.
Update User
Modify user account details (userController.js:132-181):
Request Body:
{
"name" : "Jane Smith Updated" ,
"email" : "[email protected] " ,
"role" : "Admin" ,
"isActive" : true
}
Implementation:
exports . updateUser = async ( req , res ) => {
const { name , email , role , isActive } = req . body ;
const user = await User . findById ( req . params . id );
if ( ! user ) {
return sendErrorResponse ( res , 'User not found' , 404 );
}
// Update fields
if ( name ) user . name = name ;
if ( role ) user . role = role ;
if ( typeof isActive !== 'undefined' ) user . isActive = isActive ;
if ( email ) {
// Check if email is already taken by another user
const existingUser = await User . findOne ({
email: email . toLowerCase (),
_id: { $ne: user . _id }
});
if ( existingUser ) {
return sendErrorResponse ( res , 'Email already in use' , 400 );
}
user . email = email . toLowerCase ();
}
await user . save ();
};
Features:
Partial updates supported
Email uniqueness validation
Cannot set duplicate email
Active status toggle (enable/disable user)
Role changes allowed
Delete User
Remove user accounts (userController.js:186-212):
Implementation:
exports . deleteUser = async ( req , res ) => {
const user = await User . findById ( req . params . id );
if ( ! user ) {
return sendErrorResponse ( res , 'User not found' , 404 );
}
// Prevent admin from deleting themselves
if ( user . _id . toString () === req . user . id ) {
return sendErrorResponse ( res , 'You cannot delete your own account' , 400 );
}
await user . deleteOne ();
};
Safety Features:
Admins cannot delete their own account
Deletion is permanent (no soft delete)
Associated tasks remain but show “Unknown” worker
Consider implementing soft delete (isActive: false) instead of permanent deletion to preserve audit trails.
User Management UI
User List Table
Comprehensive user management interface (UserManagementPage.tsx:110-254):
< table className = "w-full text-left" >
< thead >
< tr >
< th > Name </ th >
< th > Email </ th >
< th > Role </ th >
< th > Actions </ th >
</ tr >
</ thead >
< tbody >
{ users . map ( user => (
< tr key = {user. id } >
< td className = "font-medium" > {user. name } </ td >
< td className = "text-slate-500" > {user. email } </ td >
< td >
< span className = { getRoleBadgeColor (user.role)} >
{ user . role }
</ span >
</ td >
< td className = "flex space-x-2 justify-end" >
< button onClick = {() => handleEdit ( user )} >
< Edit className = "w-5 h-5" />
</ button >
< button onClick = {() => handleDelete (user.id)} >
< Delete className = "w-5 h-5" />
</ button >
</ td >
</ tr >
))}
</ tbody >
</ table >
Role Badge Colors
Color-coded role indicators:
const getRoleBadgeColor = ( role : string ) => {
if ( role === 'Admin' ) {
return 'bg-red-100 text-red-800 dark:bg-red-900/50 dark:text-red-300' ;
} else if ( role === 'Manager' ) {
return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/50 dark:text-yellow-300' ;
} else {
return 'bg-blue-100 text-blue-800 dark:bg-blue-900/50 dark:text-blue-300' ;
}
};
Modal form for user creation/editing (UserManagementPage.tsx:7-108):
const AddEditUserForm : React . FC <{
user : User | null ;
onSave : ( userData : any ) => void ;
onCancel : () => void ;
}> = ({ user , onSave , onCancel }) => {
const [ formData , setFormData ] = useState ({
name: user ?. name || '' ,
email: user ?. email || '' ,
role: user ?. role || 'Worker' ,
password: '' , // Only for new users
});
return (
< form onSubmit = { handleSubmit } >
< input type = "text" name = "name" required />
< input type = "email" name = "email" required />
{! user && (
< input
type = "password"
name = "password"
required = {! user }
minLength = { 6 }
placeholder = "Minimum 6 characters"
/>
)}
< select name = "role" >
< option value = "Admin" > Admin </ option >
< option value = "Manager" > Manager </ option >
< option value = "Worker" > Worker </ option >
</ select >
</ form >
);
};
Form Features:
Password field only shown for new users
Email validation
Minimum password length: 6 characters
Role dropdown with all three options
Loading states during save
User Statistics
Get user account statistics (userController.js:240-271):
Response:
{
"stats" : {
"totalUsers" : 45 ,
"activeUsers" : 42 ,
"inactiveUsers" : 3 ,
"byRole" : {
"Admin" : 2 ,
"Manager" : 8 ,
"Worker" : 35
}
}
}
Implementation:
exports . getUserStats = async ( req , res ) => {
const totalUsers = await User . countDocuments ();
const activeUsers = await User . countDocuments ({ isActive: true });
const inactiveUsers = await User . countDocuments ({ isActive: false });
const usersByRole = await User . aggregate ([
{
$group: {
_id: '$role' ,
count: { $sum: 1 }
}
}
]);
const stats = {
totalUsers ,
activeUsers ,
inactiveUsers ,
byRole: usersByRole . reduce (( acc , item ) => {
acc [ item . _id ] = item . count ;
return acc ;
}, {})
};
};
Authentication Integration
User management integrates with JWT authentication:
Password Security
Passwords are hashed using bcrypt:
// User model pre-save hook
userSchema . pre ( 'save' , async function ( next ) {
if ( ! this . isModified ( 'password' )) return next ();
const salt = await bcrypt . genSalt ( 10 );
this . password = await bcrypt . hash ( this . password , salt );
next ();
});
Safe User Object
Exclude password from responses:
userSchema . methods . toSafeObject = function () {
const obj = this . toObject ();
delete obj . password ;
return obj ;
};
Access Control Middleware
Role-based route protection:
// Admin-only routes
router . get ( '/users' , authMiddleware , roleMiddleware ([ 'Admin' ]), getAllUsers );
router . post ( '/users' , authMiddleware , roleMiddleware ([ 'Admin' ]), createUser );
// Manager/Admin routes
router . post ( '/tasks' , authMiddleware , roleMiddleware ([ 'Manager' , 'Admin' ]), createTask );
// All authenticated users
router . get ( '/inventory' , authMiddleware , getAllInventory );
Active Status Management
Enable/Disable Users
Toggle user active status without deletion:
// Disable user
PUT / api / users / : id
{
"isActive" : false
}
// Enable user
PUT / api / users / : id
{
"isActive" : true
}
Effects of Disabled Status:
User cannot log in
Existing sessions invalidated
Cannot be assigned new tasks
Existing task assignments remain
User data preserved for audit trails
Best Practices
Minimum 6 characters (consider increasing to 8+)
Encourage strong passwords with:
Uppercase and lowercase letters
Numbers
Special characters
Implement password complexity validation
Consider password expiration policies
Onboarding:
Create user account with Worker role
Send welcome email with login credentials
Assign initial training tasks
Monitor first-week activity
Offboarding:
Disable account (set isActive: false)
Reassign active tasks to other workers
Archive user data for compliance
Keep account for audit purposes
Role Assignment Guidelines
Start new users as Workers
Promote to Manager after proven competence
Limit Admin accounts to 2-3 key personnel
Regular review of role assignments
Document role change reasons
Never share Admin credentials
Regularly audit user access logs
Disable inactive accounts after 90 days
Implement two-factor authentication (future)
Monitor for suspicious login attempts
User Model Schema
const userSchema = new mongoose . Schema ({
name: {
type: String ,
required: true ,
trim: true
},
email: {
type: String ,
required: true ,
unique: true ,
lowercase: true ,
trim: true
},
password: {
type: String ,
required: true ,
minlength: 6
},
role: {
type: String ,
enum: [ 'Admin' , 'Manager' , 'Worker' ],
default: 'Worker'
},
isActive: {
type: Boolean ,
default: true
}
}, {
timestamps: true // Adds createdAt and updatedAt
});
API Summary
Method Endpoint Description Access GET /api/usersGet all users Admin GET /api/users/:idGet user by ID Admin POST /api/usersCreate user Admin PUT /api/users/:idUpdate user Admin DELETE /api/users/:idDelete user Admin GET /api/users/workersGet active workers Manager/Admin GET /api/users/statsGet user statistics Admin
Task Management Assign tasks to workers
Analytics Dashboard Role-based dashboards
Authentication Login and JWT tokens
User Management API User endpoints and permissions