您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 

622 行
18 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 ReactApexChart from "react-apexcharts";
  12. import { ApexOptions } from "apexcharts";
  13. import { GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
  14. import ReportProblemIcon from "@mui/icons-material/ReportProblem";
  15. import dynamic from "next/dynamic";
  16. import "../../app/global.css";
  17. import { AnyARecord, AnyCnameRecord } from "dns";
  18. import SearchBox, { Criterion } from "../SearchBox";
  19. import ProgressByTeamSearch from "@/components/ProgressByTeamSearch";
  20. import { Suspense } from "react";
  21. const ReactApexChart = dynamic(() => import('react-apexcharts'), { ssr: false });
  22. const ProgressByTeam: React.FC = () => {
  23. const [activeTab, setActiveTab] = useState("financialSummary");
  24. const [SearchCriteria, setSearchCriteria] = React.useState({});
  25. const { t } = useTranslation("dashboard");
  26. const [teamCode, setTeamCode] = useState("");
  27. const [teamName, setTeamName] = useState("");
  28. const [projectArray, setProjectArray]: any[] = useState([]);
  29. const [percentageArray, setPercentageArray]: any[] = useState([]);
  30. const [colorArray, setColorArray]: any[] = useState([]);
  31. const [selectionModel, setSelectionModel]: any[] = React.useState([]);
  32. const [pieChartColor, setPieChartColor]: any[] = React.useState([]);
  33. const [totalSpentPercentage, setTotalSpentPercentage]: any = React.useState();
  34. const [projectBudgetManhour, setProjectBudgetManhour]: any =
  35. React.useState("-");
  36. const [actualManhourSpent, setActualManhourSpent]: any = React.useState("-");
  37. const [remainedManhour, setRemainedManhour]: any = React.useState("-");
  38. const [lastUpdate, setLastUpdate]: any = React.useState("-");
  39. const [dropdownDemo, setDropdownDemo] = useState("");
  40. const [dateDemo, setDateDemo] = useState(null);
  41. const [checkboxDemo, setCheckboxDemo] = useState(false);
  42. const [receiptFromDate, setReceiptFromDate] = useState(null);
  43. const [receiptToDate, setReceiptToDate] = useState(null);
  44. const [selectedRows, setSelectedRows] = useState([]);
  45. const rows = [
  46. {
  47. id: 1,
  48. teamCode: "TEAM-001",
  49. teamName: "Team A",
  50. noOfProjects: "5",
  51. },
  52. {
  53. id: 2,
  54. teamCode: "TEAM-001",
  55. teamName: "Team B",
  56. noOfProjects: "5",
  57. },
  58. {
  59. id: 3,
  60. teamCode: "TEAM-001",
  61. teamName: "Team C",
  62. noOfProjects: "3",
  63. },
  64. {
  65. id: 4,
  66. teamCode: "TEAM-001",
  67. teamName: "Team D",
  68. noOfProjects: "1",
  69. },
  70. ];
  71. //['#f57f90', '#94f7d6', '#87c5f5', '#ab95f5', '#fcd68b']
  72. const rows2 = [
  73. {
  74. id: 1,
  75. project: "Consultancy Project 123",
  76. team: "XXX",
  77. teamLeader: "XXX",
  78. currentStage: "Contract Documentation",
  79. budgetedManhour: "200.00",
  80. spentManhour: "120.00",
  81. remainedManhour: "80.00",
  82. comingPaymentMilestone: "31/03/2024",
  83. alert: false,
  84. color: "#f57f90",
  85. },
  86. {
  87. id: 2,
  88. project: "Consultancy Project 456",
  89. team: "XXX",
  90. teamLeader: "XXX",
  91. currentStage: "Report Preparation",
  92. budgetedManhour: "400.00",
  93. spentManhour: "200.00",
  94. remainedManhour: "200.00",
  95. comingPaymentMilestone: "20/02/2024",
  96. alert: false,
  97. color: "#94f7d6",
  98. },
  99. {
  100. id: 3,
  101. project: "Construction Project A",
  102. team: "YYY",
  103. teamLeader: "YYY",
  104. currentStage: "Construction",
  105. budgetedManhour: "187.50",
  106. spentManhour: "200.00",
  107. remainedManhour: "12.50",
  108. comingPaymentMilestone: "13/12/2023",
  109. alert: true,
  110. color: "#87c5f5",
  111. },
  112. {
  113. id: 4,
  114. project: "Construction Project B",
  115. team: "XXX",
  116. teamLeader: "XXX",
  117. currentStage: "Post Construction",
  118. budgetedManhour: "100.00",
  119. spentManhour: "40.00",
  120. remainedManhour: "60.00",
  121. comingPaymentMilestone: "05/01/2024",
  122. alert: false,
  123. color: "#ab95f5",
  124. },
  125. {
  126. id: 5,
  127. project: "Construction Project C",
  128. team: "YYY",
  129. teamLeader: "YYY",
  130. currentStage: "Construction",
  131. budgetedManhour: "300.00",
  132. spentManhour: "150.00",
  133. remainedManhour: "150.00",
  134. comingPaymentMilestone: "31/03/2024",
  135. alert: false,
  136. color: "#fcd68b",
  137. },
  138. ];
  139. const columns = [
  140. {
  141. id: "clientCode",
  142. field: "clientCode",
  143. headerName: "Client Code",
  144. flex: 1,
  145. },
  146. {
  147. id: "clientName",
  148. field: "clientName",
  149. headerName: "Client Name",
  150. flex: 1,
  151. },
  152. {
  153. id: "clientSubsidiaryCode",
  154. field: "clientSubsidiaryCode",
  155. headerName: "Client Subsidiary Code",
  156. flex: 1,
  157. },
  158. {
  159. id: "noOfProjects",
  160. field: "noOfProjects",
  161. headerName: "No. of Projects",
  162. flex: 1,
  163. },
  164. ];
  165. const columns2 = [
  166. {
  167. id: "color",
  168. field: "color",
  169. headerName: "",
  170. renderCell: (params: any) => {
  171. return (
  172. <span
  173. className="dot"
  174. style={{
  175. height: "15px",
  176. width: "15px",
  177. borderRadius: "50%",
  178. backgroundColor: `${params.row.color}`,
  179. display: "inline-block",
  180. }}
  181. ></span>
  182. );
  183. },
  184. flex: 0.1,
  185. },
  186. {
  187. id: "project",
  188. field: "project",
  189. headerName: "Project",
  190. flex: 1,
  191. },
  192. {
  193. id: "team",
  194. field: "team",
  195. headerName: "Team",
  196. flex: 0.8,
  197. },
  198. {
  199. id: "teamLeader",
  200. field: "teamLeader",
  201. headerName: "Team Leader",
  202. flex: 0.8,
  203. },
  204. {
  205. id: "currentStage",
  206. field: "currentStage",
  207. headerName: "Current Stage",
  208. flex: 1,
  209. },
  210. {
  211. id: "budgetedManhour",
  212. field: "budgetedManhour",
  213. headerName: "Budgeted Manhour",
  214. flex: 0.8,
  215. },
  216. {
  217. id: "spentManhour",
  218. field: "spentManhour",
  219. headerName: "Spent Manhour",
  220. renderCell: (params: any) => {
  221. if (params.row.budgetedManhour - params.row.spentManhour <= 0) {
  222. return (
  223. <span className="text-red-300">{params.row.spentManhour}</span>
  224. );
  225. } else {
  226. return <span>{params.row.spentManhour}</span>;
  227. }
  228. },
  229. flex: 0.8,
  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. flex: 1,
  245. },
  246. {
  247. id: "comingPaymentMilestone",
  248. field: "comingPaymentMilestone",
  249. headerName: "Coming Payment Milestone",
  250. flex: 1,
  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.2,
  268. },
  269. ];
  270. const InputFields = [
  271. {
  272. id: "teamCode",
  273. label: "Team Code",
  274. type: "text",
  275. value: teamCode,
  276. setValue: setTeamCode,
  277. },
  278. {
  279. id: "teamName",
  280. label: "Team Name",
  281. type: "text",
  282. value: teamName,
  283. setValue: setTeamName,
  284. },
  285. // { id: 'dropdownDemo', label: "dropdownDemo", type: 'dropdown', options: [{id:"1", label:"1"}], value: dropdownDemo, setValue: setDropdownDemo },
  286. // { id: 'dateDemo', label:'dateDemo', type: 'date', value: dateDemo, setValue: setDateDemo },
  287. // { id: 'checkboxDemo', label:'checkboxDemo', type: 'checkbox', value: checkboxDemo, setValue: setCheckboxDemo },
  288. // { id: ['receiptFromDate','receiptToDate'], label: ["收貨日期","收貨日期"], value: [receiptFromDate ? receiptFromDate : null, receiptToDate ? receiptToDate : null],
  289. // setValue: [setReceiptFromDate, setReceiptToDate],type: 'dateRange' },
  290. ];
  291. const stageDeadline = [
  292. "31/03/2024",
  293. "20/02/2024",
  294. "01/12/2023",
  295. "05/01/2024",
  296. "31/03/2023",
  297. ];
  298. const series2: ApexAxisChartSeries | ApexNonAxisChartSeries = [
  299. {
  300. data: [17.1, 28.6, 5.7, 48.6],
  301. },
  302. ];
  303. const series: ApexAxisChartSeries | ApexNonAxisChartSeries = [
  304. {
  305. name: "Project Resource Consumption Percentage",
  306. data: [80, 55, 40, 65, 70],
  307. },
  308. ];
  309. const options2: ApexOptions = {
  310. chart: {
  311. type: "donut",
  312. },
  313. colors: colorArray,
  314. plotOptions: {
  315. pie: {
  316. donut: {
  317. labels: {
  318. show: true,
  319. name: {
  320. show: true,
  321. },
  322. value: {
  323. show: true,
  324. fontWeight: 500,
  325. fontSize: "30px",
  326. color: "#3e98c7",
  327. },
  328. total: {
  329. show: true,
  330. showAlways: true,
  331. label: "Spent",
  332. fontFamily: "sans-serif",
  333. formatter: function (val) {
  334. return totalSpentPercentage + "%";
  335. },
  336. },
  337. },
  338. },
  339. },
  340. },
  341. labels: projectArray,
  342. legend: {
  343. show: false,
  344. },
  345. responsive: [
  346. {
  347. breakpoint: 480,
  348. options: {
  349. chart: {
  350. width: 200,
  351. },
  352. legend: {
  353. position: "bottom",
  354. show: false,
  355. },
  356. },
  357. },
  358. ],
  359. };
  360. const options: ApexOptions = {
  361. chart: {
  362. type: "bar",
  363. height: 350,
  364. },
  365. colors: ["#f57f90", "#94f7d6", "#87c5f5", "#ab95f5", "#fcd68b"],
  366. plotOptions: {
  367. bar: {
  368. horizontal: true,
  369. distributed: true,
  370. },
  371. },
  372. dataLabels: {
  373. enabled: false,
  374. },
  375. xaxis: {
  376. categories: [
  377. "Consultancy Project 123",
  378. "Consultancy Project 456",
  379. "Construction Project A",
  380. "Construction Project B",
  381. "Construction Project C",
  382. ],
  383. },
  384. yaxis: {
  385. title: {
  386. text: "Projects",
  387. },
  388. labels: {
  389. maxWidth: 200,
  390. style: {
  391. cssClass: "apexcharts-yaxis-label",
  392. },
  393. },
  394. },
  395. title: {
  396. text: "Project Resource Consumption Percentage",
  397. align: "center",
  398. },
  399. grid: {
  400. borderColor: "#f1f1f1",
  401. },
  402. annotations: {},
  403. };
  404. const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => {
  405. const selectedRowsData = rows2.filter((row) =>
  406. newSelectionModel.includes(row.id),
  407. );
  408. console.log(selectedRowsData);
  409. const projectArray = [];
  410. const pieChartColorArray = [];
  411. let totalSpent = 0;
  412. let totalBudgetManhour = 0;
  413. const percentageArray = [];
  414. for (let i = 0; i <= selectedRowsData.length; i++) {
  415. if (i === selectedRowsData.length && i > 0) {
  416. projectArray.push("Remained");
  417. } else if (selectedRowsData.length > 0) {
  418. projectArray.push(selectedRowsData[i].project);
  419. totalBudgetManhour += Number(selectedRowsData[i].budgetedManhour);
  420. totalSpent += Number(selectedRowsData[i].spentManhour);
  421. pieChartColorArray.push(selectedRowsData[i].color);
  422. }
  423. }
  424. for (let i = 0; i <= selectedRowsData.length; i++) {
  425. if (i === selectedRowsData.length && i > 0) {
  426. const remainedManhour = totalBudgetManhour - totalSpent;
  427. percentageArray.push(
  428. Number(((remainedManhour / totalBudgetManhour) * 100).toFixed(1)),
  429. );
  430. } else if (selectedRowsData.length > 0) {
  431. const percentage = (
  432. (Number(selectedRowsData[i].spentManhour) / totalBudgetManhour) *
  433. 100
  434. ).toFixed(1);
  435. percentageArray.push(Number(percentage));
  436. }
  437. }
  438. setProjectBudgetManhour(totalBudgetManhour.toFixed(2));
  439. setActualManhourSpent(totalSpent.toFixed(2));
  440. setRemainedManhour((totalBudgetManhour - totalSpent).toFixed(2));
  441. setLastUpdate(new Date().toLocaleDateString("en-GB"));
  442. setSelectionModel(newSelectionModel);
  443. console.log(projectArray);
  444. setProjectArray(projectArray);
  445. setPercentageArray(percentageArray);
  446. console.log(percentageArray);
  447. setTotalSpentPercentage(
  448. ((totalSpent / totalBudgetManhour) * 100).toFixed(1),
  449. );
  450. if (projectArray.length > 0 && projectArray.includes("Remained")) {
  451. const nonLastRecordColors = pieChartColorArray;
  452. setColorArray([
  453. ...nonLastRecordColors.slice(0, projectArray.length - 1),
  454. "#a3a3a3",
  455. ]);
  456. } else {
  457. setColorArray(pieChartColorArray);
  458. }
  459. };
  460. const applySearch = (data: any) => {
  461. console.log(data);
  462. setSearchCriteria(data);
  463. };
  464. return (
  465. <Grid item sm>
  466. {/* <CustomSearchForm applySearch={applySearch} fields={InputFields}/> */}
  467. {/* <CustomDatagrid rows={rows} columns={columns} columnWidth={200} dataGridHeight={300}/> */}
  468. <div style={{ display: "inline-block", width: "70%" }}>
  469. <Grid item xs={12} md={12} lg={12}>
  470. <Card>
  471. <CardHeader className="text-slate-500" title="Project Resource Consumption" />
  472. <div style={{ display: "inline-block", width: "99%" }}>
  473. <ReactApexChart
  474. options={options}
  475. series={series}
  476. type="bar"
  477. height={350}
  478. />
  479. </div>
  480. {/* <div style={{display:"inline-block",width:"20%",verticalAlign:"top",textAlign:"center"}}>
  481. <p><strong><u>Stage Deadline</u></strong></p>
  482. {stageDeadline.map((date, index) => {
  483. const marginTop = index === 0 ? 25 : 20;
  484. return (
  485. <p style={{marginTop:marginTop}} key={index}>{date}</p>
  486. );
  487. })}
  488. </div> */}
  489. <CardHeader
  490. className="text-slate-500"
  491. title="Current Stage Due Date"
  492. />
  493. <div
  494. style={{ display: "inline-block", width: "99%", marginLeft: 10 }}
  495. >
  496. <CustomDatagrid
  497. rows={rows2}
  498. columns={columns2}
  499. columnWidth={200}
  500. dataGridHeight={300}
  501. checkboxSelection={true}
  502. onRowSelectionModelChange={handleSelectionChange}
  503. selectionModel={selectionModel}
  504. />
  505. </div>
  506. </Card>
  507. </Grid>
  508. </div>
  509. <div
  510. style={{
  511. display: "inline-block",
  512. width: "30%",
  513. verticalAlign: "top",
  514. marginLeft: 0,
  515. }}
  516. >
  517. <Grid item xs={12} md={12} lg={12}>
  518. <Card style={{ marginLeft: 15, marginRight: 20 }}>
  519. <CardHeader
  520. className="text-slate-500"
  521. title="Overall Progress per Project"
  522. />
  523. {percentageArray.length === 0 && (
  524. <div
  525. className="mt-10 mb-10 ml-5 mr-5 text-lg font-medium text-center"
  526. style={{ color: "#898d8d" }}
  527. >
  528. Please select the project you want to check.
  529. </div>
  530. )}
  531. {percentageArray.length > 0 && (
  532. <ReactApexChart
  533. options={options2}
  534. series={percentageArray}
  535. type="donut"
  536. />
  537. )}
  538. </Card>
  539. </Grid>
  540. <Grid item xs={12} md={12} lg={12}>
  541. <Card style={{ marginLeft: 15, marginRight: 20, marginTop: 20 }}>
  542. <div>
  543. <div
  544. className="mt-5 text-lg font-medium"
  545. style={{ color: "#898d8d" }}
  546. >
  547. <span style={{ marginLeft: "5%" }}>Project Budget Manhour</span>
  548. </div>
  549. <div
  550. className="mt-2 text-2xl font-extrabold"
  551. style={{ color: "#6b87cf" }}
  552. >
  553. <span style={{ marginLeft: "5%" }}>{projectBudgetManhour}</span>
  554. </div>
  555. </div>
  556. <hr />
  557. <div>
  558. <div
  559. className="mt-2 text-lg font-medium"
  560. style={{ color: "#898d8d" }}
  561. >
  562. <span style={{ marginLeft: "5%" }}>Actual Manhour Spent</span>
  563. </div>
  564. <div
  565. className="mt-2 text-2xl font-extrabold"
  566. style={{ color: "#6b87cf" }}
  567. >
  568. <span style={{ marginLeft: "5%" }}>{actualManhourSpent}</span>
  569. </div>
  570. </div>
  571. <hr />
  572. <div>
  573. <div
  574. className="mt-2 text-lg font-medium"
  575. style={{ color: "#898d8d" }}
  576. >
  577. <span style={{ marginLeft: "5%" }}>Remained Manhour</span>
  578. </div>
  579. <div
  580. className="mt-2 text-2xl font-extrabold"
  581. style={{ color: "#6b87cf" }}
  582. >
  583. <span style={{ marginLeft: "5%" }}>{remainedManhour}</span>
  584. </div>
  585. </div>
  586. <hr />
  587. <div>
  588. <div
  589. className="mt-2 text-lg font-medium"
  590. style={{ color: "#898d8d" }}
  591. >
  592. <span style={{ marginLeft: "5%" }}>Last Update</span>
  593. </div>
  594. <div
  595. className="mt-2 mb-5 text-2xl font-extrabold"
  596. style={{ color: "#6b87cf" }}
  597. >
  598. <span style={{ marginLeft: "5%" }}>{lastUpdate}</span>
  599. </div>
  600. </div>
  601. </Card>
  602. </Grid>
  603. </div>
  604. </Grid>
  605. );
  606. };
  607. export default ProgressByTeam;