226 lines
9.1 KiB
Plaintext
226 lines
9.1 KiB
Plaintext
@{
|
|
ViewData["Title"] = "OT Details Review";
|
|
Layout = "~/Views/Shared/_Layout.cshtml";
|
|
}
|
|
|
|
<style>
|
|
.table-container {
|
|
background-color: white;
|
|
border-radius: 15px;
|
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
|
padding: 25px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.table th, .table td {
|
|
vertical-align: middle;
|
|
text-align: center;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.header-green {
|
|
background-color: #d9ead3 !important;
|
|
}
|
|
|
|
.header-blue {
|
|
background-color: #cfe2f3 !important;
|
|
}
|
|
|
|
.header-orange {
|
|
background-color: #fce5cd !important;
|
|
}
|
|
|
|
.file-info {
|
|
margin-top: 10px;
|
|
font-size: 14px;
|
|
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
|
}
|
|
|
|
.file-info a {
|
|
color: #007bff;
|
|
text-decoration: none;
|
|
font-weight: 500;
|
|
margin-left: 8px;
|
|
}
|
|
|
|
.file-info a:hover {
|
|
text-decoration: underline;
|
|
color: #0056b3;
|
|
}
|
|
|
|
</style>
|
|
|
|
<div id="reviewApp" style="max-width: 1300px; margin: auto; font-size: 13px;">
|
|
<div class="table-container table-responsive">
|
|
<div style="margin-bottom: 15px;">
|
|
<strong>Employee Name:</strong> {{ userInfo.fullName }}<br />
|
|
<strong>Department:</strong> {{ userInfo.departmentName || 'N/A' }}<br />
|
|
<template v-if="userInfo.filePath">
|
|
<div class="file-info">
|
|
<strong>Uploaded File:</strong>
|
|
<a :href="'/' + userInfo.filePath" target="_blank">
|
|
View Uploaded File
|
|
</a>
|
|
</div>
|
|
</template>
|
|
|
|
</div>
|
|
<table class="table table-bordered table-sm table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th class="header-green" rowspan="2">Date</th>
|
|
<th class="header-blue" colspan="3">Office Hour<br><small>(8:30 - 17:30)</small></th>
|
|
<th class="header-blue" colspan="3">After Office Hour<br><small>(17:30 - 8:30)</small></th>
|
|
<th class="header-orange" rowspan="2">Total OT Hours</th>
|
|
<th class="header-orange" rowspan="2">Break (min)</th>
|
|
<th class="header-orange" rowspan="2">Net OT Hours</th>
|
|
<th class="header-orange" rowspan="2">Station</th>
|
|
<th class="header-green" rowspan="2">Days</th>
|
|
<th class="header-blue" rowspan="2">Description</th>
|
|
<th class="header-green" rowspan="2">Action</th>
|
|
</tr>
|
|
<tr>
|
|
<th class="header-blue">From</th>
|
|
<th class="header-blue">To</th>
|
|
<th class="header-blue">Break</th>
|
|
<th class="header-blue">From</th>
|
|
<th class="header-blue">To</th>
|
|
<th class="header-blue">Break</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="record in otRecords" :key="record.overtimeId">
|
|
<td>{{ formatDate(record.otDate) }}</td>
|
|
<td>{{ formatTime(record.officeFrom) }}</td>
|
|
<td>{{ formatTime(record.officeTo) }}</td>
|
|
<td>{{ record.officeBreak }}</td>
|
|
<td>{{ formatTime(record.afterFrom) }}</td>
|
|
<td>{{ formatTime(record.afterTo) }}</td>
|
|
<td>{{ record.afterBreak }}</td>
|
|
<td>{{ formatHourMinute(calculateTotalTime(record)) }}</td>
|
|
<td>{{ calculateTotalBreak(record) }}</td>
|
|
<td>{{ formatHourMinute(calculateNetTime(record)) }}</td>
|
|
<td>{{ record.stationName || 'N/A' }}</td>
|
|
<td>{{ record.otDays }}</td>
|
|
<td class="wrap-text">{{ record.otDescription }}</td>
|
|
<td>
|
|
<button class="btn btn-light border rounded-circle me-1"
|
|
v-on:click="editRecord(record.overtimeId)">
|
|
<i class="bi bi-pencil-fill text-warning fs-5"></i>
|
|
</button>
|
|
<button class="btn btn-light border rounded-circle"
|
|
v-on:click="deleteRecord(record.overtimeId)">
|
|
<i class="bi bi-trash-fill text-danger fs-5"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
<tr v-if="otRecords.length === 0">
|
|
<td colspan="14">No overtime details found for this submission.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="mt-3 d-flex flex-wrap gap-2">
|
|
<button class="btn btn-primary btn-sm" v-on:click=""><i class="bi bi-printer"></i> Print</button>
|
|
<button class="btn btn-dark btn-sm" v-on:click=""><i class="bi bi-file-pdf"></i> Save</button>
|
|
<button class="btn btn-success btn-sm" v-on:click=""><i class="bi bi-file-earmark-excel"></i> Excel</button>
|
|
</div>
|
|
</div>
|
|
@section Scripts {
|
|
<script>
|
|
const reviewApp = Vue.createApp({
|
|
data() {
|
|
return {
|
|
otRecords: [],
|
|
userInfo: {
|
|
fullName: '',
|
|
departmentName: '',
|
|
filePath: ''
|
|
},
|
|
|
|
};
|
|
},
|
|
methods: {
|
|
fetchOtRecords() {
|
|
const params = new URLSearchParams(window.location.search);
|
|
const statusId = params.get("statusId");
|
|
|
|
fetch(`/OvertimeAPI/GetOtRecordsByStatusId/${statusId}`)
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
this.otRecords = data.records;
|
|
this.userInfo = data.userInfo;
|
|
})
|
|
.catch(err => console.error("Error fetching OT records:", err));
|
|
},
|
|
|
|
formatDate(dateStr) {
|
|
const d = new Date(dateStr);
|
|
return d.toLocaleDateString();
|
|
},
|
|
formatTime(timeSpanStr) {
|
|
if (!timeSpanStr) return null;
|
|
const [hours, minutes] = timeSpanStr.split(':');
|
|
return `${hours.padStart(2, '0')}:${minutes.padStart(2, '0')}`;
|
|
},
|
|
calculateTotalTime(record) {
|
|
let totalMinutes = 0;
|
|
if (record.officeFrom && record.officeTo) {
|
|
totalMinutes += this.timeDifferenceInMinutes(record.officeFrom, record.officeTo);
|
|
}
|
|
if (record.afterFrom && record.afterTo) {
|
|
totalMinutes += this.timeDifferenceInMinutes(record.afterFrom, record.afterTo);
|
|
}
|
|
return totalMinutes;
|
|
},
|
|
calculateTotalBreak(record) {
|
|
return (record.officeBreak || 0) + (record.afterBreak || 0);
|
|
},
|
|
calculateNetTime(record) {
|
|
const totalMinutes = this.calculateTotalTime(record);
|
|
const breakMinutes = this.calculateTotalBreak(record);
|
|
return totalMinutes - breakMinutes;
|
|
},
|
|
timeDifferenceInMinutes(start, end) {
|
|
const [startHours, startMinutes] = start.split(':').map(Number);
|
|
const [endHours, endMinutes] = end.split(':').map(Number);
|
|
const startTimeInMinutes = startHours * 60 + startMinutes;
|
|
const endTimeInMinutes = endHours * 60 + endMinutes;
|
|
return endTimeInMinutes - startTimeInMinutes;
|
|
},
|
|
formatHourMinute(totalMinutes) {
|
|
const hours = Math.floor(totalMinutes / 60);
|
|
const minutes = totalMinutes % 60;
|
|
return `${hours} hr ${minutes} min`;
|
|
},
|
|
editRecord(id) {
|
|
window.location.href = `/OTcalculate/Overtime/EditOvertime?overtimeId=${id}`;
|
|
},
|
|
|
|
deleteRecord(id) {
|
|
if (!confirm("Are you sure you want to delete this record?")) return;
|
|
|
|
fetch(`/OvertimeAPI/DeleteOvertimeInOtReview/${id}`, {
|
|
method: 'DELETE'
|
|
})
|
|
.then(res => {
|
|
if (!res.ok) throw new Error("Failed to delete record.");
|
|
return res.json();
|
|
})
|
|
.then(data => {
|
|
alert(data.message);
|
|
this.fetchOtRecords(); // Refresh the table
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
alert("Error deleting record.");
|
|
});
|
|
}
|
|
},
|
|
mounted() {
|
|
this.fetchOtRecords();
|
|
}
|
|
});
|
|
reviewApp.mount("#reviewApp");
|
|
</script>
|
|
} |