Browse Source

add "create sup project"

tags/Baseline_30082024_FRONTEND_UAT
cyril.tsui 1 year ago
parent
commit
1ab7d7223a
10 changed files with 197 additions and 92 deletions
  1. +13
    -1
      src/app/(main)/projects/create/sub/page.tsx
  2. +1
    -0
      src/app/api/projects/actions.ts
  3. +22
    -1
      src/app/api/projects/index.ts
  4. +2
    -0
      src/components/Breadcrumb/Breadcrumb.tsx
  5. +67
    -58
      src/components/ControlledAutoComplete/ControlledAutoComplete.tsx
  6. +10
    -8
      src/components/CreateProject/CreateProject.tsx
  7. +12
    -1
      src/components/CreateProject/CreateProjectWrapper.tsx
  8. +47
    -5
      src/components/CreateProject/ProjectClientDetails.tsx
  9. +1
    -0
      src/components/CreateProject/StaffAllocation.tsx
  10. +22
    -18
      src/components/CreateProject/TaskSetup.tsx

+ 13
- 1
src/app/(main)/projects/create/sub/page.tsx View File

@@ -1,6 +1,7 @@
import { fetchAllCustomers, fetchAllSubsidiaries } from "@/app/api/customer"; import { fetchAllCustomers, fetchAllSubsidiaries } from "@/app/api/customer";
import { fetchGrades } from "@/app/api/grades"; import { fetchGrades } from "@/app/api/grades";
import { import {
fetchMainProjects,
fetchProjectBuildingTypes, fetchProjectBuildingTypes,
fetchProjectCategories, fetchProjectCategories,
fetchProjectContractTypes, fetchProjectContractTypes,
@@ -12,10 +13,12 @@ import {
} from "@/app/api/projects"; } from "@/app/api/projects";
import { preloadStaff, preloadTeamLeads } from "@/app/api/staff"; import { preloadStaff, preloadTeamLeads } from "@/app/api/staff";
import { fetchAllTasks, fetchTaskTemplates } from "@/app/api/tasks"; import { fetchAllTasks, fetchTaskTemplates } from "@/app/api/tasks";
import { ServerFetchError } from "@/app/utils/fetchUtil";
import CreateProject from "@/components/CreateProject"; import CreateProject from "@/components/CreateProject";
import { I18nProvider, getServerI18n } from "@/i18n"; import { I18nProvider, getServerI18n } from "@/i18n";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { Metadata } from "next"; import { Metadata } from "next";
import { notFound } from "next/navigation";


export const metadata: Metadata = { export const metadata: Metadata = {
title: "Create Project", title: "Create Project",
@@ -39,12 +42,21 @@ const Projects: React.FC = async () => {
fetchGrades(); fetchGrades();
preloadTeamLeads(); preloadTeamLeads();
preloadStaff(); preloadStaff();
try {
const data = await fetchMainProjects();

if (!Boolean(data) || data.length === 0) {
notFound();
}
} catch (e) {
notFound();
}


return ( return (
<> <>
<Typography variant="h4">{t("Create Sub Project")}</Typography> <Typography variant="h4">{t("Create Sub Project")}</Typography>
<I18nProvider namespaces={["projects"]}> <I18nProvider namespaces={["projects"]}>
<CreateProject isEditMode={false}/>
<CreateProject isEditMode={false} isSubProject={true} />
</I18nProvider> </I18nProvider>
</> </>
); );


+ 1
- 0
src/app/api/projects/actions.ts View File

@@ -22,6 +22,7 @@ export interface CreateProjectInputs {
projectActualEnd: string; projectActualEnd: string;
projectStatus: string; projectStatus: string;
isClpProject: boolean; isClpProject: boolean;
mainProjectId?: number | null;


// Project info // Project info
serviceTypeId: number; serviceTypeId: number;


+ 22
- 1
src/app/api/projects/index.ts View File

@@ -15,6 +15,27 @@ export interface ProjectResult {
status: string; status: string;
} }


export interface MainProject {
projectId: number;
projectCode: string;
projectName: string;
projectCategoryId: number;
projectDescription: string;
projectLeadId: number;
projectStatus: string;
isClpProject: boolean;
serviceTypeId: number;
fundingTypeId: number;
contractTypeId: number;
locationId: number;
buildingTypeIds: number[];
workNatureIds: number[];
clientId: number;
clientContactId: number;
clientSubsidiaryId: number;
expectedProjectFee: number;
}

export interface ProjectCategory { export interface ProjectCategory {
id: number; id: number;
name: string; name: string;
@@ -82,7 +103,7 @@ export const fetchProjects = cache(async () => {
}); });


export const fetchMainProjects = cache(async () => { export const fetchMainProjects = cache(async () => {
return serverFetchJson<ProjectResult[]>(`${BASE_API_URL}/projects/main`, {
return serverFetchJson<MainProject[]>(`${BASE_API_URL}/projects/main`, {
next: { tags: ["projects"] }, next: { tags: ["projects"] },
}); });
}); });


+ 2
- 0
src/components/Breadcrumb/Breadcrumb.tsx View File

@@ -15,7 +15,9 @@ const pathToLabelMap: { [path: string]: string } = {
"/home": "User Workspace", "/home": "User Workspace",
"/projects": "Projects", "/projects": "Projects",
"/projects/create": "Create Project", "/projects/create": "Create Project",
"/projects/create/sub": "Sub Project",
"/projects/edit": "Edit Project", "/projects/edit": "Edit Project",
"/projects/edit/sub": "Sub Project",
"/tasks": "Task Template", "/tasks": "Task Template",
"/tasks/create": "Create Task Template", "/tasks/create": "Create Task Template",
"/staffReimbursement": "Staff Reimbursement", "/staffReimbursement": "Staff Reimbursement",


+ 67
- 58
src/components/ControlledAutoComplete/ControlledAutoComplete.tsx View File

@@ -10,7 +10,7 @@ const icon = <CheckBoxOutlineBlankIcon fontSize="medium" />;
const checkedIcon = <CheckBoxIcon fontSize="medium" />; const checkedIcon = <CheckBoxIcon fontSize="medium" />;
// label -> e.g. code - name -> 001 - WL // label -> e.g. code - name -> 001 - WL
// name -> WL // name -> WL
interface Props<T extends { id?: number | string; label?: string; name?: string }, TField extends FieldValues> {
interface Props<T extends { id?: number | string | null; label?: string; name?: string }, TField extends FieldValues> {
control: Control<TField>, control: Control<TField>,
options: T[], options: T[],
name: Path<TField>, // register name name: Path<TField>, // register name
@@ -18,7 +18,6 @@ interface Props<T extends { id?: number | string; label?: string; name?: string
noOptionsText?: string, noOptionsText?: string,
isMultiple?: boolean, isMultiple?: boolean,
rules?: RegisterOptions<FieldValues> rules?: RegisterOptions<FieldValues>
error?: boolean,
} }


function ControlledAutoComplete< function ControlledAutoComplete<
@@ -28,68 +27,78 @@ function ControlledAutoComplete<
props: Props<T, TField> props: Props<T, TField>
) { ) {
const { t } = useTranslation() const { t } = useTranslation()
const { control, options, name, label, noOptionsText, isMultiple, rules, error } = props;
const { control, options, name, label, noOptionsText, isMultiple, rules } = props;

// set default value if value is null
if (!Boolean(isMultiple) && !Boolean(control._formValues[name])) {
console.log(name, control._formValues[name])
control._formValues[name] = options[0]?.id ?? undefined
} else if (Boolean(isMultiple) && !Boolean(control._formValues[name])) {
control._formValues[name] = []
}


return ( return (
<Controller <Controller
name={name} name={name}
control={control} control={control}
rules={rules} rules={rules}
render={({ field }) => (
isMultiple ?
<Autocomplete
multiple
disableClearable
disableCloseOnSelect
disablePortal
noOptionsText={noOptionsText ?? t("No Options")}
value={options.filter(option => {
// console.log(field.value)
return field.value?.includes(option.id)
})}
options={options}
getOptionLabel={(option) => option.label ?? option.name!!}
isOptionEqualToValue={(option, value) => option.id === value.id}
renderOption={(params, option, { selected }) => {
return (
<li {...params}>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
checked={selected}
style={{ marginRight: 8 }}
/>
{option.label ?? option.name}
</li>
);
}}
onChange={(event, value) => {
field.onChange(value?.map(v => v.id))
}}
renderInput={(params) => <TextField {...params} error={error} variant="outlined" label={label} />}
/>
:
<Autocomplete
disableClearable
disablePortal
noOptionsText={noOptionsText ?? t("No Options")}
value={options.find(option => option.id === field.value) ?? options[0]}
options={options}
getOptionLabel={(option) => option.label ?? option.name!!}
isOptionEqualToValue={(option, value) => option.id === value.id}
renderOption={(params, option) => {
return (
<MenuItem {...params} key={option.id} value={option.id}>
{option.label ?? option.name}
</MenuItem>
);
}}
onChange={(event, value) => {
field.onChange(value?.id)
}}
renderInput={(params) => <TextField {...params} error={error} variant="outlined" label={label} />}
/>
)}

render={({ field, fieldState, formState }) => {

return (
isMultiple ?
<Autocomplete
multiple
disableClearable
disableCloseOnSelect
disablePortal
noOptionsText={noOptionsText ?? t("No Options")}
value={options.filter(option => {
return field.value?.includes(option.id)
})}
options={options}
getOptionLabel={(option) => option.label ?? option.name!!}
isOptionEqualToValue={(option, value) => option.id === value.id}
renderOption={(params, option, { selected }) => {
return (
<li {...params} key={option.id}>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
checked={selected}
style={{ marginRight: 8 }}
/>
{option.label ?? option.name}
</li>
);
}}
onChange={(event, value) => {
field.onChange(value?.map(v => v.id))
}}
renderInput={(params) => <TextField {...params} error={Boolean(formState.errors[name])} variant="outlined" label={label} />}
/>
:
<Autocomplete
disableClearable
disablePortal
noOptionsText={noOptionsText ?? t("No Options")}
value={options.find(option => option.id === field.value) ?? options[0]}
options={options}
getOptionLabel={(option) => option.label ?? option.name!!}
isOptionEqualToValue={(option, value) => option?.id === value?.id}
renderOption={(params, option) => {
return (
<MenuItem {...params} key={option.id} value={option.id}>
{option.label ?? option.name}
</MenuItem>
);
}}
onChange={(event, value) => {
field.onChange(value?.id)
}}
renderInput={(params) => <TextField {...params} error={Boolean(formState.errors[name])} variant="outlined" label={label} />}
/>)
}}
/> />
) )
} }


+ 10
- 8
src/components/CreateProject/CreateProject.tsx View File

@@ -33,6 +33,7 @@ import {
ContractType, ContractType,
FundingType, FundingType,
LocationType, LocationType,
MainProject,
ProjectCategory, ProjectCategory,
ServiceType, ServiceType,
WorkNature, WorkNature,
@@ -52,6 +53,8 @@ import dayjs from "dayjs";


export interface Props { export interface Props {
isEditMode: boolean; isEditMode: boolean;
isSubProject: boolean;
mainProjects?: MainProject[];
defaultInputs?: CreateProjectInputs; defaultInputs?: CreateProjectInputs;
allTasks: Task[]; allTasks: Task[];
projectCategories: ProjectCategory[]; projectCategories: ProjectCategory[];
@@ -93,6 +96,8 @@ const hasErrorsInTab = (


const CreateProject: React.FC<Props> = ({ const CreateProject: React.FC<Props> = ({
isEditMode, isEditMode,
isSubProject,
mainProjects,
defaultInputs, defaultInputs,
allTasks, allTasks,
projectCategories, projectCategories,
@@ -269,14 +274,9 @@ const CreateProject: React.FC<Props> = ({
milestones: {}, milestones: {},
totalManhour: 0, totalManhour: 0,
taskTemplateId: "All", taskTemplateId: "All",
projectCategoryId: projectCategories.length > 0 ? projectCategories[0].id : undefined,
projectLeadId: teamLeads.length > 0 ? teamLeads[0].id : undefined,
serviceTypeId: serviceTypes.length > 0 ? serviceTypes[0].id : undefined,
fundingTypeId: fundingTypes.length > 0 ? fundingTypes[0].id : undefined,
contractTypeId: contractTypes.length > 0 ? contractTypes[0].id : undefined,
locationId: locationTypes.length > 0 ? locationTypes[0].id : undefined,
clientSubsidiaryId: undefined,
clientId: allCustomers.length > 0 ? allCustomers[0].id : undefined,
projectName: mainProjects !== undefined ? mainProjects[0].projectName : undefined,
projectDescription: mainProjects !== undefined ? mainProjects[0].projectDescription : undefined,
expectedProjectFee: mainProjects !== undefined ? mainProjects[0].expectedProjectFee : undefined,
...defaultInputs, ...defaultInputs,


// manhourPercentageByGrade should have a sensible default // manhourPercentageByGrade should have a sensible default
@@ -380,6 +380,8 @@ const CreateProject: React.FC<Props> = ({
</Tabs> </Tabs>
{ {
<ProjectClientDetails <ProjectClientDetails
isSubProject={isSubProject}
mainProjects={mainProjects}
buildingTypes={buildingTypes} buildingTypes={buildingTypes}
workNatures={workNatures} workNatures={workNatures}
contractTypes={contractTypes} contractTypes={contractTypes}


+ 12
- 1
src/components/CreateProject/CreateProjectWrapper.tsx View File

@@ -1,6 +1,7 @@
import { fetchAllTasks, fetchTaskTemplates } from "@/app/api/tasks"; import { fetchAllTasks, fetchTaskTemplates } from "@/app/api/tasks";
import CreateProject from "./CreateProject"; import CreateProject from "./CreateProject";
import { import {
fetchMainProjects,
fetchProjectBuildingTypes, fetchProjectBuildingTypes,
fetchProjectCategories, fetchProjectCategories,
fetchProjectContractTypes, fetchProjectContractTypes,
@@ -14,10 +15,14 @@ import { fetchStaff, fetchTeamLeads } from "@/app/api/staff";
import { fetchAllCustomers, fetchAllSubsidiaries } from "@/app/api/customer"; import { fetchAllCustomers, fetchAllSubsidiaries } from "@/app/api/customer";
import { fetchGrades } from "@/app/api/grades"; import { fetchGrades } from "@/app/api/grades";


type CreateProjectProps = { isEditMode: false };
type CreateProjectProps = {
isEditMode: false;
isSubProject?: boolean;
};
interface EditProjectProps { interface EditProjectProps {
isEditMode: true; isEditMode: true;
projectId?: string; projectId?: string;
isSubProject?: boolean;
} }


type Props = CreateProjectProps | EditProjectProps; type Props = CreateProjectProps | EditProjectProps;
@@ -59,9 +64,14 @@ const CreateProjectWrapper: React.FC<Props> = async (props) => {
? await fetchProjectDetails(props.projectId!) ? await fetchProjectDetails(props.projectId!)
: undefined; : undefined;


const mainProjects = Boolean(props.isSubProject)
? await fetchMainProjects()
: undefined;

return ( return (
<CreateProject <CreateProject
isEditMode={props.isEditMode} isEditMode={props.isEditMode}
isSubProject={Boolean(props.isSubProject)}
defaultInputs={projectInfo} defaultInputs={projectInfo}
allTasks={tasks} allTasks={tasks}
projectCategories={projectCategories} projectCategories={projectCategories}
@@ -77,6 +87,7 @@ const CreateProjectWrapper: React.FC<Props> = async (props) => {
workNatures={workNatures} workNatures={workNatures}
allStaffs={allStaffs} allStaffs={allStaffs}
grades={grades} grades={grades}
mainProjects={mainProjects}
/> />
); );
}; };


+ 47
- 5
src/components/CreateProject/ProjectClientDetails.tsx View File

@@ -22,6 +22,7 @@ import {
ContractType, ContractType,
FundingType, FundingType,
LocationType, LocationType,
MainProject,
ProjectCategory, ProjectCategory,
ServiceType, ServiceType,
WorkNature, WorkNature,
@@ -37,6 +38,8 @@ import ControlledAutoComplete from "../ControlledAutoComplete/ControlledAutoComp


interface Props { interface Props {
isActive: boolean; isActive: boolean;
isSubProject: boolean;
mainProjects?: MainProject[];
projectCategories: ProjectCategory[]; projectCategories: ProjectCategory[];
teamLeads: StaffResult[]; teamLeads: StaffResult[];
allCustomers: Customer[]; allCustomers: Customer[];
@@ -51,6 +54,8 @@ interface Props {


const ProjectClientDetails: React.FC<Props> = ({ const ProjectClientDetails: React.FC<Props> = ({
isActive, isActive,
isSubProject,
mainProjects,
projectCategories, projectCategories,
teamLeads, teamLeads,
allCustomers, allCustomers,
@@ -70,6 +75,8 @@ const ProjectClientDetails: React.FC<Props> = ({
control, control,
setValue, setValue,
getValues, getValues,
reset,
resetField,
} = useFormContext<CreateProjectInputs>(); } = useFormContext<CreateProjectInputs>();


const subsidiaryMap = useMemo<{ const subsidiaryMap = useMemo<{
@@ -136,7 +143,6 @@ const ProjectClientDetails: React.FC<Props> = ({
// Automatically add the team lead to the allocated staff list // Automatically add the team lead to the allocated staff list
const selectedTeamLeadId = watch("projectLeadId"); const selectedTeamLeadId = watch("projectLeadId");
useEffect(() => { useEffect(() => {
console.log(selectedTeamLeadId)
if (selectedTeamLeadId !== undefined) { if (selectedTeamLeadId !== undefined) {
const currentStaffIds = getValues("allocatedStaffIds"); const currentStaffIds = getValues("allocatedStaffIds");
const newList = uniq([...currentStaffIds, selectedTeamLeadId]); const newList = uniq([...currentStaffIds, selectedTeamLeadId]);
@@ -144,6 +150,32 @@ const ProjectClientDetails: React.FC<Props> = ({
} }
}, [getValues, selectedTeamLeadId, setValue]); }, [getValues, selectedTeamLeadId, setValue]);


// Automatically update the project & client details whene select a main project
const mainProjectId = watch("mainProjectId")
useEffect(() => {
if (mainProjectId !== undefined && mainProjects !== undefined) {
const mainProject = mainProjects.find(project => project.projectId === mainProjectId);

if (mainProject !== undefined) {
setValue("projectName", mainProject.projectName)
setValue("projectCategoryId", mainProject.projectCategoryId)
setValue("projectLeadId", mainProject.projectLeadId)
setValue("serviceTypeId", mainProject.serviceTypeId)
setValue("fundingTypeId", mainProject.fundingTypeId)
setValue("contractTypeId", mainProject.contractTypeId)
setValue("locationId", mainProject.locationId)
setValue("buildingTypeIds", mainProject.buildingTypeIds)
setValue("workNatureIds", mainProject.workNatureIds)
setValue("projectDescription", mainProject.projectDescription)
setValue("expectedProjectFee", mainProject.expectedProjectFee)
setValue("isClpProject", mainProject.isClpProject)
setValue("clientId", mainProject.clientId)
setValue("clientSubsidiaryId", mainProject.clientSubsidiaryId)
setValue("clientContactId", mainProject.clientContactId)
}
}
}, [getValues, mainProjectId, setValue])

// const buildingTypeIdNameMap = buildingTypes.reduce<{ [id: number]: string }>( // const buildingTypeIdNameMap = buildingTypes.reduce<{ [id: number]: string }>(
// (acc, building) => ({ ...acc, [building.id]: building.name }), // (acc, building) => ({ ...acc, [building.id]: building.name }),
// {}, // {},
@@ -162,6 +194,18 @@ const ProjectClientDetails: React.FC<Props> = ({
{t("Project Details")} {t("Project Details")}
</Typography> </Typography>
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
{
isSubProject && mainProjects !== undefined && <><Grid item xs={6}>
<ControlledAutoComplete
control={control}
options={[...mainProjects.map(mainProject => ({ id: mainProject.projectId, label: `${mainProject.projectCode} - ${mainProject.projectName}` }))]}
name="mainProjectId"
label={t("Main Project")}
noOptionsText={t("No Main Project")}
/>
</Grid>
<Grid item sx={{ display: { xs: "none", sm: "block" } }} /></>
}
<Grid item xs={6}> <Grid item xs={6}>
<TextField <TextField
label={t("Project Code")} label={t("Project Code")}
@@ -283,7 +327,7 @@ const ProjectClientDetails: React.FC<Props> = ({
<Grid item xs={6}> <Grid item xs={6}>
<Checkbox <Checkbox
{...register("isClpProject")} {...register("isClpProject")}
defaultChecked={watch("isClpProject")}
checked={Boolean(watch("isClpProject"))}
/> />
<Typography variant="overline" display="inline"> <Typography variant="overline" display="inline">
{t("CLP Project")} {t("CLP Project")}
@@ -317,7 +361,6 @@ const ProjectClientDetails: React.FC<Props> = ({
rules={{ rules={{
required: "Please select a client" required: "Please select a client"
}} }}
error={Boolean(errors.clientId)}
/> />
</Grid> </Grid>
<Grid item sx={{ display: { xs: "none", sm: "block" } }} /> <Grid item sx={{ display: { xs: "none", sm: "block" } }} />
@@ -337,7 +380,7 @@ const ProjectClientDetails: React.FC<Props> = ({
<Grid item xs={6}> <Grid item xs={6}>
<ControlledAutoComplete <ControlledAutoComplete
control={control} control={control}
options={[{ id: undefined, label: t("No Subsidiary") }, ...customerSubsidiaryIds
options={[{ label: t("No Subsidiary") }, ...customerSubsidiaryIds
.filter((subId) => subsidiaryMap[subId]) .filter((subId) => subsidiaryMap[subId])
.map((subsidiaryId, index) => { .map((subsidiaryId, index) => {
const subsidiary = subsidiaryMap[subsidiaryId] const subsidiary = subsidiaryMap[subsidiaryId]
@@ -368,7 +411,6 @@ const ProjectClientDetails: React.FC<Props> = ({
} else return true; } else return true;
}, },
}} }}
error={Boolean(errors.clientContactId)}
/> />
</Grid> </Grid>
<Grid container sx={{ display: { xs: "none", sm: "block" } }} /> <Grid container sx={{ display: { xs: "none", sm: "block" } }} />


+ 1
- 0
src/components/CreateProject/StaffAllocation.tsx View File

@@ -25,6 +25,7 @@ import {
Tab, Tab,
Tabs, Tabs,
SelectChangeEvent, SelectChangeEvent,
Autocomplete,
} from "@mui/material"; } from "@mui/material";
import differenceWith from "lodash/differenceWith"; import differenceWith from "lodash/differenceWith";
import intersectionWith from "lodash/intersectionWith"; import intersectionWith from "lodash/intersectionWith";


+ 22
- 18
src/components/CreateProject/TaskSetup.tsx View File

@@ -7,7 +7,7 @@ import Typography from "@mui/material/Typography";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import TransferList from "../TransferList"; import TransferList from "../TransferList";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import React, { useCallback, useMemo, useState } from "react";
import React, { SyntheticEvent, useCallback, useMemo, useState } from "react";
import CardActions from "@mui/material/CardActions"; import CardActions from "@mui/material/CardActions";
import RestartAlt from "@mui/icons-material/RestartAlt"; import RestartAlt from "@mui/icons-material/RestartAlt";
import FormControl from "@mui/material/FormControl"; import FormControl from "@mui/material/FormControl";
@@ -20,6 +20,7 @@ import { CreateProjectInputs, ManhourAllocation } from "@/app/api/projects/actio
import isNumber from "lodash/isNumber"; import isNumber from "lodash/isNumber";
import intersectionWith from "lodash/intersectionWith"; import intersectionWith from "lodash/intersectionWith";
import { difference } from "lodash"; import { difference } from "lodash";
import { Autocomplete, TextField } from "@mui/material";


interface Props { interface Props {
allTasks: Task[]; allTasks: Task[];
@@ -50,9 +51,9 @@ const TaskSetup: React.FC<Props> = ({
"All" | number "All" | number
>(watch("taskTemplateId") ?? "All"); >(watch("taskTemplateId") ?? "All");
const onSelectTaskTemplate = useCallback( const onSelectTaskTemplate = useCallback(
(e: SelectChangeEvent<number | "All">) => {
if (e.target.value === "All" || isNumber(e.target.value)) {
setSelectedTaskTemplateId(e.target.value);
(event: SyntheticEvent<Element, Event>, value: NonNullable<{id: number | string, name: string}>) => {
if (value.id === "All" || isNumber(value.id)) {
setSelectedTaskTemplateId(value.id);
// onReset(); // onReset();
} }
}, },
@@ -132,21 +133,24 @@ const TaskSetup: React.FC<Props> = ({
marginBlockEnd={1} marginBlockEnd={1}
> >
<Grid item xs={6}> <Grid item xs={6}>
<FormControl fullWidth>
<InputLabel>{t("Task List Source")}</InputLabel>
<Select<"All" | number>
label={t("Task List Source")}
value={selectedTaskTemplateId}
onChange={onSelectTaskTemplate}
>
<MenuItem value={"All"}>{t("All tasks")}</MenuItem>
{taskTemplates.map((template, index) => (
<MenuItem key={`${template.id}-${index}`} value={template.id}>
{template.name}
<Autocomplete
disableClearable
disablePortal
noOptionsText={t("No Task List Source")}
value={taskTemplates.find(taskTemplate => taskTemplate.id === selectedTaskTemplateId)}
options={[{id: "All", name: t("All tasks")}, ...taskTemplates.map(taskTemplate => ({id: taskTemplate.id, name: taskTemplate.name}))]}
getOptionLabel={(taskTemplate) => taskTemplate.name}
isOptionEqualToValue={(option, value) => option?.id === value?.id}
renderOption={(params, option) => {
return (
<MenuItem {...params} key={option.id} value={option.id}>
{option.name}
</MenuItem> </MenuItem>
))}
</Select>
</FormControl>
);
}}
onChange={onSelectTaskTemplate}
renderInput={(params) => <TextField {...params} variant="outlined" label={t("Task List Source")} />}
/>
</Grid> </Grid>
</Grid> </Grid>
<TransferList <TransferList


Loading…
Cancel
Save