Ver código fonte

Small fixes

tags/Baseline_30082024_FRONTEND_UAT
Wayne 1 ano atrás
pai
commit
52ee059f1b
9 arquivos alterados com 143 adições e 82 exclusões
  1. +49
    -10
      src/components/CreateProject/CreateProject.tsx
  2. +12
    -4
      src/components/CreateProject/ProjectClientDetails.tsx
  3. +40
    -50
      src/components/CreateProject/ResourceCapacity.tsx
  4. +7
    -7
      src/components/CreateProject/ResourceMilestone.tsx
  5. +11
    -3
      src/components/CreateProject/StaffAllocation.tsx
  6. +3
    -2
      src/components/CreateProject/TaskSetup.tsx
  7. +18
    -4
      src/components/ProjectSearch/ProjectSearch.tsx
  8. +2
    -2
      src/components/TaskTemplateSearch/TaskTemplateSearch.tsx
  9. +1
    -0
      src/theme/devias-material-kit/components.ts

+ 49
- 10
src/components/CreateProject/CreateProject.tsx Ver arquivo

@@ -14,13 +14,32 @@ import TaskSetup from "./TaskSetup";
import StaffAllocation from "./StaffAllocation";
import ResourceMilestone from "./ResourceMilestone";
import { Task } from "@/app/api/tasks";
import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
import {
FieldErrors,
FormProvider,
SubmitErrorHandler,
SubmitHandler,
useForm,
} from "react-hook-form";
import { CreateProjectInputs } from "@/app/api/projects/actions";
import { Error } from "@mui/icons-material";

export interface Props {
allTasks: Task[];
}

const hasErrorsInTab = (
tabIndex: number,
errors: FieldErrors<CreateProjectInputs>,
) => {
switch (tabIndex) {
case 0:
return errors.projectName;
default:
false;
}
};

const CreateProject: React.FC<Props> = ({ allTasks }) => {
const [tabIndex, setTabIndex] = useState(0);
const { t } = useTranslation();
@@ -41,6 +60,16 @@ const CreateProject: React.FC<Props> = ({ allTasks }) => {
console.log(data);
}, []);

const onSubmitError = useCallback<SubmitErrorHandler<CreateProjectInputs>>(
(errors) => {
// Set the tab so that the focus will go there
if (errors.projectName) {
setTabIndex(0);
}
},
[],
);

const formProps = useForm<CreateProjectInputs>({
defaultValues: {
tasks: {},
@@ -49,23 +78,33 @@ const CreateProject: React.FC<Props> = ({ allTasks }) => {
},
});

const errors = formProps.formState.errors;

return (
<FormProvider {...formProps}>
<Stack
spacing={2}
component="form"
onSubmit={formProps.handleSubmit(onSubmit)}
onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)}
>
<Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable">
<Tab label={t("Project and Client Details")} />
<Tab label={t("Project Task Setup")} />
<Tab label={t("Staff Allocation")} />
<Tab label={t("Resource and Milestone")} />
<Tab
label={t("Project and Client Details")}
icon={
hasErrorsInTab(0, errors) ? (
<Error sx={{ marginInlineEnd: 1 }} color="error" />
) : undefined
}
iconPosition="end"
/>
<Tab label={t("Project Task Setup")} iconPosition="end" />
<Tab label={t("Staff Allocation")} iconPosition="end" />
<Tab label={t("Resource and Milestone")} iconPosition="end" />
</Tabs>
{tabIndex === 0 && <ProjectClientDetails />}
{tabIndex === 1 && <TaskSetup allTasks={allTasks} />}
{tabIndex === 2 && <StaffAllocation />}
{tabIndex === 3 && <ResourceMilestone allTasks={allTasks} />}
{<ProjectClientDetails isActive={tabIndex === 0} />}
{<TaskSetup allTasks={allTasks} isActive={tabIndex === 1} />}
{<StaffAllocation isActive={tabIndex === 2} />}
{<ResourceMilestone allTasks={allTasks} isActive={tabIndex === 3} />}
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
variant="outlined"


+ 12
- 4
src/components/CreateProject/ProjectClientDetails.tsx Ver arquivo

@@ -18,12 +18,17 @@ import Button from "@mui/material/Button";
import { useFormContext } from "react-hook-form";
import { CreateProjectInputs } from "@/app/api/projects/actions";

