edit
This commit is contained in:
parent
872eb2363a
commit
1400532680
@ -2,5 +2,344 @@
|
||||
ViewData["Title"] = "Edit Overtime";
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
|
||||
<div id="app" class="container mt-4 d-flex justify-content-center">
|
||||
<div class="card shadow-sm" style="width: 1100px;">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="dateInput">Date</label>
|
||||
<input type="date" class="form-control" v-model="editForm.otDate"
|
||||
v-on:input="calculateOTAndBreak">
|
||||
</div>
|
||||
|
||||
<h6 class="fw-bold">OFFICE HOURS</h6>
|
||||
<div class="row mb-3">
|
||||
<div class="col-4">
|
||||
<label for="officeFrom">From</label>
|
||||
<input type="time" class="form-control" v-model="editForm.officeFrom"
|
||||
v-on:input="calculateOTAndBreak">
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label for="officeTo">To</label>
|
||||
<input type="time" id="officeTo" class="form-control" v-model="editForm.officeTo"
|
||||
v-on:input="calculateOTAndBreak">
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label for="officeBreak">Break Hours (Minutes)</label>
|
||||
<input type="number" id="officeBreak" class="form-control" v-model="editForm.officeBreak"
|
||||
v-on:input="calculateOTAndBreak" placeholder="e.g. 30">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h6 class="fw-bold text-danger">OUTSIDE OFFICE HOURS</h6>
|
||||
<div class="row mb-2">
|
||||
<div class="col-4">
|
||||
<label for="outsideFrom">From</label>
|
||||
<input type="time" id="outsideFrom" class="form-control" v-model="editForm.outsideFrom"
|
||||
v-on:input="calculateOTAndBreak">
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label for="outsideTo">To</label>
|
||||
<input type="time" id="outsideTo" class="form-control" v-model="editForm.outsideTo"
|
||||
v-on:input="calculateOTAndBreak">
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label for="outsideBreak">Break Hours (Minutes)</label>
|
||||
<input type="number" id="outsideBreak" class="form-control" v-model="editForm.outsideBreak"
|
||||
v-on:input="calculateOTAndBreak" placeholder="e.g. 45">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3" v-if="isPSTWAIR">
|
||||
<label for="airstationDropdown">Air Station</label>
|
||||
<select id="airstationDropdown" class="form-control" v-model="editForm.stationId">
|
||||
<option value="" disabled selected>Select Station</option>
|
||||
<option v-for="station in airstationList" :key="station.stationId" :value="station.stationId">
|
||||
{{ station.stationName || 'Unnamed Station' }}
|
||||
</option>
|
||||
</select>
|
||||
<small class="text-danger">*Only for PSTW AIR</small>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="otDescription">Work Brief Description</label>
|
||||
<textarea id="otDescription" class="form-control"
|
||||
v-model="editForm.otDescription"
|
||||
v-on:input="limitCharCount"
|
||||
placeholder="Describe the work done..."></textarea>
|
||||
<small class="text-muted">
|
||||
{{ charCount }} / 150 characters
|
||||
</small>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-md-5">
|
||||
<label class="mb-2">Day</label>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" id="weekday" v-model="editForm.selectedDayType" value="Weekday">
|
||||
<label class="form-check-label" for="weekday">Weekday</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" id="weekend" v-model="editForm.selectedDayType" value="Weekend">
|
||||
<label class="form-check-label" for="weekend">Weekend</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" id="publicHoliday" v-model="editForm.selectedDayType"
|
||||
value="Public Holiday">
|
||||
<label class="form-check-label" for="publicHoliday">Public Holiday</label>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 mt-3">
|
||||
<label for="fileUpload">Upload File:</label>
|
||||
<input type="file" id="fileUpload" class="form-control" accept="application/pdf"
|
||||
v-on:change="handleFileUpload" ref="fileInput">
|
||||
<small class="text-danger">*upload pdf file only</small>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 d-flex flex-column align-items-center">
|
||||
<label for="totalOTHours">Total OT Hours</label>
|
||||
<input type="text" id="totalOTHours" class="form-control text-center" v-model="totalOTHours"
|
||||
style="width: 200px;" readonly>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 d-flex flex-column align-items-center">
|
||||
<label for="totalBreakHours">Total Break Hours</label>
|
||||
<input type="text" id="totalBreakHours" class="form-control text-center" v-model="totalBreakHours"
|
||||
style="width: 200px;" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end mt-3">
|
||||
<button class="btn btn-danger" v-on:click="goBack">Cancel</button>
|
||||
<button class="btn btn-success ms-3" v-on:click="updateRecord">Update</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@{
|
||||
await Html.RenderPartialAsync("_ValidationScriptsPartial");
|
||||
}
|
||||
<script>
|
||||
const app = Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
editForm: {
|
||||
overtimeId: null,
|
||||
otDate: "",
|
||||
officeFrom: "",
|
||||
officeTo: "",
|
||||
officeBreak: 0,
|
||||
outsideFrom: "",
|
||||
outsideTo: "",
|
||||
outsideBreak: 0,
|
||||
stationId: "",
|
||||
otDescription: "",
|
||||
otDays: "",
|
||||
pdfBase64: "",
|
||||
userId: null,
|
||||
},
|
||||
airstationList: [],
|
||||
totalOTHours: "0 hr 0 min",
|
||||
totalBreakHours: "0 hr 0 min",
|
||||
uploadedFile: null,
|
||||
currentUser: null,
|
||||
isPSTWAIR: false,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
charCount() {
|
||||
return this.editForm.otDescription.length;
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const overtimeId = urlParams.get('overtimeId');
|
||||
if (overtimeId) {
|
||||
await this.fetchOvertimeRecord(overtimeId);
|
||||
}
|
||||
|
||||
await this.fetchUser();
|
||||
|
||||
if (this.isPSTWAIR) {
|
||||
await this.fetchStations();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async fetchOvertimeRecord(id) {
|
||||
try {
|
||||
const res = await fetch(`/OvertimeAPI/GetOvertimeRecordById/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
this.populateForm(data); // Fill form fields with data
|
||||
} else {
|
||||
alert("Failed to fetch overtime record.");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Fetch error:", err);
|
||||
}
|
||||
},
|
||||
populateForm(record) {
|
||||
this.editForm = {
|
||||
...record,
|
||||
otDate: record.otDate.slice(0, 10),
|
||||
selectedDayType: record.otDays,
|
||||
};
|
||||
|
||||
this.calculateOTAndBreak();
|
||||
},
|
||||
async fetchStations() {
|
||||
try {
|
||||
const response = await fetch(`/OvertimeAPI/GetStationsByDepartment`);
|
||||
if (!response.ok) throw new Error("Failed to fetch stations");
|
||||
|
||||
this.airstationList = await response.json();
|
||||
} catch (error) {
|
||||
console.error("Error fetching stations:", error);
|
||||
}
|
||||
},
|
||||
async fetchUser() {
|
||||
try {
|
||||
const response = await fetch(`/IdentityAPI/GetUserInformation/`, { method: 'POST' });
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
this.currentUser = data?.userInfo || null;
|
||||
this.userId = this.currentUser?.id || null;
|
||||
|
||||
console.log("Fetched User:", this.currentUser);
|
||||
console.log("Dept ID:", this.currentUser?.departmentId);
|
||||
|
||||
if (this.currentUser?.department?.departmentId === 2) {
|
||||
this.isPSTWAIR = true;
|
||||
console.log("User is PSTW AIR");
|
||||
}
|
||||
} else {
|
||||
console.error(`Failed to fetch user: ${response.statusText}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching user:", error);
|
||||
}
|
||||
},
|
||||
limitCharCount(event) {
|
||||
if (this.editForm.otDescription && this.editForm.otDescription.length > 150) {
|
||||
this.editForm.otDescription = this.editForm.otDescription.substring(0, 150);
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
calculateOTAndBreak() {
|
||||
let officeOT = this.calculateTimeDifference(
|
||||
this.editForm.officeFrom,
|
||||
this.editForm.officeTo,
|
||||
this.editForm.officeBreak
|
||||
);
|
||||
|
||||
let outsideOT = this.calculateTimeDifference(
|
||||
this.editForm.outsideFrom,
|
||||
this.editForm.outsideTo,
|
||||
this.editForm.outsideBreak
|
||||
);
|
||||
|
||||
let totalOTMinutes = officeOT.minutes + outsideOT.minutes;
|
||||
let totalOTHours = officeOT.hours + outsideOT.hours + Math.floor(totalOTMinutes / 60);
|
||||
totalOTMinutes = totalOTMinutes % 60;
|
||||
|
||||
this.totalOTHours = `${totalOTHours} hr ${totalOTMinutes} min`;
|
||||
|
||||
let totalBreakMinutes = (this.editForm.officeBreak || 0) + (this.editForm.outsideBreak || 0);
|
||||
let totalBreakHours = Math.floor(totalBreakMinutes / 60);
|
||||
totalBreakMinutes = totalBreakMinutes % 60;
|
||||
|
||||
this.totalBreakHours = `${totalBreakHours} hr ${totalBreakMinutes} min`;
|
||||
},
|
||||
calculateTimeDifference(startTime, endTime, breakMinutes) {
|
||||
if (!startTime || !endTime) {
|
||||
return { hours: 0, minutes: 0 };
|
||||
}
|
||||
|
||||
const start = this.parseTime(startTime);
|
||||
const end = this.parseTime(endTime);
|
||||
|
||||
let diffMinutes = (end.hours * 60 + end.minutes) - (start.hours * 60 + start.minutes);
|
||||
if (diffMinutes < 0) {
|
||||
diffMinutes += 24 * 60;
|
||||
}
|
||||
|
||||
diffMinutes -= breakMinutes || 0;
|
||||
|
||||
const hours = Math.floor(diffMinutes / 60);
|
||||
const minutes = diffMinutes % 60;
|
||||
|
||||
return { hours, minutes };
|
||||
},
|
||||
parseTime(timeString) {
|
||||
const [hours, minutes] = timeString.split(':').map(Number);
|
||||
return { hours, minutes };
|
||||
},
|
||||
handleFileUpload(event) {
|
||||
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);
|
||||
} else {
|
||||
alert("Please upload a valid PDF file.");
|
||||
}
|
||||
},
|
||||
formatTime(timeString) {
|
||||
if (!timeString) return null;
|
||||
const [hours, minutes] = timeString.split(':');
|
||||
return `${hours.padStart(2, '0')}:${minutes.padStart(2, '0')}:00`; //HH:mm:ss format
|
||||
},
|
||||
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 res = await fetch('/OvertimeAPI/UpdateOvertimeRecord', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
alert("Record updated successfully.");
|
||||
window.location.href = "/OTcalculate/Overtime/OtRecords";
|
||||
} else {
|
||||
const errorText = await res.text();
|
||||
console.error("Server returned:", errorText);
|
||||
alert("Failed to update record.");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Update error:", err);
|
||||
}
|
||||
},
|
||||
goBack() {
|
||||
window.location.href = "/OTcalculate/Overtime/OtRecords";
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
app.mount("#app");
|
||||
</script>
|
||||
}
|
||||
@ -297,7 +297,7 @@
|
||||
},
|
||||
editRecord(index) {
|
||||
const record = this.filteredRecords[index];
|
||||
window.location.href = `/OTcalculate/Overtime/EditOvertime?id=${record.overtimeId}`;
|
||||
window.location.href = `/OTcalculate/Overtime/EditOvertime?overtimeId=${record.overtimeId}`;
|
||||
},
|
||||
async deleteRecord(index) {
|
||||
const record = this.filteredRecords[index];
|
||||
@ -322,7 +322,7 @@
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
const printWindow = window.open(blobUrl, '_blank');
|
||||
|
||||
// Automatically trigger print after window loads
|
||||
// Trigger print after window loads
|
||||
printWindow.onload = () => {
|
||||
printWindow.focus();
|
||||
printWindow.print();
|
||||
|
||||
@ -100,7 +100,8 @@
|
||||
|
||||
<div class="mb-3 mt-3">
|
||||
<label for="fileUpload">Upload File:</label>
|
||||
<input type="file" id="fileUpload" class="form-control" v-on:change="handleFileUpload" ref="fileInput">
|
||||
<input type="file" id="fileUpload" class="form-control" accept="application/pdf"
|
||||
v-on:change="handleFileUpload" ref="fileInput">
|
||||
<small class="text-danger">*upload pdf file only</small>
|
||||
</div>
|
||||
|
||||
|
||||
@ -561,7 +561,6 @@ namespace PSTW_CentralSystem.Controllers.API
|
||||
.Where(o => o.UserId == userId && o.OtDate.Month == month && o.OtDate.Year == year)
|
||||
.ToList();
|
||||
|
||||
// Optional: load logo image as byte array
|
||||
byte[]? logoImage = null;
|
||||
var logoPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images", "logo.jpg");
|
||||
|
||||
@ -576,5 +575,50 @@ namespace PSTW_CentralSystem.Controllers.API
|
||||
|
||||
#endregion
|
||||
|
||||
[HttpGet("GetOvertimeRecordById/{id}")]
|
||||
public async Task<IActionResult> GetOvertimeRecordById(int id)
|
||||
{
|
||||
var record = await _centralDbContext.Otregisters.FindAsync(id);
|
||||
if (record == null)
|
||||
return NotFound();
|
||||
|
||||
return Ok(record);
|
||||
}
|
||||
|
||||
[HttpPut("UpdateOvertimeRecord")]
|
||||
public IActionResult UpdateOvertimeRecord([FromBody] OtRegisterModel model)
|
||||
{
|
||||
try
|
||||
{
|
||||
var existing = _centralDbContext.Otregisters.FirstOrDefault(o => o.OvertimeId == model.OvertimeId);
|
||||
if (existing == null)
|
||||
return NotFound("Overtime record not found.");
|
||||
|
||||
// Update fields
|
||||
existing.OtDate = model.OtDate;
|
||||
existing.OfficeFrom = model.OfficeFrom;
|
||||
existing.OfficeTo = model.OfficeTo;
|
||||
existing.OfficeBreak = model.OfficeBreak;
|
||||
existing.OutsideFrom = model.OutsideFrom;
|
||||
existing.OutsideTo = model.OutsideTo;
|
||||
existing.OutsideBreak = model.OutsideBreak;
|
||||
existing.StationId = model.StationId;
|
||||
existing.OtDescription = model.OtDescription;
|
||||
existing.OtDays = model.OtDays;
|
||||
existing.PDFBase64 = model.PDFBase64;
|
||||
existing.UserId = model.UserId;
|
||||
|
||||
_centralDbContext.SaveChanges();
|
||||
return Ok(new { message = "Record updated successfully." });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error updating overtime record.");
|
||||
return StatusCode(500, "Failed to update record.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user