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

ProjectResourceConsumptionRanking.tsx 34 KiB

1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010
  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;