|
|
@@ -17,27 +17,94 @@ 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 { ProjectCategory } from "@/app/api/projects"; |
|
|
|
import { |
|
|
|
BuildingType, |
|
|
|
ContractType, |
|
|
|
FundingType, |
|
|
|
LocationType, |
|
|
|
ProjectCategory, |
|
|
|
ServiceType, |
|
|
|
WorkNature, |
|
|
|
} from "@/app/api/projects"; |
|
|
|
import { StaffResult } from "@/app/api/staff"; |
|
|
|
import { Contact, Customer } 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"; |
|
|
|
|
|
|
|
interface Props { |
|
|
|
isActive: boolean; |
|
|
|
projectCategories: ProjectCategory[]; |
|
|
|
teamLeads: StaffResult[]; |
|
|
|
allCustomers: Customer[]; |
|
|
|
serviceTypes: ServiceType[]; |
|
|
|
contractTypes: ContractType[]; |
|
|
|
fundingTypes: FundingType[]; |
|
|
|
locationTypes: LocationType[]; |
|
|
|
buildingTypes: BuildingType[]; |
|
|
|
workNatures: WorkNature[]; |
|
|
|
} |
|
|
|
|
|
|
|
const ProjectClientDetails: React.FC<Props> = ({ |
|
|
|
isActive, |
|
|
|
projectCategories, |
|
|
|
teamLeads |
|
|
|
teamLeads, |
|
|
|
allCustomers, |
|
|
|
serviceTypes, |
|
|
|
contractTypes, |
|
|
|
fundingTypes, |
|
|
|
locationTypes, |
|
|
|
buildingTypes, |
|
|
|
workNatures, |
|
|
|
}) => { |
|
|
|
const { t } = useTranslation(); |
|
|
|
const { |
|
|
|
register, |
|
|
|
formState: { errors }, |
|
|
|
watch, |
|
|
|
control, |
|
|
|
} = useFormContext<CreateProjectInputs>(); |
|
|
|
|
|
|
|
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]); |
|
|
|
|
|
|
|
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}> |
|
|
@@ -107,6 +174,146 @@ const ProjectClientDetails: React.FC<Props> = ({ |
|
|
|
/> |
|
|
|
</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")} |
|
|
@@ -122,65 +329,160 @@ const ProjectClientDetails: React.FC<Props> = ({ |
|
|
|
label={t("Expected Total Project Fee")} |
|
|
|
fullWidth |
|
|
|
type="number" |
|
|
|
{...register("expectedProjectFee")} |
|
|
|
{...register("expectedProjectFee", { valueAsNumber: true })} |
|
|
|
/> |
|
|
|
</Grid> |
|
|
|
</Grid> |
|
|
|
</Box> |
|
|
|
|
|
|
|
<Box> |
|
|
|
<Typography variant="overline" display="block" marginBlockEnd={1}> |
|
|
|
{t("Client Details")} |
|
|
|
</Typography> |
|
|
|
<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}> |
|
|
|
<TextField |
|
|
|
label={t("Client Code")} |
|
|
|
fullWidth |
|
|
|
{...register("clientCode")} |
|
|
|
/> |
|
|
|
</Grid> |
|
|
|
<Grid item xs={6}> |
|
|
|
<TextField |
|
|
|
label={t("Client Name")} |
|
|
|
fullWidth |
|
|
|
{...register("clientName")} |
|
|
|
/> |
|
|
|
</Grid> |
|
|
|
<Grid item xs={6}> |
|
|
|
<TextField |
|
|
|
label={t("Client Lead Name")} |
|
|
|
fullWidth |
|
|
|
{...register("clientContactName")} |
|
|
|
/> |
|
|
|
</Grid> |
|
|
|
<Grid item xs={6}> |
|
|
|
<TextField |
|
|
|
label={t("Client Lead Phone Number")} |
|
|
|
fullWidth |
|
|
|
{...register("clientPhone")} |
|
|
|
/> |
|
|
|
<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 Lead Email")} |
|
|
|
label={t("Client Type")} |
|
|
|
InputProps={{ |
|
|
|
readOnly: true, |
|
|
|
}} |
|
|
|
fullWidth |
|
|
|
{...register("clientEmail")} |
|
|
|
value={selectedCustomer?.customerType.name || ""} |
|
|
|
/> |
|
|
|
</Grid> |
|
|
|
<Grid item xs={6}> |
|
|
|
<FormControl fullWidth> |
|
|
|
<InputLabel>{t("Client Subsidiary")}</InputLabel> |
|
|
|
<Select |
|
|
|
label={t("Client Subsidiary")} |
|
|
|
value={"Test Subsidiary"} |
|
|
|
<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)} |
|
|
|
> |
|
|
|
<MenuItem value={"Test Subsidiary"}> |
|
|
|
{t("Test Subsidiary")} |
|
|
|
</MenuItem> |
|
|
|
</Select> |
|
|
|
</FormControl> |
|
|
|
</Grid> |
|
|
|
<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.map((subsidiaryId, index) => ( |
|
|
|
<MenuItem |
|
|
|
key={`${subsidiaryId}-${index}`} |
|
|
|
value={subsidiaryId} |
|
|
|
> |
|
|
|
{subsidiaryId} |
|
|
|
</MenuItem> |
|
|
|
))} |
|
|
|
</Select> |
|
|
|
)} |
|
|
|
/> |
|
|
|
</FormControl> |
|
|
|
</Grid> |
|
|
|
)} |
|
|
|
</Grid> |
|
|
|
</Box> |
|
|
|
<CardActions sx={{ justifyContent: "flex-end" }}> |
|
|
|