From ae751402d675c0bcf613c7877976bc2b86ab9df3 Mon Sep 17 00:00:00 2001 From: Simon Emanuelsson Date: Fri, 23 Aug 2024 15:53:48 +0200 Subject: [PATCH] fix: only send updated fields to API --- actions/editProfile.ts | 135 +++++++++++++++++++++--- components/Forms/Edit/Profile/index.tsx | 10 +- 2 files changed, 124 insertions(+), 21 deletions(-) diff --git a/actions/editProfile.ts b/actions/editProfile.ts index b09e852fb..ab05e949c 100644 --- a/actions/editProfile.ts +++ b/actions/editProfile.ts @@ -4,10 +4,12 @@ import { z } from "zod" import { ApiLang } from "@/constants/languages" import * as api from "@/lib/api" +import { serverClient } from "@/lib/trpc/server" import { protectedServerActionProcedure } from "@/server/trpc" import { editProfileSchema } from "@/components/Forms/Edit/Profile/schema" import { countriesMap } from "@/components/TempDesignSystem/Form/Country/countries" +import { getIntl } from "@/i18n" import { phoneValidator } from "@/utils/phoneValidator" import { Status } from "@/types/components/myPages/myProfile/edit" @@ -32,69 +34,176 @@ const editProfilePayload = z delete data.password delete data.newPassword } + if (data.phoneNumber) { + data.phoneNumber = data.phoneNumber.replaceAll(" ", "").trim() + } return data }) export const editProfile = protectedServerActionProcedure .input(editProfileSchema) .mutation(async function ({ ctx, input }) { + const intl = await getIntl() const payload = editProfilePayload.safeParse(input) if (!payload.success) { + console.error( + "editProfile payload validation error", + JSON.stringify({ + query: input, + error: payload.error, + }) + ) + return { data: input, issues: payload.error.issues.map((issue) => ({ field: issue.path.join("."), message: issue.message, })), - message: "Validation failed.", + message: intl.formatMessage({ + id: "An error occured when trying to update profile.", + }), status: Status.error, } } - const response = await api.patch(api.endpoints.v1.profile, { - body: payload.data, + const profile = await serverClient().user.get({ mask: false }) + if (!profile || "error" in profile) { + console.error( + "editProfile profile fetch error", + JSON.stringify({ + query: input, + error: profile?.error, + }) + ) + + return { + data: input, + issues: [], + message: intl.formatMessage({ + id: "An error occured when trying to update profile.", + }), + status: Status.error, + } + } + + const body: Partial> = {} + Object.keys(payload.data).forEach((key) => { + const typedKey = key as unknown as keyof z.infer< + typeof editProfilePayload + > + if (typedKey === "password" || typedKey === "newPassword") { + body[typedKey] = payload.data[typedKey] + return + } + + if (typedKey === "address") { + if ( + (payload.data.address.city === profile.address.city || + (!payload.data.address.city && !profile.address.city)) && + (payload.data.address.countryCode === profile.address.countryCode || + (!payload.data.address.countryCode && + !profile.address.countryCode)) && + (payload.data.address.streetAddress === + profile.address.streetAddress || + (!payload.data.address.streetAddress && + !profile.address.streetAddress)) && + (payload.data.address.zipCode === profile.address.zipCode || + (!payload.data.address.zipCode && !profile.address.zipCode)) + ) { + // untouched - noop + } else { + /** API clears all other fields not sent in address payload */ + body.address = payload.data.address + } + return + } + + if (payload.data[typedKey] !== profile[typedKey]) { + // @ts-ignore + body[typedKey] = payload.data[typedKey] + } + }) + + if (Object.keys(body).length === 0) { + return { + data: input, + message: intl.formatMessage({ id: "Successfully updated profile!" }), + status: Status.success, + } + } + + const apiResponse = await api.patch(api.endpoints.v1.profile, { + body, cache: "no-store", headers: { Authorization: `Bearer ${ctx.session.token.access_token}`, }, }) - if (!response.ok) { - console.info(`Response not ok`) - console.error(response) + if (!apiResponse.ok) { + const text = await apiResponse.text() + console.error( + "editProfile api patch error", + JSON.stringify({ + query: input, + error: { + status: apiResponse.status, + statusText: apiResponse.statusText, + error: text, + }, + }) + ) + return { data: input, issues: [], - message: "Server error", + message: intl.formatMessage({ + id: "An error occured when trying to update profile.", + }), status: Status.error, } } - const json = await response.json() + const json = await apiResponse.json() if (json.errors?.length) { json.errors.forEach((error: any) => { - console.info(`API Fail in response`) - console.error(error) + console.warn( + "editProfile api patch errors (not aborting)", + JSON.stringify({ + query: input, + error, + }) + ) }) } const validatedData = editProfileSchema.safeParse(json.data.attributes) if (!validatedData.success) { - console.log({ ees: validatedData.error }) + console.error( + "editProfile validation error", + JSON.stringify({ + query: input, + error: validatedData.error, + }) + ) + return { data: input, issues: validatedData.error.issues.map((issue) => ({ field: issue.path.join("."), message: issue.message, })), - message: "Data is insufficient", + message: intl.formatMessage({ + id: "An error occured when trying to update profile.", + }), status: Status.error, } } return { data: validatedData.data, - message: "All good!", + message: intl.formatMessage({ id: "Successfully updated profile!" }), status: Status.success, } }) diff --git a/components/Forms/Edit/Profile/index.tsx b/components/Forms/Edit/Profile/index.tsx index faf69db57..1332f54ee 100644 --- a/components/Forms/Edit/Profile/index.tsx +++ b/components/Forms/Edit/Profile/index.tsx @@ -82,16 +82,10 @@ export default function Form({ user }: EditFormProps) { console.error(issue) }) } - toast.error( - intl.formatMessage({ - id: "An error occured when trying to update profile.", - }) - ) + toast.error(response.message) break case Status.success: - toast.success( - intl.formatMessage({ id: "Successfully updated profile!" }) - ) + toast.success(response.message) utils.user.get.invalidate() router.push(profile[lang]) break