0% found this document useful (0 votes)
12 views23 pages

M Talha Mohsin

The document outlines the structure of an ASP.NET Core payroll application, including controllers, models, and views for different employee types such as full-time, part-time, contract, and freelance workers. It implements payroll calculation logic and validation for each employee type, as well as forms for user input. The application uses dependency injection for the payroll calculation service and provides a web interface for calculating earnings based on user input.

Uploaded by

riyanmujahid02
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views23 pages

M Talha Mohsin

The document outlines the structure of an ASP.NET Core payroll application, including controllers, models, and views for different employee types such as full-time, part-time, contract, and freelance workers. It implements payroll calculation logic and validation for each employee type, as well as forms for user input. The application uses dependency injection for the payroll calculation service and provides a web interface for calculating earnings based on user input.

Uploaded by

riyanmujahid02
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 23

Muhammad Talha Mohsin

02-131222-109

File Structure:
// Controllers/HomeController.cs
using Microsoft.AspNetCore.Mvc;

namespace InnovaytePayroll.Controllers;

public class HomeController : Controller


{
private readonly ILogger<HomeController> _logger;

public HomeController(ILogger<HomeController> logger)


{
_logger = logger;
}

public IActionResult Index()


{
return View();
}

public IActionResult Privacy()


{
return View();
}

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]


public IActionResult Error()
{
return View();
}
}

// Controllers/PayrollController.cs
using InnovaytePayroll.Models.Employees;
using InnovaytePayroll.Services;
using Microsoft.AspNetCore.Mvc;

namespace InnovaytePayroll.Controllers;

public class PayrollController : Controller


{
private readonly IPayrollCalculator _calculator;

public PayrollController(IPayrollCalculator calculator)


{
_calculator = calculator;
}

public IActionResult Index()


{
return View();
}
[HttpPost]
public IActionResult Calculate(EmployeeBase employee)
{
if (!ModelState.IsValid)
{
return View("Index", employee);
}

var result = new PayrollResult


{
Amount = _calculator.CalculatePay(employee),
Summary = _calculator.GeneratePaySummary(employee),
Employee = employee
};

return View("Results", result);


}
}

public class PayrollResult


{
public decimal Amount { get; set; }
public string Summary { get; set; }
public EmployeeBase Employee { get; set; }
}

// Models/Employees/ContractEmployee.cs
namespace InnovaytePayroll.Models.Employees;

public class ContractEmployee : EmployeeBase


{
public enum ContractType
{
FixedFee,
MilestoneBased
}

public ContractType Type { get; set; }


public decimal ProjectFee { get; set; }
public int MilestonesCompleted { get; set; }
public int TotalMilestones { get; set; }

public decimal CalculateEarnings()


{
return Type == ContractType.FixedFee
? ProjectFee
: (ProjectFee / TotalMilestones) * MilestonesCompleted;
}
}

// Models/Employees/EmployeeBase.cs
namespace InnovaytePayroll.Models.Employees;

public abstract class EmployeeBase


{
public string Id { get; set; } = Guid.NewGuid().ToString();
public string Name { get; set; }
public string EmployeeType { get; set; }
}

// Models/Employees/FreelanceWorker.cs
namespace InnovaytePayroll.Models.Employees;

public class FreelanceTask


{
public string Category { get; set; }
public decimal Rate { get; set; }
public int Count { get; set; }
}

public class FreelanceWorker : EmployeeBase


{
public List<FreelanceTask> Tasks { get; set; } = new();

public decimal CalculateEarnings()


{
return Tasks.Sum(t => t.Rate * t.Count);
}
}

// Models/Employees/FullTimeEmployee.cs
namespace InnovaytePayroll.Models.Employees;

public class FullTimeEmployee : EmployeeBase


