diff --git a/apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-details-form.tsx b/apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-details-form.tsx index 3505369d395..e5f15db5730 100644 --- a/apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-details-form.tsx +++ b/apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/profile-details-form.tsx @@ -4,6 +4,11 @@ import { hasPermission } from "@/lib/auth/partner-users/partner-user-permissions import { mutatePrefix } from "@/lib/swr/mutate"; import usePartnerPayoutsCount from "@/lib/swr/use-partner-payouts-count"; import { PartnerProps } from "@/lib/types"; +import { + IdentitySyncField, + IdentitySyncSnapshot, + useIdentitySyncConfirmModal, +} from "@/ui/modals/identity-sync-confirm-modal"; import { CountryCombobox } from "@/ui/partners/country-combobox"; import { PartnerPlatformsForm, @@ -22,8 +27,16 @@ import { import { OG_AVATAR_URL, cn } from "@dub/utils"; import { PartnerProfileType } from "@prisma/client"; import { AnimatePresence, LayoutGroup, motion } from "motion/react"; +import { useSession } from "next-auth/react"; import { useAction } from "next-safe-action/hooks"; -import { Dispatch, RefObject, SetStateAction, useEffect, useRef } from "react"; +import { + Dispatch, + RefObject, + SetStateAction, + useEffect, + useRef, + useState, +} from "react"; import { Controller, FormProvider, @@ -44,6 +57,39 @@ type BasicInfoFormData = { companyName: string | null; }; +type PendingProfileSubmit = { + data: BasicInfoFormData; + imageChanged: boolean; +}; + +function getProfileSyncCandidates({ + data, + partner, + user, + imageChanged, +}: { + data: BasicInfoFormData; + partner?: PartnerProps; + user?: { name?: string | null; email?: string | null; image?: string | null }; + imageChanged: boolean; +}) { + const candidates: IdentitySyncField[] = []; + + if (data.name !== partner?.name && data.name !== user?.name) { + candidates.push("name"); + } + + if (data.email !== partner?.email && data.email !== user?.email) { + candidates.push("email"); + } + + if (imageChanged && data.image !== user?.image) { + candidates.push("image"); + } + + return candidates; +} + export function ProfileDetailsForm({ partner, setShowMergePartnerAccountsModal, @@ -182,18 +228,19 @@ function BasicInfoForm({ }, [isSubmitSuccessful, reset, getValues]); const { profileType } = watch(); + const { data: session, update: updateSession } = useSession(); + const pendingSubmitRef = useRef(null); + const [syncModalContent, setSyncModalContent] = useState<{ + changedFields: IdentitySyncField[]; + current: IdentitySyncSnapshot; + next: IdentitySyncSnapshot; + }>({ + changedFields: [], + current: {}, + next: {}, + }); const { executeAsync } = useAction(updatePartnerProfileAction, { - onSuccess: async ({ data }) => { - if (data?.needsEmailVerification) { - toast.success( - "Please check your email to verify your new email address.", - ); - } else { - toast.success("Your profile has been updated."); - } - mutatePrefix("/api/partner-profile"); - }, onError({ error }) { if (error.validationErrors) { toast.error(parseActionError(error, "Could not update your profile.")); @@ -217,249 +264,346 @@ function BasicInfoForm({ }, }); + const saveProfile = async ({ + data, + imageChanged, + syncIdentity, + }: PendingProfileSubmit & { syncIdentity: boolean }) => { + const result = await executeAsync({ + ...data, + username: data.username || undefined, + ...(imageChanged ? { image: data.image } : {}), + syncIdentity, + }); + + if (!result?.data) { + return; + } + + if (result.data.needsEmailVerification) { + toast.success( + "Please check your email to verify your new email address.", + ); + } else { + toast.success("Your profile has been updated."); + } + + setTimeout(() => { + mutatePrefix("/api/partner-profile"); + }, 0); + + if (syncIdentity) { + await updateSession(); + } + }; + + const submitProfile = async (syncIdentity: boolean) => { + const pending = pendingSubmitRef.current; + + if (!pending) { + return; + } + + try { + await saveProfile({ ...pending, syncIdentity }); + } finally { + pendingSubmitRef.current = null; + } + }; + + const { setShowModal: setShowConfirmModal, confirmModal } = + useIdentitySyncConfirmModal({ + title: "Also update your user account?", + intro: + "You're updating your partner account. These fields differ from your user account:", + changedFields: syncModalContent.changedFields, + current: syncModalContent.current, + next: syncModalContent.next, + onConfirm: async () => { + await submitProfile(true); + }, + onCancel: async () => { + await submitProfile(false); + }, + }); + return ( -
{ - if (e.key !== "Enter") return; - - const target = e.target as HTMLElement; - if (target.tagName !== "INPUT") return; - - e.preventDefault(); - onSubmitAction(); - }} - onSubmit={handleSubmit(async (data) => { - const imageChanged = data.image !== partner?.image; - - await executeAsync({ - ...data, - username: data.username || undefined, - image: imageChanged ? data.image : null, - }); - })} - > -
-
+ + ); +} + +export function useIdentitySyncConfirmModal( + props: IdentitySyncConfirmModalProps, +) { + const [showModal, setShowModal] = useState(false); + + return { + setShowModal, + confirmModal: ( + + ), + }; +} diff --git a/packages/email/src/templates/email-updated.tsx b/packages/email/src/templates/email-updated.tsx index 79e2df98424..b8e9ccbebfb 100644 --- a/packages/email/src/templates/email-updated.tsx +++ b/packages/email/src/templates/email-updated.tsx @@ -18,11 +18,19 @@ export default function EmailUpdated({ oldEmail = "panic@thedis.co", newEmail = "panic@thedis.co", isPartnerProfile = false, + syncIdentity = false, }: { oldEmail: string; newEmail: string; isPartnerProfile?: boolean; + syncIdentity?: boolean; }) { + const profileLabel = syncIdentity + ? "user account and partner account" + : isPartnerProfile + ? "partner account" + : "account"; + return ( @@ -37,10 +45,8 @@ export default function EmailUpdated({ Your email address has been changed - The e-mail address for your Dub{" "} - {isPartnerProfile ? "partner profile" : "account"} has been - changed from {oldEmail} to{" "} - {newEmail}. + The e-mail address for your Dub {profileLabel} has been changed + from {oldEmail} to {newEmail}. If you did not make this change, please contact our support team