From 5ee65042c92c03c1a0f1a9ededbbf0d2e9f2285f Mon Sep 17 00:00:00 2001 From: lachlanshoesmith <12870244+lachlanshoesmith@users.noreply.github.com> Date: Sun, 21 Jan 2024 16:36:50 +1100 Subject: [PATCH 1/7] =?UTF-8?q?=F0=9F=94=91=20add=20login=20api=20call?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/package.json | 1 + frontend/pnpm-lock.yaml | 12 +++++++++ frontend/src/index.css | 2 ++ frontend/src/routes/Login.tsx | 49 ++++++++++++++++++++++++++++++----- 4 files changed, 58 insertions(+), 6 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 14bb6e9..e29e39e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,6 +12,7 @@ "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", + "react-hook-form": "^7.49.3", "react-router-dom": "^6.21.3" }, "devDependencies": { diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 59a8aed..013a279 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -11,6 +11,9 @@ dependencies: react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) + react-hook-form: + specifier: ^7.49.3 + version: 7.49.3(react@18.2.0) react-router-dom: specifier: ^6.21.3 version: 6.21.3(react-dom@18.2.0)(react@18.2.0) @@ -1972,6 +1975,15 @@ packages: scheduler: 0.23.0 dev: false + /react-hook-form@7.49.3(react@18.2.0): + resolution: {integrity: sha512-foD6r3juidAT1cOZzpmD/gOKt7fRsDhXXZ0y28+Al1CHgX+AY1qIN9VSIIItXRq1dN68QrRwl1ORFlwjBaAqeQ==} + engines: {node: '>=18', pnpm: '8'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 + dependencies: + react: 18.2.0 + dev: false + /react-router-dom@6.21.3(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-kNzubk7n4YHSrErzjLK72j0B5i969GsuCGazRl3G6j1zqZBLjuSlYBdVdkDOgzGdPIffUOc9nmgiadTEVoq91g==} engines: {node: '>=14.0.0'} diff --git a/frontend/src/index.css b/frontend/src/index.css index 0499343..08e6ce9 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -5,4 +5,6 @@ color-scheme: light dark; color: #dbdbdb; background-color: #222; + max-width: 480px; + margin: 0 auto; } diff --git a/frontend/src/routes/Login.tsx b/frontend/src/routes/Login.tsx index 252fcfa..bef6b7e 100644 --- a/frontend/src/routes/Login.tsx +++ b/frontend/src/routes/Login.tsx @@ -1,15 +1,52 @@ -import { Form } from 'react-router-dom'; import Button from '../components/Button/Button'; +import { useForm, SubmitHandler } from 'react-hook-form'; -function login() { +type Inputs = { + username: string; + password: string; +}; + +export default function Login() { + const { + register, + formState: { errors }, + handleSubmit, + } = useForm(); + const onSubmit: SubmitHandler = (data) => { + fetch('https://webdevcamp.fly.dev/login', { + method: 'POST', + body: JSON.stringify(data), + }).then((res) => { + console.log(res.status); + if (res.ok) console.log(res.json()); + }); + }; return ( <>

Login Page

-
+ + + -
+ ); } - -export default login; From c2000ba2f77962f4350f2f92882e60195f3b10d0 Mon Sep 17 00:00:00 2001 From: lachlanshoesmith <12870244+lachlanshoesmith@users.noreply.github.com> Date: Sun, 21 Jan 2024 20:43:48 +1100 Subject: [PATCH 2/7] =?UTF-8?q?=F0=9F=9A=A2=20fix=20password=20input=20iss?= =?UTF-8?q?ues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/routes/Login.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/routes/Login.tsx b/frontend/src/routes/Login.tsx index bef6b7e..bbbdfd0 100644 --- a/frontend/src/routes/Login.tsx +++ b/frontend/src/routes/Login.tsx @@ -13,6 +13,7 @@ export default function Login() { handleSubmit, } = useForm(); const onSubmit: SubmitHandler = (data) => { + console.log(data); fetch('https://webdevcamp.fly.dev/login', { method: 'POST', body: JSON.stringify(data), @@ -37,8 +38,7 @@ export default function Login() { /> Date: Mon, 22 Jan 2024 02:19:21 +1100 Subject: [PATCH 3/7] =?UTF-8?q?=F0=9F=94=91=20add=20basic=20login=20error?= =?UTF-8?q?=20display?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Root/Root.module.css | 5 ++++ frontend/src/components/Root/Root.tsx | 4 ++++ frontend/src/routes/Login.tsx | 24 +++++++++++++++----- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/Root/Root.module.css b/frontend/src/components/Root/Root.module.css index dce34d0..e040927 100644 --- a/frontend/src/components/Root/Root.module.css +++ b/frontend/src/components/Root/Root.module.css @@ -6,3 +6,8 @@ margin: 0 0.5rem; } } + +.smallText { + color: #999; + font-size: 0.5rem; +} diff --git a/frontend/src/components/Root/Root.tsx b/frontend/src/components/Root/Root.tsx index 79ceaa8..203af94 100644 --- a/frontend/src/components/Root/Root.tsx +++ b/frontend/src/components/Root/Root.tsx @@ -24,6 +24,10 @@ function Root() {

By Lachlan Shoesmith

+

+ webdevcamp is not associated with Skill Samurai Rouse Hill or Skill + Samurai as a broader company. +

); diff --git a/frontend/src/routes/Login.tsx b/frontend/src/routes/Login.tsx index bbbdfd0..14dc311 100644 --- a/frontend/src/routes/Login.tsx +++ b/frontend/src/routes/Login.tsx @@ -1,5 +1,6 @@ import Button from '../components/Button/Button'; import { useForm, SubmitHandler } from 'react-hook-form'; +import { useState } from 'react'; type Inputs = { username: string; @@ -12,15 +13,25 @@ export default function Login() { formState: { errors }, handleSubmit, } = useForm(); - const onSubmit: SubmitHandler = (data) => { - console.log(data); - fetch('https://webdevcamp.fly.dev/login', { + const [loginErrors, setLoginErrors] = useState(''); + + const onSubmit: SubmitHandler = async (data) => { + const res = await fetch('https://webdevcamp.fly.dev/login', { method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, body: JSON.stringify(data), - }).then((res) => { - console.log(res.status); - if (res.ok) console.log(res.json()); }); + + const responseJSON = await res.json(); + const responseDetail: string = responseJSON.detail; + + if (!res.ok) { + setLoginErrors(responseDetail); + } else { + console.log(responseDetail); + } }; return ( <> @@ -47,6 +58,7 @@ export default function Login() { /> + {loginErrors &&

{loginErrors}

} ); } From 30ae5792f840c021890bd8d155dde76643559ccf Mon Sep 17 00:00:00 2001 From: lachlanshoesmith <12870244+lachlanshoesmith@users.noreply.github.com> Date: Mon, 22 Jan 2024 02:47:44 +1100 Subject: [PATCH 4/7] =?UTF-8?q?=F0=9F=A4=A9=20add=20register=20administrat?= =?UTF-8?q?or=20route?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/index.tsx | 5 ++ frontend/src/routes/Register.tsx | 104 +++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 frontend/src/routes/Register.tsx diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index d51e51b..3d89db1 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -4,6 +4,7 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom'; import Root from './components/Root/Root.tsx'; import ErrorPage from './routes/ErrorPage.tsx'; import Login from './routes/Login.tsx'; +import Register from './routes/Register.tsx'; import Home from './routes/Home.tsx'; import './index.css'; @@ -21,6 +22,10 @@ const router = createBrowserRouter([ path: '/login', element: , }, + { + path: '/register', + element: , + }, ], }, ]); diff --git a/frontend/src/routes/Register.tsx b/frontend/src/routes/Register.tsx new file mode 100644 index 0000000..20b2a25 --- /dev/null +++ b/frontend/src/routes/Register.tsx @@ -0,0 +1,104 @@ +import Button from '../components/Button/Button'; +import { useForm, SubmitHandler } from 'react-hook-form'; +import { useState } from 'react'; + +type Inputs = { + given_name: string; + family_name: string; + username: string; + password: string; + account_type: string; + email: string; + phone_number: string; +}; + +export default function Register() { + const { + register, + formState: { errors }, + handleSubmit, + } = useForm(); + const [registerErrors, setRegisterErrors] = useState(''); + + const onSubmit: SubmitHandler = async (data) => { + const res = await fetch('https://webdevcamp.fly.dev/register', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }); + + const responseJSON = await res.json(); + const responseDetail: string = responseJSON.detail; + + if (!res.ok) { + setRegisterErrors(responseDetail); + } else { + console.log(responseDetail); + } + }; + return ( + <> +

Register new administrator

+
+ + + + { + // TODO: replace hashed_password with 'password' across the board for simplicity + } + + + + +
+ {registerErrors &&

{registerErrors}

} + + ); +} From 882a297552dafa22e2c93155c529a30aa5ef9b1a Mon Sep 17 00:00:00 2001 From: lachlanshoesmith <12870244+lachlanshoesmith@users.noreply.github.com> Date: Tue, 23 Jan 2024 19:54:30 +1100 Subject: [PATCH 5/7] =?UTF-8?q?=F0=9F=94=91=20handle=20account=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/routes/Register.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/routes/Register.tsx b/frontend/src/routes/Register.tsx index 20b2a25..5875da8 100644 --- a/frontend/src/routes/Register.tsx +++ b/frontend/src/routes/Register.tsx @@ -6,7 +6,7 @@ type Inputs = { given_name: string; family_name: string; username: string; - password: string; + hashed_password: string; account_type: string; email: string; phone_number: string; @@ -21,6 +21,7 @@ export default function Register() { const [registerErrors, setRegisterErrors] = useState(''); const onSubmit: SubmitHandler = async (data) => { + data.account_type = 'administrator'; const res = await fetch('https://webdevcamp.fly.dev/register', { method: 'POST', headers: { @@ -30,12 +31,11 @@ export default function Register() { }); const responseJSON = await res.json(); - const responseDetail: string = responseJSON.detail; if (!res.ok) { - setRegisterErrors(responseDetail); + setRegisterErrors(responseJSON.detail); } else { - console.log(responseDetail); + console.log(res.json()); } }; return ( @@ -73,7 +73,7 @@ export default function Register() { } Date: Tue, 23 Jan 2024 20:15:30 +1100 Subject: [PATCH 6/7] =?UTF-8?q?=F0=9F=93=B1=20detect=20duplicate=20phone?= =?UTF-8?q?=20numbers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/backend/main.py | 15 ++++++++++----- backend/tests/test_main.py | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/backend/backend/main.py b/backend/backend/main.py index 2866152..dc05965 100644 --- a/backend/backend/main.py +++ b/backend/backend/main.py @@ -9,6 +9,7 @@ from jose import JWTError, jwt from passlib.context import CryptContext from psycopg import DataError, IntegrityError, AsyncConnection, sql +from psycopg.errors import UniqueViolation from psycopg_pool import AsyncConnectionPool from .models import (TokenData, ProposedWebsite, RegisteringStudentRequest, @@ -362,11 +363,15 @@ async def create_account(user_data: RegisteringUser, conn: AsyncConnection): async def register_full_account(user_data: RegisteringFullUserRequest, conn: AsyncConnection): async with conn.cursor() as cur: - await cur.execute(''' - insert into Full_Account (id, email, phone_number) - values (%(id)s, %(email)s, %(phone_number)s) - ''', {'id': user_data['id'], 'email': user_data['user'].email, 'phone_number': user_data['user'].phone_number}) - await conn.commit() + try: + await cur.execute(''' + insert into Full_Account (id, email, phone_number) + values (%(id)s, %(email)s, %(phone_number)s) + ''', {'id': user_data['id'], 'email': user_data['user'].email, 'phone_number': user_data['user'].phone_number}) + await conn.commit() + except UniqueViolation: + raise HTTPException( + status_code=400, detail='Phone number taken.') @app.post('/register') diff --git a/backend/tests/test_main.py b/backend/tests/test_main.py index b0f4fbd..207a3ba 100644 --- a/backend/tests/test_main.py +++ b/backend/tests/test_main.py @@ -41,6 +41,20 @@ async def test_register_administrator_with_same_email(test_db): assert res.json()['detail'] == 'User already exists.' +@pytest.mark.anyio +async def test_register_administrator_with_same_phone_number(test_db): + res = await register_administrator() + assert res.status_code == 200 + + administrator = deepcopy(d.registering_administrator_data) + administrator['username'] = 'different_username' + administrator['email'] = 'different_email@gmail.com' + + res = await register_administrator(administrator) + assert res.status_code == 400, res.text + assert res.json()['detail'] == 'Phone number taken.' + + @pytest.mark.anyio async def test_register_student(test_db): res = await register_administrator() From cab72f6e3e0c62a9048a56e8950e7e41cef0569b Mon Sep 17 00:00:00 2001 From: lachlanshoesmith <12870244+lachlanshoesmith@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:18:07 +1100 Subject: [PATCH 7/7] =?UTF-8?q?=F0=9F=94=91=20fix=20password=20issue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/routes/Register.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/src/routes/Register.tsx b/frontend/src/routes/Register.tsx index 5875da8..93244ee 100644 --- a/frontend/src/routes/Register.tsx +++ b/frontend/src/routes/Register.tsx @@ -68,16 +68,13 @@ export default function Register() { aria-invalid={errors.username ? 'true' : 'false'} placeholder="Username" /> - { - // TODO: replace hashed_password with 'password' across the board for simplicity - }