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

1204 строки
44 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 ProgressByClientSearch from "@/components/ProgressByClientSearch";
  20. import { Suspense } from "react";
  21. import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch";
  22. import { Input, Label } from "reactstrap";
  23. import Select, { components } from "react-select";
  24. import { DateCalendar } from "@mui/x-date-pickers/DateCalendar";
  25. import { DatePicker } from "@mui/x-date-pickers/DatePicker";
  26. import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
  27. import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
  28. import dayjs, { Dayjs } from "dayjs";
  29. import isBetweenPlugin from "dayjs/plugin/isBetween";
  30. import { PickersDay, PickersDayProps } from "@mui/x-date-pickers/PickersDay";
  31. import { styled } from "@mui/material/styles";
  32. dayjs.extend(isBetweenPlugin);
  33. interface CustomPickerDayProps extends PickersDayProps<Dayjs> {
  34. isSelected: boolean;
  35. isHovered: boolean;
  36. }
  37. const CustomPickersDay = styled(PickersDay, {
  38. shouldForwardProp: (prop) => prop !== "isSelected" && prop !== "isHovered",
  39. })<CustomPickerDayProps>(({ theme, isSelected, isHovered, day }) => ({
  40. borderRadius: 0,
  41. ...(isSelected && {
  42. backgroundColor: theme.palette.primary.main,
  43. color: theme.palette.primary.contrastText,
  44. "&:hover, &:focus": {
  45. backgroundColor: theme.palette.primary.main,
  46. },
  47. }),
  48. ...(isHovered && {
  49. backgroundColor: theme.palette.primary[theme.palette.mode],
  50. "&:hover, &:focus": {
  51. backgroundColor: theme.palette.primary[theme.palette.mode],
  52. },
  53. }),
  54. ...(day.day() === 0 && {
  55. borderTopLeftRadius: "50%",
  56. borderBottomLeftRadius: "50%",
  57. }),
  58. ...(day.day() === 6 && {
  59. borderTopRightRadius: "50%",
  60. borderBottomRightRadius: "50%",
  61. }),
  62. })) as React.ComponentType<CustomPickerDayProps>;
  63. const isInSameWeek = (dayA: Dayjs, dayB: Dayjs | null | undefined) => {
  64. if (dayB == null) {
  65. return false;
  66. }
  67. return dayA.isSame(dayB, "week");
  68. };
  69. function Day(
  70. props: PickersDayProps<Dayjs> & {
  71. selectedDay?: Dayjs | null;
  72. hoveredDay?: Dayjs | null;
  73. },
  74. ) {
  75. const { day, selectedDay, hoveredDay, ...other } = props;
  76. return (
  77. <CustomPickersDay
  78. {...other}
  79. day={day}
  80. sx={{ px: 2.5 }}
  81. disableMargin
  82. selected={false}
  83. isSelected={isInSameWeek(day, selectedDay)}
  84. isHovered={isInSameWeek(day, hoveredDay)}
  85. />
  86. );
  87. }
  88. const StaffUtilization: React.FC = () => {
  89. const todayDate = new Date();
  90. const firstDayOfWeek = new Date();
  91. const lastDayOfWeek = new Date();
  92. firstDayOfWeek.setDate(todayDate.getDate() - todayDate.getDay() + 1);
  93. lastDayOfWeek.setDate(todayDate.getDate() - todayDate.getDay() + 7);
  94. const firstDayOfMonth = new Date(
  95. todayDate.getFullYear(),
  96. todayDate.getMonth(),
  97. 1,
  98. );
  99. const lastDayOfMonth = new Date(
  100. todayDate.getFullYear(),
  101. todayDate.getMonth() + 1,
  102. 0,
  103. );
  104. const [firstDayOfWeekString, setFirstDayOfWeekString] = React.useState(
  105. dayjs(firstDayOfWeek).format("DD MMM YYYY"),
  106. );
  107. const [lastDayOfWeekString, setLastDayOfWeekString] = React.useState(
  108. dayjs(lastDayOfWeek).format("DD MMM YYYY"),
  109. );
  110. const [firstDayOfMonthString, setFirstDayOfMonthString] = React.useState(
  111. dayjs(firstDayOfMonth).format("DD MMM YYYY"),
  112. );
  113. const [lastDayOfMonthString, setLastDayOfMonthString] = React.useState(
  114. dayjs(lastDayOfMonth).format("DD MMM YYYY"),
  115. );
  116. const [selectionModel, setSelectionModel]: any[] = React.useState([]);
  117. const [manHoursSpentPeriod, setManHoursSpentPeriod]: any[] = React.useState(
  118. firstDayOfWeekString + " to " + lastDayOfWeekString,
  119. );
  120. const [teamTotalManhoursSpentSelect, setTeamTotalManhoursSpentSelect]: any =
  121. React.useState("Weekly");
  122. const [staffGradeManhoursSpentSelect, setStaffGradeManhoursSpentSelect]: any =
  123. React.useState("Weekly");
  124. const [
  125. individualStaffManhoursSpentSelect,
  126. setIndividualStaffManhoursSpentSelect,
  127. ]: any = React.useState("Daily");
  128. const weekDates: any[] = [];
  129. const monthDates: any[] = [];
  130. const currentDate = dayjs();
  131. const sixMonthsAgo = currentDate.subtract(6, "month");
  132. for (let i = 0; i < 7; i++) {
  133. const currentDate = new Date(firstDayOfWeek);
  134. currentDate.setDate(firstDayOfWeek.getDate() + i);
  135. const formattedDate = dayjs(currentDate).format("DD MMM (ddd)");
  136. weekDates.push(formattedDate);
  137. }
  138. for (
  139. let date = sixMonthsAgo.clone();
  140. date.isBefore(currentDate, "month");
  141. date = date.add(1, "month")
  142. ) {
  143. monthDates.push(date.format("MM-YYYY"));
  144. }
  145. monthDates.push(currentDate.format("MM-YYYY"));
  146. // for (let i = firstDayOfMonth.getDate(); i <= lastDayOfMonth.getDate(); i++) {
  147. // const currentDate = new Date(todayDate.getFullYear(), todayDate.getMonth(), i);
  148. // const formattedDate = dayjs(currentDate).format('DD MMM');
  149. // monthDates.push(formattedDate);
  150. // }
  151. const [teamTotalManhoursSpentPeriod, setTeamTotalManhoursSpentPeriod]: any[] =
  152. React.useState(weekDates);
  153. const [
  154. teamTotalManhoursByStaffGrade,
  155. setTeamTotalManhoursByStaffGrade,
  156. ]: any[] = React.useState(weekDates);
  157. const [
  158. individualStaffManhoursSpentPeriod,
  159. setIndividualStaffManhoursSpentPeriod,
  160. ]: any[] = React.useState(weekDates);
  161. const [
  162. teamTotalManhoursSpentPlanData,
  163. setTeamTotalManhoursSpentPlanData,
  164. ]: any[] = React.useState([42, 42, 42, 42, 42, 0, 0]);
  165. const [
  166. teamTotalManhoursSpentActualData,
  167. setTeamTotalManhoursSpentActualData,
  168. ]: any[] = React.useState([45, 42, 60, 42, 58, 0, 0]);
  169. const [hoveredDay, setHoveredDay] = React.useState<Dayjs | null>(null);
  170. const [value, setValue] = React.useState<Dayjs | null>(dayjs());
  171. const [weeklyValueByStaffGrade, setWeeklyValueByStaffGrade] =
  172. React.useState<Dayjs | null>(dayjs());
  173. const [weeklyValueByIndividualStaff, setWeeklyValueByIndividualStaff] =
  174. React.useState<Dayjs | null>(dayjs());
  175. const [staffGradeManhoursSpentValue, setStaffGradeManhoursSpentValue] =
  176. React.useState<Dayjs | null>(dayjs());
  177. const [totalManHoursMonthlyFromValue, setTotalManHoursMonthlyFromValue] =
  178. React.useState<Dayjs>(dayjs(new Date()).subtract(6, "month"));
  179. const [totalManHoursMonthlyToValue, setTotalManHoursMonthlyToValue] =
  180. React.useState<Dayjs>(dayjs());
  181. const [
  182. totalManHoursByStaffGradeMonthlyFromValue,
  183. setTotalManHoursByStaffGradeMonthlyFromValue,
  184. ] = React.useState<Dayjs>(dayjs(new Date()).subtract(6, "month"));
  185. const [
  186. totalManHoursByStaffGradeMonthlyToValue,
  187. setTotalManHoursByStaffGradeMonthlyToValue,
  188. ] = React.useState<Dayjs>(dayjs());
  189. const [
  190. totalManHoursByIndividualStaffMonthlyFromValue,
  191. setTotalManHoursByIndividualStaffMonthlyFromValue,
  192. ] = React.useState<Dayjs>(dayjs(new Date()).subtract(6, "month"));
  193. const [
  194. totalManHoursByIndividualStaffMonthlyToValue,
  195. setTotalManHoursByIndividualStaffMonthlyToValue,
  196. ] = React.useState<Dayjs>(dayjs());
  197. const [
  198. totalManHoursByIndividualStaffDailyFromValue,
  199. setTotalManHoursByIndividualStaffDailyFromValue,
  200. ] = React.useState<Dayjs>(dayjs(new Date()).subtract(6, "day"));
  201. const [
  202. totalManHoursByIndividualStaffDailyToValue,
  203. setTotalManHoursByIndividualStaffDailyToValue,
  204. ] = React.useState<Dayjs>(dayjs());
  205. const [totalManHoursMaxValue, setTotalManHoursMaxValue] = React.useState(75);
  206. const teamOptions = [
  207. { value: 1, label: "XXX Team" },
  208. { value: 2, label: "YYY Team" },
  209. { value: 3, label: "ZZZ Team" },
  210. ];
  211. const columns = [
  212. {
  213. id: "projectCode",
  214. field: "projectCode",
  215. headerName: "Project Code",
  216. flex: 1,
  217. },
  218. {
  219. id: "projectName",
  220. field: "projectName",
  221. headerName: "Project Name",
  222. flex: 1,
  223. },
  224. {
  225. id: "team",
  226. field: "team",
  227. headerName: "Team",
  228. flex: 1,
  229. },
  230. {
  231. id: "teamLeader",
  232. field: "teamLeader",
  233. headerName: "Team Leader",
  234. flex: 1,
  235. },
  236. {
  237. id: "startDate",
  238. field: "startDate",
  239. headerName: "Start Date",
  240. flex: 1,
  241. },
  242. {
  243. id: "targetEndDate",
  244. field: "targetEndDate",
  245. headerName: "Target End Date",
  246. flex: 1,
  247. },
  248. {
  249. id: "client",
  250. field: "client",
  251. headerName: "Client",
  252. flex: 1,
  253. },
  254. {
  255. id: "subsidiary",
  256. field: "subsidiary",
  257. headerName: "Subsidiary",
  258. flex: 1,
  259. },
  260. ];
  261. const options: ApexOptions = {
  262. chart: {
  263. height: 350,
  264. type: "line",
  265. },
  266. stroke: {
  267. width: [2, 2],
  268. },
  269. plotOptions: {
  270. bar: {
  271. horizontal: false,
  272. distributed: false,
  273. },
  274. },
  275. dataLabels: {
  276. enabled: true,
  277. },
  278. xaxis: {
  279. categories: teamTotalManhoursSpentPeriod,
  280. },
  281. yaxis: [
  282. {
  283. title: {
  284. text: "Team Total Manhours Spent (Hour)",
  285. },
  286. min: 0,
  287. max: totalManHoursMaxValue,
  288. tickAmount: 5,
  289. },
  290. ],
  291. grid: {
  292. borderColor: "#f1f1f1",
  293. },
  294. annotations: {},
  295. series: [
  296. {
  297. name: "Planned",
  298. type: "line",
  299. color: "#efbe7d",
  300. data: teamTotalManhoursSpentPlanData,
  301. },
  302. {
  303. name: "Actual",
  304. type: "line",
  305. color: "#7cd3f2",
  306. data: teamTotalManhoursSpentActualData,
  307. },
  308. ],
  309. };
  310. const staffGradeOptions: ApexOptions = {
  311. chart: {
  312. height: 350,
  313. type: "line",
  314. },
  315. stroke: {
  316. width: [2, 2],
  317. },
  318. plotOptions: {
  319. bar: {
  320. horizontal: true,
  321. distributed: false,
  322. },
  323. },
  324. dataLabels: {
  325. enabled: true,
  326. },
  327. xaxis: {
  328. categories: [
  329. "Grade 1: A. QS / QS Trainee",
  330. "Grade 2: QS",
  331. "Grade 3: Senior QS",
  332. "Grade 4: Manager",
  333. "Grade 5: Director",
  334. ],
  335. },
  336. yaxis: [
  337. {
  338. title: {
  339. text: "Staff Grade",
  340. },
  341. min: 0,
  342. max: 60,
  343. tickAmount: 5,
  344. },
  345. ],
  346. grid: {
  347. borderColor: "#f1f1f1",
  348. },
  349. annotations: {},
  350. series: [
  351. {
  352. name: "Planned",
  353. type: "bar",
  354. color: "#efbe7d",
  355. data: [35, 45, 35, 20, 10],
  356. },
  357. {
  358. name: "Actual",
  359. type: "bar",
  360. color: "#00acb1",
  361. data: [25, 26, 33, 20, 11],
  362. },
  363. ],
  364. };
  365. const individualStaffOptions: ApexOptions = {
  366. chart: {
  367. height: 350,
  368. type: "line",
  369. },
  370. stroke: {
  371. width: [1],
  372. },
  373. plotOptions: {
  374. bar: {
  375. horizontal: true,
  376. distributed: false,
  377. },
  378. },
  379. dataLabels: {
  380. enabled: true,
  381. },
  382. xaxis: {
  383. categories: [
  384. "Consultancy Project 123 (CUST-001, Subsidiary A)",
  385. "Consultancy Project 456 (CUST-001, Subsidiary A)",
  386. "Construction Project A (CUST-001, Subsidiary A)",
  387. "Construction Project B (CUST-001, Subsidiary A)",
  388. "Construction Project C (CUST-001, Subsidiary A)",
  389. ],
  390. },
  391. yaxis: [
  392. {
  393. title: {
  394. text: "Project",
  395. },
  396. min: 0,
  397. max: 12,
  398. tickAmount: 5,
  399. },
  400. ],
  401. grid: {
  402. borderColor: "#f1f1f1",
  403. },
  404. annotations: {},
  405. series: [
  406. {
  407. name: "Manhours(Hour)",
  408. type: "bar",
  409. color: "#00acb1",
  410. data: [12, 12, 11, 12, 0],
  411. },
  412. ],
  413. };
  414. const teamTotalManhoursSpentOnClick = (r: any) => {
  415. setTeamTotalManhoursSpentSelect(r);
  416. if (r === "Weekly") {
  417. setValue(dayjs(new Date()));
  418. setTeamTotalManhoursSpentPeriod(weekDates);
  419. setTeamTotalManhoursSpentPlanData([42, 42, 42, 42, 42, 0, 0]);
  420. setTeamTotalManhoursSpentActualData([45, 42, 60, 42, 58, 0, 0]);
  421. setTotalManHoursMaxValue(75);
  422. } else if (r === "Monthly") {
  423. setTeamTotalManhoursSpentPeriod(monthDates);
  424. setTeamTotalManhoursSpentPlanData([840, 840, 840, 840, 840, 840]);
  425. setTeamTotalManhoursSpentActualData([900, 840, 1200, 840, 1160, 840]);
  426. setTotalManHoursMaxValue(1250);
  427. }
  428. };
  429. const individualStaffManhoursSpentOnClick = (r: any) => {
  430. setIndividualStaffManhoursSpentSelect(r);
  431. // if (r === "Weekly") {
  432. // setValue(dayjs(new Date))
  433. // setTeamTotalManhoursSpentPeriod(weekDates)
  434. // setTeamTotalManhoursSpentPlanData([42,42,42,42,42,0,0])
  435. // setTeamTotalManhoursSpentActualData([45,42,60,42,58,0,0])
  436. // setTotalManHoursMaxValue(75)
  437. // } else if (r === "Monthly") {
  438. // setTeamTotalManhoursSpentPeriod(monthDates)
  439. // setTeamTotalManhoursSpentPlanData([840,840,840,840,840,840])
  440. // setTeamTotalManhoursSpentActualData([900,840,1200,840,1160,840])
  441. // setTotalManHoursMaxValue(1250)
  442. // }
  443. };
  444. const selectWeeklyPeriod = (r: any) => {
  445. const selectDate = new Date(r);
  446. const firstDayOfWeek = new Date();
  447. firstDayOfWeek.setDate(selectDate.getDate() - selectDate.getDay() + 0);
  448. const weekDates: any[] = [];
  449. for (let i = 0; i < 7; i++) {
  450. const currentDate = new Date(firstDayOfWeek);
  451. currentDate.setDate(firstDayOfWeek.getDate() + i);
  452. const formattedDate = dayjs(currentDate).format("DD MMM (ddd)");
  453. weekDates.push(formattedDate);
  454. }
  455. setTeamTotalManhoursSpentPeriod(weekDates);
  456. setValue(dayjs(firstDayOfWeek));
  457. };
  458. const selectWeeklyPeriodByStaffGrade = (r: any) => {
  459. const selectDate = new Date(r);
  460. const firstDayOfWeek = new Date();
  461. firstDayOfWeek.setDate(selectDate.getDate() - selectDate.getDay() + 0);
  462. const weekDates: any[] = [];
  463. for (let i = 0; i < 7; i++) {
  464. const currentDate = new Date(firstDayOfWeek);
  465. currentDate.setDate(firstDayOfWeek.getDate() + i);
  466. const formattedDate = dayjs(currentDate).format("DD MMM (ddd)");
  467. weekDates.push(formattedDate);
  468. }
  469. setTeamTotalManhoursByStaffGrade(weekDates);
  470. setWeeklyValueByStaffGrade(dayjs(firstDayOfWeek));
  471. };
  472. const selectWeeklyPeriodIndividualStaff = (r: any) => {
  473. const selectDate = new Date(r);
  474. const firstDayOfWeek = new Date();
  475. firstDayOfWeek.setDate(selectDate.getDate() - selectDate.getDay() + 0);
  476. const weekDates: any[] = [];
  477. for (let i = 0; i < 7; i++) {
  478. const currentDate = new Date(firstDayOfWeek);
  479. currentDate.setDate(firstDayOfWeek.getDate() + i);
  480. const formattedDate = dayjs(currentDate).format("DD MMM (ddd)");
  481. weekDates.push(formattedDate);
  482. }
  483. setIndividualStaffManhoursSpentPeriod(weekDates);
  484. setWeeklyValueByIndividualStaff(dayjs(firstDayOfWeek));
  485. };
  486. const selectMonthlyPeriodFrom = (r: any) => {
  487. const monthDates: any[] = [];
  488. const monthPlanData: any[] = [];
  489. const monthActualData: any[] = [];
  490. const selectFromDate = dayjs(r);
  491. for (
  492. let date = selectFromDate.clone();
  493. date.isBefore(totalManHoursMonthlyToValue, "month");
  494. date = date.add(1, "month")
  495. ) {
  496. monthDates.push(date.format("MM-YYYY"));
  497. monthPlanData.push(840);
  498. monthActualData.push(Math.floor(Math.random() * (1200 - 840) + 840));
  499. }
  500. monthDates.push(totalManHoursMonthlyToValue.format("MM-YYYY"));
  501. monthPlanData.push(840);
  502. monthActualData.push(Math.floor(Math.random() * (1200 - 840) + 840));
  503. setTeamTotalManhoursSpentPlanData(monthPlanData);
  504. setTeamTotalManhoursSpentActualData(monthActualData);
  505. setTeamTotalManhoursSpentPeriod(monthDates);
  506. };
  507. const selectMonthlyPeriodTo = (r: any) => {
  508. const monthDates: any[] = [];
  509. const monthPlanData: any[] = [];
  510. const monthActualData: any[] = [];
  511. const selectToDate = dayjs(r);
  512. for (
  513. let date = totalManHoursMonthlyFromValue.clone();
  514. date.isBefore(selectToDate, "month");
  515. date = date.add(1, "month")
  516. ) {
  517. monthDates.push(date.format("MM-YYYY"));
  518. monthPlanData.push(840);
  519. monthActualData.push(Math.floor(Math.random() * (1200 - 840) + 840));
  520. }
  521. monthDates.push(selectToDate.format("MM-YYYY"));
  522. monthPlanData.push(840);
  523. monthActualData.push(Math.floor(Math.random() * (1200 - 840) + 840));
  524. setTeamTotalManhoursSpentPlanData(monthPlanData);
  525. setTeamTotalManhoursSpentActualData(monthActualData);
  526. setTeamTotalManhoursSpentPeriod(monthDates);
  527. };
  528. const selectStaffGradeMonthlyPeriodFrom = (r: any) => {
  529. const monthDates: any[] = [];
  530. const monthPlanData: any[] = [];
  531. const monthActualData: any[] = [];
  532. const selectFromDate = dayjs(r);
  533. for (
  534. let date = selectFromDate.clone();
  535. date.isBefore(totalManHoursMonthlyToValue, "month");
  536. date = date.add(1, "month")
  537. ) {
  538. monthDates.push(date.format("MM-YYYY"));
  539. monthPlanData.push(840);
  540. monthActualData.push(Math.floor(Math.random() * (1200 - 840) + 840));
  541. }
  542. monthDates.push(totalManHoursMonthlyToValue.format("MM-YYYY"));
  543. monthPlanData.push(840);
  544. monthActualData.push(Math.floor(Math.random() * (1200 - 840) + 840));
  545. // setTeamTotalManhoursSpentPlanData(monthPlanData)
  546. // setTeamTotalManhoursSpentActualData(monthActualData)
  547. setTeamTotalManhoursByStaffGrade(weekDates);
  548. };
  549. const selectStaffGradeMonthlyPeriodTo = (r: any) => {
  550. const monthDates: any[] = [];
  551. const monthPlanData: any[] = [];
  552. const monthActualData: any[] = [];
  553. const selectToDate = dayjs(r);
  554. for (
  555. let date = totalManHoursMonthlyFromValue.clone();
  556. date.isBefore(selectToDate, "month");
  557. date = date.add(1, "month")
  558. ) {
  559. monthDates.push(date.format("MM-YYYY"));
  560. monthPlanData.push(840);
  561. monthActualData.push(Math.floor(Math.random() * (1200 - 840) + 840));
  562. }
  563. monthDates.push(selectToDate.format("MM-YYYY"));
  564. monthPlanData.push(840);
  565. monthActualData.push(Math.floor(Math.random() * (1200 - 840) + 840));
  566. // setTeamTotalManhoursSpentPlanData(monthPlanData)
  567. // setTeamTotalManhoursSpentActualData(monthActualData)
  568. setTeamTotalManhoursByStaffGrade(weekDates);
  569. };
  570. const selectIndividualStaffMonthlyPeriodFrom = (r: any) => {
  571. const monthDates: any[] = [];
  572. const monthPlanData: any[] = [];
  573. const monthActualData: any[] = [];
  574. const selectFromDate = dayjs(r);
  575. for (
  576. let date = selectFromDate.clone();
  577. date.isBefore(totalManHoursMonthlyToValue, "month");
  578. date = date.add(1, "month")
  579. ) {
  580. monthDates.push(date.format("MM-YYYY"));
  581. monthPlanData.push(840);
  582. monthActualData.push(Math.floor(Math.random() * (1200 - 840) + 840));
  583. }
  584. monthDates.push(totalManHoursMonthlyToValue.format("MM-YYYY"));
  585. monthPlanData.push(840);
  586. monthActualData.push(Math.floor(Math.random() * (1200 - 840) + 840));
  587. // setTeamTotalManhoursSpentPlanData(monthPlanData)
  588. // setTeamTotalManhoursSpentActualData(monthActualData)
  589. setIndividualStaffManhoursSpentPeriod(weekDates);
  590. };
  591. const selectIndividualStaffMonthlyPeriodTo = (r: any) => {
  592. const monthDates: any[] = [];
  593. const monthPlanData: any[] = [];
  594. const monthActualData: any[] = [];
  595. const selectToDate = dayjs(r);
  596. for (
  597. let date = totalManHoursMonthlyFromValue.clone();
  598. date.isBefore(selectToDate, "month");
  599. date = date.add(1, "month")
  600. ) {
  601. monthDates.push(date.format("MM-YYYY"));
  602. monthPlanData.push(840);
  603. monthActualData.push(Math.floor(Math.random() * (1200 - 840) + 840));
  604. }
  605. monthDates.push(selectToDate.format("MM-YYYY"));
  606. monthPlanData.push(840);
  607. monthActualData.push(Math.floor(Math.random() * (1200 - 840) + 840));
  608. // setTeamTotalManhoursSpentPlanData(monthPlanData)
  609. // setTeamTotalManhoursSpentActualData(monthActualData)
  610. setIndividualStaffManhoursSpentPeriod(weekDates);
  611. };
  612. const options2: ApexOptions = {
  613. chart: {
  614. type: "donut",
  615. },
  616. colors: ['#f57f90','#94f7d6','#87c5f5','#ab95f5','#ab95f5'],
  617. plotOptions: {
  618. pie: {
  619. donut: {
  620. labels: {
  621. show: true,
  622. name: {
  623. show: true,
  624. },
  625. value: {
  626. show: false,
  627. fontWeight: 500,
  628. fontSize: "30px",
  629. color: "#3e98c7",
  630. },
  631. total: {
  632. show: false,
  633. showAlways: true,
  634. label: "Spent",
  635. fontFamily: "sans-serif",
  636. formatter: function (val) {
  637. return val + "%";
  638. },
  639. },
  640. },
  641. },
  642. },
  643. },
  644. series:[23.5,25.5,25.5,25.5],
  645. labels: ["Consultancy Project 123","Consultancy Project ABC","Consultancy Project A","Consultancy Project B"],
  646. legend: {
  647. show: false,
  648. },
  649. responsive: [
  650. {
  651. breakpoint: 480,
  652. options: {
  653. chart: {
  654. width: 200,
  655. },
  656. legend: {
  657. position: "bottom",
  658. show: false,
  659. },
  660. },
  661. },
  662. ],
  663. };
  664. return (
  665. <>
  666. <Grid item sm>
  667. <div style={{ display: "inline-block", width: "40%" }}>
  668. <div>
  669. <Grid item xs={12} md={12} lg={12}>
  670. <Card>
  671. <CardHeader
  672. className="text-slate-500"
  673. title="Team Total Manhours Spent"
  674. />
  675. <div style={{ display: "inline-block", width: "99%" }}>
  676. <div className="w-fit align-top mr-5 float-right">
  677. {teamTotalManhoursSpentSelect === "Weekly" && (
  678. <>
  679. <button className="text-lg bg-violet-100 border-violet-500 text-violet-500 border-solid rounded-l-md w-32">
  680. Weekly
  681. </button>
  682. <button
  683. onClick={() =>
  684. teamTotalManhoursSpentOnClick("Monthly")
  685. }
  686. className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-r-md w-32"
  687. >
  688. Monthly
  689. </button>
  690. </>
  691. )}
  692. {teamTotalManhoursSpentSelect === "Monthly" && (
  693. <>
  694. <button
  695. onClick={() =>
  696. teamTotalManhoursSpentOnClick("Weekly")
  697. }
  698. className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-l-md w-32"
  699. >
  700. Weekly
  701. </button>
  702. <button className="text-lg bg-violet-100 border-violet-500 text-violet-500 border-solid rounded-r-md w-32">
  703. Monthly
  704. </button>
  705. </>
  706. )}
  707. </div>
  708. <div className="inline-block w-fit mt-2">
  709. <div className="inline-block ml-6">
  710. <Label className="text-slate-500 font-medium">
  711. Team:&nbsp;
  712. </Label>
  713. </div>
  714. <div className="inline-block ml-1 w-60">
  715. <Select
  716. placeholder="All Team"
  717. options={teamOptions}
  718. isClearable={true}
  719. />
  720. </div>
  721. <div className="ml-6 mt-2" style={{ verticalAlign: "top" }}>
  722. {/* <Label className="text-slate-500 font-medium ml-6">
  723. Period:&nbsp;
  724. </Label> */}
  725. {teamTotalManhoursSpentSelect === "Weekly" && (
  726. <LocalizationProvider dateAdapter={AdapterDayjs}>
  727. <DatePicker
  728. className="w-72 h-10 align-top"
  729. label="Period:"
  730. value={value}
  731. format="DD-MM-YYYY"
  732. onChange={(newValue) =>
  733. selectWeeklyPeriod(newValue)
  734. }
  735. showDaysOutsideCurrentMonth
  736. displayWeekNumber
  737. slots={{ day: Day }}
  738. slotProps={{
  739. day: (ownerState) =>
  740. ({
  741. selectedDay: value,
  742. hoveredDay,
  743. onPointerEnter: () =>
  744. setHoveredDay(ownerState.day),
  745. onPointerLeave: () => setHoveredDay(null),
  746. }) as any,
  747. }}
  748. />
  749. </LocalizationProvider>
  750. )}
  751. {teamTotalManhoursSpentSelect === "Monthly" && (
  752. <LocalizationProvider dateAdapter={AdapterDayjs}>
  753. <DatePicker
  754. className="w-40 h-10 align-top"
  755. onChange={(newValue) =>
  756. selectMonthlyPeriodFrom(newValue)
  757. }
  758. defaultValue={totalManHoursMonthlyFromValue}
  759. label={"Form"}
  760. views={["month", "year"]}
  761. />
  762. <DatePicker
  763. className="w-40 h-10 align-top"
  764. onChange={(newValue) =>
  765. selectMonthlyPeriodTo(newValue)
  766. }
  767. defaultValue={totalManHoursMonthlyToValue}
  768. label={"To"}
  769. views={["month", "year"]}
  770. />
  771. </LocalizationProvider>
  772. )}
  773. </div>
  774. </div>
  775. <ReactApexChart
  776. options={options}
  777. series={options.series}
  778. type="line"
  779. height="400"
  780. />
  781. </div>
  782. </Card>
  783. </Grid>
  784. </div>
  785. <div className="mt-5">
  786. <Grid item xs={12} md={12} lg={12}>
  787. <Card>
  788. <CardHeader
  789. className="text-slate-500"
  790. title="Total Manhours Spent by Staff Grade"
  791. />
  792. <div style={{ display: "inline-block", width: "99%" }}>
  793. <div className="w-fit align-top mr-5 float-right">
  794. {staffGradeManhoursSpentSelect === "Weekly" && (
  795. <>
  796. <button className="text-lg bg-violet-100 border-violet-500 text-violet-500 border-solid rounded-l-md w-32">
  797. Weekly
  798. </button>
  799. <button
  800. onClick={() =>
  801. setStaffGradeManhoursSpentSelect("Monthly")
  802. }
  803. className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-r-md w-48"
  804. >
  805. Monthly
  806. </button>
  807. </>
  808. )}
  809. {staffGradeManhoursSpentSelect === "Monthly" && (
  810. <>
  811. <button
  812. onClick={() =>
  813. setStaffGradeManhoursSpentSelect("Weekly")
  814. }
  815. className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-l-md w-32"
  816. >
  817. Weekly
  818. </button>
  819. <button className="text-lg bg-violet-100 border-violet-500 text-violet-500 border-solid rounded-r-md w-48">
  820. Monthly
  821. </button>
  822. </>
  823. )}
  824. </div>
  825. <div className="inline-block w-fit mt-2">
  826. <div className="inline-block mt-2 ml-6">
  827. {staffGradeManhoursSpentSelect === "Weekly" && (
  828. <LocalizationProvider dateAdapter={AdapterDayjs}>
  829. <DatePicker
  830. className="w-72 h-10 align-top"
  831. label="Period:"
  832. value={value}
  833. format="DD-MM-YYYY"
  834. onChange={(newValue) =>
  835. selectWeeklyPeriodByStaffGrade(newValue)
  836. }
  837. showDaysOutsideCurrentMonth
  838. displayWeekNumber
  839. slots={{ day: Day }}
  840. slotProps={{
  841. day: (ownerState) =>
  842. ({
  843. selectedDay: value,
  844. hoveredDay,
  845. onPointerEnter: () =>
  846. setHoveredDay(ownerState.day),
  847. onPointerLeave: () => setHoveredDay(null),
  848. }) as any,
  849. }}
  850. />
  851. </LocalizationProvider>
  852. )}
  853. {staffGradeManhoursSpentSelect === "Monthly" && (
  854. <LocalizationProvider dateAdapter={AdapterDayjs}>
  855. <DatePicker
  856. className="w-40 h-10 align-top"
  857. onChange={(newValue) =>
  858. selectStaffGradeMonthlyPeriodFrom(newValue)
  859. }
  860. defaultValue={
  861. totalManHoursByStaffGradeMonthlyFromValue
  862. }
  863. label={"Form"}
  864. views={["month", "year"]}
  865. />
  866. <DatePicker
  867. className="w-40 h-10 align-top"
  868. onChange={(newValue) =>
  869. selectStaffGradeMonthlyPeriodTo(newValue)
  870. }
  871. defaultValue={
  872. totalManHoursByStaffGradeMonthlyToValue
  873. }
  874. label={"To"}
  875. views={["month", "year"]}
  876. />
  877. </LocalizationProvider>
  878. )}
  879. {/* <Label className="text-slate-500 font-medium ml-6">
  880. Period:&nbsp;
  881. </Label>
  882. <Input
  883. id={'cashFlowYear'}
  884. value={manHoursSpentPeriod}
  885. readOnly={true}
  886. bsSize="lg"
  887. className="rounded-md text-base w-56 border-slate-200 border-solid text-slate-500 text-center"
  888. /> */}
  889. </div>
  890. </div>
  891. <ReactApexChart
  892. options={staffGradeOptions}
  893. series={staffGradeOptions.series}
  894. type="bar"
  895. height="400"
  896. />
  897. </div>
  898. </Card>
  899. </Grid>
  900. </div>
  901. </div>
  902. <div
  903. style={{
  904. display: "inline-block",
  905. width: "59%",
  906. verticalAlign: "top",
  907. marginLeft: 10,
  908. }}
  909. >
  910. <Grid item xs={12} md={12} lg={12}>
  911. <Card>
  912. <Card>
  913. <CardHeader
  914. className="text-slate-500"
  915. title="Manhours Spent by Individual Staff"
  916. />
  917. <div style={{ display: "inline-block", width: "99%" }}>
  918. <div className="w-fit align-top mr-5 float-right">
  919. {individualStaffManhoursSpentSelect === "Daily" && (
  920. <>
  921. <button className="text-lg bg-violet-100 border-violet-500 text-violet-500 border-solid rounded-l-md w-32">
  922. Daily
  923. </button>
  924. <button
  925. onClick={() =>
  926. individualStaffManhoursSpentOnClick("Weekly")
  927. }
  928. className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid w-32"
  929. >
  930. Weekly
  931. </button>
  932. <button
  933. onClick={() =>
  934. individualStaffManhoursSpentOnClick("Monthly")
  935. }
  936. className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-r-md w-32"
  937. >
  938. Monthly
  939. </button>
  940. </>
  941. )}
  942. {individualStaffManhoursSpentSelect === "Weekly" && (
  943. <>
  944. <button
  945. onClick={() =>
  946. individualStaffManhoursSpentOnClick("Daily")
  947. }
  948. className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-l-md w-32"
  949. >
  950. Daily
  951. </button>
  952. <button className="text-lg bg-violet-100 border-violet-500 text-violet-500 border-solid w-32">
  953. Weekly
  954. </button>
  955. <button
  956. onClick={() =>
  957. individualStaffManhoursSpentOnClick("Monthly")
  958. }
  959. className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-r-md w-32"
  960. >
  961. Monthly
  962. </button>
  963. </>
  964. )}
  965. {individualStaffManhoursSpentSelect === "Monthly" && (
  966. <>
  967. <button
  968. onClick={() =>
  969. individualStaffManhoursSpentOnClick("Daily")
  970. }
  971. className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-l-md w-32"
  972. >
  973. Daily
  974. </button>
  975. <button
  976. onClick={() =>
  977. individualStaffManhoursSpentOnClick("Weekly")
  978. }
  979. className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid w-32"
  980. >
  981. Weekly
  982. </button>
  983. <button className="text-lg bg-violet-100 border-violet-500 text-violet-500 border-solid rounded-r-md w-32">
  984. Monthly
  985. </button>
  986. </>
  987. )}
  988. </div>
  989. <div className="inline-block w-fit mt-2">
  990. <div className="inline-block ml-6">
  991. <Label className="text-slate-500 font-medium">
  992. Staff Code and Name:&nbsp;
  993. </Label>
  994. </div>
  995. <div className="inline-block ml-1 w-60">
  996. <Select
  997. placeholder="00338-Chris Wong"
  998. options={teamOptions}
  999. isClearable={true}
  1000. />
  1001. </div>
  1002. <div className="ml-6 mt-2" style={{ verticalAlign: "top" }}>
  1003. {/* <Label className="text-slate-500 font-medium ml-6">
  1004. Period:&nbsp;
  1005. </Label> */}
  1006. {individualStaffManhoursSpentSelect === "Daily" && (
  1007. <LocalizationProvider dateAdapter={AdapterDayjs}>
  1008. <DatePicker
  1009. className="w-40 h-10 align-top"
  1010. onChange={(newValue) =>
  1011. selectIndividualStaffMonthlyPeriodFrom(newValue)
  1012. }
  1013. defaultValue={
  1014. totalManHoursByIndividualStaffDailyFromValue
  1015. }
  1016. label={"Form"}
  1017. views={["day", "month", "year"]}
  1018. />
  1019. <DatePicker
  1020. className="w-40 h-10 align-top"
  1021. onChange={(newValue) =>
  1022. selectIndividualStaffMonthlyPeriodTo(newValue)
  1023. }
  1024. defaultValue={
  1025. totalManHoursByIndividualStaffDailyToValue
  1026. }
  1027. label={"To"}
  1028. views={["day", "month", "year"]}
  1029. />
  1030. </LocalizationProvider>
  1031. )}
  1032. {individualStaffManhoursSpentSelect === "Weekly" && (
  1033. <LocalizationProvider dateAdapter={AdapterDayjs}>
  1034. <DatePicker
  1035. className="w-72 h-10 align-top"
  1036. label="Period:"
  1037. value={value}
  1038. format="DD-MM-YYYY"
  1039. onChange={(newValue) =>
  1040. selectWeeklyPeriodIndividualStaff(newValue)
  1041. }
  1042. showDaysOutsideCurrentMonth
  1043. displayWeekNumber
  1044. slots={{ day: Day }}
  1045. slotProps={{
  1046. day: (ownerState) =>
  1047. ({
  1048. selectedDay: value,
  1049. hoveredDay,
  1050. onPointerEnter: () =>
  1051. setHoveredDay(ownerState.day),
  1052. onPointerLeave: () => setHoveredDay(null),
  1053. }) as any,
  1054. }}
  1055. />
  1056. </LocalizationProvider>
  1057. )}
  1058. {individualStaffManhoursSpentSelect === "Monthly" && (
  1059. <LocalizationProvider dateAdapter={AdapterDayjs}>
  1060. <DatePicker
  1061. className="w-40 h-10 align-top"
  1062. onChange={(newValue) =>
  1063. selectIndividualStaffMonthlyPeriodFrom(newValue)
  1064. }
  1065. defaultValue={
  1066. totalManHoursByIndividualStaffMonthlyFromValue
  1067. }
  1068. label={"Form"}
  1069. views={["month", "year"]}
  1070. />
  1071. <DatePicker
  1072. className="w-40 h-10 align-top"
  1073. onChange={(newValue) =>
  1074. selectIndividualStaffMonthlyPeriodTo(newValue)
  1075. }
  1076. defaultValue={
  1077. totalManHoursByIndividualStaffMonthlyToValue
  1078. }
  1079. label={"To"}
  1080. views={["month", "year"]}
  1081. />
  1082. </LocalizationProvider>
  1083. )}
  1084. </div>
  1085. </div>
  1086. <ReactApexChart
  1087. options={individualStaffOptions}
  1088. series={individualStaffOptions.series}
  1089. type="bar"
  1090. height="380"
  1091. />
  1092. </div>
  1093. </Card>
  1094. <div style={{ display: "inline-block", width: "50%" }}>
  1095. <div
  1096. style={{
  1097. display: "inline-block",
  1098. width: "47%",
  1099. marginTop: 10,
  1100. marginLeft: 10,
  1101. }}
  1102. >
  1103. <Card style={{ height: 90 }}>
  1104. <div className="text-slate-500 text-center text-base">
  1105. Total Normal Hours Spent
  1106. </div>
  1107. <div
  1108. className="text-center w-full text-3xl font-bold"
  1109. style={{ color: "#92c1e9" }}
  1110. >
  1111. 40.00
  1112. </div>
  1113. </Card>
  1114. <Card style={{ marginTop: 10, height: 90 }}>
  1115. <div className="text-slate-500 text-center text-base">
  1116. Total Leave Hours
  1117. </div>
  1118. <div
  1119. className="text-center w-full text-3xl font-bold"
  1120. style={{ color: "#898d8d", marginTop: 10 }}
  1121. >
  1122. 0.00
  1123. </div>
  1124. </Card>
  1125. </div>
  1126. <div
  1127. style={{
  1128. display: "inline-block",
  1129. width: "47%",
  1130. marginTop: 10,
  1131. marginLeft: 10,
  1132. }}
  1133. >
  1134. <Card style={{ height: 90 }}>
  1135. <div className="text-slate-500 text-center text-base">
  1136. Total Other Hours Spent
  1137. </div>
  1138. <div
  1139. className="text-center w-full text-3xl font-bold"
  1140. style={{ color: "#92c1e9" }}
  1141. >
  1142. 7.00
  1143. </div>
  1144. </Card>
  1145. <Card style={{ marginTop: 10, height: 90 }}>
  1146. <div className="text-slate-500 text-center text-base">
  1147. Remaining Hours
  1148. </div>
  1149. <div
  1150. className="text-center w-full text-3xl font-bold"
  1151. style={{ color: "#898d8d", marginTop: 10 }}
  1152. >
  1153. 0.00
  1154. </div>
  1155. </Card>
  1156. </div>
  1157. </div>
  1158. <div style={{ display: "inline-block", width: "50%",verticalAlign:"top",marginTop:10}}>
  1159. <Card>
  1160. <CardHeader className="text-slat-500" title="Effort Proportion for individual Staff"/>
  1161. <ReactApexChart
  1162. options={options2}
  1163. series={options2.series}
  1164. type="donut"
  1165. />
  1166. </Card>
  1167. </div>
  1168. </Card>
  1169. </Grid>
  1170. </div>
  1171. </Grid>
  1172. </>
  1173. );
  1174. };
  1175. export default StaffUtilization;