{
public decimal AnnualSalary { get; set; }
public int WorkingHoursPerWeek { get; set; } = 40;
public int WeeksPerYear { get; set; } = 52;
public decimal LoggedHours { get; set; }

public decimal CalculateHourlyRate()


{
return AnnualSalary / (WorkingHoursPerWeek * WeeksPerYear);
}

public decimal CalculateMonthlySalary()


{
return AnnualSalary / 12;
}

public decimal CalculateAdjustedSalary()


{
decimal expectedHours = WorkingHoursPerWeek * (WeeksPerYear / 12.0m);
decimal adjustmentFactor = LoggedHours / expectedHours;
return CalculateMonthlySalary() * adjustmentFactor;
}
}

// Models/Employees/PartTimeEmployee.cs
namespace InnovaytePayroll.Models.Employees;

public class PartTimeEmployee : EmployeeBase


{
public decimal HourlyRate { get; set; }
public decimal HoursWorkedPerWeek { get; set; }
public const decimal MaxHoursPerWeek = 30;

public decimal CalculateSalary()


{
return HourlyRate * HoursWorkedPerWeek * 4; // Approximate monthly
}

public bool IsOvertime()


{
return HoursWorkedPerWeek > MaxHoursPerWeek;
}
}

// Models/PayrollCalculationService.cs
using InnovaytePayroll.Models.Employees;

namespace InnovaytePayroll.Models;

public class PayrollCalculationService


{
public (decimal Amount, string Summary) CalculatePay(EmployeeBase employee)
{
return employee switch
{
FullTimeEmployee ft => (
ft.CalculateAdjustedSalary(),
$"Full-time salary adjusted based on {ft.LoggedHours} logged hours. " +
$"Hourly rate: {ft.CalculateHourlyRate():C}, " +
$"Monthly salary: {ft.CalculateMonthlySalary():C}"
),

PartTimeEmployee pt => (
pt.CalculateSalary(),
pt.IsOvertime()
? $"Part-time earnings with overtime warning! ({pt.HoursWorkedPerWeek}
hours/week)"
: $"Part-time earnings for {pt.HoursWorkedPerWeek} hours/week"
),

ContractEmployee ce => (
ce.CalculateEarnings(),
ce.Type == ContractEmployee.ContractType.FixedFee
? $"Fixed fee contract payment"
: $"Milestone payment ({ce.MilestonesCompleted} of {ce.TotalMilestones}
completed)"
),
FreelanceWorker fw => (
fw.CalculateEarnings(),
$"Freelance payment for {fw.Tasks.Sum(t => t.Count)} tasks " +
$"across {fw.Tasks.Select(t => t.Category).Distinct().Count()} categories"
),

_ => throw new ArgumentException("Unknown employee type")


};
}

public void ValidateEmployee(EmployeeBase employee)


{
switch (employee)
{
case FullTimeEmployee ft:
if (ft.AnnualSalary <= 0)
throw new ArgumentException("Annual salary must be positive");
if (ft.WorkingHoursPerWeek <= 0 || ft.WorkingHoursPerWeek > 80)
throw new ArgumentException("Invalid working hours per week");
break;

case PartTimeEmployee pt:


if (pt.HourlyRate <= 0)
throw new ArgumentException("Hourly rate must be positive");
if (pt.HoursWorkedPerWeek <= 0)
throw new ArgumentException("Hours worked must be positive");
break;

case ContractEmployee ce:


if (ce.ProjectFee <= 0)
throw new ArgumentException("Project fee must be positive");
if (ce.Type == ContractEmployee.ContractType.MilestoneBased &&
(ce.MilestonesCompleted < 0 || ce.TotalMilestones <= 0))
throw new ArgumentException("Invalid milestone values");
break;

case FreelanceWorker fw:


if (fw.Tasks == null || !fw.Tasks.Any())
throw new ArgumentException("At least one task is required");
if (fw.Tasks.Any(t => t.Rate <= 0 || t.Count <= 0))
throw new ArgumentException("Invalid task rate or count");
break;
}
}
}

// Services/IPayrollCalculator.cs
using InnovaytePayroll.Models.Employees;

namespace InnovaytePayroll.Services;

public interface IPayrollCalculator


