-
This commit is contained in:
parent
9e3539caa6
commit
32d8689eb1
@ -1,41 +1,47 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using PSTW_CentralSystem.Areas.Inventory.Models;
|
||||
using PSTW_CentralSystem.Models;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.OTcalculate.Models
|
||||
{
|
||||
[Table("otregisters")]
|
||||
|
||||
|
||||
public class OtRegisterModel
|
||||
{
|
||||
[Key]
|
||||
public int OvertimeId { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime OtDate { get; set; }
|
||||
public DateTime OtDate { get; set; }
|
||||
|
||||
public TimeSpan? OfficeFrom { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime OfficeFrom { get; set; }
|
||||
[Required]
|
||||
public DateTime OfficeTo { get; set; }
|
||||
public int OfficeBreak { get; set; }
|
||||
public TimeSpan? OfficeTo { get; set; }
|
||||
|
||||
public DateTime OutsideFrom { get; set; }
|
||||
public DateTime OutsideTo { get; set; }
|
||||
public int OutsideBreak { get; set; }
|
||||
public int? OfficeBreak { get; set; }
|
||||
|
||||
public int StationId { get; set; }
|
||||
public TimeSpan? OutsideFrom { get; set; }
|
||||
|
||||
public TimeSpan? OutsideTo { get; set; }
|
||||
|
||||
public int? OutsideBreak { get; set; }
|
||||
|
||||
public int? StationId { get; set; }
|
||||
|
||||
[ForeignKey("StationId")]
|
||||
public virtual StationModel? Stations { get; set; }
|
||||
|
||||
public string? OtDescription { get; set; }
|
||||
public string? OtDays { get; set; }
|
||||
public string OtDescription { get; set; }
|
||||
|
||||
public byte[]? PDF { get; set; }
|
||||
public string OtDays { get; set; }
|
||||
|
||||
public required string PDFBase64 { get; set; }
|
||||
|
||||
[Required]
|
||||
public int UserId { get; set; }
|
||||
|
||||
[ForeignKey("UserId")]
|
||||
public virtual UserModel? Users { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,90 +9,103 @@
|
||||
<div class="card shadow-sm" style="width: 1100px;">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
@* Left Section *@
|
||||
<div class="col-md-7">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Date</label>
|
||||
<input type="date" class="form-control" v-model="selectedDate">
|
||||
<label class="form-label" for="dateInput">Date</label>
|
||||
<input type="date" id="dateInput" class="form-control" v-model="selectedDate"
|
||||
v-on:input="calculateOTAndBreak">
|
||||
</div>
|
||||
|
||||
<h6 class="fw-bold">OFFICE HOURS</h6>
|
||||
<div class="row mb-3">
|
||||
<div class="col-4">
|
||||
<label>From</label>
|
||||
<input type="time" class="form-control" v-model="officeFrom">
|
||||
<label for="officeFrom">From</label>
|
||||
<input type="time" id="officeFrom" class="form-control" v-model="officeFrom"
|
||||
v-on:input="calculateOTAndBreak">
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label>To</label>
|
||||
<input type="time" class="form-control" v-model="officeTo">
|
||||
<label for="officeTo">To</label>
|
||||
<input type="time" id="officeTo" class="form-control" v-model="officeTo"
|
||||
v-on:input="calculateOTAndBreak">
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label>Break Hours</label>
|
||||
<input type="time" class="form-control" v-model="officeBreak">
|
||||
<label for="officeBreak">Break Hours (Minutes)</label>
|
||||
<input type="number" id="officeBreak" class="form-control" v-model="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>From</label>
|
||||
<input type="time" class="form-control" v-model="outsideFrom">
|
||||
<label for="outsideFrom">From</label>
|
||||
<input type="time" id="outsideFrom" class="form-control" v-model="outsideFrom"
|
||||
v-on:input="calculateOTAndBreak">
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label>To</label>
|
||||
<input type="time" class="form-control" v-model="outsideTo">
|
||||
<label for="outsideTo">To</label>
|
||||
<input type="time" id="outsideTo" class="form-control" v-model="outsideTo"
|
||||
v-on:input="calculateOTAndBreak">
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label>Break Hours</label>
|
||||
<input type="time" class="form-control" v-model="outsideBreak">
|
||||
<label for="outsideBreak">Break Hours (Minutes)</label>
|
||||
<input type="number" id="outsideBreak" class="form-control" v-model="outsideBreak"
|
||||
v-on:input="calculateOTAndBreak" placeholder="e.g. 45">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="airstationDropdown">Air Station</label>
|
||||
<select id="airstationDropdown" class="form-control" v-model="selectedAirStation">
|
||||
<option v-for="airstation in airstationList" :key="airstation.airstationId" :value="airstation.airstationId">
|
||||
{{ airstation.airstationName }}
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Work Brief Description</label>
|
||||
<textarea class="form-control" v-model="otDescription"></textarea>
|
||||
<label for="otDescription">Work Brief Description</label>
|
||||
<textarea id="otDescription" class="form-control" v-model="otDescription"
|
||||
placeholder="Describe the work done..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Right Section *@
|
||||
<div class="col-md-5">
|
||||
<label>Day</label>
|
||||
<label class="mb-2">Day</label>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" v-model="selectedDayType" value="Weekday">
|
||||
<label class="form-check-label">Weekday</label>
|
||||
<input class="form-check-input" type="radio" id="weekday" v-model="selectedDayType" value="Weekday">
|
||||
<label class="form-check-label" for="weekday">Weekday</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" v-model="selectedDayType" value="Weekend">
|
||||
<label class="form-check-label">Weekend</label>
|
||||
<input class="form-check-input" type="radio" id="weekend" v-model="selectedDayType" value="Weekend">
|
||||
<label class="form-check-label" for="weekend">Weekend</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" v-model="selectedDayType" value="Public Holiday">
|
||||
<label class="form-check-label">Public Holiday</label>
|
||||
<input class="form-check-input" type="radio" id="publicHoliday" v-model="selectedDayType"
|
||||
value="Public Holiday">
|
||||
<label class="form-check-label" for="publicHoliday">Public Holiday</label>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 mt-3">
|
||||
<label>Upload File:</label>
|
||||
<input type="file" class="form-control" v-on:change="handleFileUpload">
|
||||
<label for="fileUpload">Upload File:</label>
|
||||
<input type="file" id="fileUpload" class="form-control" v-on:change="handleFileUpload">
|
||||
<small class="text-danger">*upload pdf file only</small>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 d-flex flex-column align-items-center">
|
||||
<label>Total OT Hours</label>
|
||||
<input type="text" class="form-control text-center" v-model="totalOTHours" style="width: 200px;" readonly>
|
||||
<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>Total Break Hours</label>
|
||||
<input type="text" class="form-control text-center" v-model="totalBreakHours" style="width: 200px;" readonly>
|
||||
<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>
|
||||
@ -105,25 +118,201 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@section Scripts {
|
||||
@{
|
||||
await Html.RenderPartialAsync("_ValidationScriptsPartial");
|
||||
}
|
||||
<script>
|
||||
const app = Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
|
||||
selectedDate: "",
|
||||
officeFrom: "",
|
||||
officeTo: "",
|
||||
officeBreak: 0,
|
||||
outsideFrom: "",
|
||||
outsideTo: "",
|
||||
outsideBreak: 0,
|
||||
selectedAirStation: "",
|
||||
airstationList: [],
|
||||
otDescription: "",
|
||||
selectedDayType: "",
|
||||
totalOTHours: "0 hr 0 min",
|
||||
totalBreakHours: "0 hr 0 min",
|
||||
uploadedFile: null,
|
||||
currentUser: null,
|
||||
userId: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
||||
async mounted() {
|
||||
this.fetchStations();
|
||||
await this.fetchUser();
|
||||
},
|
||||
mounted() {
|
||||
|
||||
|
||||
methods: {
|
||||
async fetchStations() {
|
||||
try {
|
||||
const response = await fetch(`/OvertimeAPI/GetStationsByDepartment`);
|
||||
if (!response.ok) throw new Error("Failed to fetch stations");
|
||||
|
||||
this.airstationList = await response.json();
|
||||
console.log("Fetched Stations:", this.airstationList);
|
||||
} 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 = await this.currentUser.id;
|
||||
}
|
||||
else {
|
||||
console.error(`Failed to fetch user: ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('There was a problem with the fetch operation:', error);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
clearForm() {
|
||||
this.selectedDate = "";
|
||||
this.officeFrom = "";
|
||||
this.officeTo = "";
|
||||
this.officeBreak = 0;
|
||||
this.outsideFrom = "";
|
||||
this.outsideTo = "";
|
||||
this.outsideBreak = 0;
|
||||
this.selectedAirStation = "";
|
||||
this.otDescription = "";
|
||||
this.selectedDayType = "";
|
||||
this.totalOTHours = "0 hr 0 min";
|
||||
this.totalBreakHours = "0 hr 0 min";
|
||||
this.uploadedFile = null;
|
||||
},
|
||||
calculateOTAndBreak() {
|
||||
let officeOT = this.calculateTimeDifference(this.officeFrom, this.officeTo, this.officeBreak);
|
||||
let outsideOT = this.calculateTimeDifference(this.outsideFrom, this.outsideTo, this.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.officeBreak || 0) + (this.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) {
|
||||
this.uploadedFile = event.target.files[0];
|
||||
},
|
||||
formatTime(timeString) {
|
||||
if (!timeString) return null;
|
||||
const [hours, minutes] = timeString.split(':');
|
||||
const formattedHours = hours.padStart(2, '0');
|
||||
const formattedMinutes = minutes.padStart(2, '0');
|
||||
return `${formattedHours}:${formattedMinutes}`;
|
||||
},
|
||||
async addOvertime() {
|
||||
if (!this.selectedDate || !this.selectedDayType || !this.selectedAirStation) {
|
||||
alert("Please fill in all required fields.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.uploadedFile || this.uploadedFile.type !== "application/pdf") {
|
||||
alert("Please upload a valid PDF file.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.userId) {
|
||||
console.error("User ID is not set!");
|
||||
alert("User information is missing. Please try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Sending userId:", this.userId); // Log userId
|
||||
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = async (event) => {
|
||||
const base64String = event.target.result.split(',')[1];
|
||||
|
||||
const payload = {
|
||||
otDate: this.selectedDate,
|
||||
officeFrom: this.officeFrom,
|
||||
officeTo: this.officeTo,
|
||||
officeBreak: this.officeBreak,
|
||||
outsideFrom: this.outsideFrom ? this.outsideFrom : null,
|
||||
outsideTo: this.outsideTo ? this.outsideTo : null,
|
||||
outsideBreak: this.outsideBreak,
|
||||
stationId: this.selectedAirStation,
|
||||
otDescription: this.otDescription,
|
||||
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);
|
||||
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");
|
||||
alert("Error reading the uploaded file.");
|
||||
};
|
||||
|
||||
reader.readAsDataURL(this.uploadedFile);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@ namespace PSTW_CentralSystem.Controllers.API
|
||||
}
|
||||
|
||||
#region User
|
||||
[HttpPost("GetUserInformation")]
|
||||
[HttpPost("GetUserInformation")]
|
||||
public async Task<IActionResult> GetUserInformation()
|
||||
{
|
||||
try
|
||||
|
||||
@ -12,9 +12,13 @@ using PSTW_CentralSystem.DBContext;
|
||||
using PSTW_CentralSystem.Models;
|
||||
using System.ComponentModel.Design;
|
||||
using System.Data;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using static System.Collections.Specialized.BitVector32;
|
||||
using System.Security.Claims;
|
||||
|
||||
|
||||
namespace PSTW_CentralSystem.Controllers.API
|
||||
{
|
||||
@ -360,9 +364,83 @@ namespace PSTW_CentralSystem.Controllers.API
|
||||
#endregion
|
||||
|
||||
#region OtRegister
|
||||
[HttpGet("GetStationsByDepartment")]
|
||||
public IActionResult GetStationsByDepartment()
|
||||
{
|
||||
var stations = _centralDbContext.Stations
|
||||
.Where(s => s.DepartmentId == 2)
|
||||
.Select(s => new
|
||||
{
|
||||
s.StationId,
|
||||
StationName = s.StationName ?? "Unnamed Station"
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return Ok(stations);
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("AddOvertime")]
|
||||
public async Task<IActionResult> AddOvertimeAsync([FromBody] OtRegisterModel model)
|
||||
{
|
||||
_logger.LogInformation("AddOvertimeAsync called.");
|
||||
if (model == null)
|
||||
{
|
||||
_logger.LogError("Model is null.");
|
||||
return BadRequest("Invalid data.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogInformation($"Received model: {System.Text.Json.JsonSerializer.Serialize(model)}");
|
||||
|
||||
if (model.UserId == 0)
|
||||
{
|
||||
_logger.LogWarning("No user ID provided.");
|
||||
return BadRequest("User ID is required.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(model.OfficeFrom?.ToString()))
|
||||
{
|
||||
model.OfficeFrom = TimeSpan.Parse(model.OfficeFrom.ToString());
|
||||
}
|
||||
if (!string.IsNullOrEmpty(model.OfficeTo?.ToString()))
|
||||
{
|
||||
model.OfficeTo = TimeSpan.Parse(model.OfficeTo.ToString());
|
||||
}
|
||||
if (!string.IsNullOrEmpty(model.OutsideFrom?.ToString()))
|
||||
{
|
||||
model.OutsideFrom = TimeSpan.Parse(model.OutsideFrom.ToString());
|
||||
}
|
||||
if (!string.IsNullOrEmpty(model.OutsideTo?.ToString()))
|
||||
{
|
||||
model.OutsideTo = TimeSpan.Parse(model.OutsideTo.ToString());
|
||||
}
|
||||
_logger.LogInformation($"Time spans parsed successfully.");
|
||||
}
|
||||
catch (Exception timeEx)
|
||||
{
|
||||
_logger.LogError(timeEx, "Error parsing timespans");
|
||||
return StatusCode(500, "Error parsing timespans");
|
||||
}
|
||||
|
||||
_centralDbContext.Otregisters.Add(model);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Overtime registered successfully.");
|
||||
return Ok("Overtime registered successfully.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error registering overtime.");
|
||||
return StatusCode(500, "An error occurred.");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,8 @@ namespace PSTW_CentralSystem.Models
|
||||
public class UserModel : IdentityUser<int> // Specify the type for the primary key
|
||||
{
|
||||
// Add custom properties
|
||||
//[Key]
|
||||
//public int UserId { get; set; }
|
||||
public string? FullName { get; set; }
|
||||
public int? UserInfoStatus { get; set; }
|
||||
public int? departmentId { get; set; }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user