Переглянути джерело

update project & searchbox; create a new controlled component - autocomplete

tags/Baseline_30082024_FRONTEND_UAT
cyril.tsui 1 рік тому
джерело
коміт
7716e99f6e
9 змінених файлів з 368 додано та 328 видалено
  1. +17
    -0
      src/app/(main)/projects/create/sub/not-found.tsx
  2. +53
    -0
      src/app/(main)/projects/create/sub/page.tsx
  3. +29
    -9
      src/app/(main)/projects/page.tsx
  4. +6
    -0
      src/app/api/projects/index.ts
  5. +97
    -0
      src/components/ControlledAutoComplete/ControlledAutoComplete.tsx
  6. +1
    -0
      src/components/ControlledAutoComplete/index.ts
  7. +8
    -0
      src/components/CreateProject/CreateProject.tsx
  8. +133
    -301
      src/components/CreateProject/ProjectClientDetails.tsx
  9. +24
    -18
      src/components/SearchBox/SearchBox.tsx

+ 17
- 0
src/app/(main)/projects/create/sub/not-found.tsx Переглянути файл

@@ -0,0 +1,17 @@
import { getServerI18n } from "@/i18n";
import { Stack, Typography, Link } from "@mui/material";
import NextLink from "next/link";

export default async function NotFound() {
const { t } = await getServerI18n("projects", "common");

return (
<Stack spacing={2}>
<Typography variant="h4">{t("Not Found")}</Typography>
<Typography variant="body1">{t("The sub project was not found or there was no any main projects!")}</Typography>
<Link href="/projects" component={NextLink} variant="body2">
{t("Return to all projects")}
</Link>
</Stack>
);
}

+ 53
- 0
src/app/(main)/projects/create/sub/page.tsx Переглянути файл

@@ -0,0 +1,53 @@
import { fetchAllCustomers, fetchAllSubsidiaries } from "@/app/api/customer";
import { fetchGrades } from "@/app/api/grades";
import {
fetchProjectBuildingTypes,
fetchProjectCategories,
fetchProjectContractTypes,
fetchProjectFundingTypes,
fetchProjectLocationTypes,
fetchProjectServiceTypes,
fetchProjectWorkNatures,
fetchProjects,
} from "@/app/api/projects";
import { preloadStaff, preloadTeamLeads } from "@/app/api/staff";
import { fetchAllTasks, fetchTaskTemplates } from "@/app/api/tasks";
import CreateProject from "@/components/CreateProject";
import { I18nProvider, getServerI18n } from "@/i18n";
import Typography from "@mui/material/Typography";
import { Metadata } from "next";

export const metadata: Metadata = {
title: "Create Project",
};

