| @@ -78,6 +78,14 @@ const hasErrorsInTab = ( | |||||
| return ( | return ( | ||||
| errors.projectName || errors.projectCode || errors.projectDescription | errors.projectName || errors.projectCode || errors.projectDescription | ||||
| ); | ); | ||||
| case 2: | |||||
| return ( | |||||
| errors.totalManhour || errors.manhourPercentageByGrade || errors.taskGroups | |||||
| ); | |||||
| case 3: | |||||
| return ( | |||||
| errors.milestones | |||||
| ) | |||||
| default: | default: | ||||
| false; | false; | ||||
| } | } | ||||
| @@ -132,7 +140,31 @@ const CreateProject: React.FC<Props> = ({ | |||||
| const onSubmit = useCallback<SubmitHandler<CreateProjectInputs>>( | const onSubmit = useCallback<SubmitHandler<CreateProjectInputs>>( | ||||
| async (data, event) => { | async (data, event) => { | ||||
| try { | try { | ||||
| console.log("first"); | |||||
| console.log(data); | |||||
| // detect errors | |||||
| let hasErrors = false | |||||
| if (data.totalManhour === null || data.totalManhour <= 0) { | |||||
| formProps.setError("totalManhour", { message: "totalManhour value is not valid", type: "required" }) | |||||
| hasErrors = true | |||||
| } | |||||
| const manhourPercentageByGradeKeys = Object.keys(data.manhourPercentageByGrade) | |||||
| if (manhourPercentageByGradeKeys.filter(k => data.manhourPercentageByGrade[k as any] < 0).length > 0 || | |||||
| manhourPercentageByGradeKeys.reduce((acc, value) => acc + data.manhourPercentageByGrade[value as any], 0) !== 1) { | |||||
| formProps.setError("manhourPercentageByGrade", { message: "manhourPercentageByGrade value is not valid", type: "invalid" }) | |||||
| hasErrors = true | |||||
| } | |||||
| const taskGroupKeys = Object.keys(data.taskGroups) | |||||
| if (taskGroupKeys.filter(k => data.taskGroups[k as any].percentAllocation < 0).length > 0 || | |||||
| taskGroupKeys.reduce((acc, value) => acc + data.taskGroups[value as any].percentAllocation, 0) !== 1) { | |||||
| formProps.setError("taskGroups", {message: "Task Groups value is not invalid", type: "invalid"}) | |||||
| hasErrors = true | |||||
| } | |||||
| if (hasErrors) return false | |||||
| // save project | |||||
| setServerError(""); | setServerError(""); | ||||
| let title = t("Do you want to submit?"); | let title = t("Do you want to submit?"); | ||||
| @@ -185,6 +217,7 @@ const CreateProject: React.FC<Props> = ({ | |||||
| const onSubmitError = useCallback<SubmitErrorHandler<CreateProjectInputs>>( | const onSubmitError = useCallback<SubmitErrorHandler<CreateProjectInputs>>( | ||||
| (errors) => { | (errors) => { | ||||
| console.log(errors) | |||||
| // Set the tab so that the focus will go there | // Set the tab so that the focus will go there | ||||
| if ( | if ( | ||||
| errors.projectName || | errors.projectName || | ||||
| @@ -192,6 +225,10 @@ const CreateProject: React.FC<Props> = ({ | |||||
| errors.projectCode | errors.projectCode | ||||
| ) { | ) { | ||||
| setTabIndex(0); | setTabIndex(0); | ||||
| } else if (errors.totalManhour || errors.manhourPercentageByGrade || errors.taskGroups) { | |||||
| setTabIndex(2) | |||||
| } else if (errors.milestones) { | |||||
| setTabIndex(3) | |||||
| } | } | ||||
| }, | }, | ||||
| [], | [], | ||||
| @@ -208,8 +245,8 @@ const CreateProject: React.FC<Props> = ({ | |||||
| // manhourPercentageByGrade should have a sensible default | // manhourPercentageByGrade should have a sensible default | ||||
| manhourPercentageByGrade: isEmpty(defaultInputs?.manhourPercentageByGrade) | manhourPercentageByGrade: isEmpty(defaultInputs?.manhourPercentageByGrade) | ||||
| ? grades.reduce((acc, grade) => { | ? grades.reduce((acc, grade) => { | ||||
| return { ...acc, [grade.id]: 1 / grades.length }; | |||||
| }, {}) | |||||
| return { ...acc, [grade.id]: 1 / grades.length }; | |||||
| }, {}) | |||||
| : defaultInputs?.manhourPercentageByGrade, | : defaultInputs?.manhourPercentageByGrade, | ||||
| }, | }, | ||||
| }); | }); | ||||
| @@ -253,15 +290,15 @@ const CreateProject: React.FC<Props> = ({ | |||||
| formProps.getValues("projectActualStart") && | formProps.getValues("projectActualStart") && | ||||
| formProps.getValues("projectActualEnd") | formProps.getValues("projectActualEnd") | ||||
| ) && ( | ) && ( | ||||
| <Button | |||||
| variant="outlined" | |||||
| startIcon={<Delete />} | |||||
| color="error" | |||||
| onClick={handleDelete} | |||||
| > | |||||
| {t("Delete Project")} | |||||
| </Button> | |||||
| )} | |||||
| <Button | |||||
| variant="outlined" | |||||
| startIcon={<Delete />} | |||||
| color="error" | |||||
| onClick={handleDelete} | |||||
| > | |||||
| {t("Delete Project")} | |||||
| </Button> | |||||
| )} | |||||
| </Stack> | </Stack> | ||||
| )} | )} | ||||
| <Tabs | <Tabs | ||||
| @@ -271,6 +308,7 @@ const CreateProject: React.FC<Props> = ({ | |||||
| > | > | ||||
| <Tab | <Tab | ||||
| label={t("Project and Client Details")} | label={t("Project and Client Details")} | ||||
| sx={{ marginInlineEnd: !hasErrorsInTab(1, errors) && (hasErrorsInTab(2, errors) || hasErrorsInTab(3, errors)) ? 1 : undefined }} | |||||
| icon={ | icon={ | ||||
| hasErrorsInTab(0, errors) ? ( | hasErrorsInTab(0, errors) ? ( | ||||
| <Error sx={{ marginInlineEnd: 1 }} color="error" /> | <Error sx={{ marginInlineEnd: 1 }} color="error" /> | ||||
| @@ -278,12 +316,26 @@ const CreateProject: React.FC<Props> = ({ | |||||
| } | } | ||||
| iconPosition="end" | iconPosition="end" | ||||
| /> | /> | ||||
| <Tab label={t("Project Task Setup")} iconPosition="end" /> | |||||
| <Tab | |||||
| label={t("Project Task Setup")} | |||||
| sx={{ marginInlineEnd: hasErrorsInTab(2, errors) || hasErrorsInTab(3, errors) ? 1 : undefined }} | |||||
| iconPosition="end" /> | |||||
| <Tab | <Tab | ||||
| label={t("Staff Allocation and Resource")} | label={t("Staff Allocation and Resource")} | ||||
| sx={{ marginInlineEnd: !hasErrorsInTab(2, errors) && hasErrorsInTab(3, errors) ? 1 : undefined }} | |||||
| icon={ | |||||
| hasErrorsInTab(2, errors) ? ( | |||||
| <Error sx={{ marginInlineEnd: 1 }} color="error" /> | |||||
| ) : undefined | |||||
| } | |||||
| iconPosition="end" | iconPosition="end" | ||||
| /> | /> | ||||
| <Tab label={t("Milestone")} iconPosition="end" /> | |||||
| <Tab label={t("Milestone")} | |||||
| icon={ | |||||
| hasErrorsInTab(3, errors) ? ( | |||||
| <Error sx={{ marginInlineEnd: 1 }} color="error" />) | |||||
| : undefined} | |||||
| iconPosition="end" /> | |||||
| </Tabs> | </Tabs> | ||||
| { | { | ||||
| <ProjectClientDetails | <ProjectClientDetails | ||||
| @@ -2,7 +2,7 @@ import { CreateProjectInputs } from "@/app/api/projects/actions"; | |||||
| import { TaskGroup } from "@/app/api/tasks"; | import { TaskGroup } from "@/app/api/tasks"; | ||||
| import { moneyFormatter } from "@/app/utils/formatUtil"; | import { moneyFormatter } from "@/app/utils/formatUtil"; | ||||
| import { Divider, Stack, Typography } from "@mui/material"; | import { Divider, Stack, Typography } from "@mui/material"; | ||||
| import React from "react"; | |||||
| import React, { useCallback, useEffect, useMemo } from "react"; | |||||
| import { useFormContext } from "react-hook-form"; | import { useFormContext } from "react-hook-form"; | ||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| @@ -12,17 +12,27 @@ interface Props { | |||||
| const ProjectTotalFee: React.FC<Props> = ({ taskGroups }) => { | const ProjectTotalFee: React.FC<Props> = ({ taskGroups }) => { | ||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const { watch } = useFormContext<CreateProjectInputs>(); | |||||
| const { watch, setError, clearErrors } = useFormContext<CreateProjectInputs>(); | |||||
| const milestones = watch("milestones"); | const milestones = watch("milestones"); | ||||
| const expectedTotalFee = watch("expectedProjectFee"); | const expectedTotalFee = watch("expectedProjectFee"); | ||||
| let projectTotal = 0; | let projectTotal = 0; | ||||
| useEffect(() => { | |||||
| console.log(Object.keys(milestones).reduce((acc, key) => acc + milestones[parseInt(key)].payments.reduce((acc2, value) => acc2 + value.amount, 0), 0)) | |||||
| if (Object.keys(milestones).reduce((acc, key) => acc + milestones[parseInt(key)].payments.reduce((acc2, value) => acc2 + value.amount, 0), 0) !== expectedTotalFee) { | |||||
| setError("milestones", {message: "project total is not valid", type: "invalid"}) | |||||
| } else { | |||||
| clearErrors("milestones") | |||||
| } | |||||
| }, [milestones]) | |||||
| return ( | return ( | ||||
| <Stack spacing={1}> | <Stack spacing={1}> | ||||
| {taskGroups.map((group, index) => { | {taskGroups.map((group, index) => { | ||||
| const payments = milestones[group.id]?.payments || []; | const payments = milestones[group.id]?.payments || []; | ||||
| const paymentTotal = payments.reduce((acc, p) => acc + p.amount, 0); | const paymentTotal = payments.reduce((acc, p) => acc + p.amount, 0); | ||||
| projectTotal += paymentTotal; | projectTotal += paymentTotal; | ||||
| return ( | return ( | ||||
| @@ -41,9 +51,9 @@ const ProjectTotalFee: React.FC<Props> = ({ taskGroups }) => { | |||||
| <Typography variant="h6">{t("Project Total Fee")}</Typography> | <Typography variant="h6">{t("Project Total Fee")}</Typography> | ||||
| <Typography>{moneyFormatter.format(projectTotal)}</Typography> | <Typography>{moneyFormatter.format(projectTotal)}</Typography> | ||||
| </Stack> | </Stack> | ||||
| {projectTotal > expectedTotalFee && ( | |||||
| {projectTotal !== expectedTotalFee && ( | |||||
| <Typography variant="caption" color="warning.main" alignSelf="flex-end"> | <Typography variant="caption" color="warning.main" alignSelf="flex-end"> | ||||
| {t("Project total fee is larger than the expected total fee!")} | |||||
| {t("Project total fee should be same as the expected total fee!")} | |||||
| </Typography> | </Typography> | ||||
| )} | )} | ||||
| </Stack> | </Stack> | ||||
| @@ -45,9 +45,20 @@ const leftRightBorderCellSx: SxProps = { | |||||
| borderColor: "divider", | borderColor: "divider", | ||||
| }; | }; | ||||
| const errorCellSx: SxProps = { | |||||
| outline: "1px solid", | |||||
| outlineColor: "error.main", | |||||
| // borderLeft: "1px solid", | |||||
| // borderRight: "1px solid", | |||||
| // borderTop: "1px solid", | |||||
| // borderBottom: "1px solid", | |||||
| // borderColor: 'error.main' | |||||
| } | |||||
| const ResourceAllocationByGrade: React.FC<Props> = ({ grades }) => { | const ResourceAllocationByGrade: React.FC<Props> = ({ grades }) => { | ||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const { watch, register, setValue } = useFormContext<CreateProjectInputs>(); | |||||
| const { watch, register, setValue, formState: { errors }, setError, clearErrors } = useFormContext<CreateProjectInputs>(); | |||||
| const manhourPercentageByGrade = watch("manhourPercentageByGrade"); | const manhourPercentageByGrade = watch("manhourPercentageByGrade"); | ||||
| const totalManhour = watch("totalManhour"); | const totalManhour = watch("totalManhour"); | ||||
| @@ -59,10 +70,20 @@ const ResourceAllocationByGrade: React.FC<Props> = ({ grades }) => { | |||||
| const makeUpdatePercentage = useCallback( | const makeUpdatePercentage = useCallback( | ||||
| (gradeId: Grade["id"]) => (percentage?: number) => { | (gradeId: Grade["id"]) => (percentage?: number) => { | ||||
| if (percentage !== undefined) { | if (percentage !== undefined) { | ||||
| setValue("manhourPercentageByGrade", { | |||||
| const updatedManhourPercentageByGrade = { | |||||
| ...manhourPercentageByGrade, | ...manhourPercentageByGrade, | ||||
| [gradeId]: percentage, | [gradeId]: percentage, | ||||
| }); | |||||
| } | |||||
| setValue("manhourPercentageByGrade", updatedManhourPercentageByGrade); | |||||
| 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) !== 1) { | |||||
| setError("manhourPercentageByGrade", {message: "manhourPercentageByGrade value is not valid", type: "invalid"}) | |||||
| } else { | |||||
| clearErrors("manhourPercentageByGrade") | |||||
| } | |||||
| } | } | ||||
| }, | }, | ||||
| [manhourPercentageByGrade, setValue], | [manhourPercentageByGrade, setValue], | ||||
| @@ -79,7 +100,10 @@ const ResourceAllocationByGrade: React.FC<Props> = ({ grades }) => { | |||||
| type="number" | type="number" | ||||
| {...register("totalManhour", { | {...register("totalManhour", { | ||||
| valueAsNumber: true, | valueAsNumber: true, | ||||
| required: "totalManhour code required!", | |||||
| min: 1, | |||||
| })} | })} | ||||
| error={Boolean(errors.totalManhour)} | |||||
| /> | /> | ||||
| <Box | <Box | ||||
| sx={(theme) => ({ | sx={(theme) => ({ | ||||
| @@ -115,9 +139,10 @@ const ResourceAllocationByGrade: React.FC<Props> = ({ grades }) => { | |||||
| convertValue={(inputValue) => Number(inputValue)} | convertValue={(inputValue) => Number(inputValue)} | ||||
| cellSx={{ backgroundColor: "primary.lightest" }} | cellSx={{ backgroundColor: "primary.lightest" }} | ||||
| inputSx={{ width: "3rem" }} | inputSx={{ width: "3rem" }} | ||||
| error={manhourPercentageByGrade[column.id] < 0} | |||||
| /> | /> | ||||
| ))} | ))} | ||||
| <TableCell sx={leftBorderCellSx}> | |||||
| <TableCell sx={{ ...(totalPercentage === 1 && leftBorderCellSx), ...(totalPercentage !== 1 && {...errorCellSx, borderRight: "1px solid", borderColor: "error.main"})}}> | |||||
| {percentFormatter.format(totalPercentage)} | {percentFormatter.format(totalPercentage)} | ||||
| </TableCell> | </TableCell> | ||||
| </TableRow> | </TableRow> | ||||
| @@ -144,7 +169,7 @@ const ResourceAllocationByGrade: React.FC<Props> = ({ grades }) => { | |||||
| const ResourceAllocationByStage: React.FC<Props> = ({ grades, allTasks }) => { | const ResourceAllocationByStage: React.FC<Props> = ({ grades, allTasks }) => { | ||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const { watch, setValue } = useFormContext<CreateProjectInputs>(); | |||||
| const { watch, setValue, clearErrors, setError } = useFormContext<CreateProjectInputs>(); | |||||
| const currentTaskGroups = watch("taskGroups"); | const currentTaskGroups = watch("taskGroups"); | ||||
| const taskGroups = useMemo( | const taskGroups = useMemo( | ||||
| @@ -167,13 +192,22 @@ const ResourceAllocationByStage: React.FC<Props> = ({ grades, allTasks }) => { | |||||
| const makeUpdatePercentage = useCallback( | const makeUpdatePercentage = useCallback( | ||||
| (taskGroupId: TaskGroup["id"]) => (percentage?: number) => { | (taskGroupId: TaskGroup["id"]) => (percentage?: number) => { | ||||
| if (percentage !== undefined) { | if (percentage !== undefined) { | ||||
| setValue("taskGroups", { | |||||
| const updatedTaskGroups = { | |||||
| ...currentTaskGroups, | ...currentTaskGroups, | ||||
| [taskGroupId]: { | [taskGroupId]: { | ||||
| ...currentTaskGroups[taskGroupId], | ...currentTaskGroups[taskGroupId], | ||||
| percentAllocation: percentage, | percentAllocation: percentage, | ||||
| }, | }, | ||||
| }); | |||||
| } | |||||
| setValue("taskGroups", updatedTaskGroups); | |||||
| 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) !== 1) { | |||||
| setError("taskGroups", {message: "Task Groups value is not invalid", type: "invalid"}) | |||||
| } else { | |||||
| clearErrors("taskGroups") | |||||
| } | |||||
| } | } | ||||
| }, | }, | ||||
| [currentTaskGroups, setValue], | [currentTaskGroups, setValue], | ||||
| @@ -219,8 +253,11 @@ const ResourceAllocationByStage: React.FC<Props> = ({ grades, allTasks }) => { | |||||
| renderValue={(val) => percentFormatter.format(val)} | renderValue={(val) => percentFormatter.format(val)} | ||||
| onChange={makeUpdatePercentage(tg.id)} | onChange={makeUpdatePercentage(tg.id)} | ||||
| convertValue={(inputValue) => Number(inputValue)} | convertValue={(inputValue) => Number(inputValue)} | ||||
| cellSx={{ backgroundColor: "primary.lightest" }} | |||||
| cellSx={{ | |||||
| backgroundColor: "primary.lightest", | |||||
| }} | |||||
| inputSx={{ width: "3rem" }} | inputSx={{ width: "3rem" }} | ||||
| error={currentTaskGroups[tg.id].percentAllocation < 0} | |||||
| /> | /> | ||||
| <TableCell sx={rightBorderCellSx}> | <TableCell sx={rightBorderCellSx}> | ||||
| {manhourFormatter.format( | {manhourFormatter.format( | ||||
| @@ -248,7 +285,11 @@ const ResourceAllocationByStage: React.FC<Props> = ({ grades, allTasks }) => { | |||||
| 0, | 0, | ||||
| )} | )} | ||||
| </TableCell> | </TableCell> | ||||
| <TableCell sx={leftBorderCellSx}> | |||||
| <TableCell sx={{ | |||||
| ...(Object.values(currentTaskGroups).reduce((acc, tg) => acc + tg.percentAllocation, 0,) === 1 && leftBorderCellSx), | |||||
| ...(Object.values(currentTaskGroups).reduce((acc, tg) => acc + tg.percentAllocation, 0,) !== 1 && errorCellSx) | |||||
| }} | |||||
| > | |||||
| {percentFormatter.format( | {percentFormatter.format( | ||||
| Object.values(currentTaskGroups).reduce( | Object.values(currentTaskGroups).reduce( | ||||
| (acc, tg) => acc + tg.percentAllocation, | (acc, tg) => acc + tg.percentAllocation, | ||||
| @@ -269,8 +310,8 @@ const ResourceAllocationByStage: React.FC<Props> = ({ grades, allTasks }) => { | |||||
| (acc, tg) => | (acc, tg) => | ||||
| acc + | acc + | ||||
| tg.percentAllocation * | tg.percentAllocation * | ||||
| totalManhour * | |||||
| manhourPercentageByGrade[column.id], | |||||
| totalManhour * | |||||
| manhourPercentageByGrade[column.id], | |||||
| 0, | 0, | ||||
| ); | ); | ||||
| return ( | return ( | ||||
| @@ -127,7 +127,7 @@ const NavigationContent: React.FC<Props> = ({ abilities }) => { | |||||
| {icon: <Analytics />, label:"Project Claims Report", path: "/analytics/ProjectClaimsReport"}, | {icon: <Analytics />, label:"Project Claims Report", path: "/analytics/ProjectClaimsReport"}, | ||||
| {icon: <Analytics />, label:"Project P&L Report", path: "/analytics/ProjectPLReport"}, | {icon: <Analytics />, label:"Project P&L Report", path: "/analytics/ProjectPLReport"}, | ||||
| {icon: <Analytics />, label:"Financial Status Report", path: "/analytics/FinancialStatusReport"}, | {icon: <Analytics />, label:"Financial Status Report", path: "/analytics/FinancialStatusReport"}, | ||||
| {icon: <Analytics />, label:"EX02 - Project Cash Flow Report", path: "/analytics/EX02ProjectCashFlowReport"}, | |||||
| {icon: <Analytics />, label:"Project Cash Flow Report", path: "/analytics/ProjectCashFlowReport"}, | |||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -6,6 +6,7 @@ import React, { | |||||
| useState, | useState, | ||||
| } from "react"; | } from "react"; | ||||
| import { Box, Input, SxProps, TableCell } from "@mui/material"; | import { Box, Input, SxProps, TableCell } from "@mui/material"; | ||||
| import palette from "@/theme/devias-material-kit/palette"; | |||||
| interface Props<T> { | interface Props<T> { | ||||
| value: T; | value: T; | ||||
| @@ -14,6 +15,7 @@ interface Props<T> { | |||||
| convertValue: (inputValue: string) => T; | convertValue: (inputValue: string) => T; | ||||
| cellSx?: SxProps; | cellSx?: SxProps; | ||||
| inputSx?: SxProps; | inputSx?: SxProps; | ||||
| error?: Boolean; | |||||
| } | } | ||||
| const TableCellEdit = <T,>({ | const TableCellEdit = <T,>({ | ||||
| @@ -23,8 +25,10 @@ const TableCellEdit = <T,>({ | |||||
| onChange, | onChange, | ||||
| cellSx, | cellSx, | ||||
| inputSx, | inputSx, | ||||
| error, | |||||
| }: Props<T>) => { | }: Props<T>) => { | ||||
| const [editMode, setEditMode] = useState(false); | const [editMode, setEditMode] = useState(false); | ||||
| // const [afterEdit, setAfterEdit] = useState(false); | |||||
| const [input, setInput] = useState<string>(""); | const [input, setInput] = useState<string>(""); | ||||
| const inputRef = useRef<HTMLInputElement>(null); | const inputRef = useRef<HTMLInputElement>(null); | ||||
| @@ -40,6 +44,7 @@ const TableCellEdit = <T,>({ | |||||
| const onBlur = useCallback(() => { | const onBlur = useCallback(() => { | ||||
| setEditMode(false); | setEditMode(false); | ||||
| // setAfterEdit(true) | |||||
| onChange(convertValue(input)); | onChange(convertValue(input)); | ||||
| setInput(""); | setInput(""); | ||||
| }, [convertValue, input, onChange]); | }, [convertValue, input, onChange]); | ||||
| @@ -53,8 +58,8 @@ const TableCellEdit = <T,>({ | |||||
| return ( | return ( | ||||
| <TableCell | <TableCell | ||||
| sx={{ | sx={{ | ||||
| outline: editMode ? "1px solid" : undefined, | |||||
| outlineColor: editMode ? "primary.main" : undefined, | |||||
| outline: editMode && !error ? "1px solid" : error ? "1px solid" : undefined, | |||||
| outlineColor: editMode && !error ? "primary.main" : error ? "error.main" : undefined, | |||||
| ...cellSx, | ...cellSx, | ||||
| }} | }} | ||||
| > | > | ||||
| @@ -76,7 +81,7 @@ const TableCellEdit = <T,>({ | |||||
| onBlur={onBlur} | onBlur={onBlur} | ||||
| type={typeof value === "number" ? "number" : "text"} | type={typeof value === "number" ? "number" : "text"} | ||||
| /> | /> | ||||
| <Box sx={{ display: editMode ? "none" : "block" }} onClick={onClick}> | |||||
| <Box sx={{ display: editMode ? "none" : "block" }} onClick={onClick}> | |||||
| {renderValue(value)} | {renderValue(value)} | ||||
| </Box> | </Box> | ||||
| </TableCell> | </TableCell> | ||||