diff --git a/components/Settings.tsx b/components/Settings.tsx new file mode 100644 index 0000000000..e5dd273e96 --- /dev/null +++ b/components/Settings.tsx @@ -0,0 +1,90 @@ +import Link from 'next/link'; +import { useRouter } from "next/router"; + +export default function SettingsShell(props) { + const router = useRouter(); + return ( +
+
+
+
+
+ + + {props.children} +
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/components/Shell.tsx b/components/Shell.tsx index bf52e692d2..0484c0a795 100644 --- a/components/Shell.tsx +++ b/components/Shell.tsx @@ -21,7 +21,7 @@ export default function Shell(props) {
- Calendso + Calendso
@@ -37,8 +37,8 @@ export default function Shell(props) { Integrations - - Team + + Settings
diff --git a/pages/api/auth/changepw.ts b/pages/api/auth/changepw.ts new file mode 100644 index 0000000000..e4044dee9e --- /dev/null +++ b/pages/api/auth/changepw.ts @@ -0,0 +1,47 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { hashPassword, verifyPassword } from '../../../lib/auth'; +import { getSession } from 'next-auth/client'; +import prisma from '../../../lib/prisma'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const session = await getSession({req: req}); + + if (!session) { + res.status(401).json({message: "Not authenticated"}); + return; + } + + // TODO: Add user ID to user session object + const user = await prisma.user.findFirst({ + where: { + email: session.user.email, + }, + select: { + id: true, + password: true + } + }); + + if (!user) { res.status(404).json({message: 'User not found'}); return; } + + const oldPassword = req.body.oldPassword; + const newPassword = req.body.newPassword; + const currentPassword = user.password; + + const passwordsMatch = await verifyPassword(oldPassword, currentPassword); + + if (!passwordsMatch) { res.status(403).json({message: 'Incorrect password'}); return; } + + const hashedPassword = await hashPassword(newPassword); + + const updateUser = await prisma.user.update({ + where: { + id: user.id, + }, + data: { + password: hashedPassword, + }, + }); + + res.status(200).json({message: 'Password updated successfully'}); +} \ No newline at end of file diff --git a/pages/api/auth/signup.tsx b/pages/api/auth/signup.ts similarity index 100% rename from pages/api/auth/signup.tsx rename to pages/api/auth/signup.ts diff --git a/pages/api/availability/[user].ts b/pages/api/availability/[user].ts index 1ba3248cc9..71236e2fb9 100644 --- a/pages/api/availability/[user].ts +++ b/pages/api/availability/[user].ts @@ -1,5 +1,5 @@ -import type { NextApiRequest, NextApiResponse } from 'next' -import prisma from '../../../lib/prisma' +import type { NextApiRequest, NextApiResponse } from 'next'; +import prisma from '../../../lib/prisma'; const {google} = require('googleapis'); const credentials = process.env.GOOGLE_API_CREDENTIALS; diff --git a/pages/api/user/profile.ts b/pages/api/user/profile.ts new file mode 100644 index 0000000000..88793a1f4e --- /dev/null +++ b/pages/api/user/profile.ts @@ -0,0 +1,42 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { getSession } from 'next-auth/client'; +import prisma from '../../../lib/prisma'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const session = await getSession({req: req}); + + if (!session) { + res.status(401).json({message: "Not authenticated"}); + return; + } + + // TODO: Add user ID to user session object + const user = await prisma.user.findFirst({ + where: { + email: session.user.email, + }, + select: { + id: true, + password: true + } + }); + + if (!user) { res.status(404).json({message: 'User not found'}); return; } + + const username = req.body.username; + const name = req.body.name; + const description = req.body.description; + + const updateUser = await prisma.user.update({ + where: { + id: user.id, + }, + data: { + username: username, + name: name, + bio: description + }, + }); + + res.status(200).json({message: 'Profile updated successfully'}); +} \ No newline at end of file diff --git a/pages/auth/error.tsx b/pages/auth/error.tsx index 889550c660..45591a5be8 100644 --- a/pages/auth/error.tsx +++ b/pages/auth/error.tsx @@ -18,7 +18,7 @@ export default function Error() {
- +
diff --git a/pages/auth/logout.tsx b/pages/auth/logout.tsx new file mode 100644 index 0000000000..9760509027 --- /dev/null +++ b/pages/auth/logout.tsx @@ -0,0 +1,43 @@ +import Head from 'next/head'; +import Link from 'next/link'; + +export default function Logout() { + + return ( +
+ + Logged out - Calendso + + +
+ +
+
+
+ + + +
+
+ +
+

+ We hope to see you again soon! +

+
+
+
+ +
+
+
+ ); +} \ No newline at end of file diff --git a/pages/settings/index.tsx b/pages/settings/index.tsx new file mode 100644 index 0000000000..8ff88a7dcd --- /dev/null +++ b/pages/settings/index.tsx @@ -0,0 +1,19 @@ +import { useRouter } from 'next/router'; +import { signIn, useSession, getSession } from 'next-auth/client'; + +export default function Settings() { + const [ session, loading ] = useSession(); + const router = useRouter(); + + if (loading) { + return

Loading...

; + } else { + if (!session) { + window.location.href = "/auth/login"; + } + } + + router.push('/settings/profile'); + + return null; +} \ No newline at end of file diff --git a/pages/settings/password.tsx b/pages/settings/password.tsx new file mode 100644 index 0000000000..f6daaa9b48 --- /dev/null +++ b/pages/settings/password.tsx @@ -0,0 +1,104 @@ +import Head from 'next/head'; +import Link from 'next/link'; +import { useRef } from 'react'; +import prisma from '../../lib/prisma'; +import Shell from '../../components/Shell'; +import SettingsShell from '../../components/Settings'; +import { signIn, useSession, getSession } from 'next-auth/client'; + +export default function Settings(props) { + const [ session, loading ] = useSession(); + const oldPasswordRef = useRef(); + const newPasswordRef = useRef(); + + if (loading) { + return

Loading...

; + } else { + if (!session) { + window.location.href = "/auth/login"; + } + } + + async function changePasswordHandler(event) { + event.preventDefault(); + + const enteredOldPassword = oldPasswordRef.current.value; + const enteredNewPassword = newPasswordRef.current.value; + + // TODO: Add validation + + const response = await fetch('/api/auth/changepw', { + method: 'PATCH', + body: JSON.stringify({oldPassword: enteredOldPassword, newPassword: enteredNewPassword}), + headers: { + 'Content-Type': 'application/json' + } + }); + + console.log(response); + } + + return( + + + Change Password | Calendso + + + +
+
+
+

Change Password

+

+ Change the password for your Calendso account. +

+
+ +
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ + +
+
+
+
+
+ ); +} + +export async function getServerSideProps(context) { + const session = await getSession(context); + + const user = await prisma.user.findFirst({ + where: { + email: session.user.email, + }, + select: { + id: true, + username: true, + name: true + } + }); + + return { + props: {user}, // will be passed to the page component as props + } +} \ No newline at end of file diff --git a/pages/settings/profile.tsx b/pages/settings/profile.tsx new file mode 100644 index 0000000000..4e024e5aa1 --- /dev/null +++ b/pages/settings/profile.tsx @@ -0,0 +1,157 @@ +import Head from 'next/head'; +import Link from 'next/link'; +import { useRef } from 'react'; +import prisma from '../../lib/prisma'; +import Shell from '../../components/Shell'; +import SettingsShell from '../../components/Settings'; +import { signIn, useSession, getSession } from 'next-auth/client'; + +export default function Settings(props) { + const [ session, loading ] = useSession(); + const usernameRef = useRef(); + const nameRef = useRef(); + const descriptionRef = useRef(); + + if (loading) { + return

Loading...

; + } else { + if (!session) { + window.location.href = "/auth/login"; + } + } + + async function updateProfileHandler(event) { + event.preventDefault(); + + const enteredUsername = usernameRef.current.value; + const enteredName = nameRef.current.value; + const enteredDescription = descriptionRef.current.value; + + // TODO: Add validation + + const response = await fetch('/api/user/profile', { + method: 'PATCH', + body: JSON.stringify({username: enteredUsername, name: enteredName, description: enteredDescription}), + headers: { + 'Content-Type': 'application/json' + } + }); + + console.log(response); + } + + return( + + + Profile | Calendso + + + +
+
+
+

Profile

+

+ Review and change your public page details. +

+
+ +
+
+
+
+ +
+ + {window.location.hostname}/ + + +
+
+
+ + +
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+ + +
+
+
+
+ +
+ {props.user.avatar && } + {!props.user.avatar &&
} + +
+
+
+
+
+ + +
+
+
+
+
+ ); +} + +export async function getServerSideProps(context) { + const session = await getSession(context); + + const user = await prisma.user.findFirst({ + where: { + email: session.user.email, + }, + select: { + id: true, + username: true, + name: true, + email: true, + bio: true, + avatar: true + } + }); + + return { + props: {user}, // will be passed to the page component as props + } +} \ No newline at end of file diff --git a/pages/success.tsx b/pages/success.tsx index 46c6843659..36c733b218 100644 --- a/pages/success.tsx +++ b/pages/success.tsx @@ -24,7 +24,7 @@ export default function Success(props) {