@@ -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> | |||
</> | |||
); | |||