From 42134a8db91fdc3001f8df93d3cf1a9b54534ee6 Mon Sep 17 00:00:00 2001 From: "Planoetscher Daniel (Student Com20)" <daniel.planoetscher@stud-inf.unibz.it> Date: Thu, 20 May 2021 22:37:17 +0200 Subject: [PATCH] bugfixes, other api connections --- .../src/components/forms/UserForm/index.tsx | 61 +++++++++++++++++++ .../components/forms/UserForm/user-form.scss | 47 ++++++++++++++ .../components/navigation/Sidebar/index.tsx | 24 ++++++-- .../navigation/Sidebar/sidebar.scss | 5 +- client/src/pages/Settings/index.tsx | 39 ++++++++++-- client/src/pages/Teams/TeamsStats/index.tsx | 10 ++- client/src/styles/settings.scss | 2 +- 7 files changed, 167 insertions(+), 21 deletions(-) create mode 100644 client/src/components/forms/UserForm/index.tsx create mode 100644 client/src/components/forms/UserForm/user-form.scss diff --git a/client/src/components/forms/UserForm/index.tsx b/client/src/components/forms/UserForm/index.tsx new file mode 100644 index 0000000..898c178 --- /dev/null +++ b/client/src/components/forms/UserForm/index.tsx @@ -0,0 +1,61 @@ +import { User } from 'adapters/user'; +import { FormEvent, useCallback, useState } from 'react'; +import './user-form.scss'; +import TextInput from 'components/ui/TextInput'; +import Button from 'components/ui/Button'; + +interface Props { + onSubmit?: (name?: string, email?: string,) => void; + user: User +} + +function validateEmail(email?: string): string | null { + if (email && email.length > 0) { + if (email.match('^[^\\s]+@[^\\s]+$')) { + return null; + } else { + return 'Please enter a valid email or let this field blank.' + } + } else { + return null; + } +} + +export default function UserForm({ user, onSubmit }: Props) { + const [name, setName] = useState(user.realname); + const [email, setEmail] = useState(user.email); + const handleSubmit = useCallback(async (e: FormEvent) => { + e.preventDefault(); + if (validateEmail(email) === null) { + onSubmit?.(name, email); + } + }, [onSubmit, name, email]); + return ( + <form onSubmit={handleSubmit} className="user-form"> + <div className="fields"> + <TextInput + label="Real Name" + name="name" + onChange={setName} + defaultText={name} + /> + <TextInput + label="Email address" + name="name" + validation={validateEmail} + onChange={setEmail} + defaultText={email} + /> + <div className="avatar-upload"> + <div className="label">Avatar</div> + <label htmlFor="avatar" className="avatar-field"> + <input type="file" id="avatar" name="avatar" /> + </label> + </div> + </div> + <Button type="submit" className="expanded"> + Save + </Button> + </form > + ) +} \ No newline at end of file diff --git a/client/src/components/forms/UserForm/user-form.scss b/client/src/components/forms/UserForm/user-form.scss new file mode 100644 index 0000000..9e478c1 --- /dev/null +++ b/client/src/components/forms/UserForm/user-form.scss @@ -0,0 +1,47 @@ +@use 'styles/settings.scss'as s; +@use 'styles/mixins.scss'as mx; + +.user-form { + .fields { + display: flex; + flex-wrap: wrap; + margin: -10px; + + @include mx.breakpoint(medium) { + margin: -10px; + } + + &>* { + @include mx.breakpoint(medium) { + padding: 10px; + } + } + } + + .input-element { + width: 50%; + margin-bottom: 20px; + } + + .avatar-upload { + width: 100%; + position: relative; + .label { + position: absolute; + top: -2px; + left: 30px; + font-weight: s.$weight-bold; + } + .avatar-field { + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + border-radius: 25px; + width: 100%; + height: 80px; + margin-bottom: 20px; + background: s.$light; + } + } +} \ No newline at end of file diff --git a/client/src/components/navigation/Sidebar/index.tsx b/client/src/components/navigation/Sidebar/index.tsx index 7d550df..dc36c1d 100644 --- a/client/src/components/navigation/Sidebar/index.tsx +++ b/client/src/components/navigation/Sidebar/index.tsx @@ -5,7 +5,10 @@ import { NavLink, useHistory } from 'react-router-dom'; import { clearToken, isLoggedIn } from 'adapters/auth'; import './sidebar.scss'; import { useEffect, useState } from 'react'; -import { getCurrentUser, User } from 'adapters/user'; +import { getCurrentUser, getUser, getUserActivity, User } from 'adapters/user'; +import BarChart, { ChartItem } from 'components/graphs/BarChart'; +import { Activity } from 'adapters/util'; +import LoadingScreen from 'components/ui/LoadingScreen'; interface Props { mobileShown: boolean; @@ -14,11 +17,18 @@ interface Props { export default function Sidebar({ mobileShown, setMobileShown }: Props) { const [user, setUser] = useState<User>(); + const [activity, setActivity] = useState<ChartItem[]>(); useEffect(() => { if (isLoggedIn()) { getCurrentUser().then((user) => { setUser(user); + getUserActivity().then((a) => { + setActivity(a.map(item => ({ + label: item.day, + value: item.time + }))); + }); }).catch(() => { }); } }, []) @@ -60,10 +70,14 @@ export default function Sidebar({ mobileShown, setMobileShown }: Props) { </button> </nav> </div> - <div className="stats"> - <LineGraph /> - <div className="comment">You are doing well!</div> - </div> + { + activity ? ( + <div className="stats"> + <BarChart data={activity} /> + <div className="comment">You are doing well!</div> + </div> + ) : <LoadingScreen /> + } </aside> ); } \ No newline at end of file diff --git a/client/src/components/navigation/Sidebar/sidebar.scss b/client/src/components/navigation/Sidebar/sidebar.scss index f3d753d..6ea9a97 100644 --- a/client/src/components/navigation/Sidebar/sidebar.scss +++ b/client/src/components/navigation/Sidebar/sidebar.scss @@ -136,9 +136,8 @@ font-size: fn.toRem(14); font-weight: s.$weight-semi-bold; - .line-graph { - width: 100%; - margin-bottom: 10px; + .bar-chart-container { + background: transparent; } } } \ No newline at end of file diff --git a/client/src/pages/Settings/index.tsx b/client/src/pages/Settings/index.tsx index a95b543..3097213 100644 --- a/client/src/pages/Settings/index.tsx +++ b/client/src/pages/Settings/index.tsx @@ -1,11 +1,38 @@ import './settings.scss'; +import { useCallback, useEffect, useState } from 'react'; +import { getCurrentUser, updateUser, User } from 'adapters/user'; +import LoadingScreen from 'components/ui/LoadingScreen'; +import UserForm from 'components/forms/UserForm'; +import { useHistory } from 'react-router'; export default function Settings() { - return ( - <div className="settings-page"> - <div className="content-container"> - <h1 className="underlined">Settings</h1> + const [user, setUser] = useState<User>(); + const history = useHistory(); + + useEffect(() => { + getCurrentUser().then((user) => setUser(user)) + }, []); + + + const handleSubmit = useCallback(async (name?: string, email?: string) => { + try { + if (user && updateUser({realname: name, email })) { + history.push('/tasks'); + } + } catch (e) { + } + }, [history, user]); + + if (user) { + return ( + <div className="settings-page"> + <div className="content-container"> + <h1 className="underlined">Settings</h1> + <UserForm user={user} onSubmit={handleSubmit} /> + </div> </div> - </div> - ) + ) + + } + return <LoadingScreen /> } \ No newline at end of file diff --git a/client/src/pages/Teams/TeamsStats/index.tsx b/client/src/pages/Teams/TeamsStats/index.tsx index 1686bab..f5c999a 100644 --- a/client/src/pages/Teams/TeamsStats/index.tsx +++ b/client/src/pages/Teams/TeamsStats/index.tsx @@ -19,12 +19,10 @@ export default function TeamsStats({ teamId }: Props) { useEffect(() => { getTeamActivity(teamId).then((a) => { - setActivity(a.map(item => { - return { - label: item.day, - value: item.time - } - })); + setActivity(a.map(item => ({ + label: item.day, + value: item.time + }))); }); getTeamCompletion(teamId).then((comp) => { const allAmount = comp.sum ?? 1; diff --git a/client/src/styles/settings.scss b/client/src/styles/settings.scss index 60913d7..fb92ed2 100644 --- a/client/src/styles/settings.scss +++ b/client/src/styles/settings.scss @@ -4,7 +4,7 @@ $primary: var(--primary); $primary-dark: var(--primary-dark); $secondary: #7DEFFF; $red: #E51C4A; -$light: #F1F1F1; +$light: rgba(0, 0, 0, 0.025); $dark: #180923; $light-gray: #F8F8F8; -- GitLab