| @@ -31,7 +31,9 @@ | |||
| "react-dom": "^18", | |||
| "react-hook-form": "^7.49.2", | |||
| "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": { | |||
| "@types/lodash": "^4.14.202", | |||
| @@ -2194,6 +2196,11 @@ | |||
| "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": { | |||
| "version": "0.0.1", | |||
| "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", | |||
| @@ -4433,6 +4440,11 @@ | |||
| "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": { | |||
| "version": "2.0.0", | |||
| "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", | |||
| @@ -5455,6 +5467,11 @@ | |||
| "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": { | |||
| "version": "7.49.2", | |||
| "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", | |||
| "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": { | |||
| "version": "4.4.5", | |||
| "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", | |||
| @@ -5538,6 +5589,23 @@ | |||
| "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": { | |||
| "version": "1.0.0", | |||
| "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", | |||
| @@ -6561,6 +6629,19 @@ | |||
| "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": { | |||
| "version": "1.0.2", | |||
| "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", | |||
| @@ -6583,6 +6664,14 @@ | |||
| "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": { | |||
| "version": "2.4.0", | |||
| "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", | |||
| @@ -32,7 +32,9 @@ | |||
| "react-dom": "^18", | |||
| "react-hook-form": "^7.49.2", | |||
| "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": { | |||
| "@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 WorkHistory from "@mui/icons-material/WorkHistory"; | |||
| 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 Task from "@mui/icons-material/Task"; | |||
| import Assignment from "@mui/icons-material/Assignment"; | |||
| @@ -31,9 +34,10 @@ interface NavigationItem { | |||
| const navigationItems: NavigationItem[] = [ | |||
| { icon: <WorkHistory />, label: "User Workspace", path: "/home" }, | |||
| { 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: "ClaimApproval", path: "/staffReimbursement/ClaimApproval"}, | |||
| @@ -19,20 +19,12 @@ import SearchBox, { Criterion } from "../SearchBox"; | |||
| import ProgressByClientSearch from "@/components/ProgressByClientSearch"; | |||
| import { Suspense } from "react"; | |||
| import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch"; | |||
| import {Input,Label} from "reactstrap"; | |||
| const ProjectCashFlow: React.FC = () => { | |||
| const todayDate = new Date; | |||
| 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 = [ | |||
| { | |||
| 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 = { | |||
| chart: { | |||
| height: 350, | |||
| type: 'line', | |||
| }, | |||
| stroke: { | |||
| width: [0, 0, 2, 2] | |||
| }, | |||
| plotOptions: { | |||
| bar: { | |||
| horizontal: false, | |||
| @@ -114,27 +142,48 @@ const ProjectCashFlow: React.FC = () => { | |||
| '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: { | |||
| borderColor: '#f1f1f1', | |||
| }, | |||
| @@ -142,40 +191,147 @@ const ProjectCashFlow: React.FC = () => { | |||
| }, | |||
| series:[ | |||
| { | |||
| name:"Monthly Income", | |||
| name:"Monthly_Income", | |||
| type:"column", | |||
| 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", | |||
| color: "#82b59d", | |||
| color: "#82b59a", | |||
| data:[0,160000,120000,120000,55000,55000,55000,55000,55000,70000,55000,55000] | |||
| }, | |||
| { | |||
| name:"Cumulative Income", | |||
| name:"Cumulative_Income", | |||
| type:"line", | |||
| 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", | |||
| 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"}, | |||
| {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 [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 selectedRowsData = selectedTeamData.filter((row:any) => | |||
| const selectedRowsData = projectData.filter((row:any) => | |||
| newSelectionModel.includes(row.id) | |||
| ); | |||
| console.log(selectedRowsData) | |||
| @@ -186,22 +342,95 @@ const ProjectCashFlow: React.FC = () => { | |||
| <Suspense fallback={<ProgressCashFlowSearch.Loading />}> | |||
| <ProgressCashFlowSearch/> | |||
| </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> | |||
| <div style={{display:"inline-block",width:"50%"}}> | |||
| <Grid item xs={12} md={12} lg={12}> | |||
| <Card> | |||
| <CardHeader className="text-slate-500" title="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"> | |||
| 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 | |||
| options={options} | |||
| series={options.series} | |||
| height={350} | |||
| 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="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> | |||
| </> | |||
| ); | |||