Saturday, March 8, 2025

DYNAMIC TASK ASSIGNMENT BASED ON USER ROLES

 Let’s modify the existing ASP.NET Core MVC code to meet your new requirements:

  1. Add Two Fields: Include StartDate and EndDate in the task entry form.
  2. Conditional Display: Show TaskName, StartDate, and EndDate fields only when RoleId equals 2 (e.g., "Manager"); hide them otherwise.
  3. Insert into Two Tables:
    • Insert a new task into the Tasks table for all roles.
    • If RoleId is 2, also insert into an AssignedTasks table to track tasks assigned to managers.
I’ll update the database schema, models, repository, service, controller, and view accordingly, using ADO.NET with SQL Server. This builds on your EmployeeManagement system, maintaining proper naming conventions, coding standards, and comments.

Step 1: Update Database Schema
What
  • Add StartDate and EndDate to Tasks and create an AssignedTasks table for RoleId = 2.
SQL Script
sql
-- Existing Tasks table with new fields
CREATE TABLE Tasks (
    TaskId INT PRIMARY KEY IDENTITY(1,1),
    TaskName NVARCHAR(100) NOT NULL,
    EmployeeId INT NOT NULL,
    ProjectId INT NOT NULL,
    StartDate DATETIME NOT NULL, -- New field
    EndDate DATETIME NOT NULL,   -- New field
    Status NVARCHAR(50) NOT NULL
);

-- New table for assigned tasks (for RoleId = 2)
CREATE TABLE AssignedTasks (
    AssignedTaskId INT PRIMARY KEY IDENTITY(1,1),
    TaskId INT FOREIGN KEY REFERENCES Tasks(TaskId),
    UserId INT NOT NULL,
    AssignmentDate DATETIME NOT NULL DEFAULT GETDATE()
);

-- Ensure TblUser and TblRole exist (from previous setup)
CREATE TABLE TblRole (
    RoleId INT PRIMARY KEY IDENTITY(1,1),
    RoleName NVARCHAR(50) NOT NULL
);

CREATE TABLE TblUser (
    UserId INT PRIMARY KEY IDENTITY(1,1),
    UserName NVARCHAR(50) NOT NULL,
    UserPassword NVARCHAR(50) NOT NULL,
    RoleId INT FOREIGN KEY REFERENCES TblRole(RoleId),
    IsActive BIT NOT NULL DEFAULT 1
);
Why
  • Tasks: Stores all tasks with new date fields.
  • AssignedTasks: Tracks tasks assigned to managers (RoleId = 2).

Step 2: Update Models
What
  • Modify Task and TaskViewModel, add AssignedTask.
How
  • In EmployeeManagement/Models/:
  • Task.cs:
    csharp
    // Represents a task in the system
    public class Task
    {
        public int TaskId { get; set; }
        public string TaskName { get; set; }
        public int EmployeeId { get; set; }
        public int ProjectId { get; set; }
        public DateTime StartDate { get; set; } // New field
        public DateTime EndDate { get; set; }   // New field
        public string Status { get; set; }
    }
  • AssignedTask.cs (new):
    csharp
    // Represents a task assigned to a user (for RoleId = 2)
    public class AssignedTask
    {
        public int AssignedTaskId { get; set; }
        public int TaskId { get; set; }
        public int UserId { get; set; }
        public DateTime AssignmentDate { get; set; }
    }
  • TaskViewModel.cs:
    csharp
    // View model for task assignment form
    public class TaskViewModel
    {
        public int RoleId { get; set; }
        public int UserId { get; set; }
        public string TaskName { get; set; }
        public DateTime StartDate { get; set; }
        public DateTime EndDate { get; set; }
        public List<TblRole> Roles { get; set; } = new List<TblRole>();
        public List<TblUser> Users { get; set; } = new List<TblUser>();
    }
Why
  • Models reflect the updated schema and form requirements.

Step 3: Update Repository Layer
What
  • Modify ITaskRepository and add IAssignedTaskRepository.
