Building a Modern Employee Management System with React.js and Django
Posted: 6 months agoยทLast Updated: 3 months ago
Share on LinkedIn
Share on X
Share on Facebook
Share via Email
Share on WhatsApp
Share on Telegram
Print this page
Copy Link
Share on LinkedIn
Share on X
Share on Facebook
Share on WhatsApp
Share on Telegram
Share via Email
Copy Link
Efficiently managing employees is crucial for any organization's success. From tracking attendance and managing payroll to overseeing performance and ensuring effective communication, an employee management system (EMS) can significantly streamline these tasks.
In this article, we'll walk you through creating a robust and user-friendly Employee Management System using two powerful technologies: React.js for the front end and Django for the back end. React.js, a popular JavaScript library, allows us to build interactive and dynamic user interfaces, while Django, a high-level Python web framework, provides a solid and scalable foundation for the back end.
This guide will cover the essential CRUD (Create, Read, Update, Delete) functionalities, ensuring that our EMS can handle the fundamental operations needed for managing employee data. We'll also focus on creating a robust backend that can support these operations securely and efficiently.
It's important to note that this project is a demo and may not be fully optimized for user-friendliness. However, it serves as a comprehensive starting point for anyone looking to build a more sophisticated EMS tailored to their organization's specific needs.
Whether you're a seasoned developer looking to expand your tech stack or a novice eager to dive into full-stack development, this guide will offer insights and practical steps to help you build an EMS that can handle the complexities of modern workforce management. So, let's roll up our sleeves and start building a system that can make employee management more efficient, transparent, and enjoyable for everyone involved.
Run the development server to ensure everything is set up correctly:
python manage.py runserver
Open your browser and navigate to http://127.0.0.1:8000/. You should see the Django welcome page, indicating that your Django project is up and running.
Now, let's update the Django settings to configure paths, security, installed apps, middleware, templates, and logging. Here's an example of a comprehensive settings.py file for our Employee Management System:
BASE_DIR defines the base directory for the project.
MEDIA_URL and MEDIA_ROOT configure the media files directory for storing employee photos.
Security:
SECRET_KEY is generated and stored securely in a file to ensure it remains consistent across sessions.
Applications:
INSTALLED_APPS lists the installed applications, including rest_framework for building APIs and corsheaders for handling Cross-Origin Resource Sharing.
Middleware:
MIDDLEWARE includes necessary middleware components for handling security, sessions, authentication, and CORS.
URL Configuration:
ROOT_URLCONF points to the URL configuration module for routing.
Templates:
TEMPLATES configuration for rendering HTML templates.
WSGI:
WSGI_APPLICATION specifies the WSGI application used by Django's development server and any WSGI-compatible web server.
Database:
DATABASES configuration using SQLite for simplicity in this demo.
Password Validation:
AUTH_PASSWORD_VALIDATORS ensures password strength and security.
Internationalization:
Settings for language and timezone.
Static Files:
Configuration for serving static files.
Logging:
A simple logging configuration to log debug messages to a file named debug.log.
With the Django project set up, the next step is to create the necessary components for our Employee Management System. We will follow a structured approach to build models, views, serializers, tests, URLs, and admin configurations.
Create serializers for Departments and Employees in EmployeeApp/serializers.py
Serializers in Django REST Framework are used to convert complex data types, such as querysets and model instances, into native Python datatypes that can then be easily rendered into JSON, XML, or other content types. They also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.
# EmployeeApp/serializers.pyfrom rest_framework import serializers
from EmployeeApp.models import Departments, Employees
classDepartmentSerializer(serializers.ModelSerializer):classMeta: model = Departments
fields =('DepartmentId','DepartmentName')classEmployeeSerializer(serializers.ModelSerializer):classMeta: model = Employees
fields =('EmployeeId','EmployeeName','Department','DateOfJoining','PhotoFileName')
Meta Class: Contains metadata for the serializer.
model: Specifies the model to be serialized.
fields: Specifies the fields to be included in the serialization process.
Define the views for handling API requests in EmployeeApp/views.py
Views in Django REST Framework are used to handle the HTTP requests and return HTTP responses. They contain the logic for the various actions (CRUD operations) that can be performed on the data.
# EmployeeApp/views.pyfrom rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from.models import Departments, Employees
from.serializers import DepartmentSerializer, EmployeeSerializer
from django.core.files.storage import default_storage
from django.http import Http404
import logging
logger = logging.getLogger(__name__)@api_view(['GET','POST','PUT','DELETE'])defdepartmentApi(request,id=None):if request.method =='GET':ifid:try: department = Departments.objects.get(DepartmentId=id) logger.debug(f"GET request for department with id={id}")except Departments.DoesNotExist: logger.error(f"Department with id={id} not found")raise Http404("Department not found") serializer = DepartmentSerializer(department)return Response(serializer.data)else: departments = Departments.objects.all() logger.debug("GET request for all departments") serializer = DepartmentSerializer(departments, many=True)return Response(serializer.data)elif request.method =='POST': logger.debug(f"POST request with data={request.data}") serializer = DepartmentSerializer(data=request.data)if serializer.is_valid(): serializer.save() logger.debug(f"Department created: {serializer.data}")return Response(serializer.data, status=status.HTTP_201_CREATED) logger.error(f"Validation errors: {serializer.errors}")return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)elif request.method =='PUT':ifidisNone: logger.error("PUT request with id=None")return Response({"error":"Department id is required for update"}, status=status.HTTP_400_BAD_REQUEST)try: department = Departments.objects.get(DepartmentId=id) logger.debug(f"PUT request for department with id={id}")except Departments.DoesNotExist: logger.error(f"Department with id={id} not found")raise Http404("Department not found") serializer = DepartmentSerializer(department, data=request.data)if serializer.is_valid(): serializer.save() logger.debug(f"Department updated: {serializer.data}")return Response(serializer.data) logger.error(f"Validation errors: {serializer.errors}")return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)elif request.method =='DELETE':ifidisNone: logger.error("DELETE request with id=None")return Response({"error":"Department id is required for delete"}, status=status.HTTP_400_BAD_REQUEST)try: department = Departments.objects.get(DepartmentId=id) logger.debug(f"DELETE request for department with id={id}")except Departments.DoesNotExist: logger.error(f"Department with id={id} not found")raise Http404("Department not found") department.delete() logger.debug(f"Department with id={id} deleted")return Response(status=status.HTTP_204_NO_CONTENT)@api_view(['GET','POST','PUT','DELETE'])defemployeeApi(request,id=None):if request.method =='GET':ifid:try: employee = Employees.objects.get(EmployeeId=id) logger.debug(f"GET request for employee with id={id}")except Employees.DoesNotExist: logger.error(f"Employee with id={id} not found")raise Http404("Employee not found") serializer = EmployeeSerializer(employee)return Response(serializer.data)else: employees = Employees.objects.all() logger.debug("GET request for all employees") serializer = EmployeeSerializer(employees, many=True)return Response(serializer.data)elif request.method =='POST': logger.debug(f"POST request with data={request.data}") serializer = EmployeeSerializer(data=request.data)if serializer.is_valid(): serializer.save() logger.debug(f"Employee created: {serializer.data}")return Response(serializer.data, status=status.HTTP_201_CREATED) logger.error(f"Validation errors: {serializer.errors}")return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)elif request.method =='PUT':ifidisNone: logger.error("PUT request with id=None")return Response({"error":"Employee id is required for update"}, status=status.HTTP_400_BAD_REQUEST)try: employee = Employees.objects.get(EmployeeId=id) logger.debug(f"PUT request for employee with id={id}")except Employees.DoesNotExist: logger.error(f"Employee with id={id} not found")raise Http404("Employee not found") serializer = EmployeeSerializer(employee, data=request.data)if serializer.is_valid(): serializer.save() logger.debug(f"Employee updated: {serializer.data}")return Response(serializer.data) logger.error(f"Validation errors: {serializer.errors}")return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)elif request.method =='DELETE':ifidisNone: logger.error("DELETE request with id=None")return Response({"error":"Employee id is required for delete"}, status=status.HTTP_400_BAD_REQUEST)try: employee = Employees.objects.get(EmployeeId=id) logger.debug(f"DELETE request for employee with id={id}")except Employees.DoesNotExist: logger.error(f"Employee with id={id} not found")raise Http404("Employee not found") employee.delete() logger.debug(f"Employee with id={id} deleted")return Response(status=status.HTTP_204_NO_CONTENT)# Handles file upload requests. The uploaded file is saved using Django's default storage system, and the file name is returned in the response.@api_view(['POST'])defSaveFile(request):file= request.FILES['file'] logger.debug(f"File upload request: {file.name}") file_name = default_storage.save(file.name,file) logger.debug(f"File saved as: {file_name}")return Response(file_name, status=status.HTTP_200_OK)
GET: Retrieves a single department/employee if an id is provided, otherwise retrieves all departments.
POST: Creates a new department/employee with the provided data.
PUT: Updates an existing department/employee with the provided id and data.
DELETE: Deletes a department/employee with the provided id.
These views utilize the Django REST Framework to provide a RESTful API for the Employee Management System, allowing clients to perform CRUD operations on the Departments and Employees resources, as well as upload files. The logging statements help in tracking the flow of requests and debugging any issues that arise during API interactions.
The urls.py file in Django is used to map URL patterns to views. It determines what code is executed when a particular URL is accessed.
from django.urls import re_path as url
from EmployeeApp import views
from django.conf.urls.static import static
from django.conf import settings
urlpatterns =[ url(r'^department$', views.departmentApi, name='departmentApi'), url(r'^department/([0-9]+)$', views.departmentApi, name='departmentApi'), url(r'^employee$', views.employeeApi, name='employeeApi'), url(r'^employee/([0-9]+)$', views.employeeApi, name='employeeApi'), url(r'^employee/savefile$', views.SaveFile, name='saveFile')]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Maps the URL /department to the departmentApi view. This handles operations that do not require an ID (e.g., list all departments, create a new department).
Maps the URL /department/<id> to the departmentApi view. This handles operations that require an ID (e.g., retrieve, update, delete a specific department).
Serves media files during development by appending the necessary URL patterns to handle media files (e.g., photos).
Tests are used to ensure that the application works as expected. They help verify that the code performs the intended operations and handles various cases appropriately.
setUp: Initializes the test client, creates a sample department, and an employee.
test_get_all_departments: Tests retrieving all departments.
test_get_single_department: Tests retrieving a specific department by ID.
test_create_department: Tests creating a new department.
test_update_department: Tests updating an existing department.
test_delete_department: Tests deleting a department.
test_get_all_employees: Tests retrieving all employees.
test_get_single_employee: Tests retrieving a specific employee by ID.
test_create_employee: Tests creating a new employee.
test_update_employee: Tests updating an existing employee.
test_delete_employee: Tests deleting an employee.
tearDown: Closes the temporary test file after tests are run.
test_save_file: Tests uploading and saving a file.
These tests ensure the integrity and functionality of the API by verifying that the various endpoints perform the expected operations and handle different scenarios correctly.
By following the steps above, you have successfully set up a React application for the Employee Management System.
App.css
/* Add media queries for responsive navigation */@media(max-width:768px){.navbar-nav{display: none;/* Hide the nav items on small screens */}.navbar-nav.show{display: block;/* Show the nav items when toggled */}.navbar-toggler{display: block;/* Show the toggle button on small screens */}}.navbar-toggler{display: none;/* Hide the toggle button by default */background-color:#007bff;border: none;color:white;padding:0.5rem1rem;font-size:1.25rem;cursor: pointer;}.navbar-toggler:focus{outline: none;}.dropdown-menu{display: none;/* Hide dropdown menu by default */}.dropdown.show.dropdown-menu{display: block;/* Show dropdown menu when toggled */position: absolute;/* Ensure the dropdown is positioned correctly */top:100%;/* Align the dropdown menu with the bottom of the toggle button */left:0;/* Align the dropdown menu to the left */width:100%;/* Make sure the dropdown menu spans the width of the container */background-color:white;/* Set background color to white */box-shadow:04px8pxrgba(0,0,0,0.1);/* Add a subtle shadow for visibility */}
index.css
/* Ensure the background color covers the entire page */body,html{height:100%;margin:0;background-color:#f9f9f9;overflow: hidden;}@media(max-width:767px){.table th{font-size:12px;padding:5px;}.table td{font-size:12px;padding:5px;}}/* Responsive table */.table-responsive{display: block;width:100%;overflow-x: auto;-webkit-overflow-scrolling: touch;}.table{width:100%;max-width:100%;margin-bottom:1rem;background-color:transparent;}@media(max-width:768px){.table thead{display: none;}.table,.table tbody,.table tr,.table td{display: block;width:100%;}.table tr{margin-bottom:15px;}.table td{text-align: right;padding-left:50%;position: relative;}.table td::before{content:attr(data-label);position: absolute;left:0;width:50%;padding-left:15px;font-weight: bold;text-align: left;}}/* Home container to center content */.home-container{display: flex;flex-direction: column;align-items: center;justify-content: center;min-height:75vh;padding:20px;background-color:#f9f9f9;}.home-content{max-width:600px;margin:020px;/* Ensure some margin for smaller screens */}/* Centered text and margins */.text-center{text-align: center;}.my-4{margin:2rem0;}/* Responsive home content styling */.home-content{font-size:16px;line-height:1.6;max-width:600px;width:100%;padding:20px;background-color:white;border-radius:8px;box-shadow:02px4pxrgba(0,0,0,0.1);text-align: center;/* Center align text */}.home-content p{margin-bottom:1rem;}/* Quick links styling */.quick-links{list-style-type: none;padding:0;display: flex;justify-content: center;}.quick-links li{margin:010px;}.quick-links li a{color:#007bff;text-decoration: none;}.quick-links li a:hover{text-decoration: underline;}/* Responsive design for smaller screens */@media(max-width:600px){.quick-links{flex-direction: column;}.quick-links li{margin:10px0;}}
The App.js file is the main entry point of the React application. It sets up the router, navigation, and routes for the different components of the Employee Management System.
import"./App.css";importHomefrom"./Home";importDepartmentfrom"./Department";import{Employee}from"./Employee";import{ useState }from"react";import{BrowserRouterasRouter,Route,Routes,NavLink,}from"react-router-dom";functionApp(){const[isNavOpen, setIsNavOpen]=useState(false);consttoggleNav=()=>{setIsNavOpen(!isNavOpen);};return(<Router><divclassName="App container"><h3className="text-center my-4"> Welcome to Employee Management System
</h3><navclassName="navbar navbar-expand-sm bg-light navbar-light"><buttonclassName="navbar-toggler"onClick={toggleNav}> โฐ
</button><divclassName={`navbar-nav ${isNavOpen ?"show":""}`}><divclassName="dropdown"><buttonclassName="btn btn-light btn-outline-primary dropdown-toggle"onClick={toggleNav}> Menu
</button><divclassName={`dropdown-menu ${isNavOpen ?"show":""}`}><NavLinkclassName="dropdown-item"to="/"onClick={()=>setIsNavOpen(false)}> Home
</NavLink><NavLinkclassName="dropdown-item"to="/department"onClick={()=>setIsNavOpen(false)}> Department
</NavLink><NavLinkclassName="dropdown-item"to="/employee"onClick={()=>setIsNavOpen(false)}> Employee
</NavLink></div></div></div></nav><Routes><Routepath="/"element={<Home/>}/><Routepath="/department"element={<Department/>}/><Routepath="/employee"element={<Employee/>}/></Routes></div></Router>);}exportdefaultApp;
A collapsible navigation menu is created with NavLink components for Home, Department, and Employee pages.
The dropdown class and show class are conditionally applied based on isNavOpen state to handle the menu visibility.
The Home.js file represents the Home page of the application. It provides a brief overview and quick links to navigate to other sections.
importReactfrom"react";import"./index.css";constHome=()=>{return(<divclassName="home-container"><divclassName="home-content"><p> This is the home page of our React application. Here, you can find
various resources and links to other sections of the app. Explore the
navigation menu to visit the Departments and Employees sections.
</p><p> Our application is built using React, a popular JavaScript library for
building user interfaces. We have implemented various features to
provide a seamless experience for managing departments and employees.
</p><p>Below are some quick links to get you started:</p><ulclassName="quick-links"><li><ahref="/">Home</a></li><li><ahref="/department">Departments</a></li><li><ahref="/employee">Employees</a></li></ul></div></div>);};exportdefaultHome;
API_URL: This property is a string "http://127.0.0.1:8000/". It represents the base URL for an API endpoint. In this case, it points to a local development server running on 127.0.0.1 (localhost) at port 8000. This URL is typically used to communicate with a backend server to perform operations like fetching data, updating data, etc.
PHOTO_URL: This property is also a string "http://127.0.0.1:8000/Photos/". It represents the base URL for accessing photos or images served by the same local development server. This URL is used to construct the complete path to fetch or display images stored on the server.
Both Department and Employee components use the DraggableRow component to enable drag-and-drop functionality for table rows.
State Management: Uses React's useState to manage employee data, modal states, and image uploads.
CRUD Operations: Uses fetch API for Create, Read, Update, and Delete operations.
Drag and Drop: Uses react-dnd to enable drag-and-drop functionality for employee rows.
Image Upload: Handles image uploads for employee photos.
These components are designed to be modular and reusable, focusing on managing data with CRUD operations and enhancing user experience with sorting, filtering, and drag-and-drop features.
Remember, adaptability and clarity in your codebase not only benefit your current development efforts but also pave the way for smoother collaboration and growth in your software projects.
Meet Dennis, a seasoned software engineer with 10 years of experience transforming ideas into digital reality. He has successfully guided countless projects from concept to deployment, bringing innovative solutions to life. With a passion for crafting exceptional software, Dennis has helped countless clients achieve their goals.