Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

1011 строки
34 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. import { useSearchParams } from 'next/navigation';
  22. import { fetchAllTeamProjects, TeamProjectResult, ClientSubsidiaryProjectResult, fetchTeamProjects, fetchAllTeamConsumption, fetchAllTeamConsumptionColorOrder} from "@/app/api/teamprojects/actions";
  23. import Typography from "@mui/material/Typography";
  24. // const ReactApexChart = dynamic(() => import('react-apexcharts'), { ssr: false });
  25. type SearchProjectQuery = Partial<Omit<ClientSubsidiaryProjectResult, "id">>;
  26. type SearchProjectParamNames = keyof SearchProjectQuery;
  27. interface Props {
  28. projects: TeamProjectResult[];
  29. }
  30. type SearchQuery = Partial<Omit<TeamProjectResult, "id">>;
  31. type SearchParamNames = keyof SearchQuery;
  32. const ProjectResourceConsumptionRanking: React.FC = () => {
  33. const searchParams = useSearchParams();
  34. const teamLeadId = searchParams.get('teamLeadId');
  35. const [activeTab, setActiveTab] = useState("financialSummary");
  36. const [SearchCriteria, setSearchCriteria] = React.useState({});
  37. const { t } = useTranslation("dashboard");
  38. const [projectData, setProjectData]: any[] = React.useState([]);
  39. const [filteredResult, setFilteredResult]:any[] = useState([]);
  40. const [teamCode, setTeamCode] = useState("");
  41. const [teamName, setTeamName] = useState("");
  42. const [projectArray, setProjectArray]: any[] = useState([]);
  43. const [percentageArray, setPercentageArray]: any[] = useState([]);
  44. const [colorArray, setColorArray]: any[] = useState([]);
  45. const [selectionModel, setSelectionModel]: any[] = React.useState([]);
  46. const [pieChartColor, setPieChartColor]: any[] = React.useState([]);
  47. const [filteredTeamProjectResult, setFilteredTeamProjectResult]:any[] = useState([]);
  48. const [totalSpentPercentage, setTotalSpentPercentage]: any = React.useState();
  49. const [projectBudgetManhour, setProjectBudgetManhour]: any =
  50. React.useState("-");
  51. const [actualManhourSpent, setActualManhourSpent]: any = React.useState("-");
  52. const [remainedManhour, setRemainedManhour]: any = React.useState("-");
  53. const [lastUpdate, setLastUpdate]: any = React.useState("-");
  54. const [dropdownDemo, setDropdownDemo] = useState("");
  55. const [dateDemo, setDateDemo] = useState(null);
  56. const [checkboxDemo, setCheckboxDemo] = useState(false);
  57. const [receiptFromDate, setReceiptFromDate] = useState(null);
  58. const [receiptToDate, setReceiptToDate] = useState(null);
  59. const [selectedRows, setSelectedRows] = useState([]);
  60. const [chartProjectColor, setChartProjectColor]:any[] = useState([]);
  61. const [chartProjectName, setChartProjectName]:any[] = useState([]);
  62. const [chartProjectDisplayName, setChartProjectDisplayName]:any[] = useState([]);
  63. const [chartProjectBudgetedHour, setChartProjectBudgetedHour]:any[] = useState([]);
  64. const [chartProjectSpentHour, setChartProjectSpentHour]:any[] = useState([]);
  65. const [chartManhourConsumptionPercentage, setChartManhourConsumptionPercentage]:any[] = useState([]);
  66. const [chartTeam, setChartTeam]:any[] = useState([]);
  67. const color = ["#f57f90", "#94f7d6", "#87c5f5", "#ab95f5", "#fcd68b",
  68. "#f58a9b", "#8ef4d1", "#92caf9", "#a798f9", "#fad287",
  69. "#f595a6", "#88f1cc", "#9dcff5", "#a39bf5", "#f8de83",
  70. "#f5a0b1", "#82eec7", "#a8d4f1", "#9f9ef1", "#f6ea7f",
  71. "#f5abb4", "#7cebca", "#b3d9ed", "#9ba1ed", "#f4f67b",
  72. "#f5b6b7", "#76e8cd", "#bed6e9", "#97a4e9", "#f2fa77",
  73. "#f5c1ba", "#70e5d0", "#c9d3e5", "#93a7e5", "#f0fe73",
  74. "#f5ccbd", "#6ae2d3", "#d4d0e1", "#8faae1", "#eefe6f",
  75. "#f5d7c0", "#64dfd6", "#dfc5dd", "#8badd5", "#ecfe6b",
  76. "#f5e2c3", "#5edcd9", "#eabada", "#87b0c9", "#eafc67",
  77. "#f5edc6", "#58d9dc", "#f5afd6", "#83b3bd", "#e8fc63",
  78. "#f5f8c9", "#52d6df", "#ffacd2", "#7fb6b1", "#e6fc5f",
  79. "#f5ffcc", "#4cd3e2", "#ffa9ce", "#7bb9a5", "#e4fc5b",
  80. "#f2ffcf", "#46d0e5", "#ffa6ca", "#77bc99", "#e2fc57",
  81. "#efffd2", "#40cde8", "#ffa3c6", "#73bf8d", "#e0fc53",
  82. "#ecffd5", "#3acaeb", "#ffa0c2", "#6fc281", "#defb4f",
  83. "#e9ffd8", "#34c7ee", "#ff9dbe", "#6bc575", "#dcfb4b",
  84. "#e6ffdb", "#2ec4f1", "#ff9aba", "#67c869", "#dafb47",
  85. "#e3ffde", "#28c1f4", "#ff97b6", "#63cb5d", "#d8fb43",
  86. "#e0ffe1", "#22bef7", "#ff94b2", "#5fce51", "#d6fb3f",
  87. "#ddfee4", "#1cbbfa", "#ff91ae", "#5bd145", "#d4fb3b",
  88. "#dafee7", "#16b8fd", "#ff8eaa", "#57d439", "#d2fb37",
  89. "#d7feea", "#10b5ff", "#ff8ba6", "#53d72d", "#d0fb33",
  90. "#d4feed", "#0ab2ff", "#ff88a2", "#4fda21", "#cefb2f",
  91. "#d1fef0", "#04afff", "#ff859e", "#4bdd15", "#ccfb2b"];
  92. const [teamProjectResult, setTeamProjectResult]:any[] = useState([]);
  93. const [teamProjectColorOrder, setTeamProjectColorOrder]:any[] = useState([]);
  94. const [currentPageProjectList, setCurrentPageProjectList]: any[] = React.useState([]);
  95. const [currentPageProjectNameList, setCurrentPageProjectNameList]: any[] = React.useState([]);
  96. const [currentPageProjectBudgetedManhourList, setCurrentPageProjectBudgetedManhourList]: any[] = React.useState([]);
  97. const [currentPageProjectSpentManhourList, setCurrentPageProjectSpentManhourList]: any[] = React.useState([]);
  98. const [currentPagePercentage, setCurrentPagePercentage]: any[] = React.useState([]);
  99. const [currentPageColor, setCurrentPageColor]: any[] = React.useState([]);
  100. const [currentPage, setCurrentPage] = useState(1);
  101. const recordsPerPage = 10;
  102. const [tableSorting, setTableSorting] = useState('ProjectName');
  103. const [selectedTeamIdList, setSelectedTeamIdList]: any[] = React.useState([]);
  104. const fetchTeamData = async () => {
  105. const teamprojects = await fetchTeamProjects();
  106. setProjectData(teamprojects)
  107. setFilteredResult(teamprojects)
  108. }
  109. const fetchData = async () => {
  110. console.log(selectedTeamIdList)
  111. if (selectedTeamIdList) {
  112. try {
  113. const clickResult = await fetchAllTeamConsumption(
  114. selectedTeamIdList,tableSorting)
  115. const colorOrder = await fetchAllTeamConsumptionColorOrder(
  116. selectedTeamIdList,tableSorting)
  117. console.log(clickResult)
  118. setTeamProjectResult(clickResult);
  119. setFilteredTeamProjectResult(clickResult);
  120. setTeamProjectColorOrder(colorOrder);
  121. } catch (error) {
  122. console.error('Error fetching team consumption:', error);
  123. }
  124. }
  125. }
  126. const projectSearchCriteria: Criterion<SearchProjectParamNames>[] = useMemo(
  127. () => [
  128. { label: t("Project Code"), paramName: "projectCode", type: "text" },
  129. { label: t("Project Name"), paramName: "projectName", type: "text" },
  130. ],
  131. [t],
  132. );
  133. const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
  134. () => [
  135. { label: t("Team Code"), paramName: "teamCode", type: "text" },
  136. { label: t("Team Name"), paramName: "teamName", type: "text" },
  137. ],
  138. [t],
  139. );
  140. useEffect(() => {
  141. const projectNo = []
  142. const projectName = []
  143. const projectBudgetedManHour = []
  144. const projectSpentManHour = []
  145. const manhourConsumptionPercentage = []
  146. const chartColor = []
  147. const chartTeam = []
  148. const colorOrder = []
  149. let c = 0
  150. let d = 0
  151. for (let i = 0; i < teamProjectColorOrder.length; i++){
  152. if (i === 0) {
  153. chartTeam.push('Team '+teamProjectColorOrder[i].team)
  154. colorOrder.push({"team":teamProjectColorOrder[i].team,"color":color[d]})
  155. } else if (teamProjectColorOrder[i].team !== teamProjectColorOrder[i - 1].team) {
  156. d = d + 1
  157. chartTeam.push('Team '+teamProjectColorOrder[i].team)
  158. colorOrder.push({"team":teamProjectColorOrder[i].team,"color":color[d]})
  159. }
  160. }
  161. for (let i = 0; i < teamProjectResult.length; i++){
  162. projectNo.push(teamProjectResult[i].projectCode + "(" + teamProjectResult[i].team + ")")
  163. projectName.push(teamProjectResult[i].projectName)
  164. projectBudgetedManHour.push(teamProjectResult[i].budgetedManhour)
  165. projectSpentManHour.push(teamProjectResult[i].spentManhour)
  166. manhourConsumptionPercentage.push(teamProjectResult[i].manhourConsumptionPercentage)
  167. for (let j = 0; j < colorOrder.length; j++){
  168. if (teamProjectResult[i].team === colorOrder[j].team){
  169. chartColor.push(colorOrder[j].color)
  170. teamProjectResult[i].color = color[j]
  171. }
  172. }
  173. // if (i === 0) {
  174. // chartColor.push(color[c])
  175. // teamProjectResult[i].color = color[c]
  176. // } else if (teamProjectResult[i].team !== teamProjectResult[i - 1].team) {
  177. // c = c + 1
  178. // chartColor.push(color[c])
  179. // teamProjectResult[i].color = color[c]
  180. // } else if (teamProjectResult[i].team === teamProjectResult[i - 1].team){
  181. // chartColor.push(color[c])
  182. // teamProjectResult[i].color = color[c]
  183. // }
  184. }
  185. setChartProjectColor(chartColor)
  186. setChartProjectName(projectNo)
  187. setChartProjectDisplayName(projectName)
  188. setChartProjectBudgetedHour(projectBudgetedManHour)
  189. setChartProjectSpentHour(projectSpentManHour)
  190. setChartManhourConsumptionPercentage(manhourConsumptionPercentage)
  191. setChartTeam(chartTeam)
  192. }, [teamProjectResult, teamProjectColorOrder]);
  193. useEffect(() => {
  194. fetchTeamData()
  195. }, []);
  196. useEffect(() => {
  197. fetchData()
  198. }, [selectedTeamIdList,tableSorting]);
  199. const rows = [
  200. {
  201. id: 1,
  202. teamCode: "TEAM-001",
  203. teamName: "Team A",
  204. noOfProjects: "5",
  205. },
  206. {
  207. id: 2,
  208. teamCode: "TEAM-001",
  209. teamName: "Team B",
  210. noOfProjects: "5",
  211. },
  212. {
  213. id: 3,
  214. teamCode: "TEAM-001",
  215. teamName: "Team C",
  216. noOfProjects: "3",
  217. },
  218. {
  219. id: 4,
  220. teamCode: "TEAM-001",
  221. teamName: "Team D",
  222. noOfProjects: "1",
  223. },
  224. ];
  225. //['#f57f90', '#94f7d6', '#87c5f5', '#ab95f5', '#fcd68b']
  226. const rows2 = [
  227. {
  228. id: 1,
  229. project: "Consultancy Project 123",
  230. team: "XXX",
  231. teamLeader: "XXX",
  232. currentStage: "Contract Documentation",
  233. budgetedManhour: "200.00",
  234. spentManhour: "120.00",
  235. remainedManhour: "80.00",
  236. comingPaymentMilestone: "31/03/2024",
  237. alert: false,
  238. color: "#f57f90",
  239. },
  240. {
  241. id: 2,
  242. project: "Consultancy Project 456",
  243. team: "XXX",
  244. teamLeader: "XXX",
  245. currentStage: "Report Preparation",
  246. budgetedManhour: "400.00",
  247. spentManhour: "200.00",
  248. remainedManhour: "200.00",
  249. comingPaymentMilestone: "20/02/2024",
  250. alert: false,
  251. color: "#94f7d6",
  252. },
  253. {
  254. id: 3,
  255. project: "Construction Project A",
  256. team: "YYY",
  257. teamLeader: "YYY",
  258. currentStage: "Construction",
  259. budgetedManhour: "187.50",
  260. spentManhour: "200.00",
  261. remainedManhour: "12.50",
  262. comingPaymentMilestone: "13/12/2023",
  263. alert: true,
  264. color: "#87c5f5",
  265. },
  266. {
  267. id: 4,
  268. project: "Construction Project B",
  269. team: "XXX",
  270. teamLeader: "XXX",
  271. currentStage: "Post Construction",
  272. budgetedManhour: "100.00",
  273. spentManhour: "40.00",
  274. remainedManhour: "60.00",
  275. comingPaymentMilestone: "05/01/2024",
  276. alert: false,
  277. color: "#ab95f5",
  278. },
  279. {
  280. id: 5,
  281. project: "Construction Project C",
  282. team: "YYY",
  283. teamLeader: "YYY",
  284. currentStage: "Construction",
  285. budgetedManhour: "300.00",
  286. spentManhour: "150.00",
  287. remainedManhour: "150.00",
  288. comingPaymentMilestone: "31/03/2024",
  289. alert: false,
  290. color: "#fcd68b",
  291. },
  292. ];
  293. const searchColumns = [
  294. {
  295. id: "teamCode",
  296. field: "teamCode",
  297. headerName: t("Team Code"),
  298. flex: 1,
  299. },
  300. {
  301. id: "teamName",
  302. field: "teamName",
  303. headerName: t("Team Name"),
  304. flex: 1,
  305. },
  306. {
  307. id: "projectNo",
  308. field: "projectNo",
  309. headerName: t("No. of Projects"),
  310. flex: 1,
  311. },
  312. ];
  313. const columns = [
  314. {
  315. id: "clientCode",
  316. field: "clientCode",
  317. headerName: "Client Code",
  318. flex: 1,
  319. },
  320. {
  321. id: "clientName",
  322. field: "clientName",
  323. headerName: "Client Name",
  324. flex: 1,
  325. },
  326. {
  327. id: "clientSubsidiaryCode",
  328. field: "clientSubsidiaryCode",
  329. headerName: "Client Subsidiary Code",
  330. flex: 1,
  331. },
  332. {
  333. id: "noOfProjects",
  334. field: "noOfProjects",
  335. headerName: "No. of Projects",
  336. flex: 1,
  337. },
  338. ];
  339. const columns2 = [
  340. {
  341. id: "color",
  342. field: "color",
  343. headerName: "",
  344. renderCell: (params: any) => {
  345. return (
  346. <span
  347. className="dot"
  348. style={{
  349. height: "15px",
  350. width: "15px",
  351. borderRadius: "50%",
  352. backgroundColor: `${params.row.color}`,
  353. display: "inline-block",
  354. }}
  355. ></span>
  356. );
  357. },
  358. flex: 0.1,
  359. },
  360. {
  361. id: "projectCode",
  362. field: "projectCode",
  363. headerName: t("Project Code"),
  364. minWidth:100
  365. },
  366. {
  367. id: "projectName",
  368. field: "projectName",
  369. headerName: t("Project Name"),
  370. minWidth:300
  371. },
  372. {
  373. id: "team",
  374. field: "team",
  375. headerName: t("Team"),
  376. minWidth:50
  377. },
  378. {
  379. id: "teamLead",
  380. field: "teamLead",
  381. headerName: t("Team Leader"),
  382. minWidth: 70
  383. },
  384. {
  385. id: "expectedStage",
  386. field: "expectedStage",
  387. headerName: t("Expected Stage"),
  388. minWidth: 300,
  389. renderCell: (params: any) => {
  390. if (params.row.expectedStage != null){
  391. const expectedStage = params.row.expectedStage;
  392. const lines = expectedStage.split(",").map((line:any, index:any) => (
  393. <React.Fragment key={index}>
  394. {line.trim()}
  395. <br />
  396. </React.Fragment>
  397. ));
  398. return <div>{lines}</div>;
  399. } else {
  400. return <div>-</div>;
  401. }
  402. },
  403. },
  404. {
  405. id: "budgetedManhour",
  406. field: "budgetedManhour",
  407. headerName: t("Budgeted Manhours"),
  408. minWidth: 70,
  409. type: "number",
  410. renderCell: (params: any) => {
  411. return <span>{params.row.budgetedManhour.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>;
  412. }
  413. },
  414. {
  415. id: "spentManhour",
  416. field: "spentManhour",
  417. headerName: t("Spent Manhours"),
  418. type: "number",
  419. renderCell: (params: any) => {
  420. if (params.row.budgetedManhour - params.row.spentManhour <= 0) {
  421. return (
  422. <span className="text-red-300">{params.row.spentManhour.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
  423. );
  424. } else {
  425. return <span>{params.row.spentManhour.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>;
  426. }
  427. },
  428. minWidth: 70
  429. },
  430. {
  431. id: "remainedManhour",
  432. field: "remainedManhour",
  433. headerName: t("Remained Manhours"),
  434. type: "number",
  435. renderCell: (params: any) => {
  436. if (params.row.budgetedManhour - params.row.spentManhour <= 0) {
  437. return (
  438. <span className="text-red-300">({params.row.remainedManhour.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })})</span>
  439. );
  440. } else {
  441. return <span>{params.row.remainedManhour.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>;
  442. }
  443. },
  444. minWidth: 70
  445. },
  446. {
  447. id: "comingPaymentMilestone",
  448. field: "comingPaymentMilestone",
  449. headerName: t("Coming Payment Milestones"),
  450. minWidth: 100
  451. },
  452. {
  453. id: "alert",
  454. field: "alert",
  455. headerName: t("Alert"),
  456. renderCell: (params: any) => {
  457. if (params.row.alert === 1) {
  458. return (
  459. <span className="text-red-300 text-center">
  460. <ReportProblemIcon />
  461. </span>
  462. );
  463. } else {
  464. return <span></span>;
  465. }
  466. },
  467. flex: 0.1,
  468. },
  469. ];
  470. const InputFields = [
  471. {
  472. id: "teamCode",
  473. label: "Team Code",
  474. type: "text",
  475. value: teamCode,
  476. setValue: setTeamCode,
  477. },
  478. {
  479. id: "teamName",
  480. label: "Team Name",
  481. type: "text",
  482. value: teamName,
  483. setValue: setTeamName,
  484. },
  485. // { id: 'dropdownDemo', label: "dropdownDemo", type: 'dropdown', options: [{id:"1", label:"1"}], value: dropdownDemo, setValue: setDropdownDemo },
  486. // { id: 'dateDemo', label:'dateDemo', type: 'date', value: dateDemo, setValue: setDateDemo },
  487. // { id: 'checkboxDemo', label:'checkboxDemo', type: 'checkbox', value: checkboxDemo, setValue: setCheckboxDemo },
  488. // { id: ['receiptFromDate','receiptToDate'], label: ["收貨日期","收貨日期"], value: [receiptFromDate ? receiptFromDate : null, receiptToDate ? receiptToDate : null],
  489. // setValue: [setReceiptFromDate, setReceiptToDate],type: 'dateRange' },
  490. ];
  491. const stageDeadline = [
  492. "31/03/2024",
  493. "20/02/2024",
  494. "01/12/2023",
  495. "05/01/2024",
  496. "31/03/2023",
  497. ];
  498. const series2: ApexAxisChartSeries | ApexNonAxisChartSeries = [
  499. {
  500. data: [17.1, 28.6, 5.7, 48.6],
  501. },
  502. ];
  503. const series: ApexAxisChartSeries | ApexNonAxisChartSeries = [
  504. {
  505. name: "Project Resource Consumption Percentage",
  506. data: [80, 55, 40, 65, 70],
  507. },
  508. ];
  509. const options2: ApexOptions = {
  510. chart: {
  511. type: "donut",
  512. },
  513. colors: colorArray,
  514. plotOptions: {
  515. pie: {
  516. donut: {
  517. labels: {
  518. show: true,
  519. name: {
  520. show: true,
  521. },
  522. value: {
  523. show: true,
  524. fontWeight: 500,
  525. fontSize: "30px",
  526. color: "#3e98c7",
  527. },
  528. total: {
  529. show: true,
  530. showAlways: true,
  531. label: t("Spent"),
  532. fontFamily: "sans-serif",
  533. formatter: function (val) {
  534. return totalSpentPercentage + "%";
  535. },
  536. },
  537. },
  538. },
  539. },
  540. },
  541. labels: projectArray,
  542. legend: {
  543. show: false,
  544. },
  545. tooltip: {
  546. enabled: true,
  547. y: {
  548. formatter: function (val) {
  549. return (val.toString().includes(".") ? val.toString() : val.toFixed(1)) + "%";
  550. }
  551. }
  552. },
  553. responsive: [
  554. {
  555. breakpoint: 480,
  556. options: {
  557. chart: {
  558. width: 200,
  559. },
  560. legend: {
  561. position: "bottom",
  562. show: false,
  563. },
  564. },
  565. },
  566. ],
  567. };
  568. const options: ApexOptions = {
  569. chart: {
  570. type: "bar",
  571. height: 450,
  572. },
  573. tooltip: {
  574. enabled: true, // Enable tooltip
  575. custom: ({ series, seriesIndex, dataPointIndex, w }) => {
  576. const projectCode = currentPageProjectList[dataPointIndex];
  577. const projectName = currentPageProjectNameList[dataPointIndex];
  578. const budgetManhours = currentPageProjectBudgetedManhourList[dataPointIndex];
  579. const spentManhours = currentPageProjectSpentManhourList[dataPointIndex];
  580. const value = series[seriesIndex][dataPointIndex];
  581. const tooltipContent = `
  582. <div style="width: 250px;">
  583. <span style="font-weight: bold;">${projectCode} - ${projectName}</span>
  584. <br>
  585. ${t("Budget Manhours")}: ${budgetManhours.toFixed(2)} ${t("hours")}
  586. <br>
  587. ${t("Spent Manhours")}: ${spentManhours.toFixed(2)} ${t("hours")}
  588. <br>
  589. Percentage: ${value.toString().includes(".") ? value.toString() : value.toFixed(1)}%
  590. </div>
  591. `;
  592. return tooltipContent;
  593. },
  594. },
  595. series: [{
  596. name: "Project Resource Consumption Percentage",
  597. data: currentPagePercentage,
  598. },],
  599. colors: currentPageColor,
  600. plotOptions: {
  601. bar: {
  602. horizontal: true,
  603. distributed: true,
  604. dataLabels: {
  605. position: 'top'
  606. },
  607. },
  608. },
  609. dataLabels: {
  610. enabled: true,
  611. textAnchor: 'end',
  612. formatter: (val) => `${val}%`,
  613. },
  614. xaxis: {
  615. categories: currentPageProjectList,
  616. },
  617. yaxis: {
  618. title: {
  619. text: t("Projects"),
  620. },
  621. labels: {
  622. maxWidth: 200,
  623. style: {
  624. cssClass: "apexcharts-yaxis-label",
  625. },
  626. },
  627. },
  628. title: {
  629. text: t("Project Resource Consumption Percentage"),
  630. align: "center",
  631. },
  632. grid: {
  633. borderColor: "#f1f1f1",
  634. xaxis: {
  635. lines: {
  636. show: true,
  637. }
  638. }
  639. },
  640. legend:{
  641. show: true,
  642. showForSingleSeries: true,
  643. customLegendItems: chartTeam,
  644. markers: {
  645. fillColors: color
  646. }
  647. },
  648. annotations: {},
  649. };
  650. const handleSearchSelectionChange = (newSelectionModel: GridRowSelectionModel) => {
  651. const selectedRowsData = projectData.filter((row: any) =>
  652. newSelectionModel.includes(row.id),
  653. );
  654. const teamIdList = []
  655. for (var i=0; i<selectedRowsData.length; i++){
  656. teamIdList.push(selectedRowsData[i].teamId)
  657. }
  658. setSelectedTeamIdList(teamIdList)
  659. };
  660. const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => {
  661. const selectedRowsData = teamProjectResult.filter((row:any) =>
  662. newSelectionModel.includes(row.id),
  663. );
  664. console.log(selectedRowsData);
  665. const projectArray = [];
  666. const pieChartColorArray = [];
  667. let totalSpent = 0;
  668. let totalBudgetManhour = 0;
  669. const percentageArray = [];
  670. for (let i = 0; i <= selectedRowsData.length; i++) {
  671. if (i === selectedRowsData.length && i > 0) {
  672. projectArray.push(t("Remained"));
  673. } else if (selectedRowsData.length > 0) {
  674. projectArray.push(selectedRowsData[i].projectName);
  675. totalBudgetManhour += Number(selectedRowsData[i].budgetedManhour);
  676. totalSpent += Number(selectedRowsData[i].spentManhour);
  677. pieChartColorArray.push(selectedRowsData[i].color);
  678. }
  679. }
  680. for (let i = 0; i <= selectedRowsData.length; i++) {
  681. if (i === selectedRowsData.length && i > 0) {
  682. const remainedManhour = totalBudgetManhour - totalSpent;
  683. percentageArray.push(
  684. Number(((remainedManhour / totalBudgetManhour) * 100).toFixed(1)),
  685. );
  686. } else if (selectedRowsData.length > 0) {
  687. const percentage = (
  688. (Number(selectedRowsData[i].spentManhour) / totalBudgetManhour) *
  689. 100
  690. ).toFixed(1);
  691. percentageArray.push(Number(percentage));
  692. }
  693. }
  694. setProjectBudgetManhour(totalBudgetManhour.toFixed(2));
  695. setActualManhourSpent(totalSpent.toFixed(2));
  696. setRemainedManhour((totalBudgetManhour - totalSpent).toFixed(2));
  697. setLastUpdate(new Date().toLocaleDateString("en-GB"));
  698. setSelectionModel(newSelectionModel);
  699. console.log(projectArray);
  700. setProjectArray(projectArray);
  701. setPercentageArray(percentageArray);
  702. console.log(percentageArray);
  703. setTotalSpentPercentage(
  704. ((totalSpent / totalBudgetManhour) * 100).toFixed(1),
  705. );
  706. if (projectArray.length > 0 && projectArray.includes(t("Remained"))) {
  707. const nonLastRecordColors = pieChartColorArray;
  708. setColorArray([
  709. ...nonLastRecordColors.slice(0, projectArray.length - 1),
  710. "#a3a3a3",
  711. ]);
  712. } else {
  713. setColorArray(pieChartColorArray);
  714. }
  715. };
  716. const startIndex = (currentPage - 1) * recordsPerPage;
  717. const endIndex = startIndex + recordsPerPage;
  718. useEffect(() => {
  719. console.log(chartManhourConsumptionPercentage)
  720. const currentPageProjectData = chartProjectName.slice(startIndex, endIndex)
  721. const currentPageProjectName = chartProjectDisplayName.slice(startIndex, endIndex)
  722. const currentPageProjectBudgetedManhour = chartProjectBudgetedHour.slice(startIndex, endIndex)
  723. const currentPageProjectSpentManhour = chartProjectSpentHour.slice(startIndex, endIndex)
  724. const currentPageData = chartManhourConsumptionPercentage.slice(startIndex, endIndex);
  725. const colorArray = chartProjectColor.slice(startIndex, endIndex);
  726. console.log(currentPage)
  727. console.log(Math.ceil(chartManhourConsumptionPercentage.length / recordsPerPage))
  728. setCurrentPageProjectList(currentPageProjectData)
  729. setCurrentPageProjectNameList(currentPageProjectName)
  730. setCurrentPageProjectBudgetedManhourList(currentPageProjectBudgetedManhour)
  731. setCurrentPageProjectSpentManhourList(currentPageProjectSpentManhour)
  732. setCurrentPagePercentage(currentPageData)
  733. setCurrentPageColor(colorArray)
  734. }, [chartManhourConsumptionPercentage,currentPage]);
  735. const handlePrevPage = () => {
  736. if (currentPage > 1) {
  737. setCurrentPage(currentPage - 1);
  738. }
  739. };
  740. const handleNextPage = () => {
  741. if (endIndex < chartManhourConsumptionPercentage.length) {
  742. setCurrentPage(currentPage + 1);
  743. }
  744. };
  745. const applySearch = (data: any) => {
  746. console.log(data);
  747. setSearchCriteria(data);
  748. };
  749. return (
  750. <>
  751. <Typography variant="h4" marginInlineEnd={2}>
  752. {t("Project Resource Consumption Ranking")}
  753. </Typography>
  754. <SearchBox
  755. criteria={searchCriteria}
  756. onSearch={(query) => {
  757. setFilteredResult(
  758. projectData.filter(
  759. (cp:any) =>
  760. cp.teamCode.toLowerCase().includes(query.teamCode.toLowerCase()) &&
  761. cp.teamName.toLowerCase().includes(query.teamName.toLowerCase())
  762. ),
  763. );
  764. }}
  765. />
  766. <CustomDatagrid
  767. rows={filteredResult}
  768. columns={searchColumns}
  769. columnWidth={200}
  770. dataGridHeight={300}
  771. checkboxSelection={true}
  772. onRowSelectionModelChange={handleSearchSelectionChange}
  773. selectionModel={selectionModel}
  774. />
  775. <Grid item sm>
  776. <div style={{ display: "inline-block", width: "70%" }}>
  777. <Grid item xs={12} md={12} lg={12}>
  778. <Card>
  779. <CardHeader className="text-slate-500" title= {t("Project Resource Consumption")} />
  780. <div style={{ display: "inline-block", width: "99%" }}>
  781. <div className="ml-6 text-sm">
  782. <b>
  783. {t("Sorting")}:
  784. </b>
  785. </div>
  786. <div className="ml-6 mb-2">
  787. <button onClick={() => {setTableSorting('PercentageASC')}}
  788. className="hover:cursor-pointer hover:bg-lime-50 text-sm bg-transparent border-lime-600 text-lime-600 border-solid w-50"
  789. >
  790. {t("Percentage (Ascending Order)")}
  791. </button>
  792. <button
  793. onClick={() => {setTableSorting('PercentageDESC')}}
  794. className="hover:cursor-pointer hover:bg-lime-50 text-sm bg-transparent border-lime-600 text-lime-600 border-solid w-50"
  795. >
  796. {t("Percentage (Descending Order)")}
  797. </button>
  798. <button
  799. onClick={() => {setTableSorting('ProjectName')}}
  800. className="hover:cursor-pointer hover:bg-lime-50 text-sm bg-transparent border-lime-600 text-lime-600 border-solid w-50"
  801. >
  802. {t("Project Name")}
  803. </button>
  804. </div>
  805. <ReactApexChart
  806. options={options}
  807. series={options.series}
  808. type="bar"
  809. height={500}
  810. />
  811. <div className="float-right mr-4 mb-10">
  812. {currentPage === 1 && (
  813. <button className="bg-lime-600 text-white font-bold py-2 px-4 opacity-50 cursor-not-allowed rounded-l">
  814. {t("Previous")}
  815. </button>
  816. )}
  817. {currentPage !== 1 && (
  818. <button onClick={handlePrevPage} className="bg-lime-600 hover:bg-lime-7000 text-white font-bold py-2 px-4 outline-none rounded-l">
  819. {t("Previous")}
  820. </button>
  821. )}
  822. {endIndex >= chartManhourConsumptionPercentage.length && (
  823. <button className="bg-lime-600 text-white font-bold py-2 px-4 opacity-50 cursor-not-allowed rounded-r mr-2">
  824. {t("Next")}
  825. </button>
  826. )}
  827. {endIndex < chartManhourConsumptionPercentage.length && (
  828. <button onClick={handleNextPage} className="bg-lime-600 hover:bg-lime-700 text-white font-bold py-2 px-4 outline-none rounded-r mr-2">
  829. {t("Next")}
  830. </button>
  831. )}
  832. {t("Page")}&nbsp;
  833. {chartManhourConsumptionPercentage.length === 0 && (
  834. 0
  835. )}
  836. {chartManhourConsumptionPercentage.length > 0 && (
  837. currentPage
  838. )}
  839. &nbsp;of&nbsp;
  840. {Math.ceil(chartManhourConsumptionPercentage.length / recordsPerPage)}
  841. </div>
  842. </div>
  843. </Card>
  844. <Card className="mt-2">
  845. <CardHeader
  846. className="text-slate-500"
  847. title={t("Resource Consumption and Coming Milestones")}
  848. />
  849. {teamProjectResult.length > 0 && (
  850. <SearchBox
  851. criteria={projectSearchCriteria}
  852. onSearch={(query) => {
  853. setFilteredTeamProjectResult(
  854. teamProjectResult.filter(
  855. (cp:any) =>
  856. cp.projectCode.toLowerCase().includes(query.projectCode.toLowerCase()) &&
  857. cp.projectName.toLowerCase().includes(query.projectName.toLowerCase())
  858. ),
  859. );
  860. }}
  861. />
  862. )}
  863. <div
  864. style={{ display: "inline-block", width: "99%", marginLeft: 10 }}
  865. >
  866. <CustomDatagrid
  867. rows={filteredTeamProjectResult}
  868. columns={columns2}
  869. columnWidth={200}
  870. dataGridHeight={300}
  871. checkboxSelection={true}
  872. onRowSelectionModelChange={handleSelectionChange}
  873. selectionModel={selectionModel}
  874. />
  875. </div>
  876. </Card>
  877. </Grid>
  878. </div>
  879. <div
  880. style={{
  881. display: "inline-block",
  882. width: "30%",
  883. verticalAlign: "top",
  884. marginLeft: 0,
  885. }}
  886. >
  887. <Grid item xs={12} md={12} lg={12} style={{height:710}}>
  888. <Card style={{ marginLeft: 15, marginRight: 20, height:"100%"}}>
  889. <CardHeader
  890. className="text-slate-500"
  891. title= {t("Overall Progress per Project")}
  892. />
  893. {percentageArray.length === 0 && (
  894. <div
  895. className="mt-40 mb-10 ml-5 mr-5 text-lg font-medium text-center"
  896. style={{ color: "#898d8d"}}
  897. >
  898. {t("Please select the project you want to check.")}
  899. </div>
  900. )}
  901. {percentageArray.length > 0 && (
  902. <ReactApexChart
  903. options={options2}
  904. series={percentageArray}
  905. type="donut"
  906. style={{marginTop:'10rem'}}
  907. />
  908. )}
  909. </Card>
  910. </Grid>
  911. <Grid item xs={12} md={12} lg={12}>
  912. <Card style={{ marginLeft: 15, marginRight: 20, marginTop: 15 }}>
  913. <div>
  914. <div
  915. className="mt-5 text-lg font-medium"
  916. style={{ color: "#898d8d" }}
  917. >
  918. <span style={{ margin: "5%" }}>{t("Project Budget Manhours")}</span>
  919. </div>
  920. <div
  921. className="mt-2 text-2xl font-extrabold"
  922. style={{ color: "#6b87cf", textAlign: "right" }}
  923. >
  924. <span style={{ margin: "5%" }}>{projectBudgetManhour}</span>
  925. </div>
  926. </div>
  927. <hr />
  928. <div>
  929. <div
  930. className="mt-2 text-lg font-medium"
  931. style={{ color: "#898d8d" }}
  932. >
  933. <span style={{ margin: "5%" }}>{t("Actual Manhours Spent")}</span>
  934. </div>
  935. <div
  936. className="mt-2 text-2xl font-extrabold"
  937. style={{ color: "#6b87cf", textAlign: "right" }}
  938. >
  939. <span style={{ margin: "5%" }}>{actualManhourSpent}</span>
  940. </div>
  941. </div>
  942. <hr />
  943. <div>
  944. <div
  945. className="mt-2 text-lg font-medium"
  946. style={{ color: "#898d8d" }}
  947. >
  948. <span style={{ margin: "5%" }}>{t("Remained Manhours")}</span>
  949. </div>
  950. <div
  951. className="mt-2 text-2xl font-extrabold"
  952. style={{ color: "#6b87cf", textAlign: "right" }}
  953. >
  954. <span style={{ margin: "5%" }}>{remainedManhour}</span>
  955. </div>
  956. </div>
  957. <hr />
  958. <div>
  959. <div
  960. className="mt-2 text-lg font-medium"
  961. style={{ color: "#898d8d" }}
  962. >
  963. <span style={{ margin: "5%" }}>{t("Last Update")}</span>
  964. </div>
  965. <div
  966. className="mt-2 mb-5 text-2xl font-extrabold"
  967. style={{ color: "#6b87cf", textAlign: "right" }}
  968. >
  969. <span style={{ margin: "5%" }}>{lastUpdate}</span>
  970. </div>
  971. </div>
  972. </Card>
  973. </Grid>
  974. </div>
  975. </Grid>
  976. </>
  977. );
  978. };
  979. export default ProjectResourceConsumptionRanking;