From 0a600445889677169c9742baaaa14d8a3bf12da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Mon, 3 Feb 2025 19:12:04 +0100 Subject: [PATCH 1/6] feat: Add Edit Contact page with form functionality --- .../email/administration/contacts/edit.jsx | 236 ++++++++++++++++++ .../email/administration/contacts/index.js | 8 +- 2 files changed, 240 insertions(+), 4 deletions(-) create mode 100644 src/pages/email/administration/contacts/edit.jsx diff --git a/src/pages/email/administration/contacts/edit.jsx b/src/pages/email/administration/contacts/edit.jsx new file mode 100644 index 000000000000..12846ae3140a --- /dev/null +++ b/src/pages/email/administration/contacts/edit.jsx @@ -0,0 +1,236 @@ +import React, { useEffect } from "react"; +import { useRouter } from "next/router"; +import { Grid, Divider } from "@mui/material"; +import { useForm } from "react-hook-form"; +import { useSelector } from "react-redux"; +import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "/src/components/CippFormPages/CippFormPage"; +import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import { useSettings } from "../../../../hooks/use-settings"; +import { ApiGetCall } from "../../../../api/ApiCall"; + +const EditContact = () => { + const tenantDomain = useSettings().currentTenant; + const router = useRouter(); + const { id } = router.query; + + const contactInfo = ApiGetCall({ + url: `/api/ListContacts?tenantFilter=${tenantDomain}&id=${id}`, + queryKey: `ListContacts-${id}`, + waiting: false, + }); + + useEffect(() => { + if (id) { + contactInfo.refetch(); + } + }, [router.query, id, tenantDomain]); + + const formControl = useForm({ + mode: "onChange", + defaultValues: { + displayName: "", + firstName: "", + lastName: "", + email: "", + hidefromGAL: false, + streetAddress: "", + postalCode: "", + city: "", + country: "", + companyName: "", + mobilePhone: "", + businessPhone: "", + jobTitle: "", + }, + }); + + useEffect(() => { + if (contactInfo.isSuccess && contactInfo.data?.Results?.[0]) { + const contact = contactInfo.data.Results[0]; + formControl.reset({ + displayName: contact.displayName || "", + firstName: contact.firstName || "", + lastName: contact.lastName || "", + email: contact.mail || "", + hidefromGAL: contact.hidefromGAL || false, + streetAddress: contact.streetAddress || "", + postalCode: contact.postalCode || "", + city: contact.city || "", + country: contact.countryOrRegion || "", + companyName: contact.companyName || "", + mobilePhone: contact.mobilePhone || "", + businessPhone: contact.phone || "", + jobTitle: contact.jobTitle || "", + }); + } + }, [contactInfo.isSuccess, contactInfo.data, contactInfo.isFetching]); + + if (contactInfo.isLoading) { + return
Loading...
; + } + + return ( + { + return { + tenantID: tenantDomain, + firstName: values.firstName, + lastName: values.lastName, + displayName: values.displayName, + mail: values.email, + hidefromGAL: values.hidefromGAL, + ContactID: contactInfo.data?.Results?.[0]?.id, + StreetAddress: values.streetAddress, + PostalCode: values.postalCode, + City: values.city, + Country: values.country, + companyName: values.companyName, + MobilePhone: values.mobilePhone, + BusinessPhone: values.businessPhone, + jobTitle: values.jobTitle, + }; + }} + > + + {/* Display Name */} + + + + + {/* First Name and Last Name */} + + + + + + + + + + {/* Email */} + + + + + {/* Hide from GAL */} + + + + + + + {/* Company Information */} + + + + + + + + + + {/* Address Information */} + + + + + + + + + + + + + + + + {/* Phone Numbers */} + + + + + + + + + ); +}; + +EditContact.getLayout = (page) => {page}; + +export default EditContact; diff --git a/src/pages/email/administration/contacts/index.js b/src/pages/email/administration/contacts/index.js index ac22d6596579..e08ab4e7d785 100644 --- a/src/pages/email/administration/contacts/index.js +++ b/src/pages/email/administration/contacts/index.js @@ -3,7 +3,7 @@ import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx" import { Edit, PersonAdd } from "@mui/icons-material"; import { Button } from "@mui/material"; import Link from "next/link"; -import TrashIcon from '@heroicons/react/24/outline/TrashIcon'; +import TrashIcon from "@heroicons/react/24/outline/TrashIcon"; const Page = () => { const pageTitle = "Contacts"; @@ -20,14 +20,14 @@ const Page = () => { color: "danger", icon: , }, - /* TODO: Implement edit contact { label: "Edit Contact", - link: "/email/administration/edit-contact/[id]", + link: "/email/administration/contacts/edit?id={id}", multiPost: false, + postEntireRow: true, icon: , color: "warning", - },*/ + }, ]; const simpleColumns = ["displayName", "mail", "companyName", "onPremisesSyncEnabled"]; From e6ad713b6ab85d7569a8f370606b49feaf107592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Mon, 3 Feb 2025 23:42:15 +0100 Subject: [PATCH 2/6] move up --- .../email/administration/contacts/index.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/pages/email/administration/contacts/index.js b/src/pages/email/administration/contacts/index.js index e08ab4e7d785..8d512604501f 100644 --- a/src/pages/email/administration/contacts/index.js +++ b/src/pages/email/administration/contacts/index.js @@ -9,6 +9,15 @@ const Page = () => { const pageTitle = "Contacts"; const actions = [ + , + { + label: "Edit Contact", + link: "/email/administration/contacts/edit?id=[id]", + multiPost: false, + postEntireRow: true, + icon: , + color: "warning", + }, { label: "Remove Contact", type: "GET", @@ -20,14 +29,6 @@ const Page = () => { color: "danger", icon: , }, - { - label: "Edit Contact", - link: "/email/administration/contacts/edit?id={id}", - multiPost: false, - postEntireRow: true, - icon: , - color: "warning", - }, ]; const simpleColumns = ["displayName", "mail", "companyName", "onPremisesSyncEnabled"]; From b74a81f33318afac9c7e65a067f1d38c0a7b6ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Tue, 4 Feb 2025 01:29:44 +0100 Subject: [PATCH 3/6] make it actually work --- .../email/administration/contacts/edit.jsx | 52 +++++++++++-------- .../email/administration/contacts/index.js | 6 ++- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/pages/email/administration/contacts/edit.jsx b/src/pages/email/administration/contacts/edit.jsx index 12846ae3140a..56bf1e557f6e 100644 --- a/src/pages/email/administration/contacts/edit.jsx +++ b/src/pages/email/administration/contacts/edit.jsx @@ -46,21 +46,29 @@ const EditContact = () => { }); useEffect(() => { - if (contactInfo.isSuccess && contactInfo.data?.Results?.[0]) { - const contact = contactInfo.data.Results[0]; + if (contactInfo.isSuccess && contactInfo.data?.[0]) { + const contact = contactInfo.data[0]; + // Get the address info from the first address entry + const address = contact.addresses?.[0] || {}; + + // Find phone numbers by type + const phones = contact.phones || []; + const mobilePhone = phones.find((p) => p.type === "mobile")?.number; + const businessPhone = phones.find((p) => p.type === "business")?.number; + formControl.reset({ displayName: contact.displayName || "", - firstName: contact.firstName || "", - lastName: contact.lastName || "", + firstName: contact.givenName || "", + lastName: contact.surname || "", email: contact.mail || "", hidefromGAL: contact.hidefromGAL || false, - streetAddress: contact.streetAddress || "", - postalCode: contact.postalCode || "", - city: contact.city || "", - country: contact.countryOrRegion || "", + streetAddress: address.street || "", + postalCode: address.postalCode || "", + city: address.city || "", + country: address.countryOrRegion || "", companyName: contact.companyName || "", - mobilePhone: contact.mobilePhone || "", - businessPhone: contact.phone || "", + mobilePhone: mobilePhone || "", + businessPhone: businessPhone || "", jobTitle: contact.jobTitle || "", }); } @@ -74,26 +82,28 @@ const EditContact = () => { { return { tenantID: tenantDomain, - firstName: values.firstName, - lastName: values.lastName, - displayName: values.displayName, - mail: values.email, + ContactID: contactInfo.data?.[0]?.id, + DisplayName: values.displayName, hidefromGAL: values.hidefromGAL, - ContactID: contactInfo.data?.Results?.[0]?.id, + email: values.email, + FirstName: values.firstName, + LastName: values.lastName, + Title: values.jobTitle, StreetAddress: values.streetAddress, PostalCode: values.postalCode, City: values.city, - Country: values.country, - companyName: values.companyName, - MobilePhone: values.mobilePhone, - BusinessPhone: values.businessPhone, - jobTitle: values.jobTitle, + CountryOrRegion: values.country, + Company: values.companyName, + mobilePhone: values.mobilePhone, + phone: values.businessPhone, }; }} > diff --git a/src/pages/email/administration/contacts/index.js b/src/pages/email/administration/contacts/index.js index 8d512604501f..9d75996d8652 100644 --- a/src/pages/email/administration/contacts/index.js +++ b/src/pages/email/administration/contacts/index.js @@ -9,7 +9,6 @@ const Page = () => { const pageTitle = "Contacts"; const actions = [ - , { label: "Edit Contact", link: "/email/administration/contacts/edit?id=[id]", @@ -17,6 +16,7 @@ const Page = () => { postEntireRow: true, icon: , color: "warning", + condition: (row) => !row.onPremisesSyncEnabled, }, { label: "Remove Contact", @@ -25,9 +25,11 @@ const Page = () => { data: { GUID: "id", }, - confirmText: "Are you sure you want to delete this contact?", + confirmText: + "Are you sure you want to delete this contact? Remember this will not work if the contact is AD Synced.", color: "danger", icon: , + condition: (row) => !row.onPremisesSyncEnabled, }, ]; From 852fafa1618b8fde4478c8a3e9430295b1827244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Tue, 4 Feb 2025 17:14:12 +0100 Subject: [PATCH 4/6] Country selector --- src/pages/email/administration/contacts/edit.jsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/pages/email/administration/contacts/edit.jsx b/src/pages/email/administration/contacts/edit.jsx index 56bf1e557f6e..4981c10fe029 100644 --- a/src/pages/email/administration/contacts/edit.jsx +++ b/src/pages/email/administration/contacts/edit.jsx @@ -8,6 +8,7 @@ import CippFormPage from "/src/components/CippFormPages/CippFormPage"; import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; import { useSettings } from "../../../../hooks/use-settings"; import { ApiGetCall } from "../../../../api/ApiCall"; +import countryList from "/src/data/countryList.json"; const EditContact = () => { const tenantDomain = useSettings().currentTenant; @@ -65,7 +66,9 @@ const EditContact = () => { streetAddress: address.street || "", postalCode: address.postalCode || "", city: address.city || "", - country: address.countryOrRegion || "", + country: address.countryOrRegion + ? countryList.find((c) => c.Name === address.countryOrRegion)?.Code || "" + : "", companyName: contact.companyName || "", mobilePhone: mobilePhone || "", businessPhone: businessPhone || "", @@ -100,7 +103,7 @@ const EditContact = () => { StreetAddress: values.streetAddress, PostalCode: values.postalCode, City: values.city, - CountryOrRegion: values.country, + CountryOrRegion: values.country?.value || values.country, Company: values.companyName, mobilePhone: values.mobilePhone, phone: values.businessPhone, @@ -210,9 +213,14 @@ const EditContact = () => { ({ + label: Name, + value: Code, + }))} formControl={formControl} /> From fa301687464b8b402b6cc0da73a2f8e27d19c45a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Tue, 4 Feb 2025 18:06:05 +0100 Subject: [PATCH 5/6] fix: Update label for GAL visibility and disable creatable option for country selection --- src/pages/email/administration/contacts/edit.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/email/administration/contacts/edit.jsx b/src/pages/email/administration/contacts/edit.jsx index 4981c10fe029..87e9febc70f9 100644 --- a/src/pages/email/administration/contacts/edit.jsx +++ b/src/pages/email/administration/contacts/edit.jsx @@ -163,7 +163,7 @@ const EditContact = () => { @@ -217,6 +217,7 @@ const EditContact = () => { label="Country" name="country" multiple={false} + creatable={false} options={countryList.map(({ Code, Name }) => ({ label: Name, value: Code, From f2bd99e2ffc68547b0742b094a596e5dfe7df137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Mon, 3 Feb 2025 19:12:04 +0100 Subject: [PATCH 6/6] feat: Add Edit Contact page with form functionality --- .../email/administration/contacts/edit.jsx | 255 ++++++++++++++++++ .../email/administration/contacts/index.js | 23 +- 2 files changed, 268 insertions(+), 10 deletions(-) create mode 100644 src/pages/email/administration/contacts/edit.jsx diff --git a/src/pages/email/administration/contacts/edit.jsx b/src/pages/email/administration/contacts/edit.jsx new file mode 100644 index 000000000000..87e9febc70f9 --- /dev/null +++ b/src/pages/email/administration/contacts/edit.jsx @@ -0,0 +1,255 @@ +import React, { useEffect } from "react"; +import { useRouter } from "next/router"; +import { Grid, Divider } from "@mui/material"; +import { useForm } from "react-hook-form"; +import { useSelector } from "react-redux"; +import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "/src/components/CippFormPages/CippFormPage"; +import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import { useSettings } from "../../../../hooks/use-settings"; +import { ApiGetCall } from "../../../../api/ApiCall"; +import countryList from "/src/data/countryList.json"; + +const EditContact = () => { + const tenantDomain = useSettings().currentTenant; + const router = useRouter(); + const { id } = router.query; + + const contactInfo = ApiGetCall({ + url: `/api/ListContacts?tenantFilter=${tenantDomain}&id=${id}`, + queryKey: `ListContacts-${id}`, + waiting: false, + }); + + useEffect(() => { + if (id) { + contactInfo.refetch(); + } + }, [router.query, id, tenantDomain]); + + const formControl = useForm({ + mode: "onChange", + defaultValues: { + displayName: "", + firstName: "", + lastName: "", + email: "", + hidefromGAL: false, + streetAddress: "", + postalCode: "", + city: "", + country: "", + companyName: "", + mobilePhone: "", + businessPhone: "", + jobTitle: "", + }, + }); + + useEffect(() => { + if (contactInfo.isSuccess && contactInfo.data?.[0]) { + const contact = contactInfo.data[0]; + // Get the address info from the first address entry + const address = contact.addresses?.[0] || {}; + + // Find phone numbers by type + const phones = contact.phones || []; + const mobilePhone = phones.find((p) => p.type === "mobile")?.number; + const businessPhone = phones.find((p) => p.type === "business")?.number; + + formControl.reset({ + displayName: contact.displayName || "", + firstName: contact.givenName || "", + lastName: contact.surname || "", + email: contact.mail || "", + hidefromGAL: contact.hidefromGAL || false, + streetAddress: address.street || "", + postalCode: address.postalCode || "", + city: address.city || "", + country: address.countryOrRegion + ? countryList.find((c) => c.Name === address.countryOrRegion)?.Code || "" + : "", + companyName: contact.companyName || "", + mobilePhone: mobilePhone || "", + businessPhone: businessPhone || "", + jobTitle: contact.jobTitle || "", + }); + } + }, [contactInfo.isSuccess, contactInfo.data, contactInfo.isFetching]); + + if (contactInfo.isLoading) { + return
Loading...
; + } + + return ( + { + return { + tenantID: tenantDomain, + ContactID: contactInfo.data?.[0]?.id, + DisplayName: values.displayName, + hidefromGAL: values.hidefromGAL, + email: values.email, + FirstName: values.firstName, + LastName: values.lastName, + Title: values.jobTitle, + StreetAddress: values.streetAddress, + PostalCode: values.postalCode, + City: values.city, + CountryOrRegion: values.country?.value || values.country, + Company: values.companyName, + mobilePhone: values.mobilePhone, + phone: values.businessPhone, + }; + }} + > + + {/* Display Name */} + + + + + {/* First Name and Last Name */} + + + + + + + + + + {/* Email */} + + + + + {/* Hide from GAL */} + + + + + + + {/* Company Information */} + + + + + + + + + + {/* Address Information */} + + + + + + + + + + + ({ + label: Name, + value: Code, + }))} + formControl={formControl} + /> + + + + + {/* Phone Numbers */} + + + + + + + + + ); +}; + +EditContact.getLayout = (page) => {page}; + +export default EditContact; diff --git a/src/pages/email/administration/contacts/index.js b/src/pages/email/administration/contacts/index.js index ac22d6596579..9d75996d8652 100644 --- a/src/pages/email/administration/contacts/index.js +++ b/src/pages/email/administration/contacts/index.js @@ -3,12 +3,21 @@ import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx" import { Edit, PersonAdd } from "@mui/icons-material"; import { Button } from "@mui/material"; import Link from "next/link"; -import TrashIcon from '@heroicons/react/24/outline/TrashIcon'; +import TrashIcon from "@heroicons/react/24/outline/TrashIcon"; const Page = () => { const pageTitle = "Contacts"; const actions = [ + { + label: "Edit Contact", + link: "/email/administration/contacts/edit?id=[id]", + multiPost: false, + postEntireRow: true, + icon: , + color: "warning", + condition: (row) => !row.onPremisesSyncEnabled, + }, { label: "Remove Contact", type: "GET", @@ -16,18 +25,12 @@ const Page = () => { data: { GUID: "id", }, - confirmText: "Are you sure you want to delete this contact?", + confirmText: + "Are you sure you want to delete this contact? Remember this will not work if the contact is AD Synced.", color: "danger", icon: , + condition: (row) => !row.onPremisesSyncEnabled, }, - /* TODO: Implement edit contact - { - label: "Edit Contact", - link: "/email/administration/edit-contact/[id]", - multiPost: false, - icon: , - color: "warning", - },*/ ]; const simpleColumns = ["displayName", "mail", "companyName", "onPremisesSyncEnabled"];