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