PSTW_CentralizeSystem/Areas/OTcalculate/Views/Overtime/EditOvertime.cshtml
2025-06-11 10:30:45 +08:00

728 lines
36 KiB
Plaintext

@{
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 <span class="text-danger">*</span></label>
<input type="date" class="form-control" v-model="editForm.otDate" v-on:input="calculateOTAndBreak" required>
<small class="text-danger" v-if="validationErrors.otDate">{{ validationErrors.otDate }}</small>
</div>
<h6 class="fw-bold">OFFICE HOURS</h6>
<div class="d-flex gap-3 mb-3 align-items-end flex-wrap">
<div style="flex: 1;">
<label for="officeFrom">From</label>
<input type="time" id="officeFrom" class="form-control" v-model="editForm.officeFrom" v-on:change="updateTime('officeFrom')">
</div>
<div style="flex: 1;">
<label for="officeTo">To</label>
<input type="time" id="officeTo" class="form-control" v-model="editForm.officeTo" v-on:change="updateTime('officeTo')">
</div>
<div style="flex: 1;">
<label for="officeBreak">Break Hours (Minutes)</label>
<div class="d-flex">
<select id="officeBreak" class="form-control" v-model.number="editForm.officeBreak" v-on:change="calculateOTAndBreak">
<option v-for="opt in breakOptions" :key="opt.value" :value="opt.value">{{ opt.label }}</option>
</select>
<button class="btn btn-outline-danger ms-2" v-on:click="clearOfficeHours" title="Clear Office Hours">
<i class="bi bi-x-circle"></i>
</button>
</div>
</div>
</div>
<h6 class="fw-bold text-danger">AFTER OFFICE HOURS</h6>
<div class="d-flex gap-3 mb-3 align-items-end flex-wrap">
<div style="flex: 1;">
<label for="afterFrom">From</label>
<input type="time" id="afterFrom" class="form-control" v-model="editForm.afterFrom" v-on:change="updateTime('afterFrom')">
</div>
<div style="flex: 1;">
<label for="afterTo">To</label>
<input type="time" id="afterTo" class="form-control" v-model="editForm.afterTo" v-on:change="updateTime('afterTo')">
</div>
<div style="flex: 1;">
<label for="afterBreak">Break Hours (Minutes)</label>
<div class="d-flex">
<select id="afterBreak" class="form-control" v-model.number="editForm.afterBreak" v-on:change="calculateOTAndBreak">
<option v-for="opt in breakOptions" :key="opt.value" :value="opt.value">{{ opt.label }}</option>
</select>
<button class="btn btn-outline-danger ms-2" v-on:click="clearAfterHours" title="Clear After Office Hours">
<i class="bi bi-x-circle"></i>
</button>
</div>
</div>
</div>
<div class="mb-3" v-if="isPSTWAIR">
<label for="airstationDropdown">Air Station <span class="text-danger">*</span></label>
<select id="airstationDropdown" class="form-control select2" v-model="editForm.stationId" required>
<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" v-if="validationErrors.stationId">{{ validationErrors.stationId }}</small>
<small class="text-muted">*Only for PSTW AIR</small>
</div>
<div class="mb-3" v-if="isPSTWMARINE">
<label for="marinestationDropdown">Marine Station <span class="text-danger">*</span></label>
<select id="marinestationDropdown" class="form-control select2" v-model="editForm.stationId" required>
<option value="" disabled selected>Select Station</option>
<option v-for="station in marinestationList" :key="station.stationId" :value="station.stationId">
{{ station.stationName || 'Unnamed Station' }}
</option>
</select>
<small class="text-danger" v-if="validationErrors.stationId">{{ validationErrors.stationId }}</small>
<small class="text-muted">*Only for PSTW MARINE</small>
</div>
<div class="mb-3">
<label for="otDescription">Work Brief Description <span class="text-danger">*</span></label>
<textarea id="otDescription" class="form-control" v-model="editForm.otDescription" v-on:input="limitCharCount" placeholder="Describe the work done..." required></textarea>
<small class="text-danger" v-if="validationErrors.otDescription">{{ validationErrors.otDescription }}</small>
<small class="text-muted">{{ charCount }} / 150 characters</small>
</div>
</div>
<div class="col-md-5 mt-5">
<div class="mb-3 d-flex flex-column align-items-center">
<label for="userFlexiHourDisplay">Your Flexi Hour</label>
<input type="text" id="userFlexiHourDisplay" class="form-control text-center" :value="userFlexiHourDisplay" readonly style="width: 200px;">
</div>
<div class="mb-3 d-flex flex-column align-items-center">
<label for="detectedDayType">Day</label>
<input type="text" class="form-control text-center" v-model="editForm.otDays" readonly style="width: 200px;">
</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="validateAndUpdate">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,
afterFrom: "",
afterTo: "",
afterBreak: 0,
stationId: "",
otDescription: "",
otDays: "",
userId: null,
},
airstationList: [],
marinestationList: [],
totalOTHours: "0 hr 0 min",
totalBreakHours: "0 hr 0 min",
currentUser: null,
isPSTWAIR: false,
isPSTWMARINE: false,
userState: null,
publicHolidays: [],
userFlexiHour: null,
breakOptions: Array.from({ length: 15 }, (_, i) => {
const totalMinutes = i * 30;
const hours = Math.floor(totalMinutes / 60);
const minutes = totalMinutes % 60;
let label = '';
if (hours > 0) label += `${hours} hour${hours > 1 ? 's' : ''}`;
if (minutes > 0) label += `${label ? ' ' : ''}${minutes} min`;
if (!label) label = '0 min';
return { label, value: totalMinutes };
}),
previousPage: document.referrer,
returnMonth: null,
returnYear: null,
validationErrors: {
otDate: "",
stationId: "",
otDescription: "",
timeEntries: ""
}
};
},
computed: {
charCount() {
return this.editForm.otDescription.length;
},
userFlexiHourDisplay() {
return this.userFlexiHour ? this.userFlexiHour.flexiHour : "N/A";
}
},
async mounted() {
const urlParams = new URLSearchParams(window.location.search);
const overtimeId = urlParams.get('overtimeId');
this.returnMonth = urlParams.get('month');
this.returnYear = urlParams.get('year');
if (overtimeId) {
await this.fetchOvertimeRecord(overtimeId);
}
await this.fetchUserAndRelatedData();
const fetchStationPromises = [];
if (this.isPSTWAIR) {
fetchStationPromises.push(this.fetchStations());
}
if (this.isPSTWMARINE) {
fetchStationPromises.push(this.fetchStationsMarine());
}
await Promise.all(fetchStationPromises);
this.$nextTick(() => {
this.initializeSelect2Dropdowns();
if (this.editForm.stationId) {
if (this.isPSTWAIR) {
$('#airstationDropdown').val(this.editForm.stationId).trigger('change.select2');
}
if (this.isPSTWMARINE) {
$('#marinestationDropdown').val(this.editForm.stationId).trigger('change.select2');
}
}
});
},
methods: {
initializeSelect2Dropdowns() {
if ($('#airstationDropdown').data('select2')) {
$('#airstationDropdown').select2('destroy');
}
if ($('#marinestationDropdown').data('select2')) {
$('#marinestationDropdown').select2('destroy');
}
if (this.isPSTWAIR) {
$('#airstationDropdown').select2({
placeholder: "Select Station",
allowClear: true
}).on('change', (e) => {
this.editForm.stationId = $(e.currentTarget).val();
});
}
if (this.isPSTWMARINE) {
$('#marinestationDropdown').select2({
placeholder: "Select Station",
allowClear: true
}).on('change', (e) => {
this.editForm.stationId = $(e.currentTarget).val();
});
}
},
async fetchOvertimeRecord(id) {
try {
const res = await fetch(`/OvertimeAPI/GetOvertimeRecordById/${id}`);
if (res.ok) {
const data = await res.json();
this.populateForm(data);
} else {
console.error("Failed to fetch overtime record.");
alert("Failed to fetch overtime record.");
}
} catch (err) {
console.error("Fetch error:", err);
}
},
populateForm(record) {
this.editForm = {
...record,
otDate: record.otDate ? record.otDate.slice(0, 10) : "",
};
this.calculateOTAndBreak();
this.updateDayType();
},
async fetchStations() {
try {
const response = await fetch(`/OvertimeAPI/GetStationsByDepartmentAir`);
if (!response.ok) throw new Error("Failed to fetch stations");
this.airstationList = await response.json();
} catch (error) {
console.error("Error fetching air stations:", error);
}
},
async fetchStationsMarine() {
try {
const response = await fetch(`/OvertimeAPI/GetStationsByDepartmentMarine`);
if (!response.ok) throw new Error("Failed to fetch stations");
this.marinestationList = await response.json();
} catch (error) {
console.error("Error fetching marine stations:", error);
}
},
async fetchUserAndRelatedData() {
try {
const response = await fetch(`/IdentityAPI/GetUserInformation/`, { method: 'POST' });
if (response.ok) {
const data = await response.json();
this.currentUser = data?.userInfo || null;
this.editForm.userId = this.currentUser?.id || null;
const isSuperAdmin = this.currentUser?.role?.includes("SuperAdmin");
const isSystemAdmin = this.currentUser?.role?.includes("SystemAdmin");
const isDepartmentTwo = this.currentUser?.department?.departmentId === 2;
const isDepartmentThree = this.currentUser?.department?.departmentId === 3;
this.isPSTWAIR = isSuperAdmin || isSystemAdmin || isDepartmentTwo;
this.isPSTWMARINE = isSuperAdmin || isSystemAdmin || isDepartmentThree;
if (this.editForm.userId) {
await this.fetchUserStateAndHolidays();
await this.fetchUserFlexiHour();
}
} else {
console.error(`Failed to fetch user: ${response.statusText}`);
}
} catch (error) {
console.error("Error fetching user:", error);
}
},
async fetchUserStateAndHolidays() {
try {
const response = await fetch(`/OvertimeAPI/GetUserStateAndHolidays/${this.editForm.userId}`);
if (!response.ok) {
throw new Error(`Failed to fetch user state and holidays: ${response.statusText}`);
}
const data = await response.json();
this.userState = data.state;
this.publicHolidays = data.publicHolidays;
this.updateDayType();
} catch (error) {
console.error("Error fetching user state and holidays:", error);
this.editForm.otDays = "Weekday";
}
},
async fetchUserFlexiHour() {
try {
if (!this.editForm.userId) {
console.warn("Cannot fetch flexi hour: User ID is not available.");
this.userFlexiHour = null;
return;
}
const response = await fetch(`/OvertimeAPI/GetUserFlexiHour/${this.editForm.userId}`);
if (response.ok) {
const data = await response.json();
if (data && data.flexiHour) {
this.userFlexiHour = data.flexiHour;
} else {
console.warn("Flexi hour data is missing or malformed in API response.", data);
this.userFlexiHour = null;
}
} else {
console.error(`Failed to fetch user flexi hour: ${response.statusText}`);
this.userFlexiHour = null;
}
} catch (error) {
console.error("Error fetching user flexi hour:", error);
this.userFlexiHour = null;
}
},
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 afterOT = this.calculateTimeDifference(
this.editForm.afterFrom,
this.editForm.afterTo,
this.editForm.afterBreak
);
let totalOTMinutes = officeOT.minutes + afterOT.minutes;
let totalOTHours = officeOT.hours + afterOT.hours + Math.floor(totalOTMinutes / 60);
totalOTMinutes = totalOTMinutes % 60;
this.totalOTHours = `${totalOTHours} hr ${totalOTMinutes} min`;
let totalBreakMinutes = (this.editForm.officeBreak || 0) + (this.editForm.afterBreak || 0);
let totalBreakHours = Math.floor(totalBreakMinutes / 60);
totalBreakMinutes = totalBreakMinutes % 60;
this.totalBreakHours = `${totalBreakHours} hr ${totalBreakMinutes} min`;
this.updateDayType();
},
updateTime(fieldName) {
if (fieldName === 'officeFrom') {
this.editForm.officeFrom = this.roundToNearest30(this.editForm.officeFrom);
} else if (fieldName === 'officeTo') {
this.editForm.officeTo = this.roundToNearest30(this.editForm.officeTo);
} else if (fieldName === 'afterFrom') {
this.editForm.afterFrom = this.roundToNearest30(this.editForm.afterFrom);
} else if (fieldName === 'afterTo') {
this.editForm.afterTo = this.roundToNearest30(this.editForm.afterTo);
}
this.validateTimeRanges();
this.calculateOTAndBreak();
},
validateTimeRanges() {
const validateRange = (fromTime, toTime, label) => {
if (!fromTime || !toTime) return true;
const start = this.parseTime(fromTime);
const end = this.parseTime(toTime);
const minAllowedFromMinutes = 16 * 60 + 30;
const maxAllowedFromMinutes = 23 * 60 + 30;
const startMinutes = start.hours * 60 + start.minutes;
const endMinutes = end.hours * 60 + end.minutes;
if (endMinutes === 0) {
if (fromTime === "00:00") {
alert(`Invalid ${label} Time: 'From' and 'To' cannot both be 00:00 (midnight).`);
return false;
}
if (startMinutes < minAllowedFromMinutes || startMinutes > maxAllowedFromMinutes) {
alert(`Invalid ${label} Time: If 'To' is 12:00 am (00:00), 'From' must start between 4:30 pm and 11:30 pm on the same day.`);
return false;
}
} else if (endMinutes <= startMinutes) {
alert(`Invalid ${label} Time: 'To' time must be later than 'From' time for durations within the same day.`);
return false;
}
return true;
};
if (this.editForm.officeFrom && this.editForm.officeTo) {
if (!validateRange(this.editForm.officeFrom, this.editForm.officeTo, 'Office Hour')) {
this.editForm.officeTo = '';
}
}
if (this.editForm.afterFrom && this.editForm.afterTo) {
if (!validateRange(this.editForm.afterFrom, this.editForm.afterTo, 'After Office Hour')) {
this.editForm.afterTo = '';
}
}
},
roundToNearest30(timeStr) {
if (!timeStr) return timeStr;
const [hours, minutes] = timeStr.split(':').map(Number);
const totalMinutes = hours * 60 + minutes;
const remainder = totalMinutes % 30;
const roundedMinutes = remainder < 15 ? totalMinutes - remainder : totalMinutes + (30 - remainder);
const adjustedHour = Math.floor(roundedMinutes / 60) % 24;
const adjustedMinute = roundedMinutes % 60;
return `${adjustedHour.toString().padStart(2, '0')}:${adjustedMinute.toString().padStart(2, '0')}`;
},
calculateTimeDifference(startTime, endTime, breakMinutes) {
if (!startTime || !endTime) {
return { hours: 0, minutes: 0 };
}
const start = this.parseTime(startTime);
const end = this.parseTime(endTime);
let diffMinutes;
if (end.hours === 0 && end.minutes === 0 && (start.hours > 0 || start.minutes > 0)) {
diffMinutes = (24 * 60) - (start.hours * 60 + start.minutes);
} else if (end.hours * 60 + end.minutes <= start.hours * 60 + start.minutes) {
return { hours: 0, minutes: 0 };
} else {
diffMinutes = (end.hours * 60 + end.minutes) - (start.hours * 60 + start.minutes);
}
diffMinutes -= breakMinutes || 0;
if (diffMinutes < 0) diffMinutes = 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 };
},
formatTime(timeString) {
if (!timeString) return null;
const [hours, minutes] = timeString.split(':');
return `${hours.padStart(2, '0')}:${minutes.padStart(2, '0')}:00`;
},
handleDateChange() {
this.updateDayType();
this.calculateOTAndBreak();
},
updateDayType() {
if (!this.editForm.otDate || !this.userState) {
this.editForm.otDays = "";
return;
}
const selectedDateObj = new Date(this.editForm.otDate + "T00:00:00");
const dayOfWeek = selectedDateObj.getDay();
const year = selectedDateObj.getFullYear();
const month = selectedDateObj.getMonth() + 1;
const day = selectedDateObj.getDate();
const formattedDate = `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`;
if (this.publicHolidays.some(holiday => holiday.date === formattedDate)) {
this.editForm.otDays = "Public Holiday";
return;
}
const weekendId = this.userState.weekendId;
const isWeekend = (() => {
if (weekendId === 1) {
return dayOfWeek === 5 || dayOfWeek === 6; // Friday and Saturday
} else if (weekendId === 2) {
return dayOfWeek === 6 || dayOfWeek === 0; // Saturday and Sunday
} else {
return dayOfWeek === 0; // Default Sunday
}
})();
if (isWeekend) {
this.editForm.otDays = "Weekend";
return;
}
this.editForm.otDays = "Weekday";
},
validateForm() {
this.validationErrors = {
otDate: "",
stationId: "",
otDescription: "",
timeEntries: ""
};
let isValid = true;
let errorMessages = [];
if (!this.editForm.otDate) {
this.validationErrors.otDate = "Date is required.";
errorMessages.push("Date is required.");
isValid = false;
}
// Validate station (only if user is in PSTW AIR or MARINE)
if ((this.isPSTWAIR || this.isPSTWMARINE) && !this.editForm.stationId) {
this.validationErrors.stationId = "Station is required.";
errorMessages.push("Station is required.");
isValid = false;
}
// Validate work brief description
if (!this.editForm.otDescription || this.editForm.otDescription.trim() === "") {
this.validationErrors.otDescription = "Work brief description is required.";
errorMessages.push("Work brief description is required.");
isValid = false;
}
// Validate at least one time entry (either office hours or after hours)
const hasOfficeHours = this.editForm.officeFrom && this.editForm.officeTo;
const hasAfterHours = this.editForm.afterFrom && this.editForm.afterTo;
if (!hasOfficeHours && !hasAfterHours) {
this.validationErrors.timeEntries = "Please enter either Office Hours or After Hours.";
errorMessages.push("Please enter either Office Hours or After Hours.");
isValid = false;
} else {
// Validate office hours if provided
if (hasOfficeHours) {
const officeFrom = this.parseTime(this.editForm.officeFrom);
const officeTo = this.parseTime(this.editForm.officeTo);
const officeFromMinutes = officeFrom.hours * 60 + officeFrom.minutes;
const officeToMinutes = officeTo.hours * 60 + officeTo.minutes;
if (officeTo.hours === 0 && officeTo.minutes === 0) {
// Midnight case - FROM must be between 4:30 PM and 11:30 PM
const minAllowed = 16 * 60 + 30; // 4:30 PM
const maxAllowed = 23 * 60 + 30; // 11:30 PM
if (officeFromMinutes < minAllowed || officeFromMinutes > maxAllowed) {
this.validationErrors.timeEntries = "Invalid Office Time: If 'To' is 00:00, 'From' must be between 4:30 PM and 11:30 PM.";
errorMessages.push("Invalid Office Time: If 'To' is 00:00, 'From' must be between 4:30 PM and 11:30 PM.");
isValid = false;
}
} else if (officeToMinutes <= officeFromMinutes) {
this.validationErrors.timeEntries = "Invalid Office Time: 'To' time must be later than 'From' time.";
errorMessages.push("Invalid Office Time: 'To' time must be later than 'From' time.");
isValid = false;
}
}
// Validate after hours if provided
if (hasAfterHours) {
const afterFrom = this.parseTime(this.editForm.afterFrom);
const afterTo = this.parseTime(this.editForm.afterTo);
const afterFromMinutes = afterFrom.hours * 60 + afterFrom.minutes;
const afterToMinutes = afterTo.hours * 60 + afterTo.minutes;
if (afterTo.hours === 0 && afterTo.minutes === 0) {
// Midnight case - FROM must be between 4:30 PM and 11:30 PM
const minAllowed = 16 * 60 + 30; // 4:30 PM
const maxAllowed = 23 * 60 + 30; // 11:30 PM
if (afterFromMinutes < minAllowed || afterFromMinutes > maxAllowed) {
this.validationErrors.timeEntries = "Invalid After Hours Time: If 'To' is 00:00, 'From' must be between 4:30 PM and 11:30 PM.";
errorMessages.push("Invalid After Hours Time: If 'To' is 00:00, 'From' must be between 4:30 PM and 11:30 PM.");
isValid = false;
}
} else if (afterToMinutes <= afterFromMinutes) {
this.validationErrors.timeEntries = "Invalid After Hours Time: 'To' time must be later than 'From' time.";
errorMessages.push("Invalid After Hours Time: 'To' time must be later than 'From' time.");
isValid = false;
}
}
}
if (!isValid) {
alert("Please correct the following issues:\n\n" + errorMessages.join("\n"));
}
return isValid;
},
validateAndUpdate() {
if (this.validateForm()) {
this.updateRecord();
}
},
async updateRecord() {
const data = {
OvertimeId: this.editForm.overtimeId,
OtDate: this.editForm.otDate,
StationId: this.editForm.stationId || null,
OtDescription: this.editForm.otDescription || "",
OtDays: this.editForm.otDays,
OfficeFrom: this.editForm.officeFrom || null,
OfficeTo: this.editForm.officeTo || null,
OfficeBreak: this.editForm.officeBreak || 0,
AfterFrom: this.editForm.afterFrom || null,
AfterTo: this.editForm.afterTo || null,
AfterBreak: this.editForm.afterBreak || 0,
UserId: this.currentUser?.id
};
try {
const response = await fetch(`/OvertimeAPI/UpdateOvertimeRecord`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
if (response.ok) {
const result = await response.json();
alert(result.message || "Overtime record updated successfully!");
window.location.href = this.previousPage;
} else {
let errorMessage = "Failed to update overtime record.";
try {
const errorData = await response.json();
errorMessage = errorData.message || errorMessage;
} catch (jsonError) {
errorMessage = await response.text();
if (!errorMessage) errorMessage = "An unexpected error occurred.";
}
alert(`Error: ${errorMessage} (Status: ${response.status})`);
}
} catch (error) {
console.error("Error updating record:", error);
alert("An unexpected network error occurred while updating the overtime record. Please try again.");
}
},
goBack() {
if (this.returnMonth && this.returnYear) {
window.location.href = `/OTcalculate/Overtime/OtRecords?month=${this.returnMonth}&year=${this.returnYear}`;
} else {
window.location.href = this.previousPage;
}
},
clearOfficeHours() {
this.editForm.officeFrom = "";
this.editForm.officeTo = "";
this.editForm.officeBreak = 0;
this.calculateOTAndBreak();
},
clearAfterHours() {
this.editForm.afterFrom = "";
this.editForm.afterTo = "";
this.editForm.afterBreak = 0;
this.calculateOTAndBreak();
},
}
});
app.mount("#app");
</script>
}