|
|
@@ -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: |
|
|
|
</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: |
|
|
|
</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: |
|
|
@@ -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: |
|
|
@@ -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: |
|
|
|
</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> |
|
|
|