Back to Articles

Building a Modern Employee Management System with React.js and Django

Posted: 4 months agoยทLast Updated: 1 month ago
Blog Cover
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.

Image

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.

Setting Up the Django Backend

Before we dive into coding, we need to set up our development environment and create the Django project. Follow these steps to get started:

1. Install Python and Virtual Environment

First, ensure you have Python installed on your machine. You can download it from the official Python website.

Next, install virtualenv to create an isolated Python environment for your project:

pip install virtualenv

2. Create and Activate a Virtual Environment

Create a new directory for your project and navigate into it:

mkdir employee_management_system
cd employee_management_system

Create and activate a virtual environment:

python -m venv venv
source venv/bin/activate  # On Windows use `venv\Scripts\activate`

You should now see (venv) at the beginning of your command prompt, indicating that the virtual environment is active.

3. Install Django

With the virtual environment activated, install Django:

pip install django
pip install djangorestframework
pip install django-cors-headers

4. Create a New Django Project

Create a new Django project called DjangoAPI:

django-admin startproject DjangoAPI
cd DjangoAPI

5. Start the Development Server

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:

from pathlib import Path
import os
import secrets

# Paths
BASE_DIR = Path(__file__).resolve(strict=True).parent.parent
MEDIA_URL = '/Photos/'
MEDIA_ROOT = BASE_DIR / "Photos"

# Security
SECRET_KEY_FILE = BASE_DIR / 'secret_key.txt'

if SECRET_KEY_FILE.exists():
    with open(SECRET_KEY_FILE) as f:
        SECRET_KEY = f.read().strip()
else:
    SECRET_KEY = secrets.token_urlsafe(50)
    with open(SECRET_KEY_FILE, 'w') as f:
        f.write(SECRET_KEY)

DEBUG = True
ALLOWED_HOSTS = []

# Applications
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'corsheaders',
    'EmployeeApp.apps.EmployeeappConfig'
]

CORS_ORIGIN_ALLOW_ALL = True

# Middleware
MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

# URL Configuration
ROOT_URLCONF = 'DjangoAPI.urls'

# Templates
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

# WSGI
WSGI_APPLICATION = 'DjangoAPI.wsgi.application'

# Database
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

