|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 |
- "use client";
-
- import Stack from "@mui/material/Stack";
- import Box from "@mui/material/Box";
- import Card from "@mui/material/Card";
- import CardContent from "@mui/material/CardContent";
- import FormControl from "@mui/material/FormControl";
- import Grid from "@mui/material/Grid";
- import InputLabel from "@mui/material/InputLabel";
- import MenuItem from "@mui/material/MenuItem";
- import Select from "@mui/material/Select";
- import TextField from "@mui/material/TextField";
- import Typography from "@mui/material/Typography";
- import { useTranslation } from "react-i18next";
- import CardActions from "@mui/material/CardActions";
- import RestartAlt from "@mui/icons-material/RestartAlt";
- import Button from "@mui/material/Button";
- import { Controller, useFormContext } from "react-hook-form";
- import { CreateProjectInputs } from "@/app/api/projects/actions";
- import {
- BuildingType,
- ContractType,
- FundingType,
- LocationType,
- ProjectCategory,
- ServiceType,
- WorkNature,
- } from "@/app/api/projects";
- import { StaffResult } from "@/app/api/staff";
- 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 uniq from "lodash/uniq";
-
- interface Props {
- isActive: boolean;
- projectCategories: ProjectCategory[];
- teamLeads: StaffResult[];
- allCustomers: Customer[];
- allSubsidiaries: Subsidiary[];
- serviceTypes: ServiceType[];
- contractTypes: ContractType[];
- fundingTypes: FundingType[];
- locationTypes: LocationType[];
- buildingTypes: BuildingType[];
- workNatures: WorkNature[];
- }
-
- const ProjectClientDetails: React.FC<Props> = ({
- isActive,
- projectCategories,
- teamLeads,
- allCustomers,
- allSubsidiaries,
- serviceTypes,
- contractTypes,
- fundingTypes,
- locationTypes,
- buildingTypes,
- workNatures,
- }) => {
- const { t } = useTranslation();
- const {
- register,
- formState: { errors },
- watch,
- control,
- setValue,
- getValues,
- } = useFormContext<CreateProjectInputs>();
-
- const subsidiaryMap = useMemo<{
- [id: Subsidiary["id"]]: Subsidiary;
- }>(
- () => allSubsidiaries.reduce((acc, sub) => ({ ...acc, [sub.id]: sub }), {}),
- [allSubsidiaries],
- );
-
- const selectedCustomerId = watch("clientId");
- const selectedCustomer = useMemo(
- () => allCustomers.find((c) => c.id === selectedCustomerId),
- [allCustomers, selectedCustomerId],
- );
-
- const [customerContacts, setCustomerContacts] = useState<Contact[]>([]);
- const [customerSubsidiaryIds, setCustomerSubsidiaryIds] = useState<number[]>(
- [],
- );
-
- const selectedCustomerContactId = watch("clientContactId");
- const selectedCustomerContact = useMemo(
- () =>
- customerContacts.find(
- (contact) => contact.id === selectedCustomerContactId,
- ),
- [customerContacts, selectedCustomerContactId],
- );
-
- useEffect(() => {
- if (selectedCustomerId !== undefined) {
- fetchCustomer(selectedCustomerId).then(({ contacts, subsidiaryIds }) => {
- setCustomerContacts(contacts);
- setCustomerSubsidiaryIds(subsidiaryIds);
- });
- }
- }, [selectedCustomerId]);
-
- // Automatically add the team lead to the allocated staff list
- const selectedTeamLeadId = watch("projectLeadId");
- useEffect(() => {
- if (selectedTeamLeadId !== undefined) {
- const currentStaffIds = getValues("allocatedStaffIds");
- const newList = uniq([...currentStaffIds, selectedTeamLeadId]);
- setValue("allocatedStaffIds", newList);
- }
- }, [getValues, selectedTeamLeadId, setValue]);
-
- 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 }),
- {},
- );
-
- return (
- <Card sx={{ display: isActive ? "block" : "none" }}>
- <CardContent component={Stack} spacing={4}>
- <Box>
- <Typography variant="overline" display="block" marginBlockEnd={1}>
- {t("Project Details")}
- </Typography>
- <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
- <Grid item xs={6}>
- <TextField
- label={t("Project Code")}
- fullWidth
- {...register("projectCode", {
- required: "Project code required!",
- })}
- error={Boolean(errors.projectCode)}
- />
- </Grid>
- <Grid item xs={6}>
- <TextField
- label={t("Project Name")}
- fullWidth
- {...register("projectName", {
- required: "Project name required!",
- })}
- error={Boolean(errors.projectName)}
- />
- </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>
- </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>
- </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>
- </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>
- </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>
- </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>
- </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>
- </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>
- </Grid>
-
- <Grid item xs={6}>
- <TextField
- label={t("Project Description")}
- fullWidth
- {...register("projectDescription", {
- required: "Please enter a description",
- })}
- error={Boolean(errors.projectDescription)}
- />
- </Grid>
- <Grid item xs={6}>
- <TextField
- label={t("Expected Total Project Fee")}
- fullWidth
- type="number"
- {...register("expectedProjectFee", { valueAsNumber: true })}
- />
- </Grid>
- </Grid>
- </Box>
-
- <Box>
- <Stack
- direction="row"
- alignItems="center"
- marginBlockEnd={1}
- spacing={2}
- >
- <Typography variant="overline" display="block">
- {t("Client Details")}
- </Typography>
- <Button LinkComponent={Link} href="/settings/customer">
- {t("Add or Edit Clients")}
- </Button>
- </Stack>
- <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
- <Grid item xs={6}>
- <FormControl fullWidth>
- <InputLabel>{t("Client")}</InputLabel>
- <Controller
- defaultValue={allCustomers[0].id}
- control={control}
- name="clientId"
- 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>
- </Grid>
- <Grid item sx={{ display: { xs: "none", sm: "block" } }} />
- <Grid item xs={6}>
- <TextField
- label={t("Client Type")}
- InputProps={{
- readOnly: true,
- }}
- fullWidth
- value={selectedCustomer?.customerType.name || ""}
- />
- </Grid>
- <Grid item sx={{ display: { xs: "none", sm: "block" } }} />
- {customerContacts.length > 0 && (
- <>
- <Grid item xs={6}>
- <FormControl
- fullWidth
- error={Boolean(errors.clientContactId)}
- >
- <InputLabel>{t("Client Lead")}</InputLabel>
- <Controller
- rules={{
- validate: (value) => {
- if (
- !customerContacts.find(
- (contact) => contact.id === value,
- )
- ) {
- return t("Please provide a valid contact");
- } else return true;
- },
- }}
- defaultValue={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>
- ))}
- </Select>
- )}
- />
- </FormControl>
- </Grid>
- <Grid item sx={{ display: { xs: "none", sm: "block" } }} />
- <Grid item xs={6}>
- <TextField
- label={t("Client Lead Phone Number")}
- fullWidth
- InputProps={{
- readOnly: true,
- }}
- value={selectedCustomerContact?.phone || ""}
- />
- </Grid>
- <Grid item xs={6}>
- <TextField
- label={t("Client Lead Email")}
- fullWidth
- InputProps={{
- readOnly: true,
- }}
- value={selectedCustomerContact?.email || ""}
- />
- </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" }}>
- <Button variant="text" startIcon={<RestartAlt />}>
- {t("Reset")}
- </Button>
- </CardActions>
- </CardContent>
- </Card>
- );
- };
-
- export default ProjectClientDetails;
|