| @@ -6,6 +6,7 @@ import Box from "@mui/material/Box"; | |||
| import { NAVIGATION_CONTENT_WIDTH } from "@/config/uiConfig"; | |||
| import Stack from "@mui/material/Stack"; | |||
| import Breadcrumb from "@/components/Breadcrumb"; | |||
| import { I18nProvider } from "@/i18n"; | |||
| export default async function MainLayout({ | |||
| children, | |||
| @@ -31,10 +32,12 @@ export default async function MainLayout({ | |||
| padding: { xs: "1rem", sm: "1.5rem", lg: "3rem" }, | |||
| }} | |||
| > | |||
| <Stack spacing={2}> | |||
| <Breadcrumb /> | |||
| {children} | |||
| </Stack> | |||
| <I18nProvider namespaces={["common"]}> | |||
| <Stack spacing={2}> | |||
| <Breadcrumb /> | |||
| {children} | |||
| </Stack> | |||
| </I18nProvider> | |||
| </Box> | |||
| </> | |||
| ); | |||
| @@ -11,6 +11,8 @@ export interface Customer { | |||
| address: string | null; | |||
| district: string | null; | |||
| customerType: CustomerType; | |||
| contacts: Contact[]; | |||
| } | |||
| export interface SaveCustomerResponse { | |||
| @@ -40,6 +42,7 @@ export interface Subsidiary { | |||
| district: string | null; | |||
| email: string | null; | |||
| subsidiaryType: SubsidiaryType; | |||
| subsidiaryContacts: Contact[]; | |||
| } | |||
| export interface SubsidiaryTable { | |||
| @@ -20,6 +20,8 @@ export interface CreateProjectInputs { | |||
| projectLeadId: number; | |||
| projectActualStart: string; | |||
| projectActualEnd: string; | |||
| projectStatus: string; | |||
| isClpProject: boolean; | |||
| // Project info | |||
| serviceTypeId: number; | |||
| @@ -28,11 +30,14 @@ export interface CreateProjectInputs { | |||
| locationId: number; | |||
| buildingTypeIds: number[]; | |||
| workNatureIds: number[]; | |||
| taskTemplateId?: number | "All"; | |||
| // Client details | |||
| clientId: Customer["id"]; | |||
| clientContactId: number; | |||
| clientContactId?: number; | |||
| clientSubsidiaryId?: number; | |||
| subsidiaryContactId: number; | |||
| isSubsidiaryContact?: boolean; | |||
| // Allocation | |||
| totalManhour: number; | |||
| @@ -12,6 +12,7 @@ export interface ProjectResult { | |||
| category: string; | |||
| team: string; | |||
| client: string; | |||
| status: string; | |||
| } | |||
| export interface ProjectCategory { | |||
| @@ -10,7 +10,9 @@ export interface Customer { | |||
| brNo: string | null; | |||
| address: string | null; | |||
| district: string | null; | |||
| customerType: CustomerType | |||
| customerType: CustomerType; | |||
| contacts: Contact[]; | |||
| } | |||
| export interface CustomerTable { | |||
| @@ -40,7 +42,9 @@ export interface Subsidiary { | |||
| brNo: string | null; | |||
| address: string | null; | |||
| district: string | null; | |||
| subsidiaryType: SubsidiaryType | |||
| subsidiaryType: SubsidiaryType; | |||
| contacts: Contact[]; | |||
| } | |||
| export interface SaveSubsidiaryResponse { | |||
| @@ -76,7 +76,7 @@ const hasErrorsInTab = ( | |||
| switch (tabIndex) { | |||
| case 0: | |||
| return ( | |||
| errors.projectName || errors.projectCode || errors.projectDescription | |||
| errors.projectName || errors.projectDescription || errors.clientId | |||
| ); | |||
| case 2: | |||
| return ( | |||
| @@ -114,6 +114,8 @@ const CreateProject: React.FC<Props> = ({ | |||
| const { t } = useTranslation(); | |||
| const router = useRouter(); | |||
| console.log(defaultInputs) | |||
| const handleCancel = () => { | |||
| router.replace("/projects"); | |||
| }; | |||
| @@ -219,6 +221,7 @@ const CreateProject: React.FC<Props> = ({ | |||
| data.projectActualEnd = dayjs().format("YYYY-MM-DD"); | |||
| } | |||
| data.taskTemplateId = data.taskTemplateId === "All" ? undefined : data.taskTemplateId; | |||
| const response = await saveProject(data); | |||
| if (response.id > 0) { | |||
| @@ -248,7 +251,8 @@ const CreateProject: React.FC<Props> = ({ | |||
| if ( | |||
| errors.projectName || | |||
| errors.projectDescription || | |||
| errors.projectCode | |||
| // errors.projectCode || | |||
| errors.clientId | |||
| ) { | |||
| setTabIndex(0); | |||
| } else if (errors.totalManhour || errors.manhourPercentageByGrade || errors.taskGroups) { | |||
| @@ -266,6 +270,7 @@ const CreateProject: React.FC<Props> = ({ | |||
| allocatedStaffIds: [], | |||
| milestones: {}, | |||
| totalManhour: 0, | |||
| taskTemplateId: "All", | |||
| ...defaultInputs, | |||
| // manhourPercentageByGrade should have a sensible default | |||
| @@ -289,7 +294,8 @@ const CreateProject: React.FC<Props> = ({ | |||
| > | |||
| {isEditMode && !(formProps.getValues("projectDeleted") === true) && ( | |||
| <Stack direction="row" gap={1}> | |||
| {!formProps.getValues("projectActualStart") && ( | |||
| {/* {!formProps.getValues("projectActualStart") && ( */} | |||
| {formProps.getValues("projectStatus") === "Pending to Start" && ( | |||
| <Button | |||
| name="start" | |||
| type="submit" | |||
| @@ -300,8 +306,9 @@ const CreateProject: React.FC<Props> = ({ | |||
| {t("Start Project")} | |||
| </Button> | |||
| )} | |||
| {formProps.getValues("projectActualStart") && | |||
| !formProps.getValues("projectActualEnd") && ( | |||
| {/* {formProps.getValues("projectActualStart") && | |||
| !formProps.getValues("projectActualEnd") && ( */} | |||
| {formProps.getValues("projectStatus") === "On-going" && ( | |||
| <Button | |||
| name="complete" | |||
| type="submit" | |||
| @@ -313,8 +320,10 @@ const CreateProject: React.FC<Props> = ({ | |||
| </Button> | |||
| )} | |||
| {!( | |||
| formProps.getValues("projectActualStart") && | |||
| formProps.getValues("projectActualEnd") | |||
| // formProps.getValues("projectActualStart") && | |||
| // formProps.getValues("projectActualEnd") | |||
| formProps.getValues("projectStatus") === "Completed" || | |||
| formProps.getValues("projectStatus") === "Deleted" | |||
| ) && ( | |||
| <Button | |||
| variant="outlined" | |||
| @@ -412,7 +421,7 @@ const CreateProject: React.FC<Props> = ({ | |||
| startIcon={<Check />} | |||
| type="submit" | |||
| disabled={ | |||
| formProps.getValues("projectDeleted") === true || | |||
| formProps.getValues("projectDeleted") === true || formProps.getValues("projectStatus") === "Deleted" || | |||
| (!!formProps.getValues("projectActualStart") && | |||
| !!formProps.getValues("projectActualEnd")) | |||
| } | |||
| @@ -85,6 +85,7 @@ const ProjectClientDetails: React.FC<Props> = ({ | |||
| ); | |||
| const [customerContacts, setCustomerContacts] = useState<Contact[]>([]); | |||
| const [subsidiaryContacts, setSubsidiaryContacts] = useState<Contact[]>([]); | |||
| const [customerSubsidiaryIds, setCustomerSubsidiaryIds] = useState<number[]>( | |||
| [], | |||
| ); | |||
| @@ -92,21 +93,44 @@ const ProjectClientDetails: React.FC<Props> = ({ | |||
| const selectedCustomerContactId = watch("clientContactId"); | |||
| const selectedCustomerContact = useMemo( | |||
| () => | |||
| customerContacts.find( | |||
| (contact) => contact.id === selectedCustomerContactId, | |||
| ), | |||
| [customerContacts, selectedCustomerContactId], | |||
| subsidiaryContacts.length > 0 ? | |||
| subsidiaryContacts.find((contact) => contact.id === selectedCustomerContactId) | |||
| : customerContacts.find( | |||
| (contact) => contact.id === selectedCustomerContactId, | |||
| ), | |||
| [subsidiaryContacts, customerContacts, selectedCustomerContactId], | |||
| ); | |||
| // get customer (client) contact combo | |||
| useEffect(() => { | |||
| if (selectedCustomerId !== undefined) { | |||
| fetchCustomer(selectedCustomerId).then(({ contacts, subsidiaryIds }) => { | |||
| setCustomerContacts(contacts); | |||
| setCustomerSubsidiaryIds(subsidiaryIds); | |||
| 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) | |||
| }); | |||
| } | |||
| }, [selectedCustomerId]); | |||
| const clientSubsidiaryId = watch("clientSubsidiaryId") | |||
| useEffect(() => { | |||
| if (Boolean(clientSubsidiaryId)) { | |||
| // get subsidiary contact combo | |||
| const contacts = allSubsidiaries.find(subsidiary => subsidiary.id === clientSubsidiaryId)?.subsidiaryContacts!! | |||
| setSubsidiaryContacts(contacts) | |||
| setValue("clientContactId", contacts[0].id) | |||
| setValue("isSubsidiaryContact", true) | |||
| } else if (customerContacts?.length > 0) { | |||
| setSubsidiaryContacts([]) | |||
| setValue("clientContactId", customerContacts[0].id) | |||
| setValue("isSubsidiaryContact", false) | |||
| } | |||
| }, [customerContacts, clientSubsidiaryId]); | |||
| // Automatically add the team lead to the allocated staff list | |||
| const selectedTeamLeadId = watch("projectLeadId"); | |||
| useEffect(() => { | |||
| @@ -139,10 +163,13 @@ const ProjectClientDetails: React.FC<Props> = ({ | |||
| <TextField | |||
| label={t("Project Code")} | |||
| fullWidth | |||
| {...register("projectCode", { | |||
| required: "Project code required!", | |||
| })} | |||
| error={Boolean(errors.projectCode)} | |||
| disabled | |||
| {...register("projectCode", | |||
| // { | |||
| // required: "Project code required!", | |||
| // } | |||
| )} | |||
| // error={Boolean(errors.projectCode)} | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={6}> | |||
| @@ -354,6 +381,16 @@ const ProjectClientDetails: React.FC<Props> = ({ | |||
| {...register("expectedProjectFee", { valueAsNumber: true })} | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={6}> | |||
| <Checkbox | |||
| {...register("isClpProject")} | |||
| defaultChecked={watch("isClpProject")} | |||
| /> | |||
| <Typography variant="overline" display="inline"> | |||
| {t("CLP Project")} | |||
| </Typography> | |||
| </Grid> | |||
| </Grid> | |||
| </Box> | |||
| @@ -373,12 +410,15 @@ const ProjectClientDetails: React.FC<Props> = ({ | |||
| </Stack> | |||
| <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> | |||
| <Grid item xs={6}> | |||
| <FormControl fullWidth> | |||
| <FormControl fullWidth error={Boolean(errors.clientId)}> | |||
| <InputLabel>{t("Client")}</InputLabel> | |||
| <Controller | |||
| defaultValue={allCustomers[0].id} | |||
| 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) => ( | |||
| @@ -408,6 +448,50 @@ 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 | |||
| @@ -418,33 +502,44 @@ const ProjectClientDetails: React.FC<Props> = ({ | |||
| rules={{ | |||
| validate: (value) => { | |||
| if ( | |||
| !customerContacts.find( | |||
| (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={customerContacts[0].id} | |||
| defaultValue={subsidiaryContacts?.length > 0 ? subsidiaryContacts[0].id : customerContacts[0].id} | |||
| control={control} | |||
| name="clientContactId" | |||
| render={({ field }) => ( | |||
| <Select label={t("Client Lead")} {...field}> | |||
| {customerContacts.map((contact, index) => ( | |||
| <MenuItem | |||
| key={`${contact.id}-${index}`} | |||
| value={contact.id} | |||
| > | |||
| {contact.name} | |||
| </MenuItem> | |||
| ))} | |||
| {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 item sx={{ display: { xs: "none", sm: "block" } }} /> | |||
| <Grid container sx={{ display: { xs: "none", sm: "block" } }} /> | |||
| <Grid item xs={6}> | |||
| <TextField | |||
| label={t("Client Lead Phone Number")} | |||
| @@ -467,50 +562,6 @@ const ProjectClientDetails: React.FC<Props> = ({ | |||
| </Grid> | |||
| </> | |||
| )} | |||
| {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 Lead")} {...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> | |||
| </Box> | |||
| <CardActions sx={{ justifyContent: "flex-end" }}> | |||
| @@ -33,7 +33,7 @@ const TaskSetup: React.FC<Props> = ({ | |||
| isActive, | |||
| }) => { | |||
| const { t } = useTranslation(); | |||
| const { setValue, watch, clearErrors, setError } = useFormContext<CreateProjectInputs>(); | |||
| const { setValue, watch, clearErrors, setError, formState: { defaultValues } } = useFormContext<CreateProjectInputs>(); | |||
| const currentTaskGroups = watch("taskGroups"); | |||
| const currentTaskIds = Object.values(currentTaskGroups).reduce<Task["id"][]>( | |||
| (acc, group) => { | |||
| @@ -48,7 +48,7 @@ const TaskSetup: React.FC<Props> = ({ | |||
| const [selectedTaskTemplateId, setSelectedTaskTemplateId] = useState< | |||
| "All" | number | |||
| >("All"); | |||
| >(watch("taskTemplateId") ?? "All"); | |||
| const onSelectTaskTemplate = useCallback( | |||
| (e: SelectChangeEvent<number | "All">) => { | |||
| if (e.target.value === "All" || isNumber(e.target.value)) { | |||
| @@ -64,7 +64,8 @@ const TaskSetup: React.FC<Props> = ({ | |||
| (template) => template.id === selectedTaskTemplateId, | |||
| ) | |||
| if (selectedTaskTemplateId !== "All") { | |||
| if (selectedTaskTemplateId !== "All" && selectedTaskTemplateId !== watch("taskTemplateId")) { | |||
| // update the "manhour allocation by grade" by task template | |||
| const updatedManhourPercentageByGrade: ManhourAllocation = watch("manhourPercentageByGrade") | |||
| selectedTaskTemplate?.gradeAllocations.forEach((gradeAllocation) => { | |||
| @@ -73,28 +74,30 @@ const TaskSetup: React.FC<Props> = ({ | |||
| setValue("manhourPercentageByGrade", updatedManhourPercentageByGrade) | |||
| if (Object.values(updatedManhourPercentageByGrade).reduce((acc, value) => acc + value, 0) === 100) clearErrors("manhourPercentageByGrade") | |||
| else setError("manhourPercentageByGrade", {message: "manhourPercentageByGrade value is not valid", type: "invalid"}) | |||
| else setError("manhourPercentageByGrade", { message: "manhourPercentageByGrade value is not valid", type: "invalid" }) | |||
| // update the "manhour allocation by grade by stage" by task template | |||
| const updatedTaskGroups = watch("taskGroups") | |||
| const taskGroupsKeys = Object.keys(updatedTaskGroups) | |||
| selectedTaskTemplate?.groupAllocations.forEach((groupAllocation) => { | |||
| const taskGroupId = groupAllocation.taskGroup.id | |||
| if(taskGroupsKeys.includes(taskGroupId.toString())) { | |||
| updatedTaskGroups[taskGroupId] = {...updatedTaskGroups[taskGroupId], percentAllocation: groupAllocation?.percentage} | |||
| if (taskGroupsKeys.includes(taskGroupId.toString())) { | |||
| updatedTaskGroups[taskGroupId] = { ...updatedTaskGroups[taskGroupId], percentAllocation: groupAllocation?.percentage } | |||
| } | |||
| }) | |||
| const percentageToZeroGroupIds = difference(taskGroupsKeys.map(key => parseFloat(key)), selectedTaskTemplate?.groupAllocations.map(groupAllocation => groupAllocation.taskGroup.id)!!) | |||
| percentageToZeroGroupIds.forEach((percentageToZeroGroupId) => { | |||
| updatedTaskGroups[percentageToZeroGroupId] = {...updatedTaskGroups[percentageToZeroGroupId], percentAllocation: 0} | |||
| updatedTaskGroups[percentageToZeroGroupId] = { ...updatedTaskGroups[percentageToZeroGroupId], percentAllocation: 0 } | |||
| }) | |||
| setValue("taskGroups", updatedTaskGroups) | |||
| if (Object.values(updatedTaskGroups).reduce((acc, value) => acc + value.percentAllocation, 0) === 100) clearErrors("taskGroups") | |||
| else setError("taskGroups", {message: "Task Groups value is not invalid", type: "invalid"}) | |||
| else setError("taskGroups", { message: "Task Groups value is not invalid", type: "invalid" }) | |||
| } | |||
| setValue("taskTemplateId", selectedTaskTemplateId) | |||
| const taskList = | |||
| selectedTaskTemplateId === "All" | |||
| ? tasks | |||
| @@ -176,7 +179,26 @@ const TaskSetup: React.FC<Props> = ({ | |||
| }; | |||
| }, {}); | |||
| setValue("taskGroups", newTaskGroups); | |||
| // update the "manhour allocation by grade by stage" by task template | |||
| const taskGroupsKeys = Object.keys(newTaskGroups) | |||
| const selectedTaskTemplate = taskTemplates.find( | |||
| (template) => template.id === selectedTaskTemplateId, | |||
| ) | |||
| selectedTaskTemplate?.groupAllocations.forEach((groupAllocation) => { | |||
| const taskGroupId = groupAllocation.taskGroup.id | |||
| if (taskGroupsKeys.includes(taskGroupId.toString())) { | |||
| newTaskGroups[taskGroupId] = { ...newTaskGroups[taskGroupId], percentAllocation: groupAllocation?.percentage } | |||
| } | |||
| }) | |||
| const percentageToZeroGroupIds = difference(taskGroupsKeys.map(key => parseFloat(key)), selectedTaskTemplate?.groupAllocations.map(groupAllocation => groupAllocation.taskGroup.id)!!) | |||
| percentageToZeroGroupIds.forEach((percentageToZeroGroupId) => { | |||
| newTaskGroups[percentageToZeroGroupId] = { ...newTaskGroups[percentageToZeroGroupId], percentAllocation: 0 } | |||
| }) | |||
| setValue("taskGroups", newTaskGroups) | |||
| if (Object.values(newTaskGroups).reduce((acc, value) => acc + value.percentAllocation, 0) === 100) clearErrors("taskGroups") | |||
| else setError("taskGroups", { message: "Task Groups value is not invalid", type: "invalid" }) | |||
| }} | |||
| allItemsLabel={t("Task Pool")} | |||
| selectedItemsLabel={t("Project Task List")} | |||
| @@ -46,6 +46,12 @@ const ProjectSearch: React.FC<Props> = ({ projects, projectCategories }) => { | |||
| type: "select", | |||
| options: uniq(projects.map((project) => project.team)), | |||
| }, | |||
| { | |||
| label: t("Status"), | |||
| paramName: "status", | |||
| type: "select", | |||
| options: uniq(projects.map((project) => project.status)), | |||
| }, | |||
| ], | |||
| [t, projectCategories, projects], | |||
| ); | |||
| @@ -74,6 +80,7 @@ const ProjectSearch: React.FC<Props> = ({ projects, projectCategories }) => { | |||
| { name: "category", label: t("Project Category") }, | |||
| { name: "team", label: t("Team") }, | |||
| { name: "client", label: t("Client") }, | |||
| { name: "status", label: t("Status") }, | |||
| ], | |||
| [t, onProjectClick], | |||
| ); | |||
| @@ -90,7 +97,8 @@ const ProjectSearch: React.FC<Props> = ({ projects, projectCategories }) => { | |||
| 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), | |||
| (query.team === "All" || p.team === query.team) && | |||
| (query.status === "All" || p.status === query.status), | |||
| ), | |||
| ); | |||
| }} | |||