Skip to content

Commit

Permalink
Implement member profile information updates
Browse files Browse the repository at this point in the history
  • Loading branch information
bombies committed Oct 31, 2023
1 parent 7ba1207 commit 6a5c649
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,28 @@ import toast from "react-hot-toast";
import UpdateSelfMember from "@/app/(site)/hooks/user/UpdateSelfMember";
import {PatchSelfDto} from "@/app/api/me/self-user.dto";
import {handleAxiosError} from "@/utils/client/client-utils";
import {Member} from "@prisma/client";
import {AxiosError} from "axios";

const EditableUserProfile: FC = () => {
const {trigger: update, isMutating: isUpdating} = UpdateSelfMember()
const {trigger: update} = UpdateSelfMember()
const [optimisticAvatarSrc, setOptimisticAvatarSrc] = useState<string>()
const {
memberData: {
data: member,
loading: memberDataLoading,
mutateData: mutateMemberData,
optimisticData: {
editOptimisticData: editMemberData
}
}
} = useMemberData()

const doUpdate = useCallback(async (dto: PatchSelfDto) => (
update({body: dto})
const doUpdate = useCallback(async (dto: PatchSelfDto, handleError?: boolean): Promise<Member | null | undefined> => {
const work = update({body: dto})
.then(res => res.data)
.catch(handleAxiosError)
), [update])
return handleError ? work.catch(handleAxiosError) : work
}, [update])

return (
<Card
Expand Down Expand Up @@ -66,7 +69,7 @@ const EditableUserProfile: FC = () => {
onUploadSuccess={async (key) => {
if (editMemberData)
await editMemberData(
() => doUpdate({image: key}),
() => doUpdate({image: key}, true),
{
...member!,
image: key
Expand Down Expand Up @@ -105,8 +108,27 @@ const EditableUserProfile: FC = () => {
},
errorMsg: "Invalid username!"
}}
onEdit={(newValue) => {
onEdit={async (newValue) => {
if (!newValue)
return

const newUsername = newValue.toLowerCase()
if (newUsername === member!.username)
return;

await toast.promise(doUpdate({username: newUsername})
.then(async (res) => {
if (!res || !mutateMemberData)
return
await mutateMemberData(res)
})
.catch((err: AxiosError) => Promise.reject(err.response?.statusText ?? "Something went wrong!")), {
loading: "Updating username...",
success: "Successfully updated your username!",
error(err: string) {
return err
}
})
}}
>
<p className="flex gap-2">{member?.username} <EditIcon
Expand All @@ -123,8 +145,18 @@ const EditableUserProfile: FC = () => {
minLength={1}
maxLength={60}
size="sm"
onEdit={(newValue) => {
onEdit={async (newValue) => {
if (!newValue)
return

if (editMemberData)
await editMemberData(
() => doUpdate({firstName: newValue}, true),
{
...member!,
firstName: newValue
}
)
}}
>
<p className="capitalize flex gap-2">{member?.firstName} <EditIcon
Expand All @@ -140,8 +172,18 @@ const EditableUserProfile: FC = () => {
minLength={1}
maxLength={60}
size="sm"
onEdit={(newValue) => {
onEdit={async (newValue) => {
if (!newValue)
return

if (editMemberData)
await editMemberData(
() => doUpdate({lastName: newValue}, true),
{
...member!,
lastName: newValue
}
)
}}
>
<p className="capitalize flex gap-2">{member?.lastName} <EditIcon
Expand Down
2 changes: 2 additions & 0 deletions src/app/(site)/components/inputs/editable/EditableAvatar.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client"

import {FC} from "react";
import FileUpload, {FileUploadProps} from "@/app/(site)/components/FileUpload";
import MediaType from "@/app/api/utils/MediaType";
Expand Down
7 changes: 5 additions & 2 deletions src/app/(site)/components/inputs/editable/EditableInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,18 @@ const EditableInput: FC<Props> = ({isEditable, value, children, onEdit, ...input
else setEditToggled(false)
}, [isEditable, value])

const onSubmit: SubmitHandler<FormProps> = useCallback((data) => {
const onSubmit: SubmitHandler<FormProps> = useCallback((data, e) => {
if (inputProps.validate && !inputProps.validate.predicate(data.value))
return;

if (data.value === value)
return setEditToggled(false)

if (onEdit)
onEdit(data.value.length === 0 ? undefined : data.value)
setEditToggled(false)
setCurrentValue(value ?? "")
}, [onEdit, value])
}, [inputProps.validate, onEdit, value])

return (
<AnimatePresence>
Expand Down
2 changes: 2 additions & 0 deletions src/app/(site)/hooks/s3/useCloudFrontUrl.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client"

import {fetcher} from "@/utils/client/client-utils";
import useSWR from "swr";

Expand Down
5 changes: 4 additions & 1 deletion src/app/api/me/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ export const PATCH = async (req: Request) => {
return authenticated(async (session) =>
selfUserService.update(session, await req.json()), {
prismaErrors: {
recordNotFoundMessage: "Couldn't find your information!"
recordNotFoundMessage: "Couldn't find your information!",
uniqueConstraintFailed(target) {
return `There is already a user with that ${target}!`
}
}
}
)
Expand Down
34 changes: 25 additions & 9 deletions src/app/api/utils/api-utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {NextResponse} from "next/server";
import {getServerSession, Session} from "next-auth";
import authOptions from "@/app/api/auth/[...nextauth]/utils";
import {buildResponse, RouteResponseType} from "@/app/api/utils/types";
import {buildResponse} from "@/app/api/utils/types";
import {Member, Prisma} from "@prisma/client";
import prisma from "@/libs/prisma";
import PrismaClientKnownRequestError = Prisma.PrismaClientKnownRequestError;
Expand All @@ -10,10 +10,11 @@ export type RouteContext<T extends { [K: string]: string }> = {
params: T
}

export type IdObject = {id: string}
export type IdObject = { id: string }

type PrismaErrorOptions = {
recordNotFoundMessage?: string
recordNotFoundMessage?: string,
uniqueConstraintFailed?: string | ((target: string) => string),

}

Expand Down Expand Up @@ -43,9 +44,11 @@ export const authenticated = async (logic: (session: Session, member?: Member) =
status: 404,
message: "Couldn't find information for your user!"
})

return await logic(session, member)
}

return logic(session)
return await logic(session)
} catch (e) {
const prismaError = prismaErrorHandler(e, options?.prismaErrors)
if (prismaError)
Expand All @@ -61,11 +64,24 @@ export const authenticated = async (logic: (session: Session, member?: Member) =

const prismaErrorHandler = (e: any, options?: PrismaErrorOptions): NextResponse<null> | undefined => {
if (e instanceof PrismaClientKnownRequestError) {
if (e.code === "P2001")
return buildResponse({
status: 404,
message: options?.recordNotFoundMessage ?? `Couldn't find any records to complete execution!`
})
switch (e.code) {
case "P2001": {
return buildResponse({
status: 404,
message: options?.recordNotFoundMessage ?? `Couldn't find any records to complete execution!`
})
}
case "P2002": {
return buildResponse({
status: 400,
message: options?.uniqueConstraintFailed ?
(options.uniqueConstraintFailed instanceof Function
? options.uniqueConstraintFailed((e.meta!.target as string[])[0])
: options.uniqueConstraintFailed as string)
: `There is already a record with the value of that unique constraint!`
})
}
}
}
return undefined
}

0 comments on commit 6a5c649

Please sign in to comment.