| @@ -31,7 +31,9 @@ | |||||
| "react-dom": "^18", | "react-dom": "^18", | ||||
| "react-hook-form": "^7.49.2", | "react-hook-form": "^7.49.2", | ||||
| "react-i18next": "^13.5.0", | "react-i18next": "^13.5.0", | ||||
| "react-intl": "^6.5.5" | |||||
| "react-intl": "^6.5.5", | |||||
| "react-select": "^5.8.0", | |||||
| "reactstrap": "^9.2.2" | |||||
| }, | }, | ||||
| "devDependencies": { | "devDependencies": { | ||||
| "@types/lodash": "^4.14.202", | "@types/lodash": "^4.14.202", | ||||
| @@ -2194,6 +2196,11 @@ | |||||
| "node": ">= 6" | "node": ">= 6" | ||||
| } | } | ||||
| }, | }, | ||||
| "node_modules/classnames": { | |||||
| "version": "2.5.1", | |||||
| "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", | |||||
| "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" | |||||
| }, | |||||
| "node_modules/client-only": { | "node_modules/client-only": { | ||||
| "version": "0.0.1", | "version": "0.0.1", | ||||
| "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", | "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", | ||||
| @@ -4433,6 +4440,11 @@ | |||||
| "node": ">=10" | "node": ">=10" | ||||
| } | } | ||||
| }, | }, | ||||
| "node_modules/memoize-one": { | |||||
| "version": "6.0.0", | |||||
| "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", | |||||
| "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" | |||||
| }, | |||||
| "node_modules/merge-stream": { | "node_modules/merge-stream": { | ||||
| "version": "2.0.0", | "version": "2.0.0", | ||||
| "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", | "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", | ||||
| @@ -5455,6 +5467,11 @@ | |||||
| "react": "^18.2.0" | "react": "^18.2.0" | ||||
| } | } | ||||
| }, | }, | ||||
| "node_modules/react-fast-compare": { | |||||
| "version": "3.2.2", | |||||
| "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", | |||||
| "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" | |||||
| }, | |||||
| "node_modules/react-hook-form": { | "node_modules/react-hook-form": { | ||||
| "version": "7.49.2", | "version": "7.49.2", | ||||
| "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.49.2.tgz", | "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.49.2.tgz", | ||||
| @@ -5523,6 +5540,40 @@ | |||||
| "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", | "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", | ||||
| "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" | "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" | ||||
| }, | }, | ||||
| "node_modules/react-popper": { | |||||
| "version": "2.3.0", | |||||
| "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", | |||||
| "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", | |||||
| "dependencies": { | |||||
| "react-fast-compare": "^3.0.1", | |||||
| "warning": "^4.0.2" | |||||
| }, | |||||
| "peerDependencies": { | |||||
| "@popperjs/core": "^2.0.0", | |||||
| "react": "^16.8.0 || ^17 || ^18", | |||||
| "react-dom": "^16.8.0 || ^17 || ^18" | |||||
| } | |||||
| }, | |||||
| "node_modules/react-select": { | |||||
| "version": "5.8.0", | |||||
| "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.8.0.tgz", | |||||
| "integrity": "sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==", | |||||
| "dependencies": { | |||||
| "@babel/runtime": "^7.12.0", | |||||
| "@emotion/cache": "^11.4.0", | |||||
| "@emotion/react": "^11.8.1", | |||||
| "@floating-ui/dom": "^1.0.1", | |||||
| "@types/react-transition-group": "^4.4.0", | |||||
| "memoize-one": "^6.0.0", | |||||
| "prop-types": "^15.6.0", | |||||
| "react-transition-group": "^4.3.0", | |||||
| "use-isomorphic-layout-effect": "^1.1.2" | |||||
| }, | |||||
| "peerDependencies": { | |||||
| "react": "^16.8.0 || ^17.0.0 || ^18.0.0", | |||||
| "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" | |||||
| } | |||||
| }, | |||||
| "node_modules/react-transition-group": { | "node_modules/react-transition-group": { | ||||
| "version": "4.4.5", | "version": "4.4.5", | ||||
| "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", | "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", | ||||
| @@ -5538,6 +5589,23 @@ | |||||
| "react-dom": ">=16.6.0" | "react-dom": ">=16.6.0" | ||||
| } | } | ||||
| }, | }, | ||||
| "node_modules/reactstrap": { | |||||
| "version": "9.2.2", | |||||
| "resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-9.2.2.tgz", | |||||
| "integrity": "sha512-4KroiGOdqZLAnMGzHjpErW3G7bLB+QbKzzMLIDXydPIV0y74lpdL7WtXHkLWAGInd97WCPNx4+R0NQDPyzIfhw==", | |||||
| "dependencies": { | |||||
| "@babel/runtime": "^7.12.5", | |||||
| "@popperjs/core": "^2.6.0", | |||||
| "classnames": "^2.2.3", | |||||
| "prop-types": "^15.5.8", | |||||
| "react-popper": "^2.2.4", | |||||
| "react-transition-group": "^4.4.2" | |||||
| }, | |||||
| "peerDependencies": { | |||||
| "react": ">=16.8.0", | |||||
| "react-dom": ">=16.8.0" | |||||
| } | |||||
| }, | |||||
| "node_modules/read-cache": { | "node_modules/read-cache": { | ||||
| "version": "1.0.0", | "version": "1.0.0", | ||||
| "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", | "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", | ||||
| @@ -6561,6 +6629,19 @@ | |||||
| "browserslist": ">= 4.21.0" | "browserslist": ">= 4.21.0" | ||||
| } | } | ||||
| }, | }, | ||||
| "node_modules/use-isomorphic-layout-effect": { | |||||
| "version": "1.1.2", | |||||
| "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", | |||||
| "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", | |||||
| "peerDependencies": { | |||||
| "react": "^16.8.0 || ^17.0.0 || ^18.0.0" | |||||
| }, | |||||
| "peerDependenciesMeta": { | |||||
| "@types/react": { | |||||
| "optional": true | |||||
| } | |||||
| } | |||||
| }, | |||||
| "node_modules/util-deprecate": { | "node_modules/util-deprecate": { | ||||
| "version": "1.0.2", | "version": "1.0.2", | ||||
| "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", | ||||
| @@ -6583,6 +6664,14 @@ | |||||
| "node": ">=0.10.0" | "node": ">=0.10.0" | ||||
| } | } | ||||
| }, | }, | ||||
| "node_modules/warning": { | |||||
| "version": "4.0.3", | |||||
| "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", | |||||
| "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", | |||||
| "dependencies": { | |||||
| "loose-envify": "^1.0.0" | |||||
| } | |||||
| }, | |||||
| "node_modules/watchpack": { | "node_modules/watchpack": { | ||||
| "version": "2.4.0", | "version": "2.4.0", | ||||
| "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", | "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", | ||||
| @@ -32,7 +32,9 @@ | |||||
| "react-dom": "^18", | "react-dom": "^18", | ||||
| "react-hook-form": "^7.49.2", | "react-hook-form": "^7.49.2", | ||||
| "react-i18next": "^13.5.0", | "react-i18next": "^13.5.0", | ||||
| "react-intl": "^6.5.5" | |||||
| "react-intl": "^6.5.5", | |||||
| "react-select": "^5.8.0", | |||||
| "reactstrap": "^9.2.2" | |||||
| }, | }, | ||||
| "devDependencies": { | "devDependencies": { | ||||
| "@types/lodash": "^4.14.202", | "@types/lodash": "^4.14.202", | ||||
| @@ -0,0 +1,28 @@ | |||||
| import { Metadata } from "next"; | |||||
| import { I18nProvider } from "@/i18n"; | |||||
| import DashboardPage from "@/components/DashboardPage/DashboardPage"; | |||||
| import DashboardPageButton from "@/components/DashboardPage/DashboardTabButton"; | |||||
| import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch"; | |||||
| import { Suspense} from "react"; | |||||
| import Tabs, { TabsProps } from "@mui/material/Tabs"; | |||||
| import Tab from "@mui/material/Tab"; | |||||
| import Typography from "@mui/material/Typography"; | |||||
| import CompanyTeamCashFlowComponent from '@/components/CompanyTeamCashFlow' | |||||
| export const metadata: Metadata = { | |||||
| title: "Project Status by Client", | |||||
| }; | |||||
| const CompanyTeamCashFlow: React.FC = () => { | |||||
| return ( | |||||
| <I18nProvider namespaces={["dashboard"]}> | |||||
| <Typography variant="h4" marginInlineEnd={2}> | |||||
| Company / Team Cash Flow | |||||
| </Typography> | |||||
| <CompanyTeamCashFlowComponent/> | |||||
| </I18nProvider> | |||||
| ); | |||||
| }; | |||||
| export default CompanyTeamCashFlow; | |||||
| @@ -0,0 +1,285 @@ | |||||
| "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 {Input,Label} from "reactstrap"; | |||||
| import Select, {components} from 'react-select' | |||||
| const CompanyTeamCashFlow: React.FC = () => { | |||||
| const todayDate = new Date; | |||||
| const [selectionModel, setSelectionModel] : any[] = React.useState([]); | |||||
| const [cashFlowYear, setCashFlowYear] : any[] = React.useState(todayDate.getFullYear()); | |||||
| const teamOptions = [ | |||||
| { value: 1, label: 'XXX Team' }, | |||||
| { value: 2, label: 'YYY Team' }, | |||||
| { value: 3, label: 'ZZZ Team' } | |||||
| ] | |||||
| const columns = [ | |||||
| { | |||||
| id: 'projectCode', | |||||
| field: 'projectCode', | |||||
| headerName: "Project Code", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| id: 'projectName', | |||||
| field: 'projectName', | |||||
| headerName: "Project Name", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| id: 'team', | |||||
| field: 'team', | |||||
| headerName: "Team", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| id: 'teamLeader', | |||||
| field: 'teamLeader', | |||||
| headerName: "Team Leader", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| id: 'startDate', | |||||
| field: 'startDate', | |||||
| headerName: "Start Date", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| id: 'targetEndDate', | |||||
| field: 'targetEndDate', | |||||
| headerName: "Target End Date", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| id: 'client', | |||||
| field: 'client', | |||||
| headerName: "Client", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| id: 'subsidiary', | |||||
| field: 'subsidiary', | |||||
| headerName: "Subsidiary", | |||||
| flex: 1, | |||||
| }, | |||||
| ]; | |||||
| const ledgerColumns = [ | |||||
| { | |||||
| id: 'date', | |||||
| field: 'date', | |||||
| headerName: "Date", | |||||
| flex: 0.5, | |||||
| }, | |||||
| { | |||||
| id: 'expenditure', | |||||
| field: 'expenditure', | |||||
| headerName: "Expenditure (HKD)", | |||||
| flex: 0.6, | |||||
| }, | |||||
| { | |||||
| id: 'income', | |||||
| field: 'income', | |||||
| headerName: "Income (HKD)", | |||||
| flex: 0.6, | |||||
| }, | |||||
| { | |||||
| id: 'cashFlowBalance', | |||||
| field: 'cashFlowBalance', | |||||
| headerName: "Cash Flow Balance (HKD)", | |||||
| flex: 0.6, | |||||
| }, | |||||
| { | |||||
| id: 'remarks', | |||||
| field: 'remarks', | |||||
| headerName: "Remarks", | |||||
| flex: 1, | |||||
| }, | |||||
| ]; | |||||
| const options: ApexOptions = { | |||||
| chart: { | |||||
| height: 350, | |||||
| type: 'line', | |||||
| }, | |||||
| stroke: { | |||||
| width: [0, 0, 2, 2] | |||||
| }, | |||||
| plotOptions: { | |||||
| bar: { | |||||
| horizontal: false, | |||||
| distributed: false, | |||||
| }, | |||||
| }, | |||||
| dataLabels: { | |||||
| enabled: false | |||||
| }, | |||||
| xaxis: { | |||||
| categories: [ | |||||
| 'Q1', | |||||
| 'Q2', | |||||
| 'Q3', | |||||
| 'Q4', | |||||
| 'Q5', | |||||
| 'Q6', | |||||
| 'Q7', | |||||
| 'Q8', | |||||
| 'Q9', | |||||
| 'Q10', | |||||
| 'Q11', | |||||
| 'Q12', | |||||
| ], | |||||
| }, | |||||
| yaxis: [ | |||||
| { | |||||
| title: { | |||||
| text: 'Monthly Income and Expenditure(HKD)' | |||||
| }, | |||||
| min: 0, | |||||
| max: 3700000, | |||||
| tickAmount: 5 | |||||
| }, | |||||
| { | |||||
| show:false, | |||||
| seriesName: 'Monthly_Expenditure', | |||||
| title: { | |||||
| text: 'Monthly Expenditure (HKD)' | |||||
| }, | |||||
| min: 0, | |||||
| max: 3700000, | |||||
| tickAmount: 5 | |||||
| }, | |||||
| { | |||||
| seriesName: 'Cumulative_Income', | |||||
| opposite: true, | |||||
| title: { | |||||
| text: 'Cumulative Income and Expenditure(HKD)' | |||||
| }, | |||||
| min: 0, | |||||
| max: 21000000, | |||||
| tickAmount: 5 | |||||
| }, | |||||
| { | |||||
| show:false, | |||||
| seriesName: 'Cumulative_Expenditure', | |||||
| opposite: true, | |||||
| title: { | |||||
| text: 'Cumulative Expenditure (HKD)' | |||||
| }, | |||||
| min: 0, | |||||
| max: 21000000, | |||||
| tickAmount: 5 | |||||
| } | |||||
| ], | |||||
| grid: { | |||||
| borderColor: '#f1f1f1', | |||||
| }, | |||||
| annotations: { | |||||
| }, | |||||
| series:[ | |||||
| { | |||||
| name:"Monthly_Income", | |||||
| type:"column", | |||||
| color: "#ffde91", | |||||
| data:[1280000,170000,3600000,2400000,1000000,1800000,1800000,1200000,1250000,1200000,600000,2400000], | |||||
| }, | |||||
| { | |||||
| name:"Monthly_Expenditure", | |||||
| type:"column", | |||||
| color: "#82b59a", | |||||
| data:[1200000,1400000,2000000,1400000,1450000,1800000,1200000,1400000,1200000,1600000,2000000,1600000] | |||||
| }, | |||||
| { | |||||
| name:"Cumulative_Income", | |||||
| type:"line", | |||||
| color: "#EE6D7A", | |||||
| data:[500000,3000000,7000000,9000000,10000000,13000000,14000000,16000000,17000000,17500000,18000000,20000000] | |||||
| }, | |||||
| { | |||||
| name:"Cumulative_Expenditure", | |||||
| type:"line", | |||||
| color: "#7cd3f2", | |||||
| data:[400000,2800000,4000000,5200000,7100000,8000000,10000000,11000000,12100000,14000000,15400000,17200000] | |||||
| } | |||||
| ] | |||||
| }; | |||||
| return ( | |||||
| <> | |||||
| <Grid item sm> | |||||
| <div style={{display:"inline-block",width:"100%"}}> | |||||
| <Grid item xs={12} md={12} lg={12}> | |||||
| <Card> | |||||
| <CardHeader className="text-slate-500" title="Company and Team Cash Flow by Month"/> | |||||
| <div style={{display:"inline-block",width:"99%"}}> | |||||
| <div className="inline-block"> | |||||
| <Label className="text-slate-500 font-medium ml-6"> | |||||
| Period: | |||||
| </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> | |||||
| <div className="inline-block ml-2"> | |||||
| <Label className="text-slate-500 font-medium"> | |||||
| Team: | |||||
| </Label> | |||||
| </div> | |||||
| <div className="inline-block ml-1 w-60"> | |||||
| <Select | |||||
| placeholder= "All Team" | |||||
| options={teamOptions} | |||||
| isClearable={true} | |||||
| /> | |||||
| </div> | |||||
| <ReactApexChart | |||||
| options={options} | |||||
| series={options.series} | |||||
| type="line" | |||||
| height="500" | |||||
| /> | |||||
| </div> | |||||
| </Card> | |||||
| </Grid> | |||||
| </div> | |||||
| </Grid> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default CompanyTeamCashFlow; | |||||
| @@ -0,0 +1 @@ | |||||
| export { default } from "./CompanyTeamCashFlow"; | |||||
| @@ -7,6 +7,9 @@ import ListItemText from "@mui/material/ListItemText"; | |||||
| import ListItemIcon from "@mui/material/ListItemIcon"; | import ListItemIcon from "@mui/material/ListItemIcon"; | ||||
| import WorkHistory from "@mui/icons-material/WorkHistory"; | import WorkHistory from "@mui/icons-material/WorkHistory"; | ||||
| import Dashboard from "@mui/icons-material/Dashboard"; | import Dashboard from "@mui/icons-material/Dashboard"; | ||||
| import SummarizeIcon from '@mui/icons-material/Summarize'; | |||||
| import PaymentsIcon from '@mui/icons-material/Payments'; | |||||
| import AccountTreeIcon from '@mui/icons-material/AccountTree'; | |||||
| import RequestQuote from "@mui/icons-material/RequestQuote"; | import RequestQuote from "@mui/icons-material/RequestQuote"; | ||||
| import Task from "@mui/icons-material/Task"; | import Task from "@mui/icons-material/Task"; | ||||
| import Assignment from "@mui/icons-material/Assignment"; | import Assignment from "@mui/icons-material/Assignment"; | ||||
| @@ -31,9 +34,10 @@ interface NavigationItem { | |||||
| const navigationItems: NavigationItem[] = [ | const navigationItems: NavigationItem[] = [ | ||||
| { icon: <WorkHistory />, label: "User Workspace", path: "/home" }, | { icon: <WorkHistory />, label: "User Workspace", path: "/home" }, | ||||
| { icon: <Dashboard />, label: "Dashboard", path: "", children: [ | { icon: <Dashboard />, label: "Dashboard", path: "", children: [ | ||||
| { icon: <Dashboard />, label: "Project Financial Summary", path: "/dashboard/ProjectFinancialSummary" }, | |||||
| { icon: <Dashboard />, label: "Project Cash Flow", path: "/dashboard/ProjectCashFlow" }, | |||||
| { icon: <Dashboard />, label: "Project Status by Client", path: "/dashboard/ProjectStatusByClient" }, | |||||
| { icon: <SummarizeIcon />, label: "Project Financial Summary", path: "/dashboard/ProjectFinancialSummary" }, | |||||
| { icon: <PaymentsIcon />, label: "Company / Team Cash Flow", path: "/dashboard/CompanyTeamCashFlow" }, | |||||
| { icon: <PaymentsIcon />, label: "Project Cash Flow", path: "/dashboard/ProjectCashFlow" }, | |||||
| { icon: <AccountTreeIcon />, label: "Project Status by Client", path: "/dashboard/ProjectStatusByClient" }, | |||||
| ]}, | ]}, | ||||
| { icon: <RequestQuote />, label: "Staff Reimbursement", path: "/staffReimbursement", children: [ | { icon: <RequestQuote />, label: "Staff Reimbursement", path: "/staffReimbursement", children: [ | ||||
| { icon: <RequestQuote />, label: "ClaimApproval", path: "/staffReimbursement/ClaimApproval"}, | { icon: <RequestQuote />, label: "ClaimApproval", path: "/staffReimbursement/ClaimApproval"}, | ||||
| @@ -19,20 +19,12 @@ import SearchBox, { Criterion } from "../SearchBox"; | |||||
| import ProgressByClientSearch from "@/components/ProgressByClientSearch"; | import ProgressByClientSearch from "@/components/ProgressByClientSearch"; | ||||
| import { Suspense } from "react"; | import { Suspense } from "react"; | ||||
| import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch"; | import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch"; | ||||
| import {Input,Label} from "reactstrap"; | |||||
| const ProjectCashFlow: React.FC = () => { | const ProjectCashFlow: React.FC = () => { | ||||
| const todayDate = new Date; | |||||
| const [selectionModel, setSelectionModel] : any[] = React.useState([]); | const [selectionModel, setSelectionModel] : any[] = React.useState([]); | ||||
| // const series: ApexAxisChartSeries | ApexNonAxisChartSeries = [{ | |||||
| // name: 'Monthly Income', | |||||
| // type: 'line', | |||||
| // data: [80, 55, 40, 65, 70], | |||||
| // }, | |||||
| // { | |||||
| // name: 'Monthly Incomess', | |||||
| // type: 'column', | |||||
| // data: [80, 55, 40, 65, 70], | |||||
| // } | |||||
| // ]; | |||||
| const [cashFlowYear, setCashFlowYear] : any[] = React.useState(todayDate.getFullYear()); | |||||
| const columns = [ | const columns = [ | ||||
| { | { | ||||
| id: 'projectCode', | id: 'projectCode', | ||||
| @@ -84,11 +76,47 @@ const ProjectCashFlow: React.FC = () => { | |||||
| }, | }, | ||||
| ]; | ]; | ||||
| const ledgerColumns = [ | |||||
| { | |||||
| id: 'date', | |||||
| field: 'date', | |||||
| headerName: "Date", | |||||
| flex: 0.5, | |||||
| }, | |||||
| { | |||||
| id: 'expenditure', | |||||
| field: 'expenditure', | |||||
| headerName: "Expenditure (HKD)", | |||||
| flex: 0.6, | |||||
| }, | |||||
| { | |||||
| id: 'income', | |||||
| field: 'income', | |||||
| headerName: "Income (HKD)", | |||||
| flex: 0.6, | |||||
| }, | |||||
| { | |||||
| id: 'cashFlowBalance', | |||||
| field: 'cashFlowBalance', | |||||
| headerName: "Cash Flow Balance (HKD)", | |||||
| flex: 0.6, | |||||
| }, | |||||
| { | |||||
| id: 'remarks', | |||||
| field: 'remarks', | |||||
| headerName: "Remarks", | |||||
| flex: 1, | |||||
| }, | |||||
| ]; | |||||
| const options: ApexOptions = { | const options: ApexOptions = { | ||||
| chart: { | chart: { | ||||
| height: 350, | height: 350, | ||||
| type: 'line', | type: 'line', | ||||
| }, | }, | ||||
| stroke: { | |||||
| width: [0, 0, 2, 2] | |||||
| }, | |||||
| plotOptions: { | plotOptions: { | ||||
| bar: { | bar: { | ||||
| horizontal: false, | horizontal: false, | ||||
| @@ -114,27 +142,48 @@ const ProjectCashFlow: React.FC = () => { | |||||
| 'Q12', | 'Q12', | ||||
| ], | ], | ||||
| }, | }, | ||||
| yaxis: [{ | |||||
| title: { | |||||
| text: 'Monthly Income and Expenditure (HKD)' | |||||
| yaxis: [ | |||||
| { | |||||
| title: { | |||||
| text: 'Monthly Income and Expenditure(HKD)' | |||||
| }, | |||||
| min: 0, | |||||
| max: 350000, | |||||
| tickAmount: 5 | |||||
| }, | }, | ||||
| labels: { | |||||
| maxWidth: 300, | |||||
| style: { | |||||
| cssClass: 'apexcharts-yaxis-label', | |||||
| { | |||||
| show:false, | |||||
| seriesName: 'Monthly_Expenditure', | |||||
| title: { | |||||
| text: 'Monthly Expenditure (HKD)' | |||||
| }, | }, | ||||
| min: 0, | |||||
| max: 350000, | |||||
| tickAmount: 5 | |||||
| }, | }, | ||||
| }, | |||||
| { | |||||
| opposite: true, | |||||
| title: { | |||||
| text: 'Cumulative Income and Expenditure (HKD)' | |||||
| }} | |||||
| { | |||||
| seriesName: 'Cumulative_Income', | |||||
| opposite: true, | |||||
| title: { | |||||
| text: 'Cumulative Income and Expenditure(HKD)' | |||||
| }, | |||||
| min: 0, | |||||
| max: 850000, | |||||
| tickAmount: 5 | |||||
| }, | |||||
| { | |||||
| show:false, | |||||
| seriesName: 'Cumulative_Expenditure', | |||||
| opposite: true, | |||||
| title: { | |||||
| text: 'Cumulative Expenditure (HKD)' | |||||
| }, | |||||
| min: 0, | |||||
| max: 850000, | |||||
| tickAmount: 5 | |||||
| } | |||||
| ], | ], | ||||
| title: { | |||||
| text: 'Current Stage Completion Percentage', | |||||
| align: 'center' | |||||
| }, | |||||
| grid: { | grid: { | ||||
| borderColor: '#f1f1f1', | borderColor: '#f1f1f1', | ||||
| }, | }, | ||||
| @@ -142,40 +191,147 @@ const ProjectCashFlow: React.FC = () => { | |||||
| }, | }, | ||||
| series:[ | series:[ | ||||
| { | { | ||||
| name:"Monthly Income", | |||||
| name:"Monthly_Income", | |||||
| type:"column", | type:"column", | ||||
| color: "#ffde91", | color: "#ffde91", | ||||
| data:[0,110000,0,0,185000,0,0,189000,0,0,300000,0] | |||||
| data:[0,110000,0,0,185000,0,0,189000,0,0,300000,0], | |||||
| }, | }, | ||||
| { | { | ||||
| name:"Monthly Expenditure", | |||||
| name:"Monthly_Expenditure", | |||||
| type:"column", | type:"column", | ||||
| color: "#82b59d", | |||||
| color: "#82b59a", | |||||
| data:[0,160000,120000,120000,55000,55000,55000,55000,55000,70000,55000,55000] | data:[0,160000,120000,120000,55000,55000,55000,55000,55000,70000,55000,55000] | ||||
| }, | }, | ||||
| { | { | ||||
| name:"Cumulative Income", | |||||
| name:"Cumulative_Income", | |||||
| type:"line", | type:"line", | ||||
| color: "#EE6D7A", | color: "#EE6D7A", | ||||
| data:[1,2,3,5,6,9,8,5,6,1,16,15] | |||||
| data:[0,100000,100000,100000,300000,300000,300000,500000,500000,500000,800000,800000] | |||||
| }, | }, | ||||
| { | { | ||||
| name:"Cumulative Expenditure", | |||||
| name:"Cumulative_Expenditure", | |||||
| type:"line", | type:"line", | ||||
| color: "#EE6D7A", | |||||
| data:[1,2,3,5,6,9,8,5,6,1,16,15] | |||||
| color: "#7cd3f2", | |||||
| data:[0,198000,240000,400000,410000,430000,510000,580000,600000,710000,730000,790000] | |||||
| } | } | ||||
| ] | ] | ||||
| }; | }; | ||||
| const accountsReceivableOptions: ApexOptions = { | |||||
| colors: ["#20E647"], | |||||
| series: [80], | |||||
| 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:false, | |||||
| }, | |||||
| value: { | |||||
| color: "#3e98c7", | |||||
| fontSize: "3em", | |||||
| show: true | |||||
| } | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| fill: { | |||||
| type: "gradient", | |||||
| gradient: { | |||||
| shade: "dark", | |||||
| type: "vertical", | |||||
| gradientToColors: ["#87D4F9"], | |||||
| stops: [0, 100] | |||||
| } | |||||
| }, | |||||
| stroke: { | |||||
| lineCap: "round" | |||||
| }, | |||||
| labels: ['AccountsReceivable'], | |||||
| }; | |||||
| const expenditureOptions: ApexOptions = { | |||||
| colors: ["#20E647"], | |||||
| series: [95], | |||||
| 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:false, | |||||
| }, | |||||
| value: { | |||||
| color: "#3e98c7", | |||||
| fontSize: "3em", | |||||
| show: true | |||||
| } | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| fill: { | |||||
| type: "gradient", | |||||
| gradient: { | |||||
| shade: "dark", | |||||
| type: "vertical", | |||||
| gradientToColors: ["#87D4F9"], | |||||
| stops: [0, 100] | |||||
| } | |||||
| }, | |||||
| stroke: { | |||||
| lineCap: "round" | |||||
| }, | |||||
| labels: ['AccountsReceivable'], | |||||
| }; | |||||
| 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"}, | 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: 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"} | {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 [selectedTeamData, setSelectedTeamData] : any[] = React.useState(rows); | |||||
| 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 [projectData, setProjectData] : any[] = React.useState(rows); | |||||
| const [ledgerData, setLedgerData] : any[] = React.useState(ledgerRows); | |||||
| const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => { | const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => { | ||||
| const selectedRowsData = selectedTeamData.filter((row:any) => | |||||
| const selectedRowsData = projectData.filter((row:any) => | |||||
| newSelectionModel.includes(row.id) | newSelectionModel.includes(row.id) | ||||
| ); | ); | ||||
| console.log(selectedRowsData) | console.log(selectedRowsData) | ||||
| @@ -186,22 +342,95 @@ const ProjectCashFlow: React.FC = () => { | |||||
| <Suspense fallback={<ProgressCashFlowSearch.Loading />}> | <Suspense fallback={<ProgressCashFlowSearch.Loading />}> | ||||
| <ProgressCashFlowSearch/> | <ProgressCashFlowSearch/> | ||||
| </Suspense> | </Suspense> | ||||
| <CustomDatagrid rows={selectedTeamData} columns={columns} columnWidth={200} dataGridHeight={300} checkboxSelection={true} onRowSelectionModelChange={handleSelectionChange} selectionModel={selectionModel}/> | |||||
| <CustomDatagrid rows={projectData} columns={columns} columnWidth={200} dataGridHeight={300} checkboxSelection={true} onRowSelectionModelChange={handleSelectionChange} selectionModel={selectionModel}/> | |||||
| <Grid item sm> | <Grid item sm> | ||||
| <div style={{display:"inline-block",width:"50%"}}> | <div style={{display:"inline-block",width:"50%"}}> | ||||
| <Grid item xs={12} md={12} lg={12}> | <Grid item xs={12} md={12} lg={12}> | ||||
| <Card> | <Card> | ||||
| <CardHeader className="text-slate-500" title="Project Cash Flow by Month"/> | <CardHeader className="text-slate-500" title="Project Cash Flow by Month"/> | ||||
| <div style={{display:"inline-block",width:"99%"}}> | <div style={{display:"inline-block",width:"99%"}}> | ||||
| <div className="inline-block"> | |||||
| <Label className="text-slate-500 font-medium ml-6"> | |||||
| Period: | |||||
| </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 | <ReactApexChart | ||||
| options={options} | options={options} | ||||
| series={options.series} | series={options.series} | ||||
| height={350} | |||||
| type="line" | |||||
| height="auto" | |||||
| /> | /> | ||||
| </div> | </div> | ||||
| </Card> | </Card> | ||||
| </Grid> | </Grid> | ||||
| </div> | </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="Accounts Receivable (HKD)"/> | |||||
| <ReactApexChart | |||||
| options={accountsReceivableOptions} | |||||
| series={accountsReceivableOptions.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"}}>Total A. Receivable</div> | |||||
| <div className="text-lg font-medium ml-5" style={{color:"#6b87cf"}}>1,000,000.00</div><hr/> | |||||
| <div className="text-sm font-medium ml-5" style={{color:"#898d8d"}}>Amount Received</div> | |||||
| <div className="text-lg font-medium ml-5" style={{color:"#6b87cf"}}>800,000.00</div><hr/> | |||||
| <div className="text-sm font-medium ml-5" style={{color:"#898d8d"}}>Remaining Balance</div> | |||||
| <div className="text-lg font-medium ml-5 mb-2" style={{color:"#6b87cf"}}>200,000.00</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="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"}}>Budgeted Expenditure</div> | |||||
| <div className="text-lg font-medium ml-5" style={{color:"#6b87cf"}}>800,000.00</div><hr/> | |||||
| <div className="text-sm font-medium ml-5" style={{color:"#898d8d"}}>Actual Expenditure</div> | |||||
| <div className="text-lg font-medium ml-5" style={{color:"#6b87cf"}}>760,000.00</div><hr/> | |||||
| <div className="text-sm font-medium ml-5" style={{color:"#898d8d"}}>Remaining Balance</div> | |||||
| <div className="text-lg font-medium ml-5 mb-2" style={{color:"#6b87cf"}}>40,000.00</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="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> | </Grid> | ||||
| </> | </> | ||||
| ); | ); | ||||