You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

708 line
20 KiB

  1. "use client";
  2. import * as React from "react";
  3. import Grid from "@mui/material/Grid";
  4. import { useState, useEffect, useMemo } from "react";
  5. import Paper from "@mui/material/Paper";
  6. import { TFunction } from "i18next";
  7. import { useTranslation } from "react-i18next";
  8. import { Card, CardHeader } from "@mui/material";
  9. import CustomSearchForm from "../CustomSearchForm/CustomSearchForm";
  10. import CustomDatagrid from "../CustomDatagrid/CustomDatagrid";
  11. import { GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
  12. import ReportProblemIcon from "@mui/icons-material/ReportProblem";
  13. import dynamic from "next/dynamic";
  14. import "../../app/global.css";
  15. import { AnyARecord, AnyCnameRecord } from "dns";
  16. import SearchBox, { Criterion } from "../SearchBox";
  17. import ProgressByClientSearch from "@/components/ProgressByClientSearch";
  18. import { Suspense } from "react";
  19. import { ClientSubsidiaryProjectResult } from "@/app/api/clientprojects/actions";
  20. import { ClientProjectResult} from "@/app/api/clientprojects";
  21. import { ConstructionOutlined } from "@mui/icons-material";
  22. import ReactApexChart from "react-apexcharts";
  23. import { ApexOptions } from "apexcharts";
  24. import { useSearchParams } from 'next/navigation';
  25. import { fetchAllClientSubsidiaryProjects} from "@/app/api/clientprojects/actions";
  26. // const ReactApexChart = dynamic(() => import('react-apexcharts'), { ssr: false });
  27. interface Props {
  28. // clientSubsidiaryProjectResult: ClientSubsidiaryProjectResult[];
  29. }
  30. const ProgressByClient: React.FC<Props> = () => {
  31. const searchParams = useSearchParams();
  32. const customerId = searchParams.get('customerId');
  33. const subsidiaryId = searchParams.get('subsidiaryId');
  34. const [activeTab, setActiveTab] = useState("financialSummary");
  35. const [SearchCriteria, setSearchCriteria] = React.useState({});
  36. const { t } = useTranslation("dashboard");
  37. const [clientCode, setClientCode] = useState("");
  38. const [clientName, setClientName] = useState("");
  39. const [subsidiaryClientCode, setSubsidiaryClientCode] = useState("");
  40. const [subsidiaryClientName, setSubsidiaryClientName] = useState("");
  41. const [projectArray, setProjectArray]: any[] = useState([]);
  42. const [percentageArray, setPercentageArray]: any[] = useState([]);
  43. const [colorArray, setColorArray]: any[] = useState([]);
  44. const [selectionModel, setSelectionModel]: any[] = React.useState([]);
  45. const [pieChartColor, setPieChartColor]: any[] = React.useState([]);
  46. const [totalSpentPercentage, setTotalSpentPercentage]: any = React.useState();
  47. const [projectBudgetManhour, setProjectBudgetManhour]: any =
  48. React.useState("-");
  49. const [actualManhourSpent, setActualManhourSpent]: any = React.useState("-");
  50. const [remainedManhour, setRemainedManhour]: any = React.useState("-");
  51. const [lastUpdate, setLastUpdate]: any = React.useState("-");
  52. const [dropdownDemo, setDropdownDemo] = useState("");
  53. const [dateDemo, setDateDemo] = useState(null);
  54. const [checkboxDemo, setCheckboxDemo] = useState(false);
  55. const [receiptFromDate, setReceiptFromDate] = useState(null);
  56. const [receiptToDate, setReceiptToDate] = useState(null);
  57. const [selectedRows, setSelectedRows]:any[] = useState([]);
  58. const [chartProjectName, setChartProjectName]:any[] = useState([]);
  59. const [chartManhourConsumptionPercentage, setChartManhourConsumptionPercentage]:any[] = useState([]);
  60. const color = ["#f57f90", "#94f7d6", "#87c5f5", "#ab95f5", "#fcd68b"];
  61. const [clientSubsidiaryProjectResult, setClientSubsidiaryProjectResult]:any[] = useState([]);
  62. const fetchData = async () => {
  63. if (customerId && subsidiaryId) {
  64. try {
  65. if (subsidiaryId === '-'){
  66. console.log("ss")
  67. const clickResult = await fetchAllClientSubsidiaryProjects(
  68. Number(customerId),Number(0))
  69. console.log(clickResult)
  70. setClientSubsidiaryProjectResult(clickResult);
  71. } else {
  72. const clickResult = await fetchAllClientSubsidiaryProjects(
  73. Number(customerId),
  74. Number(subsidiaryId))
  75. console.log(clickResult)
  76. setClientSubsidiaryProjectResult(clickResult);
  77. }
  78. } catch (error) {
  79. console.error('Error fetching client subsidiary projects:', error);
  80. }
  81. }
  82. }
  83. useEffect(() => {
  84. const projectName = []
  85. const manhourConsumptionPercentage = []
  86. for (let i = 0; i < clientSubsidiaryProjectResult.length; i++){
  87. clientSubsidiaryProjectResult[i].color = color[i]
  88. projectName.push(clientSubsidiaryProjectResult[i].projectName)
  89. manhourConsumptionPercentage.push(clientSubsidiaryProjectResult[i].manhourConsumptionPercentage)
  90. }
  91. setChartProjectName(projectName)
  92. setChartManhourConsumptionPercentage(manhourConsumptionPercentage)
  93. }, [clientSubsidiaryProjectResult]);
  94. useEffect(() => {
  95. fetchData()
  96. }, [customerId,subsidiaryId]);
  97. const rows2 = [
  98. {
  99. id: 1,
  100. project: "Consultancy Project 123",
  101. team: "XXX",
  102. teamLeader: "XXX",
  103. currentStage: "Contract Documentation",
  104. budgetedManhour: "200.00",
  105. spentManhour: "120.00",
  106. remainedManhour: "80.00",
  107. comingPaymentMilestone: "31/03/2024",
  108. alert: false,
  109. color: "#f57f90",
  110. },
  111. {
  112. id: 2,
  113. project: "Consultancy Project 456",
  114. team: "XXX",
  115. teamLeader: "XXX",
  116. currentStage: "Report Preparation",
  117. budgetedManhour: "400.00",
  118. spentManhour: "200.00",
  119. remainedManhour: "200.00",
  120. comingPaymentMilestone: "20/02/2024",
  121. alert: false,
  122. color: "#94f7d6",
  123. },
  124. {
  125. id: 3,
  126. project: "Construction Project A",
  127. team: "YYY",
  128. teamLeader: "YYY",
  129. currentStage: "Construction",
  130. budgetedManhour: "187.50",
  131. spentManhour: "200.00",
  132. remainedManhour: "12.50",
  133. comingPaymentMilestone: "13/12/2023",
  134. alert: true,
  135. color: "#87c5f5",
  136. },
  137. {
  138. id: 4,
  139. project: "Construction Project B",
  140. team: "XXX",
  141. teamLeader: "XXX",
  142. currentStage: "Post Construction",
  143. budgetedManhour: "100.00",
  144. spentManhour: "40.00",
  145. remainedManhour: "60.00",
  146. comingPaymentMilestone: "05/01/2024",
  147. alert: false,
  148. color: "#ab95f5",
  149. },
  150. {
  151. id: 5,
  152. project: "Construction Project C",
  153. team: "YYY",
  154. teamLeader: "YYY",
  155. currentStage: "Construction",
  156. budgetedManhour: "300.00",
  157. spentManhour: "150.00",
  158. remainedManhour: "150.00",
  159. comingPaymentMilestone: "31/03/2024",
  160. alert: false,
  161. color: "#fcd68b",
  162. },
  163. ];
  164. const columns2 = [
  165. {
  166. id: "color",
  167. field: "color",
  168. headerName: "",
  169. renderCell: (param: any) => {
  170. return (
  171. <span
  172. className="dot"
  173. style={{
  174. height: "15px",
  175. width: "15px",
  176. borderRadius: "50%",
  177. backgroundColor: `${param.row.color}`,
  178. display: "inline-block",
  179. }}
  180. ></span>
  181. );
  182. },
  183. flex:0.1
  184. },
  185. {
  186. id: "projectName",
  187. field: "projectName",
  188. headerName: "Project",
  189. minWidth:300
  190. },
  191. {
  192. id: "team",
  193. field: "team",
  194. headerName: "Team",
  195. minWidth: 50
  196. },
  197. {
  198. id: "teamLead",
  199. field: "teamLead",
  200. headerName: "Team Leader",
  201. minWidth: 70
  202. },
  203. {
  204. id: "expectedStage",
  205. field: "expectedStage",
  206. headerName: "Expected Stage",
  207. minWidth: 300
  208. },
  209. {
  210. id: "budgetedManhour",
  211. field: "budgetedManhour",
  212. headerName: "Budgeted Manhour",
  213. minWidth: 70
  214. },
  215. {
  216. id: "spentManhour",
  217. field: "spentManhour",
  218. headerName: "Spent Manhour",
  219. renderCell: (params: any) => {
  220. if (params.row.budgetedManhour - params.row.spentManhour <= 0) {
  221. return (
  222. <span className="text-red-300">{params.row.spentManhour}</span>
  223. );
  224. } else {
  225. console.log(params)
  226. return <span>{params.row.spentManhour}</span>;
  227. }
  228. },
  229. minWidth: 70
  230. },
  231. {
  232. id: "remainedManhour",
  233. field: "remainedManhour",
  234. headerName: "Remained Manhour",
  235. renderCell: (params: any) => {
  236. if (params.row.budgetedManhour - params.row.spentManhour <= 0) {
  237. return (
  238. <span className="text-red-300">({params.row.remainedManhour})</span>
  239. );
  240. } else {
  241. return <span>{params.row.remainedManhour}</span>;
  242. }
  243. },
  244. minWidth: 70
  245. },
  246. {
  247. id: "comingPaymentMilestone",
  248. field: "comingPaymentMilestone",
  249. headerName: "Coming Payment Milestone",
  250. minWidth: 100
  251. },
  252. {
  253. id: "alert",
  254. field: "alert",
  255. headerName: "Alert",
  256. renderCell: (params: any) => {
  257. if (params.row.alert === true) {
  258. return (
  259. <span className="text-red-300 text-center">
  260. <ReportProblemIcon />
  261. </span>
  262. );
  263. } else {
  264. return <span></span>;
  265. }
  266. },
  267. flex:0.1
  268. },
  269. ];
  270. const optionstest: ApexOptions = {
  271. chart: {
  272. height: 350,
  273. type: "line",
  274. },
  275. stroke: {
  276. width: [0, 0, 2, 2],
  277. },
  278. plotOptions: {
  279. bar: {
  280. horizontal: false,
  281. distributed: false,
  282. },
  283. },
  284. dataLabels: {
  285. enabled: false,
  286. },
  287. xaxis: {
  288. categories: [
  289. "Q1",
  290. "Q2",
  291. "Q3",
  292. "Q4",
  293. "Q5",
  294. "Q6",
  295. "Q7",
  296. "Q8",
  297. "Q9",
  298. "Q10",
  299. "Q11",
  300. "Q12",
  301. ],
  302. },
  303. yaxis: [
  304. {
  305. title: {
  306. text: "Monthly Income and Expenditure(HKD)",
  307. },
  308. min: 0,
  309. max: 350000,
  310. tickAmount: 5,
  311. labels: {
  312. formatter: function (val) {
  313. return val.toLocaleString()
  314. }
  315. }
  316. },
  317. {
  318. show: false,
  319. seriesName: "Monthly_Expenditure",
  320. title: {
  321. text: "Monthly Expenditure (HKD)",
  322. },
  323. min: 0,
  324. max: 350000,
  325. tickAmount: 5,
  326. },
  327. {
  328. seriesName: "Cumulative_Income",
  329. opposite: true,
  330. title: {
  331. text: "Cumulative Income and Expenditure(HKD)",
  332. },
  333. min: 0,
  334. max: 850000,
  335. tickAmount: 5,
  336. labels: {
  337. formatter: function (val) {
  338. return val.toLocaleString()
  339. }
  340. }
  341. },
  342. {
  343. show: false,
  344. seriesName: "Cumulative_Expenditure",
  345. opposite: true,
  346. title: {
  347. text: "Cumulative Expenditure (HKD)",
  348. },
  349. min: 0,
  350. max: 850000,
  351. tickAmount: 5,
  352. },
  353. ],
  354. grid: {
  355. borderColor: "#f1f1f1",
  356. },
  357. annotations: {},
  358. series: [
  359. {
  360. name: "Monthly_Income",
  361. type: "column",
  362. color: "#ffde91",
  363. data: [0, 110000, 0, 0, 185000, 0, 0, 189000, 0, 0, 300000, 0],
  364. },
  365. {
  366. name: "Monthly_Expenditure",
  367. type: "column",
  368. color: "#82b59a",
  369. data: [
  370. 0, 160000, 120000, 120000, 55000, 55000, 55000, 55000, 55000, 70000,
  371. 55000, 55000,
  372. ],
  373. },
  374. {
  375. name: "Cumulative_Income",
  376. type: "line",
  377. color: "#EE6D7A",
  378. data: [
  379. 0, 100000, 100000, 100000, 300000, 300000, 300000, 500000, 500000,
  380. 500000, 800000, 800000,
  381. ],
  382. },
  383. {
  384. name: "Cumulative_Expenditure",
  385. type: "line",
  386. color: "#7cd3f2",
  387. data: [
  388. 0, 198000, 240000, 400000, 410000, 430000, 510000, 580000, 600000,
  389. 710000, 730000, 790000,
  390. ],
  391. },
  392. ],
  393. };
  394. const options2: ApexOptions = {
  395. chart: {
  396. type: "donut",
  397. },
  398. colors: colorArray,
  399. plotOptions: {
  400. pie: {
  401. donut: {
  402. labels: {
  403. show: true,
  404. name: {
  405. show: true,
  406. },
  407. value: {
  408. show: true,
  409. fontWeight: 500,
  410. fontSize: "30px",
  411. color: "#3e98c7",
  412. },
  413. total: {
  414. show: true,
  415. showAlways: true,
  416. label: "Spent",
  417. fontFamily: "sans-serif",
  418. formatter: function (val) {
  419. return totalSpentPercentage + "%";
  420. },
  421. },
  422. },
  423. },
  424. },
  425. },
  426. labels: projectArray,
  427. legend: {
  428. show: false,
  429. },
  430. responsive: [
  431. {
  432. breakpoint: 480,
  433. options: {
  434. chart: {
  435. width: 200,
  436. },
  437. legend: {
  438. position: "bottom",
  439. show: false,
  440. },
  441. },
  442. },
  443. ],
  444. };
  445. const options: ApexOptions = {
  446. chart: {
  447. type: "bar",
  448. height: 350,
  449. },
  450. series: [{
  451. name: "Project Resource Consumption Percentage",
  452. data: chartManhourConsumptionPercentage,
  453. },],
  454. colors: ["#f57f90", "#94f7d6", "#87c5f5", "#ab95f5", "#fcd68b"],
  455. plotOptions: {
  456. bar: {
  457. horizontal: true,
  458. distributed: true,
  459. },
  460. },
  461. dataLabels: {
  462. enabled: false,
  463. },
  464. xaxis: {
  465. categories: chartProjectName,
  466. },
  467. yaxis: {
  468. title: {
  469. text: "Projects",
  470. },
  471. labels: {
  472. maxWidth: 200,
  473. style: {
  474. cssClass: "apexcharts-yaxis-label",
  475. },
  476. },
  477. },
  478. title: {
  479. text: "Project Resource Consumption Percentage",
  480. align: "center",
  481. },
  482. grid: {
  483. borderColor: "#f1f1f1",
  484. },
  485. annotations: {},
  486. };
  487. const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => {
  488. const selectedRowsData:any = clientSubsidiaryProjectResult.filter((row:any) =>
  489. newSelectionModel.includes(row.projectId),
  490. );
  491. console.log(selectedRowsData);
  492. const projectArray = [];
  493. const pieChartColorArray = [];
  494. let totalSpent = 0;
  495. let totalBudgetManhour = 0;
  496. const percentageArray = [];
  497. for (let i = 0; i <= selectedRowsData.length; i++) {
  498. if (i === selectedRowsData.length && i > 0) {
  499. projectArray.push("Remained");
  500. } else if (selectedRowsData.length > 0) {
  501. projectArray.push(selectedRowsData[i].projectName);
  502. totalBudgetManhour += Number(selectedRowsData[i].budgetedManhour);
  503. totalSpent += Number(selectedRowsData[i].spentManhour);
  504. pieChartColorArray.push(color[i]);
  505. }
  506. }
  507. for (let i = 0; i <= selectedRowsData.length; i++) {
  508. if (i === selectedRowsData.length && i > 0) {
  509. const remainedManhour = totalBudgetManhour - totalSpent;
  510. percentageArray.push(
  511. Number(((remainedManhour / totalBudgetManhour) * 100).toFixed(1)),
  512. );
  513. } else if (selectedRowsData.length > 0) {
  514. const percentage = (
  515. (Number(selectedRowsData[i].spentManhour) / totalBudgetManhour) *
  516. 100
  517. ).toFixed(1);
  518. percentageArray.push(Number(percentage));
  519. }
  520. }
  521. setProjectBudgetManhour(totalBudgetManhour.toFixed(2));
  522. setActualManhourSpent(totalSpent.toFixed(2));
  523. setRemainedManhour((totalBudgetManhour - totalSpent).toFixed(2));
  524. setLastUpdate(new Date().toLocaleDateString("en-GB"));
  525. setSelectionModel(newSelectionModel);
  526. console.log(projectArray);
  527. setProjectArray(projectArray);
  528. setPercentageArray(percentageArray);
  529. console.log(percentageArray);
  530. setTotalSpentPercentage(
  531. ((totalSpent / totalBudgetManhour) * 100).toFixed(1),
  532. );
  533. if (projectArray.length > 0 && projectArray.includes("Remained")) {
  534. const nonLastRecordColors = pieChartColorArray;
  535. setColorArray([
  536. ...nonLastRecordColors.slice(0, projectArray.length - 1),
  537. "#a3a3a3",
  538. ]);
  539. } else {
  540. setColorArray(pieChartColorArray);
  541. }
  542. };
  543. const applySearch = (data: any) => {
  544. console.log(data);
  545. setSearchCriteria(data);
  546. };
  547. return (
  548. <Grid item sm>
  549. {/* <CustomSearchForm applySearch={applySearch} fields={InputFields}/> */}
  550. {/* <CustomDatagrid rows={rows} columns={columns} columnWidth={200} dataGridHeight={300}/> */}
  551. <div style={{ display: "inline-block", width: "70%" }}>
  552. <Grid item xs={12} md={12} lg={12}>
  553. <Card>
  554. <CardHeader className="text-slate-500" title="Project Resource Consumption" />
  555. <div style={{ display: "inline-block", width: "99%" }}>
  556. <ReactApexChart
  557. options={options}
  558. series={options.series}
  559. type="bar"
  560. height={350}
  561. />
  562. </div>
  563. {/* <div style={{display:"inline-block",width:"20%",verticalAlign:"top",textAlign:"center"}}>
  564. <p><strong><u>Stage Deadline</u></strong></p>
  565. {stageDeadline.map((date, index) => {
  566. const marginTop = index === 0 ? 25 : 20;
  567. return (
  568. <p style={{marginTop:marginTop}} key={index}>{date}</p>
  569. );
  570. })}
  571. </div> */}
  572. <CardHeader
  573. className="text-slate-500"
  574. title="Current Stage Due Date"
  575. />
  576. <div
  577. style={{ display: "inline-block", width: "99%", marginLeft: 10 }}
  578. >
  579. <CustomDatagrid
  580. rows={clientSubsidiaryProjectResult}
  581. columns={columns2}
  582. columnWidth={200}
  583. dataGridHeight={300}
  584. checkboxSelection={true}
  585. onRowSelectionModelChange={handleSelectionChange}
  586. selectionModel={selectionModel}
  587. />
  588. </div>
  589. </Card>
  590. </Grid>
  591. </div>
  592. <div
  593. style={{
  594. display: "inline-block",
  595. width: "30%",
  596. verticalAlign: "top",
  597. marginLeft: 0,
  598. }}
  599. >
  600. <Grid item xs={12} md={12} lg={12}>
  601. <Card style={{ marginLeft: 15, marginRight: 20 }}>
  602. <CardHeader
  603. className="text-slate-500"
  604. title="Overall Progress per Project"
  605. />
  606. {percentageArray.length === 0 && (
  607. <div
  608. className="mt-10 mb-10 ml-5 mr-5 text-lg font-medium text-center"
  609. style={{ color: "#898d8d" }}
  610. >
  611. Please select the project you want to check.
  612. </div>
  613. )}
  614. {percentageArray.length > 0 && (
  615. <ReactApexChart
  616. options={options2}
  617. series={percentageArray}
  618. type="donut"
  619. />
  620. )}
  621. </Card>
  622. </Grid>
  623. <Grid item xs={12} md={12} lg={12}>
  624. <Card style={{ marginLeft: 15, marginRight: 20, marginTop: 20 }}>
  625. <div>
  626. <div
  627. className="mt-5 text-lg font-medium"
  628. style={{ color: "#898d8d" }}
  629. >
  630. <span style={{ marginLeft: "5%" }}>Project Budget Manhour</span>
  631. </div>
  632. <div
  633. className="mt-2 text-2xl font-extrabold"
  634. style={{ color: "#6b87cf" }}
  635. >
  636. <span style={{ marginLeft: "5%" }}>{projectBudgetManhour}</span>
  637. </div>
  638. </div>
  639. <hr />
  640. <div>
  641. <div
  642. className="mt-2 text-lg font-medium"
  643. style={{ color: "#898d8d" }}
  644. >
  645. <span style={{ marginLeft: "5%" }}>Actual Manhour Spent</span>
  646. </div>
  647. <div
  648. className="mt-2 text-2xl font-extrabold"
  649. style={{ color: "#6b87cf" }}
  650. >
  651. <span style={{ marginLeft: "5%" }}>{actualManhourSpent}</span>
  652. </div>
  653. </div>
  654. <hr />
  655. <div>
  656. <div
  657. className="mt-2 text-lg font-medium"
  658. style={{ color: "#898d8d" }}
  659. >
  660. <span style={{ marginLeft: "5%" }}>Remained Manhour</span>
  661. </div>
  662. <div
  663. className="mt-2 text-2xl font-extrabold"
  664. style={{ color: "#6b87cf" }}
  665. >
  666. <span style={{ marginLeft: "5%" }}>{remainedManhour}</span>
  667. </div>
  668. </div>
  669. <hr />
  670. <div>
  671. <div
  672. className="mt-2 text-lg font-medium"
  673. style={{ color: "#898d8d" }}
  674. >
  675. <span style={{ marginLeft: "5%" }}>Last Update</span>
  676. </div>
  677. <div
  678. className="mt-2 mb-5 text-2xl font-extrabold"
  679. style={{ color: "#6b87cf" }}
  680. >
  681. <span style={{ marginLeft: "5%" }}>{lastUpdate}</span>
  682. </div>
  683. </div>
  684. </Card>
  685. </Grid>
  686. </div>
  687. </Grid>
  688. );
  689. };
  690. export default ProgressByClient;