| @@ -19,7 +19,7 @@ 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"; | |||||
| import { CardFooter, Input, Label } from "reactstrap"; | |||||
| import Select, { components } from "react-select"; | import Select, { components } from "react-select"; | ||||
| import { fetchTeamCombo, fetchTeamCashFlowChartData } from "@/app/api/teamCashflow"; | import { fetchTeamCombo, fetchTeamCashFlowChartData } from "@/app/api/teamCashflow"; | ||||
| import { VIEW_DASHBOARD_ALL } from "@/middleware"; | import { VIEW_DASHBOARD_ALL } from "@/middleware"; | ||||
| @@ -395,6 +395,11 @@ const CompanyTeamCashFlow: React.FC<Props> = ({ abilities, staff }) => { | |||||
| height="500" | height="500" | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <CardFooter> | |||||
| <Grid sx={{ ml: 2, mb: 2, fontSize: '0.8rem' }}> | |||||
| * Cumulative Expenditure: Manpower Expense + Other Expense | |||||
| </Grid> | |||||
| </CardFooter> | |||||
| </Card> | </Card> | ||||
| </Grid> | </Grid> | ||||
| </div> | </div> | ||||
| @@ -10,15 +10,19 @@ import { TeamResult } from "@/app/api/team"; | |||||
| import { SessionStaff } from "@/config/authConfig"; | import { SessionStaff } from "@/config/authConfig"; | ||||
| interface Props { | interface Props { | ||||
| teams: TeamResult[]; | |||||
| _teams: TeamResult[]; | |||||
| userStaff: SessionStaff; | userStaff: SessionStaff; | ||||
| } | } | ||||
| type SearchQuery = Partial<Omit<CrossTeamChargeReportFilter, "id">>; | type SearchQuery = Partial<Omit<CrossTeamChargeReportFilter, "id">>; | ||||
| type SearchParamNames = keyof SearchQuery; | type SearchParamNames = keyof SearchQuery; | ||||
| const GenerateCrossTeamChargeReport: React.FC<Props> = ({ teams, userStaff }) => { | |||||
| const GenerateCrossTeamChargeReport: React.FC<Props> = ({ _teams, userStaff }) => { | |||||
| const { t } = useTranslation("report"); | const { t } = useTranslation("report"); | ||||
| let teams = _teams | |||||
| if (Boolean(userStaff?.isTeamLead)) { | |||||
| teams.filter(team => team.id == userStaff.teamId) | |||||
| } | |||||
| const teamCombo = teams.map(team => `${team.code} - ${team.name}`) | const teamCombo = teams.map(team => `${team.code} - ${team.name}`) | ||||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | ||||
| @@ -50,7 +54,11 @@ const GenerateCrossTeamChargeReport: React.FC<Props> = ({ teams, userStaff }) => | |||||
| // const projectIndex = projectCombo.findIndex(({value}) => value === parseInt(query.project)) | // const projectIndex = projectCombo.findIndex(({value}) => value === parseInt(query.project)) | ||||
| const teamIndex = teamCombo.findIndex(team => team === query.team) | const teamIndex = teamCombo.findIndex(team => team === query.team) | ||||
| const response = await fetchCrossTeamChargeReport({ month: query.month, teamId: teamIndex >= 0 ? teams[teamIndex].id : "All", }) | |||||
| const response = await fetchCrossTeamChargeReport( | |||||
| { | |||||
| month: query.month, | |||||
| teamId: teamIndex >= 0 ? teams[teamIndex].id : "All", | |||||
| }) | |||||
| if (response) { | if (response) { | ||||
| downloadFile(new Uint8Array(response.blobValue), response.filename!!) | downloadFile(new Uint8Array(response.blobValue), response.filename!!) | ||||
| } | } | ||||
| @@ -9,10 +9,15 @@ interface SubComponents { | |||||
| } | } | ||||
| const GenerateCrossTeamChargeReportWrapper: React.FC & SubComponents = async () => { | const GenerateCrossTeamChargeReportWrapper: React.FC & SubComponents = async () => { | ||||
| const [ | |||||
| teams, | |||||
| userStaff | |||||
| ] = await Promise.all([ | |||||
| fetchTeam(), | |||||
| fetchUserStaff() | |||||
| ]) | |||||
| const [teams, userStaff] = await Promise.all([fetchTeam(), fetchUserStaff()]) | |||||
| return <GenerateCrossTeamChargeReport teams={!Boolean(userStaff?.isTeamLead) ? teams : teams.filter(team => team.id === userStaff?.teamId)} userStaff={userStaff}/>; | |||||
| return <GenerateCrossTeamChargeReport _teams={!Boolean(userStaff?.isTeamLead) ? teams : teams.filter(team => team.id === userStaff?.teamId)} userStaff={userStaff}/>; | |||||
| }; | }; | ||||
| GenerateCrossTeamChargeReportWrapper.Loading = GenerateCrossTeamChargeReportLoading; | GenerateCrossTeamChargeReportWrapper.Loading = GenerateCrossTeamChargeReportLoading; | ||||
| @@ -47,6 +47,7 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| const [projectBudget, setProjectBudget]:any = React.useState(0); | const [projectBudget, setProjectBudget]:any = React.useState(0); | ||||
| const [expenditure, setExpenditure]:any = React.useState(0); | const [expenditure, setExpenditure]:any = React.useState(0); | ||||
| const [remainingBudget, setRemainingBudget]:any = React.useState(0); | const [remainingBudget, setRemainingBudget]:any = React.useState(0); | ||||
| const [expense, setExpense] = React.useState(0); | |||||
| const [status, setStatus]:any = React.useState("NA"); | const [status, setStatus]:any = React.useState("NA"); | ||||
| const [plannedResources, setPlannedResources]:any = React.useState(0); | const [plannedResources, setPlannedResources]:any = React.useState(0); | ||||
| const [actualResourcesSpent, setActualResourcesSpent]:any = React.useState(0); | const [actualResourcesSpent, setActualResourcesSpent]:any = React.useState(0); | ||||
| @@ -105,6 +106,7 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| setPlannedResources(result[0].summaryInformation[0].plannedResources) | setPlannedResources(result[0].summaryInformation[0].plannedResources) | ||||
| setActualResourcesSpent(result[0].summaryInformation[0].resourcesSpent) | setActualResourcesSpent(result[0].summaryInformation[0].resourcesSpent) | ||||
| setRemainingResources(result[0].summaryInformation[0].remainingResources) | setRemainingResources(result[0].summaryInformation[0].remainingResources) | ||||
| setExpense(result[0].summaryInformation[0].expense) | |||||
| console.log(result); | console.log(result); | ||||
| const mainStageResult = result[0].summaryMainStage | const mainStageResult = result[0].summaryMainStage | ||||
| const subStageResult = result[0].summarySubStage | const subStageResult = result[0].summarySubStage | ||||
| @@ -661,10 +663,37 @@ const columns2 = [ | |||||
| <u> | <u> | ||||
| {t("Cumulative Expenditure")} | {t("Cumulative Expenditure")} | ||||
| </u> | </u> | ||||
| <br/> | |||||
| {/* <u> | |||||
| {t(" (Manpower + Other Expense)")} | |||||
| </u> */} | |||||
| </div> | </div> | ||||
| <div style={infoDataStyle}> | <div style={infoDataStyle}> | ||||
| HKD ${expenditure.toFixed(2)} | HKD ${expenditure.toFixed(2)} | ||||
| </div> | </div> | ||||
| {/* <div style={infoDataStyle}> | |||||
| {`HKD $(${(expenditure - expense).toFixed(2)} + ${expense.toFixed(2)})`} | |||||
| </div> */} | |||||
| </div> | |||||
| <div style={{ display: "inline-block", width: "33%"}}> | |||||
| <div style={infoHeaderStyle}> | |||||
| <u> | |||||
| {t("Manpower Expense")} | |||||
| </u> | |||||
| </div> | |||||
| <div style={infoDataStyle}> | |||||
| HKD ${(expenditure - expense).toFixed(2)} | |||||
| </div> | |||||
| </div> | |||||
| <div style={{ display: "inline-block", width: "33%"}}> | |||||
| <div style={infoHeaderStyle}> | |||||
| <u> | |||||
| {t("Other Expense")} | |||||
| </u> | |||||
| </div> | |||||
| <div style={infoDataStyle}> | |||||
| HKD ${(expense).toFixed(2)} | |||||
| </div> | |||||
| </div> | </div> | ||||
| <div style={{ display: "inline-block", width: "33%"}}> | <div style={{ display: "inline-block", width: "33%"}}> | ||||
| <div style={infoHeaderStyle}> | <div style={infoHeaderStyle}> | ||||