diff --git a/components/ui/UsernameInput.tsx b/components/ui/UsernameInput.tsx new file mode 100644 index 0000000000..31d2ba0333 --- /dev/null +++ b/components/ui/UsernameInput.tsx @@ -0,0 +1,17 @@ +import React from "react"; + +export const UsernameInput = React.forwardRef( (props, ref) => ( + // todo, check if username is already taken here? +
+ +
+ + {typeof window !== "undefined" && window.location.hostname}/ + + +
+
+)); \ No newline at end of file diff --git a/components/ui/alerts/Error.tsx b/components/ui/alerts/Error.tsx new file mode 100644 index 0000000000..8cc239988d --- /dev/null +++ b/components/ui/alerts/Error.tsx @@ -0,0 +1,22 @@ + +import { XCircleIcon } from '@heroicons/react/solid' + +export default function ErrorAlert(props) { + return ( +
+
+
+
+
+

Something went wrong

+
+

+ {props.message} +

+
+
+
+
+ ) +} \ No newline at end of file diff --git a/pages/api/user/profile.ts b/pages/api/user/profile.ts index f1b2e18d68..fb47f5d221 100644 --- a/pages/api/user/profile.ts +++ b/pages/api/user/profile.ts @@ -3,46 +3,58 @@ 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}); + const session = await getSession({req: req}); - if (!session) { - res.status(401).json({message: "Not authenticated"}); - return; + if (!session) { + res.status(401).json({message: "Not authenticated"}); + return; + } + + // Get user + const user = await prisma.user.findUnique({ + where: { + email: session.user.email, + }, + select: { + id: true, + password: true } + }); - // Get user - 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; + // username is changed: username is optional but it is necessary to be unique, enforce here + if (username !== user.username) { + const userConflict = await prisma.user.findFirst({ + where: { + username, + } }); + if (userConflict) { + return res.status(409).json({ message: 'Username already taken' }); + } + } - if (!user) { res.status(404).json({message: 'User not found'}); return; } + const name = req.body.name; + const description = req.body.description; + const avatar = req.body.avatar; + const timeZone = req.body.timeZone; + const weekStart = req.body.weekStart; - const username = req.body.username; - const name = req.body.name; - const description = req.body.description; - const avatar = req.body.avatar; - const timeZone = req.body.timeZone; - const weekStart = req.body.weekStart; + const updateUser = await prisma.user.update({ + where: { + id: user.id, + }, + data: { + username, + name, + avatar, + bio: description, + timeZone: timeZone, + weekStart: weekStart, + }, + }); - const updateUser = await prisma.user.update({ - where: { - id: user.id, - }, - data: { - username, - name, - avatar, - bio: description, - timeZone: timeZone, - weekStart: weekStart, - }, - }); - - res.status(200).json({message: 'Profile updated successfully'}); + return res.status(200).json({message: 'Profile updated successfully'}); } \ No newline at end of file diff --git a/pages/settings/profile.tsx b/pages/settings/profile.tsx index d0a112943b..2078796636 100644 --- a/pages/settings/profile.tsx +++ b/pages/settings/profile.tsx @@ -9,6 +9,8 @@ import SettingsShell from '../../components/Settings'; import Avatar from '../../components/Avatar'; import { signIn, useSession, getSession } from 'next-auth/client'; import TimezoneSelect from 'react-timezone-select'; +import {UsernameInput} from "../../components/ui/UsernameInput"; +import ErrorAlert from "../../components/ui/alerts/Error"; export default function Settings(props) { const [ session, loading ] = useSession(); @@ -22,12 +24,22 @@ export default function Settings(props) { const [ selectedTimeZone, setSelectedTimeZone ] = useState({ value: props.user.timeZone }); const [ selectedWeekStartDay, setSelectedWeekStartDay ] = useState(props.user.weekStart || 'Sunday'); + const [ hasErrors, setHasErrors ] = useState(false); + const [ errorMessage, setErrorMessage ] = useState(''); + if (loading) { return

Loading...

; } const closeSuccessModal = () => { setSuccessModalOpen(false); } + const handleError = async (resp) => { + if (!resp.ok) { + const error = await resp.json(); + throw new Error(error.message); + } + } + async function updateProfileHandler(event) { event.preventDefault(); @@ -46,10 +58,10 @@ export default function Settings(props) { headers: { 'Content-Type': 'application/json' } + }).then(handleError).then( () => setSuccessModalOpen(true) ).catch( (err) => { + setHasErrors(true); + setErrorMessage(err.message); }); - - router.replace(router.asPath); - setSuccessModalOpen(true); } return( @@ -60,6 +72,7 @@ export default function Settings(props) {
+ {hasErrors && }

Profile

@@ -72,15 +85,7 @@ export default function Settings(props) {
- -
- - {window.location.hostname}/ - - -
+