How
  • In EmployeeManagement/Repositories/:
  • ITaskRepository.cs:
    csharp
    public interface ITaskRepository
    {
        int AddTask(Task task); // Returns TaskId
    }
  • TaskRepository.cs:
    csharp
    using System.Data.SqlClient;
    
    public class TaskRepository : ITaskRepository
    {
        private readonly string _connectionString;
    
        public TaskRepository(string connectionString)
        {
            _connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
        }
    
        // Adds a task and returns its generated TaskId
        public int AddTask(Task task)
        {
            using (var connection = new SqlConnection(_connectionString))
            {
                connection.Open();
                var sql = @"
                    INSERT INTO Tasks (TaskName, EmployeeId, ProjectId, StartDate, EndDate, Status)
                    VALUES (@TaskName, @EmployeeId, @ProjectId, @StartDate, @EndDate, @Status);
                    SELECT CAST(SCOPE_IDENTITY() AS INT);";
                using (var command = new SqlCommand(sql, connection))
                {
                    command.Parameters.AddWithValue("@TaskName", task.TaskName ?? (object)DBNull.Value);
                    command.Parameters.AddWithValue("@EmployeeId", task.EmployeeId);
                    command.Parameters.AddWithValue("@ProjectId", task.ProjectId);
                    command.Parameters.AddWithValue("@StartDate", task.StartDate);
                    command.Parameters.AddWithValue("@EndDate", task.EndDate);
                    command.Parameters.AddWithValue("@Status", task.Status);
                    return Convert.ToInt32(command.ExecuteScalar());
                }
            }
        }
    }
  • IAssignedTaskRepository.cs (new):
    csharp
    public interface IAssignedTaskRepository
    {
        void AddAssignedTask(AssignedTask assignedTask);
    }
  • AssignedTaskRepository.cs (new):
    csharp
    using System.Data.SqlClient;
    
    public class AssignedTaskRepository : IAssignedTaskRepository
    {
        private readonly string _connectionString;
    
        public AssignedTaskRepository(string connectionString)
        {
            _connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
        }
    
        // Adds an assigned task for RoleId = 2
        public void AddAssignedTask(AssignedTask assignedTask)
        {
            using (var connection = new SqlConnection(_connectionString))
            {
                connection.Open();
                var sql = @"
                    INSERT INTO AssignedTasks (TaskId, UserId, AssignmentDate)
                    VALUES (@TaskId, @UserId, @AssignmentDate);";
                using (var command = new SqlCommand(sql, connection))
                {
                    command.Parameters.AddWithValue("@TaskId", assignedTask.TaskId);
                    command.Parameters.AddWithValue("@UserId", assignedTask.UserId);
                    command.Parameters.AddWithValue("@AssignmentDate", assignedTask.AssignmentDate);
                    command.ExecuteNonQuery();
                }
            }
        }
    }
Why
  • Repositories handle inserts into both tables, with TaskRepository returning the TaskId for linking.

Step 4: Update Service Layer
What
  • Modify IEmployeeService and EmployeeService to handle both tables.
How
  • In EmployeeManagement/Services/:
  • IEmployeeService.cs:
    csharp
    public interface IEmployeeService
    {
        int AssignTaskToEmployee(int employeeId, int projectId, string taskName, DateTime startDate, DateTime endDate, string status, int roleId, int userId);
    }
  • EmployeeService.cs:
    csharp
    public class EmployeeService : IEmployeeService
    {
        private readonly ITaskRepository _taskRepository;
        private readonly IAssignedTaskRepository _assignedTaskRepository;
    
        public EmployeeService(ITaskRepository taskRepository, IAssignedTaskRepository assignedTaskRepository)
        {
            _taskRepository = taskRepository ?? throw new ArgumentNullException(nameof(taskRepository));
            _assignedTaskRepository = assignedTaskRepository ?? throw new ArgumentNullException(nameof(assignedTaskRepository));
        }
    
        // Assigns a task and optionally logs it in AssignedTasks if RoleId = 2
        public int AssignTaskToEmployee(int employeeId, int projectId, string taskName, DateTime startDate, DateTime endDate, string status, int roleId, int userId)
        {
            if (roleId == 2) // Manager-specific validation
            {
                if (string.IsNullOrWhiteSpace(taskName))
                    throw new ArgumentException("Task name is required for managers.", nameof(taskName));
                if (endDate <= startDate)
                    throw new ArgumentException("End date must be after start date for managers.", nameof(endDate));
            }
    
            var task = new Task
            {
                EmployeeId = employeeId,
                ProjectId = projectId,
                TaskName = roleId == 2 ? taskName : "N/A", // Default for non-managers
                StartDate = roleId == 2 ? startDate : DateTime.Now, // Default for non-managers
                EndDate = roleId == 2 ? endDate : DateTime.Now.AddDays(1), // Default for non-managers
                Status = status
            };
    
            int taskId = _taskRepository.AddTask(task);
    
            if (roleId == 2)
            {
                var assignedTask = new AssignedTask
                {
                    TaskId = taskId,
                    UserId = userId,
                    AssignmentDate = DateTime.Now
                };
                _assignedTaskRepository.AddAssignedTask(assignedTask);
            }
    
            return taskId;
        }
    }
