From e321aa2f5d21d35de7d7cb5c154b273456af6b08 Mon Sep 17 00:00:00 2001 From: Henry Date: Sat, 1 Feb 2025 11:37:16 +0000 Subject: [PATCH] feat: update display name --- .../src/views/settings/components/Avatar.tsx | 5 ++ .../DeleteWorkspaceConfirmation.tsx | 5 ++ .../components/UpdateDisplayNameForm.tsx | 83 +++++++++++++++++++ .../UpdateWorkspaceDescriptionForm.tsx | 5 ++ .../components/UpdateWorkspaceNameForm.tsx | 5 ++ .../components/UpdateWorkspaceUrlForm.tsx | 5 ++ apps/web/src/views/settings/index.tsx | 6 ++ packages/api/src/routers/user.ts | 4 +- packages/db/src/repository/user.repo.ts | 6 +- 9 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 apps/web/src/views/settings/components/UpdateDisplayNameForm.tsx diff --git a/apps/web/src/views/settings/components/Avatar.tsx b/apps/web/src/views/settings/components/Avatar.tsx index 479fac09..0bc059ca 100644 --- a/apps/web/src/views/settings/components/Avatar.tsx +++ b/apps/web/src/views/settings/components/Avatar.tsx @@ -20,6 +20,11 @@ export default function Avatar({ const updateUser = api.user.update.useMutation({ onSuccess: async () => { + showPopup({ + header: "Profile image updated", + message: "Your profile image has been updated.", + icon: "success", + }); try { await utils.user.getUser.refetch(); } catch (e) { diff --git a/apps/web/src/views/settings/components/DeleteWorkspaceConfirmation.tsx b/apps/web/src/views/settings/components/DeleteWorkspaceConfirmation.tsx index 97603c70..ceb62f25 100644 --- a/apps/web/src/views/settings/components/DeleteWorkspaceConfirmation.tsx +++ b/apps/web/src/views/settings/components/DeleteWorkspaceConfirmation.tsx @@ -18,6 +18,11 @@ export function DeleteWorkspaceConfirmation() { const deleteWorkspaceMutation = api.workspace.delete.useMutation({ onSuccess: () => { closeModal(); + showPopup({ + header: "Workspace deleted", + message: "Your workspace has been deleted.", + icon: "success", + }); const filteredWorkspaces = availableWorkspaces.filter( (ws) => ws.publicId !== workspace.publicId, ); diff --git a/apps/web/src/views/settings/components/UpdateDisplayNameForm.tsx b/apps/web/src/views/settings/components/UpdateDisplayNameForm.tsx new file mode 100644 index 00000000..c05d1b07 --- /dev/null +++ b/apps/web/src/views/settings/components/UpdateDisplayNameForm.tsx @@ -0,0 +1,83 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; + +import Button from "~/components/Button"; +import Input from "~/components/Input"; +import { usePopup } from "~/providers/popup"; +import { api } from "~/utils/api"; + +const schema = z.object({ + name: z + .string() + .min(3, { + message: "Display namemust be at least 3 characters long", + }) + .max(280, { + message: "Display name cannot exceed 280 characters", + }), +}); + +type FormValues = z.infer; + +const UpdateDisplayNameForm = ({ displayName }: { displayName: string }) => { + const utils = api.useUtils(); + const { showPopup } = usePopup(); + const { + register, + handleSubmit, + formState: { isDirty, errors }, + } = useForm({ + resolver: zodResolver(schema), + values: { + name: displayName, + }, + }); + + const updateDisplayName = api.user.update.useMutation({ + onSuccess: async () => { + showPopup({ + header: "Display name updated", + message: "Your display name has been updated.", + icon: "success", + }); + try { + await utils.user.getUser.refetch(); + } catch (e) { + console.error(e); + throw e; + } + }, + onError: () => { + showPopup({ + header: "Error updating display name", + message: "Please try again later, or contact customer support.", + icon: "error", + }); + }, + }); + + const onSubmit = (data: FormValues) => { + updateDisplayName.mutate({ + name: data.name, + }); + }; + + return ( + <> +
+ +
+ + + ); +}; + +export default UpdateDisplayNameForm; diff --git a/apps/web/src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx b/apps/web/src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx index 3445f239..2cfa46b6 100644 --- a/apps/web/src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx +++ b/apps/web/src/views/settings/components/UpdateWorkspaceDescriptionForm.tsx @@ -42,6 +42,11 @@ const UpdateWorkspaceDescriptionForm = ({ const updateWorkspaceDescription = api.workspace.update.useMutation({ onSuccess: async () => { + showPopup({ + header: "Workspace description updated", + message: "Your workspace description has been updated.", + icon: "success", + }); try { await utils.workspace.all.refetch(); } catch (e) { diff --git a/apps/web/src/views/settings/components/UpdateWorkspaceNameForm.tsx b/apps/web/src/views/settings/components/UpdateWorkspaceNameForm.tsx index 7f4281b2..67bd196d 100644 --- a/apps/web/src/views/settings/components/UpdateWorkspaceNameForm.tsx +++ b/apps/web/src/views/settings/components/UpdateWorkspaceNameForm.tsx @@ -38,6 +38,11 @@ const UpdateWorkspaceNameForm = ({ const updateWorkspaceName = api.workspace.update.useMutation({ onSuccess: async () => { + showPopup({ + header: "Workspace name updated", + message: "Your workspace name has been updated.", + icon: "success", + }); try { await utils.workspace.all.refetch(); } catch (e) { diff --git a/apps/web/src/views/settings/components/UpdateWorkspaceUrlForm.tsx b/apps/web/src/views/settings/components/UpdateWorkspaceUrlForm.tsx index 0ad4bc46..be52b660 100644 --- a/apps/web/src/views/settings/components/UpdateWorkspaceUrlForm.tsx +++ b/apps/web/src/views/settings/components/UpdateWorkspaceUrlForm.tsx @@ -53,6 +53,11 @@ const UpdateWorkspaceUrlForm = ({ const updateWorkspaceSlug = api.workspace.update.useMutation({ onSuccess: async () => { + showPopup({ + header: "Workspace slug updated", + message: "Your workspace slug has been updated.", + icon: "success", + }); try { await utils.workspace.all.refetch(); } catch (e) { diff --git a/apps/web/src/views/settings/index.tsx b/apps/web/src/views/settings/index.tsx index 5a5acb07..ecbe326f 100644 --- a/apps/web/src/views/settings/index.tsx +++ b/apps/web/src/views/settings/index.tsx @@ -10,6 +10,7 @@ import { api } from "~/utils/api"; import Avatar from "./components/Avatar"; import { CustomURLConfirmation } from "./components/CustomURLConfirmation"; import { DeleteWorkspaceConfirmation } from "./components/DeleteWorkspaceConfirmation"; +import UpdateDisplayNameForm from "./components/UpdateDisplayNameForm"; import UpdateWorkspaceDescriptionForm from "./components/UpdateWorkspaceDescriptionForm"; import UpdateWorkspaceNameForm from "./components/UpdateWorkspaceNameForm"; import UpdateWorkspaceUrlForm from "./components/UpdateWorkspaceUrlForm"; @@ -56,6 +57,11 @@ export default function SettingsPage() { Profile picture + +

+ Display name +

+
diff --git a/packages/api/src/routers/user.ts b/packages/api/src/routers/user.ts index 5abb8b69..e40cf337 100644 --- a/packages/api/src/routers/user.ts +++ b/packages/api/src/routers/user.ts @@ -62,11 +62,13 @@ export const userRouter = createTRPCRouter({ }) .input( z.object({ - image: z.string(), + name: z.string().optional(), + image: z.string().optional(), }), ) .output( z.object({ + name: z.string().nullable(), image: z.string().nullable(), }), ) diff --git a/packages/db/src/repository/user.repo.ts b/packages/db/src/repository/user.repo.ts index 6fecdf9b..05742ff5 100644 --- a/packages/db/src/repository/user.repo.ts +++ b/packages/db/src/repository/user.repo.ts @@ -48,13 +48,13 @@ export const create = async ( export const update = async ( db: SupabaseClient, userId: string, - updates: { image: string | null }, + updates: { image?: string; name?: string }, ) => { const { data } = await db .from("user") - .update({ image: updates.image }) + .update({ image: updates.image, name: updates.name }) .eq("id", userId) - .select(`image`) + .select(`image, name`) .single(); return data;