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/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/ProjectCashFlow/ProjectCashFlow.tsx b/src/components/ProjectCashFlow/ProjectCashFlow.tsx index cb3b3b1..4a0e216 100644 --- a/src/components/ProjectCashFlow/ProjectCashFlow.tsx +++ b/src/components/ProjectCashFlow/ProjectCashFlow.tsx @@ -25,6 +25,7 @@ import { CashFlow } from "@/app/api/cashflow"; import dayjs from 'dayjs'; import ProjectTotalFee from "../CreateInvoice_forGen/ProjectTotalFee"; import Typography from "@mui/material/Typography"; +import { useSearchParams } from 'next/navigation'; interface Props { projects: CashFlow[]; @@ -34,8 +35,10 @@ type SearchParamNames = keyof SearchQuery; const ProjectCashFlow: React.FC = () => { 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,14 +71,18 @@ 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); + console.log(selectedRowsData) + setSelectedProjectIdList(projectIdList); } - setSelectedProjectIdList(projectIdList) }; const fetchData = async () => { @@ -85,7 +93,6 @@ const ProjectCashFlow: React.FC = () => { } const fetchChartData = async () => { const cashFlowMonthlyChartData = await fetchProjectsCashFlowMonthlyChart(selectedProjectIdList,cashFlowYear); - console.log(cashFlowMonthlyChartData) const monthlyIncome = [] const cumulativeIncome = [] const monthlyExpenditure = [] @@ -122,6 +129,7 @@ const ProjectCashFlow: React.FC = () => { } const fetchReceivableAndExpenditureData = async () => { + console.log("s2") if (selectedProjectIdList.length === 0) { setReceivedPercentage(0) setInvoicedPercentage(0) @@ -165,8 +173,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 +193,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 +217,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", @@ -791,7 +807,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 c6e007f..3d4a7ea 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; @@ -37,6 +41,7 @@ interface Props { const ProjectFinancialCard: React.FC = ({ Title, + TeamId, TotalActiveProjectNumber, TotalFees, TotalBudget, @@ -51,8 +56,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" @@ -71,72 +80,100 @@ const ProjectFinancialCard: React.FC = ({ className={`${borderColor}`} >
- {Title} + {Title !== t("All Team") ? +
{Title}
+ : +
{Title}
+ } + + {ClickedIndex === Index ? +
+ : +
+ } +
-
+ + {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")}
+ + : + <>
+ }
- {t("Total Active Project")} + {"(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)} -
+
+ {t(CashFlowStatus)} +
+
+
{"Positive: (e) > or = (d)"}
+
{"Negative: (e) < (d)"}
+

)} @@ -148,6 +185,10 @@ const ProjectFinancialCard: React.FC = ({ > {t(CashFlowStatus)}
+
+
{"Positive: (e) > or = (d)"}
+
{"Negative: (e) < (d)"}
+

)} @@ -155,7 +196,7 @@ const ProjectFinancialCard: React.FC = ({ className="text-sm mt-2 font-medium ml-5" style={{ color: "#898d8d" }} > - {t("Cost Performance Index") + " (CPI)"} + {"(i) " + t("Cost Performance Index") + " (CPI)"} {Number(CostPerformanceIndex) < 1 && ( <> @@ -165,6 +206,9 @@ const ProjectFinancialCard: React.FC = ({ > {CostPerformanceIndex} +
+
{"(i) = (e) / (d)"}
+

)} @@ -176,11 +220,14 @@ const ProjectFinancialCard: React.FC = ({ > {CostPerformanceIndex} +
+
{"(i) =(e) / (d)"}
+

)}
- {t("Projected Cash Flow Status")} + {"(j) " + t("Projected Cash Flow Status")}
{ProjectedCashFlowStatus === "Negative" && ( <> @@ -190,6 +237,10 @@ const ProjectFinancialCard: React.FC = ({ > {t(ProjectedCashFlowStatus)} +
+
{"Positive: (b) > or = (d)"}
+
{"Negative: (b) < (d)"}
+

)} @@ -208,7 +259,7 @@ const ProjectFinancialCard: React.FC = ({ className="text-sm mt-2 font-medium ml-5" style={{ color: "#898d8d" }} > - {t("Projected Cost Performance Index") + " (CPI)"} + {"(k) " + t("Projected Cost Performance Index") + " (CPI)"} {Number(ProjectedCPI) < 1 && ( <> @@ -218,6 +269,9 @@ const ProjectFinancialCard: React.FC = ({ > {ProjectedCPI} +
+
{"Positive: (b) / (d)"}
+
)} {Number(ProjectedCPI) >= 1 && ( @@ -228,6 +282,9 @@ const ProjectFinancialCard: React.FC = ({ > {ProjectedCPI} +
+
{"Positive: (b) / (d)"}
+
)} diff --git a/src/components/ProjectFinancialSummary/ProjectFinancialSummary.tsx b/src/components/ProjectFinancialSummary/ProjectFinancialSummary.tsx index e55aaf8..c529e69 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}&subsidiaryId=-`); + }} + > + {params.value} +
+ ), }, { id: 'customerName', @@ -317,6 +329,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', @@ -512,7 +534,7 @@ const columns2 = [
{projectFinancialData.map((record:any, index:any) => (
handleCardClick(record,index)}> - +
))}
diff --git a/src/components/ProjectSearch/ProjectSearch.tsx b/src/components/ProjectSearch/ProjectSearch.tsx index 2fee69e..3a1e5c2 100644 --- a/src/components/ProjectSearch/ProjectSearch.tsx +++ b/src/components/ProjectSearch/ProjectSearch.tsx @@ -28,16 +28,16 @@ const ProjectSearch: React.FC = ({ 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