|
- "use client";
- import * as React from "react";
- import Grid from "@mui/material/Grid";
- import { useState, useEffect, useMemo } from "react";
- import Paper from "@mui/material/Paper";
- import { TFunction } from "i18next";
- import { useTranslation } from "react-i18next";
- import { Card, CardHeader } from "@mui/material";
- import CustomSearchForm from "../CustomSearchForm/CustomSearchForm";
- import CustomDatagrid from "../CustomDatagrid/CustomDatagrid";
- import ReactApexChart from "react-apexcharts";
- import { ApexOptions } from "apexcharts";
- import { GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
- import ReportProblemIcon from "@mui/icons-material/ReportProblem";
- import dynamic from "next/dynamic";
- import "../../app/global.css";
- import { AnyARecord, AnyCnameRecord } from "dns";
- import SearchBox, { Criterion } from "../SearchBox";
- import ProgressByClientSearch from "@/components/ProgressByClientSearch";
- import { Suspense } from "react";
- import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch";
- import { fetchProjectsCashFlow,fetchProjectsCashFlowMonthlyChart,fetchProjectsCashFlowReceivableAndExpenditure,fetchProjectsCashFlowAnticipate,fetchProjectsCashFlowLedger} from "@/app/api/cashflow";
- import { Input, Label } from "reactstrap";
- import { CashFlow } from "@/app/api/cashflow";
- import dayjs from 'dayjs';
- import ProjectTotalFee from "../CreateInvoice_forGen/ProjectTotalFee";
- import Typography from "@mui/material/Typography";
- import { useSearchParams } from 'next/navigation';
-
- interface Props {
- projects: CashFlow[];
- }
- type SearchQuery = Partial<Omit<CashFlow, "id">>;
- type SearchParamNames = keyof SearchQuery;
-
- const ProjectCashFlow: React.FC = () => {
- const { t } = useTranslation("dashboard");
- const searchParams = useSearchParams();
- const projectId = searchParams.get('projectId');
- const todayDate = new Date();
- const [selectionModel, setSelectionModel]: any[] = useState<GridRowSelectionModel>([]);
- const [projectData, setProjectData]: any[] = React.useState([]);
- const [filteredResult, setFilteredResult]:any[] = useState([]);
- const [selectedProjectIdList, setSelectedProjectIdList]: any[] = React.useState([]);
- const [monthlyIncomeList, setMonthlyIncomeList]: any[] = React.useState([]);
- const [monthlyCumulativeIncomeList, setMonthlyCumulativeIncomeList]: any[] = React.useState([]);
- const [monthlyExpenditureList, setMonthlyExpenditureList]: any[] = React.useState([]);
- const [monthlyCumulativeExpenditureList, setMonthlyCumulativeExpenditureList]: any[] = React.useState([]);
- const [monthlyChartLeftMax, setMonthlyChartLeftMax]: any[] = React.useState(10);
- const [monthlyChartRightMax, setMonthlyChartRightMax]: any[] = React.useState(10);
- const [monthlyAnticipateLeftMax, setMonthlyAnticipateLeftMax]: any[] = React.useState(10);
- const [receivedPercentage,setReceivedPercentage]: any[] = React.useState(0);
- const [invoicedPercentage,setInvoicedPercentage]: any[] = React.useState(0);
- const [totalFee,setTotalFee]: any[] = React.useState(0);
- const [totalBudget,setTotalBudget]: any[] = React.useState(0);
- const [totalInvoiced,setTotalInvoiced]: any[] = React.useState(0);
- const [totalReceived,setTotalReceived]: any[] = React.useState(0);
- const [receivable,setReceivable]: any[] = React.useState(0);
- const [totalExpenditure,setTotalExpenditure]: any[] = React.useState(0);
- const [expense, setExpense] = React.useState(0);
- const [expenditureReceivable,setExpenditureReceivable]: any[] = React.useState(0);
- const [expenditurePercentage,setExpenditurePercentage]: any[] = React.useState(0);
- const [monthlyAnticipateIncomeList, setMonthlyAnticipateIncomeList]: any[] = React.useState([0,0,0,0,0,0,0,0,0,0,0,0]);
- const [monthlyAnticipateExpenditureList, setMonthlyAnticipateExpenditureList]: any[] = React.useState([0,0,0,0,0,0,0,0,0,0,0,0]);
- const [ledgerData, setLedgerData]: any[] = React.useState([]);
- const [isInitializing, setIsInitializing] = useState(true);
- const [cashFlowYear, setCashFlowYear]: any[] = React.useState(
- todayDate.getFullYear(),
- );
- const [anticipateCashFlowYear, setAnticipateCashFlowYear]: any[] = React.useState(
- todayDate.getFullYear(),
- );
-
- const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => {
-
- if (!isInitializing) {
- setSelectionModel(newSelectionModel);
- const selectedRowsData = projectData.filter((row: any) =>
- newSelectionModel.includes(row.id)
- );
- const projectIdList = selectedRowsData.map((row: any) => row.id);
- setSelectedProjectIdList(projectIdList);
- }
- };
-
- const fetchData = async () => {
- const cashFlowProject = await fetchProjectsCashFlow();
- setProjectData(cashFlowProject)
- setFilteredResult(cashFlowProject)
- }
- const fetchChartData = async () => {
- const cashFlowMonthlyChartData = await fetchProjectsCashFlowMonthlyChart(selectedProjectIdList,cashFlowYear);
- console.log(cashFlowMonthlyChartData)
- const monthlyIncome = []
- const cumulativeIncome = []
- const monthlyExpenditure = []
- const cumulativeExpenditure = []
- var leftMax = 0
- var rightMax = 0
- if (cashFlowMonthlyChartData.length !== 0) {
- for (var i = 0; i < cashFlowMonthlyChartData[0].incomeList.length; i++) {
- if (leftMax < cashFlowMonthlyChartData[0].incomeList[i].income || leftMax < cashFlowMonthlyChartData[0].expenditureList[i].expenditure){
- leftMax = Math.max(cashFlowMonthlyChartData[0].incomeList[i].income,cashFlowMonthlyChartData[0].expenditureList[i].expenditure)
- }
- monthlyIncome.push(cashFlowMonthlyChartData[0].incomeList[i].income)
- cumulativeIncome.push(cashFlowMonthlyChartData[0].beforeCurrentYearIncome[0].beforeCurrentYearCumulativeIncome + cashFlowMonthlyChartData[0].incomeList[i].cumulativeIncome)
- }
- for (var i = 0; i < cashFlowMonthlyChartData[0].expenditureList.length; i++) {
- if (rightMax < cashFlowMonthlyChartData[0].beforeCurrentYearIncome[0].beforeCurrentYearCumulativeIncome + cashFlowMonthlyChartData[0].incomeList[i].cumulativeIncome || rightMax < cashFlowMonthlyChartData[0].beforeCurrentYearExpenditure[0].beforeCurrentYearCumulativeExpenditure +cashFlowMonthlyChartData[0].expenditureList[i].cumulativeExpenditure){
- rightMax = Math.max(cashFlowMonthlyChartData[0].beforeCurrentYearIncome[0].beforeCurrentYearCumulativeIncome + cashFlowMonthlyChartData[0].incomeList[i].cumulativeIncome,cashFlowMonthlyChartData[0].beforeCurrentYearExpenditure[0].beforeCurrentYearCumulativeExpenditure + cashFlowMonthlyChartData[0].expenditureList[i].cumulativeExpenditure)
- }
- monthlyExpenditure.push(cashFlowMonthlyChartData[0].expenditureList[i].expenditure)
- cumulativeExpenditure.push(cashFlowMonthlyChartData[0].beforeCurrentYearExpenditure[0].beforeCurrentYearCumulativeExpenditure + cashFlowMonthlyChartData[0].expenditureList[i].cumulativeExpenditure)
- }
- setMonthlyIncomeList(monthlyIncome)
- setMonthlyCumulativeIncomeList(cumulativeIncome)
- setMonthlyExpenditureList(monthlyExpenditure)
- setMonthlyCumulativeExpenditureList(cumulativeExpenditure)
- setMonthlyChartLeftMax(leftMax)
- setMonthlyChartRightMax(rightMax)
- } else {
- setMonthlyIncomeList([0,0,0,0,0,0,0,0,0,0,0,0])
- setMonthlyCumulativeIncomeList([0,0,0,0,0,0,0,0,0,0,0,0])
- setMonthlyExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0])
- setMonthlyCumulativeExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0])
- }
-
- }
- const fetchReceivableAndExpenditureData = async () => {
- if (selectedProjectIdList.length === 0) {
- setReceivedPercentage(0)
- setInvoicedPercentage(0)
- setTotalFee(0)
- setTotalInvoiced(0)
- setTotalReceived(0)
- setReceivable(0)
- setExpenditurePercentage(0)
- setTotalBudget(0)
- setTotalExpenditure(0)
- setExpenditureReceivable(0)
- } else {
- const cashFlowReceivableAndExpenditureData = await fetchProjectsCashFlowReceivableAndExpenditure(selectedProjectIdList);
- console.log(cashFlowReceivableAndExpenditureData)
- if(cashFlowReceivableAndExpenditureData.length !== 0){
- setReceivedPercentage(cashFlowReceivableAndExpenditureData[0].receivedPercentage)
- setInvoicedPercentage(cashFlowReceivableAndExpenditureData[0].invoicedPercentage)
- setTotalFee(cashFlowReceivableAndExpenditureData[0].totalProjectFee)
- setTotalInvoiced(cashFlowReceivableAndExpenditureData[0].totalInvoiced)
- setTotalReceived(cashFlowReceivableAndExpenditureData[0].totalReceived)
- setReceivable(cashFlowReceivableAndExpenditureData[0].receivable)
- setExpenditurePercentage(cashFlowReceivableAndExpenditureData[0].expenditurePercentage)
- setTotalBudget(cashFlowReceivableAndExpenditureData[0].totalBudget)
- setTotalExpenditure(cashFlowReceivableAndExpenditureData[0].totalExpenditure)
- setExpenditureReceivable(cashFlowReceivableAndExpenditureData[0].expenditureReceivable)
- setExpense(cashFlowReceivableAndExpenditureData[0].expense)
- }
- }
- }
- const fetchAnticipateData = async () => {
- const cashFlowAnticipateData = await fetchProjectsCashFlowAnticipate(selectedProjectIdList,anticipateCashFlowYear);
- const monthlyAnticipateIncome = []
- var anticipateLeftMax = 0
- if(cashFlowAnticipateData.length !== 0){
- for (var i = 0; i < cashFlowAnticipateData[0].anticipateIncomeList.length; i++) {
- if (anticipateLeftMax < cashFlowAnticipateData[0].anticipateIncomeList[i].anticipateIncome){
- anticipateLeftMax = Math.max(cashFlowAnticipateData[0].anticipateIncomeList[i].anticipateIncome,cashFlowAnticipateData[0].anticipateIncomeList[i].anticipateIncome)
- }
- monthlyAnticipateIncome.push(cashFlowAnticipateData[0].anticipateIncomeList[i].anticipateIncome)
- }
- setMonthlyAnticipateIncomeList(monthlyAnticipateIncome)
- } else {
- setMonthlyAnticipateIncomeList([0,0,0,0,0,0,0,0,0,0,0,0])
- setMonthlyAnticipateExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0])
- }
-
- if(cashFlowAnticipateData.length !== 0){
- console.log(cashFlowAnticipateData)
-
- if (cashFlowAnticipateData[0].anticipateExpenditureList.length !== 0) {
- const anticipateExpenditureList = []
- for (var i = 0; i < cashFlowAnticipateData[0].anticipateExpenditureList.length; i++) {
- const subAnticipateExpenditure = []
- var duration = cashFlowAnticipateData[0].anticipateExpenditureList[i].Duration
- var month = cashFlowAnticipateData[0].anticipateExpenditureList[i].startMonth
- const anticipateExpenditure = cashFlowAnticipateData[0].anticipateExpenditureList[i].aniticipateExpenditure
- for (var j = 1; j < 13; j++){
- if (month === j && duration > 0) {
- subAnticipateExpenditure.push(anticipateExpenditure)
- month = month + 1
- duration = duration - 1
- } else {
- subAnticipateExpenditure.push(0)
- }
- }
- anticipateExpenditureList.push(subAnticipateExpenditure)
- }
- console.log(anticipateExpenditureList[0])
- console.log(anticipateExpenditureList)
- const result = new Array(anticipateExpenditureList[0].length).fill(0);
- for (const arr of anticipateExpenditureList) {
- for (let i = 0; i < arr.length; i++) {
- result[i] += arr[i];
- }
- }
- setMonthlyAnticipateExpenditureList(result)
- for (var i = 0; i < monthlyAnticipateIncome.length; i++) {
- if (anticipateLeftMax < monthlyAnticipateIncome[i] || anticipateLeftMax < result[i]){
- anticipateLeftMax = Math.max(monthlyAnticipateIncome[i],result[i])
- }
- setMonthlyAnticipateLeftMax(anticipateLeftMax)
- }
- } else {
- setMonthlyAnticipateExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0])
- }
-
- }
- }
- const fetchProjectCashFlowLedger = async () => {
- const cashFlowLedgerData = await fetchProjectsCashFlowLedger(selectedProjectIdList);
- setLedgerData(cashFlowLedgerData)
- }
- useEffect(() => {
- if (projectId !== null) {
- setSelectedProjectIdList([parseInt(projectId)])
- setSelectionModel([parseInt(projectId)]);
- }
- fetchData().then(() => {
- setIsInitializing(false);
- });
- }, []);
-
- useEffect(() => {
- fetchChartData();
- fetchReceivableAndExpenditureData();
- fetchAnticipateData();
- fetchProjectCashFlowLedger();
-
- }, [cashFlowYear,selectedProjectIdList]);
-
- useEffect(() => {
- fetchAnticipateData();
-
- }, [anticipateCashFlowYear,selectedProjectIdList]);
-
- const columns = [
- {
- id: "projectCode",
- field: "projectCode",
- headerName: t("Project Code"),
- flex: 1,
- },
- {
- id: "projectName",
- field: "projectName",
- headerName: t("Project Name"),
- flex: 1,
- },
- {
- id: "team",
- field: "team",
- headerName: t("Team"),
- flex: 1,
- },
- {
- id: "teamLead",
- field: "teamLead",
- headerName: t("Team Leader"),
- flex: 1,
- },
- {
- id: "startDate",
- field: "startDate",
- headerName: t("Start Date"),
- flex: 1,
- },
- {
- id: "targetEndDate",
- field: "targetEndDate",
- headerName: t("Target End Date"),
- flex: 1,
- },
- {
- id: "client",
- field: "client",
- headerName: t("Client"),
- flex: 1,
- },
- {
- id: "subsidiary",
- field: "subsidiary",
- headerName: t("Subsidiary"),
- flex: 1,
- },
- ];
-
- const ledgerColumns = [
- {
- id: "date",
- field: "date",
- headerName: t("Date"),
- flex: 0.5,
- },
- {
- id: "expenditure",
- field: "expenditure",
- headerName: t("Manpower Spent(HKD)"),
- flex: 0.6,
- type: "number",
- renderCell: (params:any) => {
- return (
- <span>${params.row.expenditure.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
- )
- }
- },
- {
- id: "expense",
- field: "expense",
- headerName: t("Expense (HKD)"),
- flex: 0.6,
- type: "number",
- renderCell: (params:any) => {
- return (
- <span>${params.row.expense.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
- )
- }
- },
- {
- id: "income",
- field: "income",
- headerName: t("Income (HKD)"),
- flex: 0.6,
- type: "number",
- renderCell: (params:any) => {
- return (
- <span>${params.row.income.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
- )
- }
- },
- {
- id: "balance",
- field: "balance",
- headerName: t("Cash Flow Balance (HKD)"),
- flex: 0.6,
- type: "number",
- renderCell: (params:any) => {
- if (params.row.balance < 0) {
- return (
- <span>(${Math.abs(params.row.balance).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })})</span>
- )
- } else {
- return (
- <span>${params.row.balance.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
- )
- }
- },
- },
- {
- id: "remarks",
- field: "remarks",
- headerName: t("Remarks"),
- flex: 1,
- },
- ];
-
- const options: ApexOptions = {
- tooltip: {
- y: {
- formatter: function(val) {
- return val.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
- }
- }
- },
- chart: {
- height: 350,
- type: "line",
- },
- stroke: {
- width: [0, 0, 2, 2],
- },
- plotOptions: {
- bar: {
- horizontal: false,
- distributed: false,
- },
- },
- dataLabels: {
- enabled: false,
- },
- xaxis: {
- categories: [
- t("JAN"),
- t("FEB"),
- t("MAR"),
- t("APR"),
- t("MAY"),
- t("JUN"),
- t("JUL"),
- t("AUG"),
- t("SEP"),
- t("OCT"),
- t("NOV"),
- t("DEC"),
- ],
- },
- yaxis: [
- {
- title: {
- text: t("Monthly Income and Expenditure (HKD)"),
- style: {
- fontSize: "13px",
- }
- },
- min: 0,
- max: monthlyChartLeftMax,
- tickAmount: 5,
- labels: {
- formatter: function (val) {
- return val.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })
- }
- }
- },
- {
- show: false,
- seriesName: "Monthly Expenditure",
- title: {
- text: "Monthly Expenditure (HKD)",
- },
- min: 0,
- max: monthlyChartLeftMax,
- tickAmount: 5,
- },
- {
- seriesName: "Cumulative Income",
- opposite: true,
- title: {
- text: t("Cumulative Income and Expenditure (HKD)"),
- style: {
- fontSize: "13px",
- }
- },
- min: 0,
- max: monthlyChartRightMax,
- tickAmount: 5,
- labels: {
- formatter: function (val) {
- return val.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })
- }
- }
- },
- {
- show: false,
- seriesName: "Cumulative Expenditure",
- opposite: true,
- title: {
- text: "Cumulative Expenditure (HKD)",
- },
- min: 0,
- max: monthlyChartRightMax,
- tickAmount: 5,
-
- },
- ],
- grid: {
- borderColor: "#f1f1f1",
- },
- annotations: {},
- series: [
- {
- name: t("Monthly Income"),
- type: "column",
- color: "#ffde91",
- data: monthlyIncomeList,
- },
- {
- name: t("Monthly Expenditure"),
- type: "column",
- color: "#82b59a",
- data: monthlyExpenditureList,
- },
- {
- name: t("Cumulative Income"),
- type: "line",
- color: "#EE6D7A",
- data: monthlyCumulativeIncomeList,
- },
- {
- name: t("Cumulative Expenditure"),
- type: "line",
- color: "#7cd3f2",
- data: monthlyCumulativeExpenditureList,
- },
- ],
- };
-
- const anticipateOptions: ApexOptions = {
- tooltip: {
- y: {
- formatter: function(val) {
- return val.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
- }
- }
- },
- chart: {
- height: 350,
- type: "line",
- },
- stroke: {
- width: [0, 0, 2, 2],
- },
- plotOptions: {
- bar: {
- horizontal: false,
- distributed: false,
- },
- },
- dataLabels: {
- enabled: false,
- },
- xaxis: {
- categories: [
- t("JAN"),
- t("FEB"),
- t("MAR"),
- t("APR"),
- t("MAY"),
- t("JUN"),
- t("JUL"),
- t("AUG"),
- t("SEP"),
- t("OCT"),
- t("NOV"),
- t("DEC"),
- ],
- },
- yaxis: [
- {
- title: {
- text: t("Anticipate Monthly Income and Expenditure") + t("HKD"),
- style: {
- fontSize: "13px",
- }
- },
- min: 0,
- max: monthlyAnticipateLeftMax,
- tickAmount: 5,
-
- labels: {
- formatter: function (val) {
- return val.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })
- }
- }
- },
- ],
- grid: {
- borderColor: "#f1f1f1",
- },
- annotations: {},
- series: [
- {
- name: t("Monthly Income"),
- type: "column",
- color: "#f1c48a",
- data: monthlyAnticipateIncomeList,
- },
- {
- name: t("Monthly Expenditure"),
- type: "column",
- color: "#89d7f3",
- data: monthlyAnticipateExpenditureList,
- }
- ],
- };
-
- const accountsReceivableOptions: ApexOptions = {
- colors: ["#20E647"],
- series: [receivedPercentage,invoicedPercentage],
- chart: {
- height: 50,
- type: "radialBar",
- },
- plotOptions: {
- radialBar: {
- hollow: {
- size: "70%",
- background: "#ffffff",
- },
- track: {
- dropShadow: {
- enabled: true,
- top: 2,
- left: 0,
- blur: 4,
- opacity: 0.15,
- },
- },
- dataLabels: {
- name: {
- show: true,
- fontSize: "0.9em",
- },
- value: {
- color: "#3e98c7",
- fontSize: "1.5em",
- show: true,
- },
- total: {
- show: true,
- color: "#20E647",
- fontSize: "0.9em",
- label: t('Receivable') + " / " + t('Invoiced'),
- formatter: function (w:any) {
- return receivedPercentage + "% / " + invoicedPercentage + "%"
- },
- }
- },
- },
- },
- fill: {
- type: "gradient",
- gradient: {
- shade: "dark",
- type: "vertical",
- gradientToColors: ["#87D4F9"],
- stops: [0, 100],
- },
- },
- stroke: {
- lineCap: "round",
- },
- labels: ["Received Amount","Invoiced Amount"],
- };
-
- const expenditureOptions: ApexOptions = {
- colors: ["#20E647"],
- series: [expenditurePercentage],
- chart: {
- height: 350,
- type: "radialBar",
- },
- plotOptions: {
- radialBar: {
- hollow: {
- size: "70%",
- background: "#ffffff",
- },
- track: {
- dropShadow: {
- enabled: true,
- top: 2,
- left: 0,
- blur: 4,
- opacity: 0.15,
- },
- },
- dataLabels: {
- name: {
- show: true,
- fontSize: "0.9em",
- },
- value: {
- color: "#3e98c7",
- fontSize: "1.5em",
- show: true,
- },
- },
- },
- },
- fill: {
- type: "gradient",
- gradient: {
- shade: "dark",
- type: "vertical",
- gradientToColors: ["#87D4F9"],
- stops: [0, 100],
- },
- },
- stroke: {
- lineCap: "round",
- },
- labels: [t("Expenditure")],
- };
-
- const rows = [
- {
- id: 1,
- projectCode: "M1001",
- projectName: "Consultancy Project A",
- team: "XXX",
- teamLeader: "XXX",
- startDate: "01/07/2022",
- targetEndDate: "01/04/2024",
- client: "Client B",
- subsidiary: "N/A",
- },
- {
- id: 2,
- projectCode: "M1301",
- projectName: "Consultancy Project AAAA",
- team: "XXX",
- teamLeader: "XXX",
- startDate: "01/09/2022",
- targetEndDate: "20/02/2024",
- client: "Client C",
- subsidiary: "Subsidiary A",
- },
- {
- id: 3,
- projectCode: "M1354",
- projectName: "Consultancy Project BBB",
- team: "YYY",
- teamLeader: "YYY",
- startDate: "01/02/2023",
- targetEndDate: "31/01/2024",
- client: "Client D",
- subsidiary: "Subsidiary C",
- },
- ];
-
- const ledgerRows = [
- {
- id: 1,
- date: "Feb 2023",
- expenditure: "-",
- income: "100,000.00",
- cashFlowBalance: "100,000.00",
- remarks: "Payment Milestone 1 (10%)",
- },
- {
- id: 2,
- date: "Feb 2023",
- expenditure: "160,000.00",
- income: "-",
- cashFlowBalance: "(60,000.00)",
- remarks: "Monthly Manpower Expenditure",
- },
- {
- id: 3,
- date: "Mar 2023",
- expenditure: "160,000.00",
- income: "-",
- cashFlowBalance: "(180,000.00)",
- remarks: "Monthly Manpower Expenditure",
- },
- {
- id: 4,
- date: "Apr 2023",
- expenditure: "120,000.00",
- income: "-",
- cashFlowBalance: "(300,000.00)",
- remarks: "Monthly Manpower Expenditure",
- },
- {
- id: 5,
- date: "May 2023",
- expenditure: "-",
- income: "200,000.00",
- cashFlowBalance: "(100,000.00)",
- remarks: "Payment Milestone 2 (20%)",
- },
- {
- id: 6,
- date: "May 2023",
- expenditure: "40,000.00",
- income: "-",
- cashFlowBalance: "(140,000.00)",
- remarks: "Monthly Manpower Expenditure",
- },
- ];
-
- const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
- () => [
- { label: t("Project Code"), paramName: "projectCode", type: "text" },
- { label: t("Project Name"), paramName: "projectName", type: "text" },
- {
- label: t("Start Date From"),
- label2: t("Start Date To"),
- paramName: "startDateFrom",
- type: "dateRange",
- },
- ],
- [t],
- );
-
- function isDateInRange(dateToCheck: string, startDate: string, endDate: string): boolean {
- if (!startDate || !endDate) {
- return false;
- }
- const dateToCheckObj = new Date(dateToCheck);
- const startDateObj = new Date(startDate);
- const endDateObj = new Date(endDate);
- return dateToCheckObj >= startDateObj && dateToCheckObj <= endDateObj;
- }
-
- return (
- <>
- {/* <Suspense fallback={<ProgressCashFlowSearch.Loading />}>
- <ProgressCashFlowSearch />
- </Suspense> */}
- <Typography variant="h4" marginInlineEnd={2}>
- {t('Project Cash Flow')}
- </Typography>
- <SearchBox
- criteria={searchCriteria}
- onSearch={(query) => {
- setFilteredResult(
- projectData.filter(
- (cp:any) =>
- cp.projectCode.toLowerCase().includes(query.projectCode.toLowerCase()) &&
- cp.projectName.toLowerCase().includes(query.projectName.toLowerCase()) &&
- (query.startDateFrom || query.startDateFromTo
- ? isDateInRange(cp.startDate, query.startDateFrom, query.startDateFromTo)
- : true)
- ),
- );
- }}
- />
- <CustomDatagrid
- rows={filteredResult}
- columns={columns}
- columnWidth={200}
- dataGridHeight={300}
- checkboxSelection={true}
- onRowSelectionModelChange={handleSelectionChange}
- rowSelectionModel={selectionModel}
- />
- <Grid item sm>
- <div style={{ display: "inline-block", width: "50%" }}>
- <Grid item xs={12} md={12} lg={12}>
- <Card>
- <CardHeader
- className="text-slate-500"
- title= {t("Project Cash Flow by Month")}
- />
- <div style={{ display: "inline-block", width: "99%" }}>
- <div className="inline-block">
- <Label className="text-slate-500 font-medium ml-6">
- {t("Year")}:
- </Label>
- <Input
- id={"cashFlowYear"}
- value={cashFlowYear}
- readOnly={true}
- bsSize="lg"
- className="rounded-md text-base w-12"
- />
- </div>
- <div className="inline-block ml-1">
- <button
- onClick={() => setCashFlowYear(cashFlowYear - 1)}
- className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base"
- >
- <
- </button>
- </div>
- <div className="inline-block ml-1">
- <button
- onClick={() => setCashFlowYear(cashFlowYear + 1)}
- className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base"
- >
- >
- </button>
- </div>
- <ReactApexChart
- options={options}
- series={options.series}
- type="line"
- height="auto"
- />
- </div>
- </Card>
- </Grid>
- </div>
- <div
- style={{
- display: "inline-block",
- width: "24%",
- verticalAlign: "top",
- marginLeft: 10,
- }}
- >
- <Grid item xs={12} md={12} lg={12}>
- <Card>
- <CardHeader
- className="text-slate-500"
- title= {t("Accounts Receivable (HKD)")}
- />
- <div style={{ display: "inline-block", width: "99%" }}>
- <ReactApexChart
- options={accountsReceivableOptions}
- series={accountsReceivableOptions.series}
- type="radialBar"
- />
- </div>
- <Card className="ml-2 mr-2 mb-4 rounded-none border-solid border-slate-100">
- <div
- className="text-sm font-medium ml-5 mt-2"
- style={{ color: "#898d8d" }}
- >
- {t("Total Project Fee")}
- </div>
- <div
- className="text-lg font-medium mx-5"
- style={{ color: "#6b87cf", textAlign: "right" }}
- >
- ${totalFee.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
- </div>
- <hr />
- <div
- className="text-sm font-medium ml-5 mt-2"
- style={{ color: "#898d8d" }}
- >
- {t("Total Invoiced Amount")}
- </div>
- <div
- className="text-lg font-medium mx-5"
- style={{ color: "#6b87cf", textAlign: "right" }}
- >
- ${totalInvoiced.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
- </div>
- <hr />
- <div
- className="text-sm font-medium ml-5"
- style={{ color: "#898d8d" }}
- >
- {t("Total Received Amount")}
- </div>
- <div
- className="text-lg font-medium mx-5"
- style={{ color: "#6b87cf", textAlign: "right" }}
- >
- ${totalReceived.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
- </div>
- <hr />
- <div
- className="text-sm font-medium ml-5"
- style={{ color: "#898d8d" }}
- >
- {t("Accounts Receivable")}
- </div>
- <div
- className="text-lg font-medium mx-5 mb-2"
- style={{ color: "#6b87cf", textAlign: "right" }}
- >
- ${receivable.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
- </div>
- </Card>
- </Card>
- </Grid>
- </div>
- <div
- style={{
- display: "inline-block",
- width: "24%",
- verticalAlign: "top",
- marginLeft: 10,
- }}
- >
- <Grid item xs={12} md={12} lg={12}>
- <Card>
- <CardHeader
- className="text-slate-500"
- title={t("Expenditure (HKD)")}
- />
- <ReactApexChart
- options={expenditureOptions}
- series={expenditureOptions.series}
- type="radialBar"
- />
- <Card className="ml-2 mr-2 mb-4 rounded-none border-solid border-slate-100">
- <div
- className="text-sm font-medium ml-5 mt-2"
- style={{ color: "#898d8d" }}
- >
- {t("Total Budget")}
- </div>
- <div
- className="text-lg font-medium mx-5"
- style={{ color: "#6b87cf", textAlign: "right" }}
- >
- ${totalBudget.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
- </div>
- <hr />
- <div
- className="text-sm font-medium ml-5"
- style={{ color: "#898d8d" }}
- >
- {t("Total Cumulative Expenditure")}
- </div>
- <div
- className="text-lg font-medium mx-5"
- style={{ color: "#6b87cf", textAlign: "right" }}
- >
- ${totalExpenditure.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
- </div>
- <hr />
- <div
- className="text-sm font-medium ml-5"
- style={{ color: "#898d8d" }}
- >
- {t("Total Manhours Spent")}
- </div>
- <div
- className="text-lg font-medium mx-5"
- style={{ color: "#6b87cf", textAlign: "right" }}
- >
- ${((totalExpenditure || 0 )-(expense || 0)).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
- </div>
- <hr />
- <div
- className="text-sm font-medium ml-5"
- style={{ color: "#898d8d" }}
- >
- {t("Total Project expense")}
- </div>
- <div
- className="text-lg font-medium mx-5"
- style={{ color: "#6b87cf", textAlign: "right" }}
- >
- ${(expense || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
- </div>
- <hr />
- <div
- className="text-sm font-medium ml-5"
- style={{ color: "#898d8d" }}
- >
- {t("Remaining Budget")}
- </div>
- <div
- className="text-lg font-medium mx-5 mb-2"
- style={{ color: "#6b87cf", textAlign: "right" }}
- >
- ${expenditureReceivable.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
- </div>
- </Card>
- </Card>
- </Grid>
- </div>
- <div
- className="mt-5"
- style={{
- display: "inline-block",
- width: "100%",
- verticalAlign: "top",
- }}
- >
- <Grid item xs={12} md={12} lg={12}>
- <Card>
- <CardHeader
- className="text-slate-500"
- title= {t("Anticipate Cash Flow by Month")}
- />
- <div style={{ display: "inline-block", width: "99%" }}>
- <div className="inline-block">
- <Label className="text-slate-500 font-medium ml-6">
- {t("Year")}:
- </Label>
- <Input
- id={"cashFlowYear"}
- value={anticipateCashFlowYear}
- readOnly={true}
- bsSize="lg"
- className="rounded-md text-base w-12"
- />
- </div>
- <div className="inline-block ml-1">
- <button
- onClick={() => setAnticipateCashFlowYear(anticipateCashFlowYear - 1)}
- className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base"
- >
- <
- </button>
- </div>
- <div className="inline-block ml-1">
- <button
- onClick={() => setAnticipateCashFlowYear(anticipateCashFlowYear + 1)}
- className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base"
- >
- >
- </button>
- </div>
- <ReactApexChart
- options={anticipateOptions}
- series={anticipateOptions.series}
- type="line"
- height="350"
- />
- </div>
- </Card>
- <Card>
- <CardHeader
- className="text-slate-500"
- title= {t("Cash Flow Ledger by Month")}
- />
- <div className="ml-4 mr-4">
- <CustomDatagrid
- rows={ledgerData}
- columns={ledgerColumns}
- columnWidth={200}
- dataGridHeight={300}
- />
- </div>
- </Card>
- </Grid>
- </div>
- </Grid>
- </>
- );
- };
-
- export default ProjectCashFlow;
|