@@ -1,24 +0,0 @@ | |||||
//src\app\(main)\analytics\DelayReport\page.tsx | |||||
import { Metadata } from "next"; | |||||
import { I18nProvider } from "@/i18n"; | |||||
import Typography from "@mui/material/Typography"; | |||||
import DelayReportComponent from "@/components/Report/DelayReport"; | |||||
export const metadata: Metadata = { | |||||
title: "Delay Report", | |||||
}; | |||||
const ProjectLateReport: React.FC = () => { | |||||
return ( | |||||
<I18nProvider namespaces={["analytics"]}> | |||||
<Typography variant="h4" marginInlineEnd={2}> | |||||
Delay Report | |||||
</Typography> | |||||
{/* <Suspense fallback={<ProgressCashFlowSearch.Loading />}> | |||||
<ProgressCashFlowSearch/> | |||||
</Suspense> */} | |||||
<DelayReportComponent /> | |||||
</I18nProvider> | |||||
); | |||||
}; | |||||
export default ProjectLateReport; |
@@ -0,0 +1,33 @@ | |||||
//src\app\(main)\analytics\DelayReport\page.tsx | |||||
import { Metadata } from "next"; | |||||
import { I18nProvider, getServerI18n } from "@/i18n"; | |||||
import Typography from "@mui/material/Typography";; | |||||
import { fetchAllCustomers } from "@/app/api/customer"; | |||||
import { fetchTeam } from "@/app/api/team"; | |||||
import { Suspense } from "react"; | |||||
import GenerateProjectPotentialDelayReport from "@/components/GenerateProjectPotentialDelayReport"; | |||||
export const metadata: Metadata = { | |||||
title: "Project Potential Delay Report", | |||||
}; | |||||
const ProjectPotentialDelayReport: React.FC = async () => { | |||||
const { t } = await getServerI18n("reports"); | |||||
fetchAllCustomers() | |||||
fetchTeam() | |||||
return ( | |||||
<> | |||||
<Typography variant="h4" marginInlineEnd={2}> | |||||
{t("Project Potential Delay Report")} | |||||
</Typography> | |||||
<I18nProvider namespaces={["report", "common"]}> | |||||
<Suspense fallback={<GenerateProjectPotentialDelayReport.Loading />}> | |||||
<GenerateProjectPotentialDelayReport /> | |||||
</Suspense> | |||||
</I18nProvider> | |||||
</> | |||||
); | |||||
}; | |||||
export default ProjectPotentialDelayReport; |
@@ -1,7 +1,7 @@ | |||||
"use server"; | "use server"; | ||||
import { serverFetchBlob, serverFetchJson } from "@/app/utils/fetchUtil"; | |||||
import { MonthlyWorkHoursReportRequest, ProjectCashFlowReportRequest, LateStartReportRequest, ProjectResourceOverconsumptionReportRequest, ProjectPandLReportRequest, ProjectCompletionReportRequest } from "."; | |||||
import { serverFetchBlob } from "@/app/utils/fetchUtil"; | |||||
import { MonthlyWorkHoursReportRequest, ProjectCashFlowReportRequest, LateStartReportRequest, ProjectResourceOverconsumptionReportRequest, ProjectPandLReportRequest, ProjectCompletionReportRequest, ProjectPotentialDelayReportRequest } from "."; | |||||
import { BASE_API_URL } from "@/config/api"; | import { BASE_API_URL } from "@/config/api"; | ||||
export interface FileResponse { | export interface FileResponse { | ||||
@@ -22,6 +22,19 @@ export const fetchProjectCashFlowReport = async (data: ProjectCashFlowReportRequ | |||||
return reportBlob | return reportBlob | ||||
}; | }; | ||||
export const fetchProjectPotentialDelayReport = async (data: ProjectPotentialDelayReportRequest) => { | |||||
const reportBlob = await serverFetchBlob<FileResponse>( | |||||
`${BASE_API_URL}/reports/ProjectPotentialDelayReport`, | |||||
{ | |||||
method: "POST", | |||||
body: JSON.stringify(data), | |||||
headers: { "Content-Type": "application/json" }, | |||||
}, | |||||
); | |||||
return reportBlob | |||||
}; | |||||
export const fetchMonthlyWorkHoursReport = async (data: MonthlyWorkHoursReportRequest) => { | export const fetchMonthlyWorkHoursReport = async (data: MonthlyWorkHoursReportRequest) => { | ||||
const reportBlob = await serverFetchBlob<FileResponse>( | const reportBlob = await serverFetchBlob<FileResponse>( | ||||
`${BASE_API_URL}/reports/StaffMonthlyWorkHourAnalysisReport`, | `${BASE_API_URL}/reports/StaffMonthlyWorkHourAnalysisReport`, | ||||
@@ -28,6 +28,16 @@ export interface ProjectCashFlowReportRequest { | |||||
dateType: string; | dateType: string; | ||||
} | } | ||||
// - Project Potential Delay Report | |||||
export interface ProjectPotentialDelayReportFilter { | |||||
team: string[]; | |||||
client: string[]; | |||||
} | |||||
export interface ProjectPotentialDelayReportRequest { | |||||
teamId: number | "All"; | |||||
clientId: number | "All"; | |||||
} | |||||
// - Monthly Work Hours Report | // - Monthly Work Hours Report | ||||
export interface MonthlyWorkHoursReportFilter { | export interface MonthlyWorkHoursReportFilter { | ||||
@@ -0,0 +1,53 @@ | |||||
"use client"; | |||||
import React, { useMemo } from "react"; | |||||
import SearchBox, { Criterion } from "../SearchBox"; | |||||
import { useTranslation } from "react-i18next"; | |||||
import { ProjectPotentialDelayReportFilter } from "@/app/api/reports"; | |||||
import { fetchProjectCashFlowReport, fetchProjectPotentialDelayReport } from "@/app/api/reports/actions"; | |||||
import { downloadFile } from "@/app/utils/commonUtil"; | |||||
import { TeamResult } from "@/app/api/team"; | |||||
import { Customer } from "@/app/api/customer"; | |||||
interface Props { | |||||
teams: TeamResult[]; | |||||
clients: Customer[]; | |||||
} | |||||
type SearchQuery = Partial<Omit<ProjectPotentialDelayReportFilter, "id">>; | |||||
type SearchParamNames = keyof SearchQuery; | |||||
const GenerateProjectPotentialDelayReport: React.FC<Props> = ({ teams, clients }) => { | |||||
const { t } = useTranslation("report"); | |||||
const teamCombo = teams.map(team => `${team.code} - ${team.name}`) | |||||
const clientCombo = clients.map(client => `${client.code} - ${client.name}`) | |||||
const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||||
() => [ | |||||
{ label: t("Team"), paramName: "team", type: "select", options: teamCombo }, | |||||
{ label: t("Client"), paramName: "client", type: "select", options: clientCombo }, | |||||
], | |||||
[t], | |||||
); | |||||
return ( | |||||
<> | |||||
<SearchBox | |||||
criteria={searchCriteria} | |||||
onSearch={async (query) => { | |||||
const teamIndex = teamCombo.findIndex(team => team === query.team) | |||||
const clientIndex = clientCombo.findIndex(client => client === query.client) | |||||
const response = await fetchProjectPotentialDelayReport({ teamId: teams[teamIndex]?.id ?? "All", clientId: clients[clientIndex]?.id ?? "All" }) | |||||
if (response) { | |||||
downloadFile(new Uint8Array(response.blobValue), response.filename!!) | |||||
} | |||||
}} | |||||
formType={"download"} | |||||
/> | |||||
</> | |||||
); | |||||
}; | |||||
export default GenerateProjectPotentialDelayReport; |
@@ -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 GenerateProjectPotentialDelayReportLoading: 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 GenerateProjectPotentialDelayReportLoading; |
@@ -0,0 +1,19 @@ | |||||
import React from "react"; | |||||
import GenerateProjectPotentialDelayReportLoading from "./GenerateProjectPotentialDelayReportLoading"; | |||||
import GenerateProjectPotentialDelayReport from "./GenerateProjectPotentialDelayReport"; | |||||
import { fetchTeam } from "@/app/api/team"; | |||||
import { fetchAllCustomers } from "@/app/api/customer"; | |||||
interface SubComponents { | |||||
Loading: typeof GenerateProjectPotentialDelayReportLoading; | |||||
} | |||||
const GenerateProjectPotentialDelayReportWrapper: React.FC & SubComponents = async () => { | |||||
const [teams, clients] = await Promise.all([fetchTeam(), fetchAllCustomers()]) | |||||
return <GenerateProjectPotentialDelayReport teams={teams} clients={clients}/>; | |||||
}; | |||||
GenerateProjectPotentialDelayReportWrapper.Loading = GenerateProjectPotentialDelayReportLoading; | |||||
export default GenerateProjectPotentialDelayReportWrapper; |
@@ -0,0 +1 @@ | |||||
export { default } from "./GenerateProjectPotentialDelayReportWrapper"; |
@@ -144,8 +144,8 @@ const NavigationContent: React.FC<Props> = ({ abilities }) => { | |||||
}, | }, | ||||
{ | { | ||||
icon: <Analytics />, | icon: <Analytics />, | ||||
label: "Delay Report", | |||||
path: "/analytics/DelayReport", | |||||
label: "Project Potential Delay Report", | |||||
path: "/analytics/ProjectPotentialDelayReport", | |||||
}, | }, | ||||
{ | { | ||||
icon: <Analytics />, | icon: <Analytics />, | ||||