This commit is contained in:
Naz 2025-03-25 15:34:38 +08:00
parent 802a81357e
commit aa316b94ae
9 changed files with 208 additions and 35 deletions

View File

@ -15,5 +15,6 @@ namespace PSTW_CentralSystem.Areas.OTcalculate.Models
[ForeignKey("StateId")]
public virtual StateModel? States { get; set; }
public DateTime LastUpdated { get; set; }
}
}

View File

@ -0,0 +1,6 @@
namespace PSTW_CentralSystem.Areas.OTcalculate.Models
{
public class OtRegisterModel
{
}
}

View File

@ -18,5 +18,7 @@ namespace PSTW_CentralSystem.Areas.OTcalculate.Models
public virtual UserModel? Users { get; set; }
public DateTime LastUpdated { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using PSTW_CentralSystem.Models;
namespace PSTW_CentralSystem.Areas.OTcalculate.Models
{
public class SettingsViewModel
{
public DateTime? LatestRateUpdate { get; set; }
public DateTime? LatestCalendarUpdate { get; set; }
}
}

View File

@ -69,6 +69,12 @@
<div class="card-body">
<label>State:</label>
<div class="row">
<div class="col-md-12">
<div class="form-check">
<input type="checkbox" class="form-check-input" v-model="selectAllChecked">
<label class="form-check-label"><strong>Select All</strong></label>
</div>
</div>
<div class="col-md-4" v-for="state in stateList" :key="state.stateId">
<div class="form-check">
<input type="checkbox" class="form-check-input" :value="state.stateId" v-model="selectedStates">
@ -101,8 +107,12 @@
<div class="card-body">
<h5>Holidays for Selected State</h5>
<ul class="list-group">
<li class="list-group-item" v-for="holiday in filteredHolidays" :key="holiday.holidayId">
{{ formatDate(holiday.holidayDate) }} - {{ holiday.holidayName }}
<li class="list-group-item d-flex justify-content-between align-items-center"
v-for="holiday in filteredHolidays" :key="holiday.holidayId">
<span>{{ formatDate(holiday.holidayDate) }} - {{ holiday.holidayName }}</span>
<button class="btn btn-danger btn-sm" v-on:click="deleteHoliday(holiday.holidayId)">
<i class="mdi mdi-delete"></i>
</button>
</li>
</ul>
</div>
@ -113,6 +123,12 @@
<div class="card-body">
<label>State:</label>
<div class="row">
<div class="col-md-12">
<div class="form-check">
<input type="checkbox" class="form-check-input" v-model="selectAllChecked">
<label class="form-check-label"><strong>Select All</strong></label>
</div>
</div>
<div class="col-md-4" v-for="state in stateList" :key="state.stateId">
<div class="form-check">
<input type="checkbox" class="form-check-input" :value="state.stateId" v-model="selectedStates">
@ -141,7 +157,6 @@
<div v-if="activeTab === 'weekend'" class="card shadow-sm">
<div class="card-body">
<h5>Weekend Days per State</h5>
<div class="row">
<div class="col-md-6" v-for="(states, day) in groupedWeekends" :key="day">
<div class="card p-2 mb-2 shadow-sm">
@ -175,7 +190,8 @@
holidayList: [],
selectedDay: "",
weekendList: [],
stateWeekends: []
stateWeekends: [],
selectAllChecked: false
};
},
@ -202,6 +218,25 @@
grouped[weekend.day].push(weekend.stateName);
});
return grouped;
},
filteredHolidays() {
return this.selectedState
? this.holidayList
.filter(h => h.stateId === this.selectedState)
.sort((a, b) => new Date(a.holidayDate) - new Date(b.holidayDate))
: [];
}
},
watch: {
selectAllChecked(ckeckedState) {
if (ckeckedState) {
this.selectedStates = this.stateList.map(state => state.stateId);
}
},
selectedStates(ckeckedState) {
this.selectAllChecked = ckeckedState.length === this.stateList.length;
}
},
@ -278,6 +313,28 @@
}
},
async deleteHoliday(holidayId) {
if (!confirm("Are you sure you want to delete this holiday?")) {
return;
}
try {
const response = await fetch(`/OvertimeAPI/DeleteHoliday/${holidayId}`, {
method: "DELETE"
});
if (response.ok) {
alert("Holiday deleted successfully!");
this.fetchHolidays();
} else {
alert("Failed to delete holiday.");
}
} catch (error) {
console.error("Error deleting holiday:", error);
alert("An error occurred while deleting the holiday.");
}
},
async fetchWeekends() {
try {
const response = await fetch("/OvertimeAPI/GetWeekendDay");
@ -289,6 +346,7 @@
alert("Failed to load weekend list.");
}
},
async fetchStateWeekends() {
try {
const response = await fetch("/OvertimeAPI/GetStateWeekends");
@ -301,7 +359,8 @@
alert("Failed to load state weekend data.");
}
},
async updateWeekend() {
async updateWeekend() {
try {
if (this.selectedStates.length === 0 || !this.selectedDay) {
alert("Please select at least one state and a day.");
@ -355,7 +414,6 @@
this.selectedDay = "";
this.selectedStates = [];
}
}
});

