Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 

801 rader
20 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. const ProjectCashFlow: React.FC = () => {
  24. const todayDate = new Date();
  25. const [selectionModel, setSelectionModel]: any[] = React.useState([]);
  26. const [cashFlowYear, setCashFlowYear]: any[] = React.useState(
  27. todayDate.getFullYear(),
  28. );
  29. const [anticipateCashFlowYear, setAnticipateCashFlowYear]: any[] = React.useState(
  30. todayDate.getFullYear(),
  31. );
  32. const columns = [
  33. {
  34. id: "projectCode",
  35. field: "projectCode",
  36. headerName: "Project Code",
  37. flex: 1,
  38. },
  39. {
  40. id: "projectName",
  41. field: "projectName",
  42. headerName: "Project Name",
  43. flex: 1,
  44. },
  45. {
  46. id: "team",
  47. field: "team",
  48. headerName: "Team",
  49. flex: 1,
  50. },
  51. {
  52. id: "teamLeader",
  53. field: "teamLeader",
  54. headerName: "Team Leader",
  55. flex: 1,
  56. },
  57. {
  58. id: "startDate",
  59. field: "startDate",
  60. headerName: "Start Date",
  61. flex: 1,
  62. },
  63. {
  64. id: "targetEndDate",
  65. field: "targetEndDate",
  66. headerName: "Target End Date",
  67. flex: 1,
  68. },
  69. {
  70. id: "client",
  71. field: "client",
  72. headerName: "Client",
  73. flex: 1,
  74. },
  75. {
  76. id: "subsidiary",
  77. field: "subsidiary",
  78. headerName: "Subsidiary",
  79. flex: 1,
  80. },
  81. ];
  82. const ledgerColumns = [
  83. {
  84. id: "date",
  85. field: "date",
  86. headerName: "Date",
  87. flex: 0.5,
  88. },
  89. {
  90. id: "expenditure",
  91. field: "expenditure",
  92. headerName: "Expenditure (HKD)",
  93. flex: 0.6,
  94. },
  95. {
  96. id: "income",
  97. field: "income",
  98. headerName: "Income (HKD)",
  99. flex: 0.6,
  100. },
  101. {
  102. id: "cashFlowBalance",
  103. field: "cashFlowBalance",
  104. headerName: "Cash Flow Balance (HKD)",
  105. flex: 0.6,
  106. },
  107. {
  108. id: "remarks",
  109. field: "remarks",
  110. headerName: "Remarks",
  111. flex: 1,
  112. },
  113. ];
  114. const options: ApexOptions = {
  115. chart: {
  116. height: 350,
  117. type: "line",
  118. },
  119. stroke: {
  120. width: [0, 0, 2, 2],
  121. },
  122. plotOptions: {
  123. bar: {
  124. horizontal: false,
  125. distributed: false,
  126. },
  127. },
  128. dataLabels: {
  129. enabled: false,
  130. },
  131. xaxis: {
  132. categories: [
  133. "Q1",
  134. "Q2",
  135. "Q3",
  136. "Q4",
  137. "Q5",
  138. "Q6",
  139. "Q7",
  140. "Q8",
  141. "Q9",
  142. "Q10",
  143. "Q11",
  144. "Q12",
  145. ],
  146. },
  147. yaxis: [
  148. {
  149. title: {
  150. text: "Monthly Income and Expenditure(HKD)",
  151. },
  152. min: 0,
  153. max: 350000,
  154. tickAmount: 5,
  155. labels: {
  156. formatter: function (val) {
  157. return val.toLocaleString()
  158. }
  159. }
  160. },
  161. {
  162. show: false,
  163. seriesName: "Monthly_Expenditure",
  164. title: {
  165. text: "Monthly Expenditure (HKD)",
  166. },
  167. min: 0,
  168. max: 350000,
  169. tickAmount: 5,
  170. },
  171. {
  172. seriesName: "Cumulative_Income",
  173. opposite: true,
  174. title: {
  175. text: "Cumulative Income and Expenditure(HKD)",
  176. },
  177. min: 0,
  178. max: 850000,
  179. tickAmount: 5,
  180. labels: {
  181. formatter: function (val) {
  182. return val.toLocaleString()
  183. }
  184. }
  185. },
  186. {
  187. show: false,
  188. seriesName: "Cumulative_Expenditure",
  189. opposite: true,
  190. title: {
  191. text: "Cumulative Expenditure (HKD)",
  192. },
  193. min: 0,
  194. max: 850000,
  195. tickAmount: 5,
  196. },
  197. ],
  198. grid: {
  199. borderColor: "#f1f1f1",
  200. },
  201. annotations: {},
  202. series: [
  203. {
  204. name: "Monthly_Income",
  205. type: "column",
  206. color: "#ffde91",
  207. data: [0, 110000, 0, 0, 185000, 0, 0, 189000, 0, 0, 300000, 0],
  208. },
  209. {
  210. name: "Monthly_Expenditure",
  211. type: "column",
  212. color: "#82b59a",
  213. data: [
  214. 0, 160000, 120000, 120000, 55000, 55000, 55000, 55000, 55000, 70000,
  215. 55000, 55000,
  216. ],
  217. },
  218. {
  219. name: "Cumulative_Income",
  220. type: "line",
  221. color: "#EE6D7A",
  222. data: [
  223. 0, 100000, 100000, 100000, 300000, 300000, 300000, 500000, 500000,
  224. 500000, 800000, 800000,
  225. ],
  226. },
  227. {
  228. name: "Cumulative_Expenditure",
  229. type: "line",
  230. color: "#7cd3f2",
  231. data: [
  232. 0, 198000, 240000, 400000, 410000, 430000, 510000, 580000, 600000,
  233. 710000, 730000, 790000,
  234. ],
  235. },
  236. ],
  237. };
  238. const anticipateOptions: ApexOptions = {
  239. chart: {
  240. height: 350,
  241. type: "line",
  242. },
  243. stroke: {
  244. width: [0, 0, 2, 2],
  245. },
  246. plotOptions: {
  247. bar: {
  248. horizontal: false,
  249. distributed: false,
  250. },
  251. },
  252. dataLabels: {
  253. enabled: false,
  254. },
  255. xaxis: {
  256. categories: [
  257. "Q1",
  258. "Q2",
  259. "Q3",
  260. "Q4",
  261. "Q5",
  262. "Q6",
  263. "Q7",
  264. "Q8",
  265. "Q9",
  266. "Q10",
  267. "Q11",
  268. "Q12",
  269. ],
  270. },
  271. yaxis: [
  272. {
  273. title: {
  274. text: "Anticipate Monthly Income and Expenditure(HKD)",
  275. },
  276. min: 0,
  277. max: 350000,
  278. tickAmount: 5,
  279. labels: {
  280. formatter: function (val) {
  281. return val.toLocaleString()
  282. }
  283. }
  284. },
  285. {
  286. show: false,
  287. seriesName: "Monthly_Expenditure",
  288. title: {
  289. text: "Monthly Expenditure (HKD)",
  290. },
  291. min: 0,
  292. max: 350000,
  293. tickAmount: 5,
  294. },
  295. {
  296. seriesName: "Cumulative_Income",
  297. opposite: true,
  298. title: {
  299. text: "Cumulative Income and Expenditure(HKD)",
  300. },
  301. min: 0,
  302. max: 850000,
  303. tickAmount: 5,
  304. labels: {
  305. formatter: function (val) {
  306. return val.toLocaleString()
  307. }
  308. }
  309. },
  310. {
  311. show: false,
  312. seriesName: "Cumulative_Expenditure",
  313. opposite: true,
  314. title: {
  315. text: "Cumulative Expenditure (HKD)",
  316. },
  317. min: 0,
  318. max: 850000,
  319. tickAmount: 5,
  320. },
  321. ],
  322. grid: {
  323. borderColor: "#f1f1f1",
  324. },
  325. annotations: {},
  326. series: [
  327. {
  328. name: "Monthly_Income",
  329. type: "column",
  330. color: "#f1c48a",
  331. data: [0, 110000, 0, 0, 185000, 0, 0, 189000, 0, 0, 300000, 0],
  332. },
  333. {
  334. name: "Monthly_Expenditure",
  335. type: "column",
  336. color: "#89d7f3",
  337. data: [
  338. 60000, 60000, 60000, 60000, 60000, 60000, 60000, 60000, 60000, 60000,
  339. 60000, 60000,
  340. ],
  341. }
  342. ],
  343. };
  344. const accountsReceivableOptions: ApexOptions = {
  345. colors: ["#20E647"],
  346. series: [80],
  347. chart: {
  348. height: 350,
  349. type: "radialBar",
  350. },
  351. plotOptions: {
  352. radialBar: {
  353. hollow: {
  354. size: "70%",
  355. background: "#ffffff",
  356. },
  357. track: {
  358. dropShadow: {
  359. enabled: true,
  360. top: 2,
  361. left: 0,
  362. blur: 4,
  363. opacity: 0.15,
  364. },
  365. },
  366. dataLabels: {
  367. name: {
  368. show: false,
  369. },
  370. value: {
  371. color: "#3e98c7",
  372. fontSize: "3em",
  373. show: true,
  374. },
  375. },
  376. },
  377. },
  378. fill: {
  379. type: "gradient",
  380. gradient: {
  381. shade: "dark",
  382. type: "vertical",
  383. gradientToColors: ["#87D4F9"],
  384. stops: [0, 100],
  385. },
  386. },
  387. stroke: {
  388. lineCap: "round",
  389. },
  390. labels: ["AccountsReceivable"],
  391. };
  392. const expenditureOptions: ApexOptions = {
  393. colors: ["#20E647"],
  394. series: [95],
  395. chart: {
  396. height: 350,
  397. type: "radialBar",
  398. },
  399. plotOptions: {
  400. radialBar: {
  401. hollow: {
  402. size: "70%",
  403. background: "#ffffff",
  404. },
  405. track: {
  406. dropShadow: {
  407. enabled: true,
  408. top: 2,
  409. left: 0,
  410. blur: 4,
  411. opacity: 0.15,
  412. },
  413. },
  414. dataLabels: {
  415. name: {
  416. show: false,
  417. },
  418. value: {
  419. color: "#3e98c7",
  420. fontSize: "3em",
  421. show: true,
  422. },
  423. },
  424. },
  425. },
  426. fill: {
  427. type: "gradient",
  428. gradient: {
  429. shade: "dark",
  430. type: "vertical",
  431. gradientToColors: ["#87D4F9"],
  432. stops: [0, 100],
  433. },
  434. },
  435. stroke: {
  436. lineCap: "round",
  437. },
  438. labels: ["AccountsReceivable"],
  439. };
  440. const rows = [
  441. {
  442. id: 1,
  443. projectCode: "M1001",
  444. projectName: "Consultancy Project A",
  445. team: "XXX",
  446. teamLeader: "XXX",
  447. startDate: "01/07/2022",
  448. targetEndDate: "01/04/2024",
  449. client: "Client B",
  450. subsidiary: "N/A",
  451. },
  452. {
  453. id: 2,
  454. projectCode: "M1301",
  455. projectName: "Consultancy Project AAAA",
  456. team: "XXX",
  457. teamLeader: "XXX",
  458. startDate: "01/09/2022",
  459. targetEndDate: "20/02/2024",
  460. client: "Client C",
  461. subsidiary: "Subsidiary A",
  462. },
  463. {
  464. id: 3,
  465. projectCode: "M1354",
  466. projectName: "Consultancy Project BBB",
  467. team: "YYY",
  468. teamLeader: "YYY",
  469. startDate: "01/02/2023",
  470. targetEndDate: "31/01/2024",
  471. client: "Client D",
  472. subsidiary: "Subsidiary C",
  473. },
  474. ];
  475. const ledgerRows = [
  476. {
  477. id: 1,
  478. date: "Feb 2023",
  479. expenditure: "-",
  480. income: "100,000.00",
  481. cashFlowBalance: "100,000.00",
  482. remarks: "Payment Milestone 1 (10%)",
  483. },
  484. {
  485. id: 2,
  486. date: "Feb 2023",
  487. expenditure: "160,000.00",
  488. income: "-",
  489. cashFlowBalance: "(60,000.00)",
  490. remarks: "Monthly Manpower Expenditure",
  491. },
  492. {
  493. id: 3,
  494. date: "Mar 2023",
  495. expenditure: "160,000.00",
  496. income: "-",
  497. cashFlowBalance: "(180,000.00)",
  498. remarks: "Monthly Manpower Expenditure",
  499. },
  500. {
  501. id: 4,
  502. date: "Apr 2023",
  503. expenditure: "120,000.00",
  504. income: "-",
  505. cashFlowBalance: "(300,000.00)",
  506. remarks: "Monthly Manpower Expenditure",
  507. },
  508. {
  509. id: 5,
  510. date: "May 2023",
  511. expenditure: "-",
  512. income: "200,000.00",
  513. cashFlowBalance: "(100,000.00)",
  514. remarks: "Payment Milestone 2 (20%)",
  515. },
  516. {
  517. id: 6,
  518. date: "May 2023",
  519. expenditure: "40,000.00",
  520. income: "-",
  521. cashFlowBalance: "(140,000.00)",
  522. remarks: "Monthly Manpower Expenditure",
  523. },
  524. ];
  525. const [projectData, setProjectData]: any[] = React.useState(rows);
  526. const [ledgerData, setLedgerData]: any[] = React.useState(ledgerRows);
  527. const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => {
  528. const selectedRowsData = projectData.filter((row: any) =>
  529. newSelectionModel.includes(row.id),
  530. );
  531. console.log(selectedRowsData);
  532. };
  533. return (
  534. <>
  535. <Suspense fallback={<ProgressCashFlowSearch.Loading />}>
  536. <ProgressCashFlowSearch />
  537. </Suspense>
  538. <CustomDatagrid
  539. rows={projectData}
  540. columns={columns}
  541. columnWidth={200}
  542. dataGridHeight={300}
  543. checkboxSelection={true}
  544. onRowSelectionModelChange={handleSelectionChange}
  545. selectionModel={selectionModel}
  546. />
  547. <Grid item sm>
  548. <div style={{ display: "inline-block", width: "50%" }}>
  549. <Grid item xs={12} md={12} lg={12}>
  550. <Card>
  551. <CardHeader
  552. className="text-slate-500"
  553. title="Project Cash Flow by Month"
  554. />
  555. <div style={{ display: "inline-block", width: "99%" }}>
  556. <div className="inline-block">
  557. <Label className="text-slate-500 font-medium ml-6">
  558. Period:&nbsp;
  559. </Label>
  560. <Input
  561. id={"cashFlowYear"}
  562. value={cashFlowYear}
  563. readOnly={true}
  564. bsSize="lg"
  565. className="rounded-md text-base w-12"
  566. />
  567. </div>
  568. <div className="inline-block ml-1">
  569. <button
  570. onClick={() => setCashFlowYear(cashFlowYear - 1)}
  571. className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base"
  572. >
  573. &lt;
  574. </button>
  575. </div>
  576. <div className="inline-block ml-1">
  577. <button
  578. onClick={() => setCashFlowYear(cashFlowYear + 1)}
  579. className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base"
  580. >
  581. &gt;
  582. </button>
  583. </div>
  584. <ReactApexChart
  585. options={options}
  586. series={options.series}
  587. type="line"
  588. height="auto"
  589. />
  590. </div>
  591. </Card>
  592. </Grid>
  593. </div>
  594. <div
  595. style={{
  596. display: "inline-block",
  597. width: "24%",
  598. verticalAlign: "top",
  599. marginLeft: 10,
  600. }}
  601. >
  602. <Grid item xs={12} md={12} lg={12}>
  603. <Card>
  604. <CardHeader
  605. className="text-slate-500"
  606. title="Accounts Receivable (HKD)"
  607. />
  608. <ReactApexChart
  609. options={accountsReceivableOptions}
  610. series={accountsReceivableOptions.series}
  611. type="radialBar"
  612. />
  613. <Card className="ml-2 mr-2 mb-4 rounded-none border-solid border-slate-100">
  614. <div
  615. className="text-sm font-medium ml-5 mt-2"
  616. style={{ color: "#898d8d" }}
  617. >
  618. Total Invoiced Amount
  619. </div>
  620. <div
  621. className="text-lg font-medium ml-5"
  622. style={{ color: "#6b87cf" }}
  623. >
  624. 1,000,000.00
  625. </div>
  626. <hr />
  627. <div
  628. className="text-sm font-medium ml-5"
  629. style={{ color: "#898d8d" }}
  630. >
  631. Total Received Amount
  632. </div>
  633. <div
  634. className="text-lg font-medium ml-5"
  635. style={{ color: "#6b87cf" }}
  636. >
  637. 800,000.00
  638. </div>
  639. <hr />
  640. <div
  641. className="text-sm font-medium ml-5"
  642. style={{ color: "#898d8d" }}
  643. >
  644. Accounts Receivable
  645. </div>
  646. <div
  647. className="text-lg font-medium ml-5 mb-2"
  648. style={{ color: "#6b87cf" }}
  649. >
  650. 200,000.00
  651. </div>
  652. </Card>
  653. </Card>
  654. </Grid>
  655. </div>
  656. <div
  657. style={{
  658. display: "inline-block",
  659. width: "24%",
  660. verticalAlign: "top",
  661. marginLeft: 10,
  662. }}
  663. >
  664. <Grid item xs={12} md={12} lg={12}>
  665. <Card>
  666. <CardHeader
  667. className="text-slate-500"
  668. title="Expenditure (HKD)"
  669. />
  670. <ReactApexChart
  671. options={expenditureOptions}
  672. series={expenditureOptions.series}
  673. type="radialBar"
  674. />
  675. <Card className="ml-2 mr-2 mb-4 rounded-none border-solid border-slate-100">
  676. <div
  677. className="text-sm font-medium ml-5 mt-2"
  678. style={{ color: "#898d8d" }}
  679. >
  680. Total Budget
  681. </div>
  682. <div
  683. className="text-lg font-medium ml-5"
  684. style={{ color: "#6b87cf" }}
  685. >
  686. 800,000.00
  687. </div>
  688. <hr />
  689. <div
  690. className="text-sm font-medium ml-5"
  691. style={{ color: "#898d8d" }}
  692. >
  693. Total Cumulative Expenditure
  694. </div>
  695. <div
  696. className="text-lg font-medium ml-5"
  697. style={{ color: "#6b87cf" }}
  698. >
  699. 760,000.00
  700. </div>
  701. <hr />
  702. <div
  703. className="text-sm font-medium ml-5"
  704. style={{ color: "#898d8d" }}
  705. >
  706. Accounts Receivable
  707. </div>
  708. <div
  709. className="text-lg font-medium ml-5 mb-2"
  710. style={{ color: "#6b87cf" }}
  711. >
  712. 40,000.00
  713. </div>
  714. </Card>
  715. </Card>
  716. </Grid>
  717. </div>
  718. <div
  719. className="mt-5"
  720. style={{
  721. display: "inline-block",
  722. width: "100%",
  723. verticalAlign: "top",
  724. }}
  725. >
  726. <Grid item xs={12} md={12} lg={12}>
  727. <Card>
  728. <CardHeader
  729. className="text-slate-500"
  730. title="Anticipate Cash Flow by Month"
  731. />
  732. <div style={{ display: "inline-block", width: "99%" }}>
  733. <div className="inline-block">
  734. <Label className="text-slate-500 font-medium ml-6">
  735. Period:&nbsp;
  736. </Label>
  737. <Input
  738. id={"cashFlowYear"}
  739. value={anticipateCashFlowYear}
  740. readOnly={true}
  741. bsSize="lg"
  742. className="rounded-md text-base w-12"
  743. />
  744. </div>
  745. <div className="inline-block ml-1">
  746. <button
  747. onClick={() => setAnticipateCashFlowYear(cashFlowYear - 1)}
  748. className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base"
  749. >
  750. &lt;
  751. </button>
  752. </div>
  753. <div className="inline-block ml-1">
  754. <button
  755. onClick={() => setAnticipateCashFlowYear(cashFlowYear + 1)}
  756. className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base"
  757. >
  758. &gt;
  759. </button>
  760. </div>
  761. <ReactApexChart
  762. options={anticipateOptions}
  763. series={anticipateOptions.series}
  764. type="line"
  765. height="350"
  766. />
  767. </div>
  768. </Card>
  769. <Card>
  770. <CardHeader
  771. className="text-slate-500"
  772. title="Cash Flow Ledger by Month"
  773. />
  774. <div className="ml-4 mr-4">
  775. <CustomDatagrid
  776. rows={ledgerData}
  777. columns={ledgerColumns}
  778. columnWidth={200}
  779. dataGridHeight={300}
  780. />
  781. </div>
  782. </Card>
  783. </Grid>
  784. </div>
  785. </Grid>
  786. </>
  787. );
  788. };
  789. export default ProjectCashFlow;