const Projects: React.FC = async () => {
const { t } = await getServerI18n("projects");

// Preload necessary dependencies
fetchAllTasks();
fetchTaskTemplates();
fetchProjectCategories();
fetchProjectContractTypes();
fetchProjectFundingTypes();
fetchProjectLocationTypes();
fetchProjectServiceTypes();
fetchProjectBuildingTypes();
fetchProjectWorkNatures();
fetchAllCustomers();
fetchAllSubsidiaries();
fetchGrades();
preloadTeamLeads();
preloadStaff();

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

export default Projects;

+ 29
- 9
src/app/(main)/projects/page.tsx Переглянути файл

@@ -1,7 +1,8 @@
import { preloadProjects } from "@/app/api/projects";
import { fetchProjectCategories, fetchProjects, preloadProjects } from "@/app/api/projects";
import ProjectSearch from "@/components/ProjectSearch";
import { getServerI18n } from "@/i18n";
import Add from "@mui/icons-material/Add";
import { ButtonGroup } from "@mui/material";
import Button from "@mui/material/Button";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
@@ -15,7 +16,9 @@ export const metadata: Metadata = {

const Projects: React.FC = async () => {
const { t } = await getServerI18n("projects");
preloadProjects();
// preloadProjects();
fetchProjectCategories();
const projects = await fetchProjects();

return (
<>
@@ -28,14 +31,31 @@ const Projects: React.FC = async () => {
<Typography variant="h4" marginInlineEnd={2}>
{t("Projects")}
</Typography>
<Button
variant="contained"
startIcon={<Add />}
LinkComponent={Link}
href="/projects/create"
<Stack
direction="row"
justifyContent="space-between"
flexWrap="wrap"
rowGap={2}
spacing={1}
>
{t("Create Project")}
</Button>
{projects.filter(project => project.status.toLowerCase() !== "deleted").length > 0 && <Button
variant="contained"
color="secondary"
startIcon={<Add />}
LinkComponent={Link}
href="/projects/create/sub"
>
{t("Create Sub Project")}
</Button>}
<Button
variant="contained"
startIcon={<Add />}
LinkComponent={Link}
href="/projects/create"
>
{t("Create Project")}
</Button>
</Stack >
</Stack>
<Suspense fallback={<ProjectSearch.Loading />}>
<ProjectSearch />


+ 6
- 0
src/app/api/projects/index.ts Переглянути файл

@@ -81,6 +81,12 @@ export const fetchProjects = cache(async () => {
});
});

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

export const fetchProjectCategories = cache(async () => {
return serverFetchJson<ProjectCategory[]>(
`${BASE_API_URL}/projects/categories`,


+ 97
- 0
src/components/ControlledAutoComplete/ControlledAutoComplete.tsx Переглянути файл

@@ -0,0 +1,97 @@
"use client"

import { Autocomplete, MenuItem, TextField, Checkbox } from "@mui/material";
import { Controller, FieldValues, Path, Control, RegisterOptions } from "react-hook-form";
import { useTranslation } from "react-i18next";
import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank';
import CheckBoxIcon from '@mui/icons-material/CheckBox';

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

function ControlledAutoComplete<
T extends { id?: number | string; label?: string; name?: string },
TField extends FieldValues
>(
props: Props<T, TField>
) {
const { t } = useTranslation()
const { control, options, name, label, noOptionsText, isMultiple, rules, error } = props;

return (
<Controller
name={name}
control={control}
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} />}
/>
)}
/>
)
}

export default ControlledAutoComplete;

+ 1
- 0
src/components/ControlledAutoComplete/index.ts Переглянути файл

@@ -0,0 +1 @@
export { default } from "./ControlledAutoComplete";

+ 8
- 0
src/components/CreateProject/CreateProject.tsx Переглянути файл

@@ -269,6 +269,14 @@ const CreateProject: React.FC<Props> = ({
milestones: {},
totalManhour: 0,
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,
...defaultInputs,

// manhourPercentageByGrade should have a sensible default


+ 133
- 301
src/components/CreateProject/ProjectClientDetails.tsx Переглянути файл

@@ -31,8 +31,9 @@ import { Contact, Customer, Subsidiary } from "@/app/api/customer";
import Link from "next/link";
import React, { useEffect, useMemo, useState } from "react";
import { fetchCustomer } from "@/app/api/customer/actions";
import { Checkbox, ListItemText } from "@mui/material";
import { Autocomplete, Checkbox, ListItemText } from "@mui/material";
import uniq from "lodash/uniq";
import ControlledAutoComplete from "../ControlledAutoComplete/ControlledAutoComplete";

interface Props {
isActive: boolean;
@@ -64,7 +65,7 @@ const ProjectClientDetails: React.FC<Props> = ({
const { t } = useTranslation();
const {
register,
formState: { errors },
formState: { errors, defaultValues },
watch,
control,
setValue,
@@ -108,8 +109,9 @@ const ProjectClientDetails: React.FC<Props> = ({
setCustomerContacts(contacts);
setCustomerSubsidiaryIds(subsidiaryIds);

if (subsidiaryIds.length > 0) setValue("clientSubsidiaryId", subsidiaryIds[0])
else setValue("clientSubsidiaryId", undefined)
// if (subsidiaryIds.length > 0) setValue("clientSubsidiaryId", subsidiaryIds[0])
// else
setValue("clientSubsidiaryId", undefined)
// if (contacts.length > 0) setValue("clientContactId", contacts[0].id)
// else setValue("clientContactId", undefined)
});
@@ -122,18 +124,19 @@ const ProjectClientDetails: React.FC<Props> = ({
// get subsidiary contact combo
const contacts = allSubsidiaries.find(subsidiary => subsidiary.id === clientSubsidiaryId)?.subsidiaryContacts!!
setSubsidiaryContacts(contacts)
setValue("clientContactId", contacts[0].id)
setValue("clientContactId", selectedCustomerId === defaultValues?.clientId && Boolean(defaultValues?.clientSubsidiaryId) ? contacts.find(contact => contact.id === defaultValues.clientContactId)?.id ?? contacts[0].id : contacts[0].id)
setValue("isSubsidiaryContact", true)
} else if (customerContacts?.length > 0) {
setSubsidiaryContacts([])
setValue("clientContactId", customerContacts[0].id)
setValue("clientContactId", selectedCustomerId === defaultValues?.clientId && !Boolean(defaultValues?.clientSubsidiaryId) ? customerContacts.find(contact => contact.id === defaultValues.clientContactId)?.id ?? customerContacts[0].id : customerContacts[0].id)
setValue("isSubsidiaryContact", false)
}
}, [customerContacts, clientSubsidiaryId]);
}, [customerContacts, clientSubsidiaryId, selectedCustomerId]);

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

const buildingTypeIdNameMap = buildingTypes.reduce<{ [id: number]: string }>(
(acc, building) => ({ ...acc, [building.id]: building.name }),
{},
);
// const buildingTypeIdNameMap = buildingTypes.reduce<{ [id: number]: string }>(
// (acc, building) => ({ ...acc, [building.id]: building.name }),
// {},
// );

const workNatureIdNameMap = workNatures.reduce<{ [id: number]: string }>(
(acc, wn) => ({ ...acc, [wn.id]: wn.name }),
{},
);
// const workNatureIdNameMap = workNatures.reduce<{ [id: number]: string }>(
// (acc, wn) => ({ ...acc, [wn.id]: wn.name }),
// {},
// );

return (
<Card sx={{ display: isActive ? "block" : "none" }}>
@@ -164,12 +167,12 @@ const ProjectClientDetails: React.FC<Props> = ({
label={t("Project Code")}
fullWidth
disabled
{...register("projectCode",
// {
// required: "Project code required!",
// }
{...register("projectCode",
// {
// required: "Project code required!",
// }
)}
// error={Boolean(errors.projectCode)}
// error={Boolean(errors.projectCode)}
/>
</Grid>
<Grid item xs={6}>
@@ -183,184 +186,79 @@ const ProjectClientDetails: React.FC<Props> = ({
/>
</Grid>
<Grid item xs={6}>
<FormControl fullWidth>
<InputLabel>{t("Project Category")}</InputLabel>
<Controller
defaultValue={projectCategories[0].id}
control={control}
name="projectCategoryId"
render={({ field }) => (
<Select label={t("Project Category")} {...field}>
{projectCategories.map((category, index) => (
<MenuItem
key={`${category.id}-${index}`}
value={category.id}
>
{t(category.name)}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
<ControlledAutoComplete
control={control}
options={projectCategories}
name="projectCategoryId"
label={t("Project Category")}
noOptionsText={t("No Project Category")}
/>
</Grid>
<Grid item xs={6}>
<FormControl fullWidth>
<InputLabel>{t("Team Lead")}</InputLabel>
<Controller
defaultValue={teamLeads[0].id}
control={control}
name="projectLeadId"
render={({ field }) => (
<Select label={t("Team Lead")} {...field}>
{teamLeads.map((staff, index) => (
<MenuItem key={`${staff.id}-${index}`} value={staff.id}>
{`${staff.staffId} - ${staff.name} (${staff.team})`}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
<ControlledAutoComplete
control={control}
options={teamLeads.map((staff) => ({ ...staff, label: `${staff.staffId} - ${staff.name} (${staff.team})` }))}
name="projectLeadId"
label={t("Team Lead")}
noOptionsText={t("No Team Lead")}
/>
</Grid>
<Grid item xs={6}>
<FormControl fullWidth>
<InputLabel>{t("Service Type")}</InputLabel>
<Controller
defaultValue={serviceTypes[0].id}
control={control}
name="serviceTypeId"
render={({ field }) => (
<Select label={t("Service Type")} {...field}>
{serviceTypes.map((type, index) => (
<MenuItem key={`${type.id}-${index}`} value={type.id}>
{type.name}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
<ControlledAutoComplete
control={control}
options={serviceTypes}
name="serviceTypeId"
label={t("Service Type")}
noOptionsText={t("No Service Type")}
/>
</Grid>
<Grid item xs={6}>
<FormControl fullWidth>
<InputLabel>{t("Funding Type")}</InputLabel>
<Controller
defaultValue={fundingTypes[0].id}
control={control}
name="fundingTypeId"
render={({ field }) => (
<Select label={t("Funding Type")} {...field}>
{fundingTypes.map((type, index) => (
<MenuItem key={`${type.id}-${index}`} value={type.id}>
{type.name}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
<ControlledAutoComplete
control={control}
options={fundingTypes}
name="fundingTypeId"
label={t("Funding Type")}
noOptionsText={t("No Funding Type")}
/>
</Grid>
<Grid item xs={6}>
<FormControl fullWidth>
<InputLabel>{t("Contract Type")}</InputLabel>
<Controller
defaultValue={contractTypes[0].id}
control={control}
name="contractTypeId"
render={({ field }) => (
<Select label={t("Contract Type")} {...field}>
{contractTypes.map((type, index) => (
<MenuItem key={`${type.id}-${index}`} value={type.id}>
{type.name}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
<ControlledAutoComplete
control={control}
options={contractTypes}
name="contractTypeId"
label={t("Contract Type")}
noOptionsText={t("No Contract Type")}
/>
</Grid>
<Grid item xs={6}>
<FormControl fullWidth>
<InputLabel>{t("Location")}</InputLabel>
<Controller
defaultValue={locationTypes[0].id}
control={control}
name="locationId"
render={({ field }) => (
<Select label={t("Location")} {...field}>
{locationTypes.map((type, index) => (
<MenuItem key={`${type.id}-${index}`} value={type.id}>
{type.name}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
<ControlledAutoComplete
control={control}
options={locationTypes}
name="locationId"
label={t("Location")}
noOptionsText={t("No Location")}
/>
</Grid>
<Grid item xs={6}>
<FormControl fullWidth>
<InputLabel>{t("Building Types")}</InputLabel>
<Controller
defaultValue={[]}
control={control}
name="buildingTypeIds"
render={({ field }) => (
<Select
renderValue={(types) =>
types
.map((type) => buildingTypeIdNameMap[type])
.join(", ")
}
multiple
label={t("Building Types")}
{...field}
>
{buildingTypes.map((type, index) => (
<MenuItem key={`${type.id}-${index}`} value={type.id}>
<Checkbox
checked={field.value.indexOf(type.id) > -1}
/>
<ListItemText primary={type.name} />
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
<ControlledAutoComplete
control={control}
options={buildingTypes}
name="buildingTypeIds"
label={t("Building Types")}
noOptionsText={t("No Building Types")}
isMultiple
/>
</Grid>

<Grid item xs={6}>
<FormControl fullWidth>
<InputLabel>{t("Work Nature")}</InputLabel>
<Controller
defaultValue={[]}
control={control}
name="workNatureIds"
render={({ field }) => (
<Select
renderValue={(types) =>
types
.map((type) => workNatureIdNameMap[type])
.join(", ")
}
multiple
label={t("Work Nature")}
{...field}
>
{workNatures.map((type, index) => (
<MenuItem key={`${type.id}-${index}`} value={type.id}>
<Checkbox
checked={field.value.indexOf(type.id) > -1}
/>
<ListItemText primary={type.name} />
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
<ControlledAutoComplete
control={control}
options={workNatures}
name="workNatureIds"
label={t("Work Nature")}
noOptionsText={t("No Work Nature")}
isMultiple
/>
</Grid>

<Grid item xs={6}>
@@ -383,13 +281,13 @@ const ProjectClientDetails: React.FC<Props> = ({
</Grid>

<Grid item xs={6}>
<Checkbox
{...register("isClpProject")}
defaultChecked={watch("isClpProject")}
/>
<Typography variant="overline" display="inline">
{t("CLP Project")}
</Typography>
<Checkbox
{...register("isClpProject")}
defaultChecked={watch("isClpProject")}
/>
<Typography variant="overline" display="inline">
{t("CLP Project")}
</Typography>
</Grid>
</Grid>
</Box>
@@ -410,29 +308,17 @@ const ProjectClientDetails: React.FC<Props> = ({
</Stack>
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
<Grid item xs={6}>
<FormControl fullWidth error={Boolean(errors.clientId)}>
<InputLabel>{t("Client")}</InputLabel>
<Controller
defaultValue={allCustomers[0]?.id}
control={control}
name="clientId"
rules={{
required: "Please select a client",
}}
render={({ field }) => (
<Select label={t("Client")} {...field}>
{allCustomers.map((customer, index) => (
<MenuItem
key={`${customer.id}-${index}`}
value={customer.id}
>
{`${customer.code} - ${customer.name}`}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
<ControlledAutoComplete
control={control}
options={allCustomers.map((customer) => ({ ...customer, label: `${customer.code} - ${customer.name}` }))}
name="clientId"
label={t("Client")}
noOptionsText={t("No Client")}
rules={{
required: "Please select a client"
}}
error={Boolean(errors.clientId)}
/>
</Grid>
<Grid item sx={{ display: { xs: "none", sm: "block" } }} />
<Grid item xs={6}>
@@ -448,96 +334,42 @@ const ProjectClientDetails: React.FC<Props> = ({
<Grid item sx={{ display: { xs: "none", sm: "block" } }} />
{customerContacts.length > 0 && (
<>
{customerSubsidiaryIds.length > 0 && (
<Grid item xs={6}>
<FormControl
fullWidth
error={Boolean(errors.clientSubsidiaryId)}
>
<InputLabel>{t("Client Subsidiary")}</InputLabel>
<Controller
// rules={{
// validate: (value) => {
// if (
// !customerSubsidiaryIds.find(
// (subsidiaryId) => subsidiaryId === value,
// )
// ) {
// return t("Please choose a valid subsidiary");
// } else return true;
// },
// }}
defaultValue={customerSubsidiaryIds[0]}
control={control}
name="clientSubsidiaryId"
render={({ field }) => (
<Select label={t("Client Subsidiary")} {...field}>
{customerSubsidiaryIds
.filter((subId) => subsidiaryMap[subId])
.map((subsidiaryId, index) => {
const subsidiary = subsidiaryMap[subsidiaryId];

return (
<MenuItem
key={`${subsidiaryId}-${index}`}
value={subsidiaryId}
>
{`${subsidiary.code} - ${subsidiary.name}`}
</MenuItem>
);
})}
</Select>
)}
/>
</FormControl>
</Grid>
)}
<Grid item xs={6}>
<FormControl
fullWidth
<ControlledAutoComplete
control={control}
options={[{ id: undefined, label: t("No Subsidiary") }, ...customerSubsidiaryIds
.filter((subId) => subsidiaryMap[subId])
.map((subsidiaryId, index) => {
const subsidiary = subsidiaryMap[subsidiaryId]
return { id: subsidiary.id, label: `${subsidiary.code} - ${subsidiary.name}` }
})]}
name="clientSubsidiaryId"
label={t("Client Subsidiary")}
noOptionsText={t("No Client Subsidiary")}
/>
</Grid>
<Grid item xs={6}>
<ControlledAutoComplete
control={control}
options={Boolean(watch("clientSubsidiaryId")) ? subsidiaryContacts : customerContacts}
name="clientContactId"
label={t("Client Lead")}
noOptionsText={t("No Client Lead")}
rules={{
validate: (value) => {
if (
(customerContacts.length > 0 && !customerContacts.find(
(contact) => contact.id === value,
)) && (subsidiaryContacts?.length > 0 && !subsidiaryContacts.find(
(contact) => contact.id === value,
))
) {
return t("Please provide a valid contact");
} else return true;
},
}}
error={Boolean(errors.clientContactId)}
>
<InputLabel>{t("Client Lead")}</InputLabel>
<Controller
rules={{
validate: (value) => {
if (
(customerContacts.length > 0 && !customerContacts.find(
(contact) => contact.id === value,
)) && (subsidiaryContacts?.length > 0 && !subsidiaryContacts.find(
(contact) => contact.id === value,
))
) {
return t("Please provide a valid contact");
} else return true;
},
}}
defaultValue={subsidiaryContacts?.length > 0 ? subsidiaryContacts[0].id : customerContacts[0].id}
control={control}
name="clientContactId"
render={({ field }) => (
<Select label={t("Client Lead")} {...field}>
{subsidiaryContacts?.length > 0 ?
subsidiaryContacts.map((contact, index) => (
<MenuItem
key={`${contact.id}-${index}`}
value={contact.id}
>
{contact.name}
</MenuItem>
))
: customerContacts.map((contact, index) => (
<MenuItem
key={`${contact.id}-${index}`}
value={contact.id}
>
{contact.name}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
/>
</Grid>
<Grid container sx={{ display: { xs: "none", sm: "block" } }} />
<Grid item xs={6}>


+ 24
- 18
src/components/SearchBox/SearchBox.tsx Переглянути файл

@@ -73,14 +73,20 @@ function SearchBox<T extends string>({
() =>
criteria.reduce<Record<T, string>>(
(acc, c) => {
return { ...acc, [c.paramName]: c.type === "select" ? !(c.needAll === false) ? "All" : c.options[0] : "" };
return {
...acc,
[c.paramName]: c.type === "select" ?
!(c.needAll === false) ? "All" :
c.options.length > 0 ? c.options[0] : ""
: ""
};
},
{} as Record<T, string>
),
[criteria]
);
const [inputs, setInputs] = useState(defaultInputs);
const makeInputChangeHandler = useCallback(
(paramName: T): React.ChangeEventHandler<HTMLInputElement> => {
return (e) => {
@@ -226,22 +232,22 @@ function SearchBox<T extends string>({
);
})}
</Grid>
<CardActions sx={{ justifyContent: "flex-end" }}>
<Button
variant="text"
startIcon={<RestartAlt />}
onClick={handleReset}
>
{t("Reset")}
</Button>
<Button
variant="outlined"
startIcon={(formType === "download" && <FileDownload />) || <Search />}
onClick={handleSearch}
>
{(formType === "download" && t("Download")) || t("Search")}
</Button>
</CardActions>
<CardActions sx={{ justifyContent: "flex-end" }}>
<Button
variant="text"
startIcon={<RestartAlt />}
onClick={handleReset}
>
{t("Reset")}
</Button>
<Button
variant="outlined"
startIcon={(formType === "download" && <FileDownload />) || <Search />}
onClick={handleSearch}
>
{(formType === "download" && t("Download")) || t("Search")}
</Button>
</CardActions>
</CardContent>
</Card>
);


Завантаження…
Відмінити
Зберегти