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.
 
 

522 linhas
19 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 { fetchFinancialSummaryCard } from "@/app/api/financialsummary";
  22. import { exportFinancialSummaryByClientExcel, searchFinancialSummaryByClient,searchFinancialSummaryByProject } from "@/app/api/financialsummary/actions";
  23. import ProjectFinancialCard from "./ProjectFinancialCard";
  24. import VisibilityIcon from '@mui/icons-material/Visibility';
  25. import { downloadFile } from "@/app/utils/commonUtil";
  26. const ProjectFinancialSummary: React.FC = () => {
  27. const [SearchCriteria, setSearchCriteria] = React.useState({});
  28. const { t } = useTranslation("dashboard");
  29. const [selectionModel, setSelectionModel]: any[] = React.useState([]);
  30. const [projectFinancialData, setProjectFinancialData]: any[] = React.useState([]);
  31. const [clientFinancialRows, setClientFinancialRows]: any[] = React.useState([]);
  32. const [projectFinancialRows, setProjectFinancialRows]: any[] = React.useState([]);
  33. const fetchData = async () => {
  34. const financialSummaryCard = await fetchFinancialSummaryCard();
  35. setProjectFinancialData(financialSummaryCard)
  36. }
  37. const fetchTableData = async (teamId?:any) => {
  38. const financialSummaryByClient = await searchFinancialSummaryByClient(teamId);
  39. console.log(financialSummaryByClient)
  40. // console.log(financialSummaryByProject)
  41. setClientFinancialRows(financialSummaryByClient)
  42. }
  43. useEffect(() => {
  44. fetchData()
  45. fetchTableData(undefined)
  46. }, []);
  47. const rows0 = [{id: 1,projectCode:"M1201",projectName:"Consultancy Project C", team:"XXX", teamLeader:"XXX", startDate:"01/08/2022", targetEndDate: "01/05/2024", client:"Client A", subsidiary:"N/A"},
  48. {id: 2,projectCode:"M1321",projectName:"Consultancy Project CCC", team:"XXX", teamLeader:"XXX", startDate:"01/08/2022", targetEndDate: "20/01/2024", client:"Client E", subsidiary:"Subsidiary B"},
  49. {id: 3,projectCode:"M1001",projectName:"Consultancy Project A", team:"YYY", teamLeader:"YYY", startDate:"01/07/2022", targetEndDate: "01/04/2024", client:"Client B", subsidiary:"N/A"},
  50. {id: 4,projectCode:"M1301",projectName:"Consultancy Project AAAA", team:"YYY", teamLeader:"YYY", startDate:"01/09/2022", targetEndDate: "20/02/2024", client:"Client C", subsidiary:"Subsidiary A"},
  51. {id: 5,projectCode:"M1354",projectName:"Consultancy Project BBB", team:"YYY", teamLeader:"YYY", startDate:"01/02/2023", targetEndDate: "31/01/2024", client:"Client D", subsidiary:"Subsidiary C"}
  52. ]
  53. const rows1 = [{id: 1,projectCode:"M1201",projectName:"Consultancy Project C", team:"XXX", teamLeader:"XXX", startDate:"01/08/2022", targetEndDate: "01/05/2024", client:"Client A", subsidiary:"N/A"},
  54. {id: 2,projectCode:"M1321",projectName:"Consultancy Project CCC", team:"XXX", teamLeader:"XXX", startDate:"01/08/2022", targetEndDate: "20/01/2024", client:"Client E", subsidiary:"Subsidiary B"},
  55. ]
  56. const rows2 = [{id: 3,projectCode:"M1001",projectName:"Consultancy Project A", team:"YYY", teamLeader:"YYY", startDate:"01/07/2022", targetEndDate: "01/04/2024", client:"Client B", subsidiary:"N/A"},
  57. {id: 4,projectCode:"M1301",projectName:"Consultancy Project AAAA", team:"YYY", teamLeader:"YYY", startDate:"01/09/2022", targetEndDate: "20/02/2024", client:"Client C", subsidiary:"Subsidiary A"},
  58. {id: 5,projectCode:"M1354",projectName:"Consultancy Project BBB", team:"YYY", teamLeader:"YYY", startDate:"01/02/2023", targetEndDate: "31/01/2024", client:"Client D", subsidiary:"Subsidiary C"}
  59. ]
  60. // const projectFinancialRows = [{id: 1,projectCode:"M1354",projectName:"Consultanct Project BBB",clientName:"Client D",cashFlowStatus:"Positive",cpi:"1.25", totalFees:"500,000.00", totalBudget:"400,000.00", totalCumulativeExpenditure:"280,000.00", totalInvoicedAmount: "350,000.00", totalUnInvoicedAmount:"150,000.00", totalReceivedAmount:"350,000.00"}
  61. // ]
  62. // const clientFinancialRows =[{id: 1,clientCode:"Cust-02",clientName:"Client B",totalProjectInvolved:"1",cashFlowStatus:"Positive",cpi:"1.25", totalFees:"500,000.00", totalBudget:"400,000.00", totalCumulativeExpenditure:"280,000.00", totalInvoicedAmount: "350,000.00", totalUnInvoicedAmount:"150,000.00", totalReceivedAmount:"350,000.00"},
  63. // {id: 2,clientCode:"Cust-03",clientName:"Client C",totalProjectInvolved:"1",cashFlowStatus:"Positive",cpi:"1.25", totalFees:"500,000.00", totalBudget:"400,000.00", totalCumulativeExpenditure:"280,000.00", totalInvoicedAmount: "350,000.00", totalUnInvoicedAmount:"150,000.00", totalReceivedAmount:"350,000.00"},
  64. // {id: 3,clientCode:"Cust-04",clientName:"Client D",totalProjectInvolved:"4",cashFlowStatus:"Positive",cpi:"1.25", totalFees:"500,000.00", totalBudget:"400,000.00", totalCumulativeExpenditure:"280,000.00", totalInvoicedAmount: "350,000.00", totalUnInvoicedAmount:"150,000.00", totalReceivedAmount:"350,000.00"}
  65. // ]
  66. const [isCardClickedIndex, setIsCardClickedIndex] = React.useState(0);
  67. const [selectedTeamData, setSelectedTeamData]: any[] = React.useState(rows0);
  68. const handleCardClick = (r: any, index:any) => {
  69. fetchTableData(r.teamId)
  70. setIsCardClickedIndex(index)
  71. };
  72. const columns = [
  73. {
  74. id: 'customerCode',
  75. field: 'customerCode',
  76. headerName: "Client Code",
  77. minWidth:50
  78. },
  79. {
  80. id: 'customerName',
  81. field: 'customerName',
  82. headerName: "Client Name",
  83. minWidth:80
  84. },
  85. {
  86. id: 'projectNo',
  87. field: 'projectNo',
  88. headerName: "Total Project Involved",
  89. minWidth:80
  90. },
  91. {
  92. id: 'cashFlowStatus',
  93. field: 'cashFlowStatus',
  94. headerName: "Cash Flow Status",
  95. minWidth:100,
  96. renderCell: (params:any) => {
  97. console.log(params.row)
  98. if (params.row.cashFlowStatus === "Positive") {
  99. return (
  100. <span className="text-lime-500">{params.row.cashFlowStatus}</span>
  101. )
  102. } else if (params.row.cashFlowStatus === "Negative") {
  103. return (
  104. <span className="text-red-500">{params.row.cashFlowStatus}</span>
  105. )
  106. }
  107. },
  108. },
  109. {
  110. id: 'cpi',
  111. field: 'cpi',
  112. headerName: "CPI",
  113. minWidth:50,
  114. renderCell: (params:any) => {
  115. if (params.row.cpi >= 1) {
  116. return (
  117. <span className="text-lime-500">{params.row.cpi}</span>
  118. )
  119. } else if (params.row.cpi < 1) {
  120. return (
  121. <span className="text-red-500">{params.row.cpi}</span>
  122. )
  123. }
  124. },
  125. },
  126. {
  127. id: 'projectedCashFlowStatus',
  128. field: 'projectedCashFlowStatus',
  129. headerName: "Projected Cash Flow Status",
  130. minWidth:100,
  131. renderCell: (params:any) => {
  132. if (params.row.projectedCashFlowStatus === "Positive") {
  133. return (
  134. <span className="text-lime-500">{params.row.projectedCashFlowStatus}</span>
  135. )
  136. } else if (params.row.projectedCashFlowStatus === "Negative") {
  137. return (
  138. <span className="text-red-500">{params.row.projectedCashFlowStatus}</span>
  139. )
  140. }
  141. },
  142. },
  143. {
  144. id: 'projectedCpi',
  145. field: 'projectedCpi',
  146. headerName: "Projected CPI",
  147. minWidth:50,
  148. renderCell: (params:any) => {
  149. if (params.row.projectedCpi >= 1) {
  150. return (
  151. <span className="text-lime-500">{params.row.projectedCpi}</span>
  152. )
  153. } else if (params.row.projectedCpi < 1) {
  154. return (
  155. <span className="text-red-500">{params.row.projectedCpi}</span>
  156. )
  157. }
  158. },
  159. },
  160. {
  161. id: 'totalFee',
  162. field: 'totalFee',
  163. headerName: "Total Fees (HKD)",
  164. minWidth:50,
  165. renderCell: (params:any) => {
  166. return (
  167. <span>${params.row.totalFee.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
  168. )
  169. },
  170. },
  171. {
  172. id: 'totalBudget',
  173. field: 'totalBudget',
  174. headerName: "Total Budget (HKD)",
  175. minWidth:50,
  176. renderCell: (params:any) => {
  177. return (
  178. <span>${params.row.totalBudget.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
  179. )
  180. },
  181. },
  182. {
  183. id: 'cumulativeExpenditure',
  184. field: 'cumulativeExpenditure',
  185. headerName: "Total Cumulative Expenditure (HKD)",
  186. minWidth:280,
  187. renderCell: (params:any) => {
  188. return (
  189. <span>${params.row.cumulativeExpenditure.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
  190. )
  191. },
  192. },
  193. {
  194. id: 'totalInvoiced',
  195. field: 'totalInvoiced',
  196. headerName: "Total Invoiced Amount (HKD)",
  197. minWidth:250,
  198. renderCell: (params:any) => {
  199. return (
  200. <span>${params.row.totalInvoiced.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
  201. )
  202. },
  203. },
  204. {
  205. id: 'totalUnInvoiced',
  206. field: 'totalUnInvoiced',
  207. headerName: "Total Un-invoiced Amount (HKD)",
  208. minWidth:250,
  209. renderCell: (params:any) => {
  210. return (
  211. <span>${params.row.totalUninvoiced.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
  212. )
  213. },
  214. },
  215. {
  216. id: 'totalReceived',
  217. field: 'totalReceived',
  218. headerName: "Total Received Amount (HKD)",
  219. minWidth:250,
  220. renderCell: (params:any) => {
  221. return (
  222. <span>${params.row.totalReceived.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
  223. )
  224. },
  225. },
  226. // {
  227. // id: 'projectCode',
  228. // field: 'projectCode',
  229. // headerName: "Project Code",
  230. // flex: 1,
  231. // },
  232. // {
  233. // id: 'projectName',
  234. // field: 'projectName',
  235. // headerName: "Project Name",
  236. // flex: 1,
  237. // },
  238. // {
  239. // id: 'team',
  240. // field: 'team',
  241. // headerName: "Team",
  242. // flex: 1,
  243. // },
  244. // {
  245. // id: 'teamLeader',
  246. // field: 'teamLeader',
  247. // headerName: "Team Leader",
  248. // flex: 1,
  249. // },
  250. // {
  251. // id: 'startDate',
  252. // field: 'startDate',
  253. // headerName: "Start Date",
  254. // flex: 1,
  255. // },
  256. // {
  257. // id: 'targetEndDate',
  258. // field: 'targetEndDate',
  259. // headerName: "Target End Date",
  260. // flex: 1,
  261. // },
  262. // {
  263. // id: 'client',
  264. // field: 'client',
  265. // headerName: "Client",
  266. // flex: 1,
  267. // },
  268. // {
  269. // id: 'subsidiary',
  270. // field: 'subsidiary',
  271. // headerName: "Subsidiary",
  272. // flex: 1,
  273. // },
  274. ];
  275. const columns2 = [
  276. {
  277. id: 'projectCode',
  278. field: 'projectCode',
  279. headerName: "Project Code",
  280. minWidth:50,
  281. },
  282. {
  283. id: 'projectName',
  284. field: 'projectName',
  285. headerName: "Project Name",
  286. minWidth:50,
  287. },
  288. {
  289. id: 'customerName',
  290. field: 'customerName',
  291. headerName: "Client Name",
  292. minWidth:50,
  293. },
  294. {
  295. id: 'subsidiaryName',
  296. field: 'subsidiaryName',
  297. headerName: "Subsidiary",
  298. minWidth:50,
  299. },
  300. {
  301. id: 'cashFlowStatus',
  302. field: 'cashFlowStatus',
  303. headerName: "Cash Flow Status",
  304. minWidth:80,
  305. renderCell: (params:any) => {
  306. if (params.row.cashFlowStatus === "Positive") {
  307. return (
  308. <span className="text-lime-500">{params.row.cashFlowStatus}</span>
  309. )
  310. } else if (params.row.cashFlowStatus === "Negative") {
  311. return (
  312. <span className="text-red-500">{params.row.cashFlowStatus}</span>
  313. )
  314. }
  315. },
  316. },
  317. {
  318. id: "cpi",
  319. field: "cpi",
  320. headerName: "CPI",
  321. minWidth:50,
  322. renderCell: (params: any) => {
  323. if (params.row.cpi >= 1) {
  324. return <span className="text-lime-500">{params.row.cpi}</span>;
  325. } else if (params.row.cpi < 1) {
  326. return <span className="text-red-500">{params.row.cpi}</span>;
  327. }
  328. },
  329. },
  330. {
  331. id: 'projectedCashFlowStatus',
  332. field: 'projectedCashFlowStatus',
  333. headerName: "Projected Cash Flow Status",
  334. minWidth:100,
  335. renderCell: (params:any) => {
  336. if (params.row.projectedCashFlowStatus === "Positive") {
  337. return (
  338. <span className="text-lime-500">{params.row.projectedCashFlowStatus}</span>
  339. )
  340. } else if (params.row.projectedCashFlowStatus === "Negative") {
  341. return (
  342. <span className="text-red-500">{params.row.projectedCashFlowStatus}</span>
  343. )
  344. }
  345. },
  346. },
  347. {
  348. id: 'projectedCpi',
  349. field: 'projectedCpi',
  350. headerName: "Projected CPI",
  351. minWidth:50,
  352. renderCell: (params:any) => {
  353. if (params.row.projectedCpi >= 1) {
  354. return (
  355. <span className="text-lime-500">{params.row.projectedCpi}</span>
  356. )
  357. } else if (params.row.projectedCpi < 1) {
  358. return (
  359. <span className="text-red-500">{params.row.projectedCpi}</span>
  360. )
  361. }
  362. },
  363. },
  364. {
  365. id: 'totalFees',
  366. field: 'totalFees',
  367. headerName: "Total Fees (HKD)",
  368. minWidth:50,
  369. renderCell: (params:any) => {
  370. return (
  371. <span>${params.row.totalFee.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
  372. )
  373. },
  374. },
  375. {
  376. id: 'totalBudget',
  377. field: 'totalBudget',
  378. headerName: "Total Budget (HKD)",
  379. minWidth:50,
  380. renderCell: (params:any) => {
  381. return (
  382. <span>${params.row.totalBudget.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
  383. )
  384. },
  385. },
  386. {
  387. id: 'totalCumulativeExpenditure',
  388. field: 'totalCumulativeExpenditure',
  389. headerName: "Total Cumulative Expenditure (HKD)",
  390. minWidth:250,
  391. renderCell: (params:any) => {
  392. return (
  393. <span>${params.row.cumulativeExpenditure.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
  394. )
  395. },
  396. },
  397. {
  398. id: 'totalInvoicedAmount',
  399. field: 'totalInvoicedAmount',
  400. headerName: "Total Invoiced Amount (HKD)",
  401. minWidth:250,
  402. renderCell: (params:any) => {
  403. return (
  404. <span>${params.row.totalInvoiced.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
  405. )
  406. },
  407. },
  408. {
  409. id: 'totalUnInvoicedAmount',
  410. field: 'totalUnInvoicedAmount',
  411. headerName: "Total Un-invoiced Amount (HKD)",
  412. minWidth:250,
  413. renderCell: (params:any) => {
  414. return (
  415. <span>${params.row.totalUninvoiced.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
  416. )
  417. },
  418. },
  419. {
  420. id: 'totalReceivedAmount',
  421. field: 'totalReceivedAmount',
  422. headerName: "Total Received Amount (HKD)",
  423. minWidth:250,
  424. renderCell: (params:any) => {
  425. return (
  426. <span>${params.row.totalReceived.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
  427. )
  428. },
  429. },
  430. ];
  431. const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => {
  432. const selectedRowsData = selectedTeamData.filter((row: any) =>
  433. newSelectionModel.includes(row.id),
  434. );
  435. console.log(selectedRowsData);
  436. };
  437. const fetchProjectTableData = async (teamId?:any,customerId?:any) => {
  438. const financialSummaryByProject = await searchFinancialSummaryByProject(teamId,customerId);
  439. setProjectFinancialRows(financialSummaryByProject)
  440. }
  441. const handleRowClick = (params:any) => {
  442. console.log(params.row.teamId);
  443. fetchProjectTableData(params.row.teamId,params.row.cid)
  444. };
  445. const handleExportByClient = async () => {
  446. const response = await exportFinancialSummaryByClientExcel({financialSummaryByClients: clientFinancialRows})
  447. if (response) {
  448. downloadFile(new Uint8Array(response.blobValue), response.filename!!)
  449. }
  450. console.log(clientFinancialRows)
  451. };
  452. const handleExportByProject = () => {
  453. console.log(projectFinancialRows)
  454. };
  455. return (
  456. <Grid item sm>
  457. <Card>
  458. <CardHeader className="text-slate-500" title="Active Project Financial Status"/>
  459. <div className="ml-10 mr-10" style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'start'}}>
  460. {projectFinancialData.map((record:any, index:any) => (
  461. <div className="hover:cursor-pointer ml-4 mt-5 mb-4 inline-block" key={index} onClick={(r) => handleCardClick(record,index)}>
  462. <ProjectFinancialCard Title={record.teamName} TotalActiveProjectNumber={record.projectNo} TotalFees={record.totalFee} TotalBudget={record.totalBudget} TotalCumulative={record.cumulativeExpenditure ?? 0} TotalInvoicedAmount={record.totalInvoiced ?? 0} TotalUnInvoicedAmount={record.unInvoiced ?? 0} TotalReceivedAmount={record.totalReceived ?? 0} CashFlowStatus={record.cashFlowStatus ?? "Negative"} CostPerformanceIndex={record.cpi ?? 0} ProjectedCashFlowStatus={record.projectedCashFlowStatus ?? "Negative"} ProjectedCPI={record.projectedCpi ?? 0} ClickedIndex={isCardClickedIndex} Index={index}/>
  463. </div>
  464. ))}
  465. </div>
  466. </Card>
  467. <Card className="mt-5">
  468. <div style={{display:"inline-block"}}>
  469. <CardHeader className="text-slate-500" title="Financial Status (by Client)"/>
  470. </div>
  471. <div style={{display:"inline-block"}}>
  472. {clientFinancialRows.length > 0 && (
  473. <button onClick={handleExportByClient} className="hover:cursor-pointer hover:bg-violet-50 text-base bg-transparent border-violet-500 text-violet-500 border-solid rounded-md w-36">
  474. Export Excel
  475. </button>
  476. )}
  477. </div>
  478. <div style={{display:"inline-block",width:"99%",marginLeft:10}}>
  479. {/* <CustomDatagrid rows={clientFinancialRows} columns={columns} columnWidth={200} dataGridHeight={300} checkboxSelection={true} onRowSelectionModelChange={handleSelectionChange} selectionModel={selectionModel}/> */}
  480. <CustomDatagrid onRowClick={handleRowClick} rows={clientFinancialRows} columns={columns} columnWidth={200} dataGridHeight={300}/>
  481. </div>
  482. </Card>
  483. <Card className="mt-5">
  484. <div style={{display:"inline-block"}}>
  485. <CardHeader className="text-slate-500" title="Financial Status (by Project)"/>
  486. </div>
  487. <div style={{display:"inline-block"}}>
  488. {projectFinancialRows.length > 0 && (
  489. <button onClick={handleExportByProject} className="hover:cursor-pointer hover:bg-violet-50 text-base bg-transparent border-violet-500 text-violet-500 border-solid rounded-md w-36">
  490. Export Excel
  491. </button>
  492. )}
  493. </div>
  494. <div style={{display:"inline-block",width:"99%",marginLeft:10}}>
  495. <CustomDatagrid rows={projectFinancialRows} columns={columns2} columnWidth={200} dataGridHeight={300}/>
  496. </div>
  497. </Card>
  498. </Grid>
  499. );
  500. };
  501. export default ProjectFinancialSummary;