+
-
+
+
@@ -147,7 +167,8 @@
stationId: "",
otDescription: "",
otDays: "",
- pdfBase64: "",
+ filePath: "",
+ newFile: null,
userId: null,
},
airstationList: [],
@@ -291,11 +312,7 @@
const file = event.target.files[0];
if (file && file.type === "application/pdf") {
- const reader = new FileReader();
- reader.onload = () => {
- this.editForm.pdfBase64 = reader.result.split(',')[1]; // Remove data URI prefix
- };
- reader.readAsDataURL(file);
+ this.editForm.newFile = file;
} else {
alert("Please upload a valid PDF file.");
}
@@ -305,21 +322,46 @@
const [hours, minutes] = timeString.split(':');
return `${hours.padStart(2, '0')}:${minutes.padStart(2, '0')}:00`; //HH:mm:ss format
},
- async updateRecord() {
+ async updateRecord() {
try {
- const payload = {
- ...this.editForm,
- otDays: this.editForm.selectedDayType, // ensure correct day value
- officeFrom: this.formatTime(this.editForm.officeFrom),
- officeTo: this.formatTime(this.editForm.officeTo),
- outsideFrom: this.formatTime(this.editForm.outsideFrom),
- outsideTo: this.formatTime(this.editForm.outsideTo)
- };
+ const formData = new FormData();
+
+ formData.append("OvertimeId", this.editForm.overtimeId);
+ formData.append("OtDate", this.editForm.otDate);
+ formData.append("StationId", this.editForm.stationId || "");
+ formData.append("OtDescription", this.editForm.otDescription || "");
+ formData.append("OtDays", this.editForm.selectedDayType);
+ formData.append("OfficeFrom", this.formatTime(this.editForm.officeFrom) || "");
+ formData.append("OfficeTo", this.formatTime(this.editForm.officeTo) || "");
+ formData.append("OutsideFrom", this.formatTime(this.editForm.outsideFrom) || "");
+ formData.append("OutsideTo", this.formatTime(this.editForm.outsideTo) || "");
+ formData.append("FilePath", this.editForm.filePath || "");
+
+
+ // Conditionally include nullable fields
+ if (this.editForm.outsideFrom) {
+ formData.append("outsideFrom", this.formatTime(this.editForm.outsideFrom));
+ }
+ if (this.editForm.outsideTo) {
+ formData.append("outsideTo", this.formatTime(this.editForm.outsideTo));
+ }
+
+ formData.append("officeBreak", this.editForm.officeBreak || 0);
+ formData.append("outsideBreak", this.editForm.outsideBreak || 0);
+
+ // Required field
+ formData.append("userId", this.currentUser?.id);
+
+ // File handling
+ if (this.editForm.newFile) {
+ formData.append("newFile", this.editForm.newFile);
+ } else {
+ formData.append("filePath", this.editForm.filePath || ""); // must send this
+ }
const res = await fetch('/OvertimeAPI/UpdateOvertimeRecord', {
- method: 'PUT',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(payload)
+ method: 'POST',
+ body: formData
});
if (res.ok) {
@@ -334,9 +376,23 @@
console.error("Update error:", err);
}
},
+
goBack() {
window.location.href = "/OTcalculate/Overtime/OtRecords";
},
+ clearOfficeHours() {
+ this.editForm.officeFrom = "";
+ this.editForm.officeTo = "";
+ this.editForm.officeBreak = 0;
+ this.calculateOTAndBreak();
+ },
+ clearOutsideHours() {
+ this.editForm.outsideFrom = "";
+ this.editForm.outsideTo = "";
+ this.editForm.outsideBreak = 0;
+ this.calculateOTAndBreak();
+ },
+
}
});
diff --git a/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml b/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml
index 768f4e8..20a188e 100644
--- a/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml
+++ b/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml
@@ -100,7 +100,7 @@
-
+
@@ -136,10 +136,10 @@
-
-
+
-
|
@@ -365,10 +365,7 @@
alert("An error occurred during submission.");
}
},
- viewPdf(base64) {
- const pdfWindow = window.open("");
- pdfWindow.document.write(`
`);
- }
+
}
});
app.mount("#app");
diff --git a/Areas/OTcalculate/Views/Overtime/OtRegister.cshtml b/Areas/OTcalculate/Views/Overtime/OtRegister.cshtml
index 3f2569a..1b49b92 100644
--- a/Areas/OTcalculate/Views/Overtime/OtRegister.cshtml
+++ b/Areas/OTcalculate/Views/Overtime/OtRegister.cshtml
@@ -270,56 +270,40 @@
return;
}
- console.log("Sending userId:", this.userId);
+ const formData = new FormData();
+ formData.append("file", this.uploadedFile);
+ formData.append("otDate", this.selectedDate);
+ formData.append("officeFrom", this.officeFrom ? this.formatTime(this.officeFrom) : null);
+ formData.append("officeTo", this.officeTo ? this.formatTime(this.officeTo) : null);
+ formData.append("officeBreak", this.officeBreak);
+ formData.append("outsideFrom", this.outsideFrom ? this.formatTime(this.outsideFrom) : null);
+ formData.append("outsideTo", this.outsideTo ? this.formatTime(this.outsideTo) : null);
+ formData.append("outsideBreak", this.outsideBreak);
+ formData.append("stationId", this.isPSTWAIR ? parseInt(this.selectedAirStation) : "");
+ formData.append("otDescription", this.otDescription.trim().split(/\s+/).slice(0, 50).join(' '));
+ formData.append("otDays", this.selectedDayType);
+ formData.append("userId", this.userId);
- const reader = new FileReader();
- reader.onload = async (event) => {
- const base64String = event.target.result.split(',')[1];
+ try {
+ const response = await fetch(`${window.location.origin}/OvertimeAPI/AddOvertime`, {
+ method: "POST",
+ body: formData,
+ });
- 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.isPSTWAIR ? parseInt(this.selectedAirStation) : null,
- otDescription: this.otDescription.trim().split(/\s+/).slice(0, 50).join(' '),
- otDays: this.selectedDayType,
- pdfBase64: base64String,
- userId: this.userId,
- };
-
-
- try {
- 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();
- } catch (error) {
- console.error("Error adding overtime:", error);
- alert("Failed to save overtime. Please check the console for errors.");
+ if (!response.ok) {
+ const errorText = await response.text();
+ throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
}
- };
- reader.onerror = () => {
- console.error("Error reading file");
- alert("Error reading the uploaded file.");
- };
-
- reader.readAsDataURL(this.uploadedFile);
+ 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.");
+ }
},
+
clearForm() {
this.selectedDate = "";
this.officeFrom = "";
diff --git a/Controllers/API/Inventory/InvMainAPI.cs b/Controllers/API/Inventory/InvMainAPI.cs
index 02e03d5..96d5e79 100644
--- a/Controllers/API/Inventory/InvMainAPI.cs
+++ b/Controllers/API/Inventory/InvMainAPI.cs
@@ -163,7 +163,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
try
{
product.QuantityProduct = 0;
- var productImage = product.ImageProduct; // Save image to wwwroot/media/inventory/images | Images name is product.ModelNo | product.ImageProduct is in base64 string
+ var productImage = product.ImageProduct;
if (!string.IsNullOrEmpty(product.ImageProduct))
{
var bytes = Convert.FromBase64String(product.ImageProduct);
diff --git a/Controllers/API/OvertimeAPI.cs b/Controllers/API/OvertimeAPI.cs
index 519aeef..c369ff4 100644
--- a/Controllers/API/OvertimeAPI.cs
+++ b/Controllers/API/OvertimeAPI.cs
@@ -36,14 +36,15 @@ namespace PSTW_CentralSystem.Controllers.API
private readonly CentralSystemContext _centralDbContext;
private readonly UserManager
_userManager;
private readonly OvertimePdfService _pdfService;
+ private readonly IWebHostEnvironment _env;
-
- public OvertimeAPI(ILogger logger, CentralSystemContext centralDbContext, UserManager userManager, OvertimePdfService pdfService)
+ public OvertimeAPI(ILogger logger, CentralSystemContext centralDbContext, UserManager userManager, OvertimePdfService pdfService, IWebHostEnvironment env)
{
_logger = logger;
_centralDbContext = centralDbContext;
_userManager = userManager;
_pdfService = pdfService;
+ _env = env;
}
#region Settings
@@ -390,38 +391,82 @@ namespace PSTW_CentralSystem.Controllers.API
[HttpPost("AddOvertime")]
- public async Task AddOvertimeAsync([FromBody] OtRegisterModel model)
+ public async Task AddOvertimeAsync([FromForm] OvertimeRequestDto request)
{
- _logger.LogInformation("AddOvertimeAsync called.");
+ _logger.LogInformation("AddOvertimeAsync called (with file upload).");
- if (model == null)
+ if (request == null)
{
- _logger.LogError("Model is null.");
+ _logger.LogError("Request is null.");
return BadRequest("Invalid data.");
}
try
{
- _logger.LogInformation($"Received model: {System.Text.Json.JsonSerializer.Serialize(model)}");
-
- if (model.UserId == 0)
+ if (request.UserId == 0)
{
_logger.LogWarning("No user ID provided.");
return BadRequest("User ID is required.");
}
- // Convert string time values to TimeSpan before saving
- model.OfficeFrom = model.GetOfficeFrom();
- model.OfficeTo = model.GetOfficeTo();
- model.OutsideFrom = model.GetOutsideFrom();
- model.OutsideTo = model.GetOutsideTo();
+ // Parse times
+ TimeSpan? officeFrom = TimeSpan.TryParse(request.OfficeFrom, out var of) ? of : (TimeSpan?)null;
+ TimeSpan? officeTo = TimeSpan.TryParse(request.OfficeTo, out var ot) ? ot : (TimeSpan?)null;
+ TimeSpan? outsideFrom = TimeSpan.TryParse(request.OutsideFrom, out var ofr) ? ofr : (TimeSpan?)null;
+ TimeSpan? outsideTo = TimeSpan.TryParse(request.OutsideTo, out var otr) ? otr : (TimeSpan?)null;
- _logger.LogInformation($"Time spans parsed successfully.");
+ if ((officeFrom != null && officeTo == null) || (officeFrom == null && officeTo != null))
+ {
+ return BadRequest("Both Office From and To must be filled if one is provided.");
+ }
- _centralDbContext.Otregisters.Add(model);
+ if ((outsideFrom != null && outsideTo == null) || (outsideFrom == null && outsideTo != null))
+ {
+ return BadRequest("Both Outside From and To must be filled if one is provided.");
+ }
+
+ // Save file
+ string pdfPath = null;
+ if (request.File != null && request.File.Length > 0)
+ {
+ var uploadsFolder = Path.Combine(_env.WebRootPath, "Media", "Overtime");
+
+ if (!Directory.Exists(uploadsFolder))
+ Directory.CreateDirectory(uploadsFolder);
+
+ var fileName = $"OT_{Guid.NewGuid()}{Path.GetExtension(request.File.FileName)}";
+ var filePath = Path.Combine(uploadsFolder, fileName);
+
+ using (var stream = new FileStream(filePath, FileMode.Create))
+ {
+ await request.File.CopyToAsync(stream);
+ }
+
+ // This is the relative public URL path
+ pdfPath = $"/media/overtime/{fileName}";
+ }
+
+
+ // Map to DB model
+ var newRecord = new OtRegisterModel
+ {
+ OtDate = request.OtDate,
+ OfficeFrom = officeFrom,
+ OfficeTo = officeTo,
+ OfficeBreak = request.OfficeBreak,
+ OutsideFrom = outsideFrom,
+ OutsideTo = outsideTo,
+ OutsideBreak = request.OutsideBreak,
+ StationId = request.StationId,
+ OtDescription = request.OtDescription,
+ OtDays = request.OtDays,
+ UserId = request.UserId,
+ FilePath = pdfPath
+ };
+
+ _centralDbContext.Otregisters.Add(newRecord);
await _centralDbContext.SaveChangesAsync();
- _logger.LogInformation("Overtime registered successfully.");
return Ok(new { message = "Overtime registered successfully." });
}
catch (Exception ex)
@@ -431,6 +476,7 @@ namespace PSTW_CentralSystem.Controllers.API
}
}
+
#endregion
#region Ot Records
@@ -455,7 +501,7 @@ namespace PSTW_CentralSystem.Controllers.API
StationName = o.Stations != null ? o.Stations.StationName : "N/A",
o.OtDescription,
o.OtDays,
- o.PDFBase64,
+ o.FilePath,
o.UserId
})
.OrderByDescending(o => o.OtDate)
@@ -470,7 +516,6 @@ namespace PSTW_CentralSystem.Controllers.API
}
}
-
[HttpDelete("DeleteOvertimeRecord/{id}")]
public IActionResult DeleteOvertimeRecord(int id)
{
@@ -478,11 +523,31 @@ namespace PSTW_CentralSystem.Controllers.API
{
var record = _centralDbContext.Otregisters.FirstOrDefault(o => o.OvertimeId == id);
if (record == null)
+ {
+ _logger.LogWarning("Overtime record not found for Id: {OvertimeId}", id);
return NotFound("Overtime record not found.");
+ }
+ // 1. Delete the file from the server
+ if (!string.IsNullOrEmpty(record.FilePath))
+ {
+ var filePath = Path.Combine(_env.WebRootPath, record.FilePath.TrimStart('/')); // Construct full path
+ if (System.IO.File.Exists(filePath))
+ {
+ System.IO.File.Delete(filePath);
+ _logger.LogInformation("File deleted successfully: {FilePath}", filePath);
+ }
+ else
+ {
+ _logger.LogWarning("File not found, could not delete: {FilePath}", filePath);
+ }
+ }
+
+ // 2. Delete the record from the database
_centralDbContext.Otregisters.Remove(record);
_centralDbContext.SaveChanges();
+ _logger.LogInformation("Overtime record deleted successfully for Id: {OvertimeId}", id);
return Ok(new { message = "Record deleted successfully." });
}
catch (Exception ex)
@@ -513,7 +578,7 @@ namespace PSTW_CentralSystem.Controllers.API
if (existingRecord != null)
{
_logger.LogDebug("Updating existing record with OvertimeId: {OvertimeId}", record.OvertimeId);
- existingRecord.PDFBase64 = record.PDFBase64;
+ existingRecord.FilePath = record.FilePath;
_centralDbContext.Otregisters.Update(existingRecord);
}
else
@@ -575,6 +640,7 @@ namespace PSTW_CentralSystem.Controllers.API
#endregion
+ #region Ot Edit
[HttpGet("GetOvertimeRecordById/{id}")]
public async Task GetOvertimeRecordById(int id)
{
@@ -585,16 +651,49 @@ namespace PSTW_CentralSystem.Controllers.API
return Ok(record);
}
- [HttpPut("UpdateOvertimeRecord")]
- public IActionResult UpdateOvertimeRecord([FromBody] OtRegisterModel model)
+ [HttpPost]
+ [Route("UpdateOvertimeRecord")]
+ public async Task UpdateOvertimeRecord([FromForm] OtRegisterModel model, IFormFile? newFile)
{
+ _logger.LogInformation("UpdateOvertimeRecord called. Model: {@Model}", model);
+
try
{
+ if (!ModelState.IsValid)
+ {
+ _logger.LogWarning("ModelState is invalid. Errors: {@ModelStateErrors}", ModelState.Values.SelectMany(v => v.Errors));
+ return BadRequest(ModelState);
+ }
+
var existing = _centralDbContext.Otregisters.FirstOrDefault(o => o.OvertimeId == model.OvertimeId);
if (existing == null)
+ {
+ _logger.LogWarning("Overtime record not found for Id: {OvertimeId}", model.OvertimeId);
return NotFound("Overtime record not found.");
+ }
- // Update fields
+ _logger.LogInformation("Existing record found: {@ExistingRecord}", existing);
+
+ string? oldFilePath = existing.FilePath; // Store the old file path
+
+ string? newPdfPath = null; // Initialize the new file path
+
+ // Handle new file upload
+ if (newFile != null && newFile.Length > 0)
+ {
+ var fileName = $"OT_{Guid.NewGuid()}{Path.GetExtension(newFile.FileName)}";
+ var savePath = Path.Combine(_env.WebRootPath, "media", "Overtime", fileName);
+ newPdfPath = $"/media/Overtime/{fileName}";
+
+ using (var stream = new FileStream(savePath, FileMode.Create))
+ {
+ await newFile.CopyToAsync(stream);
+ }
+
+ existing.FilePath = newPdfPath; // Update the file path in the database
+ }
+
+ // Update other fields
existing.OtDate = model.OtDate;
existing.OfficeFrom = model.OfficeFrom;
existing.OfficeTo = model.OfficeTo;
@@ -605,19 +704,35 @@ namespace PSTW_CentralSystem.Controllers.API
existing.StationId = model.StationId;
existing.OtDescription = model.OtDescription;
existing.OtDays = model.OtDays;
- existing.PDFBase64 = model.PDFBase64;
existing.UserId = model.UserId;
_centralDbContext.SaveChanges();
+ _logger.LogInformation("Overtime record updated successfully for Id: {OvertimeId}", model.OvertimeId);
+
+ // Delete the old file after saving the new one (if a new file was uploaded)
+ if (newFile != null && !string.IsNullOrEmpty(oldFilePath))
+ {
+ var fullOldFilePath = Path.Combine(_env.WebRootPath, oldFilePath.TrimStart('/'));
+ if (System.IO.File.Exists(fullOldFilePath))
+ {
+ System.IO.File.Delete(fullOldFilePath);
+ _logger.LogInformation("Old file deleted successfully: {OldFilePath}", fullOldFilePath);
+ }
+ else
+ {
+ _logger.LogWarning("Old file not found, could not delete: {OldFilePath}", fullOldFilePath);
+ }
+ }
+
return Ok(new { message = "Record updated successfully." });
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error updating overtime record.");
+ _logger.LogError(ex, "Error updating overtime record. Stack Trace: {StackTrace}", ex.StackTrace);
return StatusCode(500, "Failed to update record.");
}
}
-
+ #endregion
}
diff --git a/PSTW_CentralSystem.csproj b/PSTW_CentralSystem.csproj
index 01e6685..18c50a0 100644
--- a/PSTW_CentralSystem.csproj
+++ b/PSTW_CentralSystem.csproj
@@ -38,6 +38,7 @@
+
diff --git a/wwwroot/Media/Overtime/OT_0c935b6f-beb7-459d-ae67-807251e104a3.pdf b/wwwroot/Media/Overtime/OT_0c935b6f-beb7-459d-ae67-807251e104a3.pdf
new file mode 100644
index 0000000..ebd87b5
Binary files /dev/null and b/wwwroot/Media/Overtime/OT_0c935b6f-beb7-459d-ae67-807251e104a3.pdf differ