diff --git a/Areas/OTcalculate/Controllers/OvertimeController.cs b/Areas/OTcalculate/Controllers/OvertimeController.cs index cf8cd32..c5596ae 100644 --- a/Areas/OTcalculate/Controllers/OvertimeController.cs +++ b/Areas/OTcalculate/Controllers/OvertimeController.cs @@ -20,5 +20,9 @@ namespace PSTW_CentralSystem.Areas.OTcalculate.Controllers return View(); } + public IActionResult OtSTatus() + { + return View(); + } } } diff --git a/Areas/OTcalculate/Models/OtStatusModel.cs b/Areas/OTcalculate/Models/OtStatusModel.cs new file mode 100644 index 0000000..278dd1b --- /dev/null +++ b/Areas/OTcalculate/Models/OtStatusModel.cs @@ -0,0 +1,36 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace PSTW_CentralSystem.Areas.OTcalculate.Models +{ + [Table("otstatus")] + public class OtStatusModel + { + [Key] + public int StatusId { get; set; } + + [Required] + public int UserId { get; set; } + + [Required] + public int Month { get; set; } + + [Required] + public int Year { get; set; } + + public DateTime SubmitDate { get; set; } + + public string HodStatus { get; set; } = "Pending"; + + // JSON array of ApprovalUpdateLog + public string? HodUpdate { get; set; } + + public string HrStatus { get; set; } = "Pending"; + + // JSON array of ApprovalUpdateLog + public string? HrUpdate { get; set; } + + public string? FilePath { get; set; } + } + +} diff --git a/Areas/OTcalculate/Views/HrDashboard/Rate.cshtml b/Areas/OTcalculate/Views/HrDashboard/Rate.cshtml index f141866..06c4170 100644 --- a/Areas/OTcalculate/Views/HrDashboard/Rate.cshtml +++ b/Areas/OTcalculate/Views/HrDashboard/Rate.cshtml @@ -1,6 +1,4 @@ - - -@{ +@{ ViewData["Title"] = "Rate Update"; Layout = "~/Views/Shared/_Layout.cshtml"; } diff --git a/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml b/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml index 1d777f9..d372cba 100644 --- a/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml +++ b/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml @@ -104,7 +104,7 @@ Station Days Description - Action + Action From @@ -134,7 +134,7 @@ {{ record.otDescription }} - + @@ -167,10 +167,33 @@ - + + + + + @@ -189,7 +212,10 @@ selectedYear: currentYear, months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], years: Array.from({ length: 10 }, (_, i) => currentYear - 5 + i), - expandedDescriptions: {} + expandedDescriptions: {}, + showSubmitModal: false, + submitFile: null, + submittedStatus: {}, }; }, computed: { @@ -217,6 +243,10 @@ hours: Math.floor(totalMinutes / 60), minutes: Math.round(totalMinutes % 60) }; + }, + isAlreadySubmitted() { + const key = `${this.selectedYear}-${String(this.selectedMonth).padStart(2, '0')}`; + return !!this.submittedStatus[key]; } }, async mounted() { @@ -225,8 +255,21 @@ methods: { async initUserAndRecords() { await this.fetchUser(); - if (this.userId) await this.fetchOtRecords(); + if (this.userId) { + await this.fetchOtRecords(); + await this.fetchSubmissionStatus(); + } }, + async fetchSubmissionStatus() { + try { + const res = await fetch(`/OvertimeAPI/GetSubmissionStatus/${this.userId}`); + const data = await res.json(); + this.submittedStatus = data; // expect format like { '2025-04': true } + } catch (err) { + console.error("Submission status fetch error:", err); + } + }, + async fetchUser() { try { const res = await fetch('/IdentityAPI/GetUserInformation', { method: 'POST' }); @@ -342,43 +385,57 @@ alert("An error occurred while generating the PDF."); } }, - async submitRecords() { - try { - const recordsToSubmit = this.filteredRecords.map(record => ({ - overtimeId: record.overtimeId, // Make sure to include the ID for updates - otDate: record.otDate, - officeFrom: record.officeFrom, - officeTo: record.officeTo, - officeBreak: record.officeBreak, - afterFrom: record.afterFrom, - afterTo: record.afterTo, - afterBreak: record.afterBreak, - stationId: record.stationId, - otDescription: record.otDescription, - otDays: record.otDays, - filePath: record.filePath, // Include existing file path - userId: this.userId - // Add other relevant fields if necessary - })); - - const res = await fetch('/OvertimeAPI/SubmitOvertimeRecords', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(recordsToSubmit) - }); - if (res.ok) { - alert("Overtime records submitted for review."); - } else { - alert("Submission failed: " + await res.text()); - } - } catch (err) { - console.error("Submission error:", err); - alert("An error occurred during submission."); + openSubmitModal() { + this.showSubmitModal = true; + }, + handleFileChange(event) { + const file = event.target.files[0]; + if (file && file.type !== 'application/pdf') { + alert("Only PDF files are allowed."); + return; } + this.submitFile = file; }, - } - }); + async submitToHod() { + this.isSubmitting = true; + try { + if (!this.submitFile) { + alert("Please upload a PDF file."); + return; + } + + const formData = new FormData(); + formData.append("file", this.submitFile); + + // Add month & year selection logic if needed + formData.append("month", new Date().getMonth() + 1); + formData.append("year", new Date().getFullYear()); + + try { + const response = await fetch("/OvertimeAPI/SubmitOvertimeRecords", { + method: "POST", + body: formData + }); + + if (!response.ok) throw new Error("Submission failed"); + alert("Submission successful!"); + this.showSubmitModal = false; + + const key = `${this.selectedYear}-${String(this.selectedMonth).padStart(2, '0')}`; + this.submittedStatus[key] = true; + this.showSubmitModal = false; + + } catch (err) { + alert("Error: " + err.message); + } + } finally { + this.isSubmitting = false; + } + } + + } + }); app.mount("#app"); } \ No newline at end of file diff --git a/Areas/OTcalculate/Views/Overtime/OtStatus.cshtml b/Areas/OTcalculate/Views/Overtime/OtStatus.cshtml new file mode 100644 index 0000000..1207d3c --- /dev/null +++ b/Areas/OTcalculate/Views/Overtime/OtStatus.cshtml @@ -0,0 +1,123 @@ +@{ + ViewData["Title"] = "Overtime Status"; + Layout = "~/Views/Shared/_Layout.cshtml"; +} + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
MonthYearSubmitted OnHOD StatusHR StatusDetails
{{ getMonthName(status.month) }}{{ status.year }}{{ formatDate(status.submitDate) }}{{ status.hodStatus }}{{ status.hrStatus }} + +
+ + + +
+ +@section Scripts { + + +} diff --git a/Controllers/API/OvertimeAPI.cs b/Controllers/API/OvertimeAPI.cs index a06ca53..1409c15 100644 --- a/Controllers/API/OvertimeAPI.cs +++ b/Controllers/API/OvertimeAPI.cs @@ -554,6 +554,87 @@ namespace PSTW_CentralSystem.Controllers.API return File(stream, "application/pdf", $"OvertimeRecords_{year}_{month}.pdf"); } + [HttpPost("SubmitOvertimeRecords")] + public async Task SubmitOvertimeRecords([FromForm] IFormFile file, [FromForm] int month, [FromForm] int year) + { + var userIdStr = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (string.IsNullOrEmpty(userIdStr) || !int.TryParse(userIdStr, out int userId)) + return Unauthorized(); + + if (file == null || file.Length == 0) + return BadRequest("No file uploaded."); + + var uploadsFolder = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "Media", "Overtime"); + + if (!Directory.Exists(uploadsFolder)) + Directory.CreateDirectory(uploadsFolder); + + var fileName = $"OT_{userId}_{year}_{month}_{DateTime.Now.Ticks}.pdf"; + var filePath = Path.Combine(uploadsFolder, fileName); + + using (var stream = new FileStream(filePath, FileMode.Create)) + { + await file.CopyToAsync(stream); + } + + var statusRecord = new OtStatusModel + { + UserId = userId, + Month = month, + Year = year, + SubmitDate = DateTime.Now, + HodStatus = "Pending", + HrStatus = "Pending", + FilePath = $"/Media/Overtime/{fileName}" + }; + + _centralDbContext.OtStatus.Add(statusRecord); + await _centralDbContext.SaveChangesAsync(); + + return Ok(new { message = "Overtime records submitted successfully." }); + } + + [HttpGet("CheckSubmissionStatus")] + public IActionResult CheckSubmissionStatus(int month, int year) + { + var userIdStr = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (string.IsNullOrEmpty(userIdStr) || !int.TryParse(userIdStr, out int userId)) + return Unauthorized(); + + var isSubmitted = _centralDbContext.OtStatus + .Any(s => s.UserId == userId && s.Month == month && s.Year == year); + + return Ok(new { isSubmitted }); + } + + [HttpGet("GetSubmissionStatus/{userId}")] + public IActionResult GetSubmissionStatus(int userId) + { + try + { + var statuses = _centralDbContext.OtStatus + .Where(s => s.UserId == userId) + .OrderByDescending(s => s.SubmitDate) + .Select(s => new + { + s.UserId, + s.Month, + s.Year, + s.HodStatus, + s.HrStatus, + s.SubmitDate + }) + .ToList(); + + return Ok(statuses); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to fetch submission statuses."); + return StatusCode(500, "Error retrieving submission statuses."); + } + } + #endregion #region Ot Edit @@ -615,6 +696,23 @@ namespace PSTW_CentralSystem.Controllers.API } #endregion + #region OtStatus + [HttpGet("GetUserOtStatus")] + public IActionResult GetUserOtStatus() + { + var userIdStr = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (string.IsNullOrEmpty(userIdStr) || !int.TryParse(userIdStr, out int userId)) + return Unauthorized(); + + var records = _centralDbContext.OtStatus + .Where(s => s.UserId == userId) + .OrderByDescending(s => s.Year).ThenByDescending(s => s.Month) + .ToList(); + + return Ok(records); + } + + #endregion } } \ No newline at end of file diff --git a/DBContext/CentralSystemContext.cs b/DBContext/CentralSystemContext.cs index 15909dc..9f979ad 100644 --- a/DBContext/CentralSystemContext.cs +++ b/DBContext/CentralSystemContext.cs @@ -104,5 +104,7 @@ namespace PSTW_CentralSystem.DBContext public DbSet States { get; set; } public DbSet Weekends { get; set; } public DbSet Otregisters { get; set; } + public DbSet OtStatus { get; set; } + } } diff --git a/Views/Shared/_Layout.cshtml b/Views/Shared/_Layout.cshtml index 8a4e6bc..448acb4 100644 --- a/Views/Shared/_Layout.cshtml +++ b/Views/Shared/_Layout.cshtml @@ -539,6 +539,11 @@ OT Records + diff --git a/wwwroot/Media/Overtime/OT_15_2025_4_638809276784755591.pdf b/wwwroot/Media/Overtime/OT_15_2025_4_638809276784755591.pdf new file mode 100644 index 0000000..65d8852 Binary files /dev/null and b/wwwroot/Media/Overtime/OT_15_2025_4_638809276784755591.pdf differ diff --git a/wwwroot/Media/Overtime/OT_15_2025_4_638809289066407421.pdf b/wwwroot/Media/Overtime/OT_15_2025_4_638809289066407421.pdf new file mode 100644 index 0000000..65d8852 Binary files /dev/null and b/wwwroot/Media/Overtime/OT_15_2025_4_638809289066407421.pdf differ diff --git a/wwwroot/Media/Overtime/OT_15_2025_4_638809289682209687.pdf b/wwwroot/Media/Overtime/OT_15_2025_4_638809289682209687.pdf new file mode 100644 index 0000000..bfe355f Binary files /dev/null and b/wwwroot/Media/Overtime/OT_15_2025_4_638809289682209687.pdf differ diff --git a/wwwroot/Media/Overtime/OT_15_2025_4_638809323916424471.pdf b/wwwroot/Media/Overtime/OT_15_2025_4_638809323916424471.pdf new file mode 100644 index 0000000..65d8852 Binary files /dev/null and b/wwwroot/Media/Overtime/OT_15_2025_4_638809323916424471.pdf differ diff --git a/wwwroot/Media/Overtime/OT_15_2025_4_638809389078270198.pdf b/wwwroot/Media/Overtime/OT_15_2025_4_638809389078270198.pdf new file mode 100644 index 0000000..65d8852 Binary files /dev/null and b/wwwroot/Media/Overtime/OT_15_2025_4_638809389078270198.pdf differ diff --git a/wwwroot/Media/Overtime/OT_15_2025_4_638809392040377761.pdf b/wwwroot/Media/Overtime/OT_15_2025_4_638809392040377761.pdf new file mode 100644 index 0000000..65d8852 Binary files /dev/null and b/wwwroot/Media/Overtime/OT_15_2025_4_638809392040377761.pdf differ diff --git a/wwwroot/Media/Overtime/OT_1_2025_4_638809164154988965.pdf b/wwwroot/Media/Overtime/OT_1_2025_4_638809164154988965.pdf new file mode 100644 index 0000000..65d8852 Binary files /dev/null and b/wwwroot/Media/Overtime/OT_1_2025_4_638809164154988965.pdf differ diff --git a/wwwroot/Media/Overtime/OT_1_2025_4_638809168067665769.pdf b/wwwroot/Media/Overtime/OT_1_2025_4_638809168067665769.pdf new file mode 100644 index 0000000..65d8852 Binary files /dev/null and b/wwwroot/Media/Overtime/OT_1_2025_4_638809168067665769.pdf differ