浏览代码

Combine year list in financial Summary

Add holiday to "view past entrry"
main
MSI\2Fi 3 个月前
父节点
当前提交
9cd9b359a0
共有 7 个文件被更改,包括 169 次插入43 次删除
  1. +1
    -1
      src/app/utils/holidayUtils.ts
  2. +17
    -3
      src/components/PastEntryCalendar/MonthlySummary.tsx
  3. +13
    -2
      src/components/PastEntryCalendar/PastEntryCalendar.tsx
  4. +17
    -2
      src/components/PastEntryCalendar/PastEntryCalendarModal.tsx
  5. +2
    -2
      src/components/ProjectFinancialSummary/ProjectFinancialCard.tsx
  6. +48
    -33
      src/components/ProjectFinancialSummaryV2/FinancialSummary.tsx
  7. +71
    -0
      src/components/ProjectFinancialSummaryV2/Util.tsx

+ 1
- 1
src/app/utils/holidayUtils.ts 查看文件

@@ -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();


+ 17
- 3
src/components/PastEntryCalendar/MonthlySummary.tsx 查看文件

@@ -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}


+ 13
- 2
src/components/PastEntryCalendar/PastEntryCalendar.tsx 查看文件

@@ -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,
}}


+ 17
- 2
src/components/PastEntryCalendar/PastEntryCalendarModal.tsx 查看文件

@@ -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}
/>


+ 2
- 2
src/components/ProjectFinancialSummary/ProjectFinancialCard.tsx 查看文件

@@ -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"


+ 48
- 33
src/components/ProjectFinancialSummaryV2/FinancialSummary.tsx 查看文件

@@ -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>


+ 71
- 0
src/components/ProjectFinancialSummaryV2/Util.tsx 查看文件

@@ -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;
}

正在加载...
取消
保存