Selaa lähdekoodia

1. add authority to dashboard

2. fix bugs in dashboard (staff utilization, project resource summary)
tags/Baseline_30082024_FRONTEND_UAT
cyril.tsui 1 vuosi sitten
vanhempi
commit
c01670e0fb
9 muutettua tiedostoa jossa 344 lisäystä ja 270 poistoa
  1. +68
    -49
      src/components/CompanyTeamCashFlow/CompanyTeamCashFlow.tsx
  2. +11
    -0
      src/components/CompanyTeamCashFlow/CompanyTeamCashFlowWrapper.tsx
  3. +1
    -1
      src/components/CompanyTeamCashFlow/index.ts
  4. +5
    -0
      src/components/NavigationContent/NavigationContent.tsx
  5. +2
    -2
      src/components/ProjectResourceSummary/ProjectResourceSummary.tsx
  6. +238
    -217
      src/components/StaffUtilization/StaffUtilization.tsx
  7. +15
    -0
      src/components/StaffUtilization/StaffUtilizationWrapper.tsx
  8. +1
    -1
      src/components/StaffUtilization/index.ts
  9. +3
    -0
      src/middleware.ts

+ 68
- 49
src/components/CompanyTeamCashFlow/CompanyTeamCashFlow.tsx Näytä tiedosto

@@ -21,13 +21,20 @@ import { Suspense } from "react";
import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch";
import { Input, Label } from "reactstrap";
import Select, { components } from "react-select";
import {fetchTeamCombo,fetchTeamCashFlowChartData} from "@/app/api/teamCashflow";
import { fetchTeamCombo, fetchTeamCashFlowChartData } from "@/app/api/teamCashflow";
import { VIEW_DASHBOARD_ALL } from "@/middleware";
import { SessionStaff } from "@/config/authConfig";

const CompanyTeamCashFlow: React.FC = () => {
interface Props {
abilities: string[],
staff: SessionStaff,
}

const CompanyTeamCashFlow: React.FC<Props> = ({ abilities, staff }) => {
const todayDate = new Date();
const [selectionModel, setSelectionModel]: any[] = React.useState([]);
const [teamOptions, setTeamOptions]: any[] = React.useState([]);
const [teamId, setTeamId]:any = React.useState(null);
const [teamId, setTeamId]: any = React.useState(null);
const [monthlyIncomeList, setMonthlyIncomeList]: any[] = React.useState([]);
const [monthlyCumulativeIncomeList, setMonthlyCumulativeIncomeList]: any[] = React.useState([]);
const [monthlyExpenditureList, setMonthlyExpenditureList]: any[] = React.useState([]);
@@ -39,25 +46,27 @@ const CompanyTeamCashFlow: React.FC = () => {
);

const fetchChartData = async () => {
const cashFlowMonthlyChartData:any = await fetchTeamCashFlowChartData(cashFlowYear,teamId);
console.log(cashFlowMonthlyChartData[0])
const monthlyIncome = []
const cumulativeIncome = []
const monthlyExpenditure = []
const cumulativeExpenditure = []
var leftMax = 0
var rightMax = 0
// if (cashFlowMonthlyChartData.length !== 0) {
if (teamOptions.length > 0) {
const tempTeamId = abilities.includes(VIEW_DASHBOARD_ALL) ? teamId : teamOptions[0]?.value
const cashFlowMonthlyChartData: any = await fetchTeamCashFlowChartData(cashFlowYear, tempTeamId);
console.log(cashFlowMonthlyChartData[0])
const monthlyIncome = []
const cumulativeIncome = []
const monthlyExpenditure = []
const cumulativeExpenditure = []
var leftMax = 0
var rightMax = 0
// if (cashFlowMonthlyChartData.length !== 0) {
for (var i = 0; i < cashFlowMonthlyChartData[0].teamCashFlowIncome.length; i++) {
if (leftMax < cashFlowMonthlyChartData[0].teamCashFlowIncome[i].income || leftMax < cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].expenditure){
leftMax = Math.max(cashFlowMonthlyChartData[0].teamCashFlowIncome[i].income,cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].expenditure)
if (leftMax < cashFlowMonthlyChartData[0].teamCashFlowIncome[i].income || leftMax < cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].expenditure) {
leftMax = Math.max(cashFlowMonthlyChartData[0].teamCashFlowIncome[i].income, cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].expenditure)
}
monthlyIncome.push(cashFlowMonthlyChartData[0].teamCashFlowIncome[i].income)
cumulativeIncome.push(cashFlowMonthlyChartData[0].teamCashFlowIncome[i].cumulativeIncome)
}
for (var i = 0; i < cashFlowMonthlyChartData[0].teamCashFlowExpenditure.length; i++) {
if (rightMax < cashFlowMonthlyChartData[0].teamCashFlowIncome[i].cumulativeIncome || rightMax < cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].cumulativeExpenditure){
rightMax = Math.max(cashFlowMonthlyChartData[0].teamCashFlowIncome[i].cumulativeIncome,cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].cumulativeExpenditure)
if (rightMax < cashFlowMonthlyChartData[0].teamCashFlowIncome[i].cumulativeIncome || rightMax < cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].cumulativeExpenditure) {
rightMax = Math.max(cashFlowMonthlyChartData[0].teamCashFlowIncome[i].cumulativeIncome, cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].cumulativeExpenditure)
}
monthlyExpenditure.push(cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].expenditure)
cumulativeExpenditure.push(cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].cumulativeExpenditure)
@@ -68,30 +77,38 @@ const CompanyTeamCashFlow: React.FC = () => {
setMonthlyCumulativeExpenditureList(cumulativeExpenditure)
setMonthlyChartLeftMax(leftMax)
setMonthlyChartRightMax(rightMax)
// } else {
// setMonthlyIncomeList([0,0,0,0,0,0,0,0,0,0,0,0])
// setMonthlyCumulativeIncomeList([0,0,0,0,0,0,0,0,0,0,0,0])
// setMonthlyExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0])
// setMonthlyCumulativeExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0])
// }
// } else {
// setMonthlyIncomeList([0,0,0,0,0,0,0,0,0,0,0,0])
// setMonthlyCumulativeIncomeList([0,0,0,0,0,0,0,0,0,0,0,0])
// setMonthlyExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0])
// setMonthlyCumulativeExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0])
// }
}
}

