Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 

730 wiersze
26 KiB

  1. "use client";
  2. import Stack from "@mui/material/Stack";
  3. import Box from "@mui/material/Box";
  4. import Card from "@mui/material/Card";
  5. import CardContent from "@mui/material/CardContent";
  6. import Grid from "@mui/material/Grid";
  7. import TextField from "@mui/material/TextField";
  8. import { NumericFormat } from 'react-number-format';
  9. import Typography from "@mui/material/Typography";
  10. import { useTranslation } from "react-i18next";
  11. import Button from "@mui/material/Button";
  12. import { Controller, useFormContext } from "react-hook-form";
  13. import { CreateProjectInputs } from "@/app/api/projects/actions";
  14. import {
  15. BuildingType,
  16. ContractType,
  17. FundingType,
  18. LocationType,
  19. MainProject,
  20. ProjectCategory,
  21. ServiceType,
  22. WorkNature,
  23. } from "@/app/api/projects";
  24. import { StaffResult } from "@/app/api/staff";
  25. import {
  26. Contact,
  27. Customer,
  28. CustomerType,
  29. Subsidiary,
  30. } from "@/app/api/customer";
  31. import Link from "next/link";
  32. import React, { useEffect, useMemo, useState } from "react";
  33. import { fetchCustomer } from "@/app/api/customer/actions";
  34. import uniq from "lodash/uniq";
  35. import ControlledAutoComplete from "../ControlledAutoComplete/ControlledAutoComplete";
  36. import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers";
  37. import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
  38. import dayjs, { Dayjs } from 'dayjs';
  39. import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
  40. interface Props {
  41. isActive: boolean;
  42. isSubProject: boolean;
  43. isEditMode: boolean;
  44. mainProjects?: MainProject[];
  45. projectCategories: ProjectCategory[];
  46. teamLeads: StaffResult[];
  47. allStaffs: StaffResult[];
  48. allCustomers: Customer[];
  49. allSubsidiaries: Subsidiary[];
  50. serviceTypes: ServiceType[];
  51. contractTypes: ContractType[];
  52. fundingTypes: FundingType[];
  53. locationTypes: LocationType[];
  54. buildingTypes: BuildingType[];
  55. workNatures: WorkNature[];
  56. customerTypes: CustomerType[];
  57. }
  58. const ProjectClientDetails: React.FC<Props> = ({
  59. isActive,
  60. isSubProject,
  61. isEditMode,
  62. mainProjects,
  63. projectCategories,
  64. teamLeads,
  65. allStaffs,
  66. allCustomers,
  67. allSubsidiaries,
  68. serviceTypes,
  69. contractTypes,
  70. fundingTypes,
  71. locationTypes,
  72. buildingTypes,
  73. customerTypes,
  74. workNatures,
  75. }) => {
  76. const {
  77. t,
  78. i18n: { language },
  79. } = useTranslation();
  80. const {
  81. register,
  82. formState: { errors, defaultValues, touchedFields },
  83. watch,
  84. control,
  85. setValue,
  86. getValues,
  87. reset,
  88. resetField,
  89. setError,
  90. clearErrors
  91. } = useFormContext<CreateProjectInputs>();
  92. const subsidiaryMap = useMemo<{
  93. [id: Subsidiary["id"]]: Subsidiary;
  94. }>(
  95. () => allSubsidiaries.reduce((acc, sub) => ({ ...acc, [sub.id]: sub }), {}),
  96. [allSubsidiaries],
  97. );
  98. const selectedCustomerId = watch("clientId");
  99. const [customerContacts, setCustomerContacts] = useState<Contact[]>([]);
  100. const [subsidiaryContacts, setSubsidiaryContacts] = useState<Contact[]>([]);
  101. const [customerSubsidiaryIds, setCustomerSubsidiaryIds] = useState<number[]>(
  102. [],
  103. );
  104. const selectedCustomerContactId = watch("clientContactId");
  105. const selectedCustomerContact = useMemo(
  106. () =>
  107. subsidiaryContacts.length > 0
  108. ? subsidiaryContacts.find(
  109. (contact) => contact.id === selectedCustomerContactId,
  110. )
  111. : customerContacts.find(
  112. (contact) => contact.id === selectedCustomerContactId,
  113. ),
  114. [subsidiaryContacts, customerContacts, selectedCustomerContactId],
  115. );
  116. // get customer (client) contact combo
  117. const clientSubsidiaryId = watch("clientSubsidiaryId");
  118. useEffect(() => {
  119. if (selectedCustomerId !== undefined) {
  120. fetchCustomer(selectedCustomerId).then(
  121. ({ contacts, subsidiaryIds, customer }) => {
  122. // console.log(contacts)
  123. // console.log(subsidiaryIds)
  124. setCustomerContacts(contacts);
  125. setCustomerSubsidiaryIds(subsidiaryIds);
  126. setValue(
  127. "clientTypeId",
  128. touchedFields["clientTypeId"]
  129. ? customer.customerType.id
  130. : defaultValues?.clientTypeId || customer.customerType.id,
  131. {
  132. shouldTouch: isEditMode,
  133. },
  134. );
  135. if (subsidiaryIds.length > 0)
  136. setValue(
  137. "clientSubsidiaryId",
  138. clientSubsidiaryId !== undefined && clientSubsidiaryId !== null
  139. ? subsidiaryIds.includes(clientSubsidiaryId)
  140. ? clientSubsidiaryId
  141. : null
  142. : null,
  143. );
  144. // if (contacts.length > 0) setValue("clientContactId", contacts[0].id)
  145. // else setValue("clientContactId", undefined)
  146. },
  147. );
  148. }
  149. }, [selectedCustomerId]);
  150. useEffect(() => {
  151. if (Boolean(clientSubsidiaryId)) {
  152. // get subsidiary contact combo
  153. const contacts = allSubsidiaries.find(
  154. (subsidiary) => subsidiary.id === clientSubsidiaryId,
  155. )!.subsidiaryContacts;
  156. setSubsidiaryContacts(() => contacts);
  157. setValue(
  158. "clientContactId",
  159. selectedCustomerId === defaultValues?.clientId &&
  160. Boolean(defaultValues?.clientSubsidiaryId)
  161. ? contacts.find(
  162. (contact) => contact?.id === defaultValues?.clientContactId,
  163. )?.id ?? contacts[0]?.id
  164. : contacts[0]?.id,
  165. );
  166. setValue("isSubsidiaryContact", true);
  167. } else if (customerContacts?.length > 0) {
  168. setSubsidiaryContacts(() => []);
  169. setValue(
  170. "clientContactId",
  171. selectedCustomerId === defaultValues?.clientId &&
  172. !Boolean(defaultValues?.clientSubsidiaryId)
  173. ? customerContacts.find(
  174. (contact) => contact.id === defaultValues.clientContactId,
  175. )?.id ?? customerContacts[0].id
  176. : customerContacts[0].id,
  177. );
  178. setValue("isSubsidiaryContact", false);
  179. }
  180. }, [customerContacts, clientSubsidiaryId, selectedCustomerId]);
  181. // Automatically add the team lead to the allocated staff list
  182. const selectedTeamLeadId = watch("projectLeadId");
  183. useEffect(() => {
  184. if (selectedTeamLeadId !== undefined) {
  185. const currentStaffIds = getValues("allocatedStaffIds");
  186. const newList = uniq([...currentStaffIds, selectedTeamLeadId]);
  187. setValue("allocatedStaffIds", newList);
  188. }
  189. }, [getValues, selectedTeamLeadId, setValue]);
  190. // List of possible sub team lead (staffs by team)
  191. const subTeamLeads = useMemo<StaffResult[]>(() => {
  192. if (!selectedTeamLeadId) return [];
  193. const teamLead = teamLeads.find(tl => tl.id === selectedTeamLeadId);
  194. return teamLead ? allStaffs.filter(staff => staff.team === teamLead.team) : [];
  195. }, [selectedTeamLeadId, teamLeads, allStaffs]);
  196. // Automatically update the project & client details whene select a main project
  197. const mainProjectId = watch("mainProjectId");
  198. useEffect(() => {
  199. if (
  200. mainProjectId !== undefined &&
  201. mainProjects !== undefined &&
  202. !isEditMode
  203. ) {
  204. const mainProject = mainProjects.find(
  205. (project) => project.projectId === mainProjectId,
  206. );
  207. if (mainProject !== undefined) {
  208. const teamLeadIds = teamLeads.map((teamLead) => teamLead.id)
  209. const subTeamLeadIds = subTeamLeads.map((_subTeamLead) => _subTeamLead.id)
  210. setValue("projectName", mainProject.projectName);
  211. setValue("projectCategoryId", mainProject.projectCategoryId);
  212. // set project lead id to the first team lead id if the main project lead id is not in the team lead list
  213. setValue("projectLeadId", teamLeadIds.find((id) => id === mainProject.projectLeadId) ? mainProject.projectLeadId : teamLeadIds[0] ?? mainProject.projectLeadId);
  214. setValue("projectSubLeadId", subTeamLeadIds.find((id) => id === mainProject.projectSubLeadId) ? mainProject.projectSubLeadId : subTeamLeadIds[0] ?? mainProject.projectSubLeadId);
  215. setValue("serviceTypeId", mainProject.serviceTypeId);
  216. setValue("fundingTypeId", mainProject.fundingTypeId);
  217. setValue("contractTypeId", mainProject.contractTypeId);
  218. setValue("locationId", mainProject.locationId);
  219. setValue("buildingTypeIds", mainProject.buildingTypeIds);
  220. setValue("workNatureIds", mainProject.workNatureIds);
  221. setValue("projectDescription", mainProject.projectDescription);
  222. setValue("expectedProjectFee", mainProject.expectedProjectFee);
  223. setValue("subContractFee", mainProject.subContractFee);
  224. setValue("isClpProject", mainProject.isClpProject);
  225. setValue("clientId", mainProject.clientId);
  226. setValue("clientSubsidiaryId", mainProject.clientSubsidiaryId);
  227. setValue("clientContactId", mainProject.clientContactId);
  228. }
  229. }
  230. }, [getValues, mainProjectId, setValue, isEditMode]);
  231. // const buildingTypeIdNameMap = buildingTypes.reduce<{ [id: number]: string }>(
  232. // (acc, building) => ({ ...acc, [building.id]: building.name }),
  233. // {},
  234. // );
  235. // const workNatureIdNameMap = workNatures.reduce<{ [id: number]: string }>(
  236. // (acc, wn) => ({ ...acc, [wn.id]: wn.name }),
  237. // {},
  238. // );
  239. const planStart = watch("projectPlanStart")
  240. const planEnd = watch("projectPlanEnd")
  241. useEffect(() => {
  242. let hasErrors = false
  243. if(
  244. !planStart || new Date(planStart) > new Date(planEnd)
  245. ){
  246. hasErrors = true;
  247. }
  248. if(
  249. !planEnd || new Date(planStart) > new Date(planEnd)
  250. ){
  251. hasErrors = true;
  252. }
  253. if(hasErrors){
  254. setError("projectPlanStart", {
  255. message: "Project Plan Start date is not valid",
  256. type: "required",
  257. });
  258. setError("projectPlanEnd", {
  259. message: "Project Plan End date is not valid",
  260. type: "required",
  261. });
  262. }else{
  263. clearErrors("projectPlanStart")
  264. clearErrors("projectPlanEnd")
  265. }
  266. },[planStart, planEnd])
  267. return (
  268. <Card sx={{ display: isActive ? "block" : "none" }}>
  269. <CardContent component={Stack} spacing={4}>
  270. <Box>
  271. <Typography variant="overline" display="block" marginBlockEnd={1}>
  272. {t("Project Details")}
  273. </Typography>
  274. <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
  275. {isSubProject && mainProjects !== undefined && (
  276. <>
  277. <Grid item xs={6}>
  278. <ControlledAutoComplete
  279. control={control}
  280. options={[
  281. ...mainProjects.map((mainProject) => ({
  282. id: mainProject.projectId,
  283. label: `${mainProject.projectCode} - ${mainProject.projectName}`,
  284. })),
  285. ]}
  286. name="mainProjectId"
  287. label={t("Main Project")}
  288. noOptionsText={t("No Main Project")}
  289. disabled={isEditMode}
  290. />
  291. </Grid>
  292. <Grid item sx={{ display: { xs: "none", sm: "block" } }} />
  293. </>
  294. )}
  295. <Grid item xs={6}>
  296. <TextField
  297. label={t("Project Code")}
  298. fullWidth
  299. // disabled={isSubProject && mainProjects !== undefined}
  300. {...register("projectCode", {
  301. required:
  302. !(isSubProject && mainProjects !== undefined) &&
  303. "Project code required!",
  304. })}
  305. error={Boolean(errors.projectCode)}
  306. />
  307. </Grid>
  308. <Grid item xs={6}>
  309. <TextField
  310. label={t("Project Name")}
  311. fullWidth
  312. {...register("projectName", {
  313. required: "Project name required!",
  314. })}
  315. error={Boolean(errors.projectName)}
  316. />
  317. </Grid>
  318. <Grid item xs={3}>
  319. <LocalizationProvider
  320. dateAdapter={AdapterDayjs}
  321. adapterLocale={`${language}-hk`}
  322. >
  323. <DatePicker
  324. sx={{ width: "100%" }}
  325. label={t("Plan Start")}
  326. format="YYYY/MM/DD"
  327. value={planStart ? dayjs(planStart) : null}
  328. onChange={(date) => {
  329. if (!date) return;
  330. setValue("projectPlanStart", date.format(INPUT_DATE_FORMAT));
  331. }}
  332. slotProps={{
  333. textField: {
  334. // required: true,
  335. error:
  336. // Boolean(errors.projectPlanStart)
  337. // ||
  338. new Date(planStart) > new Date(planEnd)
  339. || Boolean(errors.projectPlanStart)
  340. ,
  341. },
  342. }}
  343. />
  344. </LocalizationProvider>
  345. </Grid>
  346. <Grid item xs={3}>
  347. <LocalizationProvider
  348. dateAdapter={AdapterDayjs}
  349. adapterLocale={`${language}-hk`}
  350. >
  351. <DatePicker
  352. sx={{ width: "100%" }}
  353. label={t("Plan End")}
  354. format="YYYY/MM/DD"
  355. value={planEnd ? dayjs(planEnd) : null}
  356. onChange={(date) => {
  357. if (!date) return;
  358. setValue("projectPlanEnd", date.format(INPUT_DATE_FORMAT));
  359. }}
  360. slotProps={{
  361. textField: {
  362. // required: true,
  363. error:
  364. // Boolean(errors.projectPlanEnd)
  365. // ||
  366. new Date(planStart) > new Date(planEnd)
  367. || Boolean(errors.projectPlanEnd)
  368. ,
  369. },
  370. }}
  371. />
  372. </LocalizationProvider>
  373. </Grid>
  374. <Grid item xs={6}>
  375. <ControlledAutoComplete
  376. control={control}
  377. options={projectCategories}
  378. name="projectCategoryId"
  379. label={t("Project Category")}
  380. noOptionsText={t("No Project Category")}
  381. />
  382. </Grid>
  383. <Grid item xs={6}>
  384. <ControlledAutoComplete
  385. control={control}
  386. options={teamLeads.map((staff) => ({
  387. ...staff,
  388. label: `${staff.staffId} - ${staff.name} (${staff.team})`,
  389. }))}
  390. name="projectLeadId"
  391. label={t("Team Lead")}
  392. noOptionsText={t("No Team Lead")}
  393. />
  394. </Grid>
  395. {subTeamLeads.length > 0 && <Grid item xs={6}>
  396. <ControlledAutoComplete
  397. control={control}
  398. options={
  399. [{id: undefined, label: 'N/A'},
  400. ...subTeamLeads
  401. // .filter(staff => staff.team === teamLeads.find(teamLead => teamLead.id === selectedTeamLeadId)?.team)
  402. .map((staff) => ({
  403. ...staff,
  404. label: `${staff.staffId} - ${staff.name} (${staff.team})`,
  405. }))]}
  406. name="projectSubLeadId"
  407. label={t("Sub Team Lead")}
  408. noOptionsText={t("No Sub Team Lead")}
  409. />
  410. </Grid>}
  411. <Grid item xs={6}>
  412. <ControlledAutoComplete
  413. control={control}
  414. options={serviceTypes}
  415. name="serviceTypeId"
  416. label={t("Service Type")}
  417. noOptionsText={t("No Service Type")}
  418. />
  419. </Grid>
  420. <Grid item xs={6}>
  421. <ControlledAutoComplete
  422. control={control}
  423. options={fundingTypes}
  424. name="fundingTypeId"
  425. label={t("Funding Type")}
  426. noOptionsText={t("No Funding Type")}
  427. />
  428. </Grid>
  429. <Grid item xs={6}>
  430. <ControlledAutoComplete
  431. control={control}
  432. options={contractTypes}
  433. name="contractTypeId"
  434. label={t("Contract Type")}
  435. noOptionsText={t("No Contract Type")}
  436. />
  437. </Grid>
  438. <Grid item xs={6}>
  439. <ControlledAutoComplete
  440. control={control}
  441. options={locationTypes}
  442. name="locationId"
  443. label={t("Location")}
  444. noOptionsText={t("No Location")}
  445. />
  446. </Grid>
  447. <Grid item xs={6}>
  448. <ControlledAutoComplete
  449. control={control}
  450. options={buildingTypes}
  451. name="buildingTypeIds"
  452. label={t("Building Types")}
  453. noOptionsText={t("No Building Types")}
  454. isMultiple
  455. />
  456. </Grid>
  457. <Grid item xs={6}>
  458. <ControlledAutoComplete
  459. control={control}
  460. options={workNatures}
  461. name="workNatureIds"
  462. label={t("Work Nature")}
  463. noOptionsText={t("No Work Nature")}
  464. isMultiple
  465. />
  466. </Grid>
  467. <Grid item xs={6}>
  468. <TextField
  469. label={t("Project Description")}
  470. fullWidth
  471. {...register("projectDescription", {
  472. required: "Please enter a description",
  473. })}
  474. error={Boolean(errors.projectDescription)}
  475. />
  476. </Grid>
  477. <Grid item xs={6}>
  478. <Controller
  479. control={control}
  480. name="expectedProjectFee"
  481. render={({ field: { onChange, onBlur, name, value, ref } }) => (
  482. <NumericFormat
  483. label={t("Expected Total Project Fee")}
  484. fullWidth
  485. prefix="HK$"
  486. onValueChange={(values) => {
  487. // console.log(values)
  488. onChange(values.floatValue)
  489. }}
  490. customInput={TextField}
  491. thousandSeparator
  492. valueIsNumericString
  493. decimalScale={2}
  494. fixedDecimalScale
  495. name={name}
  496. value={value}
  497. onBlur={onBlur}
  498. inputRef={ref}
  499. />
  500. )}
  501. />
  502. {/* <TextField
  503. label={t("Expected Total Project Fee")}
  504. fullWidth
  505. type="number"
  506. inputProps={{
  507. step: "0.01",
  508. }}
  509. {...register("expectedProjectFee", { valueAsNumber: true })}
  510. /> */}
  511. </Grid>
  512. <Grid item xs={6}>
  513. <Controller
  514. control={control}
  515. name="subContractFee"
  516. render={({ field: { onChange, onBlur, name, value, ref } }) => (
  517. <NumericFormat
  518. label={t("Sub-Contract Fee")}
  519. fullWidth
  520. prefix="HK$"
  521. onValueChange={(values) => {
  522. // console.log(values)
  523. onChange(values.floatValue)
  524. }}
  525. customInput={TextField}
  526. thousandSeparator
  527. valueIsNumericString
  528. decimalScale={2}
  529. fixedDecimalScale
  530. name={name}
  531. value={value}
  532. onBlur={onBlur}
  533. inputRef={ref}
  534. />
  535. )}
  536. />
  537. {/* <TextField
  538. label={t("Sub-Contract Fee")}
  539. fullWidth
  540. type="number"
  541. inputProps={{ step: "0.01" }}
  542. // InputLabelProps={{
  543. // shrink: Boolean(watch("subContractFee")),
  544. // }}
  545. {...register("subContractFee", { valueAsNumber: true })}
  546. /> */}
  547. </Grid>
  548. {/* <Grid item xs={6}>
  549. <Checkbox
  550. {...register("isClpProject")}
  551. checked={Boolean(watch("isClpProject"))}
  552. disabled={isSubProject && mainProjects !== undefined}
  553. />
  554. <Typography variant="overline" display="inline">
  555. {t("CLP Project")}
  556. </Typography>
  557. </Grid> */}
  558. </Grid>
  559. </Box>
  560. <Box>
  561. <Stack
  562. direction="row"
  563. alignItems="center"
  564. marginBlockEnd={1}
  565. spacing={2}
  566. >
  567. <Typography variant="overline" display="block">
  568. {t("Client Details")}
  569. </Typography>
  570. {/* <Button LinkComponent={Link} href="/settings/customer">
  571. {t("Add or Edit Clients")}
  572. </Button> */}
  573. </Stack>
  574. <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
  575. <Grid item xs={6}>
  576. <ControlledAutoComplete
  577. control={control}
  578. options={allCustomers.map((customer) => ({
  579. ...customer,
  580. label: `${customer.name}`,
  581. // label: `${customer.code} - ${customer.name}`,
  582. }))}
  583. name="clientId"
  584. label={t("Client")}
  585. noOptionsText={t("No Client")}
  586. rules={{
  587. required: "Please select a client",
  588. }}
  589. />
  590. </Grid>
  591. {/* <Grid item sx={{ display: { xs: "none", sm: "block" } }} /> */}
  592. <Grid item xs={6}>
  593. <ControlledAutoComplete
  594. control={control}
  595. options={customerTypes}
  596. name="clientTypeId"
  597. label={t("Client Type")}
  598. noOptionsText={t("No Client Type")}
  599. rules={{
  600. required: "Please select a client type",
  601. }}
  602. />
  603. </Grid>
  604. </Grid>
  605. <Grid item sx={{ display: { xs: "none", sm: "block" } }} />
  606. {customerContacts.length > 0 && (
  607. <Box>
  608. <Stack
  609. direction="row"
  610. alignItems="center"
  611. marginBlockEnd={1}
  612. spacing={2}
  613. >
  614. <Typography variant="overline" display="block">
  615. {t("Subsidiary Details")}
  616. </Typography>
  617. </Stack>
  618. <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
  619. <Grid item xs={6}>
  620. <ControlledAutoComplete
  621. control={control}
  622. options={[
  623. { label: t("No Subsidiary") },
  624. ...customerSubsidiaryIds
  625. .filter((subId) => subsidiaryMap[subId])
  626. .map((subsidiaryId, index) => {
  627. const subsidiary = subsidiaryMap[subsidiaryId];
  628. return {
  629. id: subsidiary.id,
  630. label: `${subsidiary.name}`,
  631. // label: `${subsidiary.code} - ${subsidiary.name}`,
  632. };
  633. }),
  634. ]}
  635. name="clientSubsidiaryId"
  636. label={t("Client Subsidiary")}
  637. noOptionsText={t("No Client Subsidiary")}
  638. />
  639. </Grid>
  640. <Grid item xs={6}>
  641. <ControlledAutoComplete
  642. control={control}
  643. options={
  644. Boolean(watch("clientSubsidiaryId"))
  645. ? subsidiaryContacts
  646. : customerContacts
  647. }
  648. name="clientContactId"
  649. label={t("Client Lead")}
  650. noOptionsText={t("No Client Lead")}
  651. rules={{
  652. validate: (value) => {
  653. if (
  654. customerContacts.length > 0 &&
  655. !customerContacts.find(
  656. (contact) => contact.id === value,
  657. ) &&
  658. subsidiaryContacts?.length > 0 &&
  659. !subsidiaryContacts.find(
  660. (contact) => contact.id === value,
  661. )
  662. ) {
  663. return t("Please provide a valid contact");
  664. } else return true;
  665. },
  666. }}
  667. />
  668. </Grid>
  669. <Grid container sx={{ display: { xs: "none", sm: "block" } }} />
  670. <Grid item xs={6}>
  671. <TextField
  672. label={t("Client Lead Phone Number")}
  673. fullWidth
  674. InputProps={{
  675. readOnly: true,
  676. }}
  677. value={selectedCustomerContact?.phone || ""}
  678. />
  679. </Grid>
  680. <Grid item xs={6}>
  681. <TextField
  682. label={t("Client Lead Email")}
  683. fullWidth
  684. InputProps={{
  685. readOnly: true,
  686. }}
  687. value={selectedCustomerContact?.email || ""}
  688. />
  689. </Grid>
  690. </Grid>
  691. </Box>
  692. )}
  693. {/* </Grid> */}
  694. </Box>
  695. {/* <CardActions sx={{ justifyContent: "flex-end" }}>
  696. <Button variant="text" startIcon={<RestartAlt />}>
  697. {t("Reset")}
  698. </Button>
  699. </CardActions> */}
  700. </CardContent>
  701. </Card>
  702. );
  703. };
  704. export default ProjectClientDetails;