| @@ -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 ProgressByTeamSearch from "@/components/ProgressByTeamSearch"; | |||||
| import { Suspense } from "react"; | |||||
| import Tabs, { TabsProps } from "@mui/material/Tabs"; | |||||
| import Tab from "@mui/material/Tab"; | |||||
| import Typography from "@mui/material/Typography"; | |||||
| import ProjectResourceConsumptionRankingDisplay from "@/components/ProjectResourceConsumptionRanking"; | |||||
| import { preloadClientProjects } from "@/app/api/clientprojects"; | |||||
| export const metadata: Metadata = { | |||||
| title: "Project Resource Consumption Ranking", | |||||
| }; | |||||
| const ProjectResourceConsumptionRanking: React.FC = () => { | |||||
| preloadClientProjects(); | |||||
| return ( | |||||
| <I18nProvider namespaces={["dashboard"]}> | |||||
| <Typography variant="h4" marginInlineEnd={2}> | |||||
| Project Resource Consumption Ranking | |||||
| </Typography> | |||||
| <ProjectResourceConsumptionRankingDisplay/> | |||||
| </I18nProvider> | |||||
| ); | |||||
| }; | |||||
| export default ProjectResourceConsumptionRanking; | |||||
| @@ -39,6 +39,8 @@ export interface CashFlowReceivableAndExpenditure { | |||||
| totalBudget: number; | totalBudget: number; | ||||
| totalExpenditure: number; | totalExpenditure: number; | ||||
| expenditureReceivable: number; | expenditureReceivable: number; | ||||
| totalProjectFee: number; | |||||
| invoicedPercentage: number; | |||||
| } | } | ||||
| export interface CashFlowAnticipatedChartResult { | export interface CashFlowAnticipatedChartResult { | ||||
| anticipateIncomeList: any[]; | anticipateIncomeList: any[]; | ||||
| @@ -21,14 +21,14 @@ export interface ClientSubsidiaryProjectResult { | |||||
| comingPaymentMilestone: string; | comingPaymentMilestone: string; | ||||
| } | } | ||||
| export const fetchAllClientSubsidiaryProjects = cache(async (customerId: number, subsidiaryId?: number) => { | |||||
| export const fetchAllClientSubsidiaryProjects = cache(async (customerId: number, tableSorting:string, subsidiaryId?: number) => { | |||||
| if (subsidiaryId === 0){ | if (subsidiaryId === 0){ | ||||
| return serverFetchJson<ClientSubsidiaryProjectResult[]>( | return serverFetchJson<ClientSubsidiaryProjectResult[]>( | ||||
| `${BASE_API_URL}/dashboard/searchCustomerSubsidiaryProject?customerId=${customerId}` | |||||
| `${BASE_API_URL}/dashboard/searchCustomerSubsidiaryProject?customerId=${customerId}&tableSorting=${tableSorting}` | |||||
| ); | ); | ||||
| } else { | } else { | ||||
| return serverFetchJson<ClientSubsidiaryProjectResult[]>( | return serverFetchJson<ClientSubsidiaryProjectResult[]>( | ||||
| `${BASE_API_URL}/dashboard/searchCustomerSubsidiaryProject?customerId=${customerId}&subsidiaryId=${subsidiaryId}` | |||||
| `${BASE_API_URL}/dashboard/searchCustomerSubsidiaryProject?customerId=${customerId}&subsidiaryId=${subsidiaryId}&tableSorting=${tableSorting}` | |||||
| ); | ); | ||||
| } | } | ||||
| @@ -21,9 +21,43 @@ export interface ClientSubsidiaryProjectResult { | |||||
| comingPaymentMilestone: string; | comingPaymentMilestone: string; | ||||
| } | } | ||||
| export const fetchAllTeamProjects = cache(async (teamLeadId: number) => { | |||||
| export interface TeamConsumptionResult { | |||||
| color: string; | |||||
| team: string; | |||||
| teamLead: string; | |||||
| budgetedManhour: number; | |||||
| spentManhour: number; | |||||
| remainedManhour: number; | |||||
| manhourConsumptionPercentage: number; | |||||
| } | |||||
| export interface TeamProjectResult { | |||||
| id: number; | |||||
| teamId: number; | |||||
| teamLeadId: number; | |||||
| teamCode: string; | |||||
| teamName: string; | |||||
| projectNo: number; | |||||
| } | |||||
| export const fetchTeamProjects = cache(async () => { | |||||
| return serverFetchJson<TeamProjectResult[]>(`${BASE_API_URL}/dashboard/searchTeamProjectNo`); | |||||
| }); | |||||
| export const fetchAllTeamProjects = cache(async (teamLeadId: number, tableSorting:string) => { | |||||
| return serverFetchJson<ClientSubsidiaryProjectResult[]>( | return serverFetchJson<ClientSubsidiaryProjectResult[]>( | ||||
| `${BASE_API_URL}/dashboard/searchTeamProject?teamLeadId=${teamLeadId}` | |||||
| `${BASE_API_URL}/dashboard/searchTeamProject?teamLeadId=${teamLeadId}&tableSorting=${tableSorting}` | |||||
| ); | ); | ||||
| }); | }); | ||||
| export const fetchAllTeamConsumption = cache(async (teamIdList: number[],tableSorting:string) => { | |||||
| if (teamIdList.length !== 0) { | |||||
| const queryParams = new URLSearchParams(); | |||||
| queryParams.append('teamIdList', teamIdList.join(',')); | |||||
| return serverFetchJson<TeamConsumptionResult[]>(`${BASE_API_URL}/dashboard/searchTeamConsumption?${queryParams.toString()}&tableSorting=${tableSorting}`); | |||||
| } else { | |||||
| return []; | |||||
| } | |||||
| }); | |||||
| @@ -21,6 +21,7 @@ const pathToLabelMap: { [path: string]: string } = { | |||||
| "/dashboard/ProjectResourceSummary": "Project Resource Summary", | "/dashboard/ProjectResourceSummary": "Project Resource Summary", | ||||
| "/dashboard/ProjectStatusByClient": "Project Status by Client", | "/dashboard/ProjectStatusByClient": "Project Status by Client", | ||||
| "/dashboard/ProjectStatusByTeam": "Project Status by Team", | "/dashboard/ProjectStatusByTeam": "Project Status by Team", | ||||
| "/dashboard/ProjectResourceConsumptionRanking": "Project Resource Consumption Ranking", | |||||
| "/dashboard/StaffUtilization": "Staff Utilization", | "/dashboard/StaffUtilization": "Staff Utilization", | ||||
| "/projects": "Projects", | "/projects": "Projects", | ||||
| "/projects/create": "Create Project", | "/projects/create": "Create Project", | ||||
| @@ -127,6 +127,11 @@ const NavigationContent: React.FC<Props> = ({ abilities, username }) => { | |||||
| label: "Project Status by Team", | label: "Project Status by Team", | ||||
| path: "/dashboard/ProjectStatusByTeam", | path: "/dashboard/ProjectStatusByTeam", | ||||
| }, | }, | ||||
| { | |||||
| icon: <AccountTreeIcon />, | |||||
| label: "Project Resource Consumption Ranking", | |||||
| path: "/dashboard/ProjectResourceConsumptionRanking", | |||||
| }, | |||||
| { | { | ||||
| icon: <PeopleIcon />, | icon: <PeopleIcon />, | ||||
| label: "Staff Utilization", | label: "Staff Utilization", | ||||
| @@ -59,6 +59,9 @@ const ProgressByClient: React.FC<Props> = () => { | |||||
| const [receiptToDate, setReceiptToDate] = useState(null); | const [receiptToDate, setReceiptToDate] = useState(null); | ||||
| const [selectedRows, setSelectedRows]:any[] = useState([]); | const [selectedRows, setSelectedRows]:any[] = useState([]); | ||||
| const [chartProjectName, setChartProjectName]:any[] = useState([]); | const [chartProjectName, setChartProjectName]:any[] = useState([]); | ||||
| const [chartProjectDisplayName, setChartProjectDisplayName]:any[] = useState([]); | |||||
| const [chartProjectBudgetedHour, setChartProjectBudgetedHour]:any[] = useState([]); | |||||
| const [chartProjectSpentHour, setChartProjectSpentHour]:any[] = useState([]); | |||||
| const [chartManhourConsumptionPercentage, setChartManhourConsumptionPercentage]:any[] = useState([]); | const [chartManhourConsumptionPercentage, setChartManhourConsumptionPercentage]:any[] = useState([]); | ||||
| const color = ["#f57f90", "#94f7d6", "#87c5f5", "#ab95f5", "#fcd68b", | const color = ["#f57f90", "#94f7d6", "#87c5f5", "#ab95f5", "#fcd68b", | ||||
| "#f58a9b", "#8ef4d1", "#92caf9", "#a798f9", "#fad287", | "#f58a9b", "#8ef4d1", "#92caf9", "#a798f9", "#fad287", | ||||
| @@ -86,6 +89,7 @@ const ProgressByClient: React.FC<Props> = () => { | |||||
| "#d4feed", "#0ab2ff", "#ff88a2", "#4fda21", "#cefb2f", | "#d4feed", "#0ab2ff", "#ff88a2", "#4fda21", "#cefb2f", | ||||
| "#d1fef0", "#04afff", "#ff859e", "#4bdd15", "#ccfb2b"]; | "#d1fef0", "#04afff", "#ff859e", "#4bdd15", "#ccfb2b"]; | ||||
| const [clientSubsidiaryProjectResult, setClientSubsidiaryProjectResult]:any[] = useState([]); | const [clientSubsidiaryProjectResult, setClientSubsidiaryProjectResult]:any[] = useState([]); | ||||
| const [tableSorting, setTableSorting] = useState('ProjectName'); | |||||
| const fetchData = async () => { | const fetchData = async () => { | ||||
| if (customerId && subsidiaryId) { | if (customerId && subsidiaryId) { | ||||
| @@ -93,12 +97,13 @@ const ProgressByClient: React.FC<Props> = () => { | |||||
| if (subsidiaryId === '-'){ | if (subsidiaryId === '-'){ | ||||
| console.log("ss") | console.log("ss") | ||||
| const clickResult = await fetchAllClientSubsidiaryProjects( | const clickResult = await fetchAllClientSubsidiaryProjects( | ||||
| Number(customerId),Number(0)) | |||||
| Number(customerId),tableSorting,Number(0)) | |||||
| console.log(clickResult) | console.log(clickResult) | ||||
| setClientSubsidiaryProjectResult(clickResult); | setClientSubsidiaryProjectResult(clickResult); | ||||
| } else { | } else { | ||||
| const clickResult = await fetchAllClientSubsidiaryProjects( | const clickResult = await fetchAllClientSubsidiaryProjects( | ||||
| Number(customerId), | Number(customerId), | ||||
| tableSorting, | |||||
| Number(subsidiaryId)) | Number(subsidiaryId)) | ||||
| console.log(clickResult) | console.log(clickResult) | ||||
| setClientSubsidiaryProjectResult(clickResult); | setClientSubsidiaryProjectResult(clickResult); | ||||
| @@ -113,19 +118,28 @@ const ProgressByClient: React.FC<Props> = () => { | |||||
| useEffect(() => { | useEffect(() => { | ||||
| const projectCode = [] | const projectCode = [] | ||||
| const projectName = [] | |||||
| const projectBudgetedManHour = [] | |||||
| const projectSpentManHour = [] | |||||
| const manhourConsumptionPercentage = [] | const manhourConsumptionPercentage = [] | ||||
| for (let i = 0; i < clientSubsidiaryProjectResult.length; i++){ | for (let i = 0; i < clientSubsidiaryProjectResult.length; i++){ | ||||
| clientSubsidiaryProjectResult[i].color = color[i] | clientSubsidiaryProjectResult[i].color = color[i] | ||||
| projectCode.push(clientSubsidiaryProjectResult[i].projectCode) | |||||
| projectCode.push(clientSubsidiaryProjectResult[i].projectCode + "(" + clientSubsidiaryProjectResult[i].team + ")") | |||||
| projectName.push(clientSubsidiaryProjectResult[i].projectName) | |||||
| projectBudgetedManHour.push(clientSubsidiaryProjectResult[i].budgetedManhour) | |||||
| projectSpentManHour.push(clientSubsidiaryProjectResult[i].spentManhour) | |||||
| manhourConsumptionPercentage.push(clientSubsidiaryProjectResult[i].manhourConsumptionPercentage) | manhourConsumptionPercentage.push(clientSubsidiaryProjectResult[i].manhourConsumptionPercentage) | ||||
| } | } | ||||
| setChartProjectName(projectCode) | setChartProjectName(projectCode) | ||||
| setChartProjectDisplayName(projectName) | |||||
| setChartProjectBudgetedHour(projectBudgetedManHour) | |||||
| setChartProjectSpentHour(projectSpentManHour) | |||||
| setChartManhourConsumptionPercentage(manhourConsumptionPercentage) | setChartManhourConsumptionPercentage(manhourConsumptionPercentage) | ||||
| }, [clientSubsidiaryProjectResult]); | }, [clientSubsidiaryProjectResult]); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| fetchData() | fetchData() | ||||
| }, [customerId,subsidiaryId]); | |||||
| }, [customerId,subsidiaryId,tableSorting]); | |||||
| @@ -383,7 +397,31 @@ const ProgressByClient: React.FC<Props> = () => { | |||||
| const options: ApexOptions = { | const options: ApexOptions = { | ||||
| chart: { | chart: { | ||||
| type: "bar", | type: "bar", | ||||
| height: 350, | |||||
| height: 450, | |||||
| }, | |||||
| tooltip: { | |||||
| enabled: true, // Enable tooltip | |||||
| custom: ({ series, seriesIndex, dataPointIndex, w }) => { | |||||
| const projectCode = chartProjectName[dataPointIndex]; | |||||
| const projectName = chartProjectDisplayName[dataPointIndex]; | |||||
| const budgetManhours = chartProjectBudgetedHour[dataPointIndex]; | |||||
| const spentManhours = chartProjectSpentHour[dataPointIndex]; | |||||
| const value = series[seriesIndex][dataPointIndex]; | |||||
| const tooltipContent = ` | |||||
| <div style="width: 250px;"> | |||||
| <span style="font-weight: bold;">${projectCode} - ${projectName}</span> | |||||
| <br> | |||||
| Budget Manhours: ${budgetManhours} hours | |||||
| <br> | |||||
| Spent Manhours: ${spentManhours} hours | |||||
| <br> | |||||
| Percentage: ${value}% | |||||
| </div> | |||||
| `; | |||||
| return tooltipContent; | |||||
| }, | |||||
| }, | }, | ||||
| series: [{ | series: [{ | ||||
| name: "Project Resource Consumption Percentage", | name: "Project Resource Consumption Percentage", | ||||
| @@ -418,10 +456,15 @@ const ProgressByClient: React.FC<Props> = () => { | |||||
| bar: { | bar: { | ||||
| horizontal: true, | horizontal: true, | ||||
| distributed: true, | distributed: true, | ||||
| dataLabels: { | |||||
| position: 'top' | |||||
| }, | |||||
| }, | }, | ||||
| }, | }, | ||||
| dataLabels: { | dataLabels: { | ||||
| enabled: false, | |||||
| enabled: true, | |||||
| textAnchor: 'end', | |||||
| formatter: (val) => `${val}%`, | |||||
| }, | }, | ||||
| xaxis: { | xaxis: { | ||||
| categories: chartProjectName, | categories: chartProjectName, | ||||
| @@ -443,6 +486,11 @@ const ProgressByClient: React.FC<Props> = () => { | |||||
| }, | }, | ||||
| grid: { | grid: { | ||||
| borderColor: "#f1f1f1", | borderColor: "#f1f1f1", | ||||
| xaxis: { | |||||
| lines: { | |||||
| show: true, | |||||
| } | |||||
| } | |||||
| }, | }, | ||||
| annotations: {}, | annotations: {}, | ||||
| }; | }; | ||||
| @@ -517,11 +565,35 @@ const ProgressByClient: React.FC<Props> = () => { | |||||
| <Card> | <Card> | ||||
| <CardHeader className="text-slate-500" title="Project Resource Consumption" /> | <CardHeader className="text-slate-500" title="Project Resource Consumption" /> | ||||
| <div style={{ display: "inline-block", width: "99%" }}> | <div style={{ display: "inline-block", width: "99%" }}> | ||||
| <div className="ml-6 text-sm"> | |||||
| <b> | |||||
| Sorting: | |||||
| </b> | |||||
| </div> | |||||
| <div className="ml-6 mb-2"> | |||||
| <button onClick={() => {setTableSorting('PercentageASC')}} | |||||
| className="hover:cursor-pointer hover:bg-violet-50 text-sm bg-transparent border-violet-500 text-violet-500 border-solid w-36" | |||||
| > | |||||
| Percentage ASC | |||||
| </button> | |||||
| <button | |||||
| onClick={() => {setTableSorting('PercentageDESC')}} | |||||
| className="hover:cursor-pointer hover:bg-violet-50 text-sm bg-transparent border-violet-500 text-violet-500 border-solid w-36" | |||||
| > | |||||
| Percentage DESC | |||||
| </button> | |||||
| <button | |||||
| onClick={() => {setTableSorting('ProjectName')}} | |||||
| className="hover:cursor-pointer hover:bg-violet-50 text-sm bg-transparent border-violet-500 text-violet-500 border-solid w-36" | |||||
| > | |||||
| Project Name | |||||
| </button> | |||||
| </div> | |||||
| <ReactApexChart | <ReactApexChart | ||||
| options={options} | options={options} | ||||
| series={options.series} | series={options.series} | ||||
| type="bar" | type="bar" | ||||
| height={350} | |||||
| height={500} | |||||
| /> | /> | ||||
| </div> | </div> | ||||
| {/* <div style={{display:"inline-block",width:"20%",verticalAlign:"top",textAlign:"center"}}> | {/* <div style={{display:"inline-block",width:"20%",verticalAlign:"top",textAlign:"center"}}> | ||||
| @@ -533,6 +605,8 @@ const ProgressByClient: React.FC<Props> = () => { | |||||
| ); | ); | ||||
| })} | })} | ||||
| </div> */} | </div> */} | ||||
| </Card> | |||||
| <Card className="mt-2"> | |||||
| <CardHeader | <CardHeader | ||||
| className="text-slate-500" | className="text-slate-500" | ||||
| title="Resource Consumption and Coming Milestone" | title="Resource Consumption and Coming Milestone" | ||||
| @@ -561,15 +635,15 @@ const ProgressByClient: React.FC<Props> = () => { | |||||
| marginLeft: 0, | marginLeft: 0, | ||||
| }} | }} | ||||
| > | > | ||||
| <Grid item xs={12} md={12} lg={12}> | |||||
| <Card style={{ marginLeft: 15, marginRight: 20 }}> | |||||
| <Grid item xs={12} md={12} lg={12} style={{height:620}}> | |||||
| <Card style={{ marginLeft: 15, marginRight: 20, height:"100%"}}> | |||||
| <CardHeader | <CardHeader | ||||
| className="text-slate-500" | className="text-slate-500" | ||||
| title="Overall Progress per Project" | title="Overall Progress per Project" | ||||
| /> | /> | ||||
| {percentageArray.length === 0 && ( | {percentageArray.length === 0 && ( | ||||
| <div | <div | ||||
| className="mt-10 mb-10 ml-5 mr-5 text-lg font-medium text-center" | |||||
| className="mt-40 mb-10 ml-5 mr-5 text-lg font-medium text-center" | |||||
| style={{ color: "#898d8d" }} | style={{ color: "#898d8d" }} | ||||
| > | > | ||||
| Please select the project you want to check. | Please select the project you want to check. | ||||
| @@ -580,12 +654,13 @@ const ProgressByClient: React.FC<Props> = () => { | |||||
| options={options2} | options={options2} | ||||
| series={percentageArray} | series={percentageArray} | ||||
| type="donut" | type="donut" | ||||
| style={{marginTop:'10rem'}} | |||||
| /> | /> | ||||
| )} | )} | ||||
| </Card> | </Card> | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} md={12} lg={12}> | <Grid item xs={12} md={12} lg={12}> | ||||
| <Card style={{ marginLeft: 15, marginRight: 20, marginTop: 20 }}> | |||||
| <Card style={{ marginLeft: 15, marginRight: 20, marginTop: 20}}> | |||||
| <div> | <div> | ||||
| <div | <div | ||||
| className="mt-5 text-lg font-medium" | className="mt-5 text-lg font-medium" | ||||
| @@ -49,6 +49,9 @@ const ProgressByTeam: React.FC = () => { | |||||
| const [receiptToDate, setReceiptToDate] = useState(null); | const [receiptToDate, setReceiptToDate] = useState(null); | ||||
| const [selectedRows, setSelectedRows] = useState([]); | const [selectedRows, setSelectedRows] = useState([]); | ||||
| const [chartProjectName, setChartProjectName]:any[] = useState([]); | const [chartProjectName, setChartProjectName]:any[] = useState([]); | ||||
| const [chartProjectDisplayName, setChartProjectDisplayName]:any[] = useState([]); | |||||
| const [chartProjectBudgetedHour, setChartProjectBudgetedHour]:any[] = useState([]); | |||||
| const [chartProjectSpentHour, setChartProjectSpentHour]:any[] = useState([]); | |||||
| const [chartManhourConsumptionPercentage, setChartManhourConsumptionPercentage]:any[] = useState([]); | const [chartManhourConsumptionPercentage, setChartManhourConsumptionPercentage]:any[] = useState([]); | ||||
| const color = ["#f57f90", "#94f7d6", "#87c5f5", "#ab95f5", "#fcd68b", | const color = ["#f57f90", "#94f7d6", "#87c5f5", "#ab95f5", "#fcd68b", | ||||
| "#f58a9b", "#8ef4d1", "#92caf9", "#a798f9", "#fad287", | "#f58a9b", "#8ef4d1", "#92caf9", "#a798f9", "#fad287", | ||||
| @@ -77,16 +80,21 @@ const ProgressByTeam: React.FC = () => { | |||||
| "#d1fef0", "#04afff", "#ff859e", "#4bdd15", "#ccfb2b"]; | "#d1fef0", "#04afff", "#ff859e", "#4bdd15", "#ccfb2b"]; | ||||
| const [teamProjectResult, setTeamProjectResult]:any[] = useState([]); | const [teamProjectResult, setTeamProjectResult]:any[] = useState([]); | ||||
| const [currentPageProjectList, setCurrentPageProjectList]: any[] = React.useState([]); | const [currentPageProjectList, setCurrentPageProjectList]: any[] = React.useState([]); | ||||
| const [currentPageProjectNameList, setCurrentPageProjectNameList]: any[] = React.useState([]); | |||||
| const [currentPageProjectBudgetedManhourList, setCurrentPageProjectBudgetedManhourList]: any[] = React.useState([]); | |||||
| const [currentPageProjectSpentManhourList, setCurrentPageProjectSpentManhourList]: any[] = React.useState([]); | |||||
| const [currentPagePercentage, setCurrentPagePercentage]: any[] = React.useState([]); | const [currentPagePercentage, setCurrentPagePercentage]: any[] = React.useState([]); | ||||
| const [currentPageColor, setCurrentPageColor]: any[] = React.useState([]); | const [currentPageColor, setCurrentPageColor]: any[] = React.useState([]); | ||||
| const [currentPage, setCurrentPage] = useState(1); | const [currentPage, setCurrentPage] = useState(1); | ||||
| const recordsPerPage = 10; | const recordsPerPage = 10; | ||||
| const [tableSorting, setTableSorting] = useState('ProjectName'); | |||||
| const fetchData = async () => { | const fetchData = async () => { | ||||
| console.log(tableSorting) | |||||
| if (teamLeadId) { | if (teamLeadId) { | ||||
| try { | try { | ||||
| const clickResult = await fetchAllTeamProjects( | const clickResult = await fetchAllTeamProjects( | ||||
| Number(teamLeadId)) | |||||
| Number(teamLeadId),tableSorting) | |||||
| console.log(clickResult) | console.log(clickResult) | ||||
| setTeamProjectResult(clickResult); | setTeamProjectResult(clickResult); | ||||
| } catch (error) { | } catch (error) { | ||||
| @@ -97,19 +105,29 @@ const ProgressByTeam: React.FC = () => { | |||||
| useEffect(() => { | useEffect(() => { | ||||
| const projectNo = [] | const projectNo = [] | ||||
| const projectName = [] | |||||
| const projectBudgetedManHour = [] | |||||
| const projectSpentManHour = [] | |||||
| const manhourConsumptionPercentage = [] | const manhourConsumptionPercentage = [] | ||||
| for (let i = 0; i < teamProjectResult.length; i++){ | for (let i = 0; i < teamProjectResult.length; i++){ | ||||
| teamProjectResult[i].color = color[i] | teamProjectResult[i].color = color[i] | ||||
| projectNo.push(teamProjectResult[i].projectCode) | |||||
| console.log(teamProjectResult[i]) | |||||
| projectNo.push(teamProjectResult[i].projectCode + "(" + teamProjectResult[i].team + ")") | |||||
| projectName.push(teamProjectResult[i].projectName) | |||||
| projectBudgetedManHour.push(teamProjectResult[i].budgetedManhour) | |||||
| projectSpentManHour.push(teamProjectResult[i].spentManhour) | |||||
| manhourConsumptionPercentage.push(teamProjectResult[i].manhourConsumptionPercentage) | manhourConsumptionPercentage.push(teamProjectResult[i].manhourConsumptionPercentage) | ||||
| } | } | ||||
| setChartProjectName(projectNo) | setChartProjectName(projectNo) | ||||
| setChartProjectDisplayName(projectName) | |||||
| setChartProjectBudgetedHour(projectBudgetedManHour) | |||||
| setChartProjectSpentHour(projectSpentManHour) | |||||
| setChartManhourConsumptionPercentage(manhourConsumptionPercentage) | setChartManhourConsumptionPercentage(manhourConsumptionPercentage) | ||||
| }, [teamProjectResult]); | }, [teamProjectResult]); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| fetchData() | fetchData() | ||||
| }, [teamLeadId]); | |||||
| }, [teamLeadId,tableSorting]); | |||||
| const rows = [ | const rows = [ | ||||
| { | { | ||||
| @@ -462,7 +480,31 @@ const ProgressByTeam: React.FC = () => { | |||||
| const options: ApexOptions = { | const options: ApexOptions = { | ||||
| chart: { | chart: { | ||||
| type: "bar", | type: "bar", | ||||
| height: 350, | |||||
| height: 450, | |||||
| }, | |||||
| tooltip: { | |||||
| enabled: true, // Enable tooltip | |||||
| custom: ({ series, seriesIndex, dataPointIndex, w }) => { | |||||
| const projectCode = currentPageProjectList[dataPointIndex]; | |||||
| const projectName = currentPageProjectNameList[dataPointIndex]; | |||||
| const budgetManhours = currentPageProjectBudgetedManhourList[dataPointIndex]; | |||||
| const spentManhours = currentPageProjectSpentManhourList[dataPointIndex]; | |||||
| const value = series[seriesIndex][dataPointIndex]; | |||||
| const tooltipContent = ` | |||||
| <div style="width: 250px;"> | |||||
| <span style="font-weight: bold;">${projectCode} - ${projectName}</span> | |||||
| <br> | |||||
| Budget Manhours: ${budgetManhours} hours | |||||
| <br> | |||||
| Spent Manhours: ${spentManhours} hours | |||||
| <br> | |||||
| Percentage: ${value}% | |||||
| </div> | |||||
| `; | |||||
| return tooltipContent; | |||||
| }, | |||||
| }, | }, | ||||
| series: [{ | series: [{ | ||||
| name: "Project Resource Consumption Percentage", | name: "Project Resource Consumption Percentage", | ||||
| @@ -473,10 +515,15 @@ const ProgressByTeam: React.FC = () => { | |||||
| bar: { | bar: { | ||||
| horizontal: true, | horizontal: true, | ||||
| distributed: true, | distributed: true, | ||||
| dataLabels: { | |||||
| position: 'top' | |||||
| }, | |||||
| }, | }, | ||||
| }, | }, | ||||
| dataLabels: { | dataLabels: { | ||||
| enabled: false, | |||||
| enabled: true, | |||||
| textAnchor: 'end', | |||||
| formatter: (val) => `${val}%`, | |||||
| }, | }, | ||||
| xaxis: { | xaxis: { | ||||
| categories: currentPageProjectList, | categories: currentPageProjectList, | ||||
| @@ -498,6 +545,11 @@ const ProgressByTeam: React.FC = () => { | |||||
| }, | }, | ||||
| grid: { | grid: { | ||||
| borderColor: "#f1f1f1", | borderColor: "#f1f1f1", | ||||
| xaxis: { | |||||
| lines: { | |||||
| show: true, | |||||
| } | |||||
| } | |||||
| }, | }, | ||||
| annotations: {}, | annotations: {}, | ||||
| }; | }; | ||||
| @@ -564,11 +616,17 @@ const ProgressByTeam: React.FC = () => { | |||||
| useEffect(() => { | useEffect(() => { | ||||
| console.log(chartManhourConsumptionPercentage) | console.log(chartManhourConsumptionPercentage) | ||||
| const currentPageProjectData = chartProjectName.slice(startIndex, endIndex) | const currentPageProjectData = chartProjectName.slice(startIndex, endIndex) | ||||
| const currentPageProjectName = chartProjectDisplayName.slice(startIndex, endIndex) | |||||
| const currentPageProjectBudgetedManhour = chartProjectBudgetedHour.slice(startIndex, endIndex) | |||||
| const currentPageProjectSpentManhour = chartProjectSpentHour.slice(startIndex, endIndex) | |||||
| const currentPageData = chartManhourConsumptionPercentage.slice(startIndex, endIndex); | const currentPageData = chartManhourConsumptionPercentage.slice(startIndex, endIndex); | ||||
| const colorArray = color.slice(startIndex, endIndex); | const colorArray = color.slice(startIndex, endIndex); | ||||
| console.log(currentPage) | console.log(currentPage) | ||||
| console.log(Math.ceil(chartManhourConsumptionPercentage.length / recordsPerPage)) | console.log(Math.ceil(chartManhourConsumptionPercentage.length / recordsPerPage)) | ||||
| setCurrentPageProjectList(currentPageProjectData) | setCurrentPageProjectList(currentPageProjectData) | ||||
| setCurrentPageProjectNameList(currentPageProjectName) | |||||
| setCurrentPageProjectBudgetedManhourList(currentPageProjectBudgetedManhour) | |||||
| setCurrentPageProjectSpentManhourList(currentPageProjectSpentManhour) | |||||
| setCurrentPagePercentage(currentPageData) | setCurrentPagePercentage(currentPageData) | ||||
| setCurrentPageColor(colorArray) | setCurrentPageColor(colorArray) | ||||
| }, [chartManhourConsumptionPercentage,currentPage]); | }, [chartManhourConsumptionPercentage,currentPage]); | ||||
| @@ -598,11 +656,35 @@ const ProgressByTeam: React.FC = () => { | |||||
| <Card> | <Card> | ||||
| <CardHeader className="text-slate-500" title="Project Resource Consumption" /> | <CardHeader className="text-slate-500" title="Project Resource Consumption" /> | ||||
| <div style={{ display: "inline-block", width: "99%" }}> | <div style={{ display: "inline-block", width: "99%" }}> | ||||
| <div className="ml-6 text-sm"> | |||||
| <b> | |||||
| Sorting: | |||||
| </b> | |||||
| </div> | |||||
| <div className="ml-6 mb-2"> | |||||
| <button onClick={() => {setTableSorting('PercentageASC')}} | |||||
| className="hover:cursor-pointer hover:bg-violet-50 text-sm bg-transparent border-violet-500 text-violet-500 border-solid w-36" | |||||
| > | |||||
| Percentage ASC | |||||
| </button> | |||||
| <button | |||||
| onClick={() => {setTableSorting('PercentageDESC')}} | |||||
| className="hover:cursor-pointer hover:bg-violet-50 text-sm bg-transparent border-violet-500 text-violet-500 border-solid w-36" | |||||
| > | |||||
| Percentage DESC | |||||
| </button> | |||||
| <button | |||||
| onClick={() => {setTableSorting('ProjectName')}} | |||||
| className="hover:cursor-pointer hover:bg-violet-50 text-sm bg-transparent border-violet-500 text-violet-500 border-solid w-36" | |||||
| > | |||||
| Project Name | |||||
| </button> | |||||
| </div> | |||||
| <ReactApexChart | <ReactApexChart | ||||
| options={options} | options={options} | ||||
| series={options.series} | series={options.series} | ||||
| type="bar" | type="bar" | ||||
| height={350} | |||||
| height={500} | |||||
| /> | /> | ||||
| <div className="float-right mr-4 mb-10"> | <div className="float-right mr-4 mb-10"> | ||||
| {currentPage === 1 && ( | {currentPage === 1 && ( | ||||
| @@ -646,6 +728,8 @@ const ProgressByTeam: React.FC = () => { | |||||
| ); | ); | ||||
| })} | })} | ||||
| </div> */} | </div> */} | ||||
| </Card> | |||||
| <Card className="mt-2"> | |||||
| <CardHeader | <CardHeader | ||||
| className="text-slate-500" | className="text-slate-500" | ||||
| title="Resource Consumption and Coming Milestone" | title="Resource Consumption and Coming Milestone" | ||||
| @@ -674,16 +758,16 @@ const ProgressByTeam: React.FC = () => { | |||||
| marginLeft: 0, | marginLeft: 0, | ||||
| }} | }} | ||||
| > | > | ||||
| <Grid item xs={12} md={12} lg={12}> | |||||
| <Card style={{ marginLeft: 15, marginRight: 20 }}> | |||||
| <Grid item xs={12} md={12} lg={12} style={{height:710}}> | |||||
| <Card style={{ marginLeft: 15, marginRight: 20, height:"100%"}}> | |||||
| <CardHeader | <CardHeader | ||||
| className="text-slate-500" | className="text-slate-500" | ||||
| title="Overall Progress per Project" | title="Overall Progress per Project" | ||||
| /> | /> | ||||
| {percentageArray.length === 0 && ( | {percentageArray.length === 0 && ( | ||||
| <div | <div | ||||
| className="mt-10 mb-10 ml-5 mr-5 text-lg font-medium text-center" | |||||
| style={{ color: "#898d8d" }} | |||||
| className="mt-40 mb-10 ml-5 mr-5 text-lg font-medium text-center" | |||||
| style={{ color: "#898d8d"}} | |||||
| > | > | ||||
| Please select the project you want to check. | Please select the project you want to check. | ||||
| </div> | </div> | ||||
| @@ -693,12 +777,13 @@ const ProgressByTeam: React.FC = () => { | |||||
| options={options2} | options={options2} | ||||
| series={percentageArray} | series={percentageArray} | ||||
| type="donut" | type="donut" | ||||
| style={{marginTop:'10rem'}} | |||||
| /> | /> | ||||
| )} | )} | ||||
| </Card> | </Card> | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} md={12} lg={12}> | <Grid item xs={12} md={12} lg={12}> | ||||
| <Card style={{ marginLeft: 15, marginRight: 20, marginTop: 20 }}> | |||||
| <Card style={{ marginLeft: 15, marginRight: 20, marginTop: 15 }}> | |||||
| <div> | <div> | ||||
| <div | <div | ||||
| className="mt-5 text-lg font-medium" | className="mt-5 text-lg font-medium" | ||||
| @@ -23,6 +23,7 @@ import { fetchProjectsCashFlow,fetchProjectsCashFlowMonthlyChart,fetchProjectsCa | |||||
| import { Input, Label } from "reactstrap"; | import { Input, Label } from "reactstrap"; | ||||
| import { CashFlow } from "@/app/api/cashflow"; | import { CashFlow } from "@/app/api/cashflow"; | ||||
| import dayjs from 'dayjs'; | import dayjs from 'dayjs'; | ||||
| import ProjectTotalFee from "../CreateInvoice_forGen/ProjectTotalFee"; | |||||
| interface Props { | interface Props { | ||||
| projects: CashFlow[]; | projects: CashFlow[]; | ||||
| @@ -45,6 +46,8 @@ const ProjectCashFlow: React.FC = () => { | |||||
| const [monthlyChartRightMax, setMonthlyChartRightMax]: any[] = React.useState(10); | const [monthlyChartRightMax, setMonthlyChartRightMax]: any[] = React.useState(10); | ||||
| const [monthlyAnticipateLeftMax, setMonthlyAnticipateLeftMax]: any[] = React.useState(10); | const [monthlyAnticipateLeftMax, setMonthlyAnticipateLeftMax]: any[] = React.useState(10); | ||||
| const [receivedPercentage,setReceivedPercentage]: any[] = React.useState(0); | const [receivedPercentage,setReceivedPercentage]: any[] = React.useState(0); | ||||
| const [invoicedPercentage,setInvoicedPercentage]: any[] = React.useState(0); | |||||
| const [totalFee,setTotalFee]: any[] = React.useState(0); | |||||
| const [totalBudget,setTotalBudget]: any[] = React.useState(0); | const [totalBudget,setTotalBudget]: any[] = React.useState(0); | ||||
| const [totalInvoiced,setTotalInvoiced]: any[] = React.useState(0); | const [totalInvoiced,setTotalInvoiced]: any[] = React.useState(0); | ||||
| const [totalReceived,setTotalReceived]: any[] = React.useState(0); | const [totalReceived,setTotalReceived]: any[] = React.useState(0); | ||||
| @@ -121,6 +124,8 @@ const ProjectCashFlow: React.FC = () => { | |||||
| const cashFlowReceivableAndExpenditureData = await fetchProjectsCashFlowReceivableAndExpenditure(selectedProjectIdList); | const cashFlowReceivableAndExpenditureData = await fetchProjectsCashFlowReceivableAndExpenditure(selectedProjectIdList); | ||||
| if(cashFlowReceivableAndExpenditureData.length !== 0){ | if(cashFlowReceivableAndExpenditureData.length !== 0){ | ||||
| setReceivedPercentage(cashFlowReceivableAndExpenditureData[0].receivedPercentage) | setReceivedPercentage(cashFlowReceivableAndExpenditureData[0].receivedPercentage) | ||||
| setInvoicedPercentage(cashFlowReceivableAndExpenditureData[0].invoicedPercentage) | |||||
| setTotalFee(cashFlowReceivableAndExpenditureData[0].totalProjectFee) | |||||
| setTotalInvoiced(cashFlowReceivableAndExpenditureData[0].totalInvoiced) | setTotalInvoiced(cashFlowReceivableAndExpenditureData[0].totalInvoiced) | ||||
| setTotalReceived(cashFlowReceivableAndExpenditureData[0].totalReceived) | setTotalReceived(cashFlowReceivableAndExpenditureData[0].totalReceived) | ||||
| setReceivable(cashFlowReceivableAndExpenditureData[0].receivable) | setReceivable(cashFlowReceivableAndExpenditureData[0].receivable) | ||||
| @@ -511,9 +516,9 @@ const ProjectCashFlow: React.FC = () => { | |||||
| const accountsReceivableOptions: ApexOptions = { | const accountsReceivableOptions: ApexOptions = { | ||||
| colors: ["#20E647"], | colors: ["#20E647"], | ||||
| series: [receivedPercentage], | |||||
| series: [receivedPercentage,invoicedPercentage], | |||||
| chart: { | chart: { | ||||
| height: 350, | |||||
| height: 50, | |||||
| type: "radialBar", | type: "radialBar", | ||||
| }, | }, | ||||
| plotOptions: { | plotOptions: { | ||||
| @@ -533,13 +538,23 @@ const ProjectCashFlow: React.FC = () => { | |||||
| }, | }, | ||||
| dataLabels: { | dataLabels: { | ||||
| name: { | name: { | ||||
| show: false, | |||||
| show: true, | |||||
| fontSize: "0.9em", | |||||
| }, | }, | ||||
| value: { | value: { | ||||
| color: "#3e98c7", | color: "#3e98c7", | ||||
| fontSize: "3em", | |||||
| fontSize: "1.5em", | |||||
| show: true, | show: true, | ||||
| }, | }, | ||||
| total: { | |||||
| show: true, | |||||
| color: "#20E647", | |||||
| fontSize: "0.9em", | |||||
| label: 'Receivable / Invoiced', | |||||
| formatter: function (w:any) { | |||||
| return receivedPercentage + "% / " + invoicedPercentage + "%" | |||||
| }, | |||||
| } | |||||
| }, | }, | ||||
| }, | }, | ||||
| }, | }, | ||||
| @@ -555,7 +570,7 @@ const ProjectCashFlow: React.FC = () => { | |||||
| stroke: { | stroke: { | ||||
| lineCap: "round", | lineCap: "round", | ||||
| }, | }, | ||||
| labels: ["AccountsReceivable"], | |||||
| labels: ["Accounts Receivable","Account Invoiced"], | |||||
| }; | }; | ||||
| const expenditureOptions: ApexOptions = { | const expenditureOptions: ApexOptions = { | ||||
| @@ -582,11 +597,12 @@ const ProjectCashFlow: React.FC = () => { | |||||
| }, | }, | ||||
| dataLabels: { | dataLabels: { | ||||
| name: { | name: { | ||||
| show: false, | |||||
| show: true, | |||||
| fontSize: "0.9em", | |||||
| }, | }, | ||||
| value: { | value: { | ||||
| color: "#3e98c7", | color: "#3e98c7", | ||||
| fontSize: "3em", | |||||
| fontSize: "1.5em", | |||||
| show: true, | show: true, | ||||
| }, | }, | ||||
| }, | }, | ||||
| @@ -604,7 +620,7 @@ const ProjectCashFlow: React.FC = () => { | |||||
| stroke: { | stroke: { | ||||
| lineCap: "round", | lineCap: "round", | ||||
| }, | }, | ||||
| labels: ["AccountsReceivable"], | |||||
| labels: ["Accounts Expenditure"], | |||||
| }; | }; | ||||
| const rows = [ | const rows = [ | ||||
| @@ -812,12 +828,26 @@ const ProjectCashFlow: React.FC = () => { | |||||
| className="text-slate-500" | className="text-slate-500" | ||||
| title="Accounts Receivable (HKD)" | title="Accounts Receivable (HKD)" | ||||
| /> | /> | ||||
| <div style={{ display: "inline-block", width: "99%" }}> | |||||
| <ReactApexChart | <ReactApexChart | ||||
| options={accountsReceivableOptions} | options={accountsReceivableOptions} | ||||
| series={accountsReceivableOptions.series} | series={accountsReceivableOptions.series} | ||||
| type="radialBar" | type="radialBar" | ||||
| /> | /> | ||||
| </div> | |||||
| <Card className="ml-2 mr-2 mb-4 rounded-none border-solid border-slate-100"> | <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 Project Fee | |||||
| </div> | |||||
| <div | |||||
| className="text-lg font-medium ml-5" | |||||
| style={{ color: "#6b87cf" }} | |||||
| > | |||||
| ${totalFee.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
| </div> | |||||
| <div | <div | ||||
| className="text-sm font-medium ml-5 mt-2" | className="text-sm font-medium ml-5 mt-2" | ||||
| style={{ color: "#898d8d" }} | style={{ color: "#898d8d" }} | ||||
| @@ -25,10 +25,13 @@ interface Props { | |||||
| TotalBudget: number; | TotalBudget: number; | ||||
| TotalCumulative: number; | TotalCumulative: number; | ||||
| TotalInvoicedAmount: number; | TotalInvoicedAmount: number; | ||||
| TotalUnInvoicedAmount: number; | |||||
| TotalReceivedAmount: number; | TotalReceivedAmount: number; | ||||
| CashFlowStatus: string; | CashFlowStatus: string; | ||||
| CostPerformanceIndex: number; | CostPerformanceIndex: number; | ||||
| ClickedIndex: number; | ClickedIndex: number; | ||||
| ProjectedCPI: number; | |||||
| ProjectedCashFlowStatus: string; | |||||
| Index: number; | Index: number; | ||||
| } | } | ||||
| @@ -39,10 +42,13 @@ const ProjectFinancialCard: React.FC<Props> = ({ | |||||
| TotalBudget, | TotalBudget, | ||||
| TotalCumulative, | TotalCumulative, | ||||
| TotalInvoicedAmount, | TotalInvoicedAmount, | ||||
| TotalUnInvoicedAmount, | |||||
| TotalReceivedAmount, | TotalReceivedAmount, | ||||
| CashFlowStatus, | CashFlowStatus, | ||||
| CostPerformanceIndex, | CostPerformanceIndex, | ||||
| ClickedIndex, | ClickedIndex, | ||||
| ProjectedCPI, | |||||
| ProjectedCashFlowStatus, | |||||
| Index, | Index, | ||||
| }) => { | }) => { | ||||
| const [SearchCriteria, setSearchCriteria] = React.useState({}); | const [SearchCriteria, setSearchCriteria] = React.useState({}); | ||||
| @@ -106,6 +112,13 @@ const ProjectFinancialCard: React.FC<Props> = ({ | |||||
| {TotalInvoicedAmount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | {TotalInvoicedAmount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | ||||
| </div> | </div> | ||||
| <hr /> | <hr /> | ||||
| <div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}> | |||||
| Total Un-Invoiced Amount | |||||
| </div> | |||||
| <div className="text-lg font-medium ml-5" style={{ color: "#6b87cf" }}> | |||||
| {TotalUnInvoicedAmount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
| </div> | |||||
| <hr /> | |||||
| <div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}> | <div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}> | ||||
| Total Received Amount | Total Received Amount | ||||
| </div> | </div> | ||||
| @@ -164,6 +177,57 @@ const ProjectFinancialCard: React.FC<Props> = ({ | |||||
| </div> | </div> | ||||
| </> | </> | ||||
| )} | )} | ||||
| <div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}> | |||||
| Projected Cash Flow Status | |||||
| </div> | |||||
| {ProjectedCashFlowStatus === "Negative" && ( | |||||
| <> | |||||
| <div | |||||
| className="text-lg font-medium ml-5" | |||||
| style={{ color: "#f896aa" }} | |||||
| > | |||||
| {ProjectedCashFlowStatus} | |||||
| </div> | |||||
| <hr /> | |||||
| </> | |||||
| )} | |||||
| {ProjectedCashFlowStatus === "Positive" && ( | |||||
| <> | |||||
| <div | |||||
| className="text-lg font-medium ml-5" | |||||
| style={{ color: "#71d19e" }} | |||||
| > | |||||
| {CashFlowStatus} | |||||
| </div> | |||||
| <hr /> | |||||
| </> | |||||
| )} | |||||
| <div | |||||
| className="text-sm mt-2 font-medium ml-5" | |||||
| style={{ color: "#898d8d" }} | |||||
| > | |||||
| Projected Cost Performance Index (CPI) | |||||
| </div> | |||||
| {Number(ProjectedCPI) < 1 && ( | |||||
| <> | |||||
| <div | |||||
| className="text-lg font-medium ml-5 mb-2" | |||||
| style={{ color: "#f896aa" }} | |||||
| > | |||||
| {ProjectedCPI} | |||||
| </div> | |||||
| </> | |||||
| )} | |||||
| {Number(ProjectedCPI) >= 1 && ( | |||||
| <> | |||||
| <div | |||||
| className="text-lg font-medium ml-5 mb-2" | |||||
| style={{ color: "#71d19e" }} | |||||
| > | |||||
| {ProjectedCPI} | |||||
| </div> | |||||
| </> | |||||
| )} | |||||
| </Card> | </Card> | ||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -133,6 +133,40 @@ const ProjectFinancialSummary: React.FC = () => { | |||||
| } | } | ||||
| }, | }, | ||||
| }, | }, | ||||
| { | |||||
| id: 'projectedCashFlowStatus', | |||||
| field: 'projectedCashFlowStatus', | |||||
| headerName: "Projected Cash Flow Status", | |||||
| minWidth:100, | |||||
| renderCell: (params:any) => { | |||||
| if (params.row.projectedCashFlowStatus === "Positive") { | |||||
| return ( | |||||
| <span className="text-lime-500">{params.row.projectedCashFlowStatus}</span> | |||||
| ) | |||||
| } else if (params.row.projectedCashFlowStatus === "Negative") { | |||||
| return ( | |||||
| <span className="text-red-500">{params.row.projectedCashFlowStatus}</span> | |||||
| ) | |||||
| } | |||||
| }, | |||||
| }, | |||||
| { | |||||
| id: 'projectedCpi', | |||||
| field: 'projectedCpi', | |||||
| headerName: "Projected CPI", | |||||
| minWidth:50, | |||||
| renderCell: (params:any) => { | |||||
| if (params.row.projectedCpi >= 1) { | |||||
| return ( | |||||
| <span className="text-lime-500">{params.row.projectedCpi}</span> | |||||
| ) | |||||
| } else if (params.row.projectedCpi < 1) { | |||||
| return ( | |||||
| <span className="text-red-500">{params.row.projectedCpi}</span> | |||||
| ) | |||||
| } | |||||
| }, | |||||
| }, | |||||
| { | { | ||||
| id: 'totalFee', | id: 'totalFee', | ||||
| field: 'totalFee', | field: 'totalFee', | ||||
| @@ -292,86 +326,119 @@ const columns2 = [ | |||||
| } | } | ||||
| }, | }, | ||||
| }, | }, | ||||
| { | |||||
| id: "cpi", | |||||
| field: "cpi", | |||||
| headerName: "CPI", | |||||
| minWidth:50, | |||||
| renderCell: (params: any) => { | |||||
| if (params.row.cpi >= 1) { | |||||
| return <span className="text-lime-500">{params.row.cpi}</span>; | |||||
| } else if (params.row.cpi < 1) { | |||||
| return <span className="text-red-500">{params.row.cpi}</span>; | |||||
| } | |||||
| }, | |||||
| { | |||||
| id: "cpi", | |||||
| field: "cpi", | |||||
| headerName: "CPI", | |||||
| minWidth:50, | |||||
| renderCell: (params: any) => { | |||||
| if (params.row.cpi >= 1) { | |||||
| return <span className="text-lime-500">{params.row.cpi}</span>; | |||||
| } else if (params.row.cpi < 1) { | |||||
| return <span className="text-red-500">{params.row.cpi}</span>; | |||||
| } | |||||
| }, | }, | ||||
| { | |||||
| id: 'totalFees', | |||||
| field: 'totalFees', | |||||
| headerName: "Total Fees (HKD)", | |||||
| minWidth:50, | |||||
| renderCell: (params:any) => { | |||||
| }, | |||||
| { | |||||
| id: 'projectedCashFlowStatus', | |||||
| field: 'projectedCashFlowStatus', | |||||
| headerName: "Projected Cash Flow Status", | |||||
| minWidth:100, | |||||
| renderCell: (params:any) => { | |||||
| if (params.row.projectedCashFlowStatus === "Positive") { | |||||
| return ( | |||||
| <span className="text-lime-500">{params.row.projectedCashFlowStatus}</span> | |||||
| ) | |||||
| } else if (params.row.projectedCashFlowStatus === "Negative") { | |||||
| return ( | |||||
| <span className="text-red-500">{params.row.projectedCashFlowStatus}</span> | |||||
| ) | |||||
| } | |||||
| }, | |||||
| }, | |||||
| { | |||||
| id: 'projectedCpi', | |||||
| field: 'projectedCpi', | |||||
| headerName: "Projected CPI", | |||||
| minWidth:50, | |||||
| renderCell: (params:any) => { | |||||
| if (params.row.projectedCpi >= 1) { | |||||
| return ( | |||||
| <span className="text-lime-500">{params.row.projectedCpi}</span> | |||||
| ) | |||||
| } else if (params.row.projectedCpi < 1) { | |||||
| return ( | |||||
| <span className="text-red-500">{params.row.projectedCpi}</span> | |||||
| ) | |||||
| } | |||||
| }, | |||||
| }, | |||||
| { | |||||
| id: 'totalFees', | |||||
| field: 'totalFees', | |||||
| headerName: "Total Fees (HKD)", | |||||
| minWidth:50, | |||||
| renderCell: (params:any) => { | |||||
| return ( | |||||
| <span>${params.row.totalFee.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
| ) | |||||
| }, | |||||
| }, | |||||
| { | |||||
| id: 'totalBudget', | |||||
| field: 'totalBudget', | |||||
| headerName: "Total Budget (HKD)", | |||||
| minWidth:50, | |||||
| renderCell: (params:any) => { | |||||
| return ( | return ( | ||||
| <span>${params.row.totalFee.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
| <span>${params.row.totalBudget.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
| ) | ) | ||||
| }, | }, | ||||
| }, | |||||
| { | |||||
| id: 'totalBudget', | |||||
| field: 'totalBudget', | |||||
| headerName: "Total Budget (HKD)", | |||||
| minWidth:50, | |||||
| renderCell: (params:any) => { | |||||
| return ( | |||||
| <span>${params.row.totalBudget.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
| ) | |||||
| }, | |||||
| }, | |||||
| { | |||||
| id: 'totalCumulativeExpenditure', | |||||
| field: 'totalCumulativeExpenditure', | |||||
| headerName: "Total Cumulative Expenditure (HKD)", | |||||
| minWidth:250, | |||||
| renderCell: (params:any) => { | |||||
| return ( | |||||
| <span>${params.row.cumulativeExpenditure.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
| ) | |||||
| }, | }, | ||||
| }, | |||||
| { | |||||
| id: 'totalInvoicedAmount', | |||||
| field: 'totalInvoicedAmount', | |||||
| headerName: "Total Invoiced Amount (HKD)", | |||||
| minWidth:250, | |||||
| renderCell: (params:any) => { | |||||
| return ( | |||||
| <span>${params.row.totalInvoiced.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
| ) | |||||
| { | |||||
| id: 'totalCumulativeExpenditure', | |||||
| field: 'totalCumulativeExpenditure', | |||||
| headerName: "Total Cumulative Expenditure (HKD)", | |||||
| minWidth:250, | |||||
| renderCell: (params:any) => { | |||||
| return ( | |||||
| <span>${params.row.cumulativeExpenditure.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
| ) | |||||
| }, | |||||
| }, | }, | ||||
| }, | |||||
| { | |||||
| id: 'totalUnInvoicedAmount', | |||||
| field: 'totalUnInvoicedAmount', | |||||
| headerName: "Total Un-invoiced Amount (HKD)", | |||||
| minWidth:250, | |||||
| renderCell: (params:any) => { | |||||
| return ( | |||||
| <span>${params.row.totalUninvoiced.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
| ) | |||||
| { | |||||
| id: 'totalInvoicedAmount', | |||||
| field: 'totalInvoicedAmount', | |||||
| headerName: "Total Invoiced Amount (HKD)", | |||||
| minWidth:250, | |||||
| renderCell: (params:any) => { | |||||
| return ( | |||||
| <span>${params.row.totalInvoiced.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
| ) | |||||
| }, | |||||
| }, | |||||
| { | |||||
| id: 'totalUnInvoicedAmount', | |||||
| field: 'totalUnInvoicedAmount', | |||||
| headerName: "Total Un-invoiced Amount (HKD)", | |||||
| minWidth:250, | |||||
| renderCell: (params:any) => { | |||||
| return ( | |||||
| <span>${params.row.totalUninvoiced.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
| ) | |||||
| }, | |||||
| }, | }, | ||||
| }, | |||||
| { | |||||
| id: 'totalReceivedAmount', | |||||
| field: 'totalReceivedAmount', | |||||
| headerName: "Total Received Amount (HKD)", | |||||
| minWidth:250, | |||||
| renderCell: (params:any) => { | |||||
| return ( | |||||
| <span>${params.row.totalReceived.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
| ) | |||||
| { | |||||
| id: 'totalReceivedAmount', | |||||
| field: 'totalReceivedAmount', | |||||
| headerName: "Total Received Amount (HKD)", | |||||
| minWidth:250, | |||||
| renderCell: (params:any) => { | |||||
| return ( | |||||
| <span>${params.row.totalReceived.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
| ) | |||||
| }, | |||||
| }, | }, | ||||
| }, | |||||
| ]; | ]; | ||||
| const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => { | const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => { | ||||
| @@ -398,7 +465,7 @@ const columns2 = [ | |||||
| <div className="ml-10 mr-10" style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'start'}}> | <div className="ml-10 mr-10" style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'start'}}> | ||||
| {projectFinancialData.map((record:any, index:any) => ( | {projectFinancialData.map((record:any, index:any) => ( | ||||
| <div className="hover:cursor-pointer ml-4 mt-5 mb-4 inline-block" key={index} onClick={(r) => handleCardClick(record,index)}> | <div className="hover:cursor-pointer ml-4 mt-5 mb-4 inline-block" key={index} onClick={(r) => handleCardClick(record,index)}> | ||||
| <ProjectFinancialCard Title={record.teamName} TotalActiveProjectNumber={record.projectNo} TotalFees={record.totalFee} TotalBudget={record.totalBudget} TotalCumulative={record.cumulativeExpenditure ?? 0} TotalInvoicedAmount={record.totalInvoiced ?? 0} TotalReceivedAmount={record.totalReceived ?? 0} CashFlowStatus={record.cashFlowStatus ?? "Negative"} CostPerformanceIndex={record.cpi ?? 0} ClickedIndex={isCardClickedIndex} Index={index}/> | |||||
| <ProjectFinancialCard Title={record.teamName} TotalActiveProjectNumber={record.projectNo} TotalFees={record.totalFee} TotalBudget={record.totalBudget} TotalCumulative={record.cumulativeExpenditure ?? 0} TotalInvoicedAmount={record.totalInvoiced ?? 0} TotalUnInvoicedAmount={record.unInvoiced ?? 0} TotalReceivedAmount={record.totalReceived ?? 0} CashFlowStatus={record.cashFlowStatus ?? "Negative"} CostPerformanceIndex={record.cpi ?? 0} ProjectedCashFlowStatus={record.projectedCashFlowStatus ?? "Negative"} ProjectedCPI={record.projectedCpi ?? 0} ClickedIndex={isCardClickedIndex} Index={index}/> | |||||
| </div> | </div> | ||||
| ))} | ))} | ||||
| </div> | </div> | ||||
| @@ -0,0 +1,923 @@ | |||||
| "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 ProgressByTeamSearch from "@/components/ProgressByTeamSearch"; | |||||
| import { Suspense } from "react"; | |||||
| import { useSearchParams } from 'next/navigation'; | |||||
| import { fetchAllTeamProjects, TeamProjectResult, fetchTeamProjects, fetchAllTeamConsumption} from "@/app/api/teamprojects/actions"; | |||||
| // const ReactApexChart = dynamic(() => import('react-apexcharts'), { ssr: false }); | |||||
| interface Props { | |||||
| projects: TeamProjectResult[]; | |||||
| } | |||||
| type SearchQuery = Partial<Omit<TeamProjectResult, "id">>; | |||||
| type SearchParamNames = keyof SearchQuery; | |||||
| const ProjectResourceConsumptionRanking: React.FC = () => { | |||||
| const searchParams = useSearchParams(); | |||||
| const teamLeadId = searchParams.get('teamLeadId'); | |||||
| const [activeTab, setActiveTab] = useState("financialSummary"); | |||||
| const [SearchCriteria, setSearchCriteria] = React.useState({}); | |||||
| const { t } = useTranslation("dashboard"); | |||||
| const [projectData, setProjectData]: any[] = React.useState([]); | |||||
| const [filteredResult, setFilteredResult]:any[] = useState([]); | |||||
| const [teamCode, setTeamCode] = useState(""); | |||||
| const [teamName, setTeamName] = useState(""); | |||||
| const [projectArray, setProjectArray]: any[] = useState([]); | |||||
| const [percentageArray, setPercentageArray]: any[] = useState([]); | |||||
| const [colorArray, setColorArray]: any[] = useState([]); | |||||
| const [selectionModel, setSelectionModel]: any[] = React.useState([]); | |||||
| const [pieChartColor, setPieChartColor]: any[] = React.useState([]); | |||||
| const [totalSpentPercentage, setTotalSpentPercentage]: any = React.useState(); | |||||
| const [projectBudgetManhour, setProjectBudgetManhour]: any = | |||||
| React.useState("-"); | |||||
| const [actualManhourSpent, setActualManhourSpent]: any = React.useState("-"); | |||||
| const [remainedManhour, setRemainedManhour]: any = React.useState("-"); | |||||
| const [lastUpdate, setLastUpdate]: any = React.useState("-"); | |||||
| const [dropdownDemo, setDropdownDemo] = useState(""); | |||||
| const [dateDemo, setDateDemo] = useState(null); | |||||
| const [checkboxDemo, setCheckboxDemo] = useState(false); | |||||
| const [receiptFromDate, setReceiptFromDate] = useState(null); | |||||
| const [receiptToDate, setReceiptToDate] = useState(null); | |||||
| const [selectedRows, setSelectedRows] = useState([]); | |||||
| const [chartProjectName, setChartProjectName]:any[] = useState([]); | |||||
| const [chartProjectDisplayName, setChartProjectDisplayName]:any[] = useState([]); | |||||
| const [chartProjectBudgetedHour, setChartProjectBudgetedHour]:any[] = useState([]); | |||||
| const [chartProjectSpentHour, setChartProjectSpentHour]:any[] = useState([]); | |||||
| const [chartManhourConsumptionPercentage, setChartManhourConsumptionPercentage]:any[] = useState([]); | |||||
| const color = ["#f57f90", "#94f7d6", "#87c5f5", "#ab95f5", "#fcd68b", | |||||
| "#f58a9b", "#8ef4d1", "#92caf9", "#a798f9", "#fad287", | |||||
| "#f595a6", "#88f1cc", "#9dcff5", "#a39bf5", "#f8de83", | |||||
| "#f5a0b1", "#82eec7", "#a8d4f1", "#9f9ef1", "#f6ea7f", | |||||
| "#f5abb4", "#7cebca", "#b3d9ed", "#9ba1ed", "#f4f67b", | |||||
| "#f5b6b7", "#76e8cd", "#bed6e9", "#97a4e9", "#f2fa77", | |||||
| "#f5c1ba", "#70e5d0", "#c9d3e5", "#93a7e5", "#f0fe73", | |||||
| "#f5ccbd", "#6ae2d3", "#d4d0e1", "#8faae1", "#eefe6f", | |||||
| "#f5d7c0", "#64dfd6", "#dfc5dd", "#8badd5", "#ecfe6b", | |||||
| "#f5e2c3", "#5edcd9", "#eabada", "#87b0c9", "#eafc67", | |||||
| "#f5edc6", "#58d9dc", "#f5afd6", "#83b3bd", "#e8fc63", | |||||
| "#f5f8c9", "#52d6df", "#ffacd2", "#7fb6b1", "#e6fc5f", | |||||
| "#f5ffcc", "#4cd3e2", "#ffa9ce", "#7bb9a5", "#e4fc5b", | |||||
| "#f2ffcf", "#46d0e5", "#ffa6ca", "#77bc99", "#e2fc57", | |||||
| "#efffd2", "#40cde8", "#ffa3c6", "#73bf8d", "#e0fc53", | |||||
| "#ecffd5", "#3acaeb", "#ffa0c2", "#6fc281", "#defb4f", | |||||
| "#e9ffd8", "#34c7ee", "#ff9dbe", "#6bc575", "#dcfb4b", | |||||
| "#e6ffdb", "#2ec4f1", "#ff9aba", "#67c869", "#dafb47", | |||||
| "#e3ffde", "#28c1f4", "#ff97b6", "#63cb5d", "#d8fb43", | |||||
| "#e0ffe1", "#22bef7", "#ff94b2", "#5fce51", "#d6fb3f", | |||||
| "#ddfee4", "#1cbbfa", "#ff91ae", "#5bd145", "#d4fb3b", | |||||
| "#dafee7", "#16b8fd", "#ff8eaa", "#57d439", "#d2fb37", | |||||
| "#d7feea", "#10b5ff", "#ff8ba6", "#53d72d", "#d0fb33", | |||||
| "#d4feed", "#0ab2ff", "#ff88a2", "#4fda21", "#cefb2f", | |||||
| "#d1fef0", "#04afff", "#ff859e", "#4bdd15", "#ccfb2b"]; | |||||
| const [teamProjectResult, setTeamProjectResult]:any[] = useState([]); | |||||
| const [currentPageProjectList, setCurrentPageProjectList]: any[] = React.useState([]); | |||||
| const [currentPageProjectNameList, setCurrentPageProjectNameList]: any[] = React.useState([]); | |||||
| const [currentPageProjectBudgetedManhourList, setCurrentPageProjectBudgetedManhourList]: any[] = React.useState([]); | |||||
| const [currentPageProjectSpentManhourList, setCurrentPageProjectSpentManhourList]: any[] = React.useState([]); | |||||
| const [currentPagePercentage, setCurrentPagePercentage]: any[] = React.useState([]); | |||||
| const [currentPageColor, setCurrentPageColor]: any[] = React.useState([]); | |||||
| const [currentPage, setCurrentPage] = useState(1); | |||||
| const recordsPerPage = 10; | |||||
| const [tableSorting, setTableSorting] = useState('ProjectName'); | |||||
| const [selectedTeamIdList, setSelectedTeamIdList]: any[] = React.useState([]); | |||||
| const fetchTeamData = async () => { | |||||
| const teamprojects = await fetchTeamProjects(); | |||||
| setProjectData(teamprojects) | |||||
| setFilteredResult(teamprojects) | |||||
| } | |||||
| const fetchData = async () => { | |||||
| console.log(selectedTeamIdList) | |||||
| if (selectedTeamIdList) { | |||||
| try { | |||||
| const clickResult = await fetchAllTeamConsumption( | |||||
| selectedTeamIdList,tableSorting) | |||||
| console.log(clickResult) | |||||
| setTeamProjectResult(clickResult); | |||||
| } catch (error) { | |||||
| console.error('Error fetching team consumption:', error); | |||||
| } | |||||
| } | |||||
| } | |||||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||||
| () => [ | |||||
| { label: "Team Code", paramName: "teamCode", type: "text" }, | |||||
| { label: "Team Name", paramName: "teamName", type: "text" }, | |||||
| ], | |||||
| [t], | |||||
| ); | |||||
| useEffect(() => { | |||||
| const projectNo = [] | |||||
| const projectName = [] | |||||
| const projectBudgetedManHour = [] | |||||
| const projectSpentManHour = [] | |||||
| const manhourConsumptionPercentage = [] | |||||
| for (let i = 0; i < teamProjectResult.length; i++){ | |||||
| teamProjectResult[i].color = color[i] | |||||
| console.log(teamProjectResult[i]) | |||||
| projectNo.push(teamProjectResult[i].projectCode + "(" + teamProjectResult[i].team + ")") | |||||
| projectName.push(teamProjectResult[i].projectName) | |||||
| projectBudgetedManHour.push(teamProjectResult[i].budgetedManhour) | |||||
| projectSpentManHour.push(teamProjectResult[i].spentManhour) | |||||
| manhourConsumptionPercentage.push(teamProjectResult[i].manhourConsumptionPercentage) | |||||
| } | |||||
| setChartProjectName(projectNo) | |||||
| setChartProjectDisplayName(projectName) | |||||
| setChartProjectBudgetedHour(projectBudgetedManHour) | |||||
| setChartProjectSpentHour(projectSpentManHour) | |||||
| setChartManhourConsumptionPercentage(manhourConsumptionPercentage) | |||||
| }, [teamProjectResult]); | |||||
| useEffect(() => { | |||||
| fetchTeamData() | |||||
| }, []); | |||||
| useEffect(() => { | |||||
| fetchData() | |||||
| }, [selectedTeamIdList,tableSorting]); | |||||
| const rows = [ | |||||
| { | |||||
| id: 1, | |||||
| teamCode: "TEAM-001", | |||||
| teamName: "Team A", | |||||
| noOfProjects: "5", | |||||
| }, | |||||
| { | |||||
| id: 2, | |||||
| teamCode: "TEAM-001", | |||||
| teamName: "Team B", | |||||
| noOfProjects: "5", | |||||
| }, | |||||
| { | |||||
| id: 3, | |||||
| teamCode: "TEAM-001", | |||||
| teamName: "Team C", | |||||
| noOfProjects: "3", | |||||
| }, | |||||
| { | |||||
| id: 4, | |||||
| teamCode: "TEAM-001", | |||||
| teamName: "Team D", | |||||
| noOfProjects: "1", | |||||
| }, | |||||
| ]; | |||||
| //['#f57f90', '#94f7d6', '#87c5f5', '#ab95f5', '#fcd68b'] | |||||
| const rows2 = [ | |||||
| { | |||||
| id: 1, | |||||
| project: "Consultancy Project 123", | |||||
| team: "XXX", | |||||
| teamLeader: "XXX", | |||||
| currentStage: "Contract Documentation", | |||||
| budgetedManhour: "200.00", | |||||
| spentManhour: "120.00", | |||||
| remainedManhour: "80.00", | |||||
| comingPaymentMilestone: "31/03/2024", | |||||
| alert: false, | |||||
| color: "#f57f90", | |||||
| }, | |||||
| { | |||||
| id: 2, | |||||
| project: "Consultancy Project 456", | |||||
| team: "XXX", | |||||
| teamLeader: "XXX", | |||||
| currentStage: "Report Preparation", | |||||
| budgetedManhour: "400.00", | |||||
| spentManhour: "200.00", | |||||
| remainedManhour: "200.00", | |||||
| comingPaymentMilestone: "20/02/2024", | |||||
| alert: false, | |||||
| color: "#94f7d6", | |||||
| }, | |||||
| { | |||||
| id: 3, | |||||
| project: "Construction Project A", | |||||
| team: "YYY", | |||||
| teamLeader: "YYY", | |||||
| currentStage: "Construction", | |||||
| budgetedManhour: "187.50", | |||||
| spentManhour: "200.00", | |||||
| remainedManhour: "12.50", | |||||
| comingPaymentMilestone: "13/12/2023", | |||||
| alert: true, | |||||
| color: "#87c5f5", | |||||
| }, | |||||
| { | |||||
| id: 4, | |||||
| project: "Construction Project B", | |||||
| team: "XXX", | |||||
| teamLeader: "XXX", | |||||
| currentStage: "Post Construction", | |||||
| budgetedManhour: "100.00", | |||||
| spentManhour: "40.00", | |||||
| remainedManhour: "60.00", | |||||
| comingPaymentMilestone: "05/01/2024", | |||||
| alert: false, | |||||
| color: "#ab95f5", | |||||
| }, | |||||
| { | |||||
| id: 5, | |||||
| project: "Construction Project C", | |||||
| team: "YYY", | |||||
| teamLeader: "YYY", | |||||
| currentStage: "Construction", | |||||
| budgetedManhour: "300.00", | |||||
| spentManhour: "150.00", | |||||
| remainedManhour: "150.00", | |||||
| comingPaymentMilestone: "31/03/2024", | |||||
| alert: false, | |||||
| color: "#fcd68b", | |||||
| }, | |||||
| ]; | |||||
| const searchColumns = [ | |||||
| { | |||||
| id: "teamCode", | |||||
| field: "teamCode", | |||||
| headerName: "Team Code", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| id: "teamName", | |||||
| field: "teamName", | |||||
| headerName: "Team Name", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| id: "projectNo", | |||||
| field: "projectNo", | |||||
| headerName: "No. of Projects", | |||||
| flex: 1, | |||||
| }, | |||||
| ]; | |||||
| const columns = [ | |||||
| { | |||||
| id: "clientCode", | |||||
| field: "clientCode", | |||||
| headerName: "Client Code", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| id: "clientName", | |||||
| field: "clientName", | |||||
| headerName: "Client Name", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| id: "clientSubsidiaryCode", | |||||
| field: "clientSubsidiaryCode", | |||||
| headerName: "Client Subsidiary Code", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| id: "noOfProjects", | |||||
| field: "noOfProjects", | |||||
| headerName: "No. of Projects", | |||||
| flex: 1, | |||||
| }, | |||||
| ]; | |||||
| const columns2 = [ | |||||
| { | |||||
| id: "color", | |||||
| field: "color", | |||||
| headerName: "", | |||||
| renderCell: (params: any) => { | |||||
| return ( | |||||
| <span | |||||
| className="dot" | |||||
| style={{ | |||||
| height: "15px", | |||||
| width: "15px", | |||||
| borderRadius: "50%", | |||||
| backgroundColor: `${params.row.color}`, | |||||
| display: "inline-block", | |||||
| }} | |||||
| ></span> | |||||
| ); | |||||
| }, | |||||
| flex: 0.1, | |||||
| }, | |||||
| { | |||||
| id: "projectCode", | |||||
| field: "projectCode", | |||||
| headerName: "Project No", | |||||
| minWidth:100 | |||||
| }, | |||||
| { | |||||
| id: "projectName", | |||||
| field: "projectName", | |||||
| headerName: "Project", | |||||
| minWidth:300 | |||||
| }, | |||||
| { | |||||
| id: "team", | |||||
| field: "team", | |||||
| headerName: "Team", | |||||
| minWidth:50 | |||||
| }, | |||||
| { | |||||
| id: "teamLead", | |||||
| field: "teamLead", | |||||
| headerName: "Team Leader", | |||||
| minWidth: 70 | |||||
| }, | |||||
| { | |||||
| id: "expectedStage", | |||||
| field: "expectedStage", | |||||
| headerName: "Expected Stage", | |||||
| minWidth: 300, | |||||
| renderCell: (params: any) => { | |||||
| if (params.row.expectedStage != null){ | |||||
| const expectedStage = params.row.expectedStage; | |||||
| const lines = expectedStage.split(",").map((line:any, index:any) => ( | |||||
| <React.Fragment key={index}> | |||||
| {line.trim()} | |||||
| <br /> | |||||
| </React.Fragment> | |||||
| )); | |||||
| return <div>{lines}</div>; | |||||
| } else { | |||||
| return <div>-</div>; | |||||
| } | |||||
| }, | |||||
| }, | |||||
| { | |||||
| id: "budgetedManhour", | |||||
| field: "budgetedManhour", | |||||
| headerName: "Budgeted Manhour", | |||||
| minWidth: 70, | |||||
| renderCell: (params: any) => { | |||||
| return <span>{params.row.budgetedManhour.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>; | |||||
| } | |||||
| }, | |||||
| { | |||||
| id: "spentManhour", | |||||
| field: "spentManhour", | |||||
| headerName: "Spent Manhour", | |||||
| renderCell: (params: any) => { | |||||
| if (params.row.budgetedManhour - params.row.spentManhour <= 0) { | |||||
| return ( | |||||
| <span className="text-red-300">{params.row.spentManhour.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
| ); | |||||
| } else { | |||||
| return <span>{params.row.spentManhour.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>; | |||||
| } | |||||
| }, | |||||
| minWidth: 70 | |||||
| }, | |||||
| { | |||||
| id: "remainedManhour", | |||||
| field: "remainedManhour", | |||||
| headerName: "Remained Manhour", | |||||
| renderCell: (params: any) => { | |||||
| if (params.row.budgetedManhour - params.row.spentManhour <= 0) { | |||||
| return ( | |||||
| <span className="text-red-300">({params.row.remainedManhour.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })})</span> | |||||
| ); | |||||
| } else { | |||||
| return <span>{params.row.remainedManhour.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>; | |||||
| } | |||||
| }, | |||||
| minWidth: 70 | |||||
| }, | |||||
| { | |||||
| id: "comingPaymentMilestone", | |||||
| field: "comingPaymentMilestone", | |||||
| headerName: "Coming Payment Milestone", | |||||
| minWidth: 100 | |||||
| }, | |||||
| { | |||||
| id: "alert", | |||||
| field: "alert", | |||||
| headerName: "Alert", | |||||
| renderCell: (params: any) => { | |||||
| if (params.row.alert === true) { | |||||
| return ( | |||||
| <span className="text-red-300 text-center"> | |||||
| <ReportProblemIcon /> | |||||
| </span> | |||||
| ); | |||||
| } else { | |||||
| return <span></span>; | |||||
| } | |||||
| }, | |||||
| flex: 0.1, | |||||
| }, | |||||
| ]; | |||||
| const InputFields = [ | |||||
| { | |||||
| id: "teamCode", | |||||
| label: "Team Code", | |||||
| type: "text", | |||||
| value: teamCode, | |||||
| setValue: setTeamCode, | |||||
| }, | |||||
| { | |||||
| id: "teamName", | |||||
| label: "Team Name", | |||||
| type: "text", | |||||
| value: teamName, | |||||
| setValue: setTeamName, | |||||
| }, | |||||
| // { id: 'dropdownDemo', label: "dropdownDemo", type: 'dropdown', options: [{id:"1", label:"1"}], value: dropdownDemo, setValue: setDropdownDemo }, | |||||
| // { id: 'dateDemo', label:'dateDemo', type: 'date', value: dateDemo, setValue: setDateDemo }, | |||||
| // { id: 'checkboxDemo', label:'checkboxDemo', type: 'checkbox', value: checkboxDemo, setValue: setCheckboxDemo }, | |||||
| // { id: ['receiptFromDate','receiptToDate'], label: ["收貨日期","收貨日期"], value: [receiptFromDate ? receiptFromDate : null, receiptToDate ? receiptToDate : null], | |||||
| // setValue: [setReceiptFromDate, setReceiptToDate],type: 'dateRange' }, | |||||
| ]; | |||||
| const stageDeadline = [ | |||||
| "31/03/2024", | |||||
| "20/02/2024", | |||||
| "01/12/2023", | |||||
| "05/01/2024", | |||||
| "31/03/2023", | |||||
| ]; | |||||
| const series2: ApexAxisChartSeries | ApexNonAxisChartSeries = [ | |||||
| { | |||||
| data: [17.1, 28.6, 5.7, 48.6], | |||||
| }, | |||||
| ]; | |||||
| const series: ApexAxisChartSeries | ApexNonAxisChartSeries = [ | |||||
| { | |||||
| name: "Project Resource Consumption Percentage", | |||||
| data: [80, 55, 40, 65, 70], | |||||
| }, | |||||
| ]; | |||||
| const options2: ApexOptions = { | |||||
| chart: { | |||||
| type: "donut", | |||||
| }, | |||||
| colors: colorArray, | |||||
| plotOptions: { | |||||
| pie: { | |||||
| donut: { | |||||
| labels: { | |||||
| show: true, | |||||
| name: { | |||||
| show: true, | |||||
| }, | |||||
| value: { | |||||
| show: true, | |||||
| fontWeight: 500, | |||||
| fontSize: "30px", | |||||
| color: "#3e98c7", | |||||
| }, | |||||
| total: { | |||||
| show: true, | |||||
| showAlways: true, | |||||
| label: "Spent", | |||||
| fontFamily: "sans-serif", | |||||
| formatter: function (val) { | |||||
| return totalSpentPercentage + "%"; | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| labels: projectArray, | |||||
| legend: { | |||||
| show: false, | |||||
| }, | |||||
| responsive: [ | |||||
| { | |||||
| breakpoint: 480, | |||||
| options: { | |||||
| chart: { | |||||
| width: 200, | |||||
| }, | |||||
| legend: { | |||||
| position: "bottom", | |||||
| show: false, | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| ], | |||||
| }; | |||||
| const options: ApexOptions = { | |||||
| chart: { | |||||
| type: "bar", | |||||
| height: 450, | |||||
| }, | |||||
| tooltip: { | |||||
| enabled: true, // Enable tooltip | |||||
| custom: ({ series, seriesIndex, dataPointIndex, w }) => { | |||||
| const projectCode = currentPageProjectList[dataPointIndex]; | |||||
| const projectName = currentPageProjectNameList[dataPointIndex]; | |||||
| const budgetManhours = currentPageProjectBudgetedManhourList[dataPointIndex]; | |||||
| const spentManhours = currentPageProjectSpentManhourList[dataPointIndex]; | |||||
| const value = series[seriesIndex][dataPointIndex]; | |||||
| const tooltipContent = ` | |||||
| <div style="width: 250px;"> | |||||
| <span style="font-weight: bold;">${projectCode} - ${projectName}</span> | |||||
| <br> | |||||
| Budget Manhours: ${budgetManhours} hours | |||||
| <br> | |||||
| Spent Manhours: ${spentManhours} hours | |||||
| <br> | |||||
| Percentage: ${value}% | |||||
| </div> | |||||
| `; | |||||
| return tooltipContent; | |||||
| }, | |||||
| }, | |||||
| series: [{ | |||||
| name: "Project Resource Consumption Percentage", | |||||
| data: currentPagePercentage, | |||||
| },], | |||||
| colors: currentPageColor, | |||||
| plotOptions: { | |||||
| bar: { | |||||
| horizontal: true, | |||||
| distributed: true, | |||||
| dataLabels: { | |||||
| position: 'top' | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| dataLabels: { | |||||
| enabled: true, | |||||
| textAnchor: 'end', | |||||
| formatter: (val) => `${val}%`, | |||||
| }, | |||||
| xaxis: { | |||||
| categories: currentPageProjectList, | |||||
| }, | |||||
| yaxis: { | |||||
| title: { | |||||
| text: "Projects", | |||||
| }, | |||||
| labels: { | |||||
| maxWidth: 200, | |||||
| style: { | |||||
| cssClass: "apexcharts-yaxis-label", | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| title: { | |||||
| text: "Project Resource Consumption Percentage", | |||||
| align: "center", | |||||
| }, | |||||
| grid: { | |||||
| borderColor: "#f1f1f1", | |||||
| xaxis: { | |||||
| lines: { | |||||
| show: true, | |||||
| } | |||||
| } | |||||
| }, | |||||
| annotations: {}, | |||||
| }; | |||||
| const handleSearchSelectionChange = (newSelectionModel: GridRowSelectionModel) => { | |||||
| const selectedRowsData = projectData.filter((row: any) => | |||||
| newSelectionModel.includes(row.id), | |||||
| ); | |||||
| const teamIdList = [] | |||||
| for (var i=0; i<selectedRowsData.length; i++){ | |||||
| teamIdList.push(selectedRowsData[i].id) | |||||
| } | |||||
| setSelectedTeamIdList(teamIdList) | |||||
| }; | |||||
| const handleSelectionChange = (newSelectionModel: GridRowSelectionModel) => { | |||||
| const selectedRowsData = teamProjectResult.filter((row:any) => | |||||
| newSelectionModel.includes(row.id), | |||||
| ); | |||||
| console.log(selectedRowsData); | |||||
| const projectArray = []; | |||||
| const pieChartColorArray = []; | |||||
| let totalSpent = 0; | |||||
| let totalBudgetManhour = 0; | |||||
| const percentageArray = []; | |||||
| for (let i = 0; i <= selectedRowsData.length; i++) { | |||||
| if (i === selectedRowsData.length && i > 0) { | |||||
| projectArray.push("Remained"); | |||||
| } else if (selectedRowsData.length > 0) { | |||||
| projectArray.push(selectedRowsData[i].projectName); | |||||
| totalBudgetManhour += Number(selectedRowsData[i].budgetedManhour); | |||||
| totalSpent += Number(selectedRowsData[i].spentManhour); | |||||
| pieChartColorArray.push(selectedRowsData[i].color); | |||||
| } | |||||
| } | |||||
| for (let i = 0; i <= selectedRowsData.length; i++) { | |||||
| if (i === selectedRowsData.length && i > 0) { | |||||
| const remainedManhour = totalBudgetManhour - totalSpent; | |||||
| percentageArray.push( | |||||
| Number(((remainedManhour / totalBudgetManhour) * 100).toFixed(1)), | |||||
| ); | |||||
| } else if (selectedRowsData.length > 0) { | |||||
| const percentage = ( | |||||
| (Number(selectedRowsData[i].spentManhour) / totalBudgetManhour) * | |||||
| 100 | |||||
| ).toFixed(1); | |||||
| percentageArray.push(Number(percentage)); | |||||
| } | |||||
| } | |||||
| setProjectBudgetManhour(totalBudgetManhour.toFixed(2)); | |||||
| setActualManhourSpent(totalSpent.toFixed(2)); | |||||
| setRemainedManhour((totalBudgetManhour - totalSpent).toFixed(2)); | |||||
| setLastUpdate(new Date().toLocaleDateString("en-GB")); | |||||
| setSelectionModel(newSelectionModel); | |||||
| console.log(projectArray); | |||||
| setProjectArray(projectArray); | |||||
| setPercentageArray(percentageArray); | |||||
| console.log(percentageArray); | |||||
| setTotalSpentPercentage( | |||||
| ((totalSpent / totalBudgetManhour) * 100).toFixed(1), | |||||
| ); | |||||
| if (projectArray.length > 0 && projectArray.includes("Remained")) { | |||||
| const nonLastRecordColors = pieChartColorArray; | |||||
| setColorArray([ | |||||
| ...nonLastRecordColors.slice(0, projectArray.length - 1), | |||||
| "#a3a3a3", | |||||
| ]); | |||||
| } else { | |||||
| setColorArray(pieChartColorArray); | |||||
| } | |||||
| }; | |||||
| const startIndex = (currentPage - 1) * recordsPerPage; | |||||
| const endIndex = startIndex + recordsPerPage; | |||||
| useEffect(() => { | |||||
| console.log(chartManhourConsumptionPercentage) | |||||
| const currentPageProjectData = chartProjectName.slice(startIndex, endIndex) | |||||
| const currentPageProjectName = chartProjectDisplayName.slice(startIndex, endIndex) | |||||
| const currentPageProjectBudgetedManhour = chartProjectBudgetedHour.slice(startIndex, endIndex) | |||||
| const currentPageProjectSpentManhour = chartProjectSpentHour.slice(startIndex, endIndex) | |||||
| const currentPageData = chartManhourConsumptionPercentage.slice(startIndex, endIndex); | |||||
| const colorArray = color.slice(startIndex, endIndex); | |||||
| console.log(currentPage) | |||||
| console.log(Math.ceil(chartManhourConsumptionPercentage.length / recordsPerPage)) | |||||
| setCurrentPageProjectList(currentPageProjectData) | |||||
| setCurrentPageProjectNameList(currentPageProjectName) | |||||
| setCurrentPageProjectBudgetedManhourList(currentPageProjectBudgetedManhour) | |||||
| setCurrentPageProjectSpentManhourList(currentPageProjectSpentManhour) | |||||
| setCurrentPagePercentage(currentPageData) | |||||
| setCurrentPageColor(colorArray) | |||||
| }, [chartManhourConsumptionPercentage,currentPage]); | |||||
| const handlePrevPage = () => { | |||||
| if (currentPage > 1) { | |||||
| setCurrentPage(currentPage - 1); | |||||
| } | |||||
| }; | |||||
| const handleNextPage = () => { | |||||
| if (endIndex < chartManhourConsumptionPercentage.length) { | |||||
| setCurrentPage(currentPage + 1); | |||||
| } | |||||
| }; | |||||
| const applySearch = (data: any) => { | |||||
| console.log(data); | |||||
| setSearchCriteria(data); | |||||
| }; | |||||
| return ( | |||||
| <> | |||||
| <SearchBox | |||||
| criteria={searchCriteria} | |||||
| onSearch={(query) => { | |||||
| setFilteredResult( | |||||
| projectData.filter( | |||||
| (cp:any) => | |||||
| cp.teamCode.toLowerCase().includes(query.teamCode.toLowerCase()) && | |||||
| cp.teamName.toLowerCase().includes(query.teamName.toLowerCase()) | |||||
| ), | |||||
| ); | |||||
| }} | |||||
| /> | |||||
| <CustomDatagrid | |||||
| rows={filteredResult} | |||||
| columns={searchColumns} | |||||
| columnWidth={200} | |||||
| dataGridHeight={300} | |||||
| checkboxSelection={true} | |||||
| onRowSelectionModelChange={handleSearchSelectionChange} | |||||
| selectionModel={selectionModel} | |||||
| /> | |||||
| <Grid item sm> | |||||
| <div style={{ display: "inline-block", width: "70%" }}> | |||||
| <Grid item xs={12} md={12} lg={12}> | |||||
| <Card> | |||||
| <CardHeader className="text-slate-500" title="Project Resource Consumption" /> | |||||
| <div style={{ display: "inline-block", width: "99%" }}> | |||||
| <div className="ml-6 text-sm"> | |||||
| <b> | |||||
| Sorting: | |||||
| </b> | |||||
| </div> | |||||
| <div className="ml-6 mb-2"> | |||||
| <button onClick={() => {setTableSorting('PercentageASC')}} | |||||
| className="hover:cursor-pointer hover:bg-violet-50 text-sm bg-transparent border-violet-500 text-violet-500 border-solid w-36" | |||||
| > | |||||
| Percentage ASC | |||||
| </button> | |||||
| <button | |||||
| onClick={() => {setTableSorting('PercentageDESC')}} | |||||
| className="hover:cursor-pointer hover:bg-violet-50 text-sm bg-transparent border-violet-500 text-violet-500 border-solid w-36" | |||||
| > | |||||
| Percentage DESC | |||||
| </button> | |||||
| <button | |||||
| onClick={() => {setTableSorting('ProjectName')}} | |||||
| className="hover:cursor-pointer hover:bg-violet-50 text-sm bg-transparent border-violet-500 text-violet-500 border-solid w-36" | |||||
| > | |||||
| Project Name | |||||
| </button> | |||||
| </div> | |||||
| <ReactApexChart | |||||
| options={options} | |||||
| series={options.series} | |||||
| type="bar" | |||||
| height={500} | |||||
| /> | |||||
| <div className="float-right mr-4 mb-10"> | |||||
| {currentPage === 1 && ( | |||||
| <button className="bg-blue-500 text-white font-bold py-2 px-4 opacity-50 cursor-not-allowed rounded-l"> | |||||
| Pervious | |||||
| </button> | |||||
| )} | |||||
| {currentPage !== 1 && ( | |||||
| <button onClick={handlePrevPage} className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 outline-none rounded-l"> | |||||
| Previous | |||||
| </button> | |||||
| )} | |||||
| {endIndex >= chartManhourConsumptionPercentage.length && ( | |||||
| <button className="bg-blue-500 text-white font-bold py-2 px-4 opacity-50 cursor-not-allowed rounded-r mr-2"> | |||||
| Next | |||||
| </button> | |||||
| )} | |||||
| {endIndex < chartManhourConsumptionPercentage.length && ( | |||||
| <button onClick={handleNextPage} className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 outline-none rounded-r mr-2"> | |||||
| Next | |||||
| </button> | |||||
| )} | |||||
| Page | |||||
| {chartManhourConsumptionPercentage.length === 0 && ( | |||||
| 0 | |||||
| )} | |||||
| {chartManhourConsumptionPercentage.length > 0 && ( | |||||
| currentPage | |||||
| )} | |||||
| of | |||||
| {Math.ceil(chartManhourConsumptionPercentage.length / recordsPerPage)} | |||||
| </div> | |||||
| </div> | |||||
| </Card> | |||||
| <Card className="mt-2"> | |||||
| <CardHeader | |||||
| className="text-slate-500" | |||||
| title="Resource Consumption and Coming Milestone" | |||||
| /> | |||||
| <div | |||||
| style={{ display: "inline-block", width: "99%", marginLeft: 10 }} | |||||
| > | |||||
| <CustomDatagrid | |||||
| rows={teamProjectResult} | |||||
| columns={columns2} | |||||
| columnWidth={200} | |||||
| dataGridHeight={300} | |||||
| checkboxSelection={true} | |||||
| onRowSelectionModelChange={handleSelectionChange} | |||||
| selectionModel={selectionModel} | |||||
| /> | |||||
| </div> | |||||
| </Card> | |||||
| </Grid> | |||||
| </div> | |||||
| <div | |||||
| style={{ | |||||
| display: "inline-block", | |||||
| width: "30%", | |||||
| verticalAlign: "top", | |||||
| marginLeft: 0, | |||||
| }} | |||||
| > | |||||
| <Grid item xs={12} md={12} lg={12} style={{height:710}}> | |||||
| <Card style={{ marginLeft: 15, marginRight: 20, height:"100%"}}> | |||||
| <CardHeader | |||||
| className="text-slate-500" | |||||
| title="Overall Progress per Project" | |||||
| /> | |||||
| {percentageArray.length === 0 && ( | |||||
| <div | |||||
| className="mt-40 mb-10 ml-5 mr-5 text-lg font-medium text-center" | |||||
| style={{ color: "#898d8d"}} | |||||
| > | |||||
| Please select the project you want to check. | |||||
| </div> | |||||
| )} | |||||
| {percentageArray.length > 0 && ( | |||||
| <ReactApexChart | |||||
| options={options2} | |||||
| series={percentageArray} | |||||
| type="donut" | |||||
| style={{marginTop:'10rem'}} | |||||
| /> | |||||
| )} | |||||
| </Card> | |||||
| </Grid> | |||||
| <Grid item xs={12} md={12} lg={12}> | |||||
| <Card style={{ marginLeft: 15, marginRight: 20, marginTop: 15 }}> | |||||
| <div> | |||||
| <div | |||||
| className="mt-5 text-lg font-medium" | |||||
| style={{ color: "#898d8d" }} | |||||
| > | |||||
| <span style={{ marginLeft: "5%" }}>Project Budget Manhour</span> | |||||
| </div> | |||||
| <div | |||||
| className="mt-2 text-2xl font-extrabold" | |||||
| style={{ color: "#6b87cf" }} | |||||
| > | |||||
| <span style={{ marginLeft: "5%" }}>{projectBudgetManhour}</span> | |||||
| </div> | |||||
| </div> | |||||
| <hr /> | |||||
| <div> | |||||
| <div | |||||
| className="mt-2 text-lg font-medium" | |||||
| style={{ color: "#898d8d" }} | |||||
| > | |||||
| <span style={{ marginLeft: "5%" }}>Actual Manhour Spent</span> | |||||
| </div> | |||||
| <div | |||||
| className="mt-2 text-2xl font-extrabold" | |||||
| style={{ color: "#6b87cf" }} | |||||
| > | |||||
| <span style={{ marginLeft: "5%" }}>{actualManhourSpent}</span> | |||||
| </div> | |||||
| </div> | |||||
| <hr /> | |||||
| <div> | |||||
| <div | |||||
| className="mt-2 text-lg font-medium" | |||||
| style={{ color: "#898d8d" }} | |||||
| > | |||||
| <span style={{ marginLeft: "5%" }}>Remained Manhour</span> | |||||
| </div> | |||||
| <div | |||||
| className="mt-2 text-2xl font-extrabold" | |||||
| style={{ color: "#6b87cf" }} | |||||
| > | |||||
| <span style={{ marginLeft: "5%" }}>{remainedManhour}</span> | |||||
| </div> | |||||
| </div> | |||||
| <hr /> | |||||
| <div> | |||||
| <div | |||||
| className="mt-2 text-lg font-medium" | |||||
| style={{ color: "#898d8d" }} | |||||
| > | |||||
| <span style={{ marginLeft: "5%" }}>Last Update</span> | |||||
| </div> | |||||
| <div | |||||
| className="mt-2 mb-5 text-2xl font-extrabold" | |||||
| style={{ color: "#6b87cf" }} | |||||
| > | |||||
| <span style={{ marginLeft: "5%" }}>{lastUpdate}</span> | |||||
| </div> | |||||
| </div> | |||||
| </Card> | |||||
| </Grid> | |||||
| </div> | |||||
| </Grid> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default ProjectResourceConsumptionRanking; | |||||
| @@ -0,0 +1 @@ | |||||
| export { default } from "./ProjectResourceConsumptionRanking"; | |||||
| @@ -33,6 +33,7 @@ import Box from '@mui/material/Box'; | |||||
| import Typography from '@mui/material/Typography'; | import Typography from '@mui/material/Typography'; | ||||
| import { useSearchParams } from 'next/navigation'; | import { useSearchParams } from 'next/navigation'; | ||||
| import {fetchResourceSummaryDetailResult} from "@/app/api/resourcesummary/actions"; | import {fetchResourceSummaryDetailResult} from "@/app/api/resourcesummary/actions"; | ||||
| import { set } from "lodash"; | |||||
| const ProjectResourceSummary: React.FC = () => { | const ProjectResourceSummary: React.FC = () => { | ||||
| @@ -43,6 +44,9 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| const [selectionModel, setSelectionModel]: any[] = React.useState([]); | const [selectionModel, setSelectionModel]: any[] = React.useState([]); | ||||
| const [projectName, setProjectName]:any = React.useState("NA"); | const [projectName, setProjectName]:any = React.useState("NA"); | ||||
| const [projectFee, setProjectFee]:any = React.useState(0); | const [projectFee, setProjectFee]:any = React.useState(0); | ||||
| const [projectBudget, setProjectBudget]:any = React.useState(0); | |||||
| const [expenditure, setExpenditure]:any = React.useState(0); | |||||
| const [remainingBudget, setRemainingBudget]:any = 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); | ||||
| @@ -94,6 +98,9 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| const result = await fetchResourceSummaryDetailResult(intProjectId); | const result = await fetchResourceSummaryDetailResult(intProjectId); | ||||
| setProjectName(result[0].summaryInformation[0].projectCodeAndName) | setProjectName(result[0].summaryInformation[0].projectCodeAndName) | ||||
| setProjectFee(result[0].summaryInformation[0].totalFee) | setProjectFee(result[0].summaryInformation[0].totalFee) | ||||
| setProjectBudget(result[0].summaryInformation[0].totalBudget) | |||||
| setExpenditure(result[0].summaryInformation[0].expenditure) | |||||
| setRemainingBudget(result[0].summaryInformation[0].remainingBudget) | |||||
| setStatus(result[0].summaryInformation[0].status) | setStatus(result[0].summaryInformation[0].status) | ||||
| setPlannedResources(result[0].summaryInformation[0].plannedResources) | setPlannedResources(result[0].summaryInformation[0].plannedResources) | ||||
| setActualResourcesSpent(result[0].summaryInformation[0].resourcesSpent) | setActualResourcesSpent(result[0].summaryInformation[0].resourcesSpent) | ||||
| @@ -233,8 +240,8 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| return ( | return ( | ||||
| <React.Fragment> | <React.Fragment> | ||||
| <TableRow sx={{ '& > *': { borderBottom: 'unset' } }}> | |||||
| <TableCell> | |||||
| <TableRow sx={{ '& > *': { borderBottom: 'unset' } }} className="border-t-2 border-b-0 border-l-0 border-r-0 border-solid border-slate-300"> | |||||
| <TableCell > | |||||
| {row.task.length > 0 && ( | {row.task.length > 0 && ( | ||||
| <IconButton | <IconButton | ||||
| aria-label="expand row" | aria-label="expand row" | ||||
| @@ -247,21 +254,21 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| </TableCell> | </TableCell> | ||||
| <TableCell style={{fontSize:13}}>{row.stage}</TableCell> | <TableCell style={{fontSize:13}}>{row.stage}</TableCell> | ||||
| <TableCell style={{fontSize:13}}>{row.taskCount}</TableCell> | <TableCell style={{fontSize:13}}>{row.taskCount}</TableCell> | ||||
| <TableCell style={{fontSize:13}}>{row.g1Planned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13}}>{row.g1Actual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13}}>{row.g2Planned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13}}>{row.g2Actual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13}}>{row.g3Planned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13}}>{row.g3Actual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13}}>{row.g4Planned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13}}>{row.g4Actual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13}}>{row.g5Planned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13}}>{row.g5Actual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13}}>{row.totalPlanned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13}}>{row.totalActual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#808aff"}}>{row.g1Planned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#69dbac"}}>{row.g1Actual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#808aff"}}>{row.g2Planned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#69dbac"}}>{row.g2Actual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#808aff"}}>{row.g3Planned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#69dbac"}}>{row.g3Actual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#808aff"}}>{row.g4Planned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#69dbac"}}>{row.g4Actual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#808aff"}}>{row.g5Planned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#69dbac"}}>{row.g5Actual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#808aff"}}>{row.totalPlanned.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#69dbac"}}>{row.totalActual.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| {row.task.map((taskRow:any) => ( | {row.task.map((taskRow:any) => ( | ||||
| <TableRow key={taskRow[0]} style={{backgroundColor:"#f0f3f7"}}> | |||||
| <TableRow key={taskRow[0]} style={{backgroundColor:"#f0f3f7"}} className="border-t-2 border-b-0 border-l-0 border-r-0 border-solid border-slate-300"> | |||||
| <TableCell style={{ paddingBottom: 0, paddingTop: 0}} colSpan={1}> | <TableCell style={{ paddingBottom: 0, paddingTop: 0}} colSpan={1}> | ||||
| <Collapse in={open} timeout="auto" unmountOnExit style={{marginLeft:-17, marginRight:-17}}> | <Collapse in={open} timeout="auto" unmountOnExit style={{marginLeft:-17, marginRight:-17}}> | ||||
| <Box sx={{ margin: 0 }}> | <Box sx={{ margin: 0 }}> | ||||
| @@ -307,7 +314,7 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| <Table size="small" aria-label="tasks"> | <Table size="small" aria-label="tasks"> | ||||
| <TableBody> | <TableBody> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell style={{fontSize:13}} colSpan={1}>{taskRow[4].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#808aff"}} colSpan={1}>{taskRow[4].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| </TableBody> | </TableBody> | ||||
| </Table> | </Table> | ||||
| @@ -320,7 +327,7 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| <Table size="small" aria-label="tasks"> | <Table size="small" aria-label="tasks"> | ||||
| <TableBody> | <TableBody> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell style={{fontSize:13}} colSpan={1}>{taskRow[5].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#69dbac"}} colSpan={1}>{taskRow[5].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| </TableBody> | </TableBody> | ||||
| </Table> | </Table> | ||||
| @@ -333,7 +340,7 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| <Table size="small" aria-label="tasks"> | <Table size="small" aria-label="tasks"> | ||||
| <TableBody> | <TableBody> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell style={{fontSize:13}} colSpan={1}>{taskRow[6].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#808aff"}} colSpan={1}>{taskRow[6].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| </TableBody> | </TableBody> | ||||
| </Table> | </Table> | ||||
| @@ -346,7 +353,7 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| <Table size="small" aria-label="tasks"> | <Table size="small" aria-label="tasks"> | ||||
| <TableBody> | <TableBody> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell style={{fontSize:13}} colSpan={1}>{taskRow[7].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#69dbac"}} colSpan={1}>{taskRow[7].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| </TableBody> | </TableBody> | ||||
| </Table> | </Table> | ||||
| @@ -359,7 +366,7 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| <Table size="small" aria-label="tasks"> | <Table size="small" aria-label="tasks"> | ||||
| <TableBody> | <TableBody> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell style={{fontSize:13}} colSpan={1}>{taskRow[8].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#808aff"}} colSpan={1}>{taskRow[8].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| </TableBody> | </TableBody> | ||||
| </Table> | </Table> | ||||
| @@ -372,7 +379,7 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| <Table size="small" aria-label="tasks"> | <Table size="small" aria-label="tasks"> | ||||
| <TableBody> | <TableBody> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell style={{fontSize:13}} colSpan={1}>{taskRow[9].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#69dbac"}} colSpan={1}>{taskRow[9].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| </TableBody> | </TableBody> | ||||
| </Table> | </Table> | ||||
| @@ -385,7 +392,7 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| <Table size="small" aria-label="tasks"> | <Table size="small" aria-label="tasks"> | ||||
| <TableBody> | <TableBody> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell style={{fontSize:13}} colSpan={1}>{taskRow[10].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#808aff"}} colSpan={1}>{taskRow[10].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| </TableBody> | </TableBody> | ||||
| </Table> | </Table> | ||||
| @@ -398,7 +405,7 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| <Table size="small" aria-label="tasks"> | <Table size="small" aria-label="tasks"> | ||||
| <TableBody> | <TableBody> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell style={{fontSize:13}} colSpan={1}>{taskRow[11].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#69dbac"}} colSpan={1}>{taskRow[11].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| </TableBody> | </TableBody> | ||||
| </Table> | </Table> | ||||
| @@ -411,7 +418,7 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| <Table size="small" aria-label="tasks"> | <Table size="small" aria-label="tasks"> | ||||
| <TableBody> | <TableBody> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell style={{fontSize:13}} colSpan={1}>{taskRow[12].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#808aff"}} colSpan={1}>{taskRow[12].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| </TableBody> | </TableBody> | ||||
| </Table> | </Table> | ||||
| @@ -424,7 +431,7 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| <Table size="small" aria-label="tasks"> | <Table size="small" aria-label="tasks"> | ||||
| <TableBody> | <TableBody> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell style={{fontSize:13}} colSpan={1}>{taskRow[13].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#69dbac"}} colSpan={1}>{taskRow[13].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| </TableBody> | </TableBody> | ||||
| </Table> | </Table> | ||||
| @@ -437,7 +444,7 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| <Table size="small" aria-label="tasks"> | <Table size="small" aria-label="tasks"> | ||||
| <TableBody> | <TableBody> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell style={{fontSize:13}} colSpan={1}>{taskRow[2].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#808aff"}} colSpan={1}>{taskRow[2].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| </TableBody> | </TableBody> | ||||
| </Table> | </Table> | ||||
| @@ -450,7 +457,7 @@ const ProjectResourceSummary: React.FC = () => { | |||||
| <Table size="small" aria-label="tasks"> | <Table size="small" aria-label="tasks"> | ||||
| <TableBody> | <TableBody> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell style={{fontSize:13}} colSpan={1}>{taskRow[3].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| <TableCell style={{fontSize:13, color:"#69dbac"}} colSpan={1}>{taskRow[3].toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| </TableBody> | </TableBody> | ||||
| </Table> | </Table> | ||||
| @@ -497,6 +504,9 @@ const columns2 = [ | |||||
| field: 'g1Planned', | field: 'g1Planned', | ||||
| headerName: "Planned", | headerName: "Planned", | ||||
| flex: 0.7, | flex: 0.7, | ||||
| renderCell: (params: any) => { | |||||
| return <span style={{color:'red'}}>{params.row.g1Planned}</span>; | |||||
| }, | |||||
| }, | }, | ||||
| { | { | ||||
| id: 'g1Actual', | id: 'g1Actual', | ||||
| @@ -631,6 +641,36 @@ const columns2 = [ | |||||
| HKD ${projectFee.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | HKD ${projectFee.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div style={{ display: "inline-block", width: "33%"}}> | |||||
| <div style={{ fontSize:"1em", fontWeight:"bold"}}> | |||||
| <u> | |||||
| Total Budget | |||||
| </u> | |||||
| </div> | |||||
| <div style={{fontSize:"1em"}}> | |||||
| HKD ${projectBudget.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
| </div> | |||||
| </div> | |||||
| <div style={{ display: "inline-block", width: "33%"}}> | |||||
| <div style={{ fontSize:"1em", fontWeight:"bold"}}> | |||||
| <u> | |||||
| Cumculative Expenditure | |||||
| </u> | |||||
| </div> | |||||
| <div style={{fontSize:"1em"}}> | |||||
| HKD ${expenditure.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
| </div> | |||||
| </div> | |||||
| <div style={{ display: "inline-block", width: "33%"}}> | |||||
| <div style={{ fontSize:"1em", fontWeight:"bold"}}> | |||||
| <u> | |||||
| Remaining Budget | |||||
| </u> | |||||
| </div> | |||||
| <div style={{fontSize:"1em"}}> | |||||
| HKD ${remainingBudget.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
| </div> | |||||
| </div> | |||||
| <div style={{ display: "inline-block", width: "33%"}}> | <div style={{ display: "inline-block", width: "33%"}}> | ||||
| <div style={{ fontSize:"1em", fontWeight:"bold"}}> | <div style={{ fontSize:"1em", fontWeight:"bold"}}> | ||||
| <u> | <u> | ||||
| @@ -658,7 +698,7 @@ const columns2 = [ | |||||
| </u> | </u> | ||||
| </div> | </div> | ||||
| <div style={{fontSize:"1em"}}> | <div style={{fontSize:"1em"}}> | ||||
| {(actualResourcesSpent ?? 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} Manhours | |||||
| {(actualResourcesSpent ?? 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} Manhours ({(actualResourcesSpent/plannedResources*100).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%) | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div style={{ display: "inline-block", width: "33%"}}> | <div style={{ display: "inline-block", width: "33%"}}> | ||||
| @@ -668,7 +708,7 @@ const columns2 = [ | |||||
| </u> | </u> | ||||
| </div> | </div> | ||||
| <div style={{fontSize:"1em"}}> | <div style={{fontSize:"1em"}}> | ||||
| {(remainingResources ?? 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} Manhours | |||||
| {(remainingResources ?? 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} Manhours ({(remainingResources/plannedResources*100).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%) | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -676,8 +716,11 @@ const columns2 = [ | |||||
| <CustomDatagrid rows={projectResourcesRows} columns={columns2} columnWidth={200} dataGridHeight={480} pageSize={100} columnGroupingModel={columnGroupingModel} sx={{fontSize:13}}/> | <CustomDatagrid rows={projectResourcesRows} columns={columns2} columnWidth={200} dataGridHeight={480} pageSize={100} columnGroupingModel={columnGroupingModel} sx={{fontSize:13}}/> | ||||
| </div> */} | </div> */} | ||||
| {/* <div style={{display:"inline-block",width:"99%",marginLeft:10, marginRight:10, marginTop:10}}> */} | {/* <div style={{display:"inline-block",width:"99%",marginLeft:10, marginRight:10, marginTop:10}}> */} | ||||
| <TableContainer component={Paper}> | |||||
| <Table sx={{ minWidth: 650, maxWidth:1080}} aria-label="simple table"> | |||||
| {/* </div> */} | |||||
| </Card> | |||||
| <TableContainer component={Paper}> | |||||
| <Table sx={{ minWidth: 650, maxWidth:1920}} aria-label="simple table"> | |||||
| <TableHead> | <TableHead> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell align="center" colSpan={3}> | <TableCell align="center" colSpan={3}> | ||||
| @@ -702,22 +745,22 @@ const columns2 = [ | |||||
| Total | Total | ||||
| </TableCell> | </TableCell> | ||||
| </TableRow> | </TableRow> | ||||
| <TableRow> | |||||
| <TableCell style={{width:"5%"}}/> | |||||
| <TableCell style={{fontSize:13, minWidth:300}}>Stage</TableCell> | |||||
| <TableCell style={{fontSize:13, width:"5%"}}>Task Count</TableCell> | |||||
| <TableCell style={{fontSize:13, width:"5%"}}>Planned</TableCell> | |||||
| <TableCell style={{fontSize:13, width:"5%"}}>Actual</TableCell> | |||||
| <TableCell style={{fontSize:13, width:"5%"}}>Planned</TableCell> | |||||
| <TableCell style={{fontSize:13, width:"5%"}}>Actual</TableCell> | |||||
| <TableCell style={{fontSize:13, width:"5%"}}>Planned</TableCell> | |||||
| <TableCell style={{fontSize:13, width:"5%"}}>Actual</TableCell> | |||||
| <TableCell style={{fontSize:13, width:"5%"}}>Planned</TableCell> | |||||
| <TableCell style={{fontSize:13, width:"5%"}}>Actual</TableCell> | |||||
| <TableCell style={{fontSize:13, width:"5%"}}>Planned</TableCell> | |||||
| <TableCell style={{fontSize:13, width:"5%"}}>Actual</TableCell> | |||||
| <TableCell style={{fontSize:13, width:"5%"}}>Planned</TableCell> | |||||
| <TableCell style={{fontSize:13, width:"5%"}}>Actual</TableCell> | |||||
| <TableRow className="border-t-2 border-b-0 border-l-0 border-r-0 border-solid border-slate-300"> | |||||
| <TableCell style={{minWidth:30}}/> | |||||
| <TableCell style={{fontSize:11, minWidth:150}}>Stage</TableCell> | |||||
| <TableCell style={{fontSize:11, minWidth:30}}>Task Count</TableCell> | |||||
| <TableCell style={{fontSize:11, minWidth:30, color:"#808aff"}}>Planned</TableCell> | |||||
| <TableCell style={{fontSize:11, minWidth:30, color:"#69dbac"}}>Actual</TableCell> | |||||
| <TableCell style={{fontSize:11, minWidth:30, color:"#808aff"}}>Planned</TableCell> | |||||
| <TableCell style={{fontSize:11, minWidth:30, color:"#69dbac"}}>Actual</TableCell> | |||||
| <TableCell style={{fontSize:11, minWidth:30, color:"#808aff"}}>Planned</TableCell> | |||||
| <TableCell style={{fontSize:11, minWidth:30, color:"#69dbac"}}>Actual</TableCell> | |||||
| <TableCell style={{fontSize:11, minWidth:30, color:"#808aff"}}>Planned</TableCell> | |||||
| <TableCell style={{fontSize:11, minWidth:30, color:"#69dbac"}}>Actual</TableCell> | |||||
| <TableCell style={{fontSize:11, minWidth:30, color:"#808aff"}}>Planned</TableCell> | |||||
| <TableCell style={{fontSize:11, minWidth:30, color:"#69dbac"}}>Actual</TableCell> | |||||
| <TableCell style={{fontSize:11, minWidth:30, color:"#808aff"}}>Planned</TableCell> | |||||
| <TableCell style={{fontSize:11, minWidth:30, color:"#69dbac"}}>Actual</TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| </TableHead> | </TableHead> | ||||
| <TableBody> | <TableBody> | ||||
| @@ -727,8 +770,6 @@ const columns2 = [ | |||||
| </TableBody> | </TableBody> | ||||
| </Table> | </Table> | ||||
| </TableContainer> | </TableContainer> | ||||
| {/* </div> */} | |||||
| </Card> | |||||
| </Grid> | </Grid> | ||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -34,6 +34,7 @@ import moment from "moment"; | |||||
| import { fetchTeamCombo, fetchweeklyTeamTotalManhours, fetchmonthlyTeamTotalManhours, fetchTotalManhoursByGrade, fetchWeeklyUnsubmit, fetchMonthlyUnsubmit, fetchStaffCombo, fetchDailyIndividualStaffManhours, fetchWeeklyIndividualStaffManhours, fetchMonthlyIndividualStaffManhours } from "@/app/api/staffUtilization"; | import { fetchTeamCombo, fetchweeklyTeamTotalManhours, fetchmonthlyTeamTotalManhours, fetchTotalManhoursByGrade, fetchWeeklyUnsubmit, fetchMonthlyUnsubmit, fetchStaffCombo, fetchDailyIndividualStaffManhours, fetchWeeklyIndividualStaffManhours, fetchMonthlyIndividualStaffManhours } from "@/app/api/staffUtilization"; | ||||
| import { SessionStaff } from "@/config/authConfig"; | import { SessionStaff } from "@/config/authConfig"; | ||||
| import { VIEW_DASHBOARD_ALL } from "@/middleware"; | import { VIEW_DASHBOARD_ALL } from "@/middleware"; | ||||
| import { QrCode } from "@mui/icons-material"; | |||||
| dayjs.extend(isBetweenPlugin); | dayjs.extend(isBetweenPlugin); | ||||
| interface CustomPickerDayProps extends PickersDayProps<Dayjs> { | interface CustomPickerDayProps extends PickersDayProps<Dayjs> { | ||||
| @@ -137,15 +138,15 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||||
| firstDayOfWeekString + " to " + lastDayOfWeekString, | firstDayOfWeekString + " to " + lastDayOfWeekString, | ||||
| ); | ); | ||||
| const [unsubmittedTimeSheetSelect, setUnsubmittedTimeSheetSelect]: any = | const [unsubmittedTimeSheetSelect, setUnsubmittedTimeSheetSelect]: any = | ||||
| React.useState("Weekly"); | |||||
| React.useState("Monthly"); | |||||
| const [teamTotalManhoursSpentSelect, setTeamTotalManhoursSpentSelect]: any = | const [teamTotalManhoursSpentSelect, setTeamTotalManhoursSpentSelect]: any = | ||||
| React.useState("Weekly"); | |||||
| React.useState("Monthly"); | |||||
| const [staffGradeManhoursSpentSelect, setStaffGradeManhoursSpentSelect]: any = | const [staffGradeManhoursSpentSelect, setStaffGradeManhoursSpentSelect]: any = | ||||
| React.useState("Weekly"); | |||||
| React.useState("Monthly"); | |||||
| const [ | const [ | ||||
| individualStaffManhoursSpentSelect, | individualStaffManhoursSpentSelect, | ||||
| setIndividualStaffManhoursSpentSelect, | setIndividualStaffManhoursSpentSelect, | ||||
| ]: any = React.useState("Daily"); | |||||
| ]: any = React.useState("Monthly"); | |||||
| const weekDates: any[] = []; | const weekDates: any[] = []; | ||||
| const monthDates: any[] = []; | const monthDates: any[] = []; | ||||
| const currentDate = dayjs(); | const currentDate = dayjs(); | ||||
| @@ -665,13 +666,13 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||||
| if (staffGradeManhoursSpentSelect === "Weekly") { | if (staffGradeManhoursSpentSelect === "Weekly") { | ||||
| fetchTotalManhoursByGradeData() | fetchTotalManhoursByGradeData() | ||||
| } | } | ||||
| }, [weeklyValueByStaffGrade, weeklyToValueByStaffGrade]); | |||||
| }, [staffGradeTeamId, weeklyValueByStaffGrade, weeklyToValueByStaffGrade]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (staffGradeManhoursSpentSelect === "Monthly") { | if (staffGradeManhoursSpentSelect === "Monthly") { | ||||
| fetchMonthlyTotalManhoursByGradeData() | fetchMonthlyTotalManhoursByGradeData() | ||||
| } | } | ||||
| }, [totalManHoursMonthlyFromValue, totalManHoursMonthlyToValue]); | |||||
| }, [staffGradeTeamId, totalManHoursMonthlyFromValue, totalManHoursMonthlyToValue]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (unsubmittedTimeSheetSelect === "Weekly") { | if (unsubmittedTimeSheetSelect === "Weekly") { | ||||
| @@ -703,6 +704,21 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||||
| } | } | ||||
| }, [staffId, indivdualManHoursMonthlyFromValue]); | }, [staffId, indivdualManHoursMonthlyFromValue]); | ||||
| // useEffect(() => { | |||||
| // console.log(unsubmittedTimeSheetSelect) | |||||
| // if (unsubmittedTimeSheetSelect === "Monthly" || teamTotalManhoursSpentSelect === "Monthly" || staffGradeManhoursSpentSelect === "Monthly" || individualStaffManhoursSpentSelect === "Monthly") { | |||||
| // setUnsubmittedTimeSheetSelect("Monthly") | |||||
| // setTeamTotalManhoursSpentSelect("Monthly") | |||||
| // setStaffGradeManhoursSpentSelect("Monthly") | |||||
| // setIndividualStaffManhoursSpentSelect("Monthly") | |||||
| // } else if (unsubmittedTimeSheetSelect === "Weekly" || teamTotalManhoursSpentSelect === "Weekly" || staffGradeManhoursSpentSelect === "Weekly" || individualStaffManhoursSpentSelect === "Weekly") { | |||||
| // setUnsubmittedTimeSheetSelect("Weekly") | |||||
| // setTeamTotalManhoursSpentSelect("Weekly") | |||||
| // setStaffGradeManhoursSpentSelect("Weekly") | |||||
| // setIndividualStaffManhoursSpentSelect("Weekly") | |||||
| // } | |||||
| // }, [unsubmittedTimeSheetSelect, teamTotalManhoursSpentSelect, staffGradeManhoursSpentSelect, individualStaffManhoursSpentSelect]); | |||||
| @@ -828,7 +844,7 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||||
| const staffGradeOptions: ApexOptions = { | const staffGradeOptions: ApexOptions = { | ||||
| chart: { | chart: { | ||||
| height: 350, | height: 350, | ||||
| type: "line", | |||||
| type: "bar", | |||||
| }, | }, | ||||
| stroke: { | stroke: { | ||||
| width: [2, 2], | width: [2, 2], | ||||
| @@ -972,6 +988,7 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||||
| const teamTotalManhoursSpentOnClick = (r: any) => { | const teamTotalManhoursSpentOnClick = (r: any) => { | ||||
| setTeamTotalManhoursSpentSelect(r); | setTeamTotalManhoursSpentSelect(r); | ||||
| if (r === "Weekly") { | if (r === "Weekly") { | ||||
| fetchWeeklyTeamManhourSpentData() | fetchWeeklyTeamManhourSpentData() | ||||
| setValue(dayjs().startOf('week')); | setValue(dayjs().startOf('week')); | ||||
| @@ -1321,8 +1338,15 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||||
| Weekly | Weekly | ||||
| </button> | </button> | ||||
| <button | <button | ||||
| onClick={() => | |||||
| onClick={() => { | |||||
| teamTotalManhoursSpentOnClick("Monthly") | teamTotalManhoursSpentOnClick("Monthly") | ||||
| setUnsubmittedTimeSheetSelect("Monthly") | |||||
| fetchMonthlyUnsubmittedData() | |||||
| setStaffGradeManhoursSpentSelect("Monthly") | |||||
| fetchMonthlyTotalManhoursByGradeData() | |||||
| setIndividualStaffManhoursSpentSelect("Monthly") | |||||
| fetchMonthlyIndividualManhoursData() | |||||
| } | |||||
| } | } | ||||
| className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-r-md w-32" | className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-r-md w-32" | ||||
| > | > | ||||
| @@ -1333,8 +1357,15 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||||
| {teamTotalManhoursSpentSelect === "Monthly" && ( | {teamTotalManhoursSpentSelect === "Monthly" && ( | ||||
| <> | <> | ||||
| <button | <button | ||||
| onClick={() => | |||||
| onClick={() => { | |||||
| teamTotalManhoursSpentOnClick("Weekly") | teamTotalManhoursSpentOnClick("Weekly") | ||||
| setUnsubmittedTimeSheetSelect("Weekly") | |||||
| fetchWeeklyUnsubmittedData() | |||||
| setStaffGradeManhoursSpentSelect("Weekly") | |||||
| fetchTotalManhoursByGradeData() | |||||
| setIndividualStaffManhoursSpentSelect("Weekly") | |||||
| fetchWeeklyIndividualManhoursData() | |||||
| } | |||||
| } | } | ||||
| className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-l-md w-32" | className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-l-md w-32" | ||||
| > | > | ||||
| @@ -1451,8 +1482,13 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||||
| </button> | </button> | ||||
| <button | <button | ||||
| onClick={() => { | onClick={() => { | ||||
| teamTotalManhoursSpentOnClick("Monthly") | |||||
| setUnsubmittedTimeSheetSelect("Monthly") | |||||
| fetchMonthlyUnsubmittedData() | |||||
| setStaffGradeManhoursSpentSelect("Monthly") | setStaffGradeManhoursSpentSelect("Monthly") | ||||
| fetchMonthlyTotalManhoursByGradeData() | fetchMonthlyTotalManhoursByGradeData() | ||||
| setIndividualStaffManhoursSpentSelect("Monthly") | |||||
| fetchMonthlyIndividualManhoursData() | |||||
| } | } | ||||
| } | } | ||||
| className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-r-md w-48" | className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-r-md w-48" | ||||
| @@ -1465,8 +1501,13 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||||
| <> | <> | ||||
| <button | <button | ||||
| onClick={() => { | onClick={() => { | ||||
| teamTotalManhoursSpentOnClick("Weekly") | |||||
| setUnsubmittedTimeSheetSelect("Weekly") | |||||
| fetchWeeklyUnsubmittedData() | |||||
| setStaffGradeManhoursSpentSelect("Weekly") | setStaffGradeManhoursSpentSelect("Weekly") | ||||
| fetchTotalManhoursByGradeData() | fetchTotalManhoursByGradeData() | ||||
| setIndividualStaffManhoursSpentSelect("Weekly") | |||||
| fetchWeeklyIndividualManhoursData() | |||||
| } | } | ||||
| } | } | ||||
| className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-l-md w-32" | className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-l-md w-32" | ||||
| @@ -1601,8 +1642,13 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||||
| </button> | </button> | ||||
| <button | <button | ||||
| onClick={() => { | onClick={() => { | ||||
| unsubmittedTimeSheetOnClick("Monthly") | |||||
| fetchMonthlyUnsubmittedData() | |||||
| teamTotalManhoursSpentOnClick("Monthly") | |||||
| setUnsubmittedTimeSheetSelect("Monthly") | |||||
| fetchMonthlyUnsubmittedData() | |||||
| setStaffGradeManhoursSpentSelect("Monthly") | |||||
| fetchMonthlyTotalManhoursByGradeData() | |||||
| setIndividualStaffManhoursSpentSelect("Monthly") | |||||
| fetchMonthlyIndividualManhoursData() | |||||
| } | } | ||||
| } | } | ||||
| className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-r-md w-32" | className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-r-md w-32" | ||||
| @@ -1615,8 +1661,13 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||||
| <> | <> | ||||
| <button | <button | ||||
| onClick={() => { | onClick={() => { | ||||
| unsubmittedTimeSheetOnClick("Weekly") | |||||
| fetchWeeklyUnsubmittedData() | |||||
| teamTotalManhoursSpentOnClick("Weekly") | |||||
| setUnsubmittedTimeSheetSelect("Weekly") | |||||
| fetchWeeklyUnsubmittedData() | |||||
| setStaffGradeManhoursSpentSelect("Weekly") | |||||
| fetchTotalManhoursByGradeData() | |||||
| setIndividualStaffManhoursSpentSelect("Weekly") | |||||
| fetchWeeklyIndividualManhoursData() | |||||
| } | } | ||||
| } | } | ||||
| className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid w-32" | className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid w-32" | ||||
| @@ -1788,7 +1839,7 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||||
| )} | )} | ||||
| {individualStaffManhoursSpentSelect === "Weekly" && ( | {individualStaffManhoursSpentSelect === "Weekly" && ( | ||||
| <> | <> | ||||
| <button | |||||
| {/* <button | |||||
| onClick={() => { | onClick={() => { | ||||
| individualStaffManhoursSpentOnClick("Daily") | individualStaffManhoursSpentOnClick("Daily") | ||||
| fetchDailyIndividualManhoursData() | fetchDailyIndividualManhoursData() | ||||
| @@ -1797,13 +1848,18 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||||
| className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-l-md w-32" | className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-l-md w-32" | ||||
| > | > | ||||
| Daily | Daily | ||||
| </button> | |||||
| </button> */} | |||||
| <button className="text-lg bg-violet-100 border-violet-500 text-violet-500 border-solid w-32"> | <button className="text-lg bg-violet-100 border-violet-500 text-violet-500 border-solid w-32"> | ||||
| Weekly | Weekly | ||||
| </button> | </button> | ||||
| <button | <button | ||||
| onClick={() => { | onClick={() => { | ||||
| individualStaffManhoursSpentOnClick("Monthly") | |||||
| teamTotalManhoursSpentOnClick("Monthly") | |||||
| setUnsubmittedTimeSheetSelect("Monthly") | |||||
| fetchMonthlyUnsubmittedData() | |||||
| setStaffGradeManhoursSpentSelect("Monthly") | |||||
| fetchMonthlyTotalManhoursByGradeData() | |||||
| setIndividualStaffManhoursSpentSelect("Monthly") | |||||
| fetchMonthlyIndividualManhoursData() | fetchMonthlyIndividualManhoursData() | ||||
| } | } | ||||
| } | } | ||||
| @@ -1815,7 +1871,7 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||||
| )} | )} | ||||
| {individualStaffManhoursSpentSelect === "Monthly" && ( | {individualStaffManhoursSpentSelect === "Monthly" && ( | ||||
| <> | <> | ||||
| <button | |||||
| {/* <button | |||||
| onClick={() => { | onClick={() => { | ||||
| individualStaffManhoursSpentOnClick("Daily") | individualStaffManhoursSpentOnClick("Daily") | ||||
| fetchDailyIndividualManhoursData() | fetchDailyIndividualManhoursData() | ||||
| @@ -1824,10 +1880,15 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||||
| className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-l-md w-32" | className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid rounded-l-md w-32" | ||||
| > | > | ||||
| Daily | Daily | ||||
| </button> | |||||
| </button> */} | |||||
| <button | <button | ||||
| onClick={() => { | onClick={() => { | ||||
| individualStaffManhoursSpentOnClick("Weekly") | |||||
| teamTotalManhoursSpentOnClick("Weekly") | |||||
| setUnsubmittedTimeSheetSelect("Weekly") | |||||
| fetchWeeklyUnsubmittedData() | |||||
| setStaffGradeManhoursSpentSelect("Weekly") | |||||
| fetchTotalManhoursByGradeData() | |||||
| setIndividualStaffManhoursSpentSelect("Weekly") | |||||
| fetchWeeklyIndividualManhoursData() | fetchWeeklyIndividualManhoursData() | ||||
| } | } | ||||
| } | } | ||||