소스 검색

Move sections around in project creation

tags/Baseline_30082024_FRONTEND_UAT
Wayne 1 년 전
부모
커밋
61ec4efa3f
5개의 변경된 파일64개의 추가작업 그리고 48개의 파일을 삭제
  1. +5
    -5
      src/components/CreateProject/CreateProject.tsx
  2. +5
    -23
      src/components/CreateProject/Milestone.tsx
  3. +1
    -1
      src/components/CreateProject/MilestoneSection.tsx
  4. +14
    -7
      src/components/CreateProject/ResourceAllocation.tsx
  5. +39
    -12
      src/components/CreateProject/StaffAllocation.tsx

+ 5
- 5
src/components/CreateProject/CreateProject.tsx 파일 보기

@@ -12,7 +12,7 @@ import { useTranslation } from "react-i18next";
import ProjectClientDetails from "./ProjectClientDetails";
import TaskSetup from "./TaskSetup";
import StaffAllocation from "./StaffAllocation";
import ResourceMilestone from "./ResourceMilestone";
import Milestone from "./Milestone";
import { Task, TaskTemplate } from "@/app/api/tasks";
import {
FieldErrors,
@@ -122,8 +122,8 @@ const CreateProject: React.FC<Props> = ({
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" />
<Tab label={t("Staff Allocation and Resource")} iconPosition="end" />
<Tab label={t("Milestone")} iconPosition="end" />
</Tabs>
{
<ProjectClientDetails
@@ -139,8 +139,8 @@ const CreateProject: React.FC<Props> = ({
isActive={tabIndex === 1}
/>
}
{<StaffAllocation isActive={tabIndex === 2} />}
{<ResourceMilestone allTasks={allTasks} isActive={tabIndex === 3} />}
{<StaffAllocation isActive={tabIndex === 2} allTasks={allTasks} />}
{<Milestone allTasks={allTasks} isActive={tabIndex === 3} />}
{serverError && (
<Typography variant="body2" color="error" alignSelf="flex-end">
{serverError}


src/components/CreateProject/ResourceMilestone.tsx → src/components/CreateProject/Milestone.tsx 파일 보기

@@ -2,7 +2,6 @@

import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Typography from "@mui/material/Typography";
import { useTranslation } from "react-i18next";
import Button from "@mui/material/Button";
import React, { useCallback, useMemo, useState } from "react";
@@ -21,20 +20,14 @@ import uniqBy from "lodash/uniqBy";
import { useFormContext } from "react-hook-form";
import { CreateProjectInputs } from "@/app/api/projects/actions";
import MilestoneSection from "./MilestoneSection";
import ResourceSection from "./ResourceSection";
import ProjectTotalFee from "./ProjectTotalFee";

export interface Props {
allTasks: Task[];
defaultManhourBreakdownByGrade?: { [gradeId: number]: number };
isActive: boolean;
}

const ResourceMilestone: React.FC<Props> = ({
allTasks,
defaultManhourBreakdownByGrade,
isActive,
}) => {
const Milestone: React.FC<Props> = ({ allTasks, isActive }) => {
const { t } = useTranslation();
const { watch } = useFormContext<CreateProjectInputs>();

@@ -49,17 +42,13 @@ const ResourceMilestone: React.FC<Props> = ({
const [currentTaskGroupId, setCurrentTaskGroupId] = useState(
taskGroups[0].id,
);
const [currentTasks, setCurrentTasks] = useState<typeof tasks>(
tasks.filter((t) => t.taskGroup.id === currentTaskGroupId),
);
const onSelectTaskGroup = useCallback(
(event: SelectChangeEvent<TaskGroup["id"]>) => {
const id = event.target.value;
const newTaksGroupId = typeof id === "string" ? parseInt(id) : id;
setCurrentTaskGroupId(newTaksGroupId);
setCurrentTasks(tasks.filter((t) => t.taskGroup.id === newTaksGroupId));
},
[tasks],
[],
);

return (
@@ -81,13 +70,6 @@ const ResourceMilestone: React.FC<Props> = ({
</Select>
</FormControl>
{/* MUI X-Grid will throw an error if it is rendered without any dimensions; so not rendering when not active */}
{isActive && (
<ResourceSection
tasks={currentTasks}
manhourBreakdownByGrade={defaultManhourBreakdownByGrade}
/>
)}
{/* MUI X-Grid will throw an error if it is rendered without any dimensions; so not rendering when not active */}
{isActive && <MilestoneSection taskGroupId={currentTaskGroupId} />}
<CardActions sx={{ justifyContent: "flex-end" }}>
<Button variant="text" startIcon={<RestartAlt />}>
@@ -118,14 +100,14 @@ const NoTaskState: React.FC<Pick<Props, "isActive">> = ({ isActive }) => {
);
};

const ResourceMilestoneWrapper: React.FC<Props> = (props) => {
const MilestoneWrapper: React.FC<Props> = (props) => {
const { getValues } = useFormContext<CreateProjectInputs>();

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

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

export default ResourceMilestoneWrapper;
export default MilestoneWrapper;

+ 1
- 1
src/components/CreateProject/MilestoneSection.tsx 파일 보기

@@ -218,7 +218,7 @@ const MilestoneSection: React.FC<Props> = ({ taskGroupId }) => {
return (
<Stack gap={1}>
<Typography variant="overline" display="block" marginBlockEnd={1}>
{t("Task Stage Milestones")}
{t("Stage Milestones")}
</Typography>
<LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale="zh-hk">
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>


src/components/CreateProject/ResourceSection.tsx → src/components/CreateProject/ResourceAllocation.tsx 파일 보기

@@ -11,7 +11,7 @@ import {
} from "@mui/material";
import { useState, useCallback, useEffect, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { Props as ResourceMilestoneProps } from "./ResourceMilestone";
import { Props as StaffAllocationProps } from "./StaffAllocation";
import StyledDataGrid from "../StyledDataGrid";
import { useForm, useFormContext } from "react-hook-form";
import { GridColDef, GridRowModel, useGridApiRef } from "@mui/x-data-grid";
@@ -25,8 +25,8 @@ import _reduce from "lodash/reduce";
const mockGrades = [1, 2, 3, 4, 5];

interface Props {
tasks: Task[];
manhourBreakdownByGrade: ResourceMilestoneProps["defaultManhourBreakdownByGrade"];
allTasks: Task[];
manhourBreakdownByGrade: StaffAllocationProps["defaultManhourBreakdownByGrade"];
}

type Row = ManhourAllocation & { id: "manhourAllocation" };
@@ -36,8 +36,8 @@ const parseValidManhours = (value: number | string): number => {
return isNaN(inputNumber) || inputNumber < 0 ? 0 : inputNumber;
};

const ResourceSection: React.FC<Props> = ({
tasks,
const ResourceAllocation: React.FC<Props> = ({
allTasks,
manhourBreakdownByGrade = mockGrades.reduce<
NonNullable<Props["manhourBreakdownByGrade"]>
>((acc, grade) => {
@@ -45,6 +45,13 @@ const ResourceSection: React.FC<Props> = ({
}, {}),
}) => {
const { t } = useTranslation();
const { watch } = useFormContext<CreateProjectInputs>();
const currentTasks = watch("tasks");
const tasks = useMemo(
() => allTasks.filter((task) => currentTasks[task.id]),
[allTasks, currentTasks],
);

const [selectedTaskId, setSelectedTaskId] = useState(tasks[0].id);
const makeOnTaskSelect = useCallback(
(taskId: Task["id"]): React.MouseEventHandler =>
@@ -165,7 +172,7 @@ const ResourceSection: React.FC<Props> = ({
);

return (
<Box marginBlock={4}>
<Box>
<Typography variant="overline" display="block" marginBlockEnd={1}>
{t("Task Breakdown")}
</Typography>
@@ -223,4 +230,4 @@ const ResourceSection: React.FC<Props> = ({
);
};

export default ResourceSection;
export default ResourceAllocation;

+ 39
- 12
src/components/CreateProject/StaffAllocation.tsx 파일 보기

@@ -31,6 +31,8 @@ import uniq from "lodash/uniq";
import ResourceCapacity from "./ResourceCapacity";
import { useFormContext } from "react-hook-form";
import { CreateProjectInputs } from "@/app/api/projects/actions";
import ResourceAllocation from "./ResourceAllocation";
import { Task } from "@/app/api/tasks";

interface StaffResult {
id: number;
@@ -85,25 +87,39 @@ const mockStaffs: StaffResult[] = [
name: "Kurt",
grade: "1",
id: 11,
team: "ABC",
team: "XYZ",
title: "Construction Assistant",
},
{ name: "Lawrence", grade: "2", id: 12, team: "ABC", title: "Operator" },
];

interface Props {
const staffComparator = (a: StaffResult, b: StaffResult) => {
return (
a.team.localeCompare(b.team) ||
a.grade.localeCompare(b.grade) ||
a.id - b.id
);
};

export interface Props {
allStaff?: StaffResult[];
isActive: boolean;
defaultManhourBreakdownByGrade?: { [gradeId: number]: number };
allTasks: Task[];
}

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

const [filteredStaff, setFilteredStaff] = React.useState(allStaff);
const [filteredStaff, setFilteredStaff] = React.useState(
allStaff.sort(staffComparator),
);
const [selectedStaff, setSelectedStaff] = React.useState<
typeof filteredStaff
>(
@@ -138,10 +154,10 @@ const StaffAllocation: React.FC<Props> = ({
onClick: addStaff,
buttonIcon: <PersonAdd />,
},
{ label: t("Staff ID"), name: "id" },
{ label: t("Staff Name"), name: "name" },
{ label: t("Team"), name: "team" },
{ label: t("Grade"), name: "grade" },
{ label: t("Staff ID"), name: "id" },
{ label: t("Staff Name"), name: "name" },
{ label: t("Title"), name: "title" },
],
[addStaff, t],
@@ -155,10 +171,10 @@ const StaffAllocation: React.FC<Props> = ({
onClick: removeStaff,
buttonIcon: <PersonRemove />,
},
{ label: t("Staff ID"), name: "id" },
{ label: t("Staff Name"), name: "name" },
{ label: t("Team"), name: "team" },
{ label: t("Grade"), name: "grade" },
{ label: t("Staff ID"), name: "id" },
{ label: t("Staff Name"), name: "name" },
{ label: t("Title"), name: "title" },
],
[removeStaff, t],
@@ -239,6 +255,11 @@ const StaffAllocation: React.FC<Props> = ({

return (
<>
<Card sx={{ display: isActive ? "block" : "none" }}>
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
<ResourceCapacity />
</CardContent>
</Card>
<Card sx={{ display: isActive ? "block" : "none" }}>
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
<Stack gap={2}>
@@ -322,11 +343,17 @@ const StaffAllocation: React.FC<Props> = ({
</CardActions>
</CardContent>
</Card>
<Card sx={{ display: isActive ? "block" : "none" }}>
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
<ResourceCapacity />
</CardContent>
</Card>
{/* MUI X-Grid will throw an error if it is rendered without any dimensions; so not rendering when not active */}
{isActive && (
<Card>
<CardContent>
<ResourceAllocation
allTasks={allTasks}
manhourBreakdownByGrade={defaultManhourBreakdownByGrade}
/>
</CardContent>
</Card>
)}
</>
);
};


불러오는 중...
취소
저장