From 8d8b5e16467646b2ebacf77be1388520b90023ee Mon Sep 17 00:00:00 2001 From: SrJuggernaut Date: Thu, 11 Jan 2024 20:57:17 -0600 Subject: [PATCH] feat: session --- bun.lockb | Bin 273699 -> 273699 bytes package.json | 4 +- src/app/SessionConsumer.tsx | 36 +++++++ src/app/cuenta/UpdateEmail.tsx | 129 ++++++++++++++++++++++++ src/app/cuenta/UpdatePassword.tsx | 160 ++++++++++++++++++++++++++++++ src/app/cuenta/UpdateUserName.tsx | 105 ++++++++++++++++++++ src/app/cuenta/page.tsx | 18 ++++ src/app/layout.tsx | 2 + src/app/login/LoginForm.tsx | 47 ++++++++- src/app/login/page.tsx | 2 +- src/app/register/RegisterForm.tsx | 37 ++++++- src/app/register/page.tsx | 2 +- src/components/layout/Header.tsx | 85 +++++++++++++--- src/hooks/useSession.ts | 21 ++++ src/state/sessionSlice.ts | 29 ++++++ src/state/store.ts | 4 +- tsconfig.json | 4 +- 17 files changed, 659 insertions(+), 26 deletions(-) create mode 100644 src/app/SessionConsumer.tsx create mode 100644 src/app/cuenta/UpdateEmail.tsx create mode 100644 src/app/cuenta/UpdatePassword.tsx create mode 100644 src/app/cuenta/UpdateUserName.tsx create mode 100644 src/app/cuenta/page.tsx create mode 100644 src/hooks/useSession.ts create mode 100644 src/state/sessionSlice.ts diff --git a/bun.lockb b/bun.lockb index c63038c45fad2b7be9f8d1da25cff73e9de5b9b8..7e8b3029f4557272af5b8f04bb8fdf6da620940f 100755 GIT binary patch delta 175 zcmV;g08sy<+7P4K5Rfh)X@fyDy(dmqZZes2Ah|%uj4XMW;hfS*GJ6-GuE$V-u})f- zlW2A@v)Gq){y?^`o;Htt50F|i{)aBs7|fb(H0qTptidan8u(f)S#kOAqI;r9CN6Ox zfcTp;YBGzH^7U+2?_mt5fJIlfO!0?}mI1eomI8=52LWC(E-)@JmmJUnGXXc3P|yP7 d2LWC=E-)@MhdR*$w>r@S(Fp-Jw+!0@7gE+EOT7R9 delta 175 zcmZ2{OJMOWfeCsF>#nq0%sIZ!|I3uy>r2CvB9hwMm<#1M9({H6-1fp7_p&znMNOZa z;byS;#`MyE4tM8zot*rfJ?|ZxZSzIZQ{@_}+LHu#%le0Yy>ccw%}lwEr+?$`{Zf&W zzsC1&K5EsW_(gTo6hY&H>x(*NcfVFS*xoyhaeMDHrVdMXCI&E=E^~p&n9+24zy+qq ZFtK*4i%i?CE;3)_WHjB*bDLQ_2mp$|Of&!h diff --git a/package.json b/package.json index f9e3088..69f4d9d 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "@fortawesome/react-fontawesome": "^0.2.0", "@reduxjs/toolkit": "^2.0.1", "appwrite": "^13.0.1", - "entgamers-database": "^0.0.5", + "entgamers-database": "^0.0.7", "entgamers-panda-preset": "0.1.1", "formik": "^2.4.5", "framer-motion": "^10.17.6", @@ -54,4 +54,4 @@ "husky": "^8.0.3", "typescript": "*" } -} +} \ No newline at end of file diff --git a/src/app/SessionConsumer.tsx b/src/app/SessionConsumer.tsx new file mode 100644 index 0000000..cfa4aa4 --- /dev/null +++ b/src/app/SessionConsumer.tsx @@ -0,0 +1,36 @@ +'use client' +import { useAppDispatch } from '@/hooks/useAppDispatch' +import { useAppSelector } from '@/hooks/useAppSelector' +import { setSession, setStatus } from '@/state/sessionSlice' +import { AppwriteException } from 'appwrite' +import { getSession } from 'entgamers-database/frontend/session' +import { useEffect, type FC } from 'react' + +const SessionConsumer: FC = () => { + const session = useAppSelector((state) => state.session) + const dispatch = useAppDispatch() + + useEffect(() => { + if (session.status === 'initializing' && session.session === undefined) { + dispatch(setStatus('loading')) + getSession('current') + .then((session) => { + dispatch(setSession(session)) + }) + .catch((error) => { + if (error instanceof AppwriteException) { + console.error(error) + } + }) + .finally(() => { + dispatch(setStatus('idle')) + }) + } + }, []) + + return ( + <> + + ) +} +export default SessionConsumer diff --git a/src/app/cuenta/UpdateEmail.tsx b/src/app/cuenta/UpdateEmail.tsx new file mode 100644 index 0000000..e0ed714 --- /dev/null +++ b/src/app/cuenta/UpdateEmail.tsx @@ -0,0 +1,129 @@ +'use client' +import Button from '@/components/ui/Button' +import Typography from '@/components/ui/Typography' +import FormGroup from '@/components/ui/form/FormGroup' +import Input from '@/components/ui/form/Input' +import PasswordInput from '@/components/ui/form/PasswordInput' +import { useAppDispatch } from '@/hooks/useAppDispatch' +import useSession from '@/hooks/useSession' +import { addAlert } from '@/state/feedbackSlice' +import { nanoid } from '@reduxjs/toolkit' +import { AppwriteException } from 'appwrite' +import { updateEmail } from 'entgamers-database/frontend/session' +import { useFormik } from 'formik' +import { type FC } from 'react' +import { object, string } from 'yup' + +interface UpdateEmailData { + email: string + password: string +} + +const updateEmailSchema = object({ + email: string().email('El correo electrónico no es válido').required('El correo electrónico es requerido'), + password: string().required('La contraseña es requerida') +}) + +const UpdateEmail: FC = () => { + const { status, session } = useSession('/login') + const dispatch = useAppDispatch() + + const formik = useFormik({ + initialValues: { + email: '', + password: '' + }, + onSubmit: async ({ email, password }) => { + try { + await updateEmail(email, password) + dispatch(addAlert({ + id: nanoid(), + title: 'Correo actualizado', + message: 'Ahora puedes iniciar sesión', + severity: 'success' + })) + } catch (error) { + if (error instanceof AppwriteException) { + dispatch(addAlert({ + id: nanoid(), + message: error.message, + title: 'Error mientras se actualizaba el correo', + severity: 'error' + })) + } else { + dispatch(addAlert({ + id: nanoid(), + message: 'Error desconocido', + title: 'Error mientras se actualizaba el correo', + severity: 'error' + })) + } + } + }, + validationSchema: updateEmailSchema, + isInitialValid: false + }) + + if (status !== 'idle' || session === undefined) { + // TODO: Replace with Skeleton + return null + } + + return ( + <> + Cambia tu correo +
+ + + + {formik.touched.email !== undefined && formik.errors.email !== undefined && ( + {formik.errors.email} + )} + + + + + {formik.touched.password !== undefined && formik.errors.password !== undefined && ( + {formik.errors.password} + )} + + + + +
+ + ) +} + +export default UpdateEmail diff --git a/src/app/cuenta/UpdatePassword.tsx b/src/app/cuenta/UpdatePassword.tsx new file mode 100644 index 0000000..16b47a0 --- /dev/null +++ b/src/app/cuenta/UpdatePassword.tsx @@ -0,0 +1,160 @@ +'use client' +import Button from '@/components/ui/Button' +import Typography from '@/components/ui/Typography' +import FormGroup from '@/components/ui/form/FormGroup' +import PasswordInput from '@/components/ui/form/PasswordInput' +import { useAppDispatch } from '@/hooks/useAppDispatch' +import useSession from '@/hooks/useSession' +import { addAlert } from '@/state/feedbackSlice' +import { nanoid } from '@reduxjs/toolkit' +import { AppwriteException } from 'appwrite' +import { updatePassword } from 'entgamers-database/frontend/session' +import { useFormik } from 'formik' +import { type FC } from 'react' +import { object, ref, string } from 'yup' + +interface UpdatePasswordData { + password: string + confirmPassword: string + currentPassword: string +} + +const updatePasswordSchema = object({ + password: string() + .min(6, 'La contraseña debe tener al menos 6 caracteres') + .matches(/[a-z]/, 'La contraseña debe tener al menos una letra minúscula') + .matches(/[A-Z]/, 'La contraseña debe tener al menos una letra mayúscula') + .matches(/[0-9]/, 'La contraseña debe tener al menos un número') + .required('La contraseña es requerida'), + confirmPassword: string().oneOf([ref('password')], 'Las contraseñas no coinciden').required('La confirmación de la contraseña es requerida'), + currentPassword: string().required('La contraseña actual es requerida') +}) + +const UpdatePassword: FC = () => { + const { status, session } = useSession('/login') + const dispatch = useAppDispatch() + + const formik = useFormik({ + initialValues: { + password: '', + confirmPassword: '', + currentPassword: '' + }, + onSubmit: async ({ password, currentPassword }) => { + try { + await updatePassword(password, currentPassword) + dispatch(addAlert({ + id: nanoid(), + title: 'Contrasenya actualizada', + message: 'Ahora puedes iniciar sesión', + severity: 'success' + })) + } catch (error) { + if (error instanceof AppwriteException) { + dispatch(addAlert({ + id: nanoid(), + message: error.message, + title: 'Error mientras se actualizaba la contraseña', + severity: 'error' + })) + } else { + dispatch(addAlert({ + id: nanoid(), + message: 'Error desconocido', + title: 'Error mientras se actualizaba la contraseña', + severity: 'error' + })) + } + } + }, + validationSchema: updatePasswordSchema, + isInitialValid: false + }) + + if (status !== 'idle' || session === undefined) { + // TODO: Replace with Skeleton + return null + } + + return ( + <> + Actualizar contraseña +
+ + + + {formik.touched.password !== undefined && formik.errors.password !== undefined && ( + + {formik.errors.password} + + )} + + + + + {formik.touched.confirmPassword !== undefined && formik.errors.confirmPassword !== undefined && ( + + {formik.errors.confirmPassword} + + )} + + + + + {formik.touched.currentPassword !== undefined && formik.errors.currentPassword !== undefined && ( + + {formik.errors.currentPassword} + + )} + + + + +
+ + ) +} +export default UpdatePassword diff --git a/src/app/cuenta/UpdateUserName.tsx b/src/app/cuenta/UpdateUserName.tsx new file mode 100644 index 0000000..e4f144a --- /dev/null +++ b/src/app/cuenta/UpdateUserName.tsx @@ -0,0 +1,105 @@ +'use client' +import Button from '@/components/ui/Button' +import Typography from '@/components/ui/Typography' +import FormGroup from '@/components/ui/form/FormGroup' +import Input from '@/components/ui/form/Input' +import { useAppDispatch } from '@/hooks/useAppDispatch' +import useSession from '@/hooks/useSession' +import { addAlert } from '@/state/feedbackSlice' +import { nanoid } from '@reduxjs/toolkit' +import { AppwriteException } from 'appwrite' +import { updateName } from 'entgamers-database/frontend/session' +import { useFormik } from 'formik' +import { type FC } from 'react' +import { object, string } from 'yup' + +interface UpdateUserNameData { + name: string +} + +const UpdateUserNameSchema = object({ + name: string().required('El nombre es requerido') +}) + +const UpdateUserName: FC = () => { + const { status, session } = useSession('/login') + const dispatch = useAppDispatch() + + const formik = useFormik({ + initialValues: { + name: '' + }, + onSubmit: async ({ name }) => { + try { + await updateName(name) + dispatch(addAlert({ + id: nanoid(), + title: 'Nombre actualizado', + message: 'Ahora puedes iniciar sesión', + severity: 'success' + })) + } catch (error) { + if (error instanceof AppwriteException) { + dispatch(addAlert({ + id: nanoid(), + message: error.message, + title: 'Error mientras se actualizaba el nombre', + severity: 'error' + })) + } else { + dispatch(addAlert({ + id: nanoid(), + message: 'Error desconocido', + title: 'Error mientras se actualizaba el nombre', + severity: 'error' + })) + } + } + }, + validationSchema: UpdateUserNameSchema, + isInitialValid: false + }) + + if (status !== 'idle' || session === undefined) { + // TODO: Replace with Skeleton + return null + } + return ( + <> + Cambia tu nombre de usuario +
+ + + + {formik.touched.name !== undefined && formik.errors.name !== undefined && ( + {formik.errors.name} + )} + + + + +
+ + ) +} +export default UpdateUserName diff --git a/src/app/cuenta/page.tsx b/src/app/cuenta/page.tsx new file mode 100644 index 0000000..6cb223e --- /dev/null +++ b/src/app/cuenta/page.tsx @@ -0,0 +1,18 @@ +import UpdateEmail from '@/app/cuenta/UpdateEmail' +import UpdatePassword from '@/app/cuenta/UpdatePassword' +import UpdateUserName from '@/app/cuenta/UpdateUserName' +import Typography from '@/components/ui/Typography' +import { Container } from '@/styled-system/jsx' +import { type FC } from 'react' + +const CuentaPage: FC = () => { + return ( + + Cuenta + + + + + ) +} +export default CuentaPage diff --git a/src/app/layout.tsx b/src/app/layout.tsx index b1207fc..8d328f5 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -11,6 +11,7 @@ import '@fortawesome/fontawesome-svg-core/styles.css' import { type Metadata } from 'next' import { type FC, type ReactNode } from 'react' import FeedbackConsumer from './FeedbackConsumer' +import SessionConsumer from './SessionConsumer' import StateProvider from './StateProvider' config.autoAddCss = false @@ -40,6 +41,7 @@ const RootLayout: FC = ({ children }) => {