diff --git a/client/src/components/molecules/NotificationList/ViewDetailsModal/ChangeScheduleDetails.tsx b/client/src/components/molecules/NotificationList/ViewDetailsModal/ChangeScheduleDetails.tsx new file mode 100644 index 00000000..2f7f7a49 --- /dev/null +++ b/client/src/components/molecules/NotificationList/ViewDetailsModal/ChangeScheduleDetails.tsx @@ -0,0 +1,109 @@ +import isEmpty from 'lodash/isEmpty' +import React, { FC, useEffect, useState } from 'react' + +import { INotification } from '~/utils/interfaces' +import { IWorkDayData } from '~/utils/types/notificationTypes' +import CustomDayCard from '~/components/templates/MySchedulelayout/CustomDayCard' + +type Props = { + notification: INotification +} + +const ChangeScheduleDetails: FC = ({ notification }): JSX.Element => { + const [requestedSchedule, setRequestedSchedule] = useState([]) + + const myRequestedWorkingDays = notification?.workingDays + + useEffect(() => { + if (!isEmpty(notification)) { + const empty = { + Day: '', + From: '', + To: '', + BreakFrom: '', + BreakTo: '' + } + const mondayData = + myRequestedWorkingDays?.find((item) => item?.Day === 'Monday' || item?.Day === 'monday') ?? + empty + const tuesdayData = + myRequestedWorkingDays?.find( + (item) => item?.Day === 'Tuesday' || item?.Day === 'tuesday' + ) ?? empty + const wednesdayData = + myRequestedWorkingDays?.find( + (item) => item?.Day === 'Wednesday' || item?.Day === 'wednesday' + ) ?? empty + const thursdayData = + myRequestedWorkingDays?.find( + (item) => item?.Day === 'Thursday' || item?.Day === 'thursday' + ) ?? empty + const fridayData = + myRequestedWorkingDays?.find((item) => item?.Day === 'Friday' || item?.Day === 'friday') ?? + empty + const saturdayData = + myRequestedWorkingDays?.find( + (item) => item?.Day === 'Saturday' || item?.Day === 'saturday' + ) ?? empty + const sundayData = + myRequestedWorkingDays?.find((item) => item?.Day === 'Sunday' || item?.Day === 'sunday') ?? + empty + + const shiftData: IWorkDayData[] = [ + { + ...mondayData, + Day: 'Monday' + }, + { + ...tuesdayData, + Day: 'Tuesday' + }, + { + ...wednesdayData, + Day: 'Wednesday' + }, + { + ...thursdayData, + Day: 'Thursday' + }, + { + ...fridayData, + Day: 'Friday' + }, + { + ...saturdayData, + Day: 'Saturday' + }, + { + ...sundayData, + Day: 'Sunday' + } + ] + + setRequestedSchedule(shiftData) + } + }, [notification]) + + return ( +
+
    + {requestedSchedule?.map((item, index) => ( + + ))} +
+
+ ) +} + +export default ChangeScheduleDetails diff --git a/client/src/components/molecules/NotificationList/ViewDetailsModal/index.tsx b/client/src/components/molecules/NotificationList/ViewDetailsModal/index.tsx index 2ea4a0cc..4ebf0bc8 100644 --- a/client/src/components/molecules/NotificationList/ViewDetailsModal/index.tsx +++ b/client/src/components/molecules/NotificationList/ViewDetailsModal/index.tsx @@ -1,4 +1,5 @@ import React, { FC } from 'react' +import classNames from 'classnames' import { useRouter } from 'next/router' import { BsFileEarmarkText } from 'react-icons/bs' import { ThumbsDown, ThumbsUp } from 'react-feather' @@ -20,6 +21,7 @@ import LeaveResolvedDetails from './LeaveResolvedDetails' import OffsetScheduleDetails from './OffsetScheduleDetails' import OffsetResolvedDetails from './OffsetResolvedDetails' import ESLChangeShiftDetails from './ESLChangeShiftDetails' +import ChangeScheduleDetails from './ChangeScheduleDetails' import Button from '~/components/atoms/Buttons/ButtonAction' import ChangeShiftResolvedDetails from './ChangeShiftResolved' import OvertimeResolvedDetails from './OvertimeResolvedDetails' @@ -139,6 +141,11 @@ const ViewDetailsModal: FC = ({ isOpen, row, user }): JSX.Element => { } ) } + + if (row.type.toLowerCase() === NOTIFICATION_TYPE.CHANGE_SCHEDULE) { + // TODO: implement change schedule functionality + alert('No functionality yet!') + } } return ( @@ -148,7 +155,10 @@ const ViewDetailsModal: FC = ({ isOpen, row, user }): JSX.Element => { closeModal: handleClose, status: row.status }} - className="w-full max-w-lg" + className={classNames( + 'w-full', + row.type.toLowerCase() === NOTIFICATION_TYPE.CHANGE_SCHEDULE ? 'max-w-3xl' : 'max-w-lg' + )} > = ({ isOpen, row, user }): JSX.Element => { }} /> )} + {/* DETAILS FOR CHANGE SCHEDULE REQUEST */} + {row.type.toLocaleLowerCase() === NOTIFICATION_TYPE.CHANGE_SCHEDULE && ( + + )} {row.status === STATUS_OPTIONS.PENDING && ( diff --git a/client/src/components/organisms/Header/index.tsx b/client/src/components/organisms/Header/index.tsx index 25430160..d98c7458 100644 --- a/client/src/components/organisms/Header/index.tsx +++ b/client/src/components/organisms/Header/index.tsx @@ -27,15 +27,16 @@ import useNotificationMutation from '~/hooks/useNotificationMutation' import UserMenuDropDown from '~/components/molecules/UserMenuDropdown' import NotificationPopover from '~/components/molecules/NotificationPopOver' import { NotificationRequestInput, NotificationData } from '~/utils/types/notificationTypes' +import { getESLOffsetNotificationSubscription } from '~/graphql/subscriptions/eslOffsetSubscription' +import { getFileOffsetNotificationSubscription } from '~/graphql/subscriptions/fileOffsetSubscription' +import { getChangeScheduleNotificationSubscription } from '~/graphql/subscriptions/changeScheduleSubscription' import { - getChangeShiftNotificationSubQuery, getLeaveNotificationSubQuery, + getSummaryOvertimeNotification, getOvertimeNotificationSubQuery, - getSummaryOvertimeNotification + getChangeShiftNotificationSubQuery } from '~/graphql/subscriptions/leaveSubscription' import useScreenCondition from '~/hooks/useScreenCondition' -import { getESLOffsetNotificationSubscription } from '~/graphql/subscriptions/eslOffsetSubscription' -import { getFileOffsetNotificationSubscription } from '~/graphql/subscriptions/fileOffsetSubscription' const Tooltip = dynamic(async () => await import('rc-tooltip'), { ssr: false }) @@ -111,7 +112,8 @@ const Header: FC = (props): JSX.Element => { offsets: parsedData.Offsets, managerRemarks: parsedData.ManagerRemarks, startDate: parsedData.StartDate, - endDate: parsedData.EndDate + endDate: parsedData.EndDate, + workingDays: parsedData.WorkingDays } return mapped }) @@ -219,7 +221,7 @@ const Header: FC = (props): JSX.Element => { query: getOvertimeNotificationSubQuery(userId) }, { - next: ({ data }: any) => { + next: () => { void queryClient.invalidateQueries({ queryKey: ['GET_ALL_USER_NOTIFICATION'] }) }, error: () => null, @@ -232,7 +234,7 @@ const Header: FC = (props): JSX.Element => { query: getChangeShiftNotificationSubQuery(userId) }, { - next: ({ data }: any) => { + next: () => { void queryClient.invalidateQueries({ queryKey: ['GET_ALL_USER_NOTIFICATION'] }) }, error: () => null, @@ -245,7 +247,7 @@ const Header: FC = (props): JSX.Element => { query: getESLOffsetNotificationSubscription(userId) }, { - next: ({ data }: any) => { + next: () => { void queryClient.invalidateQueries({ queryKey: ['GET_ALL_USER_NOTIFICATION'] }) }, error: () => null, @@ -258,7 +260,7 @@ const Header: FC = (props): JSX.Element => { query: getFileOffsetNotificationSubscription(userId) }, { - next: ({ data }: any) => { + next: () => { void queryClient.invalidateQueries({ queryKey: ['GET_ALL_USER_NOTIFICATION'] }) }, error: () => null, @@ -271,7 +273,20 @@ const Header: FC = (props): JSX.Element => { query: getSummaryOvertimeNotification(userId) }, { - next: ({ data }: any) => { + next: () => { + void queryClient.invalidateQueries({ queryKey: ['GET_ALL_USER_NOTIFICATION'] }) + }, + error: () => null, + complete: () => null + } + ) + + clientWebsocket.subscribe( + { + query: getChangeScheduleNotificationSubscription(userId) + }, + { + next: () => { void queryClient.invalidateQueries({ queryKey: ['GET_ALL_USER_NOTIFICATION'] }) }, error: () => null, diff --git a/client/src/graphql/mutations/changeScheduleMutation.ts b/client/src/graphql/mutations/changeScheduleMutation.ts new file mode 100644 index 00000000..0bc583c0 --- /dev/null +++ b/client/src/graphql/mutations/changeScheduleMutation.ts @@ -0,0 +1,9 @@ +import { gql } from 'graphql-request' + +export const CREATE_CHANGE_SCHEDULE_MUTATION = gql` + mutation ($request: ChangeSchedRequestInput!) { + changeScheduleRequest(request: $request) { + id + } + } +` diff --git a/client/src/graphql/subscriptions/changeScheduleSubscription.ts b/client/src/graphql/subscriptions/changeScheduleSubscription.ts new file mode 100644 index 00000000..5574ff5e --- /dev/null +++ b/client/src/graphql/subscriptions/changeScheduleSubscription.ts @@ -0,0 +1,11 @@ +export const getChangeScheduleNotificationSubscription = (id: number): string => ` + subscription { + notificationCreated(id: ${id}) { + id + type + data + readAt + isRead + } + } +` diff --git a/client/src/graphql/subscriptions/leaveSubscription.ts b/client/src/graphql/subscriptions/leaveSubscription.ts index 776beb1f..26100200 100644 --- a/client/src/graphql/subscriptions/leaveSubscription.ts +++ b/client/src/graphql/subscriptions/leaveSubscription.ts @@ -34,13 +34,13 @@ export const getChangeShiftNotificationSubQuery = (id: number): string => ` } ` export const getSummaryOvertimeNotification = (id: number): string => ` -subscription { - overtimeSummaryCreated(id: ${id}) { - id - type - data - readAt - isRead + subscription { + overtimeSummaryCreated(id: ${id}) { + id + type + data + readAt + isRead + } } -} ` diff --git a/client/src/hooks/useChangeSchedule.ts b/client/src/hooks/useChangeSchedule.ts new file mode 100644 index 00000000..59587330 --- /dev/null +++ b/client/src/hooks/useChangeSchedule.ts @@ -0,0 +1,31 @@ +import toast from 'react-hot-toast' +import { useMutation, UseMutationResult } from '@tanstack/react-query' + +import { client } from '~/utils/shared/client' +import { IChangeSchedule } from '~/utils/interfaces/changeScheduleInterface' +import { CREATE_CHANGE_SCHEDULE_MUTATION } from '~/graphql/mutations/changeScheduleMutation' + +type ChangeFuncReturnType = UseMutationResult + +type HookReturnType = { + handleChangeScheduleMutation: () => UseMutationResult +} + +const useChangeSchedule = (): HookReturnType => { + const handleChangeScheduleMutation = (): ChangeFuncReturnType => + useMutation({ + mutationFn: async (request: IChangeSchedule) => { + return await client.request(CREATE_CHANGE_SCHEDULE_MUTATION, { request }) + }, + onError: async (err: Error) => { + const [errorMessage] = err.message.split(/:\s/, 2) + toast.error(errorMessage) + } + }) + + return { + handleChangeScheduleMutation + } +} + +export default useChangeSchedule diff --git a/client/src/pages/my-schedule/current-schedule.tsx b/client/src/pages/my-schedule/current-schedule.tsx index 8670f41a..c931e89d 100644 --- a/client/src/pages/my-schedule/current-schedule.tsx +++ b/client/src/pages/my-schedule/current-schedule.tsx @@ -36,7 +36,7 @@ const CurrentSchedule: NextPage = (): JSX.Element => { const saturdayData = mySchedules?.find((item) => item?.day === 'Saturday') ?? empty const sundayData = mySchedules?.find((item) => item?.day === 'Sunday') ?? empty - const shitdata: WorkingDayTime[] = [ + const shiftData: WorkingDayTime[] = [ { ...mondayData, day: 'Monday' @@ -67,7 +67,7 @@ const CurrentSchedule: NextPage = (): JSX.Element => { } ] - setCurrentSchedule(shitdata) + setCurrentSchedule(shiftData) } }, [mySchedules]) diff --git a/client/src/pages/my-schedule/request-new-schedule.tsx b/client/src/pages/my-schedule/request-new-schedule.tsx index 86db73a0..f0efbdf1 100644 --- a/client/src/pages/my-schedule/request-new-schedule.tsx +++ b/client/src/pages/my-schedule/request-new-schedule.tsx @@ -1,28 +1,33 @@ import { NextPage } from 'next' import classNames from 'classnames' +import toast from 'react-hot-toast' import isEmpty from 'lodash/isEmpty' import ReactSelect from 'react-select' import { CheckSquare } from 'react-feather' import makeAnimated from 'react-select/animated' import React, { useEffect, useState } from 'react' import { yupResolver } from '@hookform/resolvers/yup' -import { SubmitHandler, useForm } from 'react-hook-form' +import { Controller, SubmitHandler, useForm } from 'react-hook-form' import Card from '~/components/atoms/Card' +import useProject from '~/hooks/useProject' import Input from '~/components/atoms/Input' import Alert from '~/components/atoms/Alert' -import SpinnerIcon from '~/utils/icons/SpinnerIcon' import FadeInOut from '~/components/templates/FadeInOut' +import useChangeSchedule from '~/hooks/useChangeSchedule' +import { LeaderDetails } from '~/utils/types/projectTypes' +import { User as UserType } from '~/utils/types/userTypes' import DayButton from '~/components/atoms/Buttons/DayButton' import { RequestNewScheduleSchema } from '~/utils/validation' +import useEmployeeSchedule from '~/hooks/useEmployeeSchedule' import { customStyles } from '~/utils/customReactSelectStyles' -import { shiftSchedule } from '~/utils/constants/shiftSchedule' +import { IWorkDay } from '~/utils/types/employeeScheduleTypes' +import { generateUserSelect } from '~/utils/createLeaveHelpers' import ClearButton from '~/components/atoms/Buttons/ClearButton' import ButtonAction from '~/components/atoms/Buttons/ButtonAction' import MaxWidthContainer from '~/components/atoms/MaxWidthContainer' import ApplyToAllModal from '~/components/molecules/ApplyToAllModal' import MyScheduleLayout from '~/components/templates/MySchedulelayout' -import UnderConstructionPage from '~/components/pages/UnderContructionPage' import { ReactSelectOption, TimeEntryWithBreak, @@ -32,14 +37,34 @@ import { const animatedComponents = makeAnimated() const RequestNewSchedule: NextPage = (): JSX.Element => { - const [selectedShift, setSelectedShift] = useState() - + const [leaders, setLeaders] = useState([]) const [errorMessage, setErrorMessage] = useState('') + const [allSchedule, setAllSchedule] = useState([]) + const [selectedShift, setSelectedShift] = useState() const [isOpenApplyToAll, setIsOpenApplyToAll] = useState(false) + // EMPLOYEE SCHEDULE HOOKS + const { getAllEmployeeScheduleQuery } = useEmployeeSchedule() + const { data: allEmployeeData } = getAllEmployeeScheduleQuery() + const allEmployeeSchedule = allEmployeeData?.allEmployeeScheduleDetails + + // PROJECT HOOKS + const { getLeadersQuery } = useProject() + const { + data: leadersList, + isLoading: isLoadingLeaders, + isSuccess: isLeadersSuccess, + isFetching: isLeadersFetching + } = getLeadersQuery(undefined) + + // CHANGE SCHEDULE HOOKS + const { handleChangeScheduleMutation } = useChangeSchedule() + const changeScheduleMutation = handleChangeScheduleMutation() + const { reset, watch, + control, setValue, register, getValues, @@ -62,14 +87,51 @@ const RequestNewSchedule: NextPage = (): JSX.Element => { const handleSaveSchedule: SubmitHandler = async ( data ): Promise => { - return await new Promise((resolve) => { - if (isButtonSelected) { - alert(JSON.stringify(data, null, 2)) - } else { - setErrorMessage('Please select atleast one Day of the week button.') + const leaderIds = data?.teamLeaders?.map((item) => parseInt(item.value)) + const workingDays: IWorkDay[] = [] + const daysOfWeek: string[] = [ + 'monday', + 'tuesday', + 'wednesday', + 'thursday', + 'friday', + 'saturday', + 'sunday' + ] + + for (const day of daysOfWeek) { + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions + const dayData = (data as any)[`${day}Selected`] && (data as any)[day] + + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions + if (dayData) { + const workDay: IWorkDay = { + day: day.charAt(0).toUpperCase() + day.slice(1), + from: dayData.timeIn, + to: dayData.timeOut, + breakFrom: dayData.breakFrom, + breakTo: dayData.breakTo + } + workingDays.push(workDay) } - resolve() - }) + } + + if (isButtonSelected && !isEmpty(watch('teamLeaders'))) { + await changeScheduleMutation.mutateAsync( + { + leaderIds, + workingDays + }, + { + onSettled: () => { + toast.success('Requested New Schedule Successfully.') + handleReset() + } + } + ) + } else { + setErrorMessage('Please select atleast one Day of the week button.') + } } useEffect(() => { @@ -78,6 +140,28 @@ const RequestNewSchedule: NextPage = (): JSX.Element => { } }, [isButtonSelected]) + useEffect(() => { + if (leadersList !== undefined) setLeaders(leadersList.allLeaders) + }, [leadersList]) + + useEffect(() => { + if (allEmployeeSchedule !== undefined) { + const empty = [{ value: '', label: '' }] + const customShift: ReactSelectOption = { + value: '0', + label: 'Custom Shift' + } + const filteredSchedule: ReactSelectOption[] | undefined = allEmployeeSchedule?.map( + (sched) => ({ + value: sched?.id.toString(), + label: sched?.scheduleName + }) + ) + filteredSchedule?.unshift(customShift) + setAllSchedule(filteredSchedule ?? empty) + } + }, [allEmployeeSchedule]) + const empty = { timeIn: '', timeOut: '', @@ -86,8 +170,8 @@ const RequestNewSchedule: NextPage = (): JSX.Element => { } const handleReset = (): void => { - setSelectedShift(shiftSchedule[0]) setErrorMessage('') + setSelectedShift(allSchedule[0]) reset({ mondaySelected: false, tuesdaySelected: false, @@ -102,7 +186,8 @@ const RequestNewSchedule: NextPage = (): JSX.Element => { thursday: empty, friday: empty, saturday: empty, - sunday: empty + sunday: empty, + teamLeaders: [] }) } @@ -116,95 +201,83 @@ const RequestNewSchedule: NextPage = (): JSX.Element => { }, [selectedShift]) const getScheduleShiftData = (selectedShift: ReactSelectOption): void => { - const shiftSelect = { - mondaySelected: true, - tuesdaySelected: true, - wednesdaySelected: true, - thursdaySelected: true, - fridaySelected: true, - saturdaySelected: false, - sundaySelected: false - } - const morningShiftData = { - ...shiftSelect, + const newShiftData = allEmployeeSchedule?.find( + (item) => item.id.toString() === selectedShift.value + ) + + const { scheduleName, days } = newShiftData ?? {} + + const mondayData = days?.[0] + const tuesdayData = days?.[1] + const wednesdayData = days?.[2] + const thursdayData = days?.[3] + const fridayData = days?.[4] + const saturdayData = days?.[5] + const sundayData = days?.[6] + + const shiftData = { + scheduleName: scheduleName ?? '', + mondaySelected: mondayData?.isDaySelected ?? false, monday: { - timeIn: '09:30', - timeOut: '06:30', - breakFrom: '12:00', - breakTo: '13:00' + timeIn: mondayData?.timeIn ?? '', + timeOut: mondayData?.timeOut ?? '', + breakFrom: mondayData?.breakFrom ?? '', + breakTo: mondayData?.breakTo ?? '' }, + tuesdaySelected: tuesdayData?.isDaySelected ?? false, tuesday: { - timeIn: '09:30', - timeOut: '06:30', - breakFrom: '12:00', - breakTo: '03:00' + timeIn: tuesdayData?.timeIn ?? '', + timeOut: tuesdayData?.timeOut ?? '', + breakFrom: tuesdayData?.breakFrom ?? '', + breakTo: tuesdayData?.breakTo ?? '' }, + wednesdaySelected: wednesdayData?.isDaySelected ?? false, wednesday: { - timeIn: '09:30', - timeOut: '06:30', - breakFrom: '12:00', - breakTo: '03:00' + timeIn: wednesdayData?.timeIn ?? '', + timeOut: wednesdayData?.timeOut ?? '', + breakFrom: wednesdayData?.breakFrom ?? '', + breakTo: wednesdayData?.breakTo ?? '' }, + thursdaySelected: thursdayData?.isDaySelected ?? false, thursday: { - timeIn: '09:30', - timeOut: '06:30', - breakFrom: '12:00', - breakTo: '03:00' + timeIn: thursdayData?.timeIn ?? '', + timeOut: thursdayData?.timeOut ?? '', + breakFrom: thursdayData?.breakFrom ?? '', + breakTo: thursdayData?.breakTo ?? '' }, + fridaySelected: fridayData?.isDaySelected ?? false, friday: { - timeIn: '09:30', - timeOut: '06:30', - breakFrom: '12:00', - breakTo: '03:00' - } - } - const afternoonShiftData = { - ...shiftSelect, - monday: { - timeIn: '01:00', - timeOut: '10:00', - breakFrom: '05:00', - breakTo: '01:00' - }, - tuesday: { - timeIn: '01:00', - timeOut: '10:00', - breakFrom: '05:00', - breakTo: '06:00' + timeIn: fridayData?.timeIn ?? '', + timeOut: fridayData?.timeOut ?? '', + breakFrom: fridayData?.breakFrom ?? '', + breakTo: fridayData?.breakTo ?? '' }, - wednesday: { - timeIn: '06:00', - timeOut: '10:00', - breakFrom: '05:00', - breakTo: '06:00' - }, - thursday: { - timeIn: '06:00', - timeOut: '10:00', - breakFrom: '05:00', - breakTo: '06:00' + saturdaySelected: saturdayData?.isDaySelected ?? false, + saturday: { + timeIn: saturdayData?.timeIn ?? '', + timeOut: saturdayData?.timeOut ?? '', + breakFrom: saturdayData?.breakFrom ?? '', + breakTo: saturdayData?.breakTo ?? '' }, - friday: { - timeIn: '06:00', - timeOut: '10:00', - breakFrom: '05:00', - breakTo: '06:00' + sundaySelected: sundayData?.isDaySelected ?? false, + sunday: { + timeIn: sundayData?.timeIn ?? '', + timeOut: sundayData?.timeOut ?? '', + breakFrom: sundayData?.breakFrom ?? '', + breakTo: sundayData?.breakTo ?? '' } } + switch (selectedShift.value) { - case '1': + case '0': handleReset() break - case '2': - reset(morningShiftData) - break - case '3': - reset(afternoonShiftData) - break default: - handleReset() + reset(shiftData) break } + + setSelectedShift(selectedShift) } const handleOpenApplyToAllToggle = (): void => setIsOpenApplyToAll(!isOpenApplyToAll) @@ -229,15 +302,11 @@ const RequestNewSchedule: NextPage = (): JSX.Element => { setIsOpenApplyToAll(false) } - if (process.env.NEXT_PUBLIC_DISPLAY_MY_SCHEDULE_PAGE === 'false') { - return - } - return ( - +

Schedule Shift:

@@ -246,11 +315,11 @@ const RequestNewSchedule: NextPage = (): JSX.Element => { className="text-xs" value={selectedShift} styles={customStyles} - options={shiftSchedule} + options={allSchedule} closeMenuOnSelect={true} isDisabled={isSubmitting} instanceId="scheduleShift" - defaultValue={shiftSchedule[0]} + defaultValue={allSchedule[0]} components={animatedComponents} onChange={(option) => handleShiftChange(option as ReactSelectOption)} /> @@ -1108,10 +1177,55 @@ const RequestNewSchedule: NextPage = (): JSX.Element => { )}
+ + {/* Team Leader Select */} +
+ + ( + + state.isFocused + ? 'border-primary' + : !isEmpty(errors.teamLeaders) + ? 'border-rose-500 ring-rose-500' + : 'border-slate-300' + }} + backspaceRemovesValue={true} + instanceId="teamLeaderReqNewShed" + options={generateUserSelect(leaders as UserType[])} + components={animatedComponents} + className="w-full" + /> + )} + /> + {!isEmpty(errors.teamLeaders) && ( + {errors?.teamLeaders.message} + )} +
{ type="submit" variant={!isButtonSelected ? 'secondary' : 'primary'} disabled={isSubmitting || !isButtonSelected} - className="flex w-24 items-center justify-center space-x-2 rounded-md py-2 text-xs" + className="w-24 rounded-md py-2 text-xs" > - {isSubmitting ? ( - <> - - Saving... - - ) : ( - <> - Save - - )} + Save
diff --git a/client/src/pages/notifications.tsx b/client/src/pages/notifications.tsx index 5bc26750..25c25c5c 100644 --- a/client/src/pages/notifications.tsx +++ b/client/src/pages/notifications.tsx @@ -75,7 +75,8 @@ const Notifications: NextPage = (): JSX.Element => { offsets: parsedData.Offsets, managerRemarks: parsedData.ManagerRemarks, startDate: parsedData.StartDate, - endDate: parsedData.EndDate + endDate: parsedData.EndDate, + workingDays: parsedData.WorkingDays } return mapped }) diff --git a/client/src/pages/schedule-management.tsx b/client/src/pages/schedule-management.tsx index fac868c5..73ba8846 100644 --- a/client/src/pages/schedule-management.tsx +++ b/client/src/pages/schedule-management.tsx @@ -111,9 +111,9 @@ const ScheduleManagement: NextPage = (): JSX.Element => { }, [allEmployeeSchedule]) // THIS WILL SUBMIT TO EITHER SAVE OR UPDATE SCHEDULE - const handleSaveSchedule: SubmitHandler = async (data): Promise => { + const handleSaveSchedule: SubmitHandler = async (data): Promise => { const workingDays: IWorkDay[] = [] - const daysOfWeek = [ + const daysOfWeek: string[] = [ 'monday', 'tuesday', 'wednesday', @@ -122,9 +122,10 @@ const ScheduleManagement: NextPage = (): JSX.Element => { 'saturday', 'sunday' ] + for (const day of daysOfWeek) { // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - const dayData = data[`${day}Selected`] && data[day] + const dayData = (data as any)[`${day}Selected`] && (data as any)[day] // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (dayData) { workingDays.push({ diff --git a/client/src/utils/constants/notificationTypes.ts b/client/src/utils/constants/notificationTypes.ts index cc9a784f..70e375a5 100644 --- a/client/src/utils/constants/notificationTypes.ts +++ b/client/src/utils/constants/notificationTypes.ts @@ -3,7 +3,7 @@ export const SpecificType = { APPROVAL: 'approve', DISAPPROVAL: 'disapprove', SUMMARY: 'summary' -} +} as const export const NOTIFICATION_TYPE = { OVERTIME: 'overtime', @@ -19,5 +19,6 @@ export const NOTIFICATION_TYPE = { OFFSET: 'offset', OFFSET_RESOLVED: 'offset_resolved', ESL_CHANGE_SHIFT: 'esl change shift', - ESL_CHANGE_SHIFT_RESOLVED: 'esl change shift_resolved' -} + ESL_CHANGE_SHIFT_RESOLVED: 'esl change shift_resolved', + CHANGE_SCHEDULE: 'change schedule' +} as const diff --git a/client/src/utils/interfaces/changeScheduleInterface.ts b/client/src/utils/interfaces/changeScheduleInterface.ts new file mode 100644 index 00000000..5489e376 --- /dev/null +++ b/client/src/utils/interfaces/changeScheduleInterface.ts @@ -0,0 +1,6 @@ +import { IWorkDay } from './../types/employeeScheduleTypes' + +export interface IChangeSchedule { + leaderIds: number[] + workingDays: IWorkDay[] +} diff --git a/client/src/utils/interfaces/index.tsx b/client/src/utils/interfaces/index.tsx index 8bbc0e64..c2fd18a8 100644 --- a/client/src/utils/interfaces/index.tsx +++ b/client/src/utils/interfaces/index.tsx @@ -1,6 +1,7 @@ import * as Icons from 'react-feather' import { IESLOffset } from './eslOffsetInterface' +import { IWorkDayData } from '../types/notificationTypes' import { ReactSelectOption, TimeEntryWithBreak } from './../types/formValues' export type IconName = keyof typeof Icons @@ -66,6 +67,7 @@ export interface INotification { managerRemarks: string | null startDate: string endDate: string + workingDays: IWorkDayData[] } export interface IEmployeeManagement { diff --git a/client/src/utils/types/formValues.ts b/client/src/utils/types/formValues.ts index 1470bdff..cc87838a 100644 --- a/client/src/utils/types/formValues.ts +++ b/client/src/utils/types/formValues.ts @@ -235,4 +235,5 @@ export type RequestNewScheduleFormData = { friday: TimeEntryWithBreak saturday: TimeEntryWithBreak sunday: TimeEntryWithBreak + teamLeaders: ReactSelectOption[] } diff --git a/client/src/utils/types/notificationTypes.ts b/client/src/utils/types/notificationTypes.ts index f3a76525..6af8d1bc 100644 --- a/client/src/utils/types/notificationTypes.ts +++ b/client/src/utils/types/notificationTypes.ts @@ -37,8 +37,17 @@ export type NotificationData = { ManagerRemarks: string | null StartDate: string EndDate: string + WorkingDays: IWorkDayData[] } export type NotificationRequestInput = { id: number } + +export type IWorkDayData = { + Day: string + From: string + To: string + BreakFrom: string + BreakTo: string +} diff --git a/client/src/utils/validation/index.ts b/client/src/utils/validation/index.ts index fcb89828..6d994e29 100644 --- a/client/src/utils/validation/index.ts +++ b/client/src/utils/validation/index.ts @@ -1,4 +1,5 @@ import * as yup from 'yup' +import { ReactSelectOption } from '../types/formValues' import { WorkInterruptionType } from './../constants/work-status' @@ -422,7 +423,26 @@ export const ReassignScheduleSchema = yup.object().shape({ schedule: SelectSchema.label('Schedule') }) -export const RequestNewScheduleSchema = ScheduleSchema.omit(['scheduleName']) +type RequestNewScheduleSchemaData = typeof ScheduleSchema & { + teamLeaders: ReactSelectOption[] +} + +export const RequestNewScheduleSchema: RequestNewScheduleSchemaData = yup + .object() + .shape({ + ...ScheduleSchema.fields, // Include existing fields from ScheduleSchema + teamLeaders: yup + .array() + .min(1, 'At least one team leader must be selected') + .of( + yup.object().shape({ + value: yup.string().required(), + label: yup.string().required() + }) + ) + .required('Team leader is required') + }) + .omit(['scheduleName']) export const ApplyToAllSchema = yup.object().shape({ timeIn: yup.string().required('Time In is required'),