This commit is contained in:
Naz 2025-04-08 17:21:11 +08:00
parent db408bb2db
commit 95db83ab3e
8 changed files with 128 additions and 36 deletions

View File

@ -0,0 +1,22 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using PSTW_CentralSystem.Areas.OTcalculate.Models;
using PSTW_CentralSystem.DBContext;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace PSTW_CentralSystem.Areas.OTcalculate.Controllers
{
[Area("OTcalculate")]
[Authorize]
public class HodDashboardController : Controller
{
public IActionResult HodApproval()
{
return View();
}
}
}

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using PSTW_CentralSystem.Areas.OTcalculate.Models;
using PSTW_CentralSystem.DBContext;
@ -10,6 +11,7 @@ using System.Threading.Tasks;
namespace PSTW_CentralSystem.Areas.OTcalculate.Controllers
{
[Area("OTcalculate")]
[Authorize]
public class HrDashboardController : Controller
{

View File

@ -1,8 +1,10 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace PSTW_CentralSystem.Areas.OTcalculate.Controllers
{
[Area("OTcalculate")]
[Authorize]
public class OvertimeController : Controller
{
public IActionResult OtRegister()

View File

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

View File

@ -28,6 +28,7 @@ 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; }

View File

@ -0,0 +1,17 @@
@{
ViewData["Title"] = "Overtime Approval";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<div class="container">
<div class="row justify-content-center">
<div class="col-6 col-md-6 col-lg-3">
<div class="card card-hover">
<h6 class="text-white">Rate</h6>
</div>
</div>
</div>
</div>

View File

@ -241,44 +241,70 @@
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];
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: base64String,
userId: this.userId,
};
// Generate a crypto key
const key = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"]
);
try {
const response = await fetch(`${window.location.origin}/OvertimeAPI/AddOvertime`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
// Export the key (for sending to backend if needed or for testing)
const exportedKey = await crypto.subtle.exportKey("jwk", key);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
}
// 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();
};
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");

View File

@ -539,6 +539,21 @@
</li>
</ul>
</li>
<li class="sidebar-item">
<a class="sidebar-link has-arrow waves-effect waves-dark"
href="javascript:void(0)"
aria-expanded="false">
<i class="mdi mdi-receipt"></i><span class="hide-menu">HoD Dashboard</span>
</a>
<ul aria-expanded="false" class="collapse first-level">
<li class="sidebar-item">
<a class="sidebar-link waves-effect waves-dark sidebar-link" asp-area="OTcalculate" asp-controller="HodDashboard" asp-action="HodApproval" aria-expanded="false">
<i class="mdi mdi-view-dashboard"></i><span class="hide-menu">OT Approval</span>
</a>
</li>
</ul>
</li>
<!-- <li class="sidebar-item">
<a class="sidebar-link waves-effect waves-dark sidebar-link"
href="charts.html"