const ProjectClientDetails: React.FC = () => {
const ProjectClientDetails: React.FC<{ isActive: boolean }> = ({
isActive,
}) => {
const { t } = useTranslation();
const { register } = useFormContext<CreateProjectInputs>();
const {
register,
formState: { errors },
} = useFormContext<CreateProjectInputs>();

return (
<Card>
<Card sx={{ display: isActive ? "block" : "none" }}>
<CardContent component={Stack} spacing={4}>
<Box>
<Typography variant="overline" display="block" marginBlockEnd={1}>
@@ -48,7 +53,10 @@ const ProjectClientDetails: React.FC = () => {
<TextField
label={t("Project Name")}
fullWidth
{...register("projectName")}
{...register("projectName", {
required: "Project name required!",
})}
error={Boolean(errors.projectName)}
/>
</Grid>
<Grid item xs={6}>


+ 40
- 50
src/components/CreateProject/ResourceCapacity.tsx Ver arquivo

@@ -1,8 +1,6 @@
import { manhourFormatter } from "@/app/utils/formatUtil";
import {
Box,
Card,
CardContent,
Stack,
Table,
TableBody,
@@ -94,55 +92,47 @@ const ResourceCapacity: React.FC<Props> = ({ items = mockItems }) => {
);

return (
<Card>
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
<Stack gap={2}>
<Typography variant="overline" display="block">
{t("Resource Capacity")}
</Typography>
<Box sx={{ marginInline: -3 }}>
<TableContainer>
<Table>
<TableHead>
<TableRow>
{columns.map((column, idx) => (
<TableCell key={`${column.name.toString()}${idx}`}>
{column.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{items.map((item, index) => {
return (
<TableRow
hover
tabIndex={-1}
key={`${item.grade}-${index}`}
>
{columns.map((column, idx) => {
const columnName = column.name;
const cellData = item[columnName];
<Stack gap={2}>
<Typography variant="overline" display="block">
{t("Resource Capacity")}
</Typography>
<Box sx={{ marginInline: -3 }}>
<TableContainer>
<Table>
<TableHead>
<TableRow>
{columns.map((column, idx) => (
<TableCell key={`${column.name.toString()}${idx}`}>
{column.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{items.map((item, index) => {
return (
<TableRow hover tabIndex={-1} key={`${item.grade}-${index}`}>
{columns.map((column, idx) => {
const columnName = column.name;
const cellData = item[columnName];

return (
<TableCell key={`${columnName.toString()}-${idx}`}>
{columnName !== "headcount" &&
typeof cellData === "number"
? manhourFormatter.format(cellData)
: cellData}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
</Box>
</Stack>
</CardContent>
</Card>
return (
<TableCell key={`${columnName.toString()}-${idx}`}>
{columnName !== "headcount" &&
typeof cellData === "number"
? manhourFormatter.format(cellData)
: cellData}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
</Box>
</Stack>
);
};



+ 7
- 7
src/components/CreateProject/ResourceMilestone.tsx Ver arquivo

@@ -15,11 +15,9 @@ import {
MenuItem,
Select,
SelectChangeEvent,
Stack,
} from "@mui/material";
import { Task, TaskGroup } from "@/app/api/tasks";
import uniqBy from "lodash/uniqBy";
import { moneyFormatter } from "@/app/utils/formatUtil";
import { useFormContext } from "react-hook-form";
import { CreateProjectInputs } from "@/app/api/projects/actions";
import MilestoneSection from "./MilestoneSection";
@@ -29,11 +27,13 @@ import ProjectTotalFee from "./ProjectTotalFee";
export interface Props {
allTasks: Task[];
defaultManhourBreakdownByGrade?: { [gradeId: number]: number };
isActive: boolean;
}

const ResourceMilestone: React.FC<Props> = ({
allTasks,
defaultManhourBreakdownByGrade,
isActive,
}) => {
const { t } = useTranslation();
const { getValues } = useFormContext<CreateProjectInputs>();
@@ -65,7 +65,7 @@ const ResourceMilestone: React.FC<Props> = ({

return (
<>
<Card>
<Card sx={{ display: isActive ? "block" : "none" }}>
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
<FormControl>
<InputLabel>{t("Task Stage")}</InputLabel>
@@ -93,7 +93,7 @@ const ResourceMilestone: React.FC<Props> = ({
</CardActions>
</CardContent>
</Card>
<Card>
<Card sx={{ display: isActive ? "block" : "none" }}>
<CardContent>
<ProjectTotalFee taskGroups={taskGroups} />
</CardContent>
@@ -102,10 +102,10 @@ const ResourceMilestone: React.FC<Props> = ({
);
};

const NoTaskState: React.FC = () => {
const NoTaskState: React.FC<Pick<Props, "isActive">> = ({ isActive }) => {
const { t } = useTranslation();
return (
<Card>
<Card sx={{ display: isActive ? "block" : "none" }}>
<CardContent>
<Alert severity="warning">
{t('Please add some tasks in "Project Task Setup" first!')}
@@ -119,7 +119,7 @@ const ResourceMilestoneWrapper: React.FC<Props> = (props) => {
const { getValues } = useFormContext<CreateProjectInputs>();

if (Object.keys(getValues("tasks")).length === 0) {
return <NoTaskState />;
return <NoTaskState isActive={props.isActive} />;
}

return <ResourceMilestone {...props} />;


+ 11
- 3
src/components/CreateProject/StaffAllocation.tsx Ver arquivo

@@ -93,9 +93,13 @@ const mockStaffs: StaffResult[] = [

interface Props {
allStaff?: StaffResult[];
isActive: boolean;
}

const StaffAllocation: React.FC<Props> = ({ allStaff = mockStaffs }) => {
const StaffAllocation: React.FC<Props> = ({
allStaff = mockStaffs,
isActive,
}) => {
const { t } = useTranslation();
const { setValue, getValues } = useFormContext<CreateProjectInputs>();

@@ -235,7 +239,7 @@ const StaffAllocation: React.FC<Props> = ({ allStaff = mockStaffs }) => {

return (
<>
<Card>
<Card sx={{ display: isActive ? "block" : "none" }}>
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
<Stack gap={2}>
<Typography variant="overline" display="block">
@@ -318,7 +322,11 @@ const StaffAllocation: React.FC<Props> = ({ allStaff = mockStaffs }) => {
</CardActions>
</CardContent>
</Card>
<ResourceCapacity />
<Card sx={{ display: isActive ? "block" : "none" }}>
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
<ResourceCapacity />
</CardContent>
</Card>
</>
);
};


+ 3
- 2
src/components/CreateProject/TaskSetup.tsx Ver arquivo

@@ -20,9 +20,10 @@ import { CreateProjectInputs } from "@/app/api/projects/actions";

interface Props {
allTasks: Task[];
isActive: boolean;
}

const TaskSetup: React.FC<Props> = ({ allTasks: tasks }) => {
const TaskSetup: React.FC<Props> = ({ allTasks: tasks, isActive }) => {
const { t } = useTranslation();
const { getValues, setValue } = useFormContext<CreateProjectInputs>();
const currentTasks = getValues("tasks");
@@ -38,7 +39,7 @@ const TaskSetup: React.FC<Props> = ({ allTasks: tasks }) => {
}, [currentTasks, tasks]);

return (
<Card>
<Card sx={{ display: isActive ? "block" : "none" }}>
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
<Typography variant="overline" display="block" marginBlockEnd={1}>
{t("Task List Setup")}


+ 18
- 4
src/components/ProjectSearch/ProjectSearch.tsx Ver arquivo

@@ -28,24 +28,28 @@ const ProjectSearch: React.FC<Props> = ({ projects }) => {
label: t("Client name"),
paramName: "client",
type: "select",
options: ["A", "B"],
options: ["Client A", "Client B", "Client C"],
},
{
label: t("Project category"),
paramName: "category",
type: "select",
options: ["A", "B"],
options: ["Confirmed Project", "Project to be bidded"],
},
{
label: t("Team"),
paramName: "team",
type: "select",
options: ["A", "B"],
options: ["TW", "WY"],
},
],
[t],
);

const onReset = useCallback(() => {
setFilteredProjects(projects);
}, [projects]);

const onProjectClick = useCallback((project: ProjectResult) => {
console.log(project);
}, []);
@@ -72,8 +76,18 @@ const ProjectSearch: React.FC<Props> = ({ projects }) => {
<SearchBox
criteria={searchCriteria}
onSearch={(query) => {
console.log(query);
setFilteredProjects(
projects.filter(
(p) =>
p.code.toLowerCase().includes(query.code.toLowerCase()) &&
p.name.toLowerCase().includes(query.name.toLowerCase()) &&
(query.client === "All" || p.client === query.client) &&
(query.category === "All" || p.category === query.category) &&
(query.team === "All" || p.team === query.team),
),
);
}}
onReset={onReset}
/>
<SearchResults<ProjectResult>
items={filteredProjects}


+ 2
- 2
src/components/TaskTemplateSearch/TaskTemplateSearch.tsx Ver arquivo

@@ -55,8 +55,8 @@ const TaskTemplateSearch: React.FC<Props> = ({ taskTemplates }) => {
setFilteredTemplates(
taskTemplates.filter(
(task) =>
task.code.toLowerCase().includes(query.code) &&
task.name.toLowerCase().includes(query.name),
task.code.toLowerCase().includes(query.code.toLowerCase()) &&
task.name.toLowerCase().includes(query.name.toLowerCase()),
),
);
}}


+ 1
- 0
src/theme/devias-material-kit/components.ts Ver arquivo

@@ -277,6 +277,7 @@ const components: ThemeOptions["components"] = {
fontWeight: 500,
lineHeight: 1.71,
minWidth: "auto",
minHeight: 48,
paddingLeft: 0,
paddingRight: 0,
textTransform: "none",


Carregando…
Cancelar
Salvar