Переглянути джерело

Add new man hour daaily report

develop
MSI\2Fi 3 місяці тому
джерело
коміт
caddc10a7d
8 змінених файлів з 221 додано та 3 видалено
  1. +28
    -0
      src/app/(main)/analytics/ProjectMonthlyReport/page.tsx
  2. +16
    -2
      src/app/api/reports/actions.ts
  3. +11
    -0
      src/app/api/reports/index.ts
  4. +82
    -0
      src/components/GenerateProjectMonthlyReport/GenerateProjectMonthlyReport.tsx
  5. +38
    -0
      src/components/GenerateProjectMonthlyReport/GenerateProjectMonthlyReportLoading.tsx
  6. +38
    -0
      src/components/GenerateProjectMonthlyReport/GenerateProjectMonthlyReportWrapper.tsx
  7. +1
    -0
      src/components/GenerateProjectMonthlyReport/index.ts
  8. +7
    -1
      src/components/NavigationContent/NavigationContent.tsx

+ 28
- 0
src/app/(main)/analytics/ProjectMonthlyReport/page.tsx Переглянути файл

@@ -0,0 +1,28 @@
import { Metadata } from "next";
import { Suspense } from "react";
import { I18nProvider, getServerI18n } from "@/i18n";
import GenerateProjectMonthlyReport from "@/components/GenerateProjectMonthlyReport";
import { Typography } from "@mui/material";

export const metadata: Metadata = {
title: "Staff Monthly Work Hours Analysis Report",
};

const ProjectMonthlyReport: React.FC = async () => {
const { t } = await getServerI18n("report");

return (
<>
<Typography variant="h4" marginInlineEnd={2}>
{t("Staff Monthly Work Hours Analysis Report")}
</Typography>
<I18nProvider namespaces={["report", "common"]}>
<Suspense fallback={<GenerateProjectMonthlyReport.Loading />}>
<GenerateProjectMonthlyReport />
</Suspense>
</I18nProvider>
</>
);
};

export default ProjectMonthlyReport;

+ 16
- 2
src/app/api/reports/actions.ts Переглянути файл

@@ -1,7 +1,7 @@
"use server";

import { serverFetchBlob } from "@/app/utils/fetchUtil";
import { MonthlyWorkHoursReportRequest, ProjectCashFlowReportRequest, LateStartReportRequest, ProjectResourceOverconsumptionReportRequest, ProjectPandLReportRequest, ProjectCompletionReportRequest, ProjectPotentialDelayReportRequest, CostAndExpenseReportRequest, CrossTeamChargeReportRequest, ProjectManhourSummaryReportRequest } from ".";
import { MonthlyWorkHoursReportRequest, ProjectCashFlowReportRequest, LateStartReportRequest, ProjectResourceOverconsumptionReportRequest, ProjectPandLReportRequest, ProjectCompletionReportRequest, ProjectPotentialDelayReportRequest, CostAndExpenseReportRequest, CrossTeamChargeReportRequest, ProjectManhourSummaryReportRequest, ProjectMonthlyReportRequest } from ".";
import { BASE_API_URL } from "@/config/api";

export interface FileResponse {
@@ -148,4 +148,18 @@ export const fetchProjectManhourSummaryReport = async (data: ProjectManhourSumma
);

return reportBlob
};
};


export const fetchProjectMonthlyReport = async (data: ProjectMonthlyReportRequest) => {
const reportBlob = await serverFetchBlob<FileResponse>(
`${BASE_API_URL}/reports/gen-project-monthly-report`,
{
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
},
);

return reportBlob
};

+ 11
- 0
src/app/api/reports/index.ts Переглянути файл

@@ -136,4 +136,15 @@ export interface ProjectManhourSummaryReportRequest {
teamId: number;
startDate: string;
endDate: string;
}

export interface ProjectMonthlyReportRequest {
projectId: number;
yearMonth: string;
holidays: String[];
}

export interface ProjectMonthlyReportFilter {
projects: string[];
date: string;
}

+ 82
- 0
src/components/GenerateProjectMonthlyReport/GenerateProjectMonthlyReport.tsx Переглянути файл

@@ -0,0 +1,82 @@
"use client";
import React, { useMemo } from "react";
import SearchBox, { Criterion } from "../SearchBox";
import { useTranslation } from "react-i18next";
import { ProjectResult } from "@/app/api/projects";
import {
fetchProjectMonthlyReport,
} from "@/app/api/reports/actions";
import { downloadFile } from "@/app/utils/commonUtil";
import { ProjectMonthlyReportFilter } from "@/app/api/reports";


import dayjs from "dayjs";
import { getPublicHolidaysForNYears } from "@/app/utils/holidayUtils";

interface Props {
projects: ProjectResult[];
companyHolidays: String[];
}

