diff --git a/src/app/(main)/projects/page.tsx b/src/app/(main)/projects/page.tsx index 0980a19..3c39531 100644 --- a/src/app/(main)/projects/page.tsx +++ b/src/app/(main)/projects/page.tsx @@ -1,7 +1,7 @@ import { fetchProjectCategories, fetchProjects, preloadProjects } from "@/app/api/projects"; import { fetchUserAbilities } from "@/app/utils/fetchUtil"; import ProjectSearch from "@/components/ProjectSearch"; -import { getServerI18n } from "@/i18n"; +import { getServerI18n, I18nProvider } from "@/i18n"; import { MAINTAIN_PROJECT, VIEW_PROJECT } from "@/middleware"; import Add from "@mui/icons-material/Add"; import Button from "@mui/material/Button"; @@ -28,6 +28,7 @@ const Projects: React.FC = async () => { return ( <> + { rowGap={2} > - {t("Projects")} + {t("Project Management")} {abilities.includes(MAINTAIN_PROJECT) && { }> + ); }; diff --git a/src/app/api/clientprojects/actions.ts b/src/app/api/clientprojects/actions.ts index 3b7af13..b3980df 100644 --- a/src/app/api/clientprojects/actions.ts +++ b/src/app/api/clientprojects/actions.ts @@ -23,6 +23,10 @@ export interface ClientSubsidiaryProjectResult { export const fetchAllClientSubsidiaryProjects = cache(async (customerId: number, tableSorting:string, subsidiaryId?: number) => { if (subsidiaryId === 0){ + return serverFetchJson( + `${BASE_API_URL}/dashboard/searchCustomerSubsidiaryProject?customerId=${customerId}&subsidiaryId=${subsidiaryId}&tableSorting=${tableSorting}` + ); + } else if (subsidiaryId === undefined) { return serverFetchJson( `${BASE_API_URL}/dashboard/searchCustomerSubsidiaryProject?customerId=${customerId}&tableSorting=${tableSorting}` ); diff --git a/src/components/AppBar/Profile.tsx b/src/components/AppBar/Profile.tsx index c14d318..508f640 100644 --- a/src/components/AppBar/Profile.tsx +++ b/src/components/AppBar/Profile.tsx @@ -66,8 +66,8 @@ const Profile: React.FC = ({ avatarImageSrc, profileName }) => { { router.replace("/changepassword") }}>{t("Change Password")} - {/* {language === "zh" && { onLangClick("en") }}>{t("Change To English Version")}} - {language === "en" && { onLangClick("zh") }}>{t("Change To Chinese Version")}} */} + {language === "zh" && { onLangClick("en") }}>{t("Change To English Version")}} + {language === "en" && { onLangClick("zh") }}>{t("Change To Chinese Version")}} signOut()}>{t("Sign out")} diff --git a/src/components/Breadcrumb/Breadcrumb.tsx b/src/components/Breadcrumb/Breadcrumb.tsx index 2ad36bc..b6e608d 100644 --- a/src/components/Breadcrumb/Breadcrumb.tsx +++ b/src/components/Breadcrumb/Breadcrumb.tsx @@ -23,7 +23,7 @@ const pathToLabelMap: { [path: string]: string } = { "/dashboard/ProjectStatusByTeam": "Project Status by Team", "/dashboard/ProjectResourceConsumptionRanking": "Project Resource Consumption Ranking", "/dashboard/StaffUtilization": "Staff Utilization", - "/projects": "Projects", + "/projects": "Project Management", "/projects/create": "Create Project", "/projects/createSub": "Sub Project", "/projects/edit": "Edit Project", diff --git a/src/components/CustomDatagrid/CustomDatagrid.tsx b/src/components/CustomDatagrid/CustomDatagrid.tsx index 344c3aa..5b30ebb 100644 --- a/src/components/CustomDatagrid/CustomDatagrid.tsx +++ b/src/components/CustomDatagrid/CustomDatagrid.tsx @@ -19,7 +19,8 @@ interface CustomDatagridProps { onRowSelectionModelChange?: ( newSelectionModel: GridRowSelectionModel, ) => void; - selectionModel?: any; + selectionModel?: GridRowSelectionModel; + rowSelectionModel?: GridRowSelectionModel; columnGroupingModel?: any; pageSize?:any; } @@ -33,6 +34,7 @@ const CustomDatagrid: React.FC = ({ sx, dataGridHeight, checkboxSelection, // Destructure the new prop + rowSelectionModel, onRowSelectionModelChange, // Destructure the new prop selectionModel, onRowClick, @@ -200,6 +202,7 @@ const CustomDatagrid: React.FC = ({ getRowHeight={() => 'auto'} onRowClick={onRowClick} checkboxSelection={checkboxSelection} + rowSelectionModel={rowSelectionModel} onRowSelectionModelChange={onRowSelectionModelChange} experimentalFeatures={{ columnGrouping: true }} columnGroupingModel={columnGroupingModel} @@ -233,6 +236,7 @@ const CustomDatagrid: React.FC = ({ getRowHeight={() => 'auto'} onRowClick={onRowClick} checkboxSelection={checkboxSelection} + rowSelectionModel={rowSelectionModel} onRowSelectionModelChange={onRowSelectionModelChange} experimentalFeatures={{ columnGrouping: true }} columnGroupingModel={columnGroupingModel} @@ -266,6 +270,7 @@ const CustomDatagrid: React.FC = ({ getRowHeight={() => 'auto'} onRowClick={onRowClick} checkboxSelection={checkboxSelection} + rowSelectionModel={rowSelectionModel} onRowSelectionModelChange={onRowSelectionModelChange} experimentalFeatures={{ columnGrouping: true }} columnGroupingModel={columnGroupingModel} @@ -301,6 +306,7 @@ const CustomDatagrid: React.FC = ({ onRowClick={onRowClick} style={{ marginRight: 0 }} checkboxSelection={checkboxSelection} + rowSelectionModel={rowSelectionModel} onRowSelectionModelChange={onRowSelectionModelChange} experimentalFeatures={{ columnGrouping: true }} columnGroupingModel={columnGroupingModel} diff --git a/src/components/NavigationContent/NavigationContent.tsx b/src/components/NavigationContent/NavigationContent.tsx index 84709f8..72df45d 100644 --- a/src/components/NavigationContent/NavigationContent.tsx +++ b/src/components/NavigationContent/NavigationContent.tsx @@ -117,11 +117,11 @@ const NavigationContent: React.FC = ({ abilities, username }) => { abilities!.includes(ability), ), children: [ - { - icon: , - label: "Financial Summary", - path: "/dashboard/ProjectFinancialSummary", - }, + // { + // icon: , + // label: "Financial Summary", + // path: "/dashboard/ProjectFinancialSummary", + // }, { icon: , label: "Company / Team Cash Flow", @@ -162,6 +162,12 @@ const NavigationContent: React.FC = ({ abilities, username }) => { }, ], }, + { + icon: , + label: "Financial Summary", + path: "/dashboard/ProjectFinancialSummary", + showOnMobile: true, + }, // No Claim function in Breaur, will be implement later // { // icon: , diff --git a/src/components/ProgressByClient/ProgressByClient.tsx b/src/components/ProgressByClient/ProgressByClient.tsx index e7c429b..3cffb0d 100644 --- a/src/components/ProgressByClient/ProgressByClient.tsx +++ b/src/components/ProgressByClient/ProgressByClient.tsx @@ -25,6 +25,9 @@ import { useSearchParams } from 'next/navigation'; import { fetchAllClientSubsidiaryProjects} from "@/app/api/clientprojects/actions"; // const ReactApexChart = dynamic(() => import('react-apexcharts'), { ssr: false }); +type SearchProjectQuery = Partial>; +type SearchProjectParamNames = keyof SearchProjectQuery; + interface Props { // clientSubsidiaryProjectResult: ClientSubsidiaryProjectResult[]; } @@ -51,6 +54,7 @@ const ProgressByClient: React.FC = () => { React.useState("-"); const [actualManhourSpent, setActualManhourSpent]: any = React.useState("-"); const [remainedManhour, setRemainedManhour]: any = React.useState("-"); + const [filteredClientSubsidiaryProjectResult, setFilteredClientSubsidiaryProjectResult]:any[] = useState([]); const [lastUpdate, setLastUpdate]: any = React.useState("-"); const [dropdownDemo, setDropdownDemo] = useState(""); const [dateDemo, setDateDemo] = useState(null); @@ -100,6 +104,7 @@ const ProgressByClient: React.FC = () => { Number(customerId),tableSorting,Number(0)) console.log(clickResult) setClientSubsidiaryProjectResult(clickResult); + setFilteredClientSubsidiaryProjectResult(clickResult); } else { const clickResult = await fetchAllClientSubsidiaryProjects( Number(customerId), @@ -110,6 +115,14 @@ const ProgressByClient: React.FC = () => { } + } catch (error) { + console.error('Error fetching client subsidiary projects:', error); + } + } else if (customerId) { + try { + const clickResult = await fetchAllClientSubsidiaryProjects( + Number(customerId),tableSorting) + setClientSubsidiaryProjectResult(clickResult); } catch (error) { console.error('Error fetching client subsidiary projects:', error); } @@ -141,7 +154,13 @@ const ProgressByClient: React.FC = () => { fetchData() }, [customerId,subsidiaryId,tableSorting]); - + const projectSearchCriteria: Criterion[] = useMemo( + () => [ + { label: t("Project Code"), paramName: "projectCode", type: "text" }, + { label: t("Project Name"), paramName: "projectName", type: "text" }, + ], + [t], + ); const rows2 = [ { @@ -626,11 +645,25 @@ const ProgressByClient: React.FC = () => { className="text-slate-500" title= {t("Resource Consumption and Coming Milestones")} /> + {clientSubsidiaryProjectResult.length > 0 && ( + { + setFilteredClientSubsidiaryProjectResult( + clientSubsidiaryProjectResult.filter( + (cp:any) => + cp.projectCode.toLowerCase().includes(query.projectCode.toLowerCase()) && + cp.projectName.toLowerCase().includes(query.projectName.toLowerCase()) + ), + ); + }} + /> + )}
import('react-apexcharts'), { ssr: false }); +type SearchProjectQuery = Partial>; +type SearchProjectParamNames = keyof SearchProjectQuery; + const ProgressByTeam: React.FC = () => { const searchParams = useSearchParams(); const teamLeadId = searchParams.get('teamLeadId'); @@ -51,6 +54,7 @@ const ProgressByTeam: React.FC = () => { const [chartProjectName, setChartProjectName]:any[] = useState([]); const [chartProjectDisplayName, setChartProjectDisplayName]:any[] = useState([]); const [chartProjectBudgetedHour, setChartProjectBudgetedHour]:any[] = useState([]); + const [filteredTeamProjectResult, setFilteredTeamProjectResult]:any[] = useState([]); const [chartProjectSpentHour, setChartProjectSpentHour]:any[] = useState([]); const [chartManhourConsumptionPercentage, setChartManhourConsumptionPercentage]:any[] = useState([]); const color = ["#f57f90", "#94f7d6", "#87c5f5", "#ab95f5", "#fcd68b", @@ -89,6 +93,14 @@ const ProgressByTeam: React.FC = () => { const recordsPerPage = 10; const [tableSorting, setTableSorting] = useState('ProjectName'); + const projectSearchCriteria: Criterion[] = useMemo( + () => [ + { label: t("Project Code"), paramName: "projectCode", type: "text" }, + { label: t("Project Name"), paramName: "projectName", type: "text" }, + ], + [t], + ); + const fetchData = async () => { console.log(tableSorting) if (teamLeadId) { @@ -97,6 +109,7 @@ const ProgressByTeam: React.FC = () => { Number(teamLeadId),tableSorting) console.log(clickResult) setTeamProjectResult(clickResult); + setFilteredTeamProjectResult(clickResult); } catch (error) { console.error('Error fetching team projects:', error); } @@ -742,11 +755,25 @@ const ProgressByTeam: React.FC = () => { className="text-slate-500" title= {t("Resource Consumption and Coming Milestones")} /> + {teamProjectResult.length > 0 && ( + { + setFilteredTeamProjectResult( + teamProjectResult.filter( + (cp:any) => + cp.projectCode.toLowerCase().includes(query.projectCode.toLowerCase()) && + cp.projectName.toLowerCase().includes(query.projectName.toLowerCase()) + ), + ); + }} + /> + )}
{ const { t } = useTranslation("dashboard"); + const searchParams = useSearchParams(); + const projectId = searchParams.get('projectId'); const todayDate = new Date(); - const [selectionModel, setSelectionModel]: any[] = React.useState([]); + const [selectionModel, setSelectionModel]: any[] = useState([]); const [projectData, setProjectData]: any[] = React.useState([]); const [filteredResult, setFilteredResult]:any[] = useState([]); const [selectedProjectIdList, setSelectedProjectIdList]: any[] = React.useState([]); @@ -59,6 +62,7 @@ const ProjectCashFlow: React.FC = () => { const [monthlyAnticipateIncomeList, setMonthlyAnticipateIncomeList]: any[] = React.useState([0,0,0,0,0,0,0,0,0,0,0,0]); const [monthlyAnticipateExpenditureList, setMonthlyAnticipateExpenditureList]: any[] = React.useState([0,0,0,0,0,0,0,0,0,0,0,0]); const [ledgerData, setLedgerData]: any[] = React.useState([]); + const [isInitializing, setIsInitializing] = useState(true); const [cashFlowYear, setCashFlowYear]: any[] = React.useState( todayDate.getFullYear(), ); @@ -67,25 +71,24 @@ const ProjectCashFlow: React.FC = () => { ); const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => { - const selectedRowsData = projectData.filter((row: any) => - newSelectionModel.includes(row.id), - ); - const projectIdList = [] - for (var i=0; i + newSelectionModel.includes(row.id) + ); + const projectIdList = selectedRowsData.map((row: any) => row.id); + setSelectedProjectIdList(projectIdList); } - setSelectedProjectIdList(projectIdList) }; const fetchData = async () => { const cashFlowProject = await fetchProjectsCashFlow(); - console.log(cashFlowProject) setProjectData(cashFlowProject) setFilteredResult(cashFlowProject) } const fetchChartData = async () => { const cashFlowMonthlyChartData = await fetchProjectsCashFlowMonthlyChart(selectedProjectIdList,cashFlowYear); - console.log(cashFlowMonthlyChartData) const monthlyIncome = [] const cumulativeIncome = [] const monthlyExpenditure = [] @@ -165,8 +168,7 @@ const ProjectCashFlow: React.FC = () => { setMonthlyAnticipateIncomeList([0,0,0,0,0,0,0,0,0,0,0,0]) setMonthlyAnticipateExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0]) } - - console.log(cashFlowAnticipateData) + if(cashFlowAnticipateData.length !== 0){ if (cashFlowAnticipateData[0].anticipateExpenditureList.length !== 0) { const anticipateExpenditureList = [] @@ -186,7 +188,6 @@ const ProjectCashFlow: React.FC = () => { } anticipateExpenditureList.push(subAnticipateExpenditure) } - console.log(anticipateExpenditureList) const result = new Array(anticipateExpenditureList[0].length).fill(0); for (const arr of anticipateExpenditureList) { for (let i = 0; i < arr.length; i++) { @@ -211,18 +212,28 @@ const ProjectCashFlow: React.FC = () => { setLedgerData(cashFlowLedgerData) } useEffect(() => { - fetchData() + if (projectId !== null) { + setSelectedProjectIdList([parseInt(projectId)]) + setSelectionModel([parseInt(projectId)]); + } + fetchData().then(() => { + setIsInitializing(false); + }); }, []); useEffect(() => { - fetchChartData() - fetchReceivableAndExpenditureData() - fetchAnticipateData() - fetchProjectCashFlowLedger() + fetchChartData(); + fetchReceivableAndExpenditureData(); + fetchAnticipateData(); + fetchProjectCashFlowLedger(); + }, [cashFlowYear,selectedProjectIdList]); + useEffect(() => { - fetchAnticipateData() + fetchAnticipateData(); + }, [anticipateCashFlowYear,selectedProjectIdList]); + const columns = [ { id: "projectCode", @@ -751,15 +762,12 @@ const ProjectCashFlow: React.FC = () => { ); function isDateInRange(dateToCheck: string, startDate: string, endDate: string): boolean { - console.log(startDate) - console.log(endDate) if (!startDate || !endDate) { return false; } const dateToCheckObj = new Date(dateToCheck); const startDateObj = new Date(startDate); const endDateObj = new Date(endDate); - console.log(dateToCheckObj) return dateToCheckObj >= startDateObj && dateToCheckObj <= endDateObj; } @@ -774,7 +782,6 @@ const ProjectCashFlow: React.FC = () => { { - console.log(query) setFilteredResult( projectData.filter( (cp:any) => @@ -794,7 +801,7 @@ const ProjectCashFlow: React.FC = () => { dataGridHeight={300} checkboxSelection={true} onRowSelectionModelChange={handleSelectionChange} - selectionModel={selectionModel} + rowSelectionModel={selectionModel} />
diff --git a/src/components/ProjectFinancialSummary/ProjectFinancialCard.tsx b/src/components/ProjectFinancialSummary/ProjectFinancialCard.tsx index 4ecc36f..205f656 100644 --- a/src/components/ProjectFinancialSummary/ProjectFinancialCard.tsx +++ b/src/components/ProjectFinancialSummary/ProjectFinancialCard.tsx @@ -17,9 +17,13 @@ import { AnyARecord, AnyCnameRecord } from "dns"; import SearchBox, { Criterion } from "../SearchBox"; import ProgressByClientSearch from "@/components/ProgressByClientSearch"; import { Suspense } from "react"; +import RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked'; +import RadioButtonCheckedIcon from '@mui/icons-material/RadioButtonChecked'; +import { useRouter } from "next/navigation"; interface Props { Title: string; + TeamId: number; TotalActiveProjectNumber: number; TotalFees: number; TotalBudget: number; @@ -41,6 +45,7 @@ const dataPositiveStyle:any = { color: "#71d19e", textAlign: "right" } const ProjectFinancialCard: React.FC = ({ Title, + TeamId, TotalActiveProjectNumber, TotalFees, TotalBudget, @@ -55,8 +60,12 @@ const ProjectFinancialCard: React.FC = ({ ProjectedCashFlowStatus, Index, }) => { + const router = useRouter(); const [SearchCriteria, setSearchCriteria] = React.useState({}); const { t } = useTranslation("dashboard"); + const handleCheckProjectStatusClick = (TeamId:number) => { + router.push(`/dashboard/ProjectStatusByTeam?teamLeadId=${TeamId}`); + }; const borderColor = CashFlowStatus === "Negative" ? "border-red-300 border-solid" @@ -75,116 +84,233 @@ const ProjectFinancialCard: React.FC = ({ className={`${borderColor}`} >
- {Title} + {Title !== t("All Team") ? +
{Title}
+ : +
{Title}
+ } + + {ClickedIndex === Index ? +
+ : +
+ } +
-
-
- {t("Total Active Project")} + + {Title !== t("All Team") ? + <> +
handleCheckProjectStatusClick(TeamId)} className="bg-lime-100 mb-2 border-b-2 pt-1 pb-1 hover:bg-lime-200" style={{ width: "100%", textAlign: "center", color: "#898d8d" }}>{t("Check Project Status")}
+ + : + <>
+ } +
+ {"(a) " + t("Total Active Project")}
{TotalActiveProjectNumber.toLocaleString()}

-
- {t("Total Fees")} +
+ {"(b) " + t("Total Fees")}
{TotalFees.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}

-
- {t("Total Budget")} +
+ {"(c) " + t("Total Budget")}
{TotalBudget.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
+
+
+
{"(c) = (b) * 80%"}
+
+

-
- {t("Total Cumulative Expenditure")} +
+ {"(d) " + t("Total Cumulative Expenditure")}
{TotalCumulative.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}

-
- {t("Total Invoiced Amount")} +
+ {"(e) " + t("Total Invoiced Amount")}
{TotalInvoicedAmount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}

-
- {t("Total Un-Invoiced Amount")} +
+ {"(f) " + t("Total Un-Invoiced Amount")}
{TotalUnInvoicedAmount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
+
+
+
{"(f) =(b) - (e)"}
+
+

-
- {t("Total Received Amount")} +
+ {"(g) " + t("Total Received Amount")}
{TotalReceivedAmount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}

-
- {t("Cash Flow Status")} +
+ {"(h) " + t("Cash Flow Status")}
+ {CashFlowStatus === "Negative" && ( + <> +
+ {t(CashFlowStatus)} +
+
+
+
{"Positive: (e) > or = (d)"}
+
{"Negative: (e) < (d)"}
+
+
+
+ + )} + {CashFlowStatus === "Positive" && ( <>
{t(CashFlowStatus)}
+
+
+
{"Positive: (e) > or = (d)"}
+
{"Negative: (e) < (d)"}
+
+

+ )}
- {t("Cost Performance Index") + " (CPI)"} + {"(i) " + t("Cost Performance Index") + " (CPI)"}
{ ( <>
+ {CostPerformanceIndex} +
+
+
+
{"(i) = (e) / (d)"}
+
+
+
+ + )} + {Number(CostPerformanceIndex) >= 1 && ( + <> +
{CostPerformanceIndex}
+
+
+
{"(i) =(e) / (d)"}
+
+

)} -
- {t("Projected Cash Flow Status")} +
+ {"(j) " + t("Projected Cash Flow Status")}
- <> -
- {t(ProjectedCashFlowStatus)} -
-
- + {ProjectedCashFlowStatus === "Negative" && ( + <> +
+ {t(ProjectedCashFlowStatus)} +
+
+
+
{"Positive: (b) > or = (d)"}
+
{"Negative: (b) < (d)"}
+
+
+
+ + )} + {ProjectedCashFlowStatus === "Positive" && ( + <> +
+ {t(ProjectedCashFlowStatus)} +
+
+
+
{"Positive: (b) > or = (d)"}
+
{"Negative: (b) < (d)"}
+
+
+
+ + )}
- {t("Projected Cost Performance Index") + " (CPI)"} + {"(k) " + t("Projected Cost Performance Index") + " (CPI)"}
+ {Number(ProjectedCPI) < 1 && ( <> -
- {ProjectedCPI} -
+
+ {ProjectedCPI} +
+
+
{"Positive: (b) / (d)"}
+
+ )} + {Number(ProjectedCPI) >= 1 && ( + <> +
+ {ProjectedCPI} +
+
+
{"Positive: (b) / (d)"}
+
+ + )} ); }; diff --git a/src/components/ProjectFinancialSummary/ProjectFinancialSummary.tsx b/src/components/ProjectFinancialSummary/ProjectFinancialSummary.tsx index 88fcdc7..080f2d1 100644 --- a/src/components/ProjectFinancialSummary/ProjectFinancialSummary.tsx +++ b/src/components/ProjectFinancialSummary/ProjectFinancialSummary.tsx @@ -24,6 +24,7 @@ import ProjectFinancialCard from "./ProjectFinancialCard"; import VisibilityIcon from '@mui/icons-material/Visibility'; import { downloadFile } from "@/app/utils/commonUtil"; import Typography from "@mui/material/Typography"; +import { useRouter } from "next/navigation"; type SearchProjectQuery = Partial>; type SearchClientQuery = Partial>; @@ -31,6 +32,7 @@ type SearchProjectParamNames = keyof SearchProjectQuery; type SearchClientParamNames = keyof SearchClientQuery; const ProjectFinancialSummary: React.FC = () => { + const router = useRouter(); const [SearchCriteria, setSearchCriteria] = React.useState({}); const { t } = useTranslation("dashboard"); const [selectionModel, setSelectionModel]: any[] = React.useState([]); @@ -111,7 +113,17 @@ const ProjectFinancialSummary: React.FC = () => { id: 'customerCode', field: 'customerCode', headerName: t("Client Code"), - minWidth:50 + minWidth:50, + renderCell: (params: any) => ( +
{ + router.push(`/dashboard/ProjectStatusByClient?customerId=${params.row.id}`); + }} + > + {params.value} +
+ ), }, { id: 'customerName', @@ -323,6 +335,16 @@ const columns2 = [ field: 'projectCode', headerName: t("Project Code"), minWidth:50, + renderCell: (params: any) => ( +
{ + router.push(`/dashboard/ProjectCashFlow?projectId=${params.row.id}`); + }} + > + {params.value} +
+ ), }, { id: 'projectName', @@ -524,7 +546,7 @@ const columns2 = [
{projectFinancialData.map((record:any, index:any) => (
handleCardClick(record,index)}> - +
))}
diff --git a/src/components/ProjectResourceConsumptionRanking/ProjectResourceConsumptionRanking.tsx b/src/components/ProjectResourceConsumptionRanking/ProjectResourceConsumptionRanking.tsx index 3b70fae..582fc17 100644 --- a/src/components/ProjectResourceConsumptionRanking/ProjectResourceConsumptionRanking.tsx +++ b/src/components/ProjectResourceConsumptionRanking/ProjectResourceConsumptionRanking.tsx @@ -19,10 +19,11 @@ import SearchBox, { Criterion } from "../SearchBox"; import ProgressByTeamSearch from "@/components/ProgressByTeamSearch"; import { Suspense } from "react"; import { useSearchParams } from 'next/navigation'; -import { fetchAllTeamProjects, TeamProjectResult, fetchTeamProjects, fetchAllTeamConsumption, fetchAllTeamConsumptionColorOrder} from "@/app/api/teamprojects/actions"; +import { fetchAllTeamProjects, TeamProjectResult, ClientSubsidiaryProjectResult, fetchTeamProjects, fetchAllTeamConsumption, fetchAllTeamConsumptionColorOrder} from "@/app/api/teamprojects/actions"; import Typography from "@mui/material/Typography"; // const ReactApexChart = dynamic(() => import('react-apexcharts'), { ssr: false }); - +type SearchProjectQuery = Partial>; +type SearchProjectParamNames = keyof SearchProjectQuery; interface Props { projects: TeamProjectResult[]; } @@ -44,6 +45,7 @@ const ProjectResourceConsumptionRanking: React.FC = () => { const [colorArray, setColorArray]: any[] = useState([]); const [selectionModel, setSelectionModel]: any[] = React.useState([]); const [pieChartColor, setPieChartColor]: any[] = React.useState([]); + const [filteredTeamProjectResult, setFilteredTeamProjectResult]:any[] = useState([]); const [totalSpentPercentage, setTotalSpentPercentage]: any = React.useState(); const [projectBudgetManhour, setProjectBudgetManhour]: any = React.useState("-"); @@ -117,6 +119,7 @@ const ProjectResourceConsumptionRanking: React.FC = () => { selectedTeamIdList,tableSorting) console.log(clickResult) setTeamProjectResult(clickResult); + setFilteredTeamProjectResult(clickResult); setTeamProjectColorOrder(colorOrder); } catch (error) { console.error('Error fetching team consumption:', error); @@ -124,6 +127,14 @@ const ProjectResourceConsumptionRanking: React.FC = () => { } } + const projectSearchCriteria: Criterion[] = useMemo( + () => [ + { label: t("Project Code"), paramName: "projectCode", type: "text" }, + { label: t("Project Name"), paramName: "projectName", type: "text" }, + ], + [t], + ); + const searchCriteria: Criterion[] = useMemo( () => [ { label: t("Team Code"), paramName: "teamCode", type: "text" }, @@ -865,11 +876,25 @@ const ProjectResourceConsumptionRanking: React.FC = () => { className="text-slate-500" title={t("Resource Consumption and Coming Milestones")} /> + {teamProjectResult.length > 0 && ( + { + setFilteredTeamProjectResult( + teamProjectResult.filter( + (cp:any) => + cp.projectCode.toLowerCase().includes(query.projectCode.toLowerCase()) && + cp.projectName.toLowerCase().includes(query.projectName.toLowerCase()) + ), + ); + }} + /> + )}
= ({ projects, projectCategories, abilities const searchCriteria: Criterion[] = useMemo( () => [ - { label: t("Project code"), paramName: "code", type: "text" }, - { label: t("Project name"), paramName: "name", type: "text" }, + { label: t("Project Code"), paramName: "code", type: "text" }, + { label: t("Project Name"), paramName: "name", type: "text" }, { - label: t("Client name"), + label: t("Client Name"), paramName: "client", type: "autocomplete", options: uniqBy(projects.map((project) => ({value: project.client, label: project.client})), "value").sort((a, b) => a.value >= b.value ? 1 : -1), }, { - label: t("Project category"), + label: t("Project Category"), paramName: "category", type: "select", options: projectCategories.map((category) => category.name), diff --git a/src/i18n/en/dashboard.json b/src/i18n/en/dashboard.json index 23d25ef..22feb43 100644 --- a/src/i18n/en/dashboard.json +++ b/src/i18n/en/dashboard.json @@ -155,5 +155,6 @@ "Stage": "Stage", "Task Count": "Task Count", "Total": "Total", - "Status": "Status" + "Status": "Status", + "Check Project Status": "Check Project Status" } \ No newline at end of file diff --git a/src/i18n/en/projects.json b/src/i18n/en/projects.json index 9e26dfe..266a863 100644 --- a/src/i18n/en/projects.json +++ b/src/i18n/en/projects.json @@ -1 +1,16 @@ -{} \ No newline at end of file +{ + "Project Management": "Project Management", + "Create Sub Project": "Create Sub Project", + "Create Project": "Create Project", + "Project Code": "Project Code", + "Project Name": "Project Name", + "Client Name": "Client Name", + "Client": "Client", + "Project Category": "Project Category", + "Team": "Team", + "Status": "Status", + "Details": "Details", + "Awarded Project": "Awarded Project", + "Project to be bidded": "Project to be bidded", + "On-going": "On-going" +} \ No newline at end of file diff --git a/src/i18n/zh/dashboard.json b/src/i18n/zh/dashboard.json index 406f84d..be87cd2 100644 --- a/src/i18n/zh/dashboard.json +++ b/src/i18n/zh/dashboard.json @@ -156,5 +156,6 @@ "Stage": "階段", "Task Count": "工作數量", "Total": "總計", - "Status": "狀態" + "Status": "狀態", + "Check Project Status": "查看項目狀態" } \ No newline at end of file diff --git a/src/i18n/zh/projects.json b/src/i18n/zh/projects.json index 9e26dfe..93550f4 100644 --- a/src/i18n/zh/projects.json +++ b/src/i18n/zh/projects.json @@ -1 +1,16 @@ -{} \ No newline at end of file +{ + "Project Management": "項目管理", + "Create Sub Project": "創建子項目", + "Create Project": "創建項目", + "Project Code": "項目代碼", + "Project Name": "項目名稱", + "Client Name": "客戶名稱", + "Client": "客戶", + "Project Category": "項目類別", + "Team": "團隊", + "Status": "狀態", + "Details": "詳細信息", + "Awarded Project": "已獲得的項目", + "Project to be bidded": "待投標項目", + "On-going": "進行中" +} \ No newline at end of file