View File

@ -177,7 +177,8 @@
},
{
"title": "Select Rate",
"data": "id",
"data": "id",
"className": "text-center",
"render": function (data)
{
return `<input type='checkbox' class='rate-checkbox' value='${data}'>`;

View File

@ -4,6 +4,7 @@
}
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<div class="container">
<div class="row justify-content-center">
<div class="col-6 col-md-6 col-lg-3">
@ -36,42 +37,81 @@
<div class="row">
<div id="dateUpdate" class="card m-1">
<div class="row" v-if="addSection == true">
<form v-on:submit.prevent="updateDate" data-aos="fade-right">
<div class="container updateDate" data-aos="fade-right">
<div class="row" data-aos="fade-right">
<div class="col-md-12">
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">
<div class="card-header" style="background-color: white;">
<h3 class="date-heading text-center">UPDATE DATE</h3>
</div>
<div id="app" class="card m-1">
<div class="row">
<div class="container updateDate justify-content-center align-items-center p-4 shadow">
<div class="row">
<div class="col-md-12">
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">
<div class="card-header" style="background-color: white;">
<h3 class="date-heading text-center">LATEST UPDATE DATES</h3>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-12">
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">
<div class="card-header" style="background-color: white;">
<lable class="date-heading text-center">Rate Latest Update:</lable>
<div class="row">
<div class="col-md-12">
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">
<div class="card-header text-center" style="background-color: white;">
<label class="date-heading text-center">Rate Latest Update: {{ rateUpdateDate || 'N/A' }}</label>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-12">
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">
<div class="card-header" style="background-color: white;">
<lable class="date-heading text-center">Calendar Latest Update:</lable>
<div class="row">
<div class="col-md-12">
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">
<div class="card-header text-center" style="background-color: white;">
<label class="date-heading text-center">Calendar Latest Update: {{ calendarUpdateDate || 'N/A' }}</label>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
@section Scripts {
@{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
<script src="https://unpkg.com/vue@3.2.37/dist/vue.global.js"></script>
<script>
window.onload = function () {
const app = Vue.createApp({
data() {
return {
rateUpdateDate: null,
calendarUpdateDate: null,
};
},
mounted() {
this.fetchUpdateDates();
},
methods: {
async fetchUpdateDates() {
try {
const response = await fetch("/OvertimeAPI/GetUpdateDates", {
method: "GET",
headers: { "Content-Type": "application/json" },
});
if (!response.ok) throw new Error("Failed to fetch update dates");
const data = await response.json();
this.rateUpdateDate = data.rateUpdateDate;
this.calendarUpdateDate = data.calendarUpdateDate;
} catch (error) {
console.error(error);
}
},
},
}).mount("#app");
};
</script>
}

View File

@ -2,13 +2,21 @@
ViewData["Title"] = "Register Overtime";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<style>
</style>
<div id="OTregister" >
<div class="row card">
<div class="card-header">
<p>Name:</p>
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<div id="app">
<div class="container mt-3">
<div class="card m-1 shadow-sm">
<div class="card-body col-md-6">
<div class="card p-2 mb-2">
<label>Date:</label>
<input type="date" class="form-control" v-model="selectedDate">
<div class="d-flex align-items-center gap-2">
<h4>OFFICE HOURS</h4>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -32,6 +32,28 @@ namespace PSTW_CentralSystem.Controllers.API
_centralDbContext = centralDbContext;
_userManager = userManager;
}
[HttpGet("GetUpdateDates")]
public IActionResult GetUpdateDates()
{
try
{
var latestRateUpdate = _centralDbContext.Rates.OrderByDescending(r => r.LastUpdated).FirstOrDefault()?.LastUpdated;
var latestCalendarUpdate = _centralDbContext.Holidays.OrderByDescending(c => c.LastUpdated).FirstOrDefault()?.LastUpdated;
var updateDates = new
{
rateUpdateDate = latestRateUpdate.HasValue ? latestRateUpdate.Value.ToString("dd MMMM yyyy") : null,
calendarUpdateDate = latestCalendarUpdate.HasValue ? latestCalendarUpdate.Value.ToString("dd MMMM yyyy") : null
};
return Json(updateDates);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
#region Rate
[HttpPost("UpdateRates")]
@ -195,6 +217,29 @@ namespace PSTW_CentralSystem.Controllers.API
var holidays = _centralDbContext.Holidays.ToList();
return Ok(holidays);
}
[HttpDelete("DeleteHoliday/{id}")]
public async Task<IActionResult> DeleteHoliday(int id)
{
try
{
var holiday = await _centralDbContext.Holidays.FindAsync(id);
if (holiday == null)
{
return NotFound("Holiday not found.");
}
_centralDbContext.Holidays.Remove(holiday);
await _centralDbContext.SaveChangesAsync();
return Ok(new { message = "Holiday deleted successfully." });
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
#endregion
#region Weekend