Edit user, edit profile, create user form

This commit is contained in:
bourquecharles
2021-07-20 16:53:27 -04:00
parent b69527cc2f
commit c3562b48bd
12 changed files with 554 additions and 258 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "ucentral-libs",
"version": "0.8.15",
"version": "0.8.16",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "ucentral-libs",
"version": "0.8.15",
"version": "0.8.16",
"devDependencies": {
"@babel/core": "^7.14.6",
"@babel/plugin-proposal-class-properties": "^7.14.5",

View File

@@ -1,6 +1,6 @@
{
"name": "ucentral-libs",
"version": "0.8.15",
"version": "0.8.16",
"main": "dist/index.js",
"source": "src/index.js",
"engines": {

View File

@@ -28,7 +28,7 @@ const CreateUserForm = ({ t, fields, updateField, createUser, loading, policies
return (
<CForm>
<CFormGroup row>
<CFormGroup row className="pb-3">
<CLabel sm="2" col htmlFor="email">
{t('user.email_address')}
</CLabel>
@@ -42,14 +42,41 @@ const CreateUserForm = ({ t, fields, updateField, createUser, loading, policies
/>
<CInvalidFeedback>{t('user.provide_email')}</CInvalidFeedback>
</CCol>
<CLabel sm="2" col htmlFor="userRole">
{t('user.user_role')}
</CLabel>
<CCol sm="4">
<CSelect custom id="userRole" defaultValue="Admin" onChange={updateField}>
<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>
</CFormGroup>
<CFormGroup row>
<CLabel sm="2" col htmlFor="name">
{t('user.name')}
</CLabel>
<CCol sm="4">
<CInput id="name" value={fields.name.value} onChange={updateField} maxLength="20" />
</CCol>
<CLabel sm="2" col htmlFor="description">
{t('user.description')}
</CLabel>
<CCol sm="4">
<CInput
id="description"
value={fields.description.value}
onChange={updateField}
maxLength="50"
/>
<small className="text-muted">{t('common.optional')}</small>
</CCol>
</CFormGroup>
<CFormGroup row>
<CFormGroup row className="pb-3">
<CLabel sm="2" col htmlFor="currentPassword">
{t('user.password')}
</CLabel>
@@ -89,19 +116,6 @@ const CreateUserForm = ({ t, fields, updateField, createUser, loading, policies
</CCol>
</CFormGroup>
<CFormGroup row>
<CLabel sm="2" col htmlFor="userRole">
{t('user.user_role')}
</CLabel>
<CCol sm="4">
<CSelect custom id="userRole" defaultValue="Admin" onChange={updateField}>
<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>
<CLabel sm="2" col htmlFor="notes">
{t('user.note')}
</CLabel>
@@ -110,35 +124,9 @@ const CreateUserForm = ({ t, fields, updateField, createUser, loading, policies
<small className="text-muted">{t('common.optional')}</small>
</CCol>
</CFormGroup>
<CFormGroup row>
<CLabel sm="2" col htmlFor="description">
{t('user.description')}
</CLabel>
<CCol sm="4">
<CInput
id="description"
value={fields.description.value}
onChange={updateField}
maxLength="50"
/>
<small className="text-muted">{t('common.optional')}</small>
</CCol>
<CLabel sm="2" col />
<CCol sm="4" />
</CFormGroup>
<CRow>
<CCol />
<CCol xs={3} className="mt-2 text-right">
<CLink
className="c-subheader-nav-link"
aria-current="page"
href={policies.accessPolicy}
target="_blank"
style={{ paddingRight: '30px' }}
hidden={policies.accessPolicy.length === 0}
>
{t('common.access_policy')}
</CLink>
<CCol xs={2} className="mt-2 text-right">
<CLink
className="c-subheader-nav-link"
aria-current="page"
@@ -149,10 +137,10 @@ const CreateUserForm = ({ t, fields, updateField, createUser, loading, policies
{t('common.password_policy')}
</CLink>
</CCol>
<CCol xs={1} className="text-right">
<CCol xs={2} className="text-center">
<LoadingButton
label={t('user.create')}
isLoadingLabel={t('common.loading_ellipsis')}
isLoadingLabel={t('user.creating')}
isLoading={loading}
action={createUser}
block={false}

View File

@@ -0,0 +1,140 @@
import React, { useState } from 'react';
import {
CButton,
CCol,
CForm,
CFormGroup,
CInput,
CInputGroup,
CInputGroupAppend,
CInvalidFeedback,
CLabel,
CLink,
CPopover,
CRow,
CSelect,
} from '@coreui/react';
import PropTypes from 'prop-types';
import CIcon from '@coreui/icons-react';
import NotesTable from '../NotesTable';
import LoadingButton from '../LoadingButton';
const EditMyProfile = ({ t, user, updateUserWithId, loading, saveUser, policies, addNote }) => {
const [showPassword, setShowPassword] = useState(false);
const toggleShowPassword = () => {
setShowPassword(!showPassword);
};
return (
<CForm>
<CFormGroup row>
<CLabel sm="2" col htmlFor="name">
{t('user.name')}
</CLabel>
<CCol sm="4">
<CInput id="name" value={user.name.value} onChange={updateUserWithId} maxLength="20" />
</CCol>
<CLabel sm="2" col htmlFor="description">
{t('user.description')}
</CLabel>
<CCol sm="4">
<CInput
id="description"
value={user.description.value}
onChange={updateUserWithId}
maxLength="50"
/>
</CCol>
</CFormGroup>
<CFormGroup row>
<CLabel sm="2" col htmlFor="userRole">
{t('user.user_role')}
</CLabel>
<CCol sm="4">
<CSelect custom id="userRole" onChange={updateUserWithId} value={user.userRole.value}>
<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>
<CLabel sm="2" col htmlFor="currentPassword">
{t('login.new_password')}
</CLabel>
<CCol sm="4">
<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 sm="6">
<NotesTable
t={t}
notes={user.notes.value}
addNote={addNote}
loading={loading}
size="lg"
/>
</CCol>
</CFormGroup>
<CRow>
<CCol />
<CCol xs={1} className="mt-2 text-right">
<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={1} className="text-center">
<LoadingButton
label={t('common.save')}
isLoadingLabel={t('common.saving')}
isLoading={loading}
action={saveUser}
block={false}
disabled={loading}
/>
</CCol>
</CRow>
</CForm>
);
};
EditMyProfile.propTypes = {
t: PropTypes.func.isRequired,
user: PropTypes.instanceOf(Object).isRequired,
updateUserWithId: PropTypes.func.isRequired,
loading: PropTypes.bool.isRequired,
saveUser: PropTypes.func.isRequired,
policies: PropTypes.instanceOf(Object).isRequired,
addNote: PropTypes.func.isRequired,
};
export default React.memo(EditMyProfile);

View File

@@ -0,0 +1,149 @@
import React, { useState } from 'react';
import {
CButton,
CCol,
CForm,
CFormGroup,
CInput,
CInputGroup,
CInputGroupAppend,
CInvalidFeedback,
CLabel,
CLink,
CPopover,
CRow,
CSelect,
CSwitch,
} from '@coreui/react';
import PropTypes from 'prop-types';
import CIcon from '@coreui/icons-react';
import NotesTable from '../NotesTable';
import LoadingButton from '../LoadingButton';
const EditUserForm = ({ t, user, updateUserWithId, loading, saveUser, policies, addNote }) => {
const [showPassword, setShowPassword] = useState(false);
const toggleShowPassword = () => {
setShowPassword(!showPassword);
};
return (
<CForm>
<CFormGroup row>
<CLabel sm="2" col htmlFor="name">
{t('user.name')}
</CLabel>
<CCol sm="4">
<CInput id="name" value={user.name.value} onChange={updateUserWithId} maxLength="20" />
</CCol>
<CLabel sm="2" col htmlFor="description">
{t('user.description')}
</CLabel>
<CCol sm="4">
<CInput
id="description"
value={user.description.value}
onChange={updateUserWithId}
maxLength="50"
/>
</CCol>
</CFormGroup>
<CFormGroup row>
<CLabel sm="2" col htmlFor="userRole">
{t('user.user_role')}
</CLabel>
<CCol sm="4">
<CSelect custom id="userRole" onChange={updateUserWithId} value={user.userRole.value}>
<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>
<CLabel sm="2" col htmlFor="currentPassword">
{t('login.new_password')}
</CLabel>
<CCol sm="4">
<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>
<CLabel sm="3" col htmlFor="changePassword">
{t('user.force_password_change')}
</CLabel>
<CCol sm="1">
<CInputGroup>
<CSwitch
id="changePassword"
color="success"
defaultChecked={user.changePassword.value}
onClick={updateUserWithId}
size="lg"
/>
</CInputGroup>
</CCol>
<CCol sm="8">
<NotesTable t={t} notes={user.notes.value} addNote={addNote} loading={loading} />
</CCol>
</CFormGroup>
<CRow>
<CCol />
<CCol xs={2} className="mt-2 text-right">
<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={2} className="text-center">
<LoadingButton
label={t('common.save')}
isLoadingLabel={t('common.saving')}
isLoading={loading}
action={saveUser}
block={false}
disabled={loading}
/>
</CCol>
</CRow>
</CForm>
);
};
EditUserForm.propTypes = {
t: PropTypes.func.isRequired,
user: PropTypes.instanceOf(Object).isRequired,
updateUserWithId: PropTypes.func.isRequired,
loading: PropTypes.bool.isRequired,
saveUser: PropTypes.func.isRequired,
policies: PropTypes.instanceOf(Object).isRequired,
addNote: PropTypes.func.isRequired,
};
export default React.memo(EditUserForm);

View File

@@ -0,0 +1,49 @@
import React from 'react';
import PropTypes from 'prop-types';
import { CModal, CModalBody, CModalHeader, CModalTitle } from '@coreui/react';
import EditUserForm from '../EditUserForm';
const EditUserModal = ({
t,
user,
updateUserWithId,
saveUser,
loading,
policies,
show,
toggle,
addNote,
}) => (
<CModal show={show} onClose={toggle} size="xl">
<CModalHeader>
<CModalTitle>
{t('user.edit')} {user.email.value}
</CModalTitle>
</CModalHeader>
<CModalBody>
<EditUserForm
t={t}
user={user}
updateUserWithId={updateUserWithId}
saveUser={saveUser}
loading={loading}
policies={policies}
addNote={addNote}
/>
</CModalBody>
</CModal>
);
EditUserModal.propTypes = {
t: PropTypes.func.isRequired,
user: PropTypes.instanceOf(Object).isRequired,
updateUserWithId: PropTypes.func.isRequired,
loading: PropTypes.bool.isRequired,
saveUser: PropTypes.func.isRequired,
policies: PropTypes.instanceOf(Object).isRequired,
show: PropTypes.bool.isRequired,
toggle: PropTypes.func.isRequired,
addNote: PropTypes.func.isRequired,
};
export default React.memo(EditUserModal);

View File

@@ -0,0 +1,87 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { CDataTable, CRow, CCol, CLabel, CInput } from '@coreui/react';
import { prettyDate } from '../../utils/formatting';
import LoadingButton from '../LoadingButton';
const NotesTable = ({ t, notes, addNote, loading, size }) => {
const [currentNote, setCurrentNote] = useState('');
const columns = [
{ key: 'created', label: t('common.date'), _style: { width: '30%' } },
{ key: 'createdBy', label: t('common.created_by'), _style: { width: '20%' } },
{ key: 'note', label: t('configuration.note'), _style: { width: '50%' } },
];
const saveNote = () => {
addNote(currentNote);
};
useEffect(() => {
setCurrentNote('');
}, [notes]);
return (
<div>
<CRow>
<CLabel col sm="2">
{t('configuration.notes')} :
</CLabel>
<CCol sm={size === 'm' ? '7' : '8'}>
<CInput
id="notes-input"
name="text-input"
value={currentNote}
onChange={(e) => setCurrentNote(e.target.value)}
/>
</CCol>
<CCol sm={size === 'm' ? '3' : '2'}>
<LoadingButton
label={t('common.add')}
isLoadingLabel={t('common.adding_ellipsis')}
isLoading={loading}
action={saveNote}
disabled={loading || currentNote === ''}
/>
</CCol>
</CRow>
<CRow className="pt-3">
<CCol>
<div className="overflow-auto" style={{ height: '200px' }}>
<CDataTable
striped
responsive
border
loading={loading}
fields={columns}
items={notes || []}
noItemsView={{ noItems: t('common.no_items') }}
sorterValue={{ column: 'created', desc: 'true' }}
scopedSlots={{
created: (item) => (
<td>
{item.created && item.created !== 0 ? prettyDate(item.created) : t('common.na')}
</td>
),
}}
/>
</div>
</CCol>
</CRow>
</div>
);
};
NotesTable.propTypes = {
t: PropTypes.func.isRequired,
notes: PropTypes.instanceOf(Array).isRequired,
addNote: PropTypes.func.isRequired,
loading: PropTypes.bool.isRequired,
size: PropTypes.string,
};
NotesTable.defaultProps = {
size: 'm',
};
export default NotesTable;

View File

@@ -2,18 +2,17 @@ import React, { useState } from 'react';
import PropTypes from 'prop-types';
import ReactPaginate from 'react-paginate';
import {
CButton,
CCard,
CCardHeader,
CSelect,
CCol,
CRow,
CCardBody,
CCardHeader,
CCol,
CDataTable,
CPopover,
CButton,
CLink,
CRow,
CSelect,
} from '@coreui/react';
import { cilBan, cilCheckCircle, cilInfo, cilPlus, cilTrash } from '@coreui/icons';
import { cilBan, cilCheckCircle, cilPencil, cilSync, cilTrash } from '@coreui/icons';
import CIcon from '@coreui/icons-react';
import { capitalizeFirstLetter, prettyDate } from '../../utils/formatting';
import DeleteModal from '../DeleteModal';
@@ -28,6 +27,9 @@ const UserListTable = ({
setPage,
deleteUser,
deleteLoading,
toggleCreate,
toggleEdit,
refreshUsers,
}) => {
const [idToDelete, setIdToDelete] = useState('');
const [showDeleteModal, setShowDeleteModal] = useState(false);
@@ -50,13 +52,20 @@ 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: '25%' } },
{ key: 'description', label: t('user.description'), _style: { width: '24%' } },
{ key: 'validated', label: t('user.validated'), _style: { width: '5%' } },
{ key: 'lastLogin', label: t('user.last_login'), _style: { width: '20%' } },
{
key: 'user_actions',
key: 'user_details',
label: '',
_style: { width: '5%' },
_style: { width: '3%' },
sorter: false,
filter: false,
},
{
key: 'user_delete',
label: '',
_style: { width: '3%' },
sorter: false,
filter: false,
},
@@ -68,17 +77,44 @@ const UserListTable = ({
<CCardHeader>
<CRow>
<CCol />
<CCol xs={1}>
<CSelect
custom
defaultValue={usersPerPage}
onChange={(e) => setUsersPerPage(e.target.value)}
disabled={loading}
>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
</CSelect>
<CCol xs={2}>
<CRow>
<CCol xs={5}>
<div className="text-right">
<CSelect
custom
defaultValue={usersPerPage}
onChange={(e) => setUsersPerPage(e.target.value)}
disabled={loading}
>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
</CSelect>
</div>
</CCol>
<CCol xs={5}>
<div className="text-right">
<CButton
color="primary"
variant="outline"
shape="square"
onClick={toggleCreate}
>
{t('user.create')}
</CButton>
</div>
</CCol>
<CCol xs={2}>
<div className="text-center">
<CPopover content={t('common.refresh')}>
<CButton onClick={refreshUsers} color="primary" variant="outline">
<CIcon name="cil-sync" content={cilSync} />
</CButton>
</CPopover>
</div>
</CCol>
</CRow>
</CCol>
</CRow>
</CCardHeader>
@@ -89,23 +125,6 @@ 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="text-center">
@@ -120,35 +139,33 @@ const UserListTable = ({
userRole: (item) => (
<td>{item.userRole ? capitalizeFirstLetter(item.userRole) : ''}</td>
),
user_actions: (item) => (
user_details: (item) => (
<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>
<CPopover content={t('common.edit')}>
<CButton
color="primary"
variant="outline"
shape="square"
size="sm"
onClick={() => toggleEdit(item.Id)}
>
<CIcon name="cil-pencil" content={cilPencil} size="sm" />
</CButton>
</CPopover>
</td>
),
user_delete: (item) => (
<td className="py-2 text-center">
<CPopover content={t('common.delete')}>
<CButton
onClick={() => handleDeleteClick(item.Id)}
color="primary"
variant="outline"
size="sm"
>
<CIcon content={cilTrash} size="sm" />
</CButton>
</CPopover>
</td>
),
}}
@@ -196,6 +213,9 @@ UserListTable.propTypes = {
setPage: PropTypes.func.isRequired,
deleteUser: PropTypes.func.isRequired,
deleteLoading: PropTypes.bool.isRequired,
toggleCreate: PropTypes.func.isRequired,
toggleEdit: PropTypes.func.isRequired,
refreshUsers: PropTypes.func.isRequired,
};
export default React.memo(UserListTable);

View File

@@ -1,139 +0,0 @@
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);

View File

@@ -18,7 +18,7 @@ export default (initialState) => {
(key, newValues) => {
setUser({
...user,
[user]: {
[key]: {
...user[key],
...newValues,
},

View File

@@ -10,7 +10,9 @@ 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';
export { default as EditUserModal } from './components/EditUserModal';
export { default as EditUserForm } from './components/EditUserForm';
export { default as EditMyProfile } from './components/EditMyProfile';
// Pages
export { default as LoginPage } from './components/LoginPage';

View File

@@ -62,7 +62,7 @@ const Header = ({
<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">
<div className="c-avatar avatar">
<ImgWithFallback
src={user.avatar && user.avatar !== '' ? user.avatar : '/'}
fallback={() => emailToName(user.email)}
@@ -70,11 +70,11 @@ const Header = ({
</div>
</CDropdownToggle>
<CDropdownMenu className="pt-0" placement="bottom-end">
<CDropdownItem>
<div className="px-3">My Account</div>
<CDropdownItem to={() => '/myprofile'}>
<div className="px-3">{t('user.my_profile')}</div>
</CDropdownItem>
<CDropdownItem onClick={() => logout(authToken, endpoints.ucentralsec)}>
<strong className="px-3">Logout</strong>
<strong className="px-3">{t('common.logout')}</strong>
<CIcon name="cilAccountLogout" content={cilAccountLogout} />
</CDropdownItem>
</CDropdownMenu>