{
decimal CalculatePay(EmployeeBase employee);
string GeneratePaySummary(EmployeeBase employee);
}
// Services/PayrollCalculator.cs
using InnovaytePayroll.Models.Employees;

namespace InnovaytePayroll.Services;

public class PayrollCalculator : IPayrollCalculator


{
public decimal CalculatePay(EmployeeBase employee)
{
return employee switch
{
FullTimeEmployee ft => ft.CalculateAdjustedSalary(),
PartTimeEmployee pt => pt.CalculateSalary(),
ContractEmployee ce => ce.CalculateEarnings(),
FreelanceWorker fw => fw.CalculateEarnings(),
_ => throw new ArgumentException("Unknown employee type")
};
}

public string GeneratePaySummary(EmployeeBase employee)


{
return employee switch
{
FullTimeEmployee ft =>
$"Full-time employee: Monthly salary ${ft.CalculateMonthlySalary():0.00} " +
$"(Adjusted to ${ft.CalculateAdjustedSalary():0.00} based on {ft.LoggedHours} logged
hours). " +
$"Hourly rate: ${ft.CalculateHourlyRate():0.00}",

PartTimeEmployee pt =>
$"Part-time employee: Monthly earnings ${pt.CalculateSalary():0.00} " +
$"at ${pt.HourlyRate:0.00}/hour for {pt.HoursWorkedPerWeek} hours/week. " +
(pt.IsOvertime() ? "Warning: Overtime hours detected!" : ""),

ContractEmployee ce =>
$"Contract employee: {ce.Type} model. " +
$"Earned ${ce.CalculateEarnings():0.00} of ${ce.ProjectFee:0.00} " +
(ce.Type == ContractEmployee.ContractType.MilestoneBased
? $"(Completed {ce.MilestonesCompleted} of {ce.TotalMilestones} milestones)"
: ""),

FreelanceWorker fw =>
$"Freelancer: Earned ${fw.CalculateEarnings():0.00} for {fw.Tasks.Sum(t => t.Count)}
tasks " +
$"across {fw.Tasks.Select(t => t.Category).Distinct().Count()} categories",

_ => "Unknown employee type"


};
}
}

@* Views/Payroll/_ContractForm.cshtml *@
@model ContractEmployee

<form asp-action="Calculate" asp-controller="Payroll" method="post">


<input type="hidden" name="EmployeeType" value="Contract" />

<div class="mb-3">
<label asp-for="Name" class="form-label">Contractor Name</label>
<input asp-for="Name" class="form-control" required />
<span asp-validation-for="Name" class="text-danger"></span>
</div>

<div class="mb-3">
<label class="form-label">Contract Type</label>
<div class="form-check">
<input class="form-check-input" type="radio" asp-for="Type"
value="@ContractEmployee.ContractType.FixedFee" checked />
<label class="form-check-label">Fixed Fee</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" asp-for="Type"
value="@ContractEmployee.ContractType.MilestoneBased" />
<label class="form-check-label">Milestone Based</label>
</div>
</div>

<div class="mb-3">
<label asp-for="ProjectFee" class="form-label">Project Fee ($)</label>
<input asp-for="ProjectFee" class="form-control" required min="0" step="0.01" />
<span asp-validation-for="ProjectFee" class="text-danger"></span>
</div>

<div id="milestoneFields" class="mb-3 d-none">


<div class="row">
<div class="col-md-6">
<label asp-for="MilestonesCompleted" class="form-label">Milestones
Completed</label>
<input asp-for="MilestonesCompleted" class="form-control" min="0" />
<span asp-validation-for="MilestonesCompleted" class="text-danger"></span>
</div>
<div class="col-md-6">
<label asp-for="TotalMilestones" class="form-label">Total Milestones</label>
<input asp-for="TotalMilestones" class="form-control" min="1" />
<span asp-validation-for="TotalMilestones" class="text-danger"></span>
</div>
</div>
</div>

<div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg">Calculate Earnings</button>
</div>
</form>

