Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/account page #900

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
73467c7
Fix: Removed unused Variables
anmol-fzr May 8, 2024
e6209d0
Add: UI for Account Page
anmol-fzr May 9, 2024
6a3df00
Refactor: User Logout Code
anmol-fzr May 9, 2024
994a4d0
Add: API for profile updation
anmol-fzr May 9, 2024
e5b42b1
Merge branch 'chaynHQ:develop' into feature/account-page
anmol-fzr May 9, 2024
6fee191
Add: Email Prefs Update func
anmol-fzr May 10, 2024
ef70f98
Add: UI for Account Deletion
anmol-fzr May 10, 2024
b6818f6
Remove unused imports and refactor code in ProfileDetailsForm and Acc…
anmol-fzr May 10, 2024
1f715f5
Add deleteUserMutation to API
anmol-fzr May 10, 2024
915a759
Add account settings translations for multiple languages
anmol-fzr May 10, 2024
7e123d8
Add account settings translations
anmol-fzr May 10, 2024
a8ad55c
Update delete account button label translations
anmol-fzr May 10, 2024
68759d4
Add user profile update test case
anmol-fzr May 10, 2024
e62bd75
Fix: disabled delete account button
anmol-fzr May 10, 2024
962651a
Merge branch 'chaynHQ:develop' into feature/account-page
anmol-fzr May 10, 2024
92673f3
Fix: Linting Errors
anmol-fzr May 11, 2024
1aa202f
Fix: Code Suggestions in Code Review
anmol-fzr May 12, 2024
6c6d761
Removed: Unneccessay code, comments and log statements
anmol-fzr May 12, 2024
f1f83f9
Removed: profile update functionality
anmol-fzr May 12, 2024
1fc3ef3
Tests: fix testing code to match with functionality
anmol-fzr May 12, 2024
a17f6aa
Removed: Account Actions
anmol-fzr May 12, 2024
4cff5d1
Test: add update profile test to check for name and email to be prefi…
anmol-fzr May 17, 2024
079d538
Fix: Margin of EmailPref
anmol-fzr May 17, 2024
244a0ae
Deleted: Account Actions Component
anmol-fzr May 20, 2024
b2cd7de
Removed - Comments
anmol-fzr May 20, 2024
2c2bb33
Add: Error Handling to profile update
anmol-fzr May 20, 2024
b3ae012
Moved: Profile details to seperate component
anmol-fzr May 20, 2024
04313cb
Addon to prev commit
anmol-fzr May 20, 2024
839acf2
test: Add email pref update to update-user-test
anmol-fzr May 20, 2024
8d9ef14
Merge branch 'develop' into feature/account-page
anmol-fzr May 20, 2024
266e858
Merge branch 'develop' into feature/account-page
anmol-fzr May 26, 2024
5faeef7
Merge branch 'develop' into feature/account-page
anmol-fzr Jun 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ export const {
useGetUserMutation,
useAddUserMutation,
useUpdateUserMutation,
useDeleteUserMutation,
useAssignPartnerAccessMutation,
useAddPartnerAccessMutation,
useStartSessionMutation,
Expand Down
84 changes: 84 additions & 0 deletions components/account/DeleteAccountModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { LoadingButton } from '@mui/lab';
import { Box, Button, Modal, Typography } from '@mui/material';
import { useTranslations } from 'next-intl';
import { useDeleteUserMutation } from '../../app/api';
import { getAuth } from 'firebase/auth'

const modalStyle = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: { xs: 'calc(100vw - 2rem)', sm: '90%' },
maxWidth: '700px',
maxHeight: '75vh',
borderRadius: '20px',
bgcolor: 'background.paper',
} as const;

const modalContentStyle = {
maxWidth: 800,
margin: 'auto',
paddingX: { xs: 2, sm: 4 },
paddingY: { xs: 4, sm: 6 },
} as const;

interface Props {
open: boolean;
onClose: () => void
}

const DeleteAccountModal = ({ open, onClose }: Props) => {
const [deleteUser, { isLoading }] = useDeleteUserMutation()

const onUserDelete = () => {
const auth = getAuth()
const currUser = auth.currentUser

if (currUser === null) {
return
}

deleteUser(currUser)
}

const t = useTranslations('Account.accountSettings');

return (
<Modal
{...{ open, onClose }}
aria-labelledby='modal-title'
aria-describedby='modal-description'
>
<Box sx={modalStyle}>
<Box sx={modalContentStyle}>
<Typography id='modal-title' component='h2' variant='h2'>
{t('actions.button.delAcc')} ?
</Typography>
<Typography id='modal-description' fontStyle='italic'>
{t('actions.desc')}
</Typography>
<div style={{ display: 'inline-flex', width: '100%', flexWrap: 'wrap', marginTop: '20px', gap: 8 }}>
<LoadingButton
color='error'
loading={isLoading}
onClick={() => onUserDelete()}
variant='outlined'
>
{t('actions.button.delAcc')}
</LoadingButton>
<Button
color='secondary'
variant='contained'
onClick={onClose}
>
{t('actions.button.cancel')}
</Button>
</div>
</Box>
</Box>
</Modal>
);
};

