You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

603 lines
17 KiB

  1. "use client";
  2. import {
  3. FinancialSummaryByProject,
  4. FinancialSummaryByClient,
  5. FinancialByProject,
  6. } from "@/app/api/financialsummary";
  7. import SearchBox, { Criterion } from "../SearchBox";
  8. import { useEffect, useMemo, useState } from "react";
  9. import CustomDatagrid from "../CustomDatagrid";
  10. import { useTranslation } from "react-i18next";
  11. import { useRouter } from "next/navigation";
  12. import { Box, Grid } from "@mui/material";
  13. import { SumOfByClient } from "./gptFn";
  14. // import { summarizeFinancialData } from "./gptFn";
  15. interface Props {
  16. financialSummByProject: FinancialByProject[];
  17. financialSummByClient: SumOfByClient[];
  18. }
  19. type SearchQuery = Partial<Omit<FinancialSummaryByProject, "id">>;
  20. type SearchParamNames = keyof SearchQuery;
  21. type SearchQuery2 = Partial<Omit<SumOfByClient, "id">>;
  22. type SearchParamNames2 = keyof SearchQuery2;
  23. const FinancialStatusByProject: React.FC<Props> = ({
  24. financialSummByProject,
  25. financialSummByClient
  26. }) => {
  27. console.log(financialSummByProject);
  28. // console.log(financialSummByClient);
  29. const { t } = useTranslation("dashboard");
  30. const router = useRouter();
  31. const [filteredByProjectRows, setFilteredByProjectRows] = useState(financialSummByProject);
  32. const [filteredByClientRows, setFilteredByClientRows] = useState(financialSummByClient);
  33. console.log(filteredByProjectRows);
  34. console.log(filteredByClientRows);
  35. // const testing = useMemo(() => summarizeFinancialData(filteredByProjectRows), [])
  36. const greenColor = "text-lime-500";
  37. const redColor = "text-red-500";
  38. const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
  39. () => [
  40. { label: t("Project Code"), paramName: "projectCode", type: "text" },
  41. { label: t("Project Name"), paramName: "projectName", type: "text" },
  42. ],
  43. [t],
  44. );
  45. const searchCriteria2: Criterion<SearchParamNames2>[] = useMemo(
  46. () => [
  47. { label: t("Client Code"), paramName: "customerCode", type: "text" },
  48. { label: t("Client Name"), paramName: "customerName", type: "text" },
  49. ],
  50. [t],
  51. );
  52. useEffect(() => {
  53. setFilteredByProjectRows(financialSummByProject);
  54. setFilteredByClientRows(financialSummByClient)
  55. }, [financialSummByProject, financialSummByClient]);
  56. const columns1 = [
  57. {
  58. id: "projectCode",
  59. field: "projectCode",
  60. headerName: t("Project Code"),
  61. minWidth: 50,
  62. renderCell: (params: any) => (
  63. <div
  64. className="text-blue-600 hover:underline cursor-pointer"
  65. onClick={() => {
  66. router.push(
  67. `/dashboard/ProjectCashFlow?projectId=${params.row.id}`
  68. );
  69. }}
  70. >
  71. {params.value}
  72. </div>
  73. ),
  74. },
  75. {
  76. id: "projectName",
  77. field: "projectName",
  78. headerName: t("Project Name"),
  79. minWidth: 50,
  80. },
  81. {
  82. id: "customerName",
  83. field: "customerName",
  84. headerName: t("Client Name"),
  85. minWidth: 50,
  86. },
  87. {
  88. id: "subsidiaryName",
  89. field: "subsidiaryName",
  90. headerName: t("Subsidiary"),
  91. minWidth: 50,
  92. },
  93. {
  94. id: "cashFlowStatus",
  95. field: "cashFlowStatus",
  96. headerName: t("Cash Flow Status"),
  97. minWidth: 80,
  98. renderCell: (params: any) => {
  99. if (params.row.invoicedAmount >= params.row.cumulativeExpenditure) {
  100. return <span className={greenColor}>{t("Positive")}</span>;
  101. } else {
  102. return <span className={redColor}>{t("Negative")}</span>;
  103. }
  104. },
  105. },
  106. {
  107. id: "cpi",
  108. field: "cpi",
  109. headerName: "CPI",
  110. minWidth: 50,
  111. renderCell: (params: any) => {
  112. var cpi = params.invoicedAmount/(params.projectExpense + params.invoicedAmount) || 0
  113. return (
  114. <span className={cpi >= 1 ? greenColor : redColor}>
  115. {cpi.toLocaleString(undefined, {
  116. minimumFractionDigits: 2,
  117. maximumFractionDigits: 2,
  118. })}
  119. </span>
  120. );
  121. },
  122. },
  123. {
  124. id: "projectedCashFlowStatus",
  125. field: "projectedCashFlowStatus",
  126. headerName: t("Projected Cash Flow Status"),
  127. minWidth: 100,
  128. renderCell: (params: any) => {
  129. var cumulativeExpenditure = params.row.projectExpense + params.row.invoicedAmount
  130. if (params.row.totalFee >= cumulativeExpenditure) {
  131. return <span className={greenColor}>{t("Positive")}</span>;
  132. } else {
  133. return <span className={redColor}>{t("Negative")}</span>;
  134. }
  135. },
  136. },
  137. {
  138. id: "projectedCpi",
  139. field: "projectedCpi",
  140. headerName: t("Projected CPI"),
  141. minWidth: 50,
  142. renderCell: (params: any) => {
  143. var projectedCpi = params.row.totalFee/(params.row.projectExpense + params.row.invoicedAmount)
  144. return (
  145. <span
  146. className={projectedCpi >= 1 ? greenColor : redColor}
  147. >
  148. {projectedCpi.toLocaleString(undefined, {
  149. minimumFractionDigits: 2,
  150. maximumFractionDigits: 2,
  151. })}
  152. </span>
  153. );
  154. },
  155. },
  156. {
  157. id: "totalFee",
  158. field: "totalFee",
  159. headerName: t("Total Fees") + t("HKD"),
  160. type: "number",
  161. minWidth: 50,
  162. renderCell: (params: any) => {
  163. return (
  164. <span>
  165. $
  166. {params.row.totalFee.toLocaleString(undefined, {
  167. minimumFractionDigits: 2,
  168. maximumFractionDigits: 2,
  169. })}
  170. </span>
  171. );
  172. },
  173. },
  174. {
  175. id: "totalBudget",
  176. field: "totalBudget",
  177. headerName: t("Total Budget") + t("HKD"),
  178. minWidth: 50,
  179. type: "number",
  180. renderCell: (params: any) => {
  181. return (
  182. <span>
  183. $
  184. {params.row.totalBudget.toLocaleString(undefined, {
  185. minimumFractionDigits: 2,
  186. maximumFractionDigits: 2,
  187. })}
  188. </span>
  189. );
  190. },
  191. },
  192. {
  193. id: "cumulativeExpenditure",
  194. field: "cumulativeExpenditure",
  195. headerName: t("Total Cumulative Expenditure") + t("HKD"),
  196. minWidth: 250,
  197. type: "number",
  198. renderCell: (params: any) => {
  199. var cumulativeExpenditure = params.row.projectExpense + params.row.invoicedAmount
  200. return (
  201. <span>
  202. $
  203. {cumulativeExpenditure.toLocaleString(undefined, {
  204. minimumFractionDigits: 2,
  205. maximumFractionDigits: 2,
  206. })}
  207. </span>
  208. );
  209. },
  210. },
  211. {
  212. id: "manhourExpense",
  213. field: "manhourExpense",
  214. headerName: t("Manpower Expenses") + t("HKD"),
  215. minWidth: 280,
  216. type: "number",
  217. renderCell: (params: any) => {
  218. return (
  219. <span>
  220. $
  221. {params.row.manhourExpense.toLocaleString(undefined, {
  222. minimumFractionDigits: 2,
  223. maximumFractionDigits: 2,
  224. })}
  225. </span>
  226. );
  227. },
  228. },
  229. {
  230. id: "projectExpense",
  231. field: "projectExpense",
  232. headerName: t("Project Expense") + t("HKD"),
  233. minWidth: 280,
  234. type: "number",
  235. renderCell: (params: any) => {
  236. return (
  237. <span>
  238. $
  239. {(params.row.projectExpense ?? 0).toLocaleString(undefined, {
  240. minimumFractionDigits: 2,
  241. maximumFractionDigits: 2,
  242. })}
  243. </span>
  244. );
  245. },
  246. },
  247. {
  248. id: "invoicedAmount",
  249. field: "invoicedAmount",
  250. headerName: t("Total Invoiced Amount") + t("HKD"),
  251. minWidth: 250,
  252. type: "number",
  253. renderCell: (params: any) => {
  254. return (
  255. <span>
  256. $
  257. {params.row.invoicedAmount.toLocaleString(undefined, {
  258. minimumFractionDigits: 2,
  259. maximumFractionDigits: 2,
  260. })}
  261. </span>
  262. );
  263. },
  264. },
  265. {
  266. id: "nonInvoicedAmount",
  267. field: "nonInvoicedAmount",
  268. headerName: t("Total Un-Invoiced Amount") + t("HKD"),
  269. minWidth: 250,
  270. type: "number",
  271. renderCell: (params: any) => {
  272. var nonInvoiced = params.row.totalFee - params.row.invoicedAmount
  273. return (
  274. <span>
  275. $
  276. {nonInvoiced.toLocaleString(undefined, {
  277. minimumFractionDigits: 2,
  278. maximumFractionDigits: 2,
  279. })}
  280. </span>
  281. );
  282. },
  283. },
  284. {
  285. id: "paidAmount",
  286. field: "paidAmount",
  287. headerName: t("Total Received Amount") + t("HKD"),
  288. minWidth: 250,
  289. type: "number",
  290. renderCell: (params: any) => {
  291. return (
  292. <span>
  293. $
  294. {params.row.paidAmount.toLocaleString(undefined, {
  295. minimumFractionDigits: 2,
  296. maximumFractionDigits: 2,
  297. })}
  298. </span>
  299. );
  300. },
  301. },
  302. ];
  303. const columns2 = [
  304. {
  305. id: "customerCode",
  306. field: "customerCode",
  307. headerName: t("Client Code"),
  308. minWidth: 50,
  309. renderCell: (params: any) => (
  310. <div
  311. className="text-blue-600 hover:underline cursor-pointer"
  312. onClick={() => {
  313. router.push(
  314. `/dashboard/ProjectStatusByClient?customerId=${params.row.id}`
  315. );
  316. }}
  317. >
  318. {params.value}
  319. </div>
  320. ),
  321. },
  322. {
  323. id: "customerName",
  324. field: "customerName",
  325. headerName: t("Client Name"),
  326. minWidth: 80,
  327. },
  328. {
  329. id: "sumOfProjects",
  330. field: "sumOfProjects",
  331. headerName: t("Total Project Involved"),
  332. minWidth: 80,
  333. },
  334. {
  335. id: "cashFlowStatus",
  336. field: "cashFlowStatus",
  337. headerName: t("Cash Flow Status"),
  338. minWidth: 100,
  339. renderCell: (params: any) => {
  340. var cumulativeExpenditure = params.row.projectExpense + params.row.invoicedAmount
  341. return params.row.invoicedAmount >= cumulativeExpenditure ?
  342. <span className={greenColor}>{t("Positive")}</span>
  343. : <span className={redColor}>{t("Negative")}</span>
  344. },
  345. },
  346. {
  347. id: "cpi",
  348. field: "cpi",
  349. headerName: t("CPI"),
  350. minWidth: 50,
  351. renderCell: (params: any) => {
  352. var cumulativeExpenditure = params.row.projectExpense + params.row.invoicedAmount
  353. var cpi = cumulativeExpenditure != 0 ? params.row.invoicedAmount/cumulativeExpenditure : 0
  354. var cpiString = cpi.toLocaleString(undefined, {
  355. minimumFractionDigits: 2,
  356. maximumFractionDigits: 2,
  357. })
  358. return (cpi >= 1) ?
  359. <span className={greenColor}>{cpiString}</span>:
  360. <span className={redColor}>{cpiString}</span>
  361. },
  362. },
  363. {
  364. id: "projectedCashFlowStatus",
  365. field: "projectedCashFlowStatus",
  366. headerName: t("Projected Cash Flow Status"),
  367. minWidth: 100,
  368. renderCell: (params: any) => {
  369. var cumulativeExpenditure = params.row.projectExpense + params.row.invoicedAmount
  370. var status = params.row.invoiceAmount >= cumulativeExpenditure
  371. return status ?
  372. <span className={greenColor}>{t("Positive")}</span>
  373. : <span className={redColor}>{t("Negative")}</span>
  374. },
  375. },
  376. {
  377. id: "projectedCpi",
  378. field: "projectedCpi",
  379. headerName: t("Projected CPI"),
  380. minWidth: 50,
  381. renderCell: (params: any) => {
  382. var cumulativeExpenditure = params.row.projectExpense + params.row.invoicedAmount
  383. var projectCpi = cumulativeExpenditure != 0 ? params.row.totalFee/cumulativeExpenditure : 0
  384. var projectCpiString = projectCpi.toLocaleString(undefined, {
  385. minimumFractionDigits: 2,
  386. maximumFractionDigits: 2,
  387. })
  388. if (projectCpi >= 1) {
  389. return <span className={greenColor}>{projectCpiString}</span>;
  390. } else {
  391. return <span className={redColor}>{projectCpiString}</span>;
  392. }
  393. },
  394. },
  395. {
  396. id: "totalFee",
  397. field: "totalFee",
  398. headerName: t("Total Fees") + t("HKD"),
  399. minWidth: 50,
  400. type: "number",
  401. renderCell: (params: any) => {
  402. return (
  403. <span>
  404. $
  405. {params.row.totalFee.toLocaleString(undefined, {
  406. minimumFractionDigits: 2,
  407. maximumFractionDigits: 2,
  408. })}
  409. </span>
  410. );
  411. },
  412. },
  413. {
  414. id: "totalBudget",
  415. field: "totalBudget",
  416. headerName: t("Total Budget") + t("HKD"),
  417. minWidth: 50,
  418. type: "number",
  419. renderCell: (params: any) => {
  420. return (
  421. <span>
  422. $
  423. {params.row.totalBudget.toLocaleString(undefined, {
  424. minimumFractionDigits: 2,
  425. maximumFractionDigits: 2,
  426. })}
  427. </span>
  428. );
  429. },
  430. },
  431. {
  432. id: "cumulativeExpenditure",
  433. field: "cumulativeExpenditure",
  434. headerName: t("Total Cumulative Expenditure") + t("HKD"),
  435. minWidth: 280,
  436. type: "number",
  437. renderCell: (params: any) => {
  438. var cumulativeExpenditure = params.row.projectExpense + params.row.invoicedAmount
  439. return (
  440. <span>
  441. $
  442. {cumulativeExpenditure.toLocaleString(undefined, {
  443. minimumFractionDigits: 2,
  444. maximumFractionDigits: 2,
  445. })}
  446. </span>
  447. );
  448. },
  449. },
  450. {
  451. id: "manhourExpense",
  452. field: "manhourExpense",
  453. headerName: t("Manpower Expenses") + t("HKD"),
  454. minWidth: 280,
  455. type: "number",
  456. renderCell: (params: any) => {
  457. return (
  458. <span>
  459. $
  460. {params.row.manhourExpense.toLocaleString(undefined, {
  461. minimumFractionDigits: 2,
  462. maximumFractionDigits: 2,
  463. })}
  464. </span>
  465. );
  466. },
  467. },
  468. {
  469. id: "projectExpense",
  470. field: "projectExpense",
  471. headerName: t("Project Expense") + t("HKD"),
  472. minWidth: 280,
  473. type: "number",
  474. renderCell: (params: any) => {
  475. return (
  476. <span>
  477. $
  478. {(params.row.projectExpense ?? 0).toLocaleString(undefined, {
  479. minimumFractionDigits: 2,
  480. maximumFractionDigits: 2,
  481. })}
  482. </span>
  483. );
  484. },
  485. },
  486. {
  487. id: "invoicedAmount",
  488. field: "invoicedAmount",
  489. headerName: t("Total Invoiced Amount") + t("HKD"),
  490. minWidth: 250,
  491. type: "number",
  492. renderCell: (params: any) => {
  493. return (
  494. <span>
  495. $
  496. {params.row.invoicedAmount.toLocaleString(undefined, {
  497. minimumFractionDigits: 2,
  498. maximumFractionDigits: 2,
  499. })}
  500. </span>
  501. );
  502. },
  503. },
  504. {
  505. id: "nonInvoicedAmount",
  506. field: "nonInvoicedAmount",
  507. headerName: t("Total Un-Invoiced Amount") + t("HKD"),
  508. minWidth: 250,
  509. type: "number",
  510. renderCell: (params: any) => {
  511. var uninvoiced = params.row.totalFee - params.row.invoicedAmount
  512. return (
  513. <span>
  514. $
  515. {uninvoiced.toLocaleString(undefined, {
  516. minimumFractionDigits: 2,
  517. maximumFractionDigits: 2,
  518. })}
  519. </span>
  520. );
  521. },
  522. },
  523. {
  524. id: "paidAmount",
  525. field: "paidAmount",
  526. headerName: t("Total Received Amount") + t("HKD"),
  527. minWidth: 250,
  528. type: "number",
  529. renderCell: (params: any) => {
  530. return (
  531. <span>
  532. $
  533. {params.row.paidAmount.toLocaleString(undefined, {
  534. minimumFractionDigits: 2,
  535. maximumFractionDigits: 2,
  536. })}
  537. </span>
  538. );
  539. },
  540. },
  541. ];
  542. return (
  543. <>
  544. <Box sx={{ mt: 3 }}>
  545. <SearchBox
  546. criteria={searchCriteria}
  547. onSearch={(query) => {
  548. setFilteredByProjectRows(
  549. filteredByProjectRows.filter(
  550. (cp:any) =>
  551. cp.projectCode.toLowerCase().includes(query.projectCode.toLowerCase()) &&
  552. cp.projectName.toLowerCase().includes(query.projectName.toLowerCase())
  553. ),
  554. );
  555. }}
  556. />
  557. <div style={{ display: "inline-block", width: "99%", marginLeft: 10 }}>
  558. <CustomDatagrid
  559. rows={filteredByProjectRows}
  560. columns={columns1}
  561. columnWidth={200}
  562. dataGridHeight={300}
  563. />
  564. </div>
  565. {/* <SearchResults<StaffResult> items={filteredStaff} columns={columns} /> */}
  566. </Box>
  567. <Box sx={{ mt: 3 }}>
  568. <SearchBox
  569. criteria={searchCriteria2}
  570. onSearch={(query) => {
  571. setFilteredByClientRows(
  572. filteredByClientRows.filter(
  573. (cp:any) =>
  574. cp.customerCode.toLowerCase().includes(query.customerCode.toLowerCase()) &&
  575. cp.customerName.toLowerCase().includes(query.customerName.toLowerCase())
  576. ),
  577. );
  578. }}
  579. />
  580. <div style={{ display: "inline-block", width: "99%", marginLeft: 10 }}>
  581. <CustomDatagrid
  582. rows={filteredByClientRows}
  583. columns={columns2}
  584. columnWidth={200}
  585. dataGridHeight={300}
  586. />
  587. </div>
  588. </Box>
  589. </>
  590. );
  591. };
  592. export default FinancialStatusByProject;