@@ -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; | |||
totalExpenditure: number; | |||
expenditureReceivable: number; | |||
totalProjectFee: number; | |||
invoicedPercentage: number; | |||
} | |||
export interface CashFlowAnticipatedChartResult { | |||
anticipateIncomeList: any[]; | |||
@@ -21,14 +21,14 @@ export interface ClientSubsidiaryProjectResult { | |||
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){ | |||
return serverFetchJson<ClientSubsidiaryProjectResult[]>( | |||
`${BASE_API_URL}/dashboard/searchCustomerSubsidiaryProject?customerId=${customerId}` | |||
`${BASE_API_URL}/dashboard/searchCustomerSubsidiaryProject?customerId=${customerId}&tableSorting=${tableSorting}` | |||
); | |||
} else { | |||
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; | |||
} | |||
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[]>( | |||
`${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/ProjectStatusByClient": "Project Status by Client", | |||
"/dashboard/ProjectStatusByTeam": "Project Status by Team", | |||
"/dashboard/ProjectResourceConsumptionRanking": "Project Resource Consumption Ranking", | |||
"/dashboard/StaffUtilization": "Staff Utilization", | |||
"/projects": "Projects", | |||
"/projects/create": "Create Project", | |||
@@ -127,6 +127,11 @@ const NavigationContent: React.FC<Props> = ({ abilities, username }) => { | |||
label: "Project Status by Team", | |||
path: "/dashboard/ProjectStatusByTeam", | |||
}, | |||
{ | |||
icon: <AccountTreeIcon />, | |||
label: "Project Resource Consumption Ranking", | |||
path: "/dashboard/ProjectResourceConsumptionRanking", | |||
}, | |||
{ | |||
icon: <PeopleIcon />, | |||
label: "Staff Utilization", | |||
@@ -59,6 +59,9 @@ const ProgressByClient: React.FC<Props> = () => { | |||
const [receiptToDate, setReceiptToDate] = useState(null); | |||
const [selectedRows, setSelectedRows]: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 color = ["#f57f90", "#94f7d6", "#87c5f5", "#ab95f5", "#fcd68b", | |||
"#f58a9b", "#8ef4d1", "#92caf9", "#a798f9", "#fad287", | |||
@@ -86,6 +89,7 @@ const ProgressByClient: React.FC<Props> = () => { | |||
"#d4feed", "#0ab2ff", "#ff88a2", "#4fda21", "#cefb2f", | |||
"#d1fef0", "#04afff", "#ff859e", "#4bdd15", "#ccfb2b"]; | |||
const [clientSubsidiaryProjectResult, setClientSubsidiaryProjectResult]:any[] = useState([]); | |||
const [tableSorting, setTableSorting] = useState('ProjectName'); | |||
const fetchData = async () => { | |||
if (customerId && subsidiaryId) { | |||
@@ -93,12 +97,13 @@ const ProgressByClient: React.FC<Props> = () => { | |||
if (subsidiaryId === '-'){ | |||
console.log("ss") | |||
const clickResult = await fetchAllClientSubsidiaryProjects( | |||
Number(customerId),Number(0)) | |||
Number(customerId),tableSorting,Number(0)) | |||
console.log(clickResult) | |||
setClientSubsidiaryProjectResult(clickResult); | |||
} else { | |||
const clickResult = await fetchAllClientSubsidiaryProjects( | |||
Number(customerId), | |||
tableSorting, | |||
Number(subsidiaryId)) | |||
console.log(clickResult) | |||
setClientSubsidiaryProjectResult(clickResult); | |||
@@ -113,19 +118,28 @@ const ProgressByClient: React.FC<Props> = () => { | |||
useEffect(() => { | |||
const projectCode = [] | |||
const projectName = [] | |||
const projectBudgetedManHour = [] | |||
const projectSpentManHour = [] | |||
const manhourConsumptionPercentage = [] | |||
for (let i = 0; i < clientSubsidiaryProjectResult.length; 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) | |||
} | |||
setChartProjectName(projectCode) | |||
setChartProjectDisplayName(projectName) | |||
setChartProjectBudgetedHour(projectBudgetedManHour) | |||
setChartProjectSpentHour(projectSpentManHour) | |||
setChartManhourConsumptionPercentage(manhourConsumptionPercentage) | |||
}, [clientSubsidiaryProjectResult]); | |||
useEffect(() => { | |||
fetchData() | |||
}, [customerId,subsidiaryId]); | |||
}, [customerId,subsidiaryId,tableSorting]); | |||
@@ -383,7 +397,31 @@ const ProgressByClient: React.FC<Props> = () => { | |||
const options: ApexOptions = { | |||
chart: { | |||
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: [{ | |||
name: "Project Resource Consumption Percentage", | |||
@@ -418,10 +456,15 @@ const ProgressByClient: React.FC<Props> = () => { | |||
bar: { | |||
horizontal: true, | |||
distributed: true, | |||
dataLabels: { | |||
position: 'top' | |||
}, | |||
}, | |||
}, | |||
dataLabels: { | |||
enabled: false, | |||
enabled: true, | |||
textAnchor: 'end', | |||
formatter: (val) => `${val}%`, | |||
}, | |||
xaxis: { | |||
categories: chartProjectName, | |||
@@ -443,6 +486,11 @@ const ProgressByClient: React.FC<Props> = () => { | |||
}, | |||
grid: { | |||
borderColor: "#f1f1f1", | |||
xaxis: { | |||
lines: { | |||
show: true, | |||
} | |||
} | |||
}, | |||
annotations: {}, | |||
}; | |||
@@ -517,11 +565,35 @@ const ProgressByClient: React.FC<Props> = () => { | |||
<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={350} | |||
height={500} | |||
/> | |||
</div> | |||
{/* <div style={{display:"inline-block",width:"20%",verticalAlign:"top",textAlign:"center"}}> | |||
@@ -533,6 +605,8 @@ const ProgressByClient: React.FC<Props> = () => { | |||
); | |||
})} | |||
</div> */} | |||
</Card> | |||
<Card className="mt-2"> | |||
<CardHeader | |||
className="text-slate-500" | |||
title="Resource Consumption and Coming Milestone" | |||
@@ -561,15 +635,15 @@ const ProgressByClient: React.FC<Props> = () => { | |||
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 | |||
className="text-slate-500" | |||
title="Overall Progress per Project" | |||
/> | |||
{percentageArray.length === 0 && ( | |||
<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" }} | |||
> | |||
Please select the project you want to check. | |||
@@ -580,12 +654,13 @@ const ProgressByClient: React.FC<Props> = () => { | |||
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: 20 }}> | |||
<Card style={{ marginLeft: 15, marginRight: 20, marginTop: 20}}> | |||
<div> | |||
<div | |||
className="mt-5 text-lg font-medium" | |||
@@ -49,6 +49,9 @@ const ProgressByTeam: React.FC = () => { | |||
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", | |||
@@ -77,16 +80,21 @@ const ProgressByTeam: React.FC = () => { | |||
"#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 fetchData = async () => { | |||
console.log(tableSorting) | |||
if (teamLeadId) { | |||
try { | |||
const clickResult = await fetchAllTeamProjects( | |||
Number(teamLeadId)) | |||
Number(teamLeadId),tableSorting) | |||
console.log(clickResult) | |||
setTeamProjectResult(clickResult); | |||
} catch (error) { | |||
@@ -97,19 +105,29 @@ const ProgressByTeam: React.FC = () => { | |||
useEffect(() => { | |||
const projectNo = [] | |||
const projectName = [] | |||
const projectBudgetedManHour = [] | |||
const projectSpentManHour = [] | |||
const manhourConsumptionPercentage = [] | |||
for (let i = 0; i < teamProjectResult.length; 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) | |||
} | |||
setChartProjectName(projectNo) | |||
setChartProjectDisplayName(projectName) | |||
setChartProjectBudgetedHour(projectBudgetedManHour) | |||
setChartProjectSpentHour(projectSpentManHour) | |||
setChartManhourConsumptionPercentage(manhourConsumptionPercentage) | |||
}, [teamProjectResult]); | |||
useEffect(() => { | |||
fetchData() | |||
}, [teamLeadId]); | |||
}, [teamLeadId,tableSorting]); | |||
const rows = [ | |||
{ | |||
@@ -462,7 +480,31 @@ const ProgressByTeam: React.FC = () => { | |||
const options: ApexOptions = { | |||
chart: { | |||
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: [{ | |||
name: "Project Resource Consumption Percentage", | |||
@@ -473,10 +515,15 @@ const ProgressByTeam: React.FC = () => { | |||
bar: { | |||
horizontal: true, | |||
distributed: true, | |||
dataLabels: { | |||
position: 'top' | |||
}, | |||
}, | |||
}, | |||
dataLabels: { | |||
enabled: false, | |||
enabled: true, | |||
textAnchor: 'end', | |||
formatter: (val) => `${val}%`, | |||
}, | |||
xaxis: { | |||
categories: currentPageProjectList, | |||
@@ -498,6 +545,11 @@ const ProgressByTeam: React.FC = () => { | |||
}, | |||
grid: { | |||
borderColor: "#f1f1f1", | |||
xaxis: { | |||
lines: { | |||
show: true, | |||
} | |||
} | |||
}, | |||
annotations: {}, | |||
}; | |||
@@ -564,11 +616,17 @@ const ProgressByTeam: React.FC = () => { | |||
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]); | |||
@@ -598,11 +656,35 @@ const ProgressByTeam: React.FC = () => { | |||
<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={350} | |||
height={500} | |||
/> | |||
<div className="float-right mr-4 mb-10"> | |||
{currentPage === 1 && ( | |||
@@ -646,6 +728,8 @@ const ProgressByTeam: React.FC = () => { | |||
); | |||
})} | |||
</div> */} | |||
</Card> | |||
<Card className="mt-2"> | |||
<CardHeader | |||
className="text-slate-500" | |||
title="Resource Consumption and Coming Milestone" | |||
@@ -674,16 +758,16 @@ const ProgressByTeam: React.FC = () => { | |||
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 | |||
className="text-slate-500" | |||
title="Overall Progress per Project" | |||
/> | |||
{percentageArray.length === 0 && ( | |||
<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. | |||
</div> | |||
@@ -693,12 +777,13 @@ const ProgressByTeam: React.FC = () => { | |||
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: 20 }}> | |||
<Card style={{ marginLeft: 15, marginRight: 20, marginTop: 15 }}> | |||
<div> | |||
<div | |||
className="mt-5 text-lg font-medium" | |||
@@ -23,6 +23,7 @@ import { fetchProjectsCashFlow,fetchProjectsCashFlowMonthlyChart,fetchProjectsCa | |||
import { Input, Label } from "reactstrap"; | |||
import { CashFlow } from "@/app/api/cashflow"; | |||
import dayjs from 'dayjs'; | |||
import ProjectTotalFee from "../CreateInvoice_forGen/ProjectTotalFee"; | |||
interface Props { | |||
projects: CashFlow[]; | |||
@@ -45,6 +46,8 @@ const ProjectCashFlow: React.FC = () => { | |||
const [monthlyChartRightMax, setMonthlyChartRightMax]: any[] = React.useState(10); | |||
const [monthlyAnticipateLeftMax, setMonthlyAnticipateLeftMax]: any[] = React.useState(10); | |||
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 [totalInvoiced,setTotalInvoiced]: any[] = React.useState(0); | |||
const [totalReceived,setTotalReceived]: any[] = React.useState(0); | |||
@@ -121,6 +124,8 @@ const ProjectCashFlow: React.FC = () => { | |||
const cashFlowReceivableAndExpenditureData = await fetchProjectsCashFlowReceivableAndExpenditure(selectedProjectIdList); | |||
if(cashFlowReceivableAndExpenditureData.length !== 0){ | |||
setReceivedPercentage(cashFlowReceivableAndExpenditureData[0].receivedPercentage) | |||
setInvoicedPercentage(cashFlowReceivableAndExpenditureData[0].invoicedPercentage) | |||
setTotalFee(cashFlowReceivableAndExpenditureData[0].totalProjectFee) | |||
setTotalInvoiced(cashFlowReceivableAndExpenditureData[0].totalInvoiced) | |||
setTotalReceived(cashFlowReceivableAndExpenditureData[0].totalReceived) | |||
setReceivable(cashFlowReceivableAndExpenditureData[0].receivable) | |||
@@ -511,9 +516,9 @@ const ProjectCashFlow: React.FC = () => { | |||
const accountsReceivableOptions: ApexOptions = { | |||
colors: ["#20E647"], | |||
series: [receivedPercentage], | |||
series: [receivedPercentage,invoicedPercentage], | |||
chart: { | |||
height: 350, | |||
height: 50, | |||
type: "radialBar", | |||
}, | |||
plotOptions: { | |||
@@ -533,13 +538,23 @@ const ProjectCashFlow: React.FC = () => { | |||
}, | |||
dataLabels: { | |||
name: { | |||
show: false, | |||
show: true, | |||
fontSize: "0.9em", | |||
}, | |||
value: { | |||
color: "#3e98c7", | |||
fontSize: "3em", | |||
fontSize: "1.5em", | |||
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: { | |||
lineCap: "round", | |||
}, | |||
labels: ["AccountsReceivable"], | |||
labels: ["Accounts Receivable","Account Invoiced"], | |||
}; | |||
const expenditureOptions: ApexOptions = { | |||
@@ -582,11 +597,12 @@ const ProjectCashFlow: React.FC = () => { | |||
}, | |||
dataLabels: { | |||
name: { | |||
show: false, | |||
show: true, | |||
fontSize: "0.9em", | |||
}, | |||
value: { | |||
color: "#3e98c7", | |||
fontSize: "3em", | |||
fontSize: "1.5em", | |||
show: true, | |||
}, | |||
}, | |||
@@ -604,7 +620,7 @@ const ProjectCashFlow: React.FC = () => { | |||
stroke: { | |||
lineCap: "round", | |||
}, | |||
labels: ["AccountsReceivable"], | |||
labels: ["Accounts Expenditure"], | |||
}; | |||
const rows = [ | |||
@@ -812,12 +828,26 @@ const ProjectCashFlow: React.FC = () => { | |||
className="text-slate-500" | |||
title="Accounts Receivable (HKD)" | |||
/> | |||
<div style={{ display: "inline-block", width: "99%" }}> | |||
<ReactApexChart | |||
options={accountsReceivableOptions} | |||
series={accountsReceivableOptions.series} | |||
type="radialBar" | |||
/> | |||
</div> | |||
<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 | |||
className="text-sm font-medium ml-5 mt-2" | |||
style={{ color: "#898d8d" }} | |||
@@ -25,10 +25,13 @@ interface Props { | |||
TotalBudget: number; | |||
TotalCumulative: number; | |||
TotalInvoicedAmount: number; | |||
TotalUnInvoicedAmount: number; | |||
TotalReceivedAmount: number; | |||
CashFlowStatus: string; | |||
CostPerformanceIndex: number; | |||
ClickedIndex: number; | |||
ProjectedCPI: number; | |||
ProjectedCashFlowStatus: string; | |||
Index: number; | |||
} | |||
@@ -39,10 +42,13 @@ const ProjectFinancialCard: React.FC<Props> = ({ | |||
TotalBudget, | |||
TotalCumulative, | |||
TotalInvoicedAmount, | |||
TotalUnInvoicedAmount, | |||
TotalReceivedAmount, | |||
CashFlowStatus, | |||
CostPerformanceIndex, | |||
ClickedIndex, | |||
ProjectedCPI, | |||
ProjectedCashFlowStatus, | |||
Index, | |||
}) => { | |||
const [SearchCriteria, setSearchCriteria] = React.useState({}); | |||
@@ -106,6 +112,13 @@ const ProjectFinancialCard: React.FC<Props> = ({ | |||
{TotalInvoicedAmount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||
</div> | |||
<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" }}> | |||
Total Received Amount | |||
</div> | |||
@@ -164,6 +177,57 @@ const ProjectFinancialCard: React.FC<Props> = ({ | |||
</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> | |||
); | |||
}; | |||
@@ -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', | |||
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 ( | |||
<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) => { | |||
@@ -398,7 +465,7 @@ const columns2 = [ | |||
<div className="ml-10 mr-10" style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'start'}}> | |||
{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)}> | |||
<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> | |||
@@ -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 { useSearchParams } from 'next/navigation'; | |||
import {fetchResourceSummaryDetailResult} from "@/app/api/resourcesummary/actions"; | |||
import { set } from "lodash"; | |||
const ProjectResourceSummary: React.FC = () => { | |||
@@ -43,6 +44,9 @@ const ProjectResourceSummary: React.FC = () => { | |||
const [selectionModel, setSelectionModel]: any[] = React.useState([]); | |||
const [projectName, setProjectName]:any = React.useState("NA"); | |||
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 [plannedResources, setPlannedResources]:any = React.useState(0); | |||
const [actualResourcesSpent, setActualResourcesSpent]:any = React.useState(0); | |||
@@ -94,6 +98,9 @@ const ProjectResourceSummary: React.FC = () => { | |||
const result = await fetchResourceSummaryDetailResult(intProjectId); | |||
setProjectName(result[0].summaryInformation[0].projectCodeAndName) | |||
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) | |||
setPlannedResources(result[0].summaryInformation[0].plannedResources) | |||
setActualResourcesSpent(result[0].summaryInformation[0].resourcesSpent) | |||
@@ -233,8 +240,8 @@ const ProjectResourceSummary: React.FC = () => { | |||
return ( | |||
<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 && ( | |||
<IconButton | |||
aria-label="expand row" | |||
@@ -247,21 +254,21 @@ const ProjectResourceSummary: React.FC = () => { | |||
</TableCell> | |||
<TableCell style={{fontSize:13}}>{row.stage}</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> | |||
{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}> | |||
<Collapse in={open} timeout="auto" unmountOnExit style={{marginLeft:-17, marginRight:-17}}> | |||
<Box sx={{ margin: 0 }}> | |||
@@ -307,7 +314,7 @@ const ProjectResourceSummary: React.FC = () => { | |||
<Table size="small" aria-label="tasks"> | |||
<TableBody> | |||
<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> | |||
</TableBody> | |||
</Table> | |||
@@ -320,7 +327,7 @@ const ProjectResourceSummary: React.FC = () => { | |||
<Table size="small" aria-label="tasks"> | |||
<TableBody> | |||
<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> | |||
</TableBody> | |||
</Table> | |||
@@ -333,7 +340,7 @@ const ProjectResourceSummary: React.FC = () => { | |||
<Table size="small" aria-label="tasks"> | |||
<TableBody> | |||
<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> | |||
</TableBody> | |||
</Table> | |||
@@ -346,7 +353,7 @@ const ProjectResourceSummary: React.FC = () => { | |||
<Table size="small" aria-label="tasks"> | |||
<TableBody> | |||
<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> | |||
</TableBody> | |||
</Table> | |||
@@ -359,7 +366,7 @@ const ProjectResourceSummary: React.FC = () => { | |||
<Table size="small" aria-label="tasks"> | |||
<TableBody> | |||
<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> | |||
</TableBody> | |||
</Table> | |||
@@ -372,7 +379,7 @@ const ProjectResourceSummary: React.FC = () => { | |||
<Table size="small" aria-label="tasks"> | |||
<TableBody> | |||
<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> | |||
</TableBody> | |||
</Table> | |||
@@ -385,7 +392,7 @@ const ProjectResourceSummary: React.FC = () => { | |||
<Table size="small" aria-label="tasks"> | |||
<TableBody> | |||
<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> | |||
</TableBody> | |||
</Table> | |||
@@ -398,7 +405,7 @@ const ProjectResourceSummary: React.FC = () => { | |||
<Table size="small" aria-label="tasks"> | |||
<TableBody> | |||
<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> | |||
</TableBody> | |||
</Table> | |||
@@ -411,7 +418,7 @@ const ProjectResourceSummary: React.FC = () => { | |||
<Table size="small" aria-label="tasks"> | |||
<TableBody> | |||
<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> | |||
</TableBody> | |||
</Table> | |||
@@ -424,7 +431,7 @@ const ProjectResourceSummary: React.FC = () => { | |||
<Table size="small" aria-label="tasks"> | |||
<TableBody> | |||
<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> | |||
</TableBody> | |||
</Table> | |||
@@ -437,7 +444,7 @@ const ProjectResourceSummary: React.FC = () => { | |||
<Table size="small" aria-label="tasks"> | |||
<TableBody> | |||
<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> | |||
</TableBody> | |||
</Table> | |||
@@ -450,7 +457,7 @@ const ProjectResourceSummary: React.FC = () => { | |||
<Table size="small" aria-label="tasks"> | |||
<TableBody> | |||
<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> | |||
</TableBody> | |||
</Table> | |||
@@ -497,6 +504,9 @@ const columns2 = [ | |||
field: 'g1Planned', | |||
headerName: "Planned", | |||
flex: 0.7, | |||
renderCell: (params: any) => { | |||
return <span style={{color:'red'}}>{params.row.g1Planned}</span>; | |||
}, | |||
}, | |||
{ | |||
id: 'g1Actual', | |||
@@ -631,6 +641,36 @@ const columns2 = [ | |||
HKD ${projectFee.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||
</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={{ fontSize:"1em", fontWeight:"bold"}}> | |||
<u> | |||
@@ -658,7 +698,7 @@ const columns2 = [ | |||
</u> | |||
</div> | |||
<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 style={{ display: "inline-block", width: "33%"}}> | |||
@@ -668,7 +708,7 @@ const columns2 = [ | |||
</u> | |||
</div> | |||
<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> | |||
@@ -676,8 +716,11 @@ const columns2 = [ | |||
<CustomDatagrid rows={projectResourcesRows} columns={columns2} columnWidth={200} dataGridHeight={480} pageSize={100} columnGroupingModel={columnGroupingModel} sx={{fontSize:13}}/> | |||
</div> */} | |||
{/* <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> | |||
<TableRow> | |||
<TableCell align="center" colSpan={3}> | |||
@@ -702,22 +745,22 @@ const columns2 = [ | |||
Total | |||
</TableCell> | |||
</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> | |||
</TableHead> | |||
<TableBody> | |||
@@ -727,8 +770,6 @@ const columns2 = [ | |||
</TableBody> | |||
</Table> | |||
</TableContainer> | |||
{/* </div> */} | |||
</Card> | |||
</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 { SessionStaff } from "@/config/authConfig"; | |||
import { VIEW_DASHBOARD_ALL } from "@/middleware"; | |||
import { QrCode } from "@mui/icons-material"; | |||
dayjs.extend(isBetweenPlugin); | |||
interface CustomPickerDayProps extends PickersDayProps<Dayjs> { | |||
@@ -137,15 +138,15 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||
firstDayOfWeekString + " to " + lastDayOfWeekString, | |||
); | |||
const [unsubmittedTimeSheetSelect, setUnsubmittedTimeSheetSelect]: any = | |||
React.useState("Weekly"); | |||
React.useState("Monthly"); | |||
const [teamTotalManhoursSpentSelect, setTeamTotalManhoursSpentSelect]: any = | |||
React.useState("Weekly"); | |||
React.useState("Monthly"); | |||
const [staffGradeManhoursSpentSelect, setStaffGradeManhoursSpentSelect]: any = | |||
React.useState("Weekly"); | |||
React.useState("Monthly"); | |||
const [ | |||
individualStaffManhoursSpentSelect, | |||
setIndividualStaffManhoursSpentSelect, | |||
]: any = React.useState("Daily"); | |||
]: any = React.useState("Monthly"); | |||
const weekDates: any[] = []; | |||
const monthDates: any[] = []; | |||
const currentDate = dayjs(); | |||
@@ -665,13 +666,13 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||
if (staffGradeManhoursSpentSelect === "Weekly") { | |||
fetchTotalManhoursByGradeData() | |||
} | |||
}, [weeklyValueByStaffGrade, weeklyToValueByStaffGrade]); | |||
}, [staffGradeTeamId, weeklyValueByStaffGrade, weeklyToValueByStaffGrade]); | |||
useEffect(() => { | |||
if (staffGradeManhoursSpentSelect === "Monthly") { | |||
fetchMonthlyTotalManhoursByGradeData() | |||
} | |||
}, [totalManHoursMonthlyFromValue, totalManHoursMonthlyToValue]); | |||
}, [staffGradeTeamId, totalManHoursMonthlyFromValue, totalManHoursMonthlyToValue]); | |||
useEffect(() => { | |||
if (unsubmittedTimeSheetSelect === "Weekly") { | |||
@@ -703,6 +704,21 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||
} | |||
}, [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 = { | |||
chart: { | |||
height: 350, | |||
type: "line", | |||
type: "bar", | |||
}, | |||
stroke: { | |||
width: [2, 2], | |||
@@ -972,6 +988,7 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||
const teamTotalManhoursSpentOnClick = (r: any) => { | |||
setTeamTotalManhoursSpentSelect(r); | |||
if (r === "Weekly") { | |||
fetchWeeklyTeamManhourSpentData() | |||
setValue(dayjs().startOf('week')); | |||
@@ -1321,8 +1338,15 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||
Weekly | |||
</button> | |||
<button | |||
onClick={() => | |||
onClick={() => { | |||
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" | |||
> | |||
@@ -1333,8 +1357,15 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||
{teamTotalManhoursSpentSelect === "Monthly" && ( | |||
<> | |||
<button | |||
onClick={() => | |||
onClick={() => { | |||
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" | |||
> | |||
@@ -1451,8 +1482,13 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||
</button> | |||
<button | |||
onClick={() => { | |||
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-48" | |||
@@ -1465,8 +1501,13 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||
<> | |||
<button | |||
onClick={() => { | |||
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" | |||
@@ -1601,8 +1642,13 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||
</button> | |||
<button | |||
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" | |||
@@ -1615,8 +1661,13 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||
<> | |||
<button | |||
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" | |||
@@ -1788,7 +1839,7 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||
)} | |||
{individualStaffManhoursSpentSelect === "Weekly" && ( | |||
<> | |||
<button | |||
{/* <button | |||
onClick={() => { | |||
individualStaffManhoursSpentOnClick("Daily") | |||
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" | |||
> | |||
Daily | |||
</button> | |||
</button> */} | |||
<button className="text-lg bg-violet-100 border-violet-500 text-violet-500 border-solid w-32"> | |||
Weekly | |||
</button> | |||
<button | |||
onClick={() => { | |||
individualStaffManhoursSpentOnClick("Monthly") | |||
teamTotalManhoursSpentOnClick("Monthly") | |||
setUnsubmittedTimeSheetSelect("Monthly") | |||
fetchMonthlyUnsubmittedData() | |||
setStaffGradeManhoursSpentSelect("Monthly") | |||
fetchMonthlyTotalManhoursByGradeData() | |||
setIndividualStaffManhoursSpentSelect("Monthly") | |||
fetchMonthlyIndividualManhoursData() | |||
} | |||
} | |||
@@ -1815,7 +1871,7 @@ const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => { | |||
)} | |||
{individualStaffManhoursSpentSelect === "Monthly" && ( | |||
<> | |||
<button | |||
{/* <button | |||
onClick={() => { | |||
individualStaffManhoursSpentOnClick("Daily") | |||
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" | |||
> | |||
Daily | |||
</button> | |||
</button> */} | |||
<button | |||
onClick={() => { | |||
individualStaffManhoursSpentOnClick("Weekly") | |||
teamTotalManhoursSpentOnClick("Weekly") | |||
setUnsubmittedTimeSheetSelect("Weekly") | |||
fetchWeeklyUnsubmittedData() | |||
setStaffGradeManhoursSpentSelect("Weekly") | |||
fetchTotalManhoursByGradeData() | |||
setIndividualStaffManhoursSpentSelect("Weekly") | |||
fetchWeeklyIndividualManhoursData() | |||
} | |||
} | |||