From 68233785dfcd1f666b5efbb70dde4e606ff58afd Mon Sep 17 00:00:00 2001 From: jessrey-asterisk Date: Thu, 7 Nov 2024 17:08:08 +0800 Subject: [PATCH 1/4] [BUGTASK] - [HRIS - 429] Leader Approving/Disapproving Bug --- api/Services/ApprovalService.cs | 200 ++++++++++-------- api/Services/NotificationService.cs | 42 +++- .../OvertimeResolvedDetails.tsx | 28 ++- client/src/pages/notifications.tsx | 15 +- client/src/utils/interfaces/index.tsx | 2 + client/src/utils/types/notificationTypes.ts | 3 + 6 files changed, 191 insertions(+), 99 deletions(-) diff --git a/api/Services/ApprovalService.cs b/api/Services/ApprovalService.cs index 42ce1f87..1ebd61fd 100644 --- a/api/Services/ApprovalService.cs +++ b/api/Services/ApprovalService.cs @@ -35,102 +35,134 @@ public async Task ApproveDisapproveOvertime(ApproveOvertimeRequest overtim errors = _customInputValidation.checkApproveOvertimeRequestInput(overtimeRequest); if (errors.Count > 0) throw new GraphQLException(errors); - // check if approving/disapproving is manager - if (_customInputValidation.CheckManagerUser(overtimeRequest.UserId).Result) + try { - // validate input for manager case - errors = _customInputValidation.checkManagerApproveOvertimeRequestInput(overtimeRequest); - if (errors.Count > 0) throw new GraphQLException(errors); - - // approve/disapprove operation - var headManager = await context.Users.Where(x => x.PositionId == PositionEnum.MANAGER || (x.PositionId == PositionEnum.ASSISTANT_MANAGER && x.Id == overtimeRequest.UserId)).ToListAsync(); - var notification = await context.OvertimeNotifications.Where(x => x.OvertimeId == overtimeRequest.OvertimeId && x.RecipientId == overtimeRequest.UserId && x.Type == NotificationTypeEnum.OVERTIME).FirstOrDefaultAsync(); - var overtime = await context.Overtimes - .Include(x => x.TimeEntry.TimeIn) - .Include(x => x.TimeEntry.TimeOut) - .FirstOrDefaultAsync(x => x.Id == overtimeRequest.OvertimeId); - var notificationData = notification != null ? JsonConvert.DeserializeObject(notification.Data) : null; - - if ((overtime != null && notificationData != null) || headManager.Count > 0) + // check if approving/disapproving is manager + if (_customInputValidation.CheckManagerUser(overtimeRequest.UserId).Result) { - overtime!.IsManagerApproved = overtimeRequest.IsApproved; - - if (overtimeRequest.IsApproved) - { - if (notificationData != null) - notificationData!.Status = RequestStatus.APPROVED; - overtime.ApprovedMinutes = overtimeRequest.ApprovedMinutes; - } - if (!overtimeRequest.IsApproved) + // validate input for manager case + errors = _customInputValidation.checkManagerApproveOvertimeRequestInput(overtimeRequest); + if (errors.Count > 0) throw new GraphQLException(errors); + + // approve/disapprove operation + var headManager = await context.Users.Where(x => x.PositionId == PositionEnum.MANAGER || (x.PositionId == PositionEnum.ASSISTANT_MANAGER && x.Id == overtimeRequest.UserId)).ToListAsync(); + var notification = await context.OvertimeNotifications.Where(x => x.OvertimeId == overtimeRequest.OvertimeId && x.RecipientId == overtimeRequest.UserId && x.Type == NotificationTypeEnum.OVERTIME).FirstOrDefaultAsync(); + var overtime = await context.Overtimes + .Include(x => x.TimeEntry.TimeIn) + .Include(x => x.TimeEntry.TimeOut) + .FirstOrDefaultAsync(x => x.Id == overtimeRequest.OvertimeId); + var notificationData = notification != null ? JsonConvert.DeserializeObject(notification.Data) : null; + + if ((overtime != null && notificationData != null) || headManager.Count > 0) { - if (notificationData != null) - notificationData!.Status = RequestStatus.DISAPPROVED; - overtime.ApprovedMinutes = 0; + overtime!.IsManagerApproved = overtimeRequest.IsApproved; + + if (overtimeRequest.IsApproved) + { + if (notificationData != null) + notificationData!.Status = RequestStatus.APPROVED; + overtime.ApprovedMinutes = overtimeRequest.ApprovedMinutes; + } + if (!overtimeRequest.IsApproved) + { + if (notificationData != null) + notificationData!.Status = RequestStatus.DISAPPROVED; + overtime.ApprovedMinutes = 0; + } + + overtime.ManagerRemarks = overtimeRequest.ManagerRemarks; } - overtime.ManagerRemarks = overtimeRequest.ManagerRemarks; - } + if (notification != null) notification.Data = JsonConvert.SerializeObject(notificationData); - if (notification != null) notification.Data = JsonConvert.SerializeObject(notificationData); + // create notification + if (overtime != null && overtime.IsManagerApproved == true && overtime.IsLeaderApproved == true) + await _notificationService.CreateOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, APPROVED); - // create notification - if (overtime != null && overtime.IsManagerApproved == true && overtime.IsLeaderApproved == true) - await _notificationService.createOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, APPROVED); + if (overtime != null && (overtime.IsLeaderApproved == null && overtime.IsManagerApproved == false)) + await _notificationService.CreateOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, DISAPPROVED); + else if (overtime != null && (overtime.IsLeaderApproved == true && overtime.IsManagerApproved == false)) + await _notificationService.CreateOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, DISAPPROVED); + else if (overtime != null && (overtime.IsLeaderApproved == false && overtime.IsManagerApproved == false)) + await _notificationService.CreateOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, DISAPPROVED); - if (overtime != null && (overtime.IsLeaderApproved == null && overtime.IsManagerApproved == false)) - await _notificationService.createOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, DISAPPROVED); - else if (overtime != null && (overtime.IsLeaderApproved == true && overtime.IsManagerApproved == false)) - await _notificationService.createOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, DISAPPROVED); - else if (overtime != null && (overtime.IsLeaderApproved == false && overtime.IsManagerApproved == false)) - await _notificationService.createOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, DISAPPROVED); + await context.SaveChangesAsync(); + return true; + } - await context.SaveChangesAsync(); - return true; } + catch (Exception e) + { + Console.WriteLine($"Manager Error: {e.Message}"); + Console.WriteLine($"Manager Stack Trace: {e.StackTrace}"); + + // Optionally, if you are using a logging framework: + // _logger.LogError(e, "Unexpected Execution Error BISHH"); - // check if approving/disapproving is projectleader (from notifications) - if (_customInputValidation.checkProjectLeaderUser(overtimeRequest.UserId).Result) + // Rethrow the exception with a custom message + throw new GraphQLException("Unexpected Execution Error BISHH", e); + } + + try { - // validate input for project leader case - errors = _customInputValidation.checkLeaderApproveOvertimeRequestInput(overtimeRequest); - if (errors.Count > 0) throw new GraphQLException(errors); - - // approve/disapprove operation - var notification = await context.OvertimeNotifications.FindAsync(overtimeRequest.NotificationId); - var overtime = await context.Overtimes.FindAsync(notification?.OvertimeId); - var notificationData = notification != null ? JsonConvert.DeserializeObject(notification.Data) : null; - - // if approving project leader doesn't match - var isProjectLeader = _customInputValidation.checkApprovingProjectLeader(overtimeRequest.UserId, overtime!.Id, MultiProjectTypeEnum.OVERTIME).Result; - if (!isProjectLeader) throw new GraphQLException(ErrorBuilder.New().SetMessage(InputValidationMessageEnum.MISMATCH_PROJECT_LEADER).Build()); - - if (overtime != null && isProjectLeader) - overtime.IsLeaderApproved = overtimeRequest.IsApproved; - - // Update notification data - if (overtimeRequest.IsApproved && notificationData != null) notificationData!.Status = RequestStatus.APPROVED; - if (!overtimeRequest.IsApproved && notificationData != null) notificationData!.Status = RequestStatus.DISAPPROVED; - - if (notification != null) notification.Data = JsonConvert.SerializeObject(notificationData); - - // create notification - if (overtime != null && overtime.IsManagerApproved == true && overtime.IsLeaderApproved == true) - await _notificationService.createOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, APPROVED); - else if (overtime != null && overtime.IsManagerApproved == null && overtime.IsLeaderApproved == true) - await _notificationService.createOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, APPROVED); - if (overtime != null && (overtime.IsLeaderApproved == false && overtime.IsManagerApproved == null)) - await _notificationService.createOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, DISAPPROVED); - else if (overtime != null && (overtime.IsLeaderApproved == null && overtime.IsManagerApproved == false)) - await _notificationService.createOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, DISAPPROVED); - else if (overtime != null && (overtime.IsLeaderApproved == false && overtime.IsManagerApproved == false)) - await _notificationService.createOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, DISAPPROVED); - else if (overtime != null && (overtime.IsLeaderApproved == true && overtime.IsManagerApproved == false)) - await _notificationService.createOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, DISAPPROVED); - - // end - await context.SaveChangesAsync(); - return true; + // check if approving/disapproving is projectleader (from notifications) + if (_customInputValidation.checkProjectLeaderUser(overtimeRequest.UserId).Result) + { + // validate input for project leader case + errors = _customInputValidation.checkLeaderApproveOvertimeRequestInput(overtimeRequest); + if (errors.Count > 0) throw new GraphQLException(errors); + + // approve/disapprove operation + var notification = await context.OvertimeNotifications.FindAsync(overtimeRequest.NotificationId); + var overtime = await context.Overtimes.FindAsync(notification?.OvertimeId); + var notificationData = notification != null ? JsonConvert.DeserializeObject(notification.Data) : null; + + // if approving project leader doesn't match + var isProjectLeader = _customInputValidation.checkApprovingProjectLeader(overtimeRequest.UserId, overtime!.Id, MultiProjectTypeEnum.OVERTIME).Result; + if (!isProjectLeader) throw new GraphQLException(ErrorBuilder.New().SetMessage(InputValidationMessageEnum.MISMATCH_PROJECT_LEADER).Build()); + + if (overtime != null && isProjectLeader) + overtime.IsLeaderApproved = overtimeRequest.IsApproved; + + // Update notification data + if (overtimeRequest.IsApproved && notificationData != null) notificationData!.Status = RequestStatus.APPROVED; + if (!overtimeRequest.IsApproved && notificationData != null) notificationData!.Status = RequestStatus.DISAPPROVED; + + if (notification != null) notification.Data = JsonConvert.SerializeObject(notificationData); + + // create notification + if (overtime != null && overtime.IsManagerApproved == true && overtime.IsLeaderApproved == true) + await _notificationService.CreateOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, APPROVED); + else if (overtime != null && overtime.IsManagerApproved == null && overtime.IsLeaderApproved == true) + await _notificationService.CreateOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, APPROVED); + if (overtime != null && (overtime.IsLeaderApproved == false && overtime.IsManagerApproved == null)) + await _notificationService.CreateOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, DISAPPROVED); + else if (overtime != null && (overtime.IsLeaderApproved == null && overtime.IsManagerApproved == false)) + await _notificationService.CreateOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, DISAPPROVED); + else if (overtime != null && (overtime.IsLeaderApproved == false && overtime.IsManagerApproved == false)) + await _notificationService.CreateOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, DISAPPROVED); + else if (overtime != null && (overtime.IsLeaderApproved == true && overtime.IsManagerApproved == false)) + await _notificationService.CreateOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, DISAPPROVED); + + // end + await context.SaveChangesAsync(); + return true; + } + + } + catch (Exception e) + { + // Log the exception details first + Console.WriteLine($"Error: {e.Message}"); + Console.WriteLine($"Stack Trace: {e.StackTrace}"); + + // Optionally, if you are using a logging framework: + // _logger.LogError(e, "Unexpected Execution Error BISHH"); + + // Rethrow the exception with a custom message + throw new GraphQLException("Unexpected Execution Error BISHH", e); } + } return false; } @@ -186,7 +218,7 @@ public async Task ApproveDisapproveSummaryOvertime(ApproveOvertimeRequest bool isManagerApproved = overtime.IsManagerApproved == true; if (!isLeaderApproved || !isManagerApproved) { - await _notificationService.createOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, DISAPPROVED); + await _notificationService.CreateOvertimeApproveDisapproveNotification(overtime!, overtimeRequest.UserId, DISAPPROVED); } } diff --git a/api/Services/NotificationService.cs b/api/Services/NotificationService.cs index a178e664..b30c8fca 100644 --- a/api/Services/NotificationService.cs +++ b/api/Services/NotificationService.cs @@ -561,22 +561,45 @@ public async Task createLeaveApproveDisapproveNotification(Le } } - public async Task createOvertimeApproveDisapproveNotification(Overtime overtime, int fromUserId, bool IsApproved) + public async Task CreateOvertimeApproveDisapproveNotification(Overtime overtime, int fromUserId, bool IsApproved) { using (HrisContext context = _contextFactory.CreateDbContext()) { + //for time entries + var timeEntry = context.TimeEntries.Find(overtime.TimeEntryId); + //from the time entry, get the time in and time out + var timeInEntry = context.Times.Find(timeEntry?.TimeInId); + var timeOutEntry = context.Times.Find(timeEntry?.TimeOutId); + + var timeIn = timeInEntry?.TimeHour.ToString(@"hh\:mm"); + var timeOut = timeOutEntry?.TimeHour.ToString(@"hh\:mm"); + + var managerStatus = overtime.IsManagerApproved switch + { + true => RequestStatus.APPROVED, + false => RequestStatus.DISAPPROVED, + null => RequestStatus.PENDING // if IsManagerApproved is nullable + }; + + var leaderStatus = overtime.IsLeaderApproved switch + { + true => RequestStatus.APPROVED, + false => RequestStatus.DISAPPROVED, + null => RequestStatus.PENDING // if IsLeaderApproved is nullable + }; + var user = context.Users.Find(fromUserId); var projectNames = context.MultiProjects.Where(x => x.OvertimeId == overtime.Id && x.Type == MultiProjectTypeEnum.OVERTIME).Select(x => x.ProjectId == ProjectId.OTHER_PROJECT ? overtime.OtherProject : x.Project.Name); var dataToUser = JsonSerializer.Serialize(new { User = new { - Id = user?.Id, + Id = user?.Id, // Explicitly naming the properties Name = user?.Name, AvatarLink = _userService.GenerateAvatarLink(user?.ProfileImageId ?? default(int)) }, - Projects = projectNames, - RequestedMinutes = overtime.RequestedMinutes, + Projects = projectNames, // This one is fine because it's already a named property + RequestedMinutes = overtime.RequestedMinutes, // Explicitly naming the properties DateRequested = overtime.OvertimeDate, ApprovedMinutes = overtime.ApprovedMinutes, DateFiled = overtime.CreatedAt, @@ -584,10 +607,13 @@ public async Task createOvertimeApproveDisapproveNotificat Status = IsApproved ? RequestStatus.APPROVED : RequestStatus.DISAPPROVED, Remarks = overtime.Remarks, ManagerRemarks = overtime.ManagerRemarks, - RequestedTimeIn = overtime.TimeEntry.TimeIn!.TimeHour.ToString(@"hh\:mm"), - RequestedTimeOut = overtime.TimeEntry.TimeOut!.TimeHour.ToString(@"hh\:mm") - } - ); + RequestedTimeIn = timeIn, + RequestedTimeOut = timeOut, + Duration = overtime.RequestedMinutes, + ManagerApproveStatus = managerStatus, + LeaderApproveStatus = leaderStatus + }); + // Notification to Requesting User var notificationToUser = new OvertimeNotification diff --git a/client/src/components/molecules/NotificationList/ViewDetailsModal/OvertimeResolvedDetails.tsx b/client/src/components/molecules/NotificationList/ViewDetailsModal/OvertimeResolvedDetails.tsx index 2986564b..32c90bc6 100644 --- a/client/src/components/molecules/NotificationList/ViewDetailsModal/OvertimeResolvedDetails.tsx +++ b/client/src/components/molecules/NotificationList/ViewDetailsModal/OvertimeResolvedDetails.tsx @@ -1,5 +1,5 @@ import moment from 'moment' -import React, { FC } from 'react' +import React, { FC, useState } from 'react' import { INotification } from '~/utils/interfaces' import RequestStatusChip from '~/components/atoms/RequestStatusChip' @@ -18,9 +18,21 @@ const OvertimeResolvedDetails: FC = ({ notification }): JSX.Element => { dateFiled, managerRemarks, requestedTimeIn, - requestedTimeOut + requestedTimeOut, + managerApproveStatus, + leaderApproveStatus, } = notification + let manager = "" + if(managerApproveStatus!=null){ + manager = managerApproveStatus.toLowerCase() + } + + let leader = "" + if(leaderApproveStatus!=null){ + leader = leaderApproveStatus.toLowerCase() + } + return ( <>
  • @@ -33,7 +45,7 @@ const OvertimeResolvedDetails: FC = ({ notification }): JSX.Element => {
  • Duration: - {duration} + {duration? duration+" mins" : "---"}
  • Time In: @@ -51,11 +63,15 @@ const OvertimeResolvedDetails: FC = ({ notification }): JSX.Element => {
  • - Status: - + Leader Status: + +
  • +
  • + Manager Status: +
  • - Manager Remarks: + Manager's Remarks: {managerRemarks}
  • diff --git a/client/src/pages/notifications.tsx b/client/src/pages/notifications.tsx index 5bc26750..dd4e5f08 100644 --- a/client/src/pages/notifications.tsx +++ b/client/src/pages/notifications.tsx @@ -18,6 +18,7 @@ import { columns } from '~/components/molecules/NotificationList/columns' import GlobalSearchFilter from '~/components/molecules/GlobalSearchFilter' import { STATUS_OPTIONS, TYPE_OPTIONS } from '~/utils/constants/notificationFilter' import NotificationFilterDropdown from '~/components/molecules/NotificationFilterDropdown' +import { RequestStatus } from '~/utils/constants/requestStatus' export type Filters = { type: string @@ -75,7 +76,19 @@ const Notifications: NextPage = (): JSX.Element => { offsets: parsedData.Offsets, managerRemarks: parsedData.ManagerRemarks, startDate: parsedData.StartDate, - endDate: parsedData.EndDate + endDate: parsedData.EndDate, + managerApproveStatus: parsedData.ManagerApproveStatus == RequestStatus.APPROVED + ? "Approved" + : parsedData.ManagerApproveStatus == RequestStatus.DISAPPROVED + ? "Disapproved" + : "Pending", // For any other status, if applicable + + leaderApproveStatus: parsedData.LeaderApproveStatus == RequestStatus.APPROVED + ? "Approved" + : parsedData.LeaderApproveStatus == RequestStatus.DISAPPROVED + ? "Disapproved" + : "Pending", // This handles any other statuses or defaults to "Pending" + } return mapped }) diff --git a/client/src/utils/interfaces/index.tsx b/client/src/utils/interfaces/index.tsx index 8bbc0e64..59beb5dc 100644 --- a/client/src/utils/interfaces/index.tsx +++ b/client/src/utils/interfaces/index.tsx @@ -55,6 +55,8 @@ export interface INotification { duration: number dateFiled: string status: string + managerApproveStatus?: string | null, + leaderApproveStatus?: string | null, readAt: string | null isRead: boolean requestedTimeIn: string | null diff --git a/client/src/utils/types/notificationTypes.ts b/client/src/utils/types/notificationTypes.ts index f3a76525..9096c400 100644 --- a/client/src/utils/types/notificationTypes.ts +++ b/client/src/utils/types/notificationTypes.ts @@ -37,6 +37,9 @@ export type NotificationData = { ManagerRemarks: string | null StartDate: string EndDate: string + LeaveType: string | null + ManagerApproveStatus?: string + LeaderApproveStatus?: string } export type NotificationRequestInput = { From ccd805b3819b25e72b0cf7713c5ef9e6b9f0fbd4 Mon Sep 17 00:00:00 2001 From: jessrey-asterisk Date: Fri, 8 Nov 2024 16:50:08 +0800 Subject: [PATCH 2/4] [BUGTASK] - [HRIS - 429] Overtime Approval Fix and Conditions after Fix --- api/NotificationDataClasses/OvertimeData.cs | 5 +++ api/Schema/Queries/OvertimeQuery.cs | 5 +++ api/Services/NotificationService.cs | 18 +++++++--- api/Services/OvertimeService.cs | 20 +++++++++++ .../ViewDetailsModal/OvertimeDetails.tsx | 27 ++++++++++++-- .../ViewDetailsModal/index.tsx | 14 +++++++- client/src/graphql/queries/overtimeQuery.ts | 36 +++++++++++++++++++ client/src/hooks/useOvertime.ts | 5 ++- client/src/hooks/useOvertimeQuery.ts | 14 ++++++++ client/src/pages/notifications.tsx | 2 +- client/src/pages/overtime-management.tsx | 10 +++++- client/src/utils/interfaces/index.tsx | 1 + client/src/utils/types/notificationTypes.ts | 1 + 13 files changed, 147 insertions(+), 11 deletions(-) diff --git a/api/NotificationDataClasses/OvertimeData.cs b/api/NotificationDataClasses/OvertimeData.cs index ee466862..74852e1b 100644 --- a/api/NotificationDataClasses/OvertimeData.cs +++ b/api/NotificationDataClasses/OvertimeData.cs @@ -9,6 +9,11 @@ public class OvertimeData public string Type { get; set; } = default!; public string Status { get; set; } = default!; public string? Remarks { get; set; } = default!; + + public bool? IsManagerApproved { get; set; } + public bool? IsLeaderApproved { get; set; } + + public string? OvertimeId { get; set; } } public class OvertimeManagerData : OvertimeData diff --git a/api/Schema/Queries/OvertimeQuery.cs b/api/Schema/Queries/OvertimeQuery.cs index dd38de32..311eba19 100644 --- a/api/Schema/Queries/OvertimeQuery.cs +++ b/api/Schema/Queries/OvertimeQuery.cs @@ -15,6 +15,11 @@ public OvertimeQuery(OvertimeService overtimeService) return await _overtimeService.GetOvertime(UserId); } + public async Task GetOvertimeById(int id) + { + return await _overtimeService.GetOvertimeById(id); + } + public async Task> GetAllOvertime() { return await _overtimeService.Index(); diff --git a/api/Services/NotificationService.cs b/api/Services/NotificationService.cs index b30c8fca..33343cc0 100644 --- a/api/Services/NotificationService.cs +++ b/api/Services/NotificationService.cs @@ -143,7 +143,10 @@ public async Task> createOvertimeNotification(Overtim DateFiled = (DateTime)overtime.CreatedAt!, Type = NotificationDataTypeEnum.REQUEST, Status = _overtimeService.GetOvertimeRequestStatus(overtime), - Remarks = overtime.Remarks + Remarks = overtime.Remarks, + IsLeaderApproved = overtime.IsLeaderApproved, + IsManagerApproved = overtime.IsManagerApproved, + OvertimeId = overtime.Id.ToString() } ); @@ -175,7 +178,11 @@ public async Task> createOvertimeNotification(Overtim DateFiled = (DateTime)overtime.CreatedAt, Type = NotificationDataTypeEnum.REQUEST, Status = _overtimeService.GetOvertimeRequestStatus(overtime), - Remarks = overtime.Remarks + Remarks = overtime.Remarks, + IsLeaderApproved = overtime.IsLeaderApproved, + IsManagerApproved = overtime.IsManagerApproved, + OvertimeId = overtime.Id.ToString() + // TODO: create the part when either the manager/leader disapproves, it will update so that the other can't update } ); @@ -568,10 +575,10 @@ public async Task CreateOvertimeApproveDisapproveNotificat //for time entries var timeEntry = context.TimeEntries.Find(overtime.TimeEntryId); //from the time entry, get the time in and time out - var timeInEntry = context.Times.Find(timeEntry?.TimeInId); + var timeInEntry = timeEntry?.EndTime + TimeSpan.FromHours(1); var timeOutEntry = context.Times.Find(timeEntry?.TimeOutId); - var timeIn = timeInEntry?.TimeHour.ToString(@"hh\:mm"); + var timeIn = timeInEntry?.ToString(@"hh\:mm"); var timeOut = timeOutEntry?.TimeHour.ToString(@"hh\:mm"); var managerStatus = overtime.IsManagerApproved switch @@ -611,7 +618,8 @@ public async Task CreateOvertimeApproveDisapproveNotificat RequestedTimeOut = timeOut, Duration = overtime.RequestedMinutes, ManagerApproveStatus = managerStatus, - LeaderApproveStatus = leaderStatus + LeaderApproveStatus = leaderStatus, + }); diff --git a/api/Services/OvertimeService.cs b/api/Services/OvertimeService.cs index 73b220bc..0e176910 100644 --- a/api/Services/OvertimeService.cs +++ b/api/Services/OvertimeService.cs @@ -97,6 +97,26 @@ public async Task> GetOvertime(int UserId) .ToListAsync(); } } + + public async Task GetOvertimeById(int id) + { + using (HrisContext context = _contextFactory.CreateDbContext()) + { + // Find the Overtime entity by ID and return a single DTO object + return await context.Overtimes + .Include(x => x.MultiProjects) + .ThenInclude(x => x.Project) + .Include(x => x.MultiProjects) + .ThenInclude(x => x.ProjectLeader) + .Include(x => x.Manager) + .ThenInclude(x => x.Role) + .Where(w => w.Id == id) + .OrderByDescending(x => x.CreatedAt) + .Select(x => new MyOvertimeDTO(x)) + .SingleOrDefaultAsync(); // Use SingleOrDefaultAsync to return a single MyOvertimeDTO or null + } + } + public async Task> Index() { var domain = _httpService.getDomainURL(); diff --git a/client/src/components/molecules/NotificationList/ViewDetailsModal/OvertimeDetails.tsx b/client/src/components/molecules/NotificationList/ViewDetailsModal/OvertimeDetails.tsx index 9202a76d..6f88b79a 100644 --- a/client/src/components/molecules/NotificationList/ViewDetailsModal/OvertimeDetails.tsx +++ b/client/src/components/molecules/NotificationList/ViewDetailsModal/OvertimeDetails.tsx @@ -1,8 +1,9 @@ import moment from 'moment' -import React, { FC } from 'react' +import React, { FC, useEffect, useState } from 'react' import { INotification } from '~/utils/interfaces' import RequestStatusChip from '~/components/atoms/RequestStatusChip' +import { useGetOvertimeById } from '~/hooks/useOvertimeQuery' type Props = { notification: INotification @@ -11,6 +12,24 @@ type Props = { const OvertimeDetails: FC = ({ notification }): JSX.Element => { const { project, date, duration, dateFiled, status, remarks } = notification + const [overtimeId, setOvertimeId] = useState(0); + useEffect(() => { + if(notification.overtimeId){ + setOvertimeId(parseInt(notification.overtimeId)); + } + },[]); + + const { data, isLoading, error } = useGetOvertimeById(overtimeId); + + const overtime = data?.overtimeById; + + let managerApprovalState = "Pending"; + if(overtime?.isManagerApproved){ + managerApprovalState = "Approved"; + } else if (!overtime?.isManagerApproved){ + managerApprovalState = "Disapproved"; + } + return ( <>
  • @@ -32,9 +51,13 @@ const OvertimeDetails: FC = ({ notification }): JSX.Element => { {moment(new Date(dateFiled)).fromNow()}
  • -
  • + {/*
  • Status: +
  • */} +
  • + Manager Status: +
  • Remarks: diff --git a/client/src/components/molecules/NotificationList/ViewDetailsModal/index.tsx b/client/src/components/molecules/NotificationList/ViewDetailsModal/index.tsx index 2ea4a0cc..6f2ab450 100644 --- a/client/src/components/molecules/NotificationList/ViewDetailsModal/index.tsx +++ b/client/src/components/molecules/NotificationList/ViewDetailsModal/index.tsx @@ -1,4 +1,4 @@ -import React, { FC } from 'react' +import React, { FC, useEffect, useState } from 'react' import { useRouter } from 'next/router' import { BsFileEarmarkText } from 'react-icons/bs' import { ThumbsDown, ThumbsUp } from 'react-feather' @@ -29,6 +29,7 @@ import { STATUS_OPTIONS } from '~/utils/constants/notificationFilter' import { NOTIFICATION_TYPE } from '~/utils/constants/notificationTypes' import ModalHeader from '~/components/templates/ModalTemplate/ModalHeader' import ModalFooter from '~/components/templates/ModalTemplate/ModalFooter' +import { useGetOvertimeById } from '~/hooks/useOvertimeQuery' type Props = { isOpen: boolean @@ -61,6 +62,15 @@ const ViewDetailsModal: FC = ({ isOpen, row, user }): JSX.Element => { }) } + const [overtimeId, setOvertimeId] = useState(0); + useEffect(() => { + if(row.overtimeId){ + setOvertimeId(parseInt(row.overtimeId)); + } + },[]); + + const { data, isLoading, error } = useGetOvertimeById(overtimeId); + const handleApproveDisapprove = (isApproved: boolean): void => { if (row.type.toLowerCase() === NOTIFICATION_TYPE.LEAVE) { approveDisapproveLeaveMutation.mutate( @@ -284,6 +294,8 @@ const ViewDetailsModal: FC = ({ isOpen, row, user }): JSX.Element => { View In Overtime Management ) : ( + + data?.overtimeById?.isManagerApproved !==false && <>
  • Duration: - {duration? duration+" mins" : "---"} + + {duration ? duration + ' mins' : '---'}{' '} +
  • Time In: @@ -64,11 +66,11 @@ const OvertimeResolvedDetails: FC = ({ notification }): JSX.Element => {
  • Leader Status: - +
  • Manager Status: - +
  • Manager's Remarks: diff --git a/client/src/components/molecules/NotificationList/ViewDetailsModal/index.tsx b/client/src/components/molecules/NotificationList/ViewDetailsModal/index.tsx index 6f2ab450..3f38e95e 100644 --- a/client/src/components/molecules/NotificationList/ViewDetailsModal/index.tsx +++ b/client/src/components/molecules/NotificationList/ViewDetailsModal/index.tsx @@ -62,14 +62,14 @@ const ViewDetailsModal: FC = ({ isOpen, row, user }): JSX.Element => { }) } - const [overtimeId, setOvertimeId] = useState(0); + const [overtimeId, setOvertimeId] = useState(0) useEffect(() => { - if(row.overtimeId){ - setOvertimeId(parseInt(row.overtimeId)); + if (row.overtimeId) { + setOvertimeId(parseInt(row.overtimeId)) } - },[]); + }, []) - const { data, isLoading, error } = useGetOvertimeById(overtimeId); + const { data, isLoading, error } = useGetOvertimeById(overtimeId) const handleApproveDisapprove = (isApproved: boolean): void => { if (row.type.toLowerCase() === NOTIFICATION_TYPE.LEAVE) { @@ -294,26 +294,26 @@ const ViewDetailsModal: FC = ({ isOpen, row, user }): JSX.Element => { View In Overtime Management ) : ( - - data?.overtimeById?.isManagerApproved !==false && - <> - - - + data?.overtimeById?.isManagerApproved !== false && ( + <> + + + + ) )} )} diff --git a/client/src/hooks/useOvertime.ts b/client/src/hooks/useOvertime.ts index 8311b8c2..77b6ec87 100644 --- a/client/src/hooks/useOvertime.ts +++ b/client/src/hooks/useOvertime.ts @@ -1,4 +1,4 @@ -import { MyOvertimeDTO } from './../../../api_v2/src/graphql/graphql'; +import { MyOvertimeDTO } from './../../../api_v2/src/graphql/graphql' import { toast } from 'react-hot-toast' import { useQuery, @@ -194,5 +194,3 @@ export const getAllovertime = (): UseQueryResult<{ allOvertime: IAllOvertime[] } }) return result } - - diff --git a/client/src/hooks/useOvertimeQuery.ts b/client/src/hooks/useOvertimeQuery.ts index 906a5be0..c5c408bb 100644 --- a/client/src/hooks/useOvertimeQuery.ts +++ b/client/src/hooks/useOvertimeQuery.ts @@ -18,7 +18,7 @@ export const getOvertimeQuery = ( return result } -export const useGetOvertimeById = ( +export const useGetOvertimeById = ( id: number ): UseQueryResult<{ overtimeById: MyOvertimeDTO }, unknown> => { const result = useQuery({ @@ -28,4 +28,4 @@ export const useGetOvertimeById = ( }) return result -}; +} diff --git a/client/src/pages/notifications.tsx b/client/src/pages/notifications.tsx index 8b5fa4c9..57a9b958 100644 --- a/client/src/pages/notifications.tsx +++ b/client/src/pages/notifications.tsx @@ -78,17 +78,19 @@ const Notifications: NextPage = (): JSX.Element => { startDate: parsedData.StartDate, endDate: parsedData.EndDate, overtimeId: parsedData.OvertimeId, - managerApproveStatus: parsedData.ManagerApproveStatus == RequestStatus.APPROVED - ? "Approved" - : parsedData.ManagerApproveStatus == RequestStatus.DISAPPROVED - ? "Disapproved" - : "Pending", // For any other status, if applicable + managerApproveStatus: + parsedData.ManagerApproveStatus == RequestStatus.APPROVED + ? 'Approved' + : parsedData.ManagerApproveStatus == RequestStatus.DISAPPROVED + ? 'Disapproved' + : 'Pending', // For any other status, if applicable - leaderApproveStatus: parsedData.LeaderApproveStatus == RequestStatus.APPROVED - ? "Approved" - : parsedData.LeaderApproveStatus == RequestStatus.DISAPPROVED - ? "Disapproved" - : "Pending", // This handles any other statuses or defaults to "Pending" + leaderApproveStatus: + parsedData.LeaderApproveStatus == RequestStatus.APPROVED + ? 'Approved' + : parsedData.LeaderApproveStatus == RequestStatus.DISAPPROVED + ? 'Disapproved' + : 'Pending' // This handles any other statuses or defaults to "Pending" } return mapped }) diff --git a/client/src/utils/interfaces/index.tsx b/client/src/utils/interfaces/index.tsx index 479dd22e..59286e89 100644 --- a/client/src/utils/interfaces/index.tsx +++ b/client/src/utils/interfaces/index.tsx @@ -55,8 +55,8 @@ export interface INotification { duration: number dateFiled: string status: string - managerApproveStatus?: string | null, - leaderApproveStatus?: string | null, + managerApproveStatus?: string | null + leaderApproveStatus?: string | null overtimeId?: string readAt: string | null isRead: boolean diff --git a/sph-hris.sln b/sph-hris.sln new file mode 100644 index 00000000..bb249511 --- /dev/null +++ b/sph-hris.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "api", "api\api.csproj", "{260A0B00-4116-4D43-852D-7CA92D9063AE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {260A0B00-4116-4D43-852D-7CA92D9063AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {260A0B00-4116-4D43-852D-7CA92D9063AE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {260A0B00-4116-4D43-852D-7CA92D9063AE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {260A0B00-4116-4D43-852D-7CA92D9063AE}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C5FB0D1C-B673-4849-9A17-E85784D0C152} + EndGlobalSection +EndGlobal From 95ede5fff19fd44dfb33fbc205380a0dd5f92726 Mon Sep 17 00:00:00 2001 From: jessrey-asterisk Date: Mon, 11 Nov 2024 16:51:14 +0800 Subject: [PATCH 4/4] Fixes for updates --- .../ViewDetailsModal/OvertimeDetails.tsx | 10 +++++----- .../ViewDetailsModal/OvertimeResolvedDetails.tsx | 5 ++--- .../NotificationList/ViewDetailsModal/index.tsx | 4 ++-- client/src/hooks/useOvertime.ts | 3 +-- client/src/pages/notifications.tsx | 8 ++++---- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/client/src/components/molecules/NotificationList/ViewDetailsModal/OvertimeDetails.tsx b/client/src/components/molecules/NotificationList/ViewDetailsModal/OvertimeDetails.tsx index 6fdbdb08..bf610514 100644 --- a/client/src/components/molecules/NotificationList/ViewDetailsModal/OvertimeDetails.tsx +++ b/client/src/components/molecules/NotificationList/ViewDetailsModal/OvertimeDetails.tsx @@ -10,16 +10,16 @@ type Props = { } const OvertimeDetails: FC = ({ notification }): JSX.Element => { - const { project, date, duration, dateFiled, status, remarks } = notification + const { project, date, duration, dateFiled, remarks } = notification - const [overtimeId, setOvertimeId] = useState(0) + const [overtimeId, setOvertimeId] = useState(null) // Ensure the type is clear and initialized correctly useEffect(() => { - if (notification.overtimeId) { + if (notification.overtimeId !== undefined) { setOvertimeId(parseInt(notification.overtimeId)) } - }, []) + }, [notification.overtimeId]) - const { data, isLoading, error } = useGetOvertimeById(overtimeId) + const { data } = useGetOvertimeById(overtimeId ?? 0) // Fallback to 0 if overtimeId is null const overtime = data?.overtimeById diff --git a/client/src/components/molecules/NotificationList/ViewDetailsModal/OvertimeResolvedDetails.tsx b/client/src/components/molecules/NotificationList/ViewDetailsModal/OvertimeResolvedDetails.tsx index 6ec8e6e5..059846c1 100644 --- a/client/src/components/molecules/NotificationList/ViewDetailsModal/OvertimeResolvedDetails.tsx +++ b/client/src/components/molecules/NotificationList/ViewDetailsModal/OvertimeResolvedDetails.tsx @@ -1,5 +1,5 @@ import moment from 'moment' -import React, { FC, useState } from 'react' +import React, { FC } from 'react' import { INotification } from '~/utils/interfaces' import RequestStatusChip from '~/components/atoms/RequestStatusChip' @@ -11,7 +11,6 @@ type Props = { const OvertimeResolvedDetails: FC = ({ notification }): JSX.Element => { const { date, - status, project, remarks, duration, @@ -46,7 +45,7 @@ const OvertimeResolvedDetails: FC = ({ notification }): JSX.Element => {
  • Duration: - {duration ? duration + ' mins' : '---'}{' '} + {duration !== null && duration !== undefined ? `${duration} mins` : '---'}
  • diff --git a/client/src/components/molecules/NotificationList/ViewDetailsModal/index.tsx b/client/src/components/molecules/NotificationList/ViewDetailsModal/index.tsx index 3f38e95e..f9463831 100644 --- a/client/src/components/molecules/NotificationList/ViewDetailsModal/index.tsx +++ b/client/src/components/molecules/NotificationList/ViewDetailsModal/index.tsx @@ -64,12 +64,12 @@ const ViewDetailsModal: FC = ({ isOpen, row, user }): JSX.Element => { const [overtimeId, setOvertimeId] = useState(0) useEffect(() => { - if (row.overtimeId) { + if (row.overtimeId !== undefined) { setOvertimeId(parseInt(row.overtimeId)) } }, []) - const { data, isLoading, error } = useGetOvertimeById(overtimeId) + const { data } = useGetOvertimeById(overtimeId) const handleApproveDisapprove = (isApproved: boolean): void => { if (row.type.toLowerCase() === NOTIFICATION_TYPE.LEAVE) { diff --git a/client/src/hooks/useOvertime.ts b/client/src/hooks/useOvertime.ts index 77b6ec87..01a9bcaa 100644 --- a/client/src/hooks/useOvertime.ts +++ b/client/src/hooks/useOvertime.ts @@ -1,4 +1,3 @@ -import { MyOvertimeDTO } from './../../../api_v2/src/graphql/graphql' import { toast } from 'react-hot-toast' import { useQuery, @@ -9,7 +8,7 @@ import { } from '@tanstack/react-query' import { client } from '~/utils/shared/client' -import { GET_ALL_OVERTIME, GET_OVERTIME_BY_ID } from '~/graphql/queries/overtimeQuery' +import { GET_ALL_OVERTIME } from '~/graphql/queries/overtimeQuery' import { CREATE_OVERTIME_MUTATION, CREATE_BULK_OVERTIME_MUTATION, diff --git a/client/src/pages/notifications.tsx b/client/src/pages/notifications.tsx index 57a9b958..b43ffa27 100644 --- a/client/src/pages/notifications.tsx +++ b/client/src/pages/notifications.tsx @@ -79,16 +79,16 @@ const Notifications: NextPage = (): JSX.Element => { endDate: parsedData.EndDate, overtimeId: parsedData.OvertimeId, managerApproveStatus: - parsedData.ManagerApproveStatus == RequestStatus.APPROVED + parsedData.ManagerApproveStatus === RequestStatus.APPROVED ? 'Approved' - : parsedData.ManagerApproveStatus == RequestStatus.DISAPPROVED + : parsedData.ManagerApproveStatus === RequestStatus.DISAPPROVED ? 'Disapproved' : 'Pending', // For any other status, if applicable leaderApproveStatus: - parsedData.LeaderApproveStatus == RequestStatus.APPROVED + parsedData.LeaderApproveStatus === RequestStatus.APPROVED ? 'Approved' - : parsedData.LeaderApproveStatus == RequestStatus.DISAPPROVED + : parsedData.LeaderApproveStatus === RequestStatus.DISAPPROVED ? 'Disapproved' : 'Pending' // This handles any other statuses or defaults to "Pending" }