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";
}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | {{ formatDate(record.otDate) }} |
+ {{ formatTime(record.officeFrom) }} |
+ {{ formatTime(record.officeTo) }} |
+ {{ record.officeBreak }} min |
+ {{ formatTime(record.outsideFrom) }} |
+ {{ formatTime(record.outsideTo) }} |
+ {{ record.outsideBreak }} min |
+ {{ calcTotalHours(record).toFixed(2) }} |
+ {{ calcBreakTotal(record) }} |
+ {{ formatHourMinute(calcNetHours(record)) }} |
+ {{ record.stationName || 'N/A' }} |
+ {{ record.otDescription }} |
+
+
+
+
+ -
+ |
+
+
+
+ |
+
+
+
+ | No records found for selected month and year. |
+
+
+ | TOTAL |
+ |
+ {{ totalHours.toFixed(2) }} |
+ {{ totalBreak }} |
+ {{ formatHourMinute(totalNetTime) }} |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+@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 @@
+