mirror of
https://github.com/optim-enterprises-bv/OptimCloud-gw-ui.git
synced 2025-11-02 03:07:46 +00:00
Merge pull request #6 from stephb9959/main
Option to delete logs and healthchecks, modified how the default config is evaluated
This commit is contained in:
2
.env
2
.env
@@ -1,2 +0,0 @@
|
||||
REACT_APP_DEFAULT_GATEWAY_URL=https://ucentral.dpaas.arilia.com:16001
|
||||
REACT_APP_ALLOW_GATEWAY_CHANGE=false
|
||||
14
README.md
14
README.md
@@ -23,16 +23,4 @@ git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui
|
||||
cd wlan-cloud-ucentralgw-ui
|
||||
npm run build
|
||||
```
|
||||
Once the build is done, you can move the `build` folder on your server.
|
||||
|
||||
### Environment variables
|
||||
There are two environment variables currently used to control the gateway URL and also controlling if the users can modify the gateway URL. You can modify these values in the `.env` file located in the root of the project.
|
||||
|
||||
During development, you will need to stop and start the project again to see those changes come into effect.
|
||||
```asm
|
||||
REACT_APP_DEFAULT_GATEWAY_URL=https://ucentral.dpaas.arilia.com:16001
|
||||
REACT_APP_ALLOW_GATEWAY_CHANGE=false
|
||||
```
|
||||
- `REACT_APP_DEFAULT_GATEWAY_URL` points to the actual uCentral gateway, including the port.
|
||||
- `REACT_APP_ALLOW_GATEWAY_CHANGE` : when set to `true` will allow a user to change the gateway name she wants to use. When set to `false`, will not show a text field for the gateway and will only allow users to go to the gateway speficied in `REACT_APP_DEFAULT_GATEWAY_URL`.
|
||||
|
||||
Once the build is done, you can move the `build` folder on your server.
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ucentral-client",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ucentral-client",
|
||||
"version": "0.9.2",
|
||||
"version": "0.9.3",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@coreui/coreui": "^3.4.0",
|
||||
|
||||
4
public/config.json
Normal file
4
public/config.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"DEFAULT_GATEWAY_URL": "https://ucentral.dpaas.arilia.com:16001",
|
||||
"ALLOW_GATEWAY_CHANGE": false
|
||||
}
|
||||
@@ -103,6 +103,12 @@
|
||||
"explanation": "Möchten Sie diesen Befehl wirklich löschen? Diese Aktion ist nicht umkehrbar.",
|
||||
"title": "Befehl löschen"
|
||||
},
|
||||
"delete_logs": {
|
||||
"date": "Wählen Sie das Datum des ältesten Protokolls aus, das Sie behalten möchten",
|
||||
"device_logs_title": "Geräteprotokolle löschen",
|
||||
"explanation": "Dadurch werden alle {{object}} vor dem von Ihnen gewählten Datum gelöscht. Seien Sie vorsichtig, diese Aktion ist nicht umkehrbar.",
|
||||
"healthchecks_title": "Healthchecks löschen"
|
||||
},
|
||||
"device_logs": {
|
||||
"log": "Log",
|
||||
"severity": "Schwere",
|
||||
|
||||
@@ -103,6 +103,12 @@
|
||||
"explanation": "Are you sure you want to delete this command? This action is not reversible.",
|
||||
"title": "Delete Command"
|
||||
},
|
||||
"delete_logs": {
|
||||
"date": "Select the date of the oldest log you would like to keep",
|
||||
"device_logs_title": "Delete Device Logs",
|
||||
"explanation": "This will delete all of the {{object}} before the date you choose. Be careful, this action is not reversible.",
|
||||
"healthchecks_title": "Delete Healthchecks"
|
||||
},
|
||||
"device_logs": {
|
||||
"log": "Log",
|
||||
"severity": "Severity",
|
||||
|
||||
@@ -103,6 +103,12 @@
|
||||
"explanation": "¿Está seguro de que desea eliminar este comando? Esta acción no es reversible.",
|
||||
"title": "Eliminar comando"
|
||||
},
|
||||
"delete_logs": {
|
||||
"date": "Seleccione la fecha del registro más antiguo que le gustaría conservar",
|
||||
"device_logs_title": "Eliminar registros de dispositivos",
|
||||
"explanation": "Esto eliminará todos los {{object}} antes de la fecha que elija. Tenga cuidado, esta acción no es reversible.",
|
||||
"healthchecks_title": "Eliminar comprobaciones de estado"
|
||||
},
|
||||
"device_logs": {
|
||||
"log": "Iniciar sesión",
|
||||
"severity": "Gravedad",
|
||||
|
||||
@@ -103,6 +103,12 @@
|
||||
"explanation": "Êtes-vous sûr de vouloir supprimer cette commande ? Cette action n'est pas réversible.",
|
||||
"title": "Supprimer la commande"
|
||||
},
|
||||
"delete_logs": {
|
||||
"date": "Sélectionnez la date du plus ancien journal que vous souhaitez conserver",
|
||||
"device_logs_title": "Supprimer les journaux de l'appareil",
|
||||
"explanation": "Cela supprimera tous les {{object}} avant la date que vous choisissez. Attention, cette action n'est pas réversible.",
|
||||
"healthchecks_title": "Supprimer les vérifications d'état"
|
||||
},
|
||||
"device_logs": {
|
||||
"log": "Bûche",
|
||||
"severity": "Gravité",
|
||||
|
||||
@@ -103,6 +103,12 @@
|
||||
"explanation": "Tem certeza de que deseja excluir este comando? esta ação não é reversível.",
|
||||
"title": "Apagar Comando"
|
||||
},
|
||||
"delete_logs": {
|
||||
"date": "Selecione a data do registro mais antigo que você gostaria de manter",
|
||||
"device_logs_title": "Excluir registros do dispositivo",
|
||||
"explanation": "Isso excluirá todos os {{object}} antes da data que você escolheu. Cuidado, esta ação não é reversível.",
|
||||
"healthchecks_title": "Excluir verificações de saúde"
|
||||
},
|
||||
"device_logs": {
|
||||
"log": "Registro",
|
||||
"severity": "Gravidade",
|
||||
|
||||
64
src/components/ConfirmFooter/index.js
Normal file
64
src/components/ConfirmFooter/index.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PropTypes from 'prop-types';
|
||||
import { CButton, CSpinner, CModalFooter } from '@coreui/react';
|
||||
|
||||
const ConfirmFooter = ({ isShown, isLoading, action, color, variant, block, toggleParent }) => {
|
||||
const { t } = useTranslation();
|
||||
const [askingIfSure, setAskingIfSure] = useState(false);
|
||||
|
||||
const confirmingIfSure = () => {
|
||||
setAskingIfSure(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setAskingIfSure(false);
|
||||
}, [isShown]);
|
||||
|
||||
return (
|
||||
<CModalFooter>
|
||||
<div hidden={!askingIfSure}>{t('common.are_you_sure')}</div>
|
||||
<CButton
|
||||
disabled={isLoading}
|
||||
hidden={askingIfSure}
|
||||
color={color}
|
||||
variant={variant}
|
||||
onClick={() => confirmingIfSure()}
|
||||
block={block}
|
||||
>
|
||||
{t('common.submit')}
|
||||
</CButton>
|
||||
<CButton
|
||||
disabled={isLoading}
|
||||
hidden={!askingIfSure}
|
||||
color={color}
|
||||
onClick={() => action()}
|
||||
block={block}
|
||||
>
|
||||
{isLoading ? t('common.loading_ellipsis') : t('common.yes')}
|
||||
<CSpinner color="light" hidden={!isLoading} component="span" size="sm" />
|
||||
</CButton>
|
||||
<CButton color="secondary" onClick={toggleParent}>
|
||||
{t('common.cancel')}
|
||||
</CButton>
|
||||
</CModalFooter>
|
||||
);
|
||||
};
|
||||
|
||||
ConfirmFooter.propTypes = {
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
block: PropTypes.bool,
|
||||
action: PropTypes.func.isRequired,
|
||||
color: PropTypes.string,
|
||||
variant: PropTypes.string,
|
||||
toggleParent: PropTypes.func.isRequired,
|
||||
isShown: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
ConfirmFooter.defaultProps = {
|
||||
color: 'primary',
|
||||
variant: '',
|
||||
block: false,
|
||||
};
|
||||
|
||||
export default ConfirmFooter;
|
||||
100
src/components/DeleteLogModal/index.js
Normal file
100
src/components/DeleteLogModal/index.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CModal, CModalHeader, CModalTitle, CModalBody, CCol, CRow } from '@coreui/react';
|
||||
import DatePicker from 'react-widgets/DatePicker';
|
||||
import PropTypes from 'prop-types';
|
||||
import ConfirmFooter from 'components/ConfirmFooter';
|
||||
import { dateToUnix } from 'utils/helper';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import { getToken } from 'utils/authHelper';
|
||||
import eventBus from 'utils/eventBus';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const DeleteLogModal = ({ serialNumber, show, toggle, object }) => {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [maxDate, setMaxDate] = useState(new Date().toString());
|
||||
|
||||
const setDate = (date) => {
|
||||
if (date) {
|
||||
setMaxDate(date.toString());
|
||||
}
|
||||
};
|
||||
|
||||
const deleteLog = async () => {
|
||||
setLoading(true);
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
},
|
||||
params: {
|
||||
endDate: dateToUnix(maxDate),
|
||||
},
|
||||
};
|
||||
return axiosInstance
|
||||
.delete(`/device/${serialNumber}/${object}`, options)
|
||||
.then(() => {})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
if (object === 'healthchecks')
|
||||
eventBus.dispatch('deletedHealth', { message: 'Healthcheck was deleted' });
|
||||
else if (object === 'logs')
|
||||
eventBus.dispatch('deletedLogs', { message: 'Deleted device logs' });
|
||||
setLoading(false);
|
||||
toggle();
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(false);
|
||||
setMaxDate(new Date().toString());
|
||||
}, [show]);
|
||||
|
||||
return (
|
||||
<CModal className={styles.modal} show={show} onClose={toggle}>
|
||||
<CModalHeader closeButton>
|
||||
<CModalTitle>
|
||||
{object === 'healthchecks'
|
||||
? t('delete_logs.healthchecks_title')
|
||||
: t('delete_logs.device_logs_title')}
|
||||
</CModalTitle>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<h6>{t('delete_logs.explanation', { object })}</h6>
|
||||
<CRow className={styles.spacedRow}>
|
||||
<CCol md="4" className={styles.spacedDate}>
|
||||
<p>{t('common.date')}:</p>
|
||||
</CCol>
|
||||
<CCol xs="12" md="8">
|
||||
<DatePicker
|
||||
selected={new Date(maxDate)}
|
||||
includeTime
|
||||
value={new Date(maxDate)}
|
||||
placeholder="Select custom date"
|
||||
disabled={loading}
|
||||
onChange={(date) => setDate(date)}
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CModalBody>
|
||||
<ConfirmFooter
|
||||
isShown={show}
|
||||
isLoading={loading}
|
||||
action={deleteLog}
|
||||
color="primary"
|
||||
toggleParent={toggle}
|
||||
/>
|
||||
</CModal>
|
||||
);
|
||||
};
|
||||
|
||||
DeleteLogModal.propTypes = {
|
||||
show: PropTypes.bool.isRequired,
|
||||
toggle: PropTypes.func.isRequired,
|
||||
object: PropTypes.string.isRequired,
|
||||
serialNumber: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default DeleteLogModal;
|
||||
11
src/components/DeleteLogModal/index.module.scss
Normal file
11
src/components/DeleteLogModal/index.module.scss
Normal file
@@ -0,0 +1,11 @@
|
||||
.modal {
|
||||
color: #3c4b64;
|
||||
}
|
||||
|
||||
.spacedRow {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.spacedColumn {
|
||||
margin-top: 7px;
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
CRow,
|
||||
CCol,
|
||||
CProgress,
|
||||
CPopover,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -18,7 +19,9 @@ import PropTypes from 'prop-types';
|
||||
import { prettyDate, dateToUnix } from 'utils/helper';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import { getToken } from 'utils/authHelper';
|
||||
import eventBus from 'utils/eventBus';
|
||||
import LoadingButton from 'components/LoadingButton';
|
||||
import DeleteLogModal from 'components/DeleteLogModal';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const DeviceHealth = ({ selectedDeviceId }) => {
|
||||
@@ -34,6 +37,11 @@ const DeviceHealth = ({ selectedDeviceId }) => {
|
||||
const [showLoadingMore, setShowLoadingMore] = useState(true);
|
||||
const [sanityLevel, setSanityLevel] = useState(null);
|
||||
const [barColor, setBarColor] = useState('gradient-dark');
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||
|
||||
const toggleDeleteModal = () => {
|
||||
setShowDeleteModal(!showDeleteModal);
|
||||
};
|
||||
|
||||
const toggle = (e) => {
|
||||
setCollapse(!collapse);
|
||||
@@ -167,6 +175,14 @@ const DeviceHealth = ({ selectedDeviceId }) => {
|
||||
}
|
||||
}, [start, end, selectedDeviceId]);
|
||||
|
||||
useEffect(() => {
|
||||
eventBus.on('deletedHealth', () => getDeviceHealth());
|
||||
|
||||
return () => {
|
||||
eventBus.remove('deletedHealth');
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<CWidgetDropdown
|
||||
header={sanityLevel ? `${sanityLevel}%` : t('common.unknown')}
|
||||
@@ -178,6 +194,20 @@ const DeviceHealth = ({ selectedDeviceId }) => {
|
||||
<div className={styles.footer}>
|
||||
<CProgress className={styles.progressBar} color="white" value={sanityLevel ?? 0} />
|
||||
<CCollapse show={collapse}>
|
||||
<div className={styles.alignRight}>
|
||||
<CPopover content={t('common.delete')}>
|
||||
<CButton
|
||||
color="light"
|
||||
shape="square"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
toggleDeleteModal();
|
||||
}}
|
||||
>
|
||||
<CIcon name="cilTrash" size="lg" />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
<CRow className={styles.spacedRow}>
|
||||
<CCol>
|
||||
{t('common.from')}:
|
||||
@@ -250,6 +280,12 @@ const DeviceHealth = ({ selectedDeviceId }) => {
|
||||
size="lg"
|
||||
/>
|
||||
</CButton>
|
||||
<DeleteLogModal
|
||||
serialNumber={selectedDeviceId}
|
||||
object="healthchecks"
|
||||
show={showDeleteModal}
|
||||
toggle={toggleDeleteModal}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -25,3 +25,7 @@
|
||||
.scrollable {
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.alignRight {
|
||||
float: right;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
CDataTable,
|
||||
CCard,
|
||||
CCardBody,
|
||||
CPopover,
|
||||
} from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -17,7 +18,9 @@ import PropTypes from 'prop-types';
|
||||
import { prettyDate, dateToUnix } from 'utils/helper';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import { getToken } from 'utils/authHelper';
|
||||
import eventBus from 'utils/eventBus';
|
||||
import LoadingButton from 'components/LoadingButton';
|
||||
import DeleteLogModal from 'components/DeleteLogModal';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const DeviceLogs = ({ selectedDeviceId }) => {
|
||||
@@ -31,6 +34,11 @@ const DeviceLogs = ({ selectedDeviceId }) => {
|
||||
const [logLimit, setLogLimit] = useState(25);
|
||||
const [loadingMore, setLoadingMore] = useState(false);
|
||||
const [showLoadingMore, setShowLoadingMore] = useState(true);
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||
|
||||
const toggleDeleteModal = () => {
|
||||
setShowDeleteModal(!showDeleteModal);
|
||||
};
|
||||
|
||||
const toggle = (e) => {
|
||||
setCollapse(!collapse);
|
||||
@@ -149,85 +157,115 @@ const DeviceLogs = ({ selectedDeviceId }) => {
|
||||
}
|
||||
}, [start, end, selectedDeviceId]);
|
||||
|
||||
useEffect(() => {
|
||||
eventBus.on('deletedLogs', () => getLogs());
|
||||
|
||||
return () => {
|
||||
eventBus.remove('deletedLogs');
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<CWidgetDropdown
|
||||
inverse="true"
|
||||
color="gradient-info"
|
||||
header={t('device_logs.title')}
|
||||
footerSlot={
|
||||
<div className={styles.footer}>
|
||||
<CCollapse show={collapse}>
|
||||
<CRow className={styles.datepickerRow}>
|
||||
<CCol>
|
||||
{t('common.from')}
|
||||
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
|
||||
</CCol>
|
||||
<CCol>
|
||||
{t('common.to')}
|
||||
<DatePicker includeTime onChange={(date) => modifyEnd(date)} />
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CCard>
|
||||
<div className={[styles.scrollableCard, 'overflow-auto'].join(' ')}>
|
||||
<CDataTable
|
||||
items={logs ?? []}
|
||||
fields={columns}
|
||||
loading={loading}
|
||||
className={styles.whiteIcon}
|
||||
sorterValue={{ column: 'recorded', desc: 'true' }}
|
||||
scopedSlots={{
|
||||
recorded: (item) => <td>{prettyDate(item.recorded)}</td>,
|
||||
show_details: (item, index) => (
|
||||
<td className="py-2">
|
||||
<CButton
|
||||
color="primary"
|
||||
variant={details.includes(index) ? '' : 'outline'}
|
||||
shape="square"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
toggleDetails(index);
|
||||
}}
|
||||
>
|
||||
<CIcon name="cilList" size="lg" />
|
||||
</CButton>
|
||||
</td>
|
||||
),
|
||||
details: (item, index) => (
|
||||
<CCollapse show={details.includes(index)}>
|
||||
<CCardBody>
|
||||
<h5>{t('common.details')}</h5>
|
||||
<div>{getDetails(index, item)}</div>
|
||||
</CCardBody>
|
||||
</CCollapse>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<CRow className={styles.loadMoreRow}>
|
||||
{showLoadingMore && (
|
||||
<LoadingButton
|
||||
label={t('common.view_more')}
|
||||
isLoadingLabel={t('common.loading_more_ellipsis')}
|
||||
isLoading={loadingMore}
|
||||
action={showMoreLogs}
|
||||
variant="outline"
|
||||
/>
|
||||
)}
|
||||
</CRow>
|
||||
<div>
|
||||
<CWidgetDropdown
|
||||
inverse="true"
|
||||
color="gradient-info"
|
||||
header={t('device_logs.title')}
|
||||
footerSlot={
|
||||
<div className={styles.footer}>
|
||||
<CCollapse show={collapse}>
|
||||
<div className={styles.alignRight}>
|
||||
<CPopover content={t('common.delete')}>
|
||||
<CButton
|
||||
color="light"
|
||||
shape="square"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
toggleDeleteModal();
|
||||
}}
|
||||
>
|
||||
<CIcon name="cilTrash" size="lg" />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CCard>
|
||||
</CCollapse>
|
||||
<CButton show={collapse ? 'true' : 'false'} color="transparent" onClick={toggle} block>
|
||||
<CIcon
|
||||
name={collapse ? 'cilChevronTop' : 'cilChevronBottom'}
|
||||
className={styles.whiteIcon}
|
||||
size="lg"
|
||||
/>
|
||||
</CButton>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<CIcon name="cilList" className={styles.whiteIcon} size="lg" />
|
||||
</CWidgetDropdown>
|
||||
<CRow className={styles.datepickerRow}>
|
||||
<CCol>
|
||||
{t('common.from')}
|
||||
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
|
||||
</CCol>
|
||||
<CCol>
|
||||
{t('common.to')}
|
||||
<DatePicker includeTime onChange={(date) => modifyEnd(date)} />
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CCard>
|
||||
<div className={[styles.scrollableCard, 'overflow-auto'].join(' ')}>
|
||||
<CDataTable
|
||||
items={logs ?? []}
|
||||
fields={columns}
|
||||
loading={loading}
|
||||
className={styles.whiteIcon}
|
||||
sorterValue={{ column: 'recorded', desc: 'true' }}
|
||||
scopedSlots={{
|
||||
recorded: (item) => <td>{prettyDate(item.recorded)}</td>,
|
||||
show_details: (item, index) => (
|
||||
<td className="py-2">
|
||||
<CButton
|
||||
color="primary"
|
||||
variant={details.includes(index) ? '' : 'outline'}
|
||||
shape="square"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
toggleDetails(index);
|
||||
}}
|
||||
>
|
||||
<CIcon name="cilList" size="lg" />
|
||||
</CButton>
|
||||
</td>
|
||||
),
|
||||
details: (item, index) => (
|
||||
<CCollapse show={details.includes(index)}>
|
||||
<CCardBody>
|
||||
<h5>{t('common.details')}</h5>
|
||||
<div>{getDetails(index, item)}</div>
|
||||
</CCardBody>
|
||||
</CCollapse>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<CRow className={styles.loadMoreRow}>
|
||||
{showLoadingMore && (
|
||||
<LoadingButton
|
||||
label={t('common.view_more')}
|
||||
isLoadingLabel={t('common.loading_more_ellipsis')}
|
||||
isLoading={loadingMore}
|
||||
action={showMoreLogs}
|
||||
variant="outline"
|
||||
/>
|
||||
)}
|
||||
</CRow>
|
||||
</div>
|
||||
</CCard>
|
||||
</CCollapse>
|
||||
<CButton show={collapse ? 'true' : 'false'} color="transparent" onClick={toggle} block>
|
||||
<CIcon
|
||||
name={collapse ? 'cilChevronTop' : 'cilChevronBottom'}
|
||||
className={styles.whiteIcon}
|
||||
size="lg"
|
||||
/>
|
||||
</CButton>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<CIcon name="cilList" className={styles.whiteIcon} size="lg" />
|
||||
</CWidgetDropdown>
|
||||
<DeleteLogModal
|
||||
serialNumber={selectedDeviceId}
|
||||
object="logs"
|
||||
show={showDeleteModal}
|
||||
toggle={toggleDeleteModal}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -17,3 +17,7 @@
|
||||
.loadMoreRow {
|
||||
margin-bottom: 1%;
|
||||
}
|
||||
|
||||
.alignRight {
|
||||
float: right;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [statOptions, setStatOptions] = useState({
|
||||
interfaceList: [],
|
||||
settings: {}
|
||||
settings: {},
|
||||
});
|
||||
|
||||
const transformIntoDataset = (data) => {
|
||||
@@ -62,7 +62,9 @@ const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => {
|
||||
interfaceList[interfaceTypes[inter.name]][0].data.push(
|
||||
Math.floor(inter.counters.tx_bytes / 1024),
|
||||
);
|
||||
interfaceList[interfaceTypes[inter.name]][1].data.push(Math.floor(inter.counters.rx_bytes / 1024));
|
||||
interfaceList[interfaceTypes[inter.name]][1].data.push(
|
||||
Math.floor(inter.counters.rx_bytes / 1024),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,10 +103,10 @@ const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => {
|
||||
|
||||
const newOptions = {
|
||||
interfaceList,
|
||||
settings: options
|
||||
settings: options,
|
||||
};
|
||||
|
||||
if(statOptions !== newOptions){
|
||||
if (statOptions !== newOptions) {
|
||||
setStatOptions(newOptions);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"REACT_APP_BASE_URL": "https://ucentral.dpaas.arilia.com:16001/api/v1"
|
||||
}
|
||||
@@ -6,7 +6,7 @@ const TheFooter = () => (
|
||||
<Translation>
|
||||
{(t) => (
|
||||
<CFooter fixed={false}>
|
||||
<div>{t('footer.version')} 0.9.2</div>
|
||||
<div>{t('footer.version')} 0.9.3</div>
|
||||
<div className="mfs-auto">
|
||||
<span className="mr-1">{t('footer.powered_by')}</span>
|
||||
<a href="https://coreui.io/react" target="_blank" rel="noopener noreferrer">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.sidebarImgFull {
|
||||
height: 75px;
|
||||
width: 75px;
|
||||
}
|
||||
|
||||
.sidebarImgMinimized {
|
||||
|
||||
@@ -30,15 +30,30 @@ const Login = () => {
|
||||
const dispatch = useDispatch();
|
||||
const [userId, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [gatewayUrl, setGatewayUrl] = useState(process.env.REACT_APP_DEFAULT_GATEWAY_URL);
|
||||
const [gatewayUrl, setGatewayUrl] = useState('');
|
||||
const [hadError, setHadError] = useState(false);
|
||||
const [emptyUsername, setEmptyUsername] = useState(false);
|
||||
const [emptyPassword, setEmptyPassword] = useState(false);
|
||||
const [emptyGateway, setEmptyGateway] = useState(false);
|
||||
const placeholderUrl = 'Gateway URL (ex: https://ucentral.dpaas.arilia.com:16001)';
|
||||
const defaultGatewayUrl = process.env.REACT_APP_DEFAULT_GATEWAY_URL;
|
||||
const allowUrlChange = process.env.REACT_APP_ALLOW_GATEWAY_CHANGE === 'true';
|
||||
const loginErrorText = t('login.login_error');
|
||||
const [defaultConfig, setDefaultConfig] = useState({
|
||||
DEFAULT_GATEWAY_URL: '',
|
||||
ALLOW_GATEWAY_CHANGE: true,
|
||||
});
|
||||
const placeholderUrl = 'Gateway URL (ex: https://your-url:port)';
|
||||
|
||||
const getDefaultConfig = async () => {
|
||||
fetch('./config.json', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((json) => {
|
||||
setDefaultConfig(json);
|
||||
})
|
||||
.catch();
|
||||
};
|
||||
|
||||
const formValidation = () => {
|
||||
setHadError(false);
|
||||
@@ -59,12 +74,13 @@ const Login = () => {
|
||||
setEmptyGateway(true);
|
||||
isSuccessful = false;
|
||||
}
|
||||
|
||||
return isSuccessful;
|
||||
};
|
||||
|
||||
const SignIn = (credentials) => {
|
||||
const gatewayUrlToUse = allowUrlChange ? gatewayUrl : defaultGatewayUrl;
|
||||
const gatewayUrlToUse = defaultConfig.ALLOW_GATEWAY_CHANGE
|
||||
? gatewayUrl
|
||||
: defaultConfig.DEFAULT_GATEWAY_URL;
|
||||
|
||||
axiosInstance
|
||||
.post(`${gatewayUrlToUse}/api/v1/oauth2`, credentials)
|
||||
@@ -93,6 +109,12 @@ const Login = () => {
|
||||
useEffect(() => {
|
||||
if (emptyGateway) setEmptyGateway(false);
|
||||
}, [gatewayUrl]);
|
||||
useEffect(() => {
|
||||
getDefaultConfig();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
setGatewayUrl(defaultConfig.DEFAULT_GATEWAY_URL);
|
||||
}, [defaultConfig]);
|
||||
|
||||
return (
|
||||
<div className="c-app c-default-layout flex-row align-items-center">
|
||||
@@ -151,7 +173,7 @@ const Login = () => {
|
||||
{t('login.please_enter_password')}
|
||||
</CInvalidFeedback>
|
||||
</CInputGroup>
|
||||
<CInputGroup className="mb-4" hidden={!allowUrlChange}>
|
||||
<CInputGroup className="mb-4" hidden={!defaultConfig.ALLOW_GATEWAY_CHANGE}>
|
||||
<CPopover content="Gateway URL">
|
||||
<CInputGroupPrepend>
|
||||
<CInputGroupText>
|
||||
@@ -175,7 +197,7 @@ const Login = () => {
|
||||
<CRow>
|
||||
<CCol>
|
||||
<CAlert show={hadError} color="danger">
|
||||
{loginErrorText}
|
||||
{t('login.login_error')}
|
||||
</CAlert>
|
||||
</CCol>
|
||||
</CRow>
|
||||
|
||||
Reference in New Issue
Block a user