Why
  • Service handles conditional logic for RoleId = 2 and orchestrates inserts into both tables.

Step 5: Configure Dependency Injection
How
  • In EmployeeManagement/Program.cs:
    csharp
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddControllersWithViews();
    
    // Register dependencies
    var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
    builder.Services.AddScoped<IUserRepository>(sp => new UserRepository(connectionString));
    builder.Services.AddScoped<IUserService, UserService>();
    builder.Services.AddScoped<ITaskRepository>(sp => new TaskRepository(connectionString));
    builder.Services.AddScoped<IAssignedTaskRepository>(sp => new AssignedTaskRepository(connectionString));
    builder.Services.AddScoped<IEmployeeService, EmployeeService>();
    
    var app = builder.Build();
    app.UseStaticFiles();
    app.UseRouting();
    app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
    app.Run();
Why
  • Ensures all repositories and services are injectable.

Step 6: Update Controller
What
  • Handle GET and POST for task assignment with conditional logic.
How
  • In EmployeeManagement/Controllers/TaskController.cs:
    csharp
    using Microsoft.AspNetCore.Mvc;
    using EmployeeManagement.Services;
    using EmployeeManagement.Models;
    
    public class TaskController : Controller
    {
        private readonly IUserService _userService;
        private readonly IEmployeeService _employeeService;
    
        public TaskController(IUserService userService, IEmployeeService employeeService)
        {
            _userService = userService;
            _employeeService = employeeService;
        }
    
        public IActionResult AssignTask()
        {
            var viewModel = new TaskViewModel
            {
                Roles = _userService.GetAllRoles(),
                Users = new List<TblUser>()
            };
            return View(viewModel);
        }
    
        [HttpGet]
        public IActionResult GetUsersByRoleId(int roleId)
        {
            var users = _userService.GetUsersByRoleId(roleId);
            return Json(users.Select(u => new { u.UserId, u.UserName }));
        }
    
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult AssignTask(TaskViewModel model)
        {
            if (ModelState.IsValid)
            {
                int taskId = _employeeService.AssignTaskToEmployee(
                    model.UserId, // EmployeeId = UserId here
                    1, // Hardcoded ProjectId for simplicity
                    model.TaskName,
                    model.StartDate,
                    model.EndDate,
                    "Pending", // Default status
                    model.RoleId,
                    model.UserId
                );
                TempData["SuccessMessage"] = $"Task assigned with ID: {taskId}";
                return RedirectToAction("AssignTask");
            }
    
            model.Roles = _userService.GetAllRoles();
            model.Users = _userService.GetUsersByRoleId(model.RoleId);
            return View(model);
        }
    }
Why
  • POST action processes the form, handling inserts based on RoleId.

Step 7: Update View
What
  • Show/hide fields based on RoleId = 2 using jQuery.
