Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 

412 linhas
13 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 { CardFooter, Input, Label } from "reactstrap";
  23. import Select, { components } from "react-select";
  24. import { fetchTeamCombo, fetchTeamCashFlowChartData } from "@/app/api/teamCashflow";
  25. import { VIEW_DASHBOARD_ALL } from "@/middleware";
  26. import { SessionStaff } from "@/config/authConfig";
  27. import Typography from "@mui/material/Typography";
  28. interface Props {
  29. abilities: string[],
  30. staff: SessionStaff,
  31. }
  32. const CompanyTeamCashFlow: React.FC<Props> = ({ abilities, staff }) => {
  33. const { t } = useTranslation("dashboard");
  34. const todayDate = new Date();
  35. const [selectionModel, setSelectionModel]: any[] = React.useState([]);
  36. const [teamOptions, setTeamOptions]: any[] = React.useState([]);
  37. const [teamId, setTeamId]: any = React.useState(null);
  38. const [monthlyIncomeList, setMonthlyIncomeList]: any[] = React.useState([]);
  39. const [monthlyCumulativeIncomeList, setMonthlyCumulativeIncomeList]: any[] = React.useState([]);
  40. const [monthlyExpenditureList, setMonthlyExpenditureList]: any[] = React.useState([]);
  41. const [monthlyCumulativeExpenditureList, setMonthlyCumulativeExpenditureList]: any[] = React.useState([]);
  42. const [monthlyChartLeftMax, setMonthlyChartLeftMax]: any[] = React.useState(0);
  43. const [monthlyChartRightMax, setMonthlyChartRightMax]: any[] = React.useState(0);
  44. const [cashFlowYear, setCashFlowYear]: any[] = React.useState(
  45. todayDate.getFullYear(),
  46. );
  47. const fetchChartData = async () => {
  48. if (teamOptions.length > 0) {
  49. const tempTeamId = abilities.includes(VIEW_DASHBOARD_ALL) ? teamId : teamOptions[0]?.value
  50. const cashFlowMonthlyChartData: any = await fetchTeamCashFlowChartData(cashFlowYear, tempTeamId);
  51. console.log(cashFlowMonthlyChartData[0])
  52. const monthlyIncome = []
  53. const cumulativeIncome = []
  54. const monthlyExpenditure = []
  55. const cumulativeExpenditure = []
  56. var leftMax = 0
  57. var rightMax = 0
  58. // if (cashFlowMonthlyChartData.length !== 0) {
  59. for (var i = 0; i < cashFlowMonthlyChartData[0].teamCashFlowIncome.length; i++) {
  60. if (leftMax < cashFlowMonthlyChartData[0].teamCashFlowIncome[i].income || leftMax < cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].expenditure) {
  61. leftMax = Math.max(cashFlowMonthlyChartData[0].teamCashFlowIncome[i].income, cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].expenditure)
  62. }
  63. monthlyIncome.push(cashFlowMonthlyChartData[0].teamCashFlowIncome[i].income)
  64. cumulativeIncome.push(cashFlowMonthlyChartData[0].teamCashFlowIncome[i].cumulativeIncome)
  65. }
  66. for (var i = 0; i < cashFlowMonthlyChartData[0].teamCashFlowExpenditure.length; i++) {
  67. if (rightMax < cashFlowMonthlyChartData[0].teamCashFlowIncome[i].cumulativeIncome || rightMax < cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].cumulativeExpenditure) {
  68. rightMax = Math.max(cashFlowMonthlyChartData[0].teamCashFlowIncome[i].cumulativeIncome, cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].cumulativeExpenditure)
  69. }
  70. monthlyExpenditure.push(cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].expenditure)
  71. cumulativeExpenditure.push(cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].cumulativeExpenditure)
  72. }
  73. setMonthlyIncomeList(monthlyIncome)
  74. setMonthlyCumulativeIncomeList(cumulativeIncome)
  75. setMonthlyExpenditureList(monthlyExpenditure)
  76. setMonthlyCumulativeExpenditureList(cumulativeExpenditure)
  77. setMonthlyChartLeftMax(leftMax)
  78. setMonthlyChartRightMax(rightMax)
  79. // } else {
  80. // setMonthlyIncomeList([0,0,0,0,0,0,0,0,0,0,0,0])
  81. // setMonthlyCumulativeIncomeList([0,0,0,0,0,0,0,0,0,0,0,0])
  82. // setMonthlyExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0])
  83. // setMonthlyCumulativeExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0])
  84. // }
  85. }
  86. }
  87. const fetchComboData = async () => {
  88. const teamComboList = []
  89. const teamCombo = await fetchTeamCombo();
  90. if (abilities.includes(VIEW_DASHBOARD_ALL)) {
  91. for (var i = 0; i < teamCombo.records.length; i++) {
  92. teamComboList.push({ value: teamCombo.records[i].id, label: teamCombo.records[i].label })
  93. }
  94. } else {
  95. const tempTeam = teamCombo.records.find(record => record.id === staff.teamId)
  96. teamComboList.push({ value: tempTeam?.id, label: tempTeam?.label })
  97. }
  98. setTeamOptions(teamComboList)
  99. }
  100. useEffect(() => {
  101. fetchComboData()
  102. }, []);
  103. useEffect(() => {
  104. fetchChartData()
  105. }, [cashFlowYear, teamId, teamOptions]);
  106. const columns = [
  107. {
  108. id: "projectCode",
  109. field: "projectCode",
  110. headerName: "Project Code",
  111. flex: 1,
  112. },
  113. {
  114. id: "projectName",
  115. field: "projectName",
  116. headerName: "Project Name",
  117. flex: 1,
  118. },
  119. {
  120. id: "team",
  121. field: "team",
  122. headerName: "Team",
  123. flex: 1,
  124. },
  125. {
  126. id: "teamLeader",
  127. field: "teamLeader",
  128. headerName: "Team Leader",
  129. flex: 1,
  130. },
  131. {
  132. id: "startDate",
  133. field: "startDate",
  134. headerName: "Start Date",
  135. flex: 1,
  136. },
  137. {
  138. id: "targetEndDate",
  139. field: "targetEndDate",
  140. headerName: "Target End Date",
  141. flex: 1,
  142. },
  143. {
  144. id: "client",
  145. field: "client",
  146. headerName: "Client",
  147. flex: 1,
  148. },
  149. {
  150. id: "subsidiary",
  151. field: "subsidiary",
  152. headerName: "Subsidiary",
  153. flex: 1,
  154. },
  155. ];
  156. const ledgerColumns = [
  157. {
  158. id: "date",
  159. field: "date",
  160. headerName: "Date",
  161. flex: 0.5,
  162. },
  163. {
  164. id: "expenditure",
  165. field: "expenditure",
  166. headerName: "Expenditure (HKD)",
  167. flex: 0.6,
  168. },
  169. {
  170. id: "income",
  171. field: "income",
  172. headerName: "Income (HKD)",
  173. flex: 0.6,
  174. },
  175. {
  176. id: "cashFlowBalance",
  177. field: "cashFlowBalance",
  178. headerName: "Cash Flow Balance (HKD)",
  179. flex: 0.6,
  180. },
  181. {
  182. id: "remarks",
  183. field: "remarks",
  184. headerName: "Remarks",
  185. flex: 1,
  186. },
  187. ];
  188. const options: ApexOptions = {
  189. tooltip: {
  190. y: {
  191. formatter: function (val) {
  192. return val.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
  193. }
  194. }
  195. },
  196. chart: {
  197. height: 350,
  198. type: "line",
  199. },
  200. stroke: {
  201. width: [0, 0, 2, 2],
  202. },
  203. plotOptions: {
  204. bar: {
  205. horizontal: false,
  206. distributed: false,
  207. },
  208. },
  209. dataLabels: {
  210. enabled: false,
  211. },
  212. xaxis: {
  213. categories: [
  214. t("JAN"),
  215. t("FEB"),
  216. t("MAR"),
  217. t("APR"),
  218. t("MAY"),
  219. t("JUN"),
  220. t("JUL"),
  221. t("AUG"),
  222. t("SEP"),
  223. t("OCT"),
  224. t("NOV"),
  225. t("DEC"),
  226. ],
  227. },
  228. yaxis: [
  229. {
  230. title: {
  231. text:t("Monthly Income and Expenditure (HKD)"),
  232. style: {
  233. fontSize: '15px'
  234. }
  235. },
  236. min: 0,
  237. max: monthlyChartLeftMax,
  238. tickAmount: 5,
  239. labels: {
  240. formatter: function (val) {
  241. return val.toLocaleString()
  242. }
  243. }
  244. },
  245. {
  246. show: false,
  247. seriesName: "Monthly Expenditure",
  248. title: {
  249. text: t("Monthly Expenditure (HKD)"),
  250. },
  251. min: 0,
  252. max: monthlyChartLeftMax,
  253. tickAmount: 5,
  254. },
  255. {
  256. seriesName: "Cumulative Income",
  257. opposite: true,
  258. title: {
  259. text: t("Cumulative Income and Expenditure (HKD)"),
  260. style: {
  261. fontSize: '15px'
  262. }
  263. },
  264. min: 0,
  265. max: monthlyChartRightMax,
  266. tickAmount: 5,
  267. labels: {
  268. formatter: function (val) {
  269. return val.toLocaleString()
  270. }
  271. }
  272. },
  273. {
  274. show: false,
  275. seriesName: "Cumulative Expenditure",
  276. opposite: true,
  277. title: {
  278. text: t("Cumulative Expenditure (HKD)"),
  279. },
  280. min: 0,
  281. max: monthlyChartRightMax,
  282. tickAmount: 5,
  283. },
  284. ],
  285. grid: {
  286. borderColor: "#f1f1f1",
  287. },
  288. annotations: {},
  289. series: [
  290. {
  291. name: t("Monthly Income"),
  292. type: "column",
  293. color: "#ffde91",
  294. data: monthlyIncomeList,
  295. },
  296. {
  297. name: t("Monthly Expenditure"),
  298. type: "column",
  299. color: "#82b59a",
  300. data: monthlyExpenditureList,
  301. },
  302. {
  303. name: t("Cumulative Income"),
  304. type: "line",
  305. color: "#EE6D7A",
  306. data: monthlyCumulativeIncomeList,
  307. },
  308. {
  309. name: t("Cumulative Expenditure"),
  310. type: "line",
  311. color: "#7cd3f2",
  312. data: monthlyCumulativeExpenditureList,
  313. },
  314. ],
  315. };
  316. return (
  317. <>
  318. <Grid item sm>
  319. <Typography variant="h4" marginInlineEnd={2}>
  320. {t("Company / Team Cash Flow")}
  321. </Typography>
  322. <div style={{ display: "inline-block", width: "100%" }} className={"mt-5"}>
  323. <Grid item xs={12} md={12} lg={12}>
  324. <Card>
  325. <CardHeader
  326. className="text-slate-500"
  327. title={t("Company and Team Cash Flow By Month")}
  328. />
  329. <div style={{ display: "inline-block", width: "99%" }}>
  330. <div className="inline-block">
  331. <Label className="text-slate-500 font-medium ml-6">
  332. {t("Year")}:&nbsp;
  333. </Label>
  334. <Input
  335. id={"cashFlowYear"}
  336. value={cashFlowYear}
  337. readOnly={true}
  338. bsSize="lg"
  339. className="rounded-md text-base w-12"
  340. />
  341. </div>
  342. <div className="inline-block ml-1">
  343. <button
  344. onClick={() => setCashFlowYear(cashFlowYear - 1)}
  345. className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base"
  346. >
  347. &lt;
  348. </button>
  349. </div>
  350. <div className="inline-block ml-1">
  351. <button
  352. onClick={() => setCashFlowYear(cashFlowYear + 1)}
  353. className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base"
  354. >
  355. &gt;
  356. </button>
  357. </div>
  358. {abilities.includes(VIEW_DASHBOARD_ALL) && <div className="inline-block">
  359. <div className="inline-block ml-2">
  360. <Label className="text-slate-500 font-medium">
  361. {t("Team")}:&nbsp;
  362. </Label>
  363. </div>
  364. <div className="inline-block ml-1 w-60">
  365. <Select
  366. placeholder={t("All Team")}
  367. options={teamOptions}
  368. isClearable={true}
  369. onChange={(selectedOption: any) => {
  370. if (selectedOption === null) {
  371. setTeamId(null);
  372. } else {
  373. setTeamId(selectedOption.value);
  374. }
  375. }}
  376. />
  377. </div>
  378. </div>}
  379. <ReactApexChart
  380. options={options}
  381. series={options.series}
  382. type="line"
  383. height="500"
  384. />
  385. </div>
  386. <CardFooter>
  387. <Grid sx={{ ml: 2, mb: 2, fontSize: '0.8rem' }}>
  388. * Cumulative Expenditure: Manpower Expense + Other Expense
  389. </Grid>
  390. </CardFooter>
  391. </Card>
  392. </Grid>
  393. </div>
  394. </Grid>
  395. </>
  396. );
  397. };
  398. export default CompanyTeamCashFlow;