Skip to content

Commit 525a217

Browse files
committed
Added Account management drawer and sign out
1 parent 0154c45 commit 525a217

11 files changed

Lines changed: 248 additions & 20 deletions

File tree

src/common/constants.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ export const TEXT_GRAY = '#47464f'
99
export const BG_GRAY = '#f6f2f7'
1010
export const PRIMARY_LIGHT = '#c3c0ff'
1111

12+
export const LANGUAGES = [
13+
{locale: 'en', name: 'English'},
14+
{locale: 'es', name: "Español"}
15+
]
16+
1217

1318
// v2
1419
export const BLUE = 'rgb(51, 115, 170)';

src/common/utils.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,37 @@ export const getSiteTitle = () => get(getAppliedServerConfig(), 'info.site.title
681681

682682
export const getRandomColor = () => `#${Math.floor(Math.random()*16777215).toString(16)}`;
683683

684+
export const logoutUser = (redirectToLogin, forced) => {
685+
const clearTokens = () => {
686+
localStorage.removeItem('token');
687+
localStorage.removeItem('id_token');
688+
localStorage.removeItem('user');
689+
localStorage.removeItem('visits');
690+
}
691+
const callback = () => {
692+
clearTokens()
693+
694+
if(redirectToLogin)
695+
window.location.hash = '#/accounts/login';
696+
else {
697+
window.location.hash = '#/';
698+
window.location.reload();
699+
}
700+
}
701+
let redirectURL;
702+
if(forced) {
703+
redirectURL = window.location.origin + '/#/accounts/login?next=' + (window.location.origin + '/'+ window.location.hash)
704+
}
705+
const logoutURL = getSSOLogoutURL(redirectURL)
706+
if(logoutURL) {
707+
clearTokens()
708+
window.location = logoutURL
709+
}
710+
else
711+
callback()
712+
}
713+
714+
684715
export const paramsToParentURI = (params, versioned=false) => {
685716
let uri = '';
686717
if(params.org)

src/components/app/Header.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ const Header = props => {
168168
>
169169
<FolderOpenIcon />
170170
</ListItemIcon>
171-
<ListItemText primary={t('my_repositories.name')} sx={{ opacity: open ? 1 : 0, fontWeight: 500 }} />
171+
<ListItemText primary={t('user.my_repositories')} sx={{ opacity: open ? 1 : 0, fontWeight: 500 }} />
172172
</ListItemButton>
173173
</ListItem>
174174
</List>

src/components/app/HeaderControls.jsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
11
import React from 'react';
22
import { useTranslation } from 'react-i18next'
3-
import PersonIcon from '@mui/icons-material/Face2';
4-
import IconButton from '@mui/material/IconButton';
53
import { getLoginURL, isLoggedIn } from '../../common/utils';
64
import Languages from './Languages';
75
import Button from '../common/Button';
6+
import UserMenu from '../users/UserMenu';
7+
import UserProfileButton from '../users/UserProfileButton';
8+
89

910
const HeaderControls = () => {
1011
const { t } = useTranslation()
12+
const [userMenu, setUserMenu] = React.useState(false)
13+
const authenticated = isLoggedIn()
1114
return (
1215
<div className='col-xs-3 padding-0' style={{textAlign: 'right'}}>
1316
<Languages />
1417
{
15-
isLoggedIn() ?
16-
<IconButton color='primary'>
17-
<PersonIcon fontSize='inherit'/>
18-
</IconButton>:
18+
authenticated ?
19+
<UserProfileButton onClick={() => setUserMenu(true)} /> :
1920
<Button className='default-button-styles' label={t('auth.sign_in')} color='primary' style={{marginLeft: '8px'}} href={getLoginURL()} component='a' />
2021
}
22+
{
23+
authenticated &&
24+
<UserMenu isOpen={userMenu} onClose={() => setUserMenu(false)} />
25+
}
2126
</div>
2227
)
2328
}

src/components/app/Languages.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next";
33
import { ListItemButton, Button } from '@mui/material'
44
import LanguageIcon from '@mui/icons-material/Language';
55
import DownIcon from '@mui/icons-material/ArrowDropDown';
6+
import { LANGUAGES } from '../../common/constants';
67
import PopperGrow from '../common/PopperGrow';
78

89
const Languages = props => {
@@ -32,7 +33,7 @@ const Languages = props => {
3233
<PopperGrow open={open} anchorRef={anchorRef} handleClose={handleClose} minWidth="100px">
3334
<div>
3435
{
35-
[{locale: 'en', name: 'English'}, {locale: 'es', name: "Español"}].map(lang => (
36+
LANGUAGES.map(lang => (
3637
<ListItemButton selected={lang.locale === locale} key={lang.locale} onClick={() => handleClick(lang.locale)}>
3738
{lang.name} - {lang.locale.toUpperCase()}
3839
</ListItemButton>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react';
2+
3+
import CloseIcon from '@mui/icons-material/Close';
4+
import IconButton from '@mui/material/IconButton';
5+
6+
const CloseIconButton = props => (
7+
<IconButton {...props}>
8+
<CloseIcon fontSize='inherit'/>
9+
</IconButton>
10+
)
11+
12+
export default CloseIconButton;

src/components/common/Drawer.jsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React from 'react';
2+
import { Drawer as MuiDrawer } from '@mui/material';
3+
4+
const Drawer = props => {
5+
const [open, setOpen] = React.useState(false)
6+
7+
React.useEffect(() => {
8+
setOpen(props.isOpen)
9+
}, [props.isOpen])
10+
11+
const _onClose = () => {
12+
setOpen(false)
13+
props.onClose()
14+
}
15+
return (
16+
<MuiDrawer
17+
ModalProps={{keepMounted: false}}
18+
anchor={props.anchor || 'right'}
19+
open={open}
20+
onClose={_onClose}
21+
sx={{
22+
zIndex: '1202',
23+
'& .MuiDrawer-paper': { minWidth: '360px', borderRadius: '16px', padding: '12px' },
24+
}}
25+
>
26+
{ props.children }
27+
</MuiDrawer>
28+
)
29+
}
30+
31+
export default Drawer

src/components/users/UserMenu.jsx

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import React from 'react';
2+
import { useTranslation } from 'react-i18next';
3+
import Divider from '@mui/material/Divider';
4+
import ProfileIcon from '@mui/icons-material/AccountCircle';
5+
import List from '@mui/material/List';
6+
import ListItemButton from '@mui/material/ListItemButton';
7+
import ListItemIcon from '@mui/material/ListItemIcon';
8+
import ListItemText from '@mui/material/ListItemText';
9+
import Collapse from '@mui/material/Collapse';
10+
import ExpandLess from '@mui/icons-material/ExpandLess';
11+
import ExpandMore from '@mui/icons-material/ExpandMore';
12+
import RepoIcon from '@mui/icons-material/FolderOutlined';
13+
import OrgIcon from '@mui/icons-material/AccountBalance';
14+
import BookmarkIcon from '@mui/icons-material/BookmarkBorder';
15+
import LanguageIcon from '@mui/icons-material/Language';
16+
import find from 'lodash/find';
17+
import { getCurrentUser, logoutUser } from '../../common/utils'
18+
import { LANGUAGES } from '../../common/constants';
19+
import Button from '../common/Button';
20+
import Drawer from '../common/Drawer';
21+
import UserProfileButton from './UserProfileButton';
22+
import CloseIconButton from '../common/CloseIconButton';
23+
24+
const UserMenu = ({ isOpen, onClose }) => {
25+
const { t, i18n } = useTranslation()
26+
const [languageOpen, setLanguageOpen] = React.useState(false)
27+
const selectedLanguage = find(LANGUAGES, {locale: i18n.language})
28+
const user = getCurrentUser()
29+
const onLanguageSelect = locale => {
30+
i18n.changeLanguage(locale)
31+
setLanguageOpen(false)
32+
}
33+
return (
34+
<Drawer isOpen={isOpen} onClose={onClose}>
35+
<div className='col-xs-12' style={{padding: '15px'}}>
36+
<div className='col-xs-12 padding-0'>
37+
<div className='col-xs-2 padding-0'>
38+
<UserProfileButton />
39+
</div>
40+
<div className='col-xs-9 padding-0'>
41+
<div className='col-xs-12 padding-0'>
42+
<b>{user?.username}</b>
43+
</div>
44+
<div className='col-xs-12 padding-0'>
45+
{user?.name}
46+
</div>
47+
</div>
48+
<div className='col-xs-1 padding-0'>
49+
<CloseIconButton onClick={onClose} />
50+
</div>
51+
</div>
52+
<div className='col-xs-12 padding-0'>
53+
<List>
54+
<ListItemButton sx={{p: 1}}>
55+
<ListItemIcon sx={{minWidth: 'auto', paddingRight: '14px'}}>
56+
<ProfileIcon />
57+
</ListItemIcon>
58+
<ListItemText primary={t('user.my_profile')} />
59+
</ListItemButton>
60+
</List>
61+
</div>
62+
<Divider style={{width: '100%'}} />
63+
<div className='col-xs-12 padding-0'>
64+
<List>
65+
<ListItemButton sx={{p: 1}}>
66+
<ListItemIcon sx={{minWidth: 'auto', paddingRight: '14px'}}>
67+
<RepoIcon />
68+
</ListItemIcon>
69+
<ListItemText primary={t('user.my_repositories')} />
70+
<span>0</span>
71+
</ListItemButton>
72+
<ListItemButton sx={{p: 1}}>
73+
<ListItemIcon sx={{minWidth: 'auto', paddingRight: '14px'}}>
74+
<OrgIcon />
75+
</ListItemIcon>
76+
<ListItemText primary={t('user.my_organizations')} />
77+
<span>0</span>
78+
</ListItemButton>
79+
<ListItemButton sx={{p: 1}}>
80+
<ListItemIcon sx={{minWidth: 'auto', paddingRight: '14px'}}>
81+
<BookmarkIcon />
82+
</ListItemIcon>
83+
<ListItemText primary={t('user.my_bookmarks')} />
84+
<span>0</span>
85+
</ListItemButton>
86+
</List>
87+
</div>
88+
<Divider style={{width: '100%'}} />
89+
<div className='col-xs-12 padding-0'>
90+
<List>
91+
<ListItemButton sx={{p: 1}} onClick={() => setLanguageOpen(!languageOpen)}>
92+
<ListItemIcon sx={{minWidth: 'auto', paddingRight: '14px'}}>
93+
<LanguageIcon />
94+
</ListItemIcon>
95+
<ListItemText primary={selectedLanguage.name} secondary={t('common.language')} />
96+
{languageOpen ? <ExpandLess /> : <ExpandMore />}
97+
</ListItemButton>
98+
<Collapse in={languageOpen} timeout="auto" unmountOnExit>
99+
<List component="div" disablePadding>
100+
{
101+
LANGUAGES.map(lang => (
102+
<ListItemButton sx={{ pl: 5.75 }} selected={lang.locale === selectedLanguage.locale} key={lang.locale} onClick={() => onLanguageSelect(lang.locale)}>
103+
<ListItemText primary={lang.locale.toUpperCase()} secondary={lang.name} />
104+
</ListItemButton>
105+
))
106+
}
107+
</List>
108+
</Collapse>
109+
</List>
110+
</div>
111+
<Divider style={{width: '100%'}} />
112+
<div className='col-xs-12 padding-0' style={{marginTop: '24px'}}>
113+
<Button label={t('auth.sign_out')} sx={{ bgcolor: 'primary.light', maxWidth: '100%' }} onClick={() => logoutUser()} />
114+
</div>
115+
</div>
116+
</Drawer>
117+
)
118+
}
119+
120+
export default UserMenu
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react';
2+
import PersonIcon from '@mui/icons-material/Face2';
3+
import IconButton from '@mui/material/IconButton';
4+
5+
const UserProfileButton = ({ onClick }) => {
6+
return (
7+
<IconButton color='primary' onClick={onClick}>
8+
<PersonIcon fontSize='inherit'/>
9+
</IconButton>
10+
)
11+
}
12+
13+
export default UserProfileButton;
Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"common": {
3-
"or": "or"
3+
"or": "or",
4+
"language": "Language"
45
},
56
"dashboard": {
67
"name": "Dashboard",
@@ -9,17 +10,21 @@
910
"hello": "Hello",
1011
"create_repository": "Create Repository"
1112
},
12-
"my_repositories": {
13-
"name": "My repositories"
14-
},
1513
"bookmarks": {
1614
"name": "Bookmarks"
1715
},
1816
"auth": {
1917
"sign_in": "Sign in",
18+
"sign_out": "Sign out",
2019
"register": "Register",
2120
"signing_in": "Signing in...",
2221
"sign_in_success": "Successfully signed in.",
2322
"sign_in_error": "Unable to signin at the time."
23+
},
24+
"user": {
25+
"my_profile": "My Profile",
26+
"my_repositories": "My repositories",
27+
"my_organizations": "My organizations",
28+
"my_bookmarks": "My bookmarks"
2429
}
2530
}

0 commit comments

Comments
 (0)