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.
+
+
+
+
+
+ );
+}
+
+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,
+ },
+ });
+ },
+ },
+);