diff --git a/src/app/api/resourcesummary/actions.ts b/src/app/api/resourcesummary/actions.ts new file mode 100644 index 0000000..3b83a0f --- /dev/null +++ b/src/app/api/resourcesummary/actions.ts @@ -0,0 +1,19 @@ +"use server"; + +import { serverFetchJson } from "@/app/utils/fetchUtil"; +import { BASE_API_URL } from "@/config/api"; +import { Dayjs } from "dayjs"; +import { cache } from "react"; + + +export interface ResourceSummaryDetailsResult { + summaryInformation: any[]; + summaryMainStage: any[]; + summarySubStage: any[]; +} + +export const fetchResourceSummaryDetailResult = cache(async (projectId: number) => { + return serverFetchJson( + `${BASE_API_URL}/dashboard/searchProjectResourcesSummaryDetails?projectId=${projectId}` + ) +}); diff --git a/src/app/api/resourcesummary/index.ts b/src/app/api/resourcesummary/index.ts index ffaba69..e2e062e 100644 --- a/src/app/api/resourcesummary/index.ts +++ b/src/app/api/resourcesummary/index.ts @@ -1,12 +1,15 @@ import { cache } from "react"; +import { serverFetchJson } from "@/app/utils/fetchUtil"; +import { BASE_API_URL } from "@/config/api"; +import "server-only"; export interface ResourceSummaryResult { id: number; projectCode: string; projectName: string; - clientCode: string; - clientName: string; - clientCodeAndName: string; + customerCode: string; + customerName: string; + customerCodeAndName: string; } export const preloadProjects = () => { @@ -14,40 +17,6 @@ export const preloadProjects = () => { }; export const fetchResourceSummary = cache(async () => { - return mockProjects; + return serverFetchJson(`${BASE_API_URL}/dashboard/searchProjectResourcesSummary`); }); -const mockProjects: ResourceSummaryResult[] = [ - { - id: 1, - projectCode: 'C-1001-001', - projectName: 'Consultancy Project A', - clientCode: 'Client-001', - clientName: 'AAA Construction', - clientCodeAndName: 'Client-001 - AAA Construction', - }, - { - id: 2, - projectCode: 'C-1002-001', - projectName: 'Consultancy Project B', - clientCode: 'Client-001', - clientName: 'AAA Construction', - clientCodeAndName: 'Client-001 - AAA Construction', - }, - { - id: 3, - projectCode: 'C-1003-001', - projectName: 'Consultancy Project C', - clientCode: 'Client-002', - clientName: 'BBB Construction', - clientCodeAndName: 'Client-002 - BBB Construction', - }, - { - id: 4, - projectCode: 'C-1004-001', - projectName: 'Consultancy Project D', - clientCode: 'Client-002', - clientName: 'BBB Construction', - clientCodeAndName: 'Client-002 - BBB Construction', - }, -]; diff --git a/src/components/ProjectResourceSummary/ProjectResourceSummary.tsx b/src/components/ProjectResourceSummary/ProjectResourceSummary.tsx index 57d25d2..0e5ea60 100644 --- a/src/components/ProjectResourceSummary/ProjectResourceSummary.tsx +++ b/src/components/ProjectResourceSummary/ProjectResourceSummary.tsx @@ -31,9 +31,13 @@ import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; +import { useSearchParams } from 'next/navigation'; +import {fetchResourceSummaryDetailResult} from "@/app/api/resourcesummary/actions"; const ProjectResourceSummary: React.FC = () => { + const searchParams = useSearchParams(); + const projectId = searchParams.get('projectId'); const [SearchCriteria, setSearchCriteria] = React.useState({}); const { t } = useTranslation("dashboard"); const [selectionModel, setSelectionModel]: any[] = React.useState([]); @@ -43,8 +47,11 @@ const ProjectResourceSummary: React.FC = () => { const [plannedResources, setPlannedResources]:any = React.useState(0); const [actualResourcesSpent, setActualResourcesSpent]:any = React.useState(0); const [remainingResources, setRemainingResources]:any = React.useState(0); + const [dataList, setDataList]:any = React.useState(); + const [rows,setRows]:any[] = React.useState([]); - function createData(stage:any, taskCount:any, g1Planned:any, g1Actual:any, g2Planned:any, g2Actual:any, g3Planned:any, g3Actual:any, g4Planned:any, g4Actual:any, g5Planned:any, g5Actual:any, totalPlanned:any, totalActual:any, task:any) { + //[subStage, subTaskCount, subTotalPlanned, subTotalActual, subG1Planned, subG1Actual, subG2Planned, subG2Actual, subG3Planned, subG3Actual, subG4Planned, subG4Actual, subG5Planned, subG5Actual]:any[] + function createData([stage, taskCount, totalPlanned, totalActual, g1Planned, g1Actual, g2Planned, g2Actual, g3Planned, g3Actual, g4Planned, g4Actual, g5Planned, g5Actual]: any[], task:any[]) { return { stage, taskCount, @@ -61,9 +68,103 @@ const ProjectResourceSummary: React.FC = () => { totalPlanned, totalActual, task:task + // subStage, + // subTaskCount, + // subTotalPlanned, + // subTotalActual, + // subG1Planned, + // subG1Actual, + // subG2Planned, + // subG2Actual, + // subG3Planned, + // subG3Actual, + // subG4Planned, + // subG4Actual, + // subG5Planned, + // subG5Actual } } + const fetchData = async () => { + var intProjectId = 0 + if (projectId !== null) { + intProjectId = parseInt(projectId); + } + if (intProjectId !== 0) { + const result = await fetchResourceSummaryDetailResult(intProjectId); + setProjectName(result[0].summaryInformation[0].projectCodeAndName) + setProjectFee(result[0].summaryInformation[0].totalFee) + setStatus(result[0].summaryInformation[0].status) + setPlannedResources(result[0].summaryInformation[0].plannedResources) + setActualResourcesSpent(result[0].summaryInformation[0].resourcesSpent) + setRemainingResources(result[0].summaryInformation[0].remainingResources) + console.log(result); + const mainStageResult = result[0].summaryMainStage + const subStageResult = result[0].summarySubStage + const mainStageFullList = [] + const subStageFullList = [] + var tgID = mainStageResult[0].id + var tID = subStageResult[0].id + var subTgId = subStageResult[0].tgId + var firstTg = true + var firstT = true + var mainStageList = [] + var subStageList = [] + var subStageSecondLayerList = [] + var arrayNumber = 0 + for (var i = 0; i < subStageResult.length ; i++) { + if (subStageResult[i].id !== tID) { + subStageSecondLayerList.push(subStageList) + if (subStageResult[i].tgId !== subTgId) { + subStageFullList.push(subStageSecondLayerList) + subStageSecondLayerList = [] + subTgId = subStageResult[i].tgId + } + tID = subStageResult[i].id + firstT = true + subStageList = [] + } + if (firstT === true) { + subStageList.push(subStageResult[i].name) + subStageList.push(subStageResult[i].taskCount) + subStageList.push(subStageResult[i].totalPlannedResource) + subStageList.push(subStageResult[i].totalActualResourcesSpent) + firstT = false + } + if (subStageResult[i].id === tID) { + subStageList.push(subStageResult[i].plannedResources) + subStageList.push(subStageResult[i].actualResourcesSpent) + } + + } + console.log(subStageFullList) + for (var i = 0; i < mainStageResult.length; i++) { + if (mainStageResult[i].id !== tgID) { + mainStageFullList.push(createData(mainStageList,subStageFullList[arrayNumber])) + arrayNumber += 1 + tgID = mainStageResult[i].id + firstTg = true + mainStageList = [] + } + if (firstTg === true) { + mainStageList.push(mainStageResult[i].name) + mainStageList.push(mainStageResult[i].taskCount) + mainStageList.push(mainStageResult[i].totalPlannedResources) + mainStageList.push(mainStageResult[i].totalActualResourcesSpent) + firstTg = false + } + if (mainStageResult[i].id === tgID) { + mainStageList.push(mainStageResult[i].plannedResources) + mainStageList.push(mainStageResult[i].actualResourcesSpent) + } + } + setRows(mainStageFullList) + } + } + useEffect(() => { + fetchData() + }, [projectId]); + function createTaskData(stage:any, taskCount:any, g1Planned:any, g1Actual:any, g2Planned:any, g2Actual:any, g3Planned:any, g3Actual:any, g4Planned:any, g4Actual:any, g5Planned:any, g5Actual:any, totalPlanned:any, totalActual:any) { return { stage, @@ -90,30 +191,30 @@ const ProjectResourceSummary: React.FC = () => { {stage:"1.4 Attend design co-ordiantion / project",taskCount:"-",g1Planned:"-",g1Actual:"29.00",g2Planned:"-", g2Actual:"9.00", g3Planned:"-", g3Actual:"7.00", g4Planned: "-", g4Actual:"2.00", g5Planned:"-", g5Actual:"1.00", totalPlanned:"-", totalActual:"48.00"}, {stage:"1.5 Prepare / Review RIC",taskCount:"-",g1Planned:"-",g1Actual:"88.00",g2Planned:"-", g2Actual:"27.00", g3Planned:"-", g3Actual:"21.00", g4Planned: "-", g4Actual:"5.00", g5Planned:"-", g5Actual:"1.00", totalPlanned:"-", totalActual:"141.75"} ] - - const task2Rows:any = [ - ] - - const task3Rows:any = [ - ] - - const task4Rows:any = [ - ] - - const task5Rows:any = [ - ] - - const task6Rows:any = [ - ] - - const rows = [ - createData("Stage 1 - Design & Cost Planning / Estimating","5","576.00","576.00","192.00", "180.00", "144.00", "140.00", "38.40", "38.00", "9.60", "9.75", "960.00", "943.75",task1Rows), - createData("Stage 2 - Tender Documentation","11", "384.00", "382.00", "128.00", "130.00", "96.00", "79.00", "25.60", "25.00", "6.40", "4.00", "640.00", "620.00",task2Rows), - createData("Stage 3 - Tender Analysis & Report & Contract Documentation","7", "384.00", "300.00", "128.00", "130.00", "96.00", "79.00", "25.60", "25.00", "6.40", "4.00", "640.00", "538.00",task3Rows), - createData("Stage 4 - Construction", "13", "480.00", "400.00", "160.00", "160.00", "120.00", "128.00", "32.00", "25.00", "8.00", "3.00", "800.00", "716.00",task4Rows), - createData("Stage 5 - Miscellaneous", "4", "96.00", "-", "32.00", "-", "24.00", "-0", "6.40", "-", "1.600", "-", "160.00", "-",task5Rows), - createData("","Total", "1920.00", "1658.00", "640.00", "600.00", "480.00", "426.00", "128.00", "113.00", "32.00", "20.75", "3,200.00", "2817.75",task6Rows), + const data:any = [ + "Stage 1 - Design & Cost Planning / Estimating", + "5", + "576.00", + "576.00", + "192.00", + "180.00", + "144.00", + "140.00", + "38.40", + "38.00", + "9.60", + "9.75", + "960.00", + "943.75", ]; + // const rows = [ + // createData(data,task1Rows), + // // createData("Stage 2 - Tender Documentation","11", "384.00", "382.00", "128.00", "130.00", "96.00", "79.00", "25.60", "25.00", "6.40", "4.00", "640.00", "620.00",task2Rows), + // // createData("Stage 3 - Tender Analysis & Report & Contract Documentation","7", "384.00", "300.00", "128.00", "130.00", "96.00", "79.00", "25.60", "25.00", "6.40", "4.00", "640.00", "538.00",task3Rows), + // // createData("Stage 4 - Construction", "13", "480.00", "400.00", "160.00", "160.00", "120.00", "128.00", "32.00", "25.00", "8.00", "3.00", "800.00", "716.00",task4Rows), + // // createData("Stage 5 - Miscellaneous", "4", "96.00", "-", "32.00", "-", "24.00", "-0", "6.40", "-", "1.600", "-", "160.00", "-",task5Rows), + // // createData("","Total", "1920.00", "1658.00", "640.00", "600.00", "480.00", "426.00", "128.00", "113.00", "32.00", "20.75", "3,200.00", "2817.75",task6Rows), + // ]; // const taskRows = [ // createTaskData("1.1 Preparation of preliminary...","-","-","172.00","-","54.00","-","42.00","-","12.00","-","3.00","-","283.00"), @@ -139,144 +240,224 @@ const ProjectResourceSummary: React.FC = () => { {row.stage} {row.taskCount} - {row.g1Planned} - {row.g1Actual} - {row.g2Planned} - {row.g2Actual} - {row.g3Planned} - {row.g3Actual} - {row.g4Planned} - {row.g4Actual} - {row.g5Planned} - {row.g5Actual} - {row.totalPlanned} - {row.totalActual} + {row.g1Planned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + {row.g1Actual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + {row.g2Planned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + {row.g2Actual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + {row.g3Planned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + {row.g3Actual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + {row.g4Planned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + {row.g4Actual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + {row.g5Planned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + {row.g5Actual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + {row.totalPlanned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + {row.totalActual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} {row.task.map((taskRow:any) => ( - <> - + - + + + + + + + +
+
- {taskRow.stage} + + + + + {taskRow[0]} + + +
+
- {taskRow.taskCount} + + + + + {taskRow[1]} + + +
+
- {taskRow.g1Planned} + + + + + {taskRow[4].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + + +
+
- {taskRow.g1Actual} + + + + + {taskRow[5].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + + +
+
- {taskRow.g2Planned} + + + + + {taskRow[6].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + + +
+
- {taskRow.g2Actual} + + + + + {taskRow[7].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + + +
+
- {taskRow.g3Planned} + + + + + {taskRow[8].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + + +
+
- {taskRow.g3Actual} + + + + + {taskRow[9].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + + +
+
- {taskRow.g4Planned} + + + + + {taskRow[10].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + + +
+
- {taskRow.g4Actual} + + + + + {taskRow[11].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + + +
+
- {taskRow.g5Planned} + + + + + {taskRow[12].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + + +
+
- {taskRow.g5Actual} + + + + + {taskRow[13].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + + +
+
- {taskRow.totalPlanned} + + + + + {taskRow[2].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + + +
+
- {taskRow.totalActual} + + + + + {taskRow[3].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + + +
+
+
- ))} - {/* - - - - {row.task.map((taskRow:any) => ( - - - {taskRow.stage} - {taskRow.taskCount} - {taskRow.g1Planned} - {taskRow.g1Actual} - {taskRow.g2Planned} - {taskRow.g2Actual} - {taskRow.g3Planned} - {taskRow.g3Actual} - {taskRow.g4Planned} - {taskRow.g4Actual} - {taskRow.g5Planned} - {taskRow.g5Actual} - {taskRow.totalPlanned} - {taskRow.totalActual} - - ))} - - - - */} + ); } - useEffect(() => { - setProjectName("C-1001-001 - Consultancy Project A") - const fee = 2000000 - setProjectFee(fee.toLocaleString()) - setStatus("Within Budget / Overconsumption") - const plannedResourcesInt = 3200 - setPlannedResources(plannedResourcesInt.toLocaleString()) - const actualResourcesSpentInt = 2817.75 - setActualResourcesSpent(actualResourcesSpentInt.toLocaleString()) - const remainingResourcesInt = 382.25 - setRemainingResources(remainingResourcesInt.toLocaleString()) - }, []) - const projectResourcesRows = [ {id: 1,stage:"Stage 1 - Design & Cost Planning / Estimating",taskCount:"5",g1Planned:"576.00",g1Actual:"576.00",g2Planned:"192.00", g2Actual:"180.00", g3Planned:"144.00", g3Actual:"140.00", g4Planned: "38.40", g4Actual:"38S.00", g5Planned:"9.60", g5Actual:"9.75", totalPlanned:"960.00", totalActual:"943.75"}, {id: 2,stage:"1.1 Preparation of preliminary...",taskCount:"-",g1Planned:"-",g1Actual:"172.00",g2Planned:"-", g2Actual:"54.00", g3Planned:"-", g3Actual:"42.00", g4Planned: "-", g4Actual:"12.00", g5Planned:"-", g5Actual:"3.00", totalPlanned:"-", totalActual:"283.00"}, @@ -440,7 +621,7 @@ const columns2 = [
- HKD {projectFee} + HKD ${projectFee.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
@@ -460,7 +641,7 @@ const columns2 = [
- {plannedResources} Manhours + {plannedResources.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} Manhours
@@ -470,7 +651,7 @@ const columns2 = [
- {actualResourcesSpent} Manhours + {actualResourcesSpent.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} Manhours
@@ -480,14 +661,14 @@ const columns2 = [
- {remainingResources} Manhours + {remainingResources.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} Manhours
{/*
*/} -
+ {/*
*/} @@ -533,16 +714,18 @@ const columns2 = [ - {rows.map((row) => ( + {rows.map((row:any) => ( ))}
-
+ {/*
*/} ); }; export default ProjectResourceSummary; + + diff --git a/src/components/ProjectResourceSummarySearch/ProjectResourceSummarySearch.tsx b/src/components/ProjectResourceSummarySearch/ProjectResourceSummarySearch.tsx index d8efb61..cd356d3 100644 --- a/src/components/ProjectResourceSummarySearch/ProjectResourceSummarySearch.tsx +++ b/src/components/ProjectResourceSummarySearch/ProjectResourceSummarySearch.tsx @@ -20,6 +20,7 @@ type SearchParamNames = keyof SearchQuery; const ProjectResourceSummarySearch: React.FC = ({ projects }) => { const { t } = useTranslation("projects"); + const router = useRouter(); const searchParams = useSearchParams() // If project searching is done on the server-side, then no need for this. const [filteredProjects, setFilteredProjects] = useState(projects); @@ -28,14 +29,21 @@ const ProjectResourceSummarySearch: React.FC = ({ projects }) => { () => [ { label: "Project Code", paramName: "projectCode", type: "text" }, { label: "Project Name", paramName: "projectName", type: "text" }, - { label: "Client Code", paramName: "clientCode", type: "text" }, - { label: "Client Name", paramName: "clientName", type: "text" }, + { label: "Client Code", paramName: "customerCode", type: "text" }, + { label: "Client Name", paramName: "customerName", type: "text" }, ], [t], ); const onTaskClick = useCallback((resourceSummaryResult: ResourceSummaryResult) => { - console.log(resourceSummaryResult) + try { + console.log(resourceSummaryResult) + router.push( + `/dashboard/ProjectResourceSummary?projectId=${resourceSummaryResult.id}` + ); + } catch (error) { + console.error('Error fetching team projects:', error); + } }, []); @@ -49,7 +57,7 @@ const ProjectResourceSummarySearch: React.FC = ({ projects }) => { }, { name: "projectCode", label: t("Project Code") }, { name: "projectName", label: t("Project Name") }, - { name: "clientCodeAndName", label: t("Client Code And Name") }, + { name: "customerCodeAndName", label: t("Client Code And Name") }, ], [onTaskClick, t], // [t], @@ -60,7 +68,15 @@ const ProjectResourceSummarySearch: React.FC = ({ projects }) => { { - console.log(query); + setFilteredProjects( + projects.filter( + (cp) => + cp.projectCode.toLowerCase().includes(query.projectCode.toLowerCase()) && + cp.projectName.toLowerCase().includes(query.projectName.toLowerCase()) && + cp.customerCodeAndName.toLowerCase().includes(query.customerCode.toLowerCase()) && + cp.customerCodeAndName.toLowerCase().includes(query.customerName.toLowerCase()) + ), + ); }} />