Files
web/apps/scandic-web/actions/editProfile.ts
Anton Gunnarsson c56a0b8ce9 Merged in feat/sw-1975-get-profile-v2 (pull request #1651)
Use get Profile V2 endpoint

Approved-by: Linus Flood
2025-04-08 06:26:00 +00:00

223 lines
6.2 KiB
TypeScript

"use server"
import { z } from "zod"
import { countriesMap } from "@/constants/countries"
import { ApiLang } from "@/constants/languages"
import * as api from "@/lib/api"
import { getProfile } from "@/lib/trpc/memoizedRequests"
import { protectedServerActionProcedure } from "@/server/trpc"
import { editProfileSchema } from "@/components/Forms/Edit/Profile/schema"
import { getIntl } from "@/i18n"
import { phoneValidator } from "@/utils/zod/phoneValidator"
import { Status } from "@/types/components/myPages/myProfile/edit"
const editProfilePayload = z
.object({
address: z.object({
city: z.string().optional(),
countryCode: z.nativeEnum(countriesMap),
streetAddress: z.string().optional(),
zipCode: z.string().min(1, { message: "Zip code is required" }),
}),
dateOfBirth: z.string(),
email: z.string().email(),
language: z.nativeEnum(ApiLang),
newPassword: z.string().optional(),
password: z.string().optional(),
phoneNumber: phoneValidator("Phone is required"),
})
.transform((data) => {
if (!data.password || !data.newPassword) {
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: intl.formatMessage({
id: "An error occured when trying to update profile.",
}),
status: Status.error,
}
}
const profile = await getProfile()
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<z.infer<typeof editProfilePayload>> = {}
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
}
// Handle case where dateOfBirth is missing in profile and not updated in form
if (
typedKey === "dateOfBirth" &&
!profile.dateOfBirth &&
payload.data.dateOfBirth === "1900-01-01"
) {
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,
}
} else {
console.log(
`[edit profile: ${profile.membershipNumber}] body keys: ${JSON.stringify(Object.keys(body))}`
)
}
const apiResponse = await api.patch(api.endpoints.v1.Profile.profile, {
body,
cache: "no-store",
headers: {
Authorization: `Bearer ${ctx.session.token.access_token}`,
},
})
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: intl.formatMessage({
id: "An error occured when trying to update profile.",
}),
status: Status.error,
}
}
const json = await apiResponse.json()
if (json.errors?.length) {
json.errors.forEach((error: any) => {
console.warn(
"editProfile api patch errors (not aborting)",
JSON.stringify({
query: input,
error,
})
)
})
}
const validatedData = editProfileSchema.safeParse(json.data.attributes)
if (!validatedData.success) {
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: intl.formatMessage({
id: "An error occured when trying to update profile.",
}),
status: Status.error,
}
}
return {
data: validatedData.data,
message: intl.formatMessage({ id: "Successfully updated profile!" }),
status: Status.success,
}
})