export default DeleteAccountModal;
97 changes: 97 additions & 0 deletions components/account/EmailPref.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { useCallback, useState } from 'react';
import { Typography, Container, CardContent, Card, Box } from '@mui/material'
import { Checkbox, FormControl, FormControlLabel, } from '@mui/material';
import { LoadingButton } from '@mui/lab';
import { useTranslations } from 'next-intl'
import { useUpdateUserMutation } from '../../app/api';
import { useTypedSelector } from '../../hooks/store';
import { ErrorDisplay } from '../../constants/common';

const formCardStyle = {
width: { xs: '100%', sm: '70%', md: '45%' },
alignSelf: 'flex-start',
} as const;

const EmailPref = () => {
const [updateUser, { isLoading }] = useUpdateUserMutation()
const [error, setError] = useState<ErrorDisplay>();

const t = useTranslations('Account.accountSettings.emailPref');
const tA = useTranslations('Account.accountSettings');

const contactPermission = useTypedSelector(state => state.user.contactPermission)
const servicePermission = useTypedSelector(state => state.user.serviceEmailsPermission)

const onSubmit = useCallback(async (ev: React.FormEvent<HTMLFormElement>) => {
const formData = new FormData(ev.currentTarget)
ev.preventDefault()

const contactPermission = formData.get('contactPermission') === 'on'
const serviceEmailsPermission = formData.get('servicePermission') === 'on'
const payload = {
contactPermission, serviceEmailsPermission
}

try {
await updateUser(payload);
} catch (error) {
setError(tA.rich('updateError'));
}
}, [updateUser, tA])

return (
<Card sx={formCardStyle}>
<CardContent>
<form onSubmit={onSubmit}>
<Typography variant='h2' component='h2'>
{t('title')}
</Typography>
<Typography fontSize='1rem !important'>
{t('desc')}
</Typography>
<Box>
<FormControl sx={{ marginY: 3, display: 'flex', flexDirection: 'column', gap: 2, }} >
<FormControlLabel
label={t('checkbox.emailOnChange')}
control={
<Checkbox
name='contactPermission'
aria-label={t('checkbox.emailOnChange')}
defaultChecked={contactPermission}
/>
}
/>
<FormControlLabel
label={t('checkbox.emailOnCourse')}
control={
<Checkbox
name='servicePermission'
aria-label={t('checkbox.emailOnCourse')}
defaultChecked={servicePermission}
/>
}
/>
{error && (
<Typography color="error.main" mb={'1rem !important'}>
{error}
</Typography>
)}
</FormControl>
</Box>
<LoadingButton
sx={{ mt: 2, mr: 1.5, }}
variant='contained'
fullWidth
loading={isLoading}
color='secondary'
type='submit'
>
{t('button.savePref')}
</LoadingButton>
</form>
</CardContent>
</Card >
)
}

export default EmailPref
28 changes: 28 additions & 0 deletions components/account/ProfileDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Typography, Card, CardContent, } from '@mui/material';
import ProfileDetailsForm from '../forms/ProfileDetailsForm';
import { useTranslations } from 'next-intl';

const formCardStyle = {
width: { xs: '100%', sm: '70%', md: '45%' },
alignSelf: 'flex-start',
} as const;

const ProfileDetails = () => {
const t = useTranslations('Account.accountSettings');

return (
<Card sx={formCardStyle}>
<CardContent>
<Typography variant="h2" component="h2">
{t('profile.title')}
</Typography>
<Typography fontSize="1rem !important">
{t('profile.desc')}
</Typography>
<ProfileDetailsForm />
</CardContent>
</Card>
)
}