@* Views/Payroll/_FreelanceForm.cshtml *@
@model FreelanceWorker

<form asp-action="Calculate" asp-controller="Payroll" method="post">


<input type="hidden" name="EmployeeType" value="Freelance" />

<div class="mb-3">
<label asp-for="Name" class="form-label">Freelancer Name</label>
<input asp-for="Name" class="form-control" required />
<span asp-validation-for="Name" class="text-danger"></span>
</div>

<div class="mb-3">
<h5>Tasks Completed</h5>
<div id="tasksContainer">
@if (Model.Tasks != null && Model.Tasks.Any())
{
for (int i = 0; i < Model.Tasks.Count; i++)
{
<div class="task-row mb-3">
<div class="row">
<div class="col-md-5">
<input asp-for="Tasks[i].Category" class="form-control"
placeholder="Task category" required />
</div>
<div class="col-md-3">
<input asp-for="Tasks[i].Rate" class="form-control" placeholder="Rate"
min="0" step="0.01" required />
</div>
<div class="col-md-3">
<input asp-for="Tasks[i].Count" class="form-control" placeholder="Count"
min="1" required />
</div>
<div class="col-md-1">
<button type="button" class="btn btn-danger remove-task">×</button>
</div>
</div>
</div>
}
}
else
{
<div class="task-row mb-3">
<div class="row">
<div class="col-md-5">
<input name="Tasks[0].Category" class="form-control" placeholder="Task
category" required />
</div>
<div class="col-md-3">
<input name="Tasks[0].Rate" class="form-control" placeholder="Rate"
min="0" step="0.01" required />
</div>
<div class="col-md-3">
<input name="Tasks[0].Count" class="form-control" placeholder="Count"
min="1" required />
</div>
<div class="col-md-1">
<button type="button" class="btn btn-danger remove-task">×</button>
</div>
</div>
</div>
}
</div>
<button type="button" id="addTaskBtn" class="btn btn-secondary btn-sm">
<i class="fas fa-plus"></i> Add Task
</button>
</div>

<div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg">Calculate Earnings</button>
</div>
</form>

@* Views/Payroll/_FullTimeForm.cshtml *@
@model FullTimeEmployee

<form asp-action="Calculate" asp-controller="Payroll" method="post">


<input type="hidden" name="EmployeeType" value="FullTime" />

<div class="mb-3">
<label asp-for="Name" class="form-label">Employee Name</label>
<input asp-for="Name" class="form-control" required />
<span asp-validation-for="Name" class="text-danger"></span>
</div>

<div class="row">
<div class="col-md-6 mb-3">
<label asp-for="AnnualSalary" class="form-label">Annual Salary ($)</label>
<input asp-for="AnnualSalary" class="form-control" required min="0" />
<span asp-validation-for="AnnualSalary" class="text-danger"></span>
</div>
<div class="col-md-6 mb-3">
<label asp-for="WorkingHoursPerWeek" class="form-label">Working
Hours/Week</label>
<input asp-for="WorkingHoursPerWeek" class="form-control" required min="1"
max="80" />
<span asp-validation-for="WorkingHoursPerWeek" class="text-danger"></span>
</div>
</div>

<div class="row">
<div class="col-md-6 mb-3">
<label asp-for="WeeksPerYear" class="form-label">Weeks/Year</label>
<input asp-for="WeeksPerYear" class="form-control" required min="1" max="52" />
<span asp-validation-for="WeeksPerYear" class="text-danger"></span>
</div>
<div class="col-md-6 mb-3">
<label asp-for="LoggedHours" class="form-label">Logged Hours (Current
Month)</label>
<input asp-for="LoggedHours" class="form-control" required min="0" />
<span asp-validation-for="LoggedHours" class="text-danger"></span>
</div>
</div>

<div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg">Calculate Salary</button>
</div>
</form>

@* Views/Payroll/_PartTimeForm.cshtml *@
@model PartTimeEmployee
<form asp-action="Calculate" asp-controller="Payroll" method="post">
<input type="hidden" name="EmployeeType" value="PartTime" />

