Version 2.5.10

This commit is contained in:
bourquecharles
2021-12-01 12:07:50 -05:00
parent 671e0bbf71
commit cdb7eb3da9
9 changed files with 139 additions and 429 deletions

20
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "ucentral-client",
"version": "2.5.9",
"version": "2.5.10",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "ucentral-client",
"version": "2.5.9",
"version": "2.5.10",
"dependencies": {
"@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1",
@@ -32,7 +32,7 @@
"react-tooltip": "^4.2.21",
"react-widgets": "^5.1.1",
"sass": "^1.35.1",
"ucentral-libs": "^1.0.45",
"ucentral-libs": "^1.0.51",
"uuid": "^8.3.2"
},
"devDependencies": {
@@ -15046,9 +15046,9 @@
}
},
"node_modules/ucentral-libs": {
"version": "1.0.45",
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.45.tgz",
"integrity": "sha512-CLLU0u9pKe9PsbkFIRx0VXmOqERhcI3EUUSQhGgdLefu6VyPbkt2jgwjL4ou/KBx+LcW7EixSq3CLmj3WlPlLQ==",
"version": "1.0.51",
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.51.tgz",
"integrity": "sha512-VcWWThbkM0wx5MJ9zlQIohQVnwFHzTy5hvcGnY8J9uIqR3EScA2S6JHnUyPWwQ1qo6G0svgmmDUKeFLW06fZcw==",
"dependencies": {
"@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1",
@@ -15058,6 +15058,7 @@
"libphonenumber-js": "^1.9.37",
"lodash": "^4.17.21",
"react-flow-renderer": "^9.6.6",
"react-i18next": "^11.11.0",
"react-paginate": "^7.1.3",
"react-phone-input-2": "^2.14.0",
"react-router-dom": "^5.2.0",
@@ -28137,9 +28138,9 @@
}
},
"ucentral-libs": {
"version": "1.0.45",
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.45.tgz",
"integrity": "sha512-CLLU0u9pKe9PsbkFIRx0VXmOqERhcI3EUUSQhGgdLefu6VyPbkt2jgwjL4ou/KBx+LcW7EixSq3CLmj3WlPlLQ==",
"version": "1.0.51",
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.51.tgz",
"integrity": "sha512-VcWWThbkM0wx5MJ9zlQIohQVnwFHzTy5hvcGnY8J9uIqR3EScA2S6JHnUyPWwQ1qo6G0svgmmDUKeFLW06fZcw==",
"requires": {
"@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1",
@@ -28149,6 +28150,7 @@
"libphonenumber-js": "^1.9.37",
"lodash": "^4.17.21",
"react-flow-renderer": "^9.6.6",
"react-i18next": "^11.11.0",
"react-paginate": "^7.1.3",
"react-phone-input-2": "^2.14.0",
"react-router-dom": "^5.2.0",

View File

@@ -1,6 +1,6 @@
{
"name": "ucentral-client",
"version": "2.5.9",
"version": "2.5.10",
"dependencies": {
"@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1",
@@ -26,7 +26,7 @@
"react-tooltip": "^4.2.21",
"react-widgets": "^5.1.1",
"sass": "^1.35.1",
"ucentral-libs": "^1.0.45",
"ucentral-libs": "^1.0.51",
"uuid": "^8.3.2"
},
"main": "index.js",

View File

@@ -272,6 +272,7 @@
"contact": {
"access_pin": "Zugangs-PIN",
"add_contact": "Kontakt hinzufügen",
"contact": "Kontakt",
"create_contact": "Kontakt erstellen",
"currently_selected_contact": "Aktuell ausgewählter Kontakt: {{contact}}",
"delete": "Kontakt löschen?",
@@ -347,7 +348,9 @@
"currently_selected_venue": "Aktuell ausgewählter Veranstaltungsort: {{config}}",
"delete_success": "Entität erfolgreich gelöscht",
"delete_warning": "Achtung: Dieser Vorgang kann nicht rückgängig gemacht werden",
"duplicate_from_node": "Mit einem bestimmten Root-Knoten duplizieren",
"duplicate_map": "Karte duplizieren",
"duplicate_with_node": "Dupliziere {{mapName}} mit {{rootName}} als Root-Knoten",
"edit_failure": "Aktualisierung fehlgeschlagen : {{error}}",
"enter_here": "Geben Sie hier die IP(s) ein, die Sie hinzufügen möchten",
"entire_tree": "Netzwerkkarte",
@@ -664,6 +667,19 @@
"uptime": "Betriebszeit",
"used_total_memory": "{{used}} verwendet / {{total}} insgesamt"
},
"subscriber": {
"create": "Abonnenten erstellen",
"edit": "Abonnent bearbeiten",
"error_create": "Fehler beim Erstellen des Abonnenten: {{error}}",
"error_delete": "Fehler beim Löschen des Abonnenten: {{error}}",
"error_fetching": "Fehler beim Abrufen von Abonnenten: {{error}}",
"error_fetching_single": "Fehler beim Abrufen des Abonnenten: {{error}}",
"error_update": "Fehler beim Aktualisieren des Abonnenten: {{error}}",
"subscribers": "Abonnenten",
"success_create": "Abonnent erfolgreich erstellt!",
"success_delete": "Abonnent erfolgreich gelöscht!",
"success_update": "Abonnent erfolgreich aktualisiert!"
},
"system": {
"error_fetching": "Fehler beim Abrufen von Systeminformationen",
"error_reloading": "Fehler beim Neuladen: {{error}}",

View File

@@ -272,6 +272,7 @@
"contact": {
"access_pin": "Access PIN",
"add_contact": "Add Contact",
"contact": "Contact",
"create_contact": "Create Contact",
"currently_selected_contact": "Currently Selected Contact: {{contact}}",
"delete": "Delete Contact?",
@@ -347,7 +348,9 @@
"currently_selected_venue": "Currently Selected Venue: {{config}}",
"delete_success": "Entity Successfully Deleted",
"delete_warning": "Warning: this operation cannot be reverted",
"duplicate_from_node": "Duplicate with specific Root Node",
"duplicate_map": "Duplicate Map",
"duplicate_with_node": "Duplicate {{mapName}} with {{rootName}} as root node",
"edit_failure": "Update unsuccessful : {{error}}",
"enter_here": "Enter the IP(s) you'd like to add here",
"entire_tree": "Network Map",
@@ -664,6 +667,19 @@
"uptime": "Uptime",
"used_total_memory": "{{used}} used / {{total}} total "
},
"subscriber": {
"create": "Create Subscriber",
"edit": "Edit Subscriber",
"error_create": "Error creating subscriber: {{error}}",
"error_delete": "Error deleting subscriber: {{error}}",
"error_fetching": "Error fetching subscribers: {{error}}",
"error_fetching_single": "Error fetching subscriber: {{error}}",
"error_update": "Error updating subscriber: {{error}}",
"subscribers": "Subscribers",
"success_create": "Subscriber successfully created!",
"success_delete": "Subscriber successfully deleted!",
"success_update": "Successfully updated subscriber!"
},
"system": {
"error_fetching": "Error while fetching system information",
"error_reloading": "Error while reloading: {{error}}",

View File

@@ -272,6 +272,7 @@
"contact": {
"access_pin": "PIN de acceso",
"add_contact": "Agregar contacto",
"contact": "Contacto",
"create_contact": "Crear contacto",
"currently_selected_contact": "Contacto seleccionado actualmente: {{contact}}",
"delete": "¿Borrar contacto?",
@@ -347,7 +348,9 @@
"currently_selected_venue": "Lugar seleccionado actualmente: {{config}}",
"delete_success": "Entidad eliminada correctamente",
"delete_warning": "Advertencia: esta operación no se puede revertir",
"duplicate_from_node": "Duplicar con un nodo raíz específico",
"duplicate_map": "Mapa duplicado",
"duplicate_with_node": "Duplicar {{mapName}} con {{rootName}} como nodo raíz",
"edit_failure": "Actualización fallida: {{error}}",
"enter_here": "Ingrese las IP que desea agregar aquí",
"entire_tree": "Mapa de red",
@@ -664,6 +667,19 @@
"uptime": "Tiempo de actividad",
"used_total_memory": "{{used}} usado / {{total}} total"
},
"subscriber": {
"create": "Crear suscriptor",
"edit": "Editar suscriptor",
"error_create": "Error al crear el suscriptor: {{error}}",
"error_delete": "Error al eliminar el suscriptor: {{error}}",
"error_fetching": "Error al obtener suscriptores: {{error}}",
"error_fetching_single": "Error al obtener el suscriptor: {{error}}",
"error_update": "Error al actualizar el suscriptor: {{error}}",
"subscribers": "Suscriptores",
"success_create": "¡Suscriptor creado correctamente!",
"success_delete": "¡Suscriptor eliminado correctamente!",
"success_update": "Suscriptor actualizado con éxito!"
},
"system": {
"error_fetching": "Error al obtener información del sistema",
"error_reloading": "Error al recargar: {{error}}",

View File

@@ -272,6 +272,7 @@
"contact": {
"access_pin": "NIP d'accès",
"add_contact": "Ajouter le contact",
"contact": "Contact",
"create_contact": "Créer un contact",
"currently_selected_contact": "Contact actuellement sélectionné : {{contact}}",
"delete": "Effacer le contact?",
@@ -347,7 +348,9 @@
"currently_selected_venue": "Lieu actuellement sélectionné : {{config}}",
"delete_success": "Entité supprimée avec succès",
"delete_warning": "Attention : cette opération ne peut pas être annulée",
"duplicate_from_node": "Dupliquer avec un nœud racine spécifique",
"duplicate_map": "Carte en double",
"duplicate_with_node": "Dupliquer {{mapName}} avec {{rootName}} comme nœud racine",
"edit_failure": "Échec de la mise à jour : {{error}}",
"enter_here": "Entrez les IP que vous souhaitez ajouter ici",
"entire_tree": "Carte du réseau",
@@ -664,6 +667,19 @@
"uptime": "La disponibilité",
"used_total_memory": "{{used}} utilisé / {{total}} total"
},
"subscriber": {
"create": "Créer un abonné",
"edit": "Modifier l'abonné",
"error_create": "Erreur lors de la création de l'abonné : {{error}}",
"error_delete": "Erreur lors de la suppression de l'abonné : {{error}}",
"error_fetching": "Erreur lors de la récupération des abonnés : {{error}}",
"error_fetching_single": "Erreur lors de la récupération de l'abonné : {{error}}",
"error_update": "Erreur lors de la mise à jour de l'abonné : {{error}}",
"subscribers": "Les abonnés",
"success_create": "Abonné créé avec succès !",
"success_delete": "Abonné supprimé avec succès !",
"success_update": "Abonné mis à jour avec succès !"
},
"system": {
"error_fetching": "Erreur lors de la récupération des informations système",
"error_reloading": "Erreur lors du rechargement : {{error}}",

View File

@@ -272,6 +272,7 @@
"contact": {
"access_pin": "PIN de acesso",
"add_contact": "Adicionar contato",
"contact": "Contato",
"create_contact": "Criar Contato",
"currently_selected_contact": "Contato atualmente selecionado: {{contact}}",
"delete": "Excluir contato?",
@@ -347,7 +348,9 @@
"currently_selected_venue": "Local selecionado atualmente: {{config}}",
"delete_success": "Entidade excluída com sucesso",
"delete_warning": "Aviso: esta operação não pode ser revertida",
"duplicate_from_node": "Duplicar com nó raiz específico",
"duplicate_map": "Mapa duplicado",
"duplicate_with_node": "Duplicar {{mapName}} com {{rootName}} como nó raiz",
"edit_failure": "Atualização malsucedida: {{error}}",
"enter_here": "Digite o (s) IP (s) que deseja adicionar aqui",
"entire_tree": "Mapa de Rede",
@@ -664,6 +667,19 @@
"uptime": "Tempo de atividade",
"used_total_memory": "{{used}} usado / {{total}} total"
},
"subscriber": {
"create": "Criar assinante",
"edit": "Editar Assinante",
"error_create": "Erro ao criar assinante: {{error}}",
"error_delete": "Erro ao excluir assinante: {{error}}",
"error_fetching": "Erro ao buscar assinantes: {{error}}",
"error_fetching_single": "Erro ao buscar assinante: {{error}}",
"error_update": "Erro ao atualizar assinante: {{error}}",
"subscribers": "Inscritos",
"success_create": "Assinante criado com sucesso!",
"success_delete": "Assinante excluído com sucesso!",
"success_update": "Assinante atualizado com sucesso!"
},
"system": {
"error_fetching": "Erro ao buscar informações do sistema",
"error_reloading": "Erro ao recarregar: {{error}}",

View File

@@ -1,6 +1,9 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import routes from 'routes';
import { CSidebarNavItem } from '@coreui/react';
import { cilBarcode, cilRouter, cilSave, cilSettings, cilPeople } from '@coreui/icons';
import CIcon from '@coreui/icons-react';
import { Header, Sidebar, Footer, PageContainer, ToastProvider, useAuth } from 'ucentral-libs';
const TheLayout = () => {
@@ -8,46 +11,52 @@ const TheLayout = () => {
const { endpoints, currentToken, user, avatar, logout } = useAuth();
const { t, i18n } = useTranslation();
const navigation = [
{
_tag: 'CSidebarNavItem',
name: t('common.devices'),
icon: 'cilRouter',
to: '/devices',
},
{
_tag: 'CSidebarNavItem',
name: t('firmware.title'),
icon: 'cilSave',
to: '/firmware',
},
{
_tag: 'CSidebarNavItem',
name: t('configuration.default_configs'),
icon: 'cilBarcode',
to: '/defaultconfigurations',
},
{
_tag: 'CSidebarNavItem',
name: t('user.users'),
to: '/users',
icon: 'cilPeople',
},
{
_tag: 'CSidebarNavItem',
name: t('common.system'),
to: '/system',
icon: 'cilSettings',
},
];
return (
<div className="c-app c-default-layout">
<Sidebar
showSidebar={showSidebar}
setShowSidebar={setShowSidebar}
logo="assets/OpenWiFi_LogoLockup_WhiteColour.svg"
options={navigation}
options={
<>
<CSidebarNavItem
className="font-weight-bold"
name={t('common.devices')}
to="/devices"
icon={<CIcon content={cilRouter} size="xl" className="mr-3" />}
/>
<CSidebarNavItem
className="font-weight-bold"
name={t('firmware.title')}
to="/firmware"
icon={<CIcon content={cilSave} size="xl" className="mr-3" />}
/>
<CSidebarNavItem
className="font-weight-bold"
name={t('configuration.default_configs')}
to="/defaultconfigurations"
icon={<CIcon content={cilBarcode} size="xl" className="mr-3" />}
/>
<CSidebarNavItem
className="font-weight-bold"
name="Configurations"
to="/configuration"
icon={<CIcon content={cilBarcode} size="xl" className="mr-3" />}
/>
<CSidebarNavItem
className="font-weight-bold"
name={t('user.users')}
to="/users"
icon={<CIcon content={cilPeople} size="xl" className="mr-3" />}
/>
<CSidebarNavItem
className="font-weight-bold"
name={t('common.system')}
to="/system"
icon={<CIcon content={cilSettings} size="xl" className="mr-3" />}
/>
</>
}
redirectTo="/devices"
/>
<div className="c-wrapper">

View File

@@ -1,392 +1,11 @@
import React, { useState, useEffect } from 'react';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { CCard, CCardBody, CCardHeader, CButton, CPopover, CButtonToolbar } from '@coreui/react';
import { cilPencil, cilSave, cilSync, cilX } from '@coreui/icons';
import CIcon from '@coreui/icons-react';
import axiosInstance from 'utils/axiosInstance';
import { testRegex } from 'utils/helper';
import { useUser, EditMyProfile, useAuth, useToast } from 'ucentral-libs';
const initialState = {
Id: {
value: '',
error: false,
editable: false,
},
newPassword: {
value: '',
error: false,
editable: true,
ignore: true,
},
confirmNewPassword: {
value: '',
error: false,
editable: true,
ignore: true,
},
email: {
value: '',
error: false,
editable: false,
},
description: {
value: '',
error: false,
editable: true,
},
name: {
value: '',
error: false,
editable: true,
},
notes: {
value: [],
editable: false,
},
userTypeProprietaryInfo: {
value: {},
error: false,
},
mfaMethod: {
value: '',
error: false,
},
};
import { ProfilePage as Page } from 'ucentral-libs';
const ProfilePage = () => {
const { t } = useTranslation();
const { currentToken, endpoints, user, getAvatar, avatar } = useAuth();
const { addToast } = useToast();
const [editing, setEditing] = useState(false);
const [loading, setLoading] = useState(false);
const [userForm, updateWithId, updateWithKey, setUser] = useUser(initialState);
const [newAvatar, setNewAvatar] = useState('');
const [newAvatarFile, setNewAvatarFile] = useState(null);
const [avatarDeleted, setAvatarDeleted] = useState(false);
const [fileInputKey, setFileInputKey] = useState(0);
const [policies, setPolicies] = useState({
passwordPolicy: '',
passwordPattern: '',
accessPolicy: '',
});
const getPasswordPolicy = () => {
axiosInstance
.post(`${endpoints.owsec}/api/v1/oauth2?requirements=true`, {})
.then((response) => {
const newPolicies = response.data;
newPolicies.accessPolicy = `${endpoints.owsec}${newPolicies.accessPolicy}`;
newPolicies.passwordPolicy = `${endpoints.owsec}${newPolicies.passwordPolicy}`;
setPolicies(response.data);
})
.catch(() => {});
};
const getUser = () => {
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
axiosInstance
.get(`${endpoints.owsec}/api/v1/user/${user.Id}`, options)
.then((response) => {
const newUser = {};
for (const key of Object.keys(response.data)) {
if (key in initialState && key !== 'currentPassword') {
newUser[key] = {
...initialState[key],
value: response.data[key],
};
}
}
newUser.mfaMethod = {
value: response.data.userTypeProprietaryInfo.mfa.enabled
? response.data.userTypeProprietaryInfo.mfa.method
: '',
error: false,
};
setUser({ ...initialState, ...newUser });
})
.catch((e) => {
addToast({
title: t('common.error'),
body: t('user.error_fetching_users', { error: e }),
color: 'danger',
autohide: true,
});
});
};
const uploadAvatar = () => {
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
const data = new FormData();
data.append('file', newAvatarFile);
axiosInstance
.post(`${endpoints.owsec}/api/v1/avatar/${user.Id}`, data, options)
.then(() => {
addToast({
title: t('user.update_success_title'),
body: t('user.update_success'),
color: 'success',
autohide: true,
});
getAvatar();
setNewAvatar('');
setNewAvatarFile(null);
setFileInputKey(fileInputKey + 1);
})
.catch(() => {
addToast({
title: t('user.update_failure_title'),
body: t('user.update_failure'),
color: 'danger',
autohide: true,
});
});
};
const updateUser = () => {
setLoading(true);
if (newAvatar !== '' && newAvatarFile !== null) {
uploadAvatar();
} else if (avatarDeleted) {
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
axiosInstance
.delete(`${endpoints.owsec}/api/v1/avatar/${user.Id}`, options)
.then(() => {
getAvatar();
})
.catch(() => {});
}
if (
userForm.newPassword.value !== '' &&
(!testRegex(userForm.newPassword.value, policies.passwordPattern) ||
userForm.newPassword.value !== userForm.confirmNewPassword.value)
) {
updateWithKey('newPassword', {
error: true,
});
setLoading(false);
} else {
const newNotes = [];
for (let i = 0; i < userForm.notes.value.length; i += 1) {
if (userForm.notes.value[i].new) newNotes.push({ note: userForm.notes.value[i].note });
}
const propInfo = { ...userForm.userTypeProprietaryInfo.value };
propInfo.mfa.method = userForm.mfaMethod.value === '' ? undefined : userForm.mfaMethod.value;
propInfo.mfa.enabled = userForm.mfaMethod.value !== '';
const parameters = {
id: user.Id,
description: userForm.description.value,
name: userForm.name.value,
notes: newNotes,
userTypeProprietaryInfo: propInfo,
currentPassword: userForm.newPassword.value !== '' ? userForm.newPassword.value : undefined,
};
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
axiosInstance
.put(`${endpoints.owsec}/api/v1/user/${user.Id}`, parameters, options)
.then(() => {
addToast({
title: t('user.update_success_title'),
body: t('user.update_success'),
color: 'success',
autohide: true,
});
// eslint-disable-next-line no-use-before-define
toggleEditing();
})
.catch((e) => {
addToast({
title: t('user.update_failure_title'),
body: t('user.update_failure', { error: e.response?.data?.ErrorDescription }),
color: 'danger',
autohide: true,
});
})
.finally(() => {
getUser();
setLoading(false);
});
}
};
const addNote = (currentNote) => {
const newNotes = [...userForm.notes.value];
newNotes.unshift({
note: currentNote,
new: true,
created: new Date().getTime() / 1000,
createdBy: '',
});
updateWithKey('notes', { value: newNotes });
};
const showPreview = (e) => {
setAvatarDeleted(false);
const imageFile = e.target.files[0];
setNewAvatar(URL.createObjectURL(imageFile));
setNewAvatarFile(imageFile);
};
const deleteAvatar = () => {
setNewAvatar('');
setAvatarDeleted(true);
};
const sendPhoneNumberTest = async (phoneNumber) => {
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
return axiosInstance
.post(`${endpoints.owsec}/api/v1/sms?validateNumber=true`, { to: phoneNumber }, options)
.then(() => true)
.catch(() => {
addToast({
title: t('common.error'),
body: t('user.error_sending_code'),
color: 'danger',
autohide: true,
});
return false;
});
};
const testVerificationCode = async (phoneNumber, code) => {
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
return axiosInstance
.post(
`${endpoints.owsec}/api/v1/sms?completeValidation=true&validationCode=${code}`,
{ to: phoneNumber },
options,
)
.then(() => true)
.catch(() => {
addToast({
title: t('common.error'),
body: t('user.wrong_validation_code'),
color: 'danger',
autohide: true,
});
return false;
});
};
const toggleEditing = () => {
if (editing) {
setAvatarDeleted(false);
setNewAvatar('');
getUser();
getAvatar();
}
setEditing(!editing);
};
useEffect(() => {
if (user.Id) {
getAvatar();
getUser();
}
if (policies.passwordPattern.length === 0) {
getPasswordPolicy();
}
}, [user.Id]);
return (
<CCard className="my-0 py-0">
<CCardHeader className="dark-header">
<div style={{ fontWeight: '600' }} className=" text-value-lg float-left">
{t('user.my_profile')}
</div>
<div className="text-right float-right">
<CButtonToolbar role="group" className="justify-content-end">
<CPopover content={t('common.save')}>
<CButton disabled={!editing} color="info" onClick={updateUser} className="mx-1">
<CIcon name="cil-save" content={cilSave} />
</CButton>
</CPopover>
<CPopover content={t('common.edit')}>
<CButton disabled={editing} color="dark" onClick={toggleEditing} className="mx-1">
<CIcon name="cil-pencil" content={cilPencil} />
</CButton>
</CPopover>
<CPopover content={t('common.stop_editing')}>
<CButton disabled={!editing} color="dark" onClick={toggleEditing} className="mx-1">
<CIcon name="cil-x" content={cilX} />
</CButton>
</CPopover>
<CPopover content={t('common.refresh')}>
<CButton disabled={editing} color="info" onClick={getUser} className="mx-1">
<CIcon content={cilSync} />
</CButton>
</CPopover>
</CButtonToolbar>
</div>
</CCardHeader>
<CCardBody>
<EditMyProfile
t={t}
user={userForm}
updateUserWithId={updateWithId}
updateWithKey={updateWithKey}
loading={loading}
policies={policies}
addNote={addNote}
avatar={avatar}
newAvatar={newAvatar}
showPreview={showPreview}
deleteAvatar={deleteAvatar}
fileInputKey={fileInputKey}
sendPhoneNumberTest={sendPhoneNumberTest}
testVerificationCode={testVerificationCode}
editing={editing}
avatarDeleted={avatarDeleted}
/>
</CCardBody>
</CCard>
);
return <Page t={t} axiosInstance={axiosInstance} />;
};
export default ProfilePage;