@@ -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() | ||||
} | } | ||||
} | } | ||||