diff --git a/apps/portal/src/components/global/AppShell.tsx b/apps/portal/src/components/global/AppShell.tsx index 9f37b355..c601257b 100644 --- a/apps/portal/src/components/global/AppShell.tsx +++ b/apps/portal/src/components/global/AppShell.tsx @@ -44,7 +44,7 @@ function ProfileJewel() { } const userNavigation = [ - // { href: '/profile', name: 'Profile' }, + { href: '/settings', name: 'Settings' }, { href: '/api/auth/signout', name: 'Sign Out', diff --git a/apps/portal/src/pages/settings.tsx b/apps/portal/src/pages/settings.tsx new file mode 100644 index 00000000..d35f9cc9 --- /dev/null +++ b/apps/portal/src/pages/settings.tsx @@ -0,0 +1,129 @@ +import Head from 'next/head'; +import type { Session } from 'next-auth'; +import { useSession } from 'next-auth/react'; +import { useState } from 'react'; +import { Button, HorizontalDivider, TextInput, useToast } from '@tih/ui'; + +import Container from '~/components/shared/Container'; + +import { trpc } from '~/utils/trpc'; + +function SettingsForm({ + session, +}: Readonly<{ + session: Session; +}>) { + const { showToast } = useToast(); + const updateProfileMutation = trpc.useMutation( + ['user.settings.profile.update'], + { + onError: () => { + showToast({ + subtitle: 'Please check that you are logged in.', + title: 'Failed to update profile', + variant: 'failure', + }); + }, + onSuccess: () => { + showToast({ + title: 'Updated profile!', + variant: 'success', + }); + }, + }, + ); + + const [name, setName] = useState(session?.user?.name); + + return ( +
+ +
+

Settings

+ +

+ This information will be displayed publicly so be careful what you + share. +

+
{ + event.preventDefault(); + updateProfileMutation.mutate({ + name: name ? name : undefined, + }); + }}> +
+
+ setName(val)} + /> +
+ {/*
+ +
+ {session?.user?.image ? ( + {session?.user?.email + ) : ( + + + + + + )} +
+
*/} +
+ +
+
+ +
+
+
+ ); +} + +export default function SettingsPage() { + const { data: session, status } = useSession(); + const isSessionLoading = status === 'loading'; + + if (isSessionLoading) { + return null; + } + + if (session == null) { + return null; + } + + return ( + <> + + Settings | Tech Interview Handbook + + + + ); +} diff --git a/apps/portal/src/server/router/index.ts b/apps/portal/src/server/router/index.ts index 383bcf47..f5462ea6 100644 --- a/apps/portal/src/server/router/index.ts +++ b/apps/portal/src/server/router/index.ts @@ -29,6 +29,7 @@ import { resumesResumeUserRouter } from './resumes/resumes-resume-user-router'; import { resumesStarUserRouter } from './resumes/resumes-star-user-router'; import { todosRouter } from './todos'; import { todosUserRouter } from './todos-user-router'; +import { userRouter } from './user-router'; export const appRouter = createRouter() .transformer(superjson) @@ -36,6 +37,7 @@ export const appRouter = createRouter() // All keys should be delimited by a period and end with a period. // Example routers. Learn more about tRPC routers: https://trpc.io/docs/v9/router .merge('auth.', protectedExampleRouter) + .merge('user.', userRouter) .merge('todos.', todosRouter) .merge('todos.user.', todosUserRouter) .merge('companies.', companiesRouter) diff --git a/apps/portal/src/server/router/user-router.ts b/apps/portal/src/server/router/user-router.ts new file mode 100644 index 00000000..036d609a --- /dev/null +++ b/apps/portal/src/server/router/user-router.ts @@ -0,0 +1,23 @@ +import { z } from 'zod'; + +import { createProtectedRouter } from './context'; + +export const userRouter = createProtectedRouter().mutation( + 'settings.profile.update', + { + input: z.object({ + name: z.string().optional(), + }), + async resolve({ ctx, input }) { + const userId = ctx.session?.user?.id; + return await ctx.prisma.user.update({ + data: { + name: input.name, + }, + where: { + id: userId, + }, + }); + }, + }, +);