export default ProfileDetails
7 changes: 2 additions & 5 deletions components/forms/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import LoadingButton from '@mui/lab/LoadingButton';
import { Box, TextField, Typography } from '@mui/material';
import { getAuth, signInWithEmailAndPassword } from 'firebase/auth';
import { useTranslations } from 'next-intl';
import { useRouter } from 'next/router';
import * as React from 'react';
import { useState } from 'react';
import { useGetUserMutation } from '../../app/api';
Expand Down Expand Up @@ -30,17 +29,16 @@ const containerStyle = {
const LoginForm = () => {
const t = useTranslations('Auth.form');
const tS = useTranslations('Shared');
const router = useRouter();

const [loading, setLoading] = useState<boolean>(false);
const [formError, setFormError] = useState<
| string
| React.ReactNodeArray
| React.ReactNode
| React.ReactElement<any, string | React.JSXElementConstructor<any>>
>();
const [emailInput, setEmailInput] = useState<string>('');
const [passwordInput, setPasswordInput] = useState<string>('');
const [getUser, { isLoading: getUserIsLoading }] = useGetUserMutation();
const [getUser] = useGetUserMutation();
annarhughes marked this conversation as resolved.
Show resolved Hide resolved
const dispatch: any = useAppDispatch();

const submitHandler = async (event: React.FormEvent<HTMLFormElement>) => {
Expand Down Expand Up @@ -91,7 +89,6 @@ const LoginForm = () => {
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;

annarhughes marked this conversation as resolved.
Show resolved Hide resolved
if (errorCode === 'auth/invalid-email') {
setFormError(t('firebase.invalidEmail'));
Expand Down
43 changes: 43 additions & 0 deletions components/forms/ProfileDetailsForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Box, TextField } from '@mui/material';
import { useTranslations } from 'next-intl';
eleanorreem marked this conversation as resolved.
Show resolved Hide resolved
import * as React from 'react';
import { useTypedSelector } from '../../hooks/store';

const containerStyle = {
marginY: 3,
} as const;

const ProfileDetailsForm = () => {
const name = useTypedSelector(state => state.user.name)
const email = useTypedSelector(state => state.user.email)

const tA = useTranslations('Account.accountSettings.form');

return (
<Box sx={containerStyle}>
<form autoComplete="off" >
<TextField
id="name"
value={name}
disabled
label={tA('nameLabel')}
variant="standard"
fullWidth
required
/>
<TextField
id="email"
value={email}
disabled
label={tA('emailLabel')}
variant="standard"
type="email"
fullWidth
required
/>
</form>
</Box>
);
};

export default ProfileDetailsForm;
32 changes: 16 additions & 16 deletions components/layout/UserMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import AddCircleOutline from '@mui/icons-material/AddCircleOutline';
import Logout from '@mui/icons-material/Logout';
import Settings from '@mui/icons-material/Settings';
import Person from '@mui/icons-material/Person';
import { Box, Button, Menu, MenuItem } from '@mui/material';
import { getAuth, signOut } from 'firebase/auth';
import { useTranslations } from 'next-intl';
import { useRouter } from 'next/router';
import * as React from 'react';
import { api } from '../../app/api';
import { clearCoursesSlice } from '../../app/coursesSlice';
import { clearPartnerAccessesSlice } from '../../app/partnerAccessSlice';
import { clearPartnerAdminSlice } from '../../app/partnerAdminSlice';
import { clearUserSlice } from '../../app/userSlice';
import { HEADER_ACCOUNT_ICON_CLICKED, HEADER_APPLY_A_CODE_CLICKED } from '../../constants/events';
import { useAppDispatch, useTypedSelector } from '../../hooks/store';
import { useTypedSelector } from '../../hooks/store';
import logEvent, { getEventUserData } from '../../utils/logEvent';
import Link from '../common/Link';
import useAuth from '../../hooks/useAuth';

const menuItemStyle = {
':hover': { backgroundColor: 'transparent' },
Expand All @@ -38,9 +34,10 @@ const buttonStyle = {
} as const;

export default function UserMenu() {
const { onLogout } = useAuth()

const router = useRouter();
const t = useTranslations('Navigation');
const dispatch: any = useAppDispatch();
const userCreatedAt = useTypedSelector((state) => state.user.createdAt);
const partnerAccesses = useTypedSelector((state) => state.partnerAccesses);
const partnerAdmin = useTypedSelector((state) => state.partnerAdmin);
Expand All @@ -60,14 +57,7 @@ export default function UserMenu() {

const logout = async () => {
// clear all state
// sign out of firebase
const auth = getAuth();
await signOut(auth);
await dispatch(clearPartnerAccessesSlice());
await dispatch(clearPartnerAdminSlice());
await dispatch(clearCoursesSlice());
await dispatch(clearUserSlice());
await dispatch(api.util.resetApiState());
await onLogout()

router.push('/auth/login');
};
Expand Down Expand Up @@ -111,6 +101,16 @@ export default function UserMenu() {
</Button>
</MenuItem>
)}
<MenuItem sx={menuItemStyle}>
<Button
component={Link}
href='/account/settings'
onClick={handleClose}
startIcon={<Settings />}
>
{t('accSettings')}
</Button>
</MenuItem>
<MenuItem sx={menuItemStyle}>
<Button id="logout-button" onClick={logout} startIcon={<Logout />}>
{t('logout')}
Expand Down
5 changes: 5 additions & 0 deletions constants/common.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { ReactElement, JSXElementConstructor, ReactNodeArray } from 'react'
//
// Always use BASE_URL const instead of NEXT_PUBLIC_BASE_URL
// Dynamic vercel preview branches have random wildcard subdomains
// so NEXT_PUBLIC_ENV doesn't work in staging/preview

export type ErrorDisplay = string | ReactElement<any, string | JSXElementConstructor<any>> | ReactNodeArray | null
export const VERCEL_PREVIEW_URL = `https://${process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL}`;
export const BASE_URL =
process.env.NEXT_PUBLIC_ENV === 'staging' ? VERCEL_PREVIEW_URL : process.env.NEXT_PUBLIC_BASE_URL;

1 change: 1 addition & 0 deletions cypress.env.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"badoo_partner_admin_password": "",
"super_admin_email": "",
"super_admin_password": "",
"public_name": "",
"public_email": "",
"public_password": "",
"api_url": ""
Expand Down
Loading
Loading