# Password Validation
AUTH_PASSWORD_VALIDATORS = [
    {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
    {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
    {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
    {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
]

# Internationalization
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True

# Static Files
STATIC_URL = '/static/'

# Default Primary Key Field Type
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

# Logging
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': 'debug.log',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

Explanation:

  1. Paths:
    • BASE_DIR defines the base directory for the project.
    • MEDIA_URL and MEDIA_ROOT configure the media files directory for storing employee photos.
  2. Security:
    • SECRET_KEY is generated and stored securely in a file to ensure it remains consistent across sessions.
  3. Applications:
    • INSTALLED_APPS lists the installed applications, including rest_framework for building APIs and corsheaders for handling Cross-Origin Resource Sharing.
  4. Middleware:
    • MIDDLEWARE includes necessary middleware components for handling security, sessions, authentication, and CORS.
  5. URL Configuration:
    • ROOT_URLCONF points to the URL configuration module for routing.
  6. Templates:
    • TEMPLATES configuration for rendering HTML templates.
  7. WSGI:
    • WSGI_APPLICATION specifies the WSGI application used by Django's development server and any WSGI-compatible web server.
  8. Database:
    • DATABASES configuration using SQLite for simplicity in this demo.
  9. Password Validation:
    • AUTH_PASSWORD_VALIDATORS ensures password strength and security.
  10. Internationalization:
    • Settings for language and timezone.
  11. Static Files:
    • Configuration for serving static files.
  12. 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.

1. Create the EmployeeApp Application

First, create a new Django application called EmployeeApp:

python manage.py startapp EmployeeApp

Add EmployeeApp to the INSTALLED_APPS list in settings.py:

# DjangoAPI/settings.py
INSTALLED_APPS = [
    ...
    'rest_framework',
    'corsheaders',
    'EmployeeApp',
]

Open EmployeeApp/models.py and define the Employee model

Models in Django are used to define the structure of the database tables. They represent the data and the logic that revolves around the data.

# EmployeeApp/models.py
from django.db import models

class Departments(models.Model):
    DepartmentId = models.AutoField(primary_key=True)
    DepartmentName = models.CharField(max_length=500)

    def __str__(self):
        return self.DepartmentName

class Employees(models.Model):
    EmployeeId = models.AutoField(primary_key=True)
    EmployeeName = models.CharField(max_length=500)
    Department = models.CharField(max_length=500)
    DateOfJoining = models.DateField()
    PhotoFileName = models.CharField(max_length=500)

    def __str__(self):
        return self.EmployeeName
  • DepartmentId: An auto-incrementing integer field that serves as the primary key.
  • DepartmentName: A character field with a maximum length of 500 characters, used to store the name of the department.
  • EmployeeId: An auto-incrementing integer field that serves as the primary key.
  • EmployeeName: A character field with a maximum length of 500 characters, used to store the name of the employee.
  • Department: A character field with a maximum length of 500 characters, used to store the department name of the employee.
  • DateOfJoining: A date field used to store the date the employee joined the company.
  • PhotoFileName: A character field with a maximum length of 500 characters, used to store the file name of the employee's photo.

Run the following commands to create and apply migrations:

python manage.py makemigrations EmployeeApp
python manage.py migrate

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.py
from rest_framework import serializers
from EmployeeApp.models import Departments, Employees

class DepartmentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Departments
        fields = ('DepartmentId', 'DepartmentName')

class EmployeeSerializer(serializers.ModelSerializer):
    class Meta:
        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.py

from 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'])
def departmentApi(request, id=None):
    if request.method == 'GET':
        if id:
            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':
        if id is None:
            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':
        if id is None:
            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'])
def employeeApi(request, id=None):
    if request.method == 'GET':
        if id:
            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':
        if id is None:
            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':
        if id is None:
            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'])
def SaveFile(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)

Department URLs

  • url(r'^department$', views.departmentApi, name='departmentApi')
    • 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).
  • url(r'^department/([0-9]+)$', views.departmentApi, name='departmentApi')
    • Maps the URL /department/<id> to the departmentApi view. This handles operations that require an ID (e.g., retrieve, update, delete a specific department).

Employee URLs

  • url(r'^employee$', views.employeeApi, name='employeeApi')
    • Maps the URL /employee to the employeeApi view. This handles operations that do not require an ID (e.g., list all employees, create a new employee).
  • url(r'^employee/([0-9]+)$', views.employeeApi, name='employeeApi')
    • Maps the URL /employee/<id> to the employeeApi view. This handles operations that require an ID (e.g., retrieve, update, delete a specific employee).

File Upload URL

  • url(r'^employee/savefile$', views.SaveFile, name='saveFile')
    • Maps the URL /employee/savefile to the SaveFile view. This handles file upload operations.

Static Media Files

  • + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
    • 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.

from django.test import TestCase, Client
from django.urls import reverse
from rest_framework import status
from .models import Departments, Employees
import json
import tempfile


class DepartmentApiTests(TestCase):

    def setUp(self):
        self.client = Client()
        self.department = Departments.objects.create(DepartmentName="Finance")

    def test_get_all_departments(self):
        response = self.client.get(reverse('departmentApi'))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_get_single_department(self):
        response = self.client.get(
            reverse('departmentApi', args=[self.department.DepartmentId]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_create_department(self):
        data = {"DepartmentName": "Marketing"}
        response = self.client.post(reverse('departmentApi'), data=json.dumps(
            data), content_type='application/json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

    def test_update_department(self):
        data = {"DepartmentName": "Human Resources"}
        response = self.client.put(reverse('departmentApi', args=[
                                   self.department.DepartmentId]), data=json.dumps(data), content_type='application/json')
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_delete_department(self):
        response = self.client.delete(
            reverse('departmentApi', args=[self.department.DepartmentId]))
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)


class EmployeeApiTests(TestCase):

    def setUp(self):
        self.client = Client()
        self.department = Departments.objects.create(DepartmentName="IT")
        self.employee = Employees.objects.create(
            EmployeeName="John Doe", Department=self.department.DepartmentName, DateOfJoining="2022-01-01", PhotoFileName="john_doe.jpg")

    def test_get_all_employees(self):
        response = self.client.get(reverse('employeeApi'))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_get_single_employee(self):
        response = self.client.get(
            reverse('employeeApi', args=[self.employee.EmployeeId]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_create_employee(self):
        data = {"EmployeeName": "Jane Doe", "Department": "IT",
                "DateOfJoining": "2022-01-01", "PhotoFileName": "jane_doe.jpg"}
        response = self.client.post(reverse('employeeApi'), data=json.dumps(
            data), content_type='application/json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

    def test_update_employee(self):
        data = {"EmployeeName": "John Smith", "Department": "IT",
                "DateOfJoining": "2022-01-01", "PhotoFileName": "john_smith.jpg"}
        response = self.client.put(reverse('employeeApi', args=[
                                   self.employee.EmployeeId]), data=json.dumps(data), content_type='application/json')
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_delete_employee(self):
        response = self.client.delete(
            reverse('employeeApi', args=[self.employee.EmployeeId]))
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)


class SaveFileTests(TestCase):

    def setUp(self):
        self.client = Client()
        self.test_file = tempfile.NamedTemporaryFile(suffix=".jpg")

    def tearDown(self):
        self.test_file.close()

    def test_save_file(self):
        with open(self.test_file.name, 'rb') as testfile:
            response = self.client.post(
                reverse('saveFile'), {'file': testfile})
        self.assertEqual(response.status_code, status.HTTP_200_OK)
  • 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.

Setting Up the React Application for the Employee Management System

Step 1: Install Node.js and npm

Ensure you have Node.js and npm installed on your machine. You can download and install them from nodejs.org.

Step 2: Create a New React Application

Open your terminal or command prompt and run the following command to create a new React application using Create React App:

npx create-react-app my-app

Step 3: Navigate to the React Application Directory

cd ui/my-app

Step 4: Install Dependencies

npm install

Step 5: Current Folder Structure Overview

ui/my-app/
|-- public/
|   |-- index.html
|-- src/
|   |-- components/
|   |-- services/
|   |-- App.js
|   |-- index.js
|-- package.json

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.5rem 1rem;
  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: 0 4px 8px rgba(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: 0 20px; /* Ensure some margin for smaller screens */
}

/* Centered text and margins */
.text-center {
  text-align: center;
}

.my-4 {
  margin: 2rem 0;
}

/* 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: 0 2px 4px rgba(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: 0 10px;
}

.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: 10px 0;
  }
}

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";
import Home from "./Home";
import Department from "./Department";
import { Employee } from "./Employee";
import { useState } from "react";
import {
  BrowserRouter as Router,
  Route, Routes,
  NavLink,
} from "react-router-dom";

function App() {
  const [isNavOpen, setIsNavOpen] = useState(false);

  const toggleNav = () => {
    setIsNavOpen(!isNavOpen);
  };

  return (
    <Router>
      <div className="App container">
        <h3 className="text-center my-4">
          Welcome to Employee Management System
        </h3>

        <nav className="navbar navbar-expand-sm bg-light navbar-light">
          <button className="navbar-toggler" onClick={toggleNav}>
            โ˜ฐ
          </button>
          <div className={`navbar-nav ${isNavOpen ? "show" : ""}`}>
            <div className="dropdown">
              <button
                className="btn btn-light btn-outline-primary dropdown-toggle"
                onClick={toggleNav}
              >
                Menu
              </button>
              <div className={`dropdown-menu ${isNavOpen ? "show" : ""}`}>
                <NavLink
                  className="dropdown-item"
                  to="/"
                  onClick={() => setIsNavOpen(false)}
                >
                  Home
                </NavLink>
                <NavLink
                  className="dropdown-item"
                  to="/department"
                  onClick={() => setIsNavOpen(false)}
                >
                  Department
                </NavLink>
                <NavLink
                  className="dropdown-item"
                  to="/employee"
                  onClick={() => setIsNavOpen(false)}
                >
                  Employee
                </NavLink>
              </div>
            </div>
          </div>
        </nav>

        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/department" element={<Department />} />
          <Route path="/employee" element={<Employee />} />
        </Routes>
      </div>
    </Router>
  );
}

export default App;

State Management:

  • const [isNavOpen, setIsNavOpen] = useState(false); manages the state for the navigation menu toggle.
  • toggleNav function toggles the navigation menu's open/close state.

Router Setup:

  • BrowserRouter as Router is used to wrap the application and enable routing.
  • Routes contains all the route definitions for the application.
  • Route elements define the component to be rendered for each path ("/", "/department", "/employee").

Navigation Menu:

  • 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.

import React from "react";
import "./index.css";

const Home = () => {
  return (
    <div className="home-container">
      <div className="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>
        <ul className="quick-links">
          <li>
            <a href="/">Home</a>
          </li>
          <li>
            <a href="/department">Departments</a>
          </li>
          <li>
            <a href="/employee">Employees</a>
          </li>
        </ul>
      </div>
    </div>
  );
};

export default Home;

Structure:

  • The Home component is a functional component that returns a simple layout with some text and links.
  • It uses a div with class home-container for the main container and home-content for content styling.

Content:

  • The component provides a brief description of the application and its features.
  • It includes a list of quick links to navigate to the Home, Departments, and Employees sections.

Styling:

  • The component imports a CSS file ("./index.css") for styling purposes.
  • It uses classes (home-container, home-content, and quick-links) to apply styles defined in the CSS file.

We have two main components:

  • Department - Manages the department data.
  • Employee - Manages the employee data.

Both components have similar functionalities:

  • Fetching data from an API.
  • Displaying the data in a table.
  • Providing CRUD operations (Create, Read, Update, Delete).
  • Implementing drag-and-drop functionality for reordering items.

Defining a JavaScript object named variables using ES6 syntax.

export const variables = {
  API_URL: "http://127.0.0.1:8000/",
  PHOTO_URL: "http://127.0.0.1:8000/Photos/",
};

The variables object has two properties:

  • 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.

import React from "react";
import { useDrag, useDrop } from "react-dnd";

const ItemType = "ROW";

const DraggableRow = ({ id, index, moveRow, children }) => {
  const ref = React.useRef(null);

  const [, drop] = useDrop({
    accept: ItemType,
    hover(item, monitor) {
      if (!ref.current) {
        return;
      }
      const dragIndex = item.index;
      const hoverIndex = index;

      if (dragIndex === hoverIndex) {
        return;
      }

      const hoverBoundingRect = ref.current.getBoundingClientRect();
      const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
      const clientOffset = monitor.getClientOffset();
      const hoverClientY = clientOffset.y - hoverBoundingRect.top;

      if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
        return;
      }

      if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
        return;
      }

      moveRow(dragIndex, hoverIndex);
      item.index = hoverIndex;
    },
  });

  const [{ isDragging }, drag] = useDrag({
    type: ItemType,
    item: { type: ItemType, id, index },
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
    }),
  });

  const opacity = isDragging ? 0.5 : 1;
  drag(drop(ref));

  return (
    <tr ref={ref} style={{ opacity }}>
      {children}
    </tr>
  );
};

export default DraggableRow;

Key Concepts:

  • useDrag: Allows an element to be draggable.
  • useDrop: Allows an element to be a drop target.
  • Drag and Drop Logic: Handles the reordering of rows when dragged and dropped.
import React, { useState, useEffect, useCallback } from "react";
import { variables } from "./Variables.js";
import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import "./index.css";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faEdit, faTrash } from '@fortawesome/free-solid-svg-icons';
import DraggableRow from "./DraggableRow"; // Import the DraggableRow component

const ItemType = "DEPARTMENT";

const Department = () => {
  const [departments, setDepartments] = useState([]);
  const [employees, setEmployees] = useState([]);
  const [modalTitle, setModalTitle] = useState("");
  const [departmentName, setDepartmentName] = useState("");
  const [departmentId, setDepartmentId] = useState(0);
  const [departmentIdFilter, setDepartmentIdFilter] = useState("");
  const [departmentNameFilter, setDepartmentNameFilter] = useState("");
  const [departmentsWithoutFilter, setDepartmentsWithoutFilter] = useState([]);

  useEffect(() => {
    refreshList();
  }, []);

  const refreshList = async () => {
    try {
      const depResponse = await fetch(variables.API_URL + "department");
      const depData = await depResponse.json();
      setDepartments(depData);
      setDepartmentsWithoutFilter(depData);

      const empResponse = await fetch(variables.API_URL + "employee");
      const empData = await empResponse.json();
      setEmployees(empData);
    } catch (error) {
      console.error("Failed to fetch data:", error);
    }
  };

  const filterFn = () => {
    const filteredData = departmentsWithoutFilter.filter(
      (el) =>
        el.DepartmentId.toString().toLowerCase().includes(departmentIdFilter.toLowerCase().trim()) &&
        el.DepartmentName.toString().toLowerCase().includes(departmentNameFilter.toLowerCase().trim())
    );
    setDepartments(filteredData);
  };

  const sortResult = (prop, asc) => {
    const sortedData = [...departmentsWithoutFilter].sort((a, b) => {
      if (asc) return a[prop] > b[prop] ? 1 : a[prop] < b[prop] ? -1 : 0;
      return b[prop] > a[prop] ? 1 : b[prop] < a[prop] ? -1 : 0;
    });
    setDepartments(sortedData);
  };

  const handleAddClick = () => {
    setModalTitle("Add Department");
    setDepartmentId(0);
    setDepartmentName("");
  };

  const handleEditClick = (dep) => {
    setModalTitle("Edit Department");
    setDepartmentId(dep.DepartmentId);
    setDepartmentName(dep.DepartmentName);
  };

  const handleCreateClick = async () => {
    try {
      const response = await fetch(variables.API_URL + "department", {
        method: "POST",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ DepartmentName: departmentName }),
      });
      const result = await response.json();
      alert(result);
      refreshList();
    } catch (error) {
      alert("Failed");
    }
  };

  const handleUpdateClick = async () => {
    try {
      const response = await fetch(variables.API_URL + "department/" + departmentId, {
        method: "PUT",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          DepartmentId: departmentId,
          DepartmentName: departmentName,
        }),
      });
      const result = await response.json();
      document.getElementById('modalCloseButton').click();
      refreshList();
    } catch (error) {
      alert("Failed");
    }
  };

  const handleDeleteClick = async (id) => {
    if (window.confirm("Are you sure?")) {
      try {
        const response = await fetch(variables.API_URL + "department/" + id, {
          method: "DELETE",
          headers: {
            Accept: "application/json",
            "Content-Type": "application/json",
          },
        });
        if (response.status === 204) {
          alert("Department deleted successfully");
        } else {
          const result = await response.json();
          alert(result);
        }
        refreshList();
      } catch (error) {
        alert("Failed");
      }
    }
  };

  const handleFilterChange = (e) => {
    const { name, value } = e.target;
    if (name === "departmentIdFilter") {
      setDepartmentIdFilter(value);
    } else if (name === "departmentNameFilter") {
      setDepartmentNameFilter(value);
    }
    filterFn();
  };

  const moveRow = useCallback(
    (dragIndex, hoverIndex) => {
      const dragRow = departments[dragIndex];
      const updatedDepartments = [...departments];
      updatedDepartments.splice(dragIndex, 1);
      updatedDepartments.splice(hoverIndex, 0, dragRow);
      setDepartments(updatedDepartments);
    },
    [departments]
  );

  return (
    <div>
      <DndProvider backend={HTML5Backend}>
        <button
          type="button"
          className="btn btn-primary m-2 float-end"
          data-bs-toggle="modal"
          data-bs-target="#exampleModal"
          onClick={handleAddClick}
        >
          Add Department
        </button>
        <div className="table-responsive">
          <table className="table table-striped">
            <thead>
              <tr>
                <th>
                  <div className="d-flex flex-row">
                    <input
                      className="form-control m-2"
                      onChange={handleFilterChange}
                      name="departmentIdFilter"
                      placeholder="Filter"
                    />
                    <button
                      type="button"
                      className="btn btn-light"
                      onClick={() => sortResult("DepartmentId", true)}
                    >
                      โ–ฒ
                    </button>
                    <button
                      type="button"
                      className="btn btn-light"
                      onClick={() => sortResult("DepartmentId", false)}
                    >
                      โ–ผ
                    </button>
                  </div>
                  DepartmentId
                </th>
                <th>
                  <div className="d-flex flex-row">
                    <input
                      className="form-control m-2"
                      onChange={handleFilterChange}
                      name="departmentNameFilter"
                      placeholder="Filter"
                    />
                    <button
                      type="button"
                      className="btn btn-light"
                      onClick={() => sortResult("DepartmentName", true)}
                    >
                      โ–ฒ
                    </button>
                    <button
                      type="button"
                      className="btn btn-light"
                      onClick={() => sortResult("DepartmentName", false)}
                    >
                      โ–ผ
                    </button>
                  </div>
                  DepartmentName
                </th>
                <th>Options</th>
              </tr>
            </thead>
            <tbody>
              {departments.map((dep, index) => (
                <DraggableRow
                  key={dep.DepartmentId}
                  id={dep.DepartmentId}
                  index={index}
                  moveRow={moveRow}
                >
                  <td>{dep.DepartmentId}</td>
                  <td>{dep.DepartmentName}</td>
                  <td>
                    <button
                      type="button"
                      className="btn btn-light mr-1"
                      data-bs-toggle="modal"
                      data-bs-target="#exampleModal"
                      onClick={() => handleEditClick(dep)}
                    >
                      <FontAwesomeIcon icon={faEdit} />
                    </button>
                    <button
                      type="button"
                      className="btn btn-light mr-1"
                      onClick={() => handleDeleteClick(dep.DepartmentId)}
                    >
                      <FontAwesomeIcon icon={faTrash} />
                    </button>
                  </td>
                </DraggableRow>
              ))}
            </tbody>
          </table>
        </div>
      </DndProvider>
      <div
        className="modal fade"
        id="exampleModal"
        tabIndex="-1"
        aria-labelledby="exampleModalLabel"
        aria-hidden="true"
      >
        <div className="modal-dialog modal-lg modal-dialog-centered">
          <div className="modal-content">
            <div className="modal-header">
              <h5 className="modal-title" id="exampleModalLabel">
                {modalTitle}
              </h5>
              <button
                type="button"
                className="btn-close"
                data-bs-dismiss="modal"
                aria-label="Close"
                id="modalCloseButton"
              ></button>
            </div>
            <div className="modal-body">
              <div className="input-group mb-3">
                <span className="input-group-text">Department Name</span>
                <input
                  type="text"
                  className="form-control"
                  value={departmentName}
                  onChange={(e) => setDepartmentName(e.target.value)}
                />
              </div>
              {departmentId === 0 ? (
                <button
                  type="button"
                  className="btn btn-primary float-start"
                  onClick={handleCreateClick}
                >
                  Create
                </button>
              ) : (
                <button
                  type="button"
                  className="btn btn-primary float-start"
                  onClick={handleUpdateClick}
                >
                  Update
                </button>
              )}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default Department;

Key Concepts:

  • State Management: Uses React's useState to manage department data, modal states, and filters.
  • CRUD Operations: Uses fetch API for Create, Read, Update, and Delete operations.
  • Filtering and Sorting: Implements filtering and sorting for department data.
  • Drag and Drop: Uses react-dnd to enable drag-and-drop functionality for department rows.

The Employee component has similar functionality to the Department component but is focused on managing employee data.

import React, { useState, useEffect, useCallback } from "react";
import { variables } from "./Variables.js";
import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import "./index.css";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faEdit, faTrash } from '@fortawesome/free-solid-svg-icons';
import DraggableRow from "./DraggableRow"; // Import the DraggableRow component

const ItemType = "EMPLOYEE";

const Employee = () => {
  const [employees, setEmployees] = useState([]);
  const [departments, setDepartments] = useState([]);
  const [modalTitle, setModalTitle] = useState("");
  const [employeeId, setEmployeeId] = useState(0);
  const [employeeName, setEmployeeName] = useState("");
  const [department, setDepartment] = useState("");
  const [dateOfJoining, setDateOfJoining] = useState("");
  const [photoFileName, setPhotoFileName] = useState("anonymous.png");
  const [photoPath, setPhotoPath] = useState(variables.PHOTO_URL);

  useEffect(() => {
    refreshList();
  }, []);

  const refreshList = async () => {
    try {
      const empResponse = await fetch(variables.API_URL + "employee");
      const empData = await empResponse.json();
      setEmployees(empData);

      const depResponse = await fetch(variables.API_URL + "department");
      const depData = await depResponse.json();
      setDepartments(depData);
    } catch (error) {
      console.error("Failed to fetch data:", error);
    }
  };

  const handleAddClick = () => {
    setModalTitle("Add Employee");
    setEmployeeId(0);
    setEmployeeName("");
    setDepartment("");
    setDateOfJoining("");
    setPhotoFileName("anonymous.png");
  };

  const handleEditClick = (emp) => {
    setModalTitle("Edit Employee");
    setEmployeeId(emp.EmployeeId);
    setEmployeeName(emp.EmployeeName);
    setDepartment(emp.Department);
    setDateOfJoining(emp.DateOfJoining);
    setPhotoFileName(emp.PhotoFileName);
  };

  const handleCreateClick = async () => {
    try {
      const response = await fetch(variables.API_URL + "employee", {
        method: "POST",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          EmployeeName: employeeName,
          Department: department,
          DateOfJoining: dateOfJoining,
          PhotoFileName: photoFileName,
        }),
      });
      const result = await response.json();
      alert(result);
      refreshList();
    } catch (error) {
      alert("Failed");
    }
  };

  const handleUpdateClick = async () => {
    try {
      const response = await fetch(variables.API_URL + "employee", {
        method: "PUT",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          EmployeeId: employeeId,
          EmployeeName: employeeName,
          Department: department,
          DateOfJoining: dateOfJoining,
          PhotoFileName: photoFileName,
        }),
      });
      const result = await response.json();
      document.getElementById('modalCloseButton').click();
      refreshList();
    } catch (error) {
      alert("Failed");
    }
  };

  const handleDeleteClick = async (id) => {
    if (window.confirm("Are you sure?")) {
      try {
        const response = await fetch(variables.API_URL + "employee/" + id, {
          method: "DELETE",
          headers: {
            Accept: "application/json",
            "Content-Type": "application/json",
          },
        });
        if (response.status === 204) {
          alert("Employee deleted successfully");
        } else {
          const result = await response.json();
          alert(result);
        }
        refreshList();
      } catch (error) {
        alert("Failed");
      }
    }
  };

  const handleImageUpload = async (e) => {
    e.preventDefault();
    const formData = new FormData();
    formData.append("file", e.target.files[0]);

    try {
      const response = await fetch(variables.API_URL + "employee/savefile", {
        method: "POST",
        body: formData,
      });
      const data = await response.json();
      setPhotoFileName(data);
    } catch (error) {
      alert("Failed to upload image");
    }
  };

  const moveRow = useCallback(
    (dragIndex, hoverIndex) => {
      const dragRow = employees[dragIndex];
      const updatedEmployees = [...employees];
      updatedEmployees.splice(dragIndex, 1);
      updatedEmployees.splice(hoverIndex, 0, dragRow);
      setEmployees(updatedEmployees);
    },
    [employees]
  );

  return (
    <div>
      <DndProvider backend={HTML5Backend}>
        <button
          type="button"
          className="btn btn-primary m-2 float-end"
          data-bs-toggle="modal"
          data-bs-target="#exampleModal"
          onClick={handleAddClick}
        >
          Add Employee
        </button>
        <div className="table-responsive">
          <table className="table table-striped">
            <thead>
              <tr>
                <th>EmployeeId</th>
                <th>EmployeeName</th>
                <th>Department</th>
                <th>DateOfJoining</th>
                <th>PhotoFileName</th>
                <th>Options</th>
              </tr>
            </thead>
            <tbody>
              {employees.map((emp, index) => (
                <DraggableRow
                  key={emp.EmployeeId}
                  id={emp.EmployeeId}
                  index={index}
                  moveRow={moveRow}
                >
                  <td>{emp.EmployeeId}</td>
                  <td>{emp.EmployeeName}</td>
                  <td>{emp.Department}</td>
                  <td>{emp.DateOfJoining}</td>
                  <td>
                    <img
                      width="50px"
                      height="50px"
                      src={photoPath + emp.PhotoFileName}
                      alt={emp.EmployeeName}
                    />
                  </td>
                  <td>
                    <button
                      type="button"
                      className="btn btn-light mr-1"
                      data-bs-toggle="modal"
                      data-bs-target="#exampleModal"
                      onClick={() => handleEditClick(emp)}
                    >
                      <FontAwesomeIcon icon={faEdit} />
                    </button>
                    <button
                      type="button"
                      className="btn btn-light mr-1"
                      onClick={() => handleDeleteClick(emp.EmployeeId)}
                    >
                      <FontAwesomeIcon icon={faTrash} />
                    </button>
                  </td>
                </DraggableRow>
              ))}
            </tbody>
          </table>
        </div>
      </DndProvider>
      <div
        className="modal fade"
        id="exampleModal"
        tabIndex="-1"
        aria-labelledby="exampleModalLabel"
        aria-hidden="true"
      >
        <div className="modal-dialog modal-lg modal-dialog-centered">
          <div className="modal-content">
            <div className="modal-header">
              <h5 className="modal-title" id="exampleModalLabel">
                {modalTitle}
              </h5>
              <button
                type="button"
                className="btn-close"
                data-bs-dismiss="modal"
                aria-label="Close"
                id="modalCloseButton"
              ></button>
            </div>
            <div className="modal-body">
              <div className="input-group mb-3">
                <span className="input-group-text">Employee Name</span>
                <input
                  type="text"
                  className="form-control"
                  value={employeeName}
                  onChange={(e) => setEmployeeName(e.target.value)}
                />
              </div>
              <div className="input-group mb-3">
                <span className="input-group-text">Department</span>
                <select
                  className="form-control"
                  value={department}
                  onChange={(e) => setDepartment(e.target.value)}
                >
                  {departments.map((dep) => (
                    <option key={dep.DepartmentId} value={dep.DepartmentName}>
                      {dep.DepartmentName}
                    </option>
                  ))}
                </select>
              </div>
              <div className="input-group mb-3">
                <span className="input-group-text">Date Of Joining</span>
                <input
                  type="date"
                  className="form-control"
                  value={dateOfJoining}
                  onChange={(e) => setDateOfJoining(e.target.value)}
                />
              </div>
              <div className="input-group mb-3">
                <span className="input-group-text">Photo</span>
                <input
                  type="file"
                  className="form-control"
                  onChange={handleImageUpload}
                />
              </div>
              <button
                type="button"
                className="btn btn-primary float-start"
                onClick={employeeId === 0 ? handleCreateClick : handleUpdateClick}
              >
                {employeeId === 0 ? "Create" : "Update"}
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default Employee;

Key Concepts:

  • 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.

Explore the GitHub repository

Happy coding!

Share on LinkedIn
Share on X
Share on Facebook
Share on WhatsApp
Share on Telegram
Share via Email
Copy Link

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.

Click here to learn more

Popular Posts

No popular posts available.

Recommended For You

No related posts found.