type SearchQuery = Partial<Omit<ProjectMonthlyReportFilter, "id">>;
type SearchParamNames = keyof SearchQuery;

const GenerateProjectMonthlyReport: React.FC<Props> = ({ projects, companyHolidays }) => {
const { t } = useTranslation("report");
const projectCombo = projects.map((project) => ({label: `${project.code} - ${project.name}`, value: project.id}))
console.log(companyHolidays)

const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
() => [
{
label: t("Project"),
paramName: "projects",
type: "autocomplete",
options: projectCombo,
needAll: false,
},
{
label: t("Date"),
paramName: "date",
type: "monthYear",
},
],
[t]
);
return (
<>
<SearchBox
formType={"download"}
criteria={searchCriteria}
onSearch={async (query: any) => {
console.log(query);
const yearNeeded = parseInt(dayjs(query.date).format("YYYY"))
const holidayList: String[] = [...getPublicHolidaysForNYears(1, yearNeeded).map((item) => dayjs(item.date).format("DD/MM/YYYY")), ...companyHolidays]
const uniqueHoliday = holidayList.filter((value, index, arr) => index === arr.indexOf(value));

let postData = {
projectId: query.projects,
yearMonth: dayjs().format("YYYY-MM").toString(),
holidays: uniqueHoliday
};
console.log(query.date.length > 0)
if (query.date.length > 0) {
postData.yearMonth = query.date
}
console.log(postData)
const response = await fetchProjectMonthlyReport(postData);
if (response) {
downloadFile(
new Uint8Array(response.blobValue),
response.filename!!
);
}
}}
/>
</>
);
};

export default GenerateProjectMonthlyReport;

+ 38
- 0
src/components/GenerateProjectMonthlyReport/GenerateProjectMonthlyReportLoading.tsx Переглянути файл

@@ -0,0 +1,38 @@
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Skeleton from "@mui/material/Skeleton";
import Stack from "@mui/material/Stack";
import React from "react";

// Can make this nicer
export const GenerateProjectMonthlyReportLoading: React.FC = () => {
return (
<>
<Card>
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={60} />
<Skeleton
variant="rounded"
height={50}
width={100}
sx={{ alignSelf: "flex-end" }}
/>
</Stack>
</CardContent>
</Card>
<Card>
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
</Stack>
</CardContent>
</Card>
</>
);
};

export default GenerateProjectMonthlyReportLoading;

+ 38
- 0
src/components/GenerateProjectMonthlyReport/GenerateProjectMonthlyReportWrapper.tsx Переглянути файл

@@ -0,0 +1,38 @@
import React from "react";
import GenerateProjectMonthlyReportLoading from "./GenerateProjectMonthlyReportLoading";
import GenerateProjectMonthlyReport from "./GenerateProjectMonthlyReport";
import { fetchStaff } from "@/app/api/staff";
import { getServerSession } from "next-auth";
import { authOptions } from "@/config/authConfig";
import { TEAM_LEAD } from "@/middleware";
import { fetchHolidays } from "@/app/api/holidays";
import { convertDateArrayToString } from "@/app/utils/formatUtil";
import { fetchProjects } from "@/app/api/projects";
interface SubComponents {
Loading: typeof GenerateProjectMonthlyReportLoading;
}

const GenerateProjectMonthlyReportWrapper: React.FC &
SubComponents = async () => {
const session: any = await getServerSession(authOptions);
const teamId = session.staff?.teamId;
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 projects = await fetchProjects();

if (role.includes(TEAM_LEAD)) {
projects = projects.filter((project) => project.teamId === teamId);
}

return <GenerateProjectMonthlyReport projects={projects} companyHolidays={companyHolidaysList} />;
};

GenerateProjectMonthlyReportWrapper.Loading = GenerateProjectMonthlyReportLoading;

export default GenerateProjectMonthlyReportWrapper;

+ 1
- 0
src/components/GenerateProjectMonthlyReport/index.ts Переглянути файл

@@ -0,0 +1 @@
export { default } from "./GenerateProjectMonthlyReportWrapper";

+ 7
- 1
src/components/NavigationContent/NavigationContent.tsx Переглянути файл

@@ -313,10 +313,15 @@ const NavigationContent: React.FC<Props> = ({ abilities, username }) => {
},
{
icon: <Analytics />,
label: "Project Manhour Summary Report",
label: "Project Manhour Summary Monthly Report",
path: "/analytics/ProjectManhourSummaryReport",
isHidden: false
},
{
icon: <Analytics />,
label: "Project Manhour Summary Daily Report",
path: "/analytics/ProjectMonthlyReport",
},
{
icon: <Analytics />,
label: "Staff Monthly Work Hours Analysis Report",
@@ -333,6 +338,7 @@ const NavigationContent: React.FC<Props> = ({ abilities, username }) => {
abilities!.includes(ability),
),
},
],
},
{


Завантаження…
Відмінити
Зберегти