From 2c9d8bc4da0f286c35a08db2645e5d244162d568 Mon Sep 17 00:00:00 2001 From: Naz <2022755409@student.uitm.edu.my> Date: Wed, 9 Apr 2025 17:30:56 +0800 Subject: [PATCH] - --- .../Controllers/OvertimeController.cs | 4 + Areas/OTcalculate/Models/OtRegisterModel.cs | 1 - .../Views/Overtime/EditOvertime.cshtml | 6 + .../Views/Overtime/OtRecords.cshtml | 295 +++++++++++++++++- .../Views/Overtime/OtRegister.cshtml | 97 +++--- Controllers/API/OvertimeAPI.cs | 60 ++++ Views/Shared/_Layout.cshtml | 2 + 7 files changed, 402 insertions(+), 63 deletions(-) create mode 100644 Areas/OTcalculate/Views/Overtime/EditOvertime.cshtml diff --git a/Areas/OTcalculate/Controllers/OvertimeController.cs b/Areas/OTcalculate/Controllers/OvertimeController.cs index 93a2757..cf8cd32 100644 --- a/Areas/OTcalculate/Controllers/OvertimeController.cs +++ b/Areas/OTcalculate/Controllers/OvertimeController.cs @@ -15,6 +15,10 @@ namespace PSTW_CentralSystem.Areas.OTcalculate.Controllers { return View(); } + public IActionResult EditOvertime() + { + return View(); + } } } diff --git a/Areas/OTcalculate/Models/OtRegisterModel.cs b/Areas/OTcalculate/Models/OtRegisterModel.cs index cd74523..89054d6 100644 --- a/Areas/OTcalculate/Models/OtRegisterModel.cs +++ b/Areas/OTcalculate/Models/OtRegisterModel.cs @@ -28,7 +28,6 @@ namespace PSTW_CentralSystem.Areas.OTcalculate.Models public string OtDescription { get; set; } public string OtDays { get; set; } public required string PDFBase64 { get; set; } - public required string IvBase64 { get; set; } [Required] public int UserId { get; set; } diff --git a/Areas/OTcalculate/Views/Overtime/EditOvertime.cshtml b/Areas/OTcalculate/Views/Overtime/EditOvertime.cshtml new file mode 100644 index 0000000..cc98900 --- /dev/null +++ b/Areas/OTcalculate/Views/Overtime/EditOvertime.cshtml @@ -0,0 +1,6 @@ +@{ + ViewData["Title"] = "Records Overtime"; + Layout = "~/Views/Shared/_Layout.cshtml"; +} + +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers \ No newline at end of file diff --git a/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml b/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml index dba9e1b..fbd9d94 100644 --- a/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml +++ b/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml @@ -1,5 +1,298 @@ @{ - ViewData["Title"] = "All Records"; + ViewData["Title"] = "My Overtime Records"; Layout = "~/Views/Shared/_Layout.cshtml"; } + + + +
+
+
+ + +
+
+ + +
+
+ + + +
+ + + +
+
+ + +@section Scripts { + + + + +} diff --git a/Areas/OTcalculate/Views/Overtime/OtRegister.cshtml b/Areas/OTcalculate/Views/Overtime/OtRegister.cshtml index 0b65585..3125308 100644 --- a/Areas/OTcalculate/Views/Overtime/OtRegister.cshtml +++ b/Areas/OTcalculate/Views/Overtime/OtRegister.cshtml @@ -62,6 +62,7 @@ {{ station.stationName || 'Unnamed Station' }} + *Only for PSTW AIR
@@ -222,7 +223,7 @@ return `${hours.padStart(2, '0')}:${minutes.padStart(2, '0')}:00`; // Ensure valid HH:mm:ss format }, async addOvertime() { - if (!this.selectedDate || !this.selectedDayType || !this.selectedAirStation) { + if (!this.selectedDate || !this.selectedDayType) { alert("Please fill in all required fields."); return; } @@ -241,70 +242,44 @@ console.log("Sending userId:", this.userId); const reader = new FileReader(); - reader.onload = async (event) => { - const base64String = event.target.result.split(',')[1]; + reader.onload = async (event) => { + const base64String = event.target.result.split(',')[1]; - // Generate a crypto key - const key = await crypto.subtle.generateKey( - { name: "AES-GCM", length: 256 }, - true, - ["encrypt", "decrypt"] - ); + const payload = { + otDate: this.selectedDate, + officeFrom: this.formatTime(this.officeFrom) || null, + officeTo: this.formatTime(this.officeTo) || null, + officeBreak: this.officeBreak || 0, + outsideFrom: this.formatTime(this.outsideFrom) || null, + outsideTo: this.formatTime(this.outsideTo) || null, + outsideBreak: this.outsideBreak || 0, + stationId: this.selectedAirStation || null, + otDescription: this.otDescription, + otDays: this.selectedDayType, + pdfBase64: base64String, + userId: this.userId, + }; - // Export the key (for sending to backend if needed or for testing) - const exportedKey = await crypto.subtle.exportKey("jwk", key); + try { + const response = await fetch(`${window.location.origin}/OvertimeAPI/AddOvertime`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }); - // Generate a random IV - const iv = crypto.getRandomValues(new Uint8Array(12)); - - // Encode data - const encoded = new TextEncoder().encode(base64String); - - // Encrypt the data - const encrypted = await crypto.subtle.encrypt( - { name: "AES-GCM", iv: iv }, - key, - encoded - ); - - // Convert encrypted data to base64 - const encryptedBase64 = btoa(String.fromCharCode(...new Uint8Array(encrypted))); - const ivBase64 = btoa(String.fromCharCode(...iv)); - - const payload = { - otDate: this.selectedDate, - officeFrom: this.formatTime(this.officeFrom) || null, - officeTo: this.formatTime(this.officeTo) || null, - officeBreak: this.officeBreak || 0, - outsideFrom: this.formatTime(this.outsideFrom) || null, - outsideTo: this.formatTime(this.outsideTo) || null, - outsideBreak: this.outsideBreak || 0, - stationId: this.selectedAirStation, - otDescription: this.otDescription, - otDays: this.selectedDayType, - pdfBase64: encryptedBase64, - ivBase64: ivBase64, // store this too - userId: this.userId, - encryptionKey: exportedKey.k, // only if you're doing a simple demo; ideally don't send this from frontend - }; - - // Send to backend - const response = await fetch(`${window.location.origin}/OvertimeAPI/AddOvertime`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - const errorText = await response.text(); - throw new Error(`HTTP error! status: ${response.status} - ${errorText}`); - } - - const result = await response.json(); - alert(result.message); - this.clearForm(); - }; + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`HTTP error! status: ${response.status} - ${errorText}`); + } + const result = await response.json(); + alert(result.message); + this.clearForm(); + } catch (error) { + console.error("Error adding overtime:", error); + alert("Failed to save overtime. Please check the console for errors."); + } + }; reader.onerror = () => { console.error("Error reading file"); diff --git a/Controllers/API/OvertimeAPI.cs b/Controllers/API/OvertimeAPI.cs index 92a79f2..e78bb85 100644 --- a/Controllers/API/OvertimeAPI.cs +++ b/Controllers/API/OvertimeAPI.cs @@ -18,6 +18,7 @@ using System.Diagnostics; using System.Reflection; using static System.Collections.Specialized.BitVector32; using System.Security.Claims; +using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages; namespace PSTW_CentralSystem.Controllers.API @@ -424,6 +425,65 @@ namespace PSTW_CentralSystem.Controllers.API #endregion + #region Ot Records + [HttpGet("GetUserOvertimeRecords/{userId}")] + public IActionResult GetUserOvertimeRecords(int userId) + { + try + { + var records = _centralDbContext.Otregisters + .Where(o => o.UserId == userId) + .Select(o => new + { + o.OvertimeId, + o.OtDate, + o.OfficeFrom, + o.OfficeTo, + o.OfficeBreak, + o.OutsideFrom, + o.OutsideTo, + o.OutsideBreak, + o.StationId, + StationName = o.Stations != null ? o.Stations.StationName : "N/A", + o.OtDescription, + o.OtDays, + o.PDFBase64, + o.UserId + }) + .OrderByDescending(o => o.OtDate) + .ToList(); + return Ok(records); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to fetch OT records."); + return StatusCode(500, "Error retrieving OT records."); + } + } + + + [HttpDelete("DeleteOvertimeRecord/{id}")] + public IActionResult DeleteOvertimeRecord(int id) + { + try + { + var record = _centralDbContext.Otregisters.FirstOrDefault(o => o.OvertimeId == id); + if (record == null) + return NotFound("Overtime record not found."); + + _centralDbContext.Otregisters.Remove(record); + _centralDbContext.SaveChanges(); + + return Ok(new { message = "Record deleted successfully." }); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to delete OT record."); + return StatusCode(500, "Error deleting OT record."); + } + } + + #endregion } } \ No newline at end of file diff --git a/Views/Shared/_Layout.cshtml b/Views/Shared/_Layout.cshtml index 331cb05..8a4e6bc 100644 --- a/Views/Shared/_Layout.cshtml +++ b/Views/Shared/_Layout.cshtml @@ -42,6 +42,7 @@ + @@ -52,6 +53,7 @@ +