mirror of
https://github.com/Telecominfraproject/wlan-cloud-ucentral-ui-libs.git
synced 2025-11-01 11:17:47 +00:00
New login, create user form, user list components
This commit is contained in:
@@ -18,7 +18,6 @@ import {
|
||||
import PropTypes from 'prop-types';
|
||||
import LoadingButton from 'components/LoadingButton';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const CreateUserForm = ({ t, fields, updateField, createUser, loading, policies }) => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
@@ -129,7 +128,7 @@ const CreateUserForm = ({ t, fields, updateField, createUser, loading, policies
|
||||
</CFormGroup>
|
||||
<CRow>
|
||||
<CCol />
|
||||
<CCol xs={3} className={styles.linksColumn}>
|
||||
<CCol xs={3} className="mt-2 text-right">
|
||||
<CLink
|
||||
className="c-subheader-nav-link"
|
||||
aria-current="page"
|
||||
@@ -150,13 +149,13 @@ const CreateUserForm = ({ t, fields, updateField, createUser, loading, policies
|
||||
{t('common.password_policy')}
|
||||
</CLink>
|
||||
</CCol>
|
||||
<CCol xs={2}>
|
||||
<CCol xs={1} className="text-right">
|
||||
<LoadingButton
|
||||
label={t('user.create')}
|
||||
isLoadingLabel={t('common.loading_ellipsis')}
|
||||
isLoading={loading}
|
||||
action={createUser}
|
||||
block
|
||||
block={false}
|
||||
disabled={loading}
|
||||
/>
|
||||
</CCol>
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
.linksColumn {
|
||||
text-align: right;
|
||||
padding-top: 5px;
|
||||
}
|
||||
19
src/components/ImgWithFallback/index.js
Normal file
19
src/components/ImgWithFallback/index.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { CImg } from '@coreui/react';
|
||||
|
||||
const ImgWithFallback = ({ src, fallback }) => {
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
if (error) {
|
||||
return <div className="avatar bg-secondary">{fallback()}</div>;
|
||||
}
|
||||
|
||||
return <CImg className="avatar" src={src} onError={() => setError(true)} />;
|
||||
};
|
||||
|
||||
ImgWithFallback.propTypes = {
|
||||
src: PropTypes.string.isRequired,
|
||||
fallback: PropTypes.func.isRequired,
|
||||
};
|
||||
export default React.memo(ImgWithFallback);
|
||||
@@ -42,4 +42,4 @@ LoadingButton.defaultProps = {
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
export default LoadingButton;
|
||||
export default React.memo(LoadingButton);
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
CPopover,
|
||||
CAlert,
|
||||
CInvalidFeedback,
|
||||
CLink,
|
||||
} from '@coreui/react';
|
||||
import PropTypes from 'prop-types';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
@@ -29,6 +30,7 @@ const ChangePasswordForm = ({
|
||||
updateField,
|
||||
changePasswordResponse,
|
||||
cancelPasswordChange,
|
||||
policies,
|
||||
}) => (
|
||||
<CForm onKeyDown={(e) => onKeyDown(e, signIn)}>
|
||||
<h1>
|
||||
@@ -88,13 +90,31 @@ const ChangePasswordForm = ({
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow>
|
||||
<CCol xs="6">
|
||||
<CCol>
|
||||
<CButton color="primary" className="px-4" onClick={() => signIn(true)} disabled={loading}>
|
||||
{loading ? t('login.changing_password') : t('login.change_password')}
|
||||
<CSpinner hidden={!loading} color="light" component="span" size="sm" />
|
||||
</CButton>
|
||||
<CLink
|
||||
className="c-subheader-nav-link px-3"
|
||||
aria-current="page"
|
||||
href={policies.accessPolicy}
|
||||
target="_blank"
|
||||
hidden={policies.accessPolicy.length === 0}
|
||||
>
|
||||
{t('common.access_policy')}
|
||||
</CLink>
|
||||
<CLink
|
||||
className="c-subheader-nav-link"
|
||||
aria-current="page"
|
||||
href={policies.passwordPolicy}
|
||||
target="_blank"
|
||||
hidden={policies.passwordPolicy.length === 0}
|
||||
>
|
||||
{t('common.password_policy')}
|
||||
</CLink>
|
||||
</CCol>
|
||||
<CCol xs="6" className={styles.forgotPassword}>
|
||||
<CCol xs="5" className={styles.forgotPassword}>
|
||||
<CButton variant="ghost" color="primary" onClick={cancelPasswordChange}>
|
||||
{t('common.cancel')}
|
||||
</CButton>
|
||||
@@ -113,6 +133,7 @@ ChangePasswordForm.propTypes = {
|
||||
updateField: PropTypes.func.isRequired,
|
||||
changePasswordResponse: PropTypes.instanceOf(Object).isRequired,
|
||||
cancelPasswordChange: PropTypes.func.isRequired,
|
||||
policies: PropTypes.instanceOf(Object).isRequired,
|
||||
};
|
||||
|
||||
export default ChangePasswordForm;
|
||||
export default React.memo(ChangePasswordForm);
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
CPopover,
|
||||
CAlert,
|
||||
CInvalidFeedback,
|
||||
CLink,
|
||||
} from '@coreui/react';
|
||||
import PropTypes from 'prop-types';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
@@ -29,6 +30,7 @@ const ForgotPasswordForm = ({
|
||||
forgotResponse,
|
||||
updateField,
|
||||
toggleForgotPassword,
|
||||
policies,
|
||||
}) => (
|
||||
<CForm onKeyDown={(e) => onKeyDown(e, sendForgotPasswordEmail)}>
|
||||
<h1>
|
||||
@@ -87,7 +89,7 @@ const ForgotPasswordForm = ({
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow>
|
||||
<CCol xs="6">
|
||||
<CCol>
|
||||
<CButton
|
||||
color="primary"
|
||||
className="px-4"
|
||||
@@ -97,8 +99,26 @@ const ForgotPasswordForm = ({
|
||||
{loading ? t('login.sending_ellipsis') : t('login.send_forgot')}
|
||||
<CSpinner hidden={!loading} color="light" component="span" size="sm" />
|
||||
</CButton>
|
||||
<CLink
|
||||
className="c-subheader-nav-link px-3"
|
||||
aria-current="page"
|
||||
href={policies.accessPolicy}
|
||||
target="_blank"
|
||||
hidden={policies.accessPolicy.length === 0}
|
||||
>
|
||||
{t('common.access_policy')}
|
||||
</CLink>
|
||||
<CLink
|
||||
className="c-subheader-nav-link"
|
||||
aria-current="page"
|
||||
href={policies.passwordPolicy}
|
||||
target="_blank"
|
||||
hidden={policies.passwordPolicy.length === 0}
|
||||
>
|
||||
{t('common.password_policy')}
|
||||
</CLink>
|
||||
</CCol>
|
||||
<CCol xs="6" className={styles.forgotPassword}>
|
||||
<CCol xs="5" className={styles.forgotPassword}>
|
||||
<CButton variant="ghost" color="primary" onClick={toggleForgotPassword}>
|
||||
{t('common.back_to_login')}
|
||||
</CButton>
|
||||
@@ -117,6 +137,7 @@ ForgotPasswordForm.propTypes = {
|
||||
fields: PropTypes.instanceOf(Object).isRequired,
|
||||
updateField: PropTypes.func.isRequired,
|
||||
toggleForgotPassword: PropTypes.func.isRequired,
|
||||
policies: PropTypes.instanceOf(Object).isRequired,
|
||||
};
|
||||
|
||||
export default ForgotPasswordForm;
|
||||
export default React.memo(ForgotPasswordForm);
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
CPopover,
|
||||
CAlert,
|
||||
CInvalidFeedback,
|
||||
CLink,
|
||||
} from '@coreui/react';
|
||||
import PropTypes from 'prop-types';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
@@ -29,6 +30,7 @@ const LoginForm = ({
|
||||
updateField,
|
||||
loginResponse,
|
||||
toggleForgotPassword,
|
||||
policies,
|
||||
}) => (
|
||||
<CForm onKeyDown={(e) => onKeyDown(e, signIn)}>
|
||||
<h1>
|
||||
@@ -105,13 +107,31 @@ const LoginForm = ({
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow>
|
||||
<CCol xs="6">
|
||||
<CCol>
|
||||
<CButton color="primary" className="px-4" onClick={signIn} disabled={loading}>
|
||||
{loading ? t('login.logging_in') : t('login.login')}
|
||||
<CSpinner hidden={!loading} color="light" component="span" size="sm" />
|
||||
</CButton>
|
||||
<CLink
|
||||
className="c-subheader-nav-link px-3"
|
||||
aria-current="page"
|
||||
href={policies.accessPolicy}
|
||||
target="_blank"
|
||||
hidden={policies.accessPolicy.length === 0}
|
||||
>
|
||||
{t('common.access_policy')}
|
||||
</CLink>
|
||||
<CLink
|
||||
className="c-subheader-nav-link"
|
||||
aria-current="page"
|
||||
href={policies.passwordPolicy}
|
||||
target="_blank"
|
||||
hidden={policies.passwordPolicy.length === 0}
|
||||
>
|
||||
{t('common.password_policy')}
|
||||
</CLink>
|
||||
</CCol>
|
||||
<CCol xs="6" className={styles.forgotPassword}>
|
||||
<CCol xs="5" className="text-right">
|
||||
<CButton variant="ghost" color="primary" onClick={toggleForgotPassword}>
|
||||
{t('common.forgot_password')}
|
||||
</CButton>
|
||||
@@ -130,6 +150,7 @@ LoginForm.propTypes = {
|
||||
updateField: PropTypes.func.isRequired,
|
||||
loginResponse: PropTypes.instanceOf(Object).isRequired,
|
||||
toggleForgotPassword: PropTypes.func.isRequired,
|
||||
policies: PropTypes.instanceOf(Object).isRequired,
|
||||
};
|
||||
|
||||
export default LoginForm;
|
||||
export default React.memo(LoginForm);
|
||||
|
||||
@@ -22,6 +22,7 @@ const LoginPage = ({
|
||||
sendForgotPasswordEmail,
|
||||
changePasswordResponse,
|
||||
cancelPasswordChange,
|
||||
policies,
|
||||
}) => {
|
||||
const getForm = () => {
|
||||
if (!isLogin) {
|
||||
@@ -37,6 +38,7 @@ const LoginPage = ({
|
||||
forgotResponse={forgotResponse}
|
||||
toggleForgotPassword={toggleForgotPassword}
|
||||
sendForgotPasswordEmail={sendForgotPasswordEmail}
|
||||
policies={policies}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -52,6 +54,7 @@ const LoginPage = ({
|
||||
updateField={updateField}
|
||||
changePasswordResponse={changePasswordResponse}
|
||||
cancelPasswordChange={cancelPasswordChange}
|
||||
policies={policies}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -66,6 +69,7 @@ const LoginPage = ({
|
||||
updateField={updateField}
|
||||
loginResponse={loginResponse}
|
||||
toggleForgotPassword={toggleForgotPassword}
|
||||
policies={policies}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -81,7 +85,7 @@ const LoginPage = ({
|
||||
alt="OpenWifi"
|
||||
/>
|
||||
<CCardGroup>
|
||||
<CCard className="p-4">
|
||||
<CCard className="p-4" color={isLogin && isPasswordChange ? 'secondary' : ''}>
|
||||
<CCardBody>{getForm()}</CCardBody>
|
||||
</CCard>
|
||||
</CCardGroup>
|
||||
@@ -108,6 +112,7 @@ LoginPage.propTypes = {
|
||||
sendForgotPasswordEmail: PropTypes.func.isRequired,
|
||||
changePasswordResponse: PropTypes.instanceOf(Object).isRequired,
|
||||
cancelPasswordChange: PropTypes.func.isRequired,
|
||||
policies: PropTypes.instanceOf(Object).isRequired,
|
||||
};
|
||||
|
||||
export default React.memo(LoginPage);
|
||||
|
||||
@@ -11,8 +11,9 @@ import {
|
||||
CDataTable,
|
||||
CPopover,
|
||||
CButton,
|
||||
CLink,
|
||||
} from '@coreui/react';
|
||||
import { cilBan, cilCheckCircle, cilTrash } from '@coreui/icons';
|
||||
import { cilBan, cilCheckCircle, cilInfo, cilPlus, cilTrash } from '@coreui/icons';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { capitalizeFirstLetter, prettyDate } from '../../utils/formatting';
|
||||
import DeleteModal from '../DeleteModal';
|
||||
@@ -49,13 +50,13 @@ const UserListTable = ({
|
||||
{ key: 'email', label: t('user.login_id'), _style: { width: '20%' } },
|
||||
{ key: 'name', label: t('user.name'), _style: { width: '20%' } },
|
||||
{ key: 'userRole', label: t('user.user_role'), _style: { width: '5%' } },
|
||||
{ key: 'description', label: t('user.description'), _style: { width: '26%' } },
|
||||
{ key: 'description', label: t('user.description'), _style: { width: '25%' } },
|
||||
{ key: 'validated', label: t('user.validated'), _style: { width: '5%' } },
|
||||
{ key: 'lastLogin', label: t('user.last_login'), _style: { width: '20%' } },
|
||||
{
|
||||
key: 'user_actions',
|
||||
label: '',
|
||||
_style: { width: '4%' },
|
||||
_style: { width: '5%' },
|
||||
sorter: false,
|
||||
filter: false,
|
||||
},
|
||||
@@ -88,9 +89,26 @@ const UserListTable = ({
|
||||
loading={loading}
|
||||
hover
|
||||
border
|
||||
columnHeaderSlot={{
|
||||
user_actions: (
|
||||
<div className="text-center">
|
||||
<CPopover content={t('user.create')}>
|
||||
<CLink
|
||||
className="c-subheader-nav-link"
|
||||
aria-current="page"
|
||||
to={() => `/users/create`}
|
||||
>
|
||||
<CButton color="primary" variant="outline" shape="square" size="sm">
|
||||
<CIcon name="cil-info" content={cilPlus} size="sm" />
|
||||
</CButton>
|
||||
</CLink>
|
||||
</CPopover>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
scopedSlots={{
|
||||
validated: (item) => (
|
||||
<td className="centeredColumn">
|
||||
<td className="text-center">
|
||||
<CPopover
|
||||
content={item.validated ? t('user.validated') : t('user.not_validated')}
|
||||
>
|
||||
@@ -103,17 +121,34 @@ const UserListTable = ({
|
||||
<td>{item.userRole ? capitalizeFirstLetter(item.userRole) : ''}</td>
|
||||
),
|
||||
user_actions: (item) => (
|
||||
<td className="py-2">
|
||||
<CPopover content={t('common.delete')}>
|
||||
<CButton
|
||||
onClick={() => handleDeleteClick(item.Id)}
|
||||
color="primary"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
>
|
||||
<CIcon content={cilTrash} size="sm" />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
<td className="py-2 text-center">
|
||||
<CRow>
|
||||
<CCol>
|
||||
<CPopover content={t('configuration.details')}>
|
||||
<CLink
|
||||
className="c-subheader-nav-link"
|
||||
aria-current="page"
|
||||
to={() => `/users/${item.Id}`}
|
||||
>
|
||||
<CButton color="primary" variant="outline" shape="square" size="sm">
|
||||
<CIcon name="cil-info" content={cilInfo} size="sm" />
|
||||
</CButton>
|
||||
</CLink>
|
||||
</CPopover>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<CPopover content={t('common.delete')}>
|
||||
<CButton
|
||||
onClick={() => handleDeleteClick(item.Id)}
|
||||
color="primary"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
>
|
||||
<CIcon content={cilTrash} size="sm" />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</td>
|
||||
),
|
||||
}}
|
||||
|
||||
139
src/components/UserProfileCard/index.js
Normal file
139
src/components/UserProfileCard/index.js
Normal file
@@ -0,0 +1,139 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
CButton,
|
||||
CCard,
|
||||
CCardBody,
|
||||
CCardHeader,
|
||||
CCol,
|
||||
CForm,
|
||||
CFormGroup,
|
||||
CInput,
|
||||
CInputGroup,
|
||||
CInputGroupAppend,
|
||||
CInvalidFeedback,
|
||||
CLabel,
|
||||
CPopover,
|
||||
CRow,
|
||||
CSelect,
|
||||
CSwitch,
|
||||
} from '@coreui/react';
|
||||
import PropTypes from 'prop-types';
|
||||
import LoadingButton from 'components/LoadingButton';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
|
||||
const CreateUserForm = ({ t, user, updateUserWithId, loading, saveUser }) => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
const toggleShowPassword = () => {
|
||||
setShowPassword(!showPassword);
|
||||
};
|
||||
|
||||
return (
|
||||
<CCard>
|
||||
<CCardHeader>{t('common.details')}</CCardHeader>
|
||||
<CCardBody>
|
||||
<CForm>
|
||||
<CFormGroup row>
|
||||
<CCol>
|
||||
<CLabel htmlFor="email">{t('user.email_address')}</CLabel>
|
||||
<p className="form-control-static mt-2">{user.email.value}</p>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<CLabel htmlFor="name">{t('user.name')}</CLabel>
|
||||
<CInput
|
||||
id="name"
|
||||
value={user.name.value}
|
||||
onChange={updateUserWithId}
|
||||
maxLength="20"
|
||||
/>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<CLabel htmlFor="changePassword">{t('user.force_password_change')}</CLabel>
|
||||
<CInputGroup>
|
||||
<CSwitch
|
||||
id="changePassword"
|
||||
color="success"
|
||||
defaultChecked={user.changePassword.value}
|
||||
onClick={updateUserWithId}
|
||||
size="lg"
|
||||
/>
|
||||
</CInputGroup>
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol>
|
||||
<CLabel htmlFor="userRole">{t('user.user_role')}</CLabel>
|
||||
<CSelect custom id="userRole" defaultValue="Admin" onChange={updateUserWithId}>
|
||||
<option value="admin">Admin</option>
|
||||
<option value="csr">CSR</option>
|
||||
<option value="root">Root</option>
|
||||
<option value="special">Special</option>
|
||||
<option value="sub">Sub</option>
|
||||
<option value="system">System</option>
|
||||
</CSelect>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<CLabel htmlFor="newPascurrentPasswordsword">{t('login.new_password')}</CLabel>
|
||||
<CInputGroup>
|
||||
<CInput
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
id="currentPassword"
|
||||
value={user.currentPassword.value}
|
||||
onChange={updateUserWithId}
|
||||
invalid={user.currentPassword.error}
|
||||
maxLength="50"
|
||||
/>
|
||||
<CInputGroupAppend>
|
||||
<CPopover content={t('user.show_hide_password')}>
|
||||
<CButton type="button" onClick={toggleShowPassword} color="secondary">
|
||||
<CIcon
|
||||
name={showPassword ? 'cil-envelope-open' : 'cil-envelope-closed'}
|
||||
size="sm"
|
||||
/>
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</CInputGroupAppend>
|
||||
<CInvalidFeedback>{t('user.provide_password')}</CInvalidFeedback>
|
||||
</CInputGroup>
|
||||
</CCol>
|
||||
</CFormGroup>
|
||||
<CFormGroup row>
|
||||
<CCol>
|
||||
<CLabel htmlFor="description">{t('user.description')}</CLabel>
|
||||
<CInput
|
||||
id="description"
|
||||
value={user.description.value}
|
||||
onChange={updateUserWithId}
|
||||
maxLength="50"
|
||||
/>
|
||||
</CCol>
|
||||
<CCol />
|
||||
</CFormGroup>
|
||||
<CRow>
|
||||
<CCol />
|
||||
<CCol xs={3} className="text-right">
|
||||
<LoadingButton
|
||||
label={t('common.save')}
|
||||
isLoadingLabel={t('common.saving')}
|
||||
isLoading={loading}
|
||||
action={saveUser}
|
||||
block={false}
|
||||
disabled={loading}
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CForm>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
);
|
||||
};
|
||||
|
||||
CreateUserForm.propTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
user: PropTypes.instanceOf(Object).isRequired,
|
||||
updateUserWithId: PropTypes.func.isRequired,
|
||||
loading: PropTypes.bool.isRequired,
|
||||
saveUser: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default React.memo(CreateUserForm);
|
||||
31
src/hooks/useUser/index.js
Normal file
31
src/hooks/useUser/index.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
export default (initialState) => {
|
||||
const [user, setUser] = useState(initialState);
|
||||
|
||||
return [
|
||||
user,
|
||||
(e) => {
|
||||
setUser({
|
||||
...user,
|
||||
[e.target.id]: {
|
||||
...user[e.target.id],
|
||||
value: e.target.value,
|
||||
error: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
(key, newValues) => {
|
||||
setUser({
|
||||
...user,
|
||||
[user]: {
|
||||
...user[key],
|
||||
...newValues,
|
||||
},
|
||||
});
|
||||
},
|
||||
(newUser) => {
|
||||
setUser(newUser);
|
||||
},
|
||||
];
|
||||
};
|
||||
@@ -10,9 +10,11 @@ export { default as UserListTable } from './components/UserListTable';
|
||||
export { default as CreateUserForm } from './components/CreateUserForm';
|
||||
export { default as LoadingButton } from './components/LoadingButton';
|
||||
export { default as ConfirmFooter } from './components/ConfirmFooter';
|
||||
export { default as UserProfileCard } from './components/UserProfileCard';
|
||||
|
||||
// Pages
|
||||
export { default as LoginPage } from './components/LoginPage';
|
||||
|
||||
// Hooks
|
||||
export { default as useFormFields } from './hooks/FormFields';
|
||||
export { default as useFormFields } from './hooks/useFormFields';
|
||||
export { default as useUser } from './hooks/useUser';
|
||||
|
||||
@@ -6,15 +6,29 @@ import {
|
||||
CHeaderNav,
|
||||
CSubheader,
|
||||
CBreadcrumbRouter,
|
||||
CLink,
|
||||
CPopover,
|
||||
CDropdown,
|
||||
CDropdownToggle,
|
||||
CDropdownMenu,
|
||||
CDropdownItem,
|
||||
} from '@coreui/react';
|
||||
import PropTypes from 'prop-types';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilAccountLogout } from '@coreui/icons';
|
||||
import LanguageSwitcher from '../../components/LanguageSwitcher';
|
||||
import ImgWithFallback from '../../components/ImgWithFallback';
|
||||
import { emailToName } from '../../utils/formatting';
|
||||
|
||||
const Header = ({ showSidebar, setShowSidebar, routes, t, i18n, logout, authToken, endpoints }) => {
|
||||
const Header = ({
|
||||
showSidebar,
|
||||
setShowSidebar,
|
||||
routes,
|
||||
t,
|
||||
i18n,
|
||||
logout,
|
||||
authToken,
|
||||
endpoints,
|
||||
user,
|
||||
}) => {
|
||||
const [translatedRoutes, setTranslatedRoutes] = useState(routes);
|
||||
|
||||
const toggleSidebar = () => {
|
||||
@@ -45,17 +59,26 @@ const Header = ({ showSidebar, setShowSidebar, routes, t, i18n, logout, authToke
|
||||
<LanguageSwitcher i18n={i18n} />
|
||||
</CHeaderNav>
|
||||
|
||||
<CHeaderNav className="px-3">
|
||||
<CPopover content={t('common.logout')}>
|
||||
<CLink className="c-subheader-nav-link">
|
||||
<CIcon
|
||||
name="cilAccountLogout"
|
||||
content={cilAccountLogout}
|
||||
size="2xl"
|
||||
onClick={() => logout(authToken, endpoints.ucentralsec)}
|
||||
/>
|
||||
</CLink>
|
||||
</CPopover>
|
||||
<CHeaderNav className="px-1">
|
||||
<CDropdown inNav className="c-header-nav-items mx-2" direction="down">
|
||||
<CDropdownToggle className="c-header-nav-link" caret={false}>
|
||||
<div className="c-avatar">
|
||||
<ImgWithFallback
|
||||
src={user.avatar && user.avatar !== '' ? user.avatar : '/'}
|
||||
fallback={() => emailToName(user.email)}
|
||||
/>
|
||||
</div>
|
||||
</CDropdownToggle>
|
||||
<CDropdownMenu className="pt-0" placement="bottom-end">
|
||||
<CDropdownItem>
|
||||
<div className="px-3">My Account</div>
|
||||
</CDropdownItem>
|
||||
<CDropdownItem onClick={() => logout(authToken, endpoints.ucentralsec)}>
|
||||
<strong className="px-3">Logout</strong>
|
||||
<CIcon name="cilAccountLogout" content={cilAccountLogout} />
|
||||
</CDropdownItem>
|
||||
</CDropdownMenu>
|
||||
</CDropdown>
|
||||
</CHeaderNav>
|
||||
|
||||
<CSubheader className="px-3 justify-content-between">
|
||||
@@ -77,6 +100,7 @@ Header.propTypes = {
|
||||
logout: PropTypes.func.isRequired,
|
||||
authToken: PropTypes.string.isRequired,
|
||||
endpoints: PropTypes.instanceOf(Object).isRequired,
|
||||
user: PropTypes.instanceOf(Object).isRequired,
|
||||
};
|
||||
|
||||
export default React.memo(Header);
|
||||
|
||||
@@ -17,3 +17,15 @@ export const prettyDate = (dateString) => {
|
||||
};
|
||||
|
||||
export const capitalizeFirstLetter = (string) => string.charAt(0).toUpperCase() + string.slice(1);
|
||||
|
||||
export const emailToName = (email) => {
|
||||
if (email) {
|
||||
const pre = email.split('@')[0];
|
||||
if (!pre.includes('.')) {
|
||||
return `${pre.substring(0, 2).toUpperCase()}`;
|
||||
}
|
||||
const parts = pre.split('.');
|
||||
return `${parts[0].charAt(0).toUpperCase()}${parts[1].charAt(0).toUpperCase()}`;
|
||||
}
|
||||
return 'N/A';
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user