How
  • In EmployeeManagement/Views/Task/AssignTask.cshtml:
    html
    @model EmployeeManagement.Models.TaskViewModel
    
    @{
        ViewData["Title"] = "Assign Task";
    }
    
    <div class="container mt-4">
        <h2>Assign Task</h2>
        <form asp-action="AssignTask" method="post">
            <div class="form-group mb-3">
                <label for="RoleId" class="form-label">Select Role</label>
                <select asp-for="RoleId" class="form-control" id="roleDropdown">
                    <option value="">-- Select Role --</option>
                    @foreach (var role in Model.Roles)
                    {
                        <option value="@role.RoleId">@role.RoleName</option>
                    }
                </select>
            </div>
            <div class="form-group mb-3">
                <label for="UserId" class="form-label">Select User</label>
                <select asp-for="UserId" class="form-control" id="userDropdown">
                    <option value="">-- Select User --</option>
                    @foreach (var user in Model.Users)
                    {
                        <option value="@user.UserId">@user.UserName</option>
                    }
                </select>
            </div>
            <div class="form-group mb-3 task-fields" style="display: none;">
                <label asp-for="TaskName" class="form-label">Task Name</label>
                <input asp-for="TaskName" class="form-control" />
                <span asp-validation-for="TaskName" class="text-danger"></span>
            </div>
            <div class="form-group mb-3 task-fields" style="display: none;">
                <label asp-for="StartDate" class="form-label">Start Date</label>
                <input asp-for="StartDate" type="datetime-local" class="form-control" />
                <span asp-validation-for="StartDate" class="text-danger"></span>
            </div>
            <div class="form-group mb-3 task-fields" style="display: none;">
                <label asp-for="EndDate" class="form-label">End Date</label>
                <input asp-for="EndDate" type="datetime-local" class="form-control" />
                <span asp-validation-for="EndDate" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Assign</button>
        </form>
        @if (TempData["SuccessMessage"] != null)
        {
            <div class="alert alert-success mt-3">@TempData["SuccessMessage"]</div>
        }
    </div>
    
    @section Scripts {
        <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
        <partial name="_ValidationScriptsPartial" />
        <script>
            $(document).ready(function () {
                $('#roleDropdown').change(function () {
                    var roleId = $(this).val();
                    toggleTaskFields(roleId);
    
                    if (roleId) {
                        $.ajax({
                            url: '@Url.Action("GetUsersByRoleId", "Task")',
                            type: 'GET',
                            data: { roleId: roleId },
                            success: function (data) {
                                var userDropdown = $('#userDropdown');
                                userDropdown.empty();
                                userDropdown.append('<option value="">-- Select User --</option>');
                                $.each(data, function (index, user) {
                                    userDropdown.append(
                                        $('<option></option>').val(user.userId).text(user.userName)
                                    );
                                });
                            },
                            error: function () {
                                alert('Error loading users.');
                            }
                        });
                    } else {
                        $('#userDropdown').empty().append('<option value="">-- Select User --</option>');
                    }
                });
    
                // Show/hide task fields based on RoleId
                function toggleTaskFields(roleId) {
                    if (roleId == 2) { // Manager role
                        $('.task-fields').show();
                    } else {
                        $('.task-fields').hide();
                    }
                }
    
                // Initial toggle based on model value (for validation errors)
                toggleTaskFields('@Model.RoleId');
            });
        </script>
    }
Why
  • Conditional Display: jQuery toggleTaskFields shows/hides fields when RoleId = 2.
  • Validation: Added client-side validation for manager fields.

Step 8: Test in Visual Studio 2022
How
  1. Build solution (Ctrl+Shift+B).
  2. Run (F5), navigate to /Task/AssignTask.
  3. Test scenarios:
    • Select "Admin" (RoleId=1): Only role and user dropdowns visible.
    • Select "Manager" (RoleId=2): Task fields appear, fill them, submit → inserts into both tables.
    • Check database for results.
Sample Output
  • RoleId = 1 (Admin):
    • Tasks: TaskId=1, TaskName="N/A", EmployeeId=1, StartDate=Now, EndDate=Now+1, Status="Pending".
    • AssignedTasks: No entry.
  • RoleId = 2 (Manager):
    • Tasks: TaskId=2, TaskName="Review Report", EmployeeId=2, StartDate=2025-03-08 09:00, EndDate=2025-03-08 17:00, Status="Pending".
    • AssignedTasks: AssignedTaskId=1, TaskId=2, UserId=2, AssignmentDate=Now.

Key Points
  • Conditional Logic: Fields hidden unless RoleId = 2, enforced in view and service.
  • Dual Insert: Tasks always updated; AssignedTasks only for managers.
  • Standards: Consistent naming (e.g., StartDate), clear comments, robust error handling.

No comments:

Post a Comment