Kaynağa Gözat

1. Project - Add auto calculation for total manhour

tags/Baseline_30082024_FRONTEND_UAT
cyril.tsui 1 yıl önce
ebeveyn
işleme
3ba98b40b7
3 değiştirilmiş dosya ile 73 ekleme ve 22 silme
  1. +1
    -0
      src/app/api/projects/actions.ts
  2. +35
    -3
      src/components/CreateProject/CreateProject.tsx
  3. +37
    -19
      src/components/CreateProject/ResourceAllocation.tsx

+ 1
- 0
src/app/api/projects/actions.ts Dosyayı Görüntüle

@@ -52,6 +52,7 @@ export interface CreateProjectInputs {
};
};
allocatedStaffIds: number[];
ratePerManhour: number;

// Milestones
milestones: {


+ 35
- 3
src/components/CreateProject/CreateProject.tsx Dosyayı Görüntüle

@@ -91,7 +91,8 @@ const hasErrorsInTab = (
return (
errors.totalManhour ||
errors.manhourPercentageByGrade ||
errors.taskGroups
errors.taskGroups ||
errors.ratePerManhour
);
case 3:
return errors.milestones;
@@ -159,7 +160,7 @@ const CreateProject: React.FC<Props> = ({
let hasErrors = false;

// Tab - Staff Allocation and Resource
if (data.totalManhour === null || data.totalManhour <= 0) {
if (data.totalManhour === null || data.totalManhour <= 0 || Number.isNaN(data.totalManhour)) {
formProps.setError("totalManhour", {
message: "totalManhour value is not valid",
type: "required",
@@ -168,6 +169,15 @@ const CreateProject: React.FC<Props> = ({
hasErrors = true;
}

if (data.ratePerManhour === null || data.ratePerManhour <= 0 || Number.isNaN(data.ratePerManhour)) {
formProps.setError("ratePerManhour", {
message: "ratePerManhour value is not valid",
type: "required",
});
setTabIndex(2);
hasErrors = true;
}

const manhourPercentageByGradeKeys = Object.keys(
data.manhourPercentageByGrade,
);
@@ -334,7 +344,8 @@ const CreateProject: React.FC<Props> = ({
} else if (
errors.totalManhour ||
errors.manhourPercentageByGrade ||
errors.taskGroups
errors.taskGroups ||
errors.ratePerManhour
) {
setTabIndex(2);
} else if (errors.milestones) {
@@ -364,6 +375,7 @@ const CreateProject: React.FC<Props> = ({
subContractFee:
mainProjects !== undefined ? mainProjects[0].subContractFee : undefined,
clientId: allCustomers !== undefined ? allCustomers[0].id : undefined,
ratePerManhour: 250,
...defaultInputs,

// manhourPercentageByGrade should have a sensible default
@@ -377,6 +389,26 @@ const CreateProject: React.FC<Props> = ({

const errors = formProps.formState.errors;

// auto calculate the total project manhour
const expectedProjectFee = formProps.watch("expectedProjectFee")
const ratePerManhour = formProps.watch("ratePerManhour")
const totalManhour = formProps.watch("totalManhour")
const [firstLoaded, setFirstLoaded] = useState(false)
React.useMemo(() => {
if ((firstLoaded && expectedProjectFee > 0 && ratePerManhour > 0)) {
console.log(ratePerManhour, formProps.watch("totalManhour"))
formProps.setValue("totalManhour", Math.ceil(expectedProjectFee / ratePerManhour))
} else {
setFirstLoaded(true)
}
}, [expectedProjectFee, ratePerManhour])

React.useMemo(() => {
if ((expectedProjectFee > 0 && ratePerManhour > 0) && (totalManhour === null || Number.isNaN(totalManhour) || totalManhour <= 0)) {
formProps.setValue("totalManhour", Math.ceil(expectedProjectFee / ratePerManhour))
}
}, [totalManhour])

return (
<>
<FormProvider {...formProps}>


+ 37
- 19
src/components/CreateProject/ResourceAllocation.tsx Dosyayı Görüntüle

@@ -12,6 +12,7 @@ import {
TableRow,
Stack,
SxProps,
Grid,
} from "@mui/material";
import React, { useCallback, useMemo } from "react";
import { useTranslation } from "react-i18next";
@@ -78,12 +79,12 @@ const ResourceAllocationByGrade: React.FC<Props> = ({ grades }) => {

const keys = Object.keys(updatedManhourPercentageByGrade)
if (keys.filter(k => updatedManhourPercentageByGrade[k as any] < 0).length > 0 ||
keys.reduce((acc, value) => acc + updatedManhourPercentageByGrade[value as any], 0) !== 100) {
setError("manhourPercentageByGrade", {message: "manhourPercentageByGrade value is not valid", type: "invalid"})
keys.reduce((acc, value) => acc + updatedManhourPercentageByGrade[value as any], 0) !== 100) {
setError("manhourPercentageByGrade", { message: "manhourPercentageByGrade value is not valid", type: "invalid" })
} else {
clearErrors("manhourPercentageByGrade")
}
}
},
[manhourPercentageByGrade, setValue],
@@ -94,17 +95,34 @@ const ResourceAllocationByGrade: React.FC<Props> = ({ grades }) => {
<Typography variant="overline" display="block" marginBlockEnd={1}>
{t("Manhour Allocation By Grade")}
</Typography>
<TextField
label={t("Total Project Manhour")}
fullWidth
type="number"
{...register("totalManhour", {
valueAsNumber: true,
required: "totalManhour code required!",
min: 1,
})}
error={Boolean(errors.totalManhour)}
/>
<Grid container columnSpacing={2}>
<Grid item xs={8}>
<TextField
label={t("Total Project Manhour")}
fullWidth
type="number"
{...register("totalManhour", {
valueAsNumber: true,
required: "totalManhour code required!",
min: 1,
})}
error={Boolean(errors.totalManhour)}
/>
</Grid>
<Grid item xs={4}>
<TextField
label={t("Rate Per Manhour")}
fullWidth
type="number"
{...register("ratePerManhour", {
valueAsNumber: true,
required: "ratePerManhour code required!",
min: 1,
})}
error={Boolean(errors.ratePerManhour)}
/>
</Grid>
</Grid>
<Box
sx={(theme) => ({
marginBlockStart: 2,
@@ -143,7 +161,7 @@ const ResourceAllocationByGrade: React.FC<Props> = ({ grades }) => {
error={manhourPercentageByGrade[column.id] < 0}
/>
))}
<TableCell sx={{ ...(totalPercentage === 100 && leftBorderCellSx), ...(totalPercentage !== 100 && {...errorCellSx, borderRight: "1px solid", borderColor: "error.main"})}}>
<TableCell sx={{ ...(totalPercentage === 100 && leftBorderCellSx), ...(totalPercentage !== 100 && { ...errorCellSx, borderRight: "1px solid", borderColor: "error.main" }) }}>
{totalPercentage + "%"}
{/* {percentFormatter.format(totalPercentage)} */}
</TableCell>
@@ -205,8 +223,8 @@ const ResourceAllocationByStage: React.FC<Props> = ({ grades, allTasks }) => {

const keys = Object.keys(updatedTaskGroups)
if (keys.filter(k => updatedTaskGroups[k as any].percentAllocation < 0).length > 0 ||
keys.reduce((acc, value) => acc + updatedTaskGroups[value as any].percentAllocation, 0) !== 100) {
setError("taskGroups", {message: "Task Groups value is not invalid", type: "invalid"})
keys.reduce((acc, value) => acc + updatedTaskGroups[value as any].percentAllocation, 0) !== 100) {
setError("taskGroups", { message: "Task Groups value is not invalid", type: "invalid" })
} else {
clearErrors("taskGroups")
}
@@ -312,9 +330,9 @@ const ResourceAllocationByStage: React.FC<Props> = ({ grades, allTasks }) => {
const hours = Object.values(currentTaskGroups).reduce(
(acc, tg) =>
acc +
tg.percentAllocation / 100 *
tg.percentAllocation / 100 *
totalManhour *
manhourPercentageByGrade[column.id] / 100 ,
manhourPercentageByGrade[column.id] / 100,
0,
);
return (


Yükleniyor…
İptal
Kaydet