<div class="mb-3">
<label asp-for="Name" class="form-label">Employee Name</label>
<input asp-for="Name" class="form-control" required />
<span asp-validation-for="Name" class="text-danger"></span>
</div>

<div class="row">
<div class="col-md-6 mb-3">
<label asp-for="HourlyRate" class="form-label">Hourly Rate ($)</label>
<input asp-for="HourlyRate" class="form-control" required min="0" step="0.01" />
<span asp-validation-for="HourlyRate" class="text-danger"></span>
</div>
<div class="col-md-6 mb-3">
<label asp-for="HoursWorkedPerWeek" class="form-label">Hours
Worked/Week</label>
<input asp-for="HoursWorkedPerWeek" class="form-control" required min="0"
max="60" />
<span asp-validation-for="HoursWorkedPerWeek" class="text-danger"></span>
<small class="text-muted">Max allowed: @PartTimeEmployee.MaxHoursPerWeek
hours/week</small>
</div>
</div>

<div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg">Calculate Salary</button>
</div>
</form>

@* Views/Payroll/Index.cshtml *@
@model InnovaytePayroll.Models.Employees.EmployeeBase

@{
ViewData["Title"] = "Payroll Calculator";
}

<link rel="stylesheet" href="~/css/purple-theme.css" />


<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />

<div class="container py-5">


<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card animate__animated animate__fadeIn">
<div class="card-header bg-transparent">
<h3 class="text-center text-purple">InnovayteTech Payroll Calculator</h3>
</div>
<div class="card-body">
<ul class="nav nav-tabs employee-type-tabs mb-4" id="employeeTypeTabs"
role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="fulltime-tab" data-bs-toggle="tab"
data-bs-target="#fulltime" type="button" role="tab">
Full-Time
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="parttime-tab" data-bs-toggle="tab"
data-bs-target="#parttime" type="button" role="tab">
Part-Time
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="contract-tab" data-bs-toggle="tab"
data-bs-target="#contract" type="button" role="tab">
Contract
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="freelance-tab" data-bs-toggle="tab"
data-bs-target="#freelance" type="button" role="tab">
Freelance
</button>
</li>
</ul>

<div class="tab-content" id="employeeTypeTabsContent">


<div class="tab-pane fade show active" id="fulltime" role="tabpanel">
<partial name="_FullTimeForm" model="new FullTimeEmployee()" />
</div>
<div class="tab-pane fade" id="parttime" role="tabpanel">
<partial name="_PartTimeForm" model="new PartTimeEmployee()" />
</div>
<div class="tab-pane fade" id="contract" role="tabpanel">
<partial name="_ContractForm" model="new ContractEmployee()" />
</div>
<div class="tab-pane fade" id="freelance" role="tabpanel">
<partial name="_FreelanceForm" model="new FreelanceWorker()" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>

@section Scripts {
<script src="~/js/payroll.js"></script>
}

@* Views/Payroll/Results.cshtml *@
@model PayrollResult

@{
ViewData["Title"] = "Payroll Results";
}

<div class="container py-5">


<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card animate__animated animate__fadeIn">
<div class="card-header bg-transparent">
<h3 class="text-center text-purple">Payroll Calculation Results</h3>
</div>
<div class="card-body">
<div class="result-display mb-4">
<h4 class="text-center">@Model.Employee.Name</h4>
<hr class="bg-light" />
<div class="text-center display-4 mb-3">
[email protected]("0.00")
</div>
<p class="text-center">@Model.Summary</p>
</div>

