Add holiday to "view past entrry"main
@@ -14,7 +14,7 @@ export const getPublicHolidaysForNYears = (years: number = 1, currYr?: number) = | |||
.flatMap((_, index) => { | |||
const currentYear = currYr ?? new Date().getFullYear(); | |||
const holidays = hd.getHolidays(currentYear - index); | |||
console.log(holidays) | |||
// console.log(holidays) | |||
return holidays.map((ele) => { | |||
const tempDay = new Date(ele.date); | |||
const tempYear = tempDay.getFullYear(); | |||
@@ -11,9 +11,9 @@ import { | |||
Typography, | |||
} from "@mui/material"; | |||
import union from "lodash/union"; | |||
import { useCallback, useMemo } from "react"; | |||
import { useCallback, useEffect, useMemo } from "react"; | |||
import dayjs, { Dayjs } from "dayjs"; | |||
import { getHolidayForDate } from "@/app/utils/holidayUtils"; | |||
import { getHolidayForDate, getPublicHolidaysForNYears } from "@/app/utils/holidayUtils"; | |||
import { HolidaysResult } from "@/app/api/holidays"; | |||
import { manhourFormatter, shortDateFormatter } from "@/app/utils/formatUtil"; | |||
import { useTranslation } from "react-i18next"; | |||
@@ -39,6 +39,14 @@ const MonthlySummary: React.FC<Props> = ({ | |||
i18n: { language }, | |||
} = useTranslation("home"); | |||
// calendar related | |||
const holidays = useMemo(() => { | |||
const holidays = getPublicHolidaysForNYears(1, currentMonth.year()).map(holiday => holiday.date) | |||
return holidays.filter(date => { | |||
return currentMonth.isSame(dayjs(date), "month") | |||
}) | |||
}, [currentMonth]); | |||
const timesheetForCurrentMonth = useMemo(() => { | |||
return pickBy(timesheet, (_, date) => { | |||
return currentMonth.isSame(dayjs(date), "month"); | |||
@@ -55,8 +63,9 @@ const MonthlySummary: React.FC<Props> = ({ | |||
return union( | |||
Object.keys(timesheetForCurrentMonth), | |||
Object.keys(leavesForCurrentMonth), | |||
holidays | |||
); | |||
}, [timesheetForCurrentMonth, leavesForCurrentMonth]).sort(); | |||
}, [timesheetForCurrentMonth, leavesForCurrentMonth, holidays]).sort(); | |||
const makeSelectDate = useCallback( | |||
(date: string) => () => { | |||
@@ -65,6 +74,11 @@ const MonthlySummary: React.FC<Props> = ({ | |||
[onDateSelect], | |||
); | |||
useEffect(()=> { | |||
console.log(holidays) | |||
console.log(timesheetForCurrentMonth) | |||
},[currentMonth, timesheetForCurrentMonth]) | |||
return ( | |||
<Stack | |||
gap={2} | |||
@@ -25,6 +25,7 @@ dayjs.tz.guess(); | |||
export interface Props { | |||
timesheet: RecordTimesheetInput; | |||
leaves: RecordLeaveInput; | |||
publicHolidays: string[]; | |||
onDateSelect: (date: string) => void; | |||
onMonthChange: (day: Dayjs) => void; | |||
} | |||
@@ -32,6 +33,7 @@ export interface Props { | |||
const getColor = ( | |||
hasTimeInput: boolean, | |||
hasLeave: boolean, | |||
isPublicHoliday: boolean, | |||
): string | undefined => { | |||
if (hasTimeInput && hasLeave) { | |||
return "success.light"; | |||
@@ -39,7 +41,9 @@ const getColor = ( | |||
return "info.light"; | |||
} else if (hasLeave) { | |||
return "warning.light"; | |||
} else { | |||
} else if (isPublicHoliday){ | |||
return "error.light"; | |||
}else { | |||
return undefined; | |||
} | |||
}; | |||
@@ -47,6 +51,7 @@ const getColor = ( | |||
const EntryDay: React.FC<PickersDayProps<Dayjs> & Props> = ({ | |||
timesheet, | |||
leaves, | |||
publicHolidays, | |||
...pickerProps | |||
}) => { | |||
const timesheetDays = Object.keys(timesheet); | |||
@@ -60,11 +65,15 @@ const EntryDay: React.FC<PickersDayProps<Dayjs> & Props> = ({ | |||
dayjs(day).isSame(pickerProps.day, "day"), | |||
); | |||
const isPublicHoliday = publicHolidays.some((day) => | |||
dayjs(day).isSame(pickerProps.day, "day"), | |||
); | |||
return ( | |||
<PickersDay | |||
{...pickerProps} | |||
disabled={!(hasTimesheetInput || hasLeaveInput)} | |||
sx={{ backgroundColor: getColor(hasTimesheetInput, hasLeaveInput) }} | |||
sx={{ backgroundColor: getColor(hasTimesheetInput, hasLeaveInput, isPublicHoliday) }} | |||
/> | |||
); | |||
}; | |||
@@ -72,6 +81,7 @@ const EntryDay: React.FC<PickersDayProps<Dayjs> & Props> = ({ | |||
const PastEntryCalendar: React.FC<Props> = ({ | |||
timesheet, | |||
leaves, | |||
publicHolidays, | |||
onDateSelect, | |||
onMonthChange, | |||
}) => { | |||
@@ -98,6 +108,7 @@ const PastEntryCalendar: React.FC<Props> = ({ | |||
day: { | |||
timesheet, | |||
leaves, | |||
publicHolidays, | |||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |||
} as any, | |||
}} | |||
@@ -12,7 +12,7 @@ import { | |||
import PastEntryCalendar, { | |||
Props as PastEntryCalendarProps, | |||
} from "./PastEntryCalendar"; | |||
import { useCallback, useState } from "react"; | |||
import { useCallback, useState, useMemo } from "react"; | |||
import { useTranslation } from "react-i18next"; | |||
import { ArrowBack } from "@mui/icons-material"; | |||
import PastEntryList from "./PastEntryList"; | |||
@@ -23,9 +23,10 @@ import FullscreenModal from "../FullscreenModal"; | |||
import MonthlySummary from "./MonthlySummary"; | |||
import { HolidaysResult } from "@/app/api/holidays"; | |||
import dayjs from "dayjs"; | |||
import { getPublicHolidaysForNYears } from "@/app/utils/holidayUtils"; | |||
interface Props | |||
extends Omit<PastEntryCalendarProps, "onDateSelect" | "onMonthChange"> { | |||
extends Omit<PastEntryCalendarProps, "onDateSelect" | "onMonthChange" | "publicHolidays"> { | |||
open: boolean; | |||
handleClose: () => void; | |||
leaveTypes: LeaveType[]; | |||
@@ -60,6 +61,13 @@ const PastEntryCalendarModal: React.FC<Props> = ({ | |||
handleClose(); | |||
}, [handleClose]); | |||
const publicHolidays = useMemo(() => { | |||
const holidays = getPublicHolidaysForNYears(1, currentMonth.year()).map(holiday => holiday.date) | |||
return holidays.filter(date => { | |||
return currentMonth.isSame(dayjs(date), "month") | |||
}) | |||
}, [currentMonth]); | |||
const content = ( | |||
<Box sx={{ display: "flex", flexDirection: { xs: "column", sm: "row" } }}> | |||
<Box> | |||
@@ -80,10 +88,17 @@ const PastEntryCalendarModal: React.FC<Props> = ({ | |||
{t("Has both timesheet and leave entry")} | |||
</Typography> | |||
</Box> | |||
<Box display="flex" alignItems="center" gap={1}> | |||
<Indicator sx={{ backgroundColor: "error.light" }} /> | |||
<Typography variant="caption"> | |||
{t("Public Holiday")} | |||
</Typography> | |||
</Box> | |||
</Stack> | |||
<PastEntryCalendar | |||
timesheet={timesheet} | |||
leaves={leaves} | |||
publicHolidays={publicHolidays} | |||
onDateSelect={setSelectedDate} | |||
onMonthChange={setMonthChange} | |||
/> | |||
@@ -307,7 +307,7 @@ const ProjectFinancialCard: React.FC<Props> = ({ | |||
> | |||
{"(k) " + t("Projected Cost Performance Index") + " (CPI)"} | |||
</div> | |||
{Number(ProjectedCPI) < 1 && ( | |||
{Number(ProjectedCPI) < 1.2 && ( | |||
<> | |||
<div | |||
className="text-lg font-medium mx-5 mb-2" | |||
@@ -320,7 +320,7 @@ const ProjectFinancialCard: React.FC<Props> = ({ | |||
</div> | |||
</> | |||
)} | |||
{Number(ProjectedCPI) >= 1 && ( | |||
{Number(ProjectedCPI) >= 1.2 && ( | |||
<> | |||
<div | |||
className="text-lg font-medium mx-5 mb-2" | |||
@@ -8,6 +8,7 @@ import dayjs from 'dayjs'; | |||
import { INPUT_DATE_FORMAT } from '@/app/utils/formatUtil'; | |||
import { SumOfByClient, SumOfByTeam, sumUpByClient, sumUpByTeam } from './gptFn'; | |||
import FinancialStatusByProject from './FinnancialStatusByProject'; | |||
import { generateYearRanges } from './Util'; | |||
interface Props { | |||
_teamId: number, | |||
@@ -17,6 +18,7 @@ interface Props { | |||
type InputDate = { | |||
startDate: string; | |||
endDate: string; | |||
label?: string; | |||
} | |||
type DateParams = { | |||
@@ -116,16 +118,28 @@ const FinancialSummaryPage: React.FC<Props> = ({ | |||
// return list | |||
// }, []) | |||
const comboList: string[] = useMemo(() => { | |||
const list = ["All"]; | |||
for (let i = 1; i < lengthOfCombo - 1; i++) { | |||
const yearRange = `${currFinancialYear - i} - ${currFinancialYear - i + 1}`; | |||
const label = i === 1 ? `${yearRange} ${t("(current year)")}` : yearRange; | |||
list.push(label); | |||
} | |||
const oldestYear = currFinancialYear - (lengthOfCombo - 2); | |||
list.push(`< ${oldestYear}`); | |||
return list; | |||
const comboList: InputDate[] = useMemo(() => { | |||
const list = [{startDate: "", endDate: "", label: "All"}]; | |||
const curMonth = new Date().getMonth() + 1 | |||
// for (let i = 1; i < lengthOfCombo - 1; i++) { | |||
// const yearRange = `${currFinancialYear - i} - ${currFinancialYear - i + 1}`; | |||
// const label = i === 1 ? `${yearRange} ${t("(current year)")}` : yearRange; | |||
// list.push(label); | |||
// } | |||
// const oldestYear = currFinancialYear - (lengthOfCombo - 2); | |||
// list.push(`< ${oldestYear}`); | |||
// for (let j = 0; j < lengthOfCombo - 3; j++) { | |||
// const yearRange = `${currFinancialYear + j} - ${currFinancialYear + j + 1}`; | |||
// const label = yearRange; | |||
// list.push(label); | |||
// } | |||
// const latestYear = currFinancialYear + (lengthOfCombo - 2); | |||
// list.push(`< ${latestYear}`); | |||
const tempList = generateYearRanges(3, curMonth) | |||
const combinedList = [...list, ...tempList] | |||
return combinedList; | |||
}, [currFinancialYear, lengthOfCombo, t]); | |||
const futureList: string[] = useMemo(() => { | |||
@@ -165,21 +179,22 @@ const FinancialSummaryPage: React.FC<Props> = ({ | |||
}, []); | |||
const handleFilter = useCallback(async (value: number) => { | |||
if (isFuture){ | |||
// if (isFuture){ | |||
setPeriod(value) | |||
console.log(value) | |||
var _startDate = dateMap[value as keyof DateParams].startDate | |||
var _endDate = dateMap[value as keyof DateParams].endDate | |||
var _startDate = comboList[value].startDate | |||
var _endDate = comboList[value].endDate | |||
console.log(_startDate) | |||
console.log(_endDate) | |||
}else{ | |||
setPeriod(value) | |||
console.log(value) | |||
var _startDate = futureDateMap[value as keyof DateParams].startDate | |||
var _endDate = futureDateMap[value as keyof DateParams].endDate | |||
console.log(_startDate) | |||
console.log(_endDate) | |||
} | |||
// } | |||
// else{ | |||
// setPeriod(value) | |||
// console.log(value) | |||
// var _startDate = futureDateMap[value as keyof DateParams].startDate | |||
// var _endDate = futureDateMap[value as keyof DateParams].endDate | |||
// console.log(_startDate) | |||
// console.log(_endDate) | |||
// } | |||
await fetchFinancialSummaryByProject(_endDate, _startDate) | |||
@@ -212,21 +227,21 @@ const FinancialSummaryPage: React.FC<Props> = ({ | |||
onChange={(e) => handleFilter(Number(e.target.value))} | |||
> | |||
{ | |||
isFuture | |||
? | |||
comboList.map((str, i) => ( | |||
<MenuItem key={i} value={i}>{str}</MenuItem> | |||
// isFuture | |||
// ? | |||
comboList.map((item, i) => ( | |||
<MenuItem key={i} value={i}>{item.label}</MenuItem> | |||
)) | |||
: | |||
Object.entries(futureDateMap).map(([year, dates], index) => { | |||
const isLastItem = index === Object.keys(futureDateMap).length - 1; | |||
const str = isLastItem ? `> ${dates.startDate.slice(0,4)}` : `${dates.startDate.slice(0,4)} - ${dates.endDate.slice(0,4)}`; | |||
return <MenuItem key={year} value={year}>{str}</MenuItem>; | |||
}) | |||
// : | |||
// Object.entries(futureDateMap).map(([year, dates], index) => { | |||
// const isLastItem = index === Object.keys(futureDateMap).length - 1; | |||
// const str = isLastItem ? `> ${dates.startDate.slice(0,4)}` : `${dates.startDate.slice(0,4)} - ${dates.endDate.slice(0,4)}`; | |||
// return <MenuItem key={year} value={year}>{str}</MenuItem>; | |||
// }) | |||
} | |||
</Select> | |||
</FormControl> | |||
<FormControlLabel | |||
{/* <FormControlLabel | |||
control={ | |||
<Switch | |||
checked={!isFuture} | |||
@@ -236,7 +251,7 @@ const FinancialSummaryPage: React.FC<Props> = ({ | |||
/> | |||
} | |||
label={isFuture ? t("Past Years") : t("Future Years")} | |||
/> | |||
/> */} | |||
</Box> | |||
</CardContent> | |||
</Card> | |||
@@ -0,0 +1,71 @@ | |||
type InputDate = { | |||
startDate: string; | |||
endDate: string; | |||
label?: string; | |||
} | |||
/** | |||
* Generates a list of year ranges based on the number of past and future years specified. | |||
* @param numberOfYears The number of past and future years to include separately. | |||
* @returns An array of objects with start year, end year, and label. | |||
*/ | |||
export function generateYearRanges(numberOfYears: number, month: number): InputDate[] { | |||
const startDate = "10-01" //start date of each financial year | |||
const endDate = "09-30" // end date of each financial year | |||
// Get the base year | |||
const baseYear = new Date().getFullYear(); | |||
// Determine the current year based on the month | |||
let currentYear: number; | |||
if (month < 10) { // October is month 10 | |||
currentYear = baseYear - 1; | |||
} else { | |||
currentYear = baseYear; | |||
} | |||
// Initialize the result array | |||
const yearRanges: InputDate[] = []; | |||
// Add past years beyond numberOfYears as one group | |||
if (numberOfYears > 0) { | |||
yearRanges.push({ | |||
startDate: "", | |||
endDate: `${(currentYear - numberOfYears)}-${endDate}`, | |||
label: `< ${currentYear - numberOfYears}` | |||
}); | |||
} | |||
// Add individual past years | |||
for (let i = numberOfYears - 1; i >= 0; i--) { | |||
const startYear = currentYear - i - 1; | |||
const endYear = currentYear - i; | |||
yearRanges.push({ startDate: `${startYear}-${startDate}`, endDate: `${endYear}-${endDate}`, label: `${startYear} - ${endYear}` }); | |||
} | |||
// Add current year | |||
yearRanges.push({ | |||
startDate: `${currentYear}-${startDate}`, | |||
endDate: `${(currentYear + 1)}-${endDate}`, | |||
label: `${currentYear} - ${currentYear + 1} (current year)` | |||
}); | |||
// Add individual future years | |||
for (let i = 0; i < numberOfYears; i++) { | |||
const startYear = currentYear + i + 1; | |||
const endYear = currentYear + i + 2; | |||
yearRanges.push({ startDate: `${startYear}-${startDate}`, endDate: `${endYear}-${endDate}`, label: `${startYear} - ${endYear}` }); | |||
} | |||
// Add future years beyond numberOfYears as one group | |||
if (numberOfYears > 0) { | |||
yearRanges.push({ | |||
startDate: `${(currentYear + numberOfYears + 1)}-${startDate}`, | |||
endDate: "", | |||
label: `> ${currentYear + numberOfYears + 1}` | |||
}); | |||
} | |||
return yearRanges; | |||
} |