From 519b6cdc711939bc79b11e0c9b5b6b4889009c5a Mon Sep 17 00:00:00 2001 From: Max Leiter Date: Tue, 19 Apr 2022 22:14:08 -0700 Subject: [PATCH] client/server: add user settings page; add displayName, email, bio fields to user model (#108) --- client/components/header/index.tsx | 12 +-- client/components/settings/index.tsx | 23 ++++ .../components/settings/sections/password.tsx | 97 +++++++++++++++++ .../components/settings/sections/profile.tsx | 100 ++++++++++++++++++ .../settings/settings-group/index.tsx | 24 +++++ .../settings-group/settings-group.module.css | 4 + client/lib/hooks/use-user-data.ts | 2 +- client/lib/types.d.ts | 3 + client/pages/settings.tsx | 15 +++ server/src/app.ts | 4 +- server/src/lib/models/User.ts | 9 ++ .../migrations/09_add_more_user_settings.ts | 26 +++++ server/src/routes/auth.ts | 37 ++++++- server/src/routes/index.ts | 2 +- server/src/routes/user.ts | 76 +++++++++++++ server/src/routes/users.ts | 31 ------ 16 files changed, 423 insertions(+), 42 deletions(-) create mode 100644 client/components/settings/index.tsx create mode 100644 client/components/settings/sections/password.tsx create mode 100644 client/components/settings/sections/profile.tsx create mode 100644 client/components/settings/settings-group/index.tsx create mode 100644 client/components/settings/settings-group/settings-group.module.css create mode 100644 client/pages/settings.tsx create mode 100644 server/src/migrations/09_add_more_user_settings.ts create mode 100644 server/src/routes/user.ts delete mode 100644 server/src/routes/users.ts diff --git a/client/components/header/index.tsx b/client/components/header/index.tsx index c1d58440..7b5efdef 100644 --- a/client/components/header/index.tsx +++ b/client/components/header/index.tsx @@ -88,12 +88,12 @@ const Header = () => { value: "yours", href: "/mine" }, - // { - // name: 'settings', - // icon: , - // value: 'settings', - // href: '/settings' - // }, + { + name: 'settings', + icon: , + value: 'settings', + href: '/settings' + }, { name: "sign out", icon: , diff --git a/client/components/settings/index.tsx b/client/components/settings/index.tsx new file mode 100644 index 00000000..802cb1ea --- /dev/null +++ b/client/components/settings/index.tsx @@ -0,0 +1,23 @@ +import { Fieldset, Text, Divider, Note, Input, Textarea, Button } from "@geist-ui/core" +import Password from "./sections/password" +import Profile from "./sections/profile" +import SettingsGroup from "./settings-group" + +const SettingsPage = () => { + return (
+

Settings

+ + + + + + +
) +} + +export default SettingsPage \ No newline at end of file diff --git a/client/components/settings/sections/password.tsx b/client/components/settings/sections/password.tsx new file mode 100644 index 00000000..4f46a3c1 --- /dev/null +++ b/client/components/settings/sections/password.tsx @@ -0,0 +1,97 @@ +import { Input, Button, useToasts } from "@geist-ui/core" +import Cookies from "js-cookie" +import { useState } from "react" + +const Password = () => { + const [password, setPassword] = useState('') + const [newPassword, setNewPassword] = useState('') + const [confirmPassword, setConfirmPassword] = useState('') + + const { setToast } = useToasts() + + const handlePasswordChange = (e: React.ChangeEvent) => { + setPassword(e.target.value) + } + + const handleNewPasswordChange = (e: React.ChangeEvent) => { + setNewPassword(e.target.value) + } + + const handleConfirmPasswordChange = (e: React.ChangeEvent) => { + setConfirmPassword(e.target.value) + } + + const onSubmit = async (e: React.FormEvent) => { + e.preventDefault() + if (!password || !newPassword || !confirmPassword) { + setToast({ + text: "Please fill out all fields", + type: "error", + }) + } + + if (newPassword !== confirmPassword) { + setToast({ + text: "New password and confirm password do not match", + type: "error", + }) + } + + const res = await fetch("/server-api/auth/change-password", { + method: "PUT", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${Cookies.get("drift-token")}`, + }, + body: JSON.stringify({ + oldPassword: password, + newPassword, + }), + }) + + + if (res.status === 200) { + setToast({ + text: "Password updated successfully", + type: "success", + }) + setPassword('') + setNewPassword('') + setConfirmPassword('') + } else { + const data = await res.json() + + setToast({ + text: data.error ?? "Failed to update password", + type: "error", + }) + } + } + + return ( +
+
+ + +
+
+ + +
+
+ + +
+ +
) +} + +export default Password \ No newline at end of file diff --git a/client/components/settings/sections/profile.tsx b/client/components/settings/sections/profile.tsx new file mode 100644 index 00000000..ed95851a --- /dev/null +++ b/client/components/settings/sections/profile.tsx @@ -0,0 +1,100 @@ +import { Note, Input, Textarea, Button, useToasts } from "@geist-ui/core" +import useUserData from "@lib/hooks/use-user-data" +import Cookies from "js-cookie" +import { useEffect, useState } from "react" + +const Profile = () => { + const user = useUserData() + const [name, setName] = useState() + const [email, setEmail] = useState() + const [bio, setBio] = useState() + + useEffect(() => { + console.log(user) + if (user?.displayName) setName(user.displayName) + if (user?.email) setEmail(user.email) + if (user?.bio) setBio(user.bio) + }, [user]) + + const { setToast } = useToasts() + + const handleNameChange = (e: React.ChangeEvent) => { + setName(e.target.value) + } + + const handleEmailChange = (e: React.ChangeEvent) => { + setEmail(e.target.value) + } + + const handleBioChange = (e: React.ChangeEvent) => { + setBio(e.target.value) + } + + const onSubmit = async (e: React.FormEvent) => { + e.preventDefault() + if (!name && !email && !bio) { + setToast({ + text: "Please fill out at least one field", + type: "error", + }) + return + } + + const data = { + displayName: name, + email, + bio, + } + + const res = await fetch("/server-api/user/profile", { + method: "PUT", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${Cookies.get("drift-token")}`, + }, + body: JSON.stringify(data), + }) + + if (res.status === 200) { + setToast({ + text: "Profile updated", + type: "success", + }) + } else { + setToast({ + text: "Something went wrong updating your profile", + type: "error", + }) + } + } + + return (<> + + This information will be publicly available on your profile + +
+
+ + +
+
+ + +
+
+ +