diff --git a/ui/src/app/profile/page.tsx b/ui/src/app/profile/page.tsx new file mode 100644 index 0000000..e892a07 --- /dev/null +++ b/ui/src/app/profile/page.tsx @@ -0,0 +1,5 @@ +import React from 'react'; + +export default function Page() { + return <>; +} diff --git a/ui/src/components/Topbar/LoginModal.tsx b/ui/src/components/Topbar/LoginModal.tsx new file mode 100644 index 0000000..7adde5a --- /dev/null +++ b/ui/src/components/Topbar/LoginModal.tsx @@ -0,0 +1,260 @@ +'use client'; + +import { login, register, refreshLoggedIn } from '@/api/auth'; +import { User } from '@/api/auth.types'; +import { + Modal, + Container, + Title, + Anchor, + Paper, + TextInput, + Button, + PasswordInput, + Group, + Checkbox, + Text +} from '@mantine/core'; +import { useForm } from '@mantine/form'; +import { notifications } from '@mantine/notifications'; + +interface LoginModalProps { + type?: string; + toggle: any; + setUser: (user: User) => void; + setRefreshId: (id: NodeJS.Timeout) => void; +} + +export function LoginModal({ type, toggle, setUser, setRefreshId }: LoginModalProps) { + function passwordValidator(value: string) { + if (value.trim().length < 10) { + return 'Password must be at least 10 characters'; + } + if (value.trim().length >= 128) { + return 'Password must be at most 128 characters'; + } + if (!/(\d)/.test(value)) { + return 'Password must contain at least one number'; + } + if (!/[a-z]/.test(value)) { + return 'Password must contain at least one lowercase letter'; + } + if (!/[A-Z]/.test(value)) { + return 'Password must contain at least one uppercase letter'; + } + if (!/[!@#$%^&*]/.test(value)) { + return 'Password must contain at least one special character'; + } + return null; + } + + function emailValidator(value: string) { + if (value.trim().length == 0) { + return 'Email is required'; + } + if (!/^\S+@\S+$/.test(value)) { + return 'Invalid email'; + } + return null; + } + + const registerForm = useForm({ + initialValues: { + firstName: '', + lastName: '', + email: '', + password: '' + }, + validate: { + firstName: (value) => (value.trim().length > 0 ? null : 'First name is required'), + lastName: (value) => (value.trim().length > 0 ? null : 'Last name is required'), + email: emailValidator, + password: passwordValidator + } + }); + + const loginForm = useForm({ + initialValues: { + email: '', + password: '', + remember: false + } + }); + + const resetForm = useForm({ + initialValues: { + email: '' + } + }); + + function onClose() { + toggle(undefined); + registerForm.reset(); + resetForm.reset(); + if (!loginForm.values.remember) { + loginForm.reset(); + } + } + + return ( + + {type == 'reset' ? ( + + Reset password + + Enter your email and we will send you a link to reset your password.{' '} + toggle('login')}> + Go Back + + + +
console.log(values))}> + + + +
+
+ ) : type == 'register' ? ( + + Create account + + Already have an account?{' '} + toggle('login')}> + Sign in + + + + +
{ + const id = notifications.show({ + loading: true, + title: `Creating account`, + message: `Please wait...`, + autoClose: false, + withCloseButton: false + }); + const registerResponse = await register({ + first_name: values.firstName, + last_name: values.lastName, + email: values.email, + password: values.password + }); + if (registerResponse) { + const loginResponse = await login(values.email, values.password); + if (loginResponse) { + setUser(loginResponse.user); + setRefreshId(refreshLoggedIn()); + onClose(); + notifications.update({ + id, + title: `Account created`, + message: `Welcome ${loginResponse.user.first_name}!`, + color: 'green', + autoClose: 2000, + loading: false + }); + } else { + notifications.update({ + id, + title: `Unable to Login`, + message: `Please try again.`, + color: 'red', + autoClose: 2000, + loading: false + }); + } + } else { + notifications.update({ + id, + title: `Unable to Register`, + message: `Please try again.`, + color: 'error', + autoClose: 2000, + loading: false + }); + } + })} + > + + + + + + +
+
+ ) : ( + + Welcome back! + + Do not have an account yet?{' '} + toggle('register')}> + Create account + + + + +
{ + const response = await login(values.email, values.password); + if (response) { + setUser(response.user); + setRefreshId(refreshLoggedIn()); + onClose(); + } else { + notifications.show({ + title: `Unable to Login`, + message: `Please try again.`, + color: 'red', + autoClose: 2000 + }); + } + })} + > + + + + + toggle('reset')}> + Forgot password? + + + + +
+
+ )} +
+ ); +} diff --git a/ui/src/components/Topbar/headerItems.ts b/ui/src/components/Topbar/headerItems.ts new file mode 100644 index 0000000..024a65e --- /dev/null +++ b/ui/src/components/Topbar/headerItems.ts @@ -0,0 +1,41 @@ +export interface HeaderItem { + name: string; + link: string; + role?: string; +} + +export const headerItems: HeaderItem[] = [ + { + name: 'Races', + link: '/races' + }, + { + name: 'Classes', + link: '/classes' + }, + { + name: 'Feats', + link: '/feats' + }, + { + name: 'Options & Features', + link: '/options' + }, + { + name: 'Backgrounds', + link: '/backgrounds' + }, + { + name: 'Items', + link: '/items' + }, + { + name: 'Spells', + link: '/spells' + }, + { + name: 'Management', + link: '/management', + role: 'admin' + } +]; diff --git a/ui/src/components/Topbar/index.tsx b/ui/src/components/Topbar/index.tsx index 7efb7bb..3d31c94 100644 --- a/ui/src/components/Topbar/index.tsx +++ b/ui/src/components/Topbar/index.tsx @@ -3,73 +3,14 @@ import Link from 'next/link'; import { usePathname } from 'next/navigation'; import './topbar.css'; -import { - Anchor, - Avatar, - Button, - Card, - Checkbox, - Container, - Grid, - Group, - Menu, - Modal, - Paper, - PasswordInput, - Text, - TextInput, - Title, - UnstyledButton -} from '@mantine/core'; +import { Avatar, Button, Card, Grid, Group, Menu, Text, UnstyledButton } from '@mantine/core'; import Cookies from 'js-cookie'; import { useEffect, useState } from 'react'; -import { useForm } from '@mantine/form'; -import { login, register, logout, me, refreshLoggedIn } from '@/api/auth'; +import { logout, me, refreshLoggedIn } from '@/api/auth'; import { User } from '@/api/auth.types'; import { useToggle } from '@mantine/hooks'; -import { notifications } from '@mantine/notifications'; - -interface HeaderItem { - name: string; - link: string; - role?: string; -} - -const headerItems: HeaderItem[] = [ - { - name: 'Races', - link: '/races' - }, - { - name: 'Classes', - link: '/classes' - }, - { - name: 'Feats', - link: '/feats' - }, - { - name: 'Options & Features', - link: '/options' - }, - { - name: 'Backgrounds', - link: '/backgrounds' - }, - { - name: 'Items', - link: '/items' - }, - { - name: 'Spells', - link: '/spells' - }, - { - name: 'Management', - link: '/management', - role: 'admin' - } -]; +import { LoginModal } from './LoginModal'; +import { HeaderItem, headerItems } from './headerItems'; export default function Topbar() { const pathName = usePathname(); @@ -145,17 +86,11 @@ export default function Topbar() { - + + + - - - - ) : type == 'register' ? ( - - Create account - - Already have an account?{' '} - toggle('login')}> - Sign in - - - - -
{ - const id = notifications.show({ - loading: true, - title: `Creating account`, - message: `Please wait...`, - autoClose: false, - withCloseButton: false - }); - const registerResponse = await register({ - first_name: values.firstName, - last_name: values.lastName, - email: values.email, - password: values.password - }); - if (registerResponse) { - const loginResponse = await login(values.email, values.password); - if (loginResponse) { - setUser(loginResponse.user); - setRefreshId(refreshLoggedIn()); - onClose(); - notifications.update({ - id, - title: `Account created`, - message: `Welcome ${loginResponse.user.first_name}!`, - color: 'green', - autoClose: 2000, - loading: false - }); - } else { - notifications.update({ - id, - title: `Unable to Login`, - message: `Please try again.`, - color: 'red', - autoClose: 2000, - loading: false - }); - } - } else { - notifications.update({ - id, - title: `Unable to Register`, - message: `Please try again.`, - color: 'error', - autoClose: 2000, - loading: false - }); - } - })} - > - - - - - - -
-
- ) : ( - - Welcome back! - - Do not have an account yet?{' '} - toggle('register')}> - Create account - - - - -
{ - const response = await login(values.email, values.password); - if (response) { - setUser(response.user); - setRefreshId(refreshLoggedIn()); - onClose(); - } else { - notifications.show({ - title: `Unable to Login`, - message: `Please try again.`, - color: 'red', - autoClose: 2000 - }); - } - })} - > - - - - - toggle('reset')}> - Forgot password? - - - - -
-
- )} - - ); -}