const fetchComboData = async () => {
const teamComboList = []
const teamCombo = await fetchTeamCombo();
for (var i = 0; i < teamCombo.records.length; i++) {
teamComboList.push({value: teamCombo.records[i].id, label: teamCombo.records[i].label})

if (abilities.includes(VIEW_DASHBOARD_ALL)) {
for (var i = 0; i < teamCombo.records.length; i++) {
teamComboList.push({ value: teamCombo.records[i].id, label: teamCombo.records[i].label })
}
} else {
const tempTeam = teamCombo.records.find(record => record.id === staff.teamId)
teamComboList.push({ value: tempTeam?.id, label: tempTeam?.label })
}

setTeamOptions(teamComboList)
}

useEffect(() => {
fetchComboData()
}, []);
}, []);

useEffect(() => {
useEffect(() => {
fetchChartData()
}, [cashFlowYear,teamId]);
}, [cashFlowYear, teamId, teamOptions]);

const columns = [
{
@@ -180,7 +197,7 @@ const CompanyTeamCashFlow: React.FC = () => {
const options: ApexOptions = {
tooltip: {
y: {
formatter: function(val) {
formatter: function (val) {
return val.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
}
@@ -227,7 +244,7 @@ const CompanyTeamCashFlow: React.FC = () => {
tickAmount: 5,
labels: {
formatter: function (val) {
return val.toLocaleString()
return val.toLocaleString()
}
}
},
@@ -252,7 +269,7 @@ const CompanyTeamCashFlow: React.FC = () => {
tickAmount: 5,
labels: {
formatter: function (val) {
return val.toLocaleString()
return val.toLocaleString()
}
}
},
@@ -339,25 +356,27 @@ const CompanyTeamCashFlow: React.FC = () => {
&gt;
</button>
</div>
<div className="inline-block ml-2">
<Label className="text-slate-500 font-medium">
Team:&nbsp;
</Label>
</div>
<div className="inline-block ml-1 w-60">
<Select
placeholder="All Team"
options={teamOptions}
isClearable={true}
onChange={(selectedOption:any) => {
if (selectedOption === null) {
setTeamId(null);
} else {
setTeamId(selectedOption.value);
}
}}
/>
</div>
{abilities.includes(VIEW_DASHBOARD_ALL) && <div className="inline-block">
<div className="inline-block ml-2">
<Label className="text-slate-500 font-medium">
Team:&nbsp;
</Label>
</div>
<div className="inline-block ml-1 w-60">
<Select
placeholder="All Team"
options={teamOptions}
isClearable={true}
onChange={(selectedOption: any) => {
if (selectedOption === null) {
setTeamId(null);
} else {
setTeamId(selectedOption.value);
}
}}
/>
</div>
</div>}
<ReactApexChart
options={options}
series={options.series}


+ 11
- 0
src/components/CompanyTeamCashFlow/CompanyTeamCashFlowWrapper.tsx Näytä tiedosto

@@ -0,0 +1,11 @@
import React from "react";
import CompanyTeamCashFlow from "./CompanyTeamCashFlow";
import { getUserAbilities, getUserStaff } from "@/app/utils/commonUtil";

const CompanyTeamCashFlowWrapper: React.FC = async () => {
const [abilities, staff] = await Promise.all([getUserAbilities(), getUserStaff()]);

return <CompanyTeamCashFlow abilities={abilities} staff={staff}/>;
};

export default CompanyTeamCashFlowWrapper;

+ 1
- 1
src/components/CompanyTeamCashFlow/index.ts Näytä tiedosto

@@ -1 +1 @@
export { default } from "./CompanyTeamCashFlow";
export { default } from "./CompanyTeamCashFlowWrapper";

+ 5
- 0
src/components/NavigationContent/NavigationContent.tsx Näytä tiedosto

@@ -40,6 +40,8 @@ import {
MAINTAIN_PROJECT,
MAINTAIN_TASK_TEMPLATE,
MAINTAIN_USER,
VIEW_DASHBOARD_ALL,
VIEW_DASHBOARD_SELF,
VIEW_MASTERDATA,
VIEW_PROJECT,
VIEW_USER,
@@ -73,6 +75,9 @@ const NavigationContent: React.FC<Props> = ({ abilities }) => {
icon: <Dashboard />,
label: "Dashboard",
path: "",
isHidden: ![VIEW_DASHBOARD_ALL, VIEW_DASHBOARD_SELF].some((ability) =>
abilities!.includes(ability),
),
children: [
{
icon: <SummarizeIcon />,


+ 2
- 2
src/components/ProjectResourceSummary/ProjectResourceSummary.tsx Näytä tiedosto

@@ -658,7 +658,7 @@ const columns2 = [
</u>
</div>
<div style={{fontSize:"1em"}}>
{actualResourcesSpent.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} Manhours
{(actualResourcesSpent ?? 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} Manhours
</div>
</div>
<div style={{ display: "inline-block", width: "33%"}}>
@@ -668,7 +668,7 @@ const columns2 = [
</u>
</div>
<div style={{fontSize:"1em"}}>
{remainingResources.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} Manhours
{(remainingResources ?? 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} Manhours
</div>
</div>
</div>


+ 238
- 217
src/components/StaffUtilization/StaffUtilization.tsx Näytä tiedosto

@@ -31,7 +31,9 @@ import { PickersDay, PickersDayProps } from "@mui/x-date-pickers/PickersDay";
import { styled } from "@mui/material/styles";
import Holidays from "date-holidays";
import moment from "moment";
import {fetchTeamCombo, fetchweeklyTeamTotalManhours, fetchmonthlyTeamTotalManhours,fetchTotalManhoursByGrade,fetchWeeklyUnsubmit,fetchMonthlyUnsubmit,fetchStaffCombo,fetchDailyIndividualStaffManhours,fetchWeeklyIndividualStaffManhours,fetchMonthlyIndividualStaffManhours} from "@/app/api/staffUtilization";
import { fetchTeamCombo, fetchweeklyTeamTotalManhours, fetchmonthlyTeamTotalManhours, fetchTotalManhoursByGrade, fetchWeeklyUnsubmit, fetchMonthlyUnsubmit, fetchStaffCombo, fetchDailyIndividualStaffManhours, fetchWeeklyIndividualStaffManhours, fetchMonthlyIndividualStaffManhours } from "@/app/api/staffUtilization";
import { SessionStaff } from "@/config/authConfig";
import { VIEW_DASHBOARD_ALL } from "@/middleware";

dayjs.extend(isBetweenPlugin);
interface CustomPickerDayProps extends PickersDayProps<Dayjs> {
@@ -95,7 +97,14 @@ function Day(
);
}

const StaffUtilization: React.FC = () => {
interface Props {
abilities: string[];
staff: SessionStaff;
}

const StaffUtilization: React.FC<Props> = ({ abilities, staff }) => {
const abilityViewDashboardAll = abilities.includes(VIEW_DASHBOARD_ALL)

const todayDate = new Date();
const firstDayOfWeek = new Date();
const lastDayOfWeek = new Date();
@@ -190,7 +199,7 @@ const StaffUtilization: React.FC = () => {
React.useState<Dayjs>(dayjs().startOf('week').add(6, 'day'));
const [weeklyValueByIndividualStaff, setWeeklyValueByIndividualStaff] =
React.useState<Dayjs>(dayjs().startOf('week'));
const [weeklyUnsubmittedTimeSheet, setWeeklyUnsubmittedTimeSheet ] =
const [weeklyUnsubmittedTimeSheet, setWeeklyUnsubmittedTimeSheet] =
React.useState<Dayjs>(dayjs().startOf('week'));
const [staffGradeManhoursSpentValue, setStaffGradeManhoursSpentValue] =
React.useState<Dayjs>(dayjs());
@@ -232,7 +241,7 @@ const StaffUtilization: React.FC = () => {
] = React.useState<Dayjs>(dayjs());
const hd = new Holidays('HK');
const currentYear = new Date().getFullYear();
const years = [currentYear -2, currentYear - 1, currentYear, currentYear + 1, currentYear + 2];
const years = [currentYear - 2, currentYear - 1, currentYear, currentYear + 1, currentYear + 2];
let allHolidays: any[] = [];
years.forEach(year => {
const holidays = hd.getHolidays(year);
@@ -241,40 +250,45 @@ const StaffUtilization: React.FC = () => {
const holidayDates = allHolidays.map(holiday => moment(holiday.date).format('YYYY-MM-DD')).join(',');
const [totalManHoursMaxValue, setTotalManHoursMaxValue] = React.useState(5);
const [totalManHoursByGradeMaxValue, setTotalManHoursByGradeMaxValue] = React.useState(5);
const [totalManhourByGradeActualManhours, setTotalManhourByGradeActualManhours]:any[] = React.useState([]);
const [totalManhourByGradePlannedManhours, setTotalManhourByGradePlannedManhours]:any[] = React.useState([]);
const [gradeNameList, setGradeNameList]:any[] = React.useState([]);
const [totalManhourByGradeActualManhours, setTotalManhourByGradeActualManhours]: any[] = React.useState([]);
const [totalManhourByGradePlannedManhours, setTotalManhourByGradePlannedManhours]: any[] = React.useState([]);
const [gradeNameList, setGradeNameList]: any[] = React.useState([]);
const [teamManhoursTeamOptions, setTeamManhoursTeamOptions]: any[] = React.useState([]);
const [staffOptions, setStaffOptions]: any[] = React.useState([]);
const [teamManhoursTeamId, setTeamManhoursTeamId]: any[] = React.useState(0);
const [teamUnsubmitTeamId, setTeamUnsubmitTeamId]: any[] = React.useState(0);
const [teamManhoursTeamId, setTeamManhoursTeamId]: any[] = React.useState(abilityViewDashboardAll ? 0 : staff.teamId);
const [teamUnsubmitTeamId, setTeamUnsubmitTeamId]: any[] = React.useState(abilityViewDashboardAll ? 0 : staff.teamId);
const [staffId, setStaffId]: any[] = React.useState(0);
const [unsubmitCount, setUnsubmitCount]: any[] = React.useState([]);
const [unsubmitStaffList, setUnsubmitStaffList]: any[] = React.useState([]);
const [individualStaffProjectList, setIndividualStaffProjectList]: any[] = React.useState([]);
const [individualStaffManhours, setIndividualStaffManhours]:any[] = React.useState([]);
const [individualStaffManhoursPercentage, setIndividualStaffManhoursPercentage]:any[] = React.useState([]);
const [totalNormalConsumption, setTotalNormalConsumption]:any = React.useState('NA');
const [totalOtConsumption, setTotalOtConsumption]:any = React.useState('NA');
const [totalLeaveHours, setTotalLeaveHours]:any = React.useState('NA');
const [individualStaffManhours, setIndividualStaffManhours]: any[] = React.useState([]);
const [individualStaffManhoursPercentage, setIndividualStaffManhoursPercentage]: any[] = React.useState([]);
const [totalNormalConsumption, setTotalNormalConsumption]: any = React.useState('NA');
const [totalOtConsumption, setTotalOtConsumption]: any = React.useState('NA');
const [totalLeaveHours, setTotalLeaveHours]: any = React.useState('NA');

const fetchComboData = async () => {
const staffComboList = []
const teamComboList = []
const teamCombo = await fetchTeamCombo();
const staffCombo = await fetchStaffCombo();
for (var i = 0; i < teamCombo.records.length; i++) {
teamComboList.push({value: teamCombo.records[i].id, label: teamCombo.records[i].label})
if (abilityViewDashboardAll) {
for (var i = 0; i < teamCombo.records.length; i++) {
teamComboList.push({ value: teamCombo.records[i].id, label: teamCombo.records[i].label })
}
} else {
const tempTeam = teamCombo.records.find(team => team.id === staff.teamId)
teamComboList.push({ value: tempTeam?.id, label: tempTeam?.label })
}
for (var i = 0; i < staffCombo.length; i++) {
staffComboList.push({value: staffCombo[i].id, label: staffCombo[i].label})
staffComboList.push({ value: staffCombo[i].id, label: staffCombo[i].label })
}
setTeamManhoursTeamOptions(teamComboList)
setStaffOptions(staffComboList)
}

const fetchWeeklyTeamManhourSpentData = async () => {
const fetchResult = await fetchweeklyTeamTotalManhours(teamManhoursTeamId,value.format('YYYY-MM-DD'));
const fetchResult = await fetchweeklyTeamTotalManhours(teamManhoursTeamId, value.format('YYYY-MM-DD'));
const weeklyActual = fetchResult[0].weeklyActualTeamTotalManhoursSpent
const weeklyPlanned = fetchResult[0].weeklyPlannedTeamTotalManhoursSpent
const weeklyActualList = []
@@ -368,7 +382,7 @@ const StaffUtilization: React.FC = () => {
const gradeList = []
const actualList = []
const plannedList = []
var manhours = 0
for (var i = 0; i < actualManhours.length; i++) {
actualList.push(actualManhours[i].manhours.toFixed(2))
@@ -381,13 +395,13 @@ const StaffUtilization: React.FC = () => {
var gradeId = plannedManhours[0].id
for (var i = 0; i < plannedManhours.length; i++) {
if (plannedManhours[i].id === gradeId) {
manhours += (plannedManhours[i].searchDuration - plannedManhours[i].startDiff - plannedManhours[i].endDiff) * plannedManhours[i].avgGradeManhour
if (chartMax < manhours) {
chartMax = manhours
}
if (i === plannedManhours.length - 1) {
plannedList.push(manhours.toFixed(2))
}
manhours += (plannedManhours[i].searchDuration - plannedManhours[i].startDiff - plannedManhours[i].endDiff) * plannedManhours[i].avgGradeManhour
if (chartMax < manhours) {
chartMax = manhours
}
if (i === plannedManhours.length - 1) {
plannedList.push(manhours.toFixed(2))
}
} else {
plannedList.push(manhours.toFixed(2))
manhours = 0
@@ -415,7 +429,7 @@ const StaffUtilization: React.FC = () => {
const gradeList = []
const actualList = []
const plannedList = []
var manhours = 0
for (var i = 0; i < actualManhours.length; i++) {
actualList.push(actualManhours[i].manhours.toFixed(2))
@@ -428,13 +442,13 @@ const StaffUtilization: React.FC = () => {
var gradeId = plannedManhours[0].id
for (var i = 0; i < plannedManhours.length; i++) {
if (plannedManhours[i].id === gradeId) {
manhours += (plannedManhours[i].searchDuration - plannedManhours[i].startDiff - plannedManhours[i].endDiff) * plannedManhours[i].avgGradeManhour
if (chartMax < manhours) {
chartMax = manhours
}
if (i === plannedManhours.length - 1) {
plannedList.push(manhours.toFixed(2))
}
manhours += (plannedManhours[i].searchDuration - plannedManhours[i].startDiff - plannedManhours[i].endDiff) * plannedManhours[i].avgGradeManhour
if (chartMax < manhours) {
chartMax = manhours
}
if (i === plannedManhours.length - 1) {
plannedList.push(manhours.toFixed(2))
}
} else {
plannedList.push(manhours.toFixed(2))
manhours = 0
@@ -486,11 +500,15 @@ const StaffUtilization: React.FC = () => {
const result = []
const projectList = []
const percentageList = []
console.log(manhoursResult)
if (manhoursResult.length > 0) {
for (var i = 0; i < manhoursResult.length; i++) {
result.push(manhoursResult[i].manhours)
projectList.push(manhoursResult[i].projectName)
percentageList.push(manhoursResult[i].percentage)
if (manhoursResult[i].id !== null) {
result.push(manhoursResult[i].manhours)
projectList.push(manhoursResult[i].projectName)
percentageList.push(manhoursResult[i].percentage)
}
}
setIndividualStaffProjectList(projectList)
setIndividualStaffManhours(result)
@@ -499,11 +517,11 @@ const StaffUtilization: React.FC = () => {
setTotalOtConsumption(totalResult[0].otManhours)
setTotalLeaveHours(leaveResult[0].leaveHours)
}
}

const fetchWeeklyIndividualManhoursData = async () => {
const fetchResult = await fetchWeeklyIndividualStaffManhours(staffId,weeklyValueByIndividualStaff.format('YYYY-MM-DD'),weeklyValueByIndividualStaff.add(6,'days').format('YYYY-MM-DD'));
const fetchResult = await fetchWeeklyIndividualStaffManhours(staffId, weeklyValueByIndividualStaff.format('YYYY-MM-DD'), weeklyValueByIndividualStaff.add(6, 'days').format('YYYY-MM-DD'));
console.log(fetchResult)
const manhoursResult = fetchResult[0].individualStaffManhoursSpentWeekly
const totalResult = fetchResult[0].individualStaffTotalManhoursSpentWeekly
@@ -524,11 +542,11 @@ const StaffUtilization: React.FC = () => {
setTotalOtConsumption(totalResult[0].otManhours)
setTotalLeaveHours(leaveResult[0].leaveHours)
}
}

const fetchMonthlyIndividualManhoursData = async () => {
const fetchResult = await fetchMonthlyIndividualStaffManhours(staffId,indivdualManHoursMonthlyFromValue.format('YYYY-MM-01'));
const fetchResult = await fetchMonthlyIndividualStaffManhours(staffId, indivdualManHoursMonthlyFromValue.format('YYYY-MM-01'));
const manhoursResult = fetchResult[0].individualStaffManhoursSpentByMonth
const totalResult = fetchResult[0].individualStaffTotalManhoursSpentByMonth
const leaveResult = fetchResult[0].individualStaffTotalLeaveHoursByMonth
@@ -548,70 +566,70 @@ const StaffUtilization: React.FC = () => {
setTotalOtConsumption(totalResult[0].otManhours)
setTotalLeaveHours(leaveResult[0].leaveHours)
}
}

useEffect(() => {
fetchComboData()
}, []);
}, []);


useEffect(() => {
if (teamTotalManhoursSpentSelect === "Weekly"){
if (teamTotalManhoursSpentSelect === "Weekly") {
fetchWeeklyTeamManhourSpentData()
}
}, [value,teamManhoursTeamId]);
}, [value, teamManhoursTeamId]);

useEffect(() => {
if (teamTotalManhoursSpentSelect === "Monthly"){
fetchMonthlyTeamManhourSpentData()
useEffect(() => {
if (teamTotalManhoursSpentSelect === "Monthly") {
fetchMonthlyTeamManhourSpentData()
}
}, [totalManHoursMonthlyFromValue,totalManHoursMonthlyToValue,teamManhoursTeamId]);
}, [totalManHoursMonthlyFromValue, totalManHoursMonthlyToValue, teamManhoursTeamId]);

useEffect(() => {
if (staffGradeManhoursSpentSelect === "Weekly"){
useEffect(() => {
if (staffGradeManhoursSpentSelect === "Weekly") {
fetchTotalManhoursByGradeData()
}
}, [weeklyValueByStaffGrade,weeklyToValueByStaffGrade]);
}, [weeklyValueByStaffGrade, weeklyToValueByStaffGrade]);

useEffect(() => {
if (staffGradeManhoursSpentSelect === "Monthly"){
useEffect(() => {
if (staffGradeManhoursSpentSelect === "Monthly") {
fetchMonthlyTotalManhoursByGradeData()
}
}, [totalManHoursMonthlyFromValue,totalManHoursMonthlyToValue]);
}, [totalManHoursMonthlyFromValue, totalManHoursMonthlyToValue]);

useEffect(() => {
if (unsubmittedTimeSheetSelect === "Weekly"){
useEffect(() => {
if (unsubmittedTimeSheetSelect === "Weekly") {
fetchWeeklyUnsubmittedData()
}
}, [teamUnsubmitTeamId, weeklyUnsubmittedTimeSheet]);
}, [teamUnsubmitTeamId, weeklyUnsubmittedTimeSheet]);

useEffect(() => {
if (unsubmittedTimeSheetSelect === "Monthly"){
useEffect(() => {
if (unsubmittedTimeSheetSelect === "Monthly") {
fetchMonthlyUnsubmittedData()
}
}, [teamUnsubmitTeamId,unsubmitMonthlyFromValue, unsubmitMonthlyToValue]);
}, [teamUnsubmitTeamId, unsubmitMonthlyFromValue, unsubmitMonthlyToValue]);

useEffect(() => {
if (individualStaffManhoursSpentSelect === "Daily"){
useEffect(() => {
if (individualStaffManhoursSpentSelect === "Daily") {
fetchDailyIndividualManhoursData()
}
}, [staffId, totalManHoursByIndividualStaffDailyFromValue]);
}, [staffId, totalManHoursByIndividualStaffDailyFromValue]);

useEffect(() => {
if (individualStaffManhoursSpentSelect === "Weekly"){
useEffect(() => {
if (individualStaffManhoursSpentSelect === "Weekly") {
fetchWeeklyIndividualManhoursData()
}
}, [staffId, weeklyValueByIndividualStaff]);
}, [staffId, weeklyValueByIndividualStaff]);

useEffect(() => {
if (individualStaffManhoursSpentSelect === "Monthly"){
useEffect(() => {
if (individualStaffManhoursSpentSelect === "Monthly") {
fetchMonthlyIndividualManhoursData()
}
}, [staffId, indivdualManHoursMonthlyFromValue]);
}, [staffId, indivdualManHoursMonthlyFromValue]);




const teamOptions = [
{ value: 1, label: "XXX Team" },
@@ -1140,7 +1158,7 @@ const StaffUtilization: React.FC = () => {
chart: {
type: "donut",
},
colors: ['#f57f90','#94f7d6','#87c5f5','#ab95f5','#ab95f5'],
colors: ['#f57f90', '#94f7d6', '#87c5f5', '#ab95f5', '#ab95f5'],
plotOptions: {
pie: {
donut: {
@@ -1168,7 +1186,7 @@ const StaffUtilization: React.FC = () => {
},
},
},
series:individualStaffManhoursPercentage,
series: individualStaffManhoursPercentage,
labels: individualStaffProjectList,
legend: {
show: false,
@@ -1234,25 +1252,28 @@ const StaffUtilization: React.FC = () => {
)}
</div>
<div className="inline-block w-fit mt-2">
<div className="inline-block ml-6">
<Label className="text-slate-500 font-medium">
Team:&nbsp;
</Label>
</div>
<div className="inline-block ml-1 w-60">
<Select
placeholder="Please select a team"
options={teamManhoursTeamOptions}
isClearable={true}
onChange={(selectedOption:any) => {
if (selectedOption === null) {
setTeamManhoursTeamId(null);
} else {
setTeamManhoursTeamId(selectedOption.value);
}
}}
/>
</div>
{abilityViewDashboardAll &&
<div className="inline-block">
<div className="inline-block ml-6">
<Label className="text-slate-500 font-medium">
Team:&nbsp;
</Label>
</div>
<div className="inline-block ml-1 w-60">
<Select
placeholder="Please select a team"
options={teamManhoursTeamOptions}
isClearable={true}
onChange={(selectedOption: any) => {
if (selectedOption === null) {
setTeamManhoursTeamId(null);
} else {
setTeamManhoursTeamId(selectedOption.value);
}
}}
/>
</div>
</div>}
<div className="ml-6 mt-2" style={{ verticalAlign: "top" }}>
{/* <Label className="text-slate-500 font-medium ml-6">
Period:&nbsp;
@@ -1337,7 +1358,7 @@ const StaffUtilization: React.FC = () => {
onClick={() => {
setStaffGradeManhoursSpentSelect("Monthly")
fetchMonthlyTotalManhoursByGradeData()
}
}
}
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"
>
@@ -1351,7 +1372,7 @@ const StaffUtilization: React.FC = () => {
onClick={() => {
setStaffGradeManhoursSpentSelect("Weekly")
fetchTotalManhoursByGradeData()
}
}
}
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"
>
@@ -1450,50 +1471,49 @@ const StaffUtilization: React.FC = () => {
>
<Grid item xs={12} md={12} lg={12}>
<Card className="mb-5">
<CardHeader
className="text-slate-500"
title="Unsubmitted Time Sheet by Staff"
/>
<div style={{ display: "inline-block", width: "99%" }}>
<div className="w-fit align-top mr-5 float-right">
{unsubmittedTimeSheetSelect === "Weekly" && (
<>
<button className="text-lg bg-violet-100 border-violet-500 text-violet-500 border-solid w-32">
Weekly
</button>
<button
onClick={() =>
{
unsubmittedTimeSheetOnClick("Monthly")
fetchMonthlyUnsubmittedData()
}
}
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"
>
Monthly
</button>
</>
)}
{unsubmittedTimeSheetSelect === "Monthly" && (
<>
<button
onClick={() =>
{
unsubmittedTimeSheetOnClick("Weekly")
fetchWeeklyUnsubmittedData()
}
}
className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid w-32"
>
Weekly
</button>
<button className="text-lg bg-violet-100 border-violet-500 text-violet-500 border-solid rounded-r-md w-32">
Monthly
</button>
</>
)}
</div>
<div className="inline-block w-fit mt-2">
<CardHeader
className="text-slate-500"
title="Unsubmitted Time Sheet by Staff"
/>
<div style={{ display: "inline-block", width: "99%" }}>
<div className="w-fit align-top mr-5 float-right">
{unsubmittedTimeSheetSelect === "Weekly" && (
<>
<button className="text-lg bg-violet-100 border-violet-500 text-violet-500 border-solid w-32">
Weekly
</button>
<button
onClick={() => {
unsubmittedTimeSheetOnClick("Monthly")
fetchMonthlyUnsubmittedData()
}
}
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"
>
Monthly
</button>
</>
)}
{unsubmittedTimeSheetSelect === "Monthly" && (
<>
<button
onClick={() => {
unsubmittedTimeSheetOnClick("Weekly")
fetchWeeklyUnsubmittedData()
}
}
className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid w-32"
>
Weekly
</button>
<button className="text-lg bg-violet-100 border-violet-500 text-violet-500 border-solid rounded-r-md w-32">
Monthly
</button>
</>
)}
</div>
<div className="inline-block w-fit mt-2">
{abilityViewDashboardAll && <div className="inline-block">
<div className="inline-block ml-6">
<Label className="text-slate-500 font-medium">
Team:&nbsp;
@@ -1504,7 +1524,7 @@ const StaffUtilization: React.FC = () => {
placeholder="Please select a team"
options={teamManhoursTeamOptions}
isClearable={true}
onChange={(selectedOption:any) => {
onChange={(selectedOption: any) => {
if (selectedOption === null) {
setTeamUnsubmitTeamId(null);
} else {
@@ -1513,50 +1533,52 @@ const StaffUtilization: React.FC = () => {
}}
/>
</div>
<div className="ml-6 mt-2" style={{ verticalAlign: "top" }}>
{/* <Label className="text-slate-500 font-medium ml-6">
</div>}

<div className="ml-6 mt-2" style={{ verticalAlign: "top" }}>
{/* <Label className="text-slate-500 font-medium ml-6">
Period:&nbsp;
</Label> */}
{unsubmittedTimeSheetSelect === "Weekly" && (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DatePicker
className="w-72 h-10 align-top"
label="Period:"
value={value}
format="DD-MM-YYYY"
onChange={(newValue) =>
selectWeeklyPeriodUnsubmittedTimeSheet(newValue)
}
showDaysOutsideCurrentMonth
displayWeekNumber
slots={{ day: Day }}
slotProps={{
day: (ownerState) =>
({
selectedDay: value,
hoveredDay,
onPointerEnter: () =>
setHoveredDay(ownerState.day),
onPointerLeave: () => setHoveredDay(null),
}) as any,
}}
/>
</LocalizationProvider>
)}
{unsubmittedTimeSheetSelect === "Monthly" && (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DatePicker
className="w-40 h-10 align-top"
onChange={(newValue) =>
selectUnsubmittedTimeSheetMonthlyPeriodFrom(newValue)
}
defaultValue={
unsubmitMonthlyFromValue
}
label={"On"}
views={["month", "year"]}
/>
{/* <DatePicker
{unsubmittedTimeSheetSelect === "Weekly" && (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DatePicker
className="w-72 h-10 align-top"
label="Period:"
value={value}
format="DD-MM-YYYY"
onChange={(newValue) =>
selectWeeklyPeriodUnsubmittedTimeSheet(newValue)
}
showDaysOutsideCurrentMonth
displayWeekNumber
slots={{ day: Day }}
slotProps={{
day: (ownerState) =>
({
selectedDay: value,
hoveredDay,
onPointerEnter: () =>
setHoveredDay(ownerState.day),
onPointerLeave: () => setHoveredDay(null),
}) as any,
}}
/>
</LocalizationProvider>
)}
{unsubmittedTimeSheetSelect === "Monthly" && (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DatePicker
className="w-40 h-10 align-top"
onChange={(newValue) =>
selectUnsubmittedTimeSheetMonthlyPeriodFrom(newValue)
}
defaultValue={
unsubmitMonthlyFromValue
}
label={"On"}
views={["month", "year"]}
/>
{/* <DatePicker
className="w-40 h-10 align-top"
onChange={(newValue) =>
selectUnsubmittedTimeSheetMonthlyPeriodTo(newValue)
@@ -1567,20 +1589,20 @@ const StaffUtilization: React.FC = () => {
label={"To"}
views={["month", "year"]}
/> */}
</LocalizationProvider>
)}
</div>
</LocalizationProvider>
)}
</div>

<ReactApexChart
options={unsubmittedTimeSheetOptions}
series={unsubmittedTimeSheetOptions.series}
type="bar"
height="380"
/>
</div>
</Card>
<Card>

<ReactApexChart
options={unsubmittedTimeSheetOptions}
series={unsubmittedTimeSheetOptions.series}
type="bar"
height="380"
/>
</div>
</Card>
<Card>
<Card>
<CardHeader
className="text-slate-500"
@@ -1597,7 +1619,7 @@ const StaffUtilization: React.FC = () => {
onClick={() => {
individualStaffManhoursSpentOnClick("Weekly")
fetchMonthlyIndividualManhoursData()
}
}
}
className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid w-32"
>
@@ -1607,7 +1629,7 @@ const StaffUtilization: React.FC = () => {
onClick={() => {
individualStaffManhoursSpentOnClick("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"
>
@@ -1621,7 +1643,7 @@ const StaffUtilization: React.FC = () => {
onClick={() => {
individualStaffManhoursSpentOnClick("Daily")
fetchDailyIndividualManhoursData()
}
}
}
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"
>
@@ -1631,11 +1653,10 @@ const StaffUtilization: React.FC = () => {
Weekly
</button>
<button
onClick={() =>
{
onClick={() => {
individualStaffManhoursSpentOnClick("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"
>
@@ -1649,7 +1670,7 @@ const StaffUtilization: React.FC = () => {
onClick={() => {
individualStaffManhoursSpentOnClick("Daily")
fetchDailyIndividualManhoursData()
}
}
}
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"
>
@@ -1657,9 +1678,9 @@ const StaffUtilization: React.FC = () => {
</button>
<button
onClick={() => {
individualStaffManhoursSpentOnClick("Weekly")
fetchMonthlyIndividualManhoursData()
}
individualStaffManhoursSpentOnClick("Weekly")
fetchMonthlyIndividualManhoursData()
}
}
className="hover:cursor-pointer hover:bg-violet-50 text-lg bg-transparent border-violet-500 text-violet-500 border-solid w-32"
>
@@ -1682,7 +1703,7 @@ const StaffUtilization: React.FC = () => {
placeholder="Please select a staff"
options={staffOptions}
isClearable={true}
onChange={(selectedOption:any) => {
onChange={(selectedOption: any) => {
if (selectedOption === null) {
setStaffId(null);
} else {
@@ -1700,7 +1721,7 @@ const StaffUtilization: React.FC = () => {
<DatePicker
className="w-40 h-10 align-top"
format="DD-MM-YYYY"
onChange={(newValue:any) =>
onChange={(newValue: any) =>
setTotalManHoursByIndividualStaffDailyFromValue(newValue)
// selectIndividualStaffMonthlyPeriodFrom(newValue)
}
@@ -1753,10 +1774,10 @@ const StaffUtilization: React.FC = () => {
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DatePicker
className="w-40 h-10 align-top"
onChange={(newValue:any) =>{
onChange={(newValue: any) => {
console.log(newValue)
setIndivdualManHoursMonthlyFromValue(newValue)
}
}
// selectIndividualStaffMonthlyPeriodFrom(newValue)
}
defaultValue={
@@ -1854,15 +1875,15 @@ const StaffUtilization: React.FC = () => {
</Card> */}
</div>
</div>
<div style={{ display: "inline-block", width: "50%",verticalAlign:"top",marginTop:10}}>
<Card>
<CardHeader className="text-slat-500" title="Effort Proportion for individual Staff"/>
<ReactApexChart
options={options2}
series={options2.series}
type="donut"
/>
</Card>
<div style={{ display: "inline-block", width: "50%", verticalAlign: "top", marginTop: 10 }}>
<Card>
<CardHeader className="text-slat-500" title="Effort Proportion for individual Staff" />
<ReactApexChart
options={options2}
series={options2.series}
type="donut"
/>
</Card>
</div>
</Card>
</Grid>


+ 15
- 0
src/components/StaffUtilization/StaffUtilizationWrapper.tsx Näytä tiedosto

@@ -0,0 +1,15 @@
import { getUserAbilities, getUserStaff } from "@/app/utils/commonUtil";
import StaffUtilization from "./StaffUtilization";

const StaffUtilizationWrapper: React.FC = async () => {
const [abilities, staff] = await Promise.all([
getUserAbilities(),
getUserStaff(),
]);

return (
<StaffUtilization abilities={abilities} staff={staff} />
);
};

export default StaffUtilizationWrapper;

+ 1
- 1
src/components/StaffUtilization/index.ts Näytä tiedosto

@@ -1 +1 @@
export { default } from "./StaffUtilization";
export { default } from "./StaffUtilizationWrapper";

+ 3
- 0
src/middleware.ts Näytä tiedosto

@@ -124,6 +124,9 @@ export default async function middleware(
if (req.nextUrl.pathname.startsWith('/invoice')) {
isAuth = [IMPORT_INVOICE].some((ability) => abilities.includes(ability));
}
if (req.nextUrl.pathname.startsWith('/dashboard')) {
isAuth = [VIEW_DASHBOARD_ALL, VIEW_DASHBOARD_SELF].some((ability) => abilities.includes(ability));
}
return isAuth
}
}


Ladataan…
Peruuta
Tallenna