<div class="details-section">
<h5 class="mb-3">Details</h5>
@if (Model.Employee is FullTimeEmployee ft)
{
<dl class="row">
<dt class="col-sm-4">Annual Salary</dt>
<dd class="col-sm-8">@ft.AnnualSalary.ToString("C")</dd>

<dt class="col-sm-4">Working Hours/Week</dt>


<dd class="col-sm-8">@ft.WorkingHoursPerWeek</dd>

<dt class="col-sm-4">Weeks/Year</dt>
<dd class="col-sm-8">@ft.WeeksPerYear</dd>

<dt class="col-sm-4">Logged Hours</dt>


<dd class="col-sm-8">@ft.LoggedHours</dd>

<dt class="col-sm-4">Hourly Rate</dt>


<dd class="col-sm-8">@ft.CalculateHourlyRate().ToString("C")</dd>
</dl>
}
else if (Model.Employee is PartTimeEmployee pt)
{
<dl class="row">
<dt class="col-sm-4">Hourly Rate</dt>
<dd class="col-sm-8">@pt.HourlyRate.ToString("C")</dd>

<dt class="col-sm-4">Hours Worked/Week</dt>


<dd class="col-sm-8">@pt.HoursWorkedPerWeek</dd>

@if (pt.IsOvertime())
{
<dt class="col-sm-4">Warning</dt>
<dd class="col-sm-8">
<div class="alert alert-warning">
Overtime detected! Max allowed is
@PartTimeEmployee.MaxHoursPerWeek hours/week.
</div>
</dd>
}
</dl>
}
else if (Model.Employee is ContractEmployee ce)
{
<dl class="row">
<dt class="col-sm-4">Contract Type</dt>
<dd class="col-sm-8">@ce.Type</dd>

<dt class="col-sm-4">Project Fee</dt>


<dd class="col-sm-8">@ce.ProjectFee.ToString("C")</dd>

@if (ce.Type == ContractEmployee.ContractType.MilestoneBased)


{
<dt class="col-sm-4">Milestones Completed</dt>
<dd class="col-sm-8">@ce.MilestonesCompleted of
@ce.TotalMilestones</dd>
}
</dl>
}
else if (Model.Employee is FreelanceWorker fw)
{
<h6>Tasks Breakdown</h6>
<table class="table table-hover">
<thead>
<tr>
<th>Category</th>
<th>Rate</th>
<th>Count</th>
<th>Subtotal</th>
</tr>
</thead>
<tbody>
@foreach (var task in fw.Tasks)
{
<tr>
<td>@task.Category</td>
<td>@task.Rate.ToString("C")</td>
<td>@task.Count</td>
<td>@((task.Rate * task.Count).ToString("C"))</td>
</tr>
}
</tbody>
<tfoot>
<tr class="table-active">
<th colspan="3">Total</th>
<th>@fw.CalculateEarnings().ToString("C")</th>
</tr>

/* wwwroot/css/purple-theme.css */
:root {
--primary-color: #6a0dad;
--primary-light: #9c27b0;
--primary-dark: #4a148c;
--secondary-color: #e1bee7;
--accent-color: #ff4081;
--text-on-primary: #ffffff;
--text-on-secondary: #000000;
--background: #f3e5f5;
--card-bg: #ffffff;
--input-bg: #f5f5f5;
}

body {
background-color: var(--background);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

.navbar {
background-color: var(--primary-color) !important;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
}

.navbar-brand, .nav-link {
color: var(--text-on-primary) !important;
}

.btn-primary {
background-color: var(--primary-color);
border-color: var(--primary-dark);
}

.btn-primary:hover {
background-color: var(--primary-light);
border-color: var(--primary-color);
}

.card {
border: none;
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
background-color: var(--card-bg);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.card:hover {
transform: translateY(-5px);
box-shadow: 0 6px 25px rgba(0, 0, 0, 0.15);
}

.form-control:focus {
border-color: var(--primary-light);
box-shadow: 0 0 0 0.25rem rgba(154, 39, 176, 0.25);
}

.employee-type-tabs .nav-link {
color: var(--primary-dark);
border: none;
padding: 12px 20px;
font-weight: 500;
}

.employee-type-tabs .nav-link.active {
color: var(--accent-color);
background-color: transparent;
border-bottom: 3px solid var(--accent-color);
}
.result-display {
background: linear-gradient(135deg, var(--primary-light), var(--primary-dark));
color: white;
border-radius: 10px;
padding: 20px;
animation: fadeIn 0.5s ease;
}

@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}

to {
opacity: 1;
transform: translateY(0);
}
}

.alert-warning {
background-color: #fff3cd;
border-color: #ffeeba;
color: #856404;
animation: pulse 2s infinite;
}

@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.7);
}

