| @@ -60,6 +60,7 @@ export interface MonthlyWorkHoursReportFilter { | |||||
| export interface MonthlyWorkHoursReportRequest { | export interface MonthlyWorkHoursReportRequest { | ||||
| id: number; | id: number; | ||||
| yearMonth: string; | yearMonth: string; | ||||
| holidays: String[]; | |||||
| } | } | ||||
| // - Project Resource Overconsumption Report | // - Project Resource Overconsumption Report | ||||
| export interface ProjectResourceOverconsumptionReportFilter { | export interface ProjectResourceOverconsumptionReportFilter { | ||||
| @@ -8,11 +8,11 @@ dayjs.extend(arraySupport); | |||||
| const hd = new Holidays("HK"); | const hd = new Holidays("HK"); | ||||
| export const getPublicHolidaysForNYears = (years: number = 1) => { | |||||
| export const getPublicHolidaysForNYears = (years: number = 1, currYr?: number) => { | |||||
| return Array(years) | return Array(years) | ||||
| .fill(undefined) | .fill(undefined) | ||||
| .flatMap((_, index) => { | .flatMap((_, index) => { | ||||
| const currentYear = new Date().getFullYear(); | |||||
| const currentYear = currYr ?? new Date().getFullYear(); | |||||
| const holidays = hd.getHolidays(currentYear + index); | const holidays = hd.getHolidays(currentYear + index); | ||||
| return holidays.map((ele) => { | return holidays.map((ele) => { | ||||
| const tempDay = new Date(ele.date); | const tempDay = new Date(ele.date); | ||||
| @@ -28,7 +28,7 @@ const ChangePassword: React.FC<PasswordRulesProps> = ({ | |||||
| try { | try { | ||||
| let haveError = false; | let haveError = false; | ||||
| // Minimum eight characters, at least one uppercase letter, one lowercase letter, one number and one special character: | // Minimum eight characters, at least one uppercase letter, one lowercase letter, one number and one special character: | ||||
| let regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/ | |||||
| let regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&-=+_]{8,}$/ | |||||
| if (data.newPassword.length < 8 || data.newPassword.length > 20) { | if (data.newPassword.length < 8 || data.newPassword.length > 20) { | ||||
| haveError = true | haveError = true | ||||
| @@ -21,7 +21,7 @@ const CostAndExpenseReportWrapper: React.FC & SubComponents = async () => { | |||||
| let teams = await fetchTeam() | let teams = await fetchTeam() | ||||
| let needAll = true | let needAll = true | ||||
| if (role === TEAM_LEAD) { | |||||
| if (role.includes(TEAM_LEAD)) { | |||||
| needAll = false | needAll = false | ||||
| teams = teams.filter((team) => team.id === teamId); | teams = teams.filter((team) => team.id === teamId); | ||||
| } | } | ||||
| @@ -13,18 +13,20 @@ import { MonthlyWorkHoursReportFilter } from "@/app/api/reports"; | |||||
| import { records } from "@/app/api/staff/actions"; | import { records } from "@/app/api/staff/actions"; | ||||
| import { StaffResult } from "@/app/api/staff"; | import { StaffResult } from "@/app/api/staff"; | ||||
| import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||
| import { getPublicHolidaysForNYears } from "@/app/utils/holidayUtils"; | |||||
| interface Props { | interface Props { | ||||
| staffs: StaffResult[]; | staffs: StaffResult[]; | ||||
| companyHolidays: String[]; | |||||
| } | } | ||||
| type SearchQuery = Partial<Omit<MonthlyWorkHoursReportFilter, "id">>; | type SearchQuery = Partial<Omit<MonthlyWorkHoursReportFilter, "id">>; | ||||
| type SearchParamNames = keyof SearchQuery; | type SearchParamNames = keyof SearchQuery; | ||||
| const GenerateMonthlyWorkHoursReport: React.FC<Props> = ({ staffs }) => { | |||||
| const GenerateMonthlyWorkHoursReport: React.FC<Props> = ({ staffs, companyHolidays }) => { | |||||
| const { t } = useTranslation("report"); | const { t } = useTranslation("report"); | ||||
| const staffCombo = staffs.map((staff) => ({label: `${staff.name} - ${staff.staffId}`, value: staff.id})) | const staffCombo = staffs.map((staff) => ({label: `${staff.name} - ${staff.staffId}`, value: staff.id})) | ||||
| console.log(companyHolidays) | |||||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | ||||
| () => [ | () => [ | ||||
| @@ -44,6 +46,12 @@ const GenerateMonthlyWorkHoursReport: React.FC<Props> = ({ staffs }) => { | |||||
| [t] | [t] | ||||
| ); | ); | ||||
| const holidayList: String[] = [...getPublicHolidaysForNYears(1, 2023).map((item) => dayjs(item.date).format("DD/MM/YYYY")), ...companyHolidays] | |||||
| const uniqueHoliday = holidayList.filter((value, index, arr) => index === arr.indexOf(value)); | |||||
| console.log(uniqueHoliday) | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <SearchBox | <SearchBox | ||||
| @@ -54,11 +62,13 @@ const GenerateMonthlyWorkHoursReport: React.FC<Props> = ({ staffs }) => { | |||||
| let postData = { | let postData = { | ||||
| id: query.staff, | id: query.staff, | ||||
| yearMonth: dayjs().format("YYYY-MM").toString(), | yearMonth: dayjs().format("YYYY-MM").toString(), | ||||
| holidays: uniqueHoliday | |||||
| }; | }; | ||||
| console.log(query.date.length > 0) | console.log(query.date.length > 0) | ||||
| if (query.date.length > 0) { | if (query.date.length > 0) { | ||||
| postData.yearMonth = query.date | postData.yearMonth = query.date | ||||
| } | } | ||||
| console.log(postData) | |||||
| const response = await fetchMonthlyWorkHoursReport(postData); | const response = await fetchMonthlyWorkHoursReport(postData); | ||||
| if (response) { | if (response) { | ||||
| downloadFile( | downloadFile( | ||||
| @@ -5,6 +5,8 @@ import { fetchStaff } from "@/app/api/staff"; | |||||
| import { getServerSession } from "next-auth"; | import { getServerSession } from "next-auth"; | ||||
| import { authOptions } from "@/config/authConfig"; | import { authOptions } from "@/config/authConfig"; | ||||
| import { TEAM_LEAD } from "@/middleware"; | import { TEAM_LEAD } from "@/middleware"; | ||||
| import { fetchHolidays } from "@/app/api/holidays"; | |||||
| import { convertDateArrayToString } from "@/app/utils/formatUtil"; | |||||
| interface SubComponents { | interface SubComponents { | ||||
| Loading: typeof GenerateMonthlyWorkHoursReportLoading; | Loading: typeof GenerateMonthlyWorkHoursReportLoading; | ||||
| } | } | ||||
| @@ -14,13 +16,20 @@ const GenerateMonthlyWorkHoursReportWrapper: React.FC & | |||||
| const session: any = await getServerSession(authOptions); | const session: any = await getServerSession(authOptions); | ||||
| const teamId = session.staff?.teamId; | const teamId = session.staff?.teamId; | ||||
| const role = session.role; | const role = session.role; | ||||
| const companyHolidays = await fetchHolidays() | |||||
| let companyHolidaysList: String[] = [] | |||||
| if (companyHolidays.length > 0) { | |||||
| companyHolidaysList = companyHolidays.map(item => convertDateArrayToString(item.date, "DD/MM/YYYY")) as String[] | |||||
| } | |||||
| console.log(companyHolidaysList) | |||||
| let staffs = await fetchStaff(); | let staffs = await fetchStaff(); | ||||
| if (role === TEAM_LEAD) { | |||||
| if (role.includes(TEAM_LEAD)) { | |||||
| staffs = staffs.filter((staff) => staff.teamId === teamId); | staffs = staffs.filter((staff) => staff.teamId === teamId); | ||||
| } | } | ||||
| return <GenerateMonthlyWorkHoursReport staffs={staffs} />; | |||||
| return <GenerateMonthlyWorkHoursReport staffs={staffs} companyHolidays={companyHolidaysList} />; | |||||
| }; | }; | ||||
| GenerateMonthlyWorkHoursReportWrapper.Loading = GenerateMonthlyWorkHoursReportLoading; | GenerateMonthlyWorkHoursReportWrapper.Loading = GenerateMonthlyWorkHoursReportLoading; | ||||
| @@ -16,7 +16,7 @@ const ProjectCompletionReportWrapper: React.FC & SubComponents = async () => { | |||||
| const teamId = session.staff?.teamId | const teamId = session.staff?.teamId | ||||
| const role = session.role | const role = session.role | ||||
| return <ProjectCompletionReport teamId={role === TEAM_LEAD && session.staff?.team ? teamId : null}/> | |||||
| return <ProjectCompletionReport teamId={role.includes(TEAM_LEAD) && session.staff?.team ? teamId : null}/> | |||||
| }; | }; | ||||
| ProjectCompletionReportWrapper.Loading = ProjectCompletionReportLoading; | ProjectCompletionReportWrapper.Loading = ProjectCompletionReportLoading; | ||||
| @@ -24,7 +24,7 @@ const ResourceOvercomsumptionReportWrapper: React.FC & | |||||
| fetchAllSubsidiaries(), | fetchAllSubsidiaries(), | ||||
| ]); | ]); | ||||
| if (role === TEAM_LEAD) { | |||||
| if (role.includes(TEAM_LEAD)) { | |||||
| needAll = false; | needAll = false; | ||||
| teams = teams.filter((team) => team.id === teamId); | teams = teams.filter((team) => team.id === teamId); | ||||
| } | } | ||||