70% {
box-shadow: 0 0 0 10px rgba(255, 193, 7, 0);
}

100% {
box-shadow: 0 0 0 0 rgba(255, 193, 7, 0);
}
}

/* wwwroot/css/site.css */
/* Base styles that work with the purple theme */
html {
font-size: 14px;
}

@media (min-width: 768px) {


html {
font-size: 16px;
}
}
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus
{
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #6a0dad;
}

html {
position: relative;
min-height: 100%;
}

body {
margin-bottom: 60px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

.text-purple {
color: #6a0dad;
}

.bg-purple {
background-color: #6a0dad;
}

.loading-spinner {
display: inline-block;
width: 2rem;
height: 2rem;
vertical-align: text-bottom;
border: 0.25em solid currentColor;
border-right-color: transparent;
border-radius: 50%;
animation: spinner-border .75s linear infinite;
}

@keyframes spinner-border {
to {
transform: rotate(360deg);
}
}

/* Print specific styles */


@media print {
body * {
visibility: hidden;
}

.card, .card * {
visibility: visible;
}

.card {
position: absolute;
left: 0;
top: 0;
width: 100%;
border: none;
box-shadow: none;
}

.no-print {
display: none !important;
}
}

// wwwroot/js/payroll.js
document.addEventListener('DOMContentLoaded', function () {
// Initialize tooltips
$('[data-toggle="tooltip"]').tooltip();

// Handle freelance task addition


$(document).on('click', '#addTaskBtn', function () {
const taskHtml = `
<div class="task-row mb-3 animate__animated animate__fadeIn">
<div class="row">
<div class="col-md-5">
<input type="text" name="Tasks[${$('.task-row').length}].Category"
class="form-control" placeholder="Task category" required />
</div>
<div class="col-md-3">
<input type="number" name="Tasks[${$('.task-row').length}].Rate"
class="form-control" placeholder="Rate" min="0" step="0.01" required />
</div>
<div class="col-md-3">
<input type="number" name="Tasks[${$('.task-row').length}].Count"
class="form-control" placeholder="Count" min="1" required />
</div>
<div class="col-md-1">
<button type="button" class="btn btn-danger remove-task">×</button>
</div>
</div>
</div>`;
$('#tasksContainer').append(taskHtml);
});

$(document).on('click', '.remove-task', function () {


$(this).closest('.task-row').addClass('animate__animated animate__fadeOut');
setTimeout(() => {
$(this).closest('.task-row').remove();
// Reindex remaining tasks
$('.task-row').each(function (index) {
$(this).find('input').each(function () {
const name = $(this).attr('name').replace(/\[\d+\]/, `[${index}]`);
$(this).attr('name', name);
});
});
}, 500);
});

// Handle contract type change


$('input[name="Type"]').change(function () {
if ($(this).val() === 'FixedFee') {
$('#milestoneFields').addClass('d-none');
} else {
$('#milestoneFields').removeClass('d-none');
}
});

// Validate part-time hours


$('input[name="HoursWorkedPerWeek"]').on('change', function () {
const maxHours = @PartTimeEmployee.MaxHoursPerWeek;
if ($(this).val() > maxHours) {
$('#overtimeWarning').removeClass('d-none').addClass('animate__animated
animate__pulse');
} else {
$('#overtimeWarning').addClass('d-none');
}
});
});

// Program.cs
using InnovaytePayroll.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.


builder.Services.AddControllersWithViews();
builder.Services.AddScoped<IPayrollCalculator, PayrollCalculator>();

var app = builder.Build();

// Configure the HTTP request pipeline.


if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Github Repositry:
Link:

https://github.com/avbcc/InnovaytePayroll.git

Output:

You might also like