Version 1.0.1

This commit is contained in:
Charles
2021-10-29 17:52:12 -04:00
parent bcfdbe966d
commit fc082dcc84
41 changed files with 1592 additions and 1134 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "ucentral-libs",
"version": "0.9.91",
"version": "1.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "ucentral-libs",
"version": "0.9.91",
"version": "1.0.1",
"license": "BSD-3-Clause",
"dependencies": {
"@coreui/coreui": "^3.4.0",

View File

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

View File

@@ -34,7 +34,7 @@ const ApiStatusCard = ({ t, info, reload }) => {
return (
<CCard>
<CCardHeader className="my-0 p-1 text-light" style={{ backgroundColor: '#2f3d54' }}>
<CCardHeader className="dark-header">
<div style={{ fontWeight: '600' }} className=" text-value-lg float-left">
{info.title}
</div>
@@ -167,6 +167,7 @@ const ApiStatusCard = ({ t, info, reload }) => {
</CModalHeader>
<CModalBody>
<CDataTable
addTableClasses="table-sm"
border
items={info?.certificates.map((cert) => ({
...cert,

View File

@@ -3,7 +3,15 @@ import PropTypes from 'prop-types';
import { CImg } from '@coreui/react';
import { emailToName } from '../../utils/formatting';
const Avatar = ({ src, fallback }) => {
const getSize = (size) => {
if (size === 'lg') {
return 'avatar-lg';
}
return '';
};
const Avatar = ({ src, fallback, size }) => {
const [error, setError] = useState(false);
useEffect(() => {
@@ -14,8 +22,8 @@ const Avatar = ({ src, fallback }) => {
if (!src || src === '' || src === 'data:;base64,' || error) {
return (
<div className="c-avatar avatar">
<div className="avatar bg-secondary">
<div className={`c-avatar avatar ${getSize(size)}`}>
<div className={`avatar bg-secondary ${getSize(size)}`}>
{fallback === 'N/A' ? fallback : emailToName(fallback)}
</div>
</div>
@@ -23,18 +31,20 @@ const Avatar = ({ src, fallback }) => {
}
return (
<div className="c-avatar avatar">
<CImg className="avatar" src={src} onError={() => setError(true)} />
<div className={`c-avatar avatar ${getSize(size)}`}>
<CImg className={`avatar ${getSize(size)}`} src={src} onError={() => setError(true)} />
</div>
);
};
Avatar.propTypes = {
size: PropTypes.string,
src: PropTypes.string,
fallback: PropTypes.string,
};
Avatar.defaultProps = {
size: 'md',
src: '',
fallback: 'N/A',
};

View File

@@ -0,0 +1,224 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import {
CDataTable,
CRow,
CCol,
CButton,
CModal,
CModalHeader,
CPopover,
CModalTitle,
CModalBody,
CInput,
} from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilPlus, cilSave, cilSearch, cilX } from '@coreui/icons';
import { prettyDate } from '../../utils/formatting';
import useToggle from '../../hooks/useToggle';
const CompactNotesTable = ({ t, notes, addNote, loading, editable, switchToDetailed }) => {
const [currentNote, setCurrentNote] = useState('');
const [showModal, toggleModal] = useToggle(false);
const [showAddModal, toggleAddModal] = useToggle(false);
const [tempNotes, setTempNotes] = useState([]);
const columns = [{ key: 'note', label: t('configuration.note'), _style: { width: '50%' } }];
const detailedColumns = [
{ key: 'created', label: t('common.date'), _style: { width: '30%' } },
{ key: 'createdBy', label: t('common.by'), _style: { width: '20%' } },
{ key: 'note', label: t('configuration.note'), _style: { width: '50%' } },
];
const saveTemp = () => {
for (const note of tempNotes) {
if (note.new) {
addNote(note.note);
}
}
toggleModal();
};
const addSingleNote = () => {
addNote(currentNote);
toggleAddModal();
};
const addTempNote = () => {
const newNotes = [...tempNotes];
newNotes.unshift({
note: currentNote,
new: true,
created: new Date().getTime() / 1000,
createdBy: '',
});
setTempNotes(newNotes);
};
useEffect(() => {
setTempNotes(notes);
setCurrentNote('');
}, [notes]);
useEffect(() => {
if (showAddModal || showModal) setCurrentNote('');
}, [showAddModal, showModal]);
return (
<div>
<CRow>
<CCol>
<div className="overflow-auto border" style={{ height: '200px' }}>
<CDataTable
responsive
border
addTableClasses="m-0 p-0 table-sm"
loading={loading}
fields={columns}
items={notes.sort((a, b) => (a.created <= b.created ? 1 : -1)) || []}
noItemsView={{ noItems: t('common.no_items') }}
columnHeaderSlot={{
note: (
// eslint-disable-next-line react/jsx-indent
<div className="align-middle">
{t('configuration.notes')}
<CButton
className="ml-2 float-right"
size="sm"
color="primary"
variant="outline"
onClick={switchToDetailed ?? toggleModal}
>
<CIcon size="sm" content={cilSearch} />
</CButton>
<CButton
hidden={!editable}
className="ml-2 float-right"
size="sm"
color="primary"
variant="outline"
onClick={toggleAddModal}
>
<CIcon size="sm" content={cilPlus} />
</CButton>
</div>
),
}}
/>
</div>
</CCol>
</CRow>
<CModal size="lg" show={showModal} onClose={toggleModal}>
<CModalHeader className="p-1">
<CModalTitle className="pl-1 pt-1">{t('configuration.notes')}</CModalTitle>
<div className="text-right">
<CPopover content={t('common.save')}>
<CButton
hidden={!editable}
color="primary"
variant="outline"
className="ml-2"
onClick={saveTemp}
>
<CIcon content={cilSave} />
</CButton>
</CPopover>
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader>
<CModalBody>
{editable ? (
<div className="d-flex flex-row mb-3">
<CInput
id="notes-input"
name="text-input"
value={currentNote}
onChange={(e) => setCurrentNote(e.target.value)}
/>
<CButton
className="ml-3"
onClick={addTempNote}
disabled={loading || currentNote === ''}
color="primary"
>
<CIcon content={cilPlus} />
</CButton>
</div>
) : null}
<div className="overflow-auto border" style={{ height: '500px' }}>
<CDataTable
responsive
border
addTableClasses="m-0 p-0"
loading={loading}
fields={detailedColumns}
items={tempNotes || []}
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>
</CModalBody>
</CModal>
<CModal size="lg" show={showAddModal} onClose={toggleAddModal}>
<CModalHeader className="p-1">
<CModalTitle className="pl-1 pt-1">{t('common.add_note')}</CModalTitle>
<div className="text-right">
<CPopover content={t('common.add')}>
<CButton
disabled={currentNote === ''}
color="primary"
variant="outline"
className="ml-2"
onClick={addSingleNote}
>
<CIcon content={cilPlus} />
</CButton>
</CPopover>
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleAddModal}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader>
<CModalBody>
<h6>{t('common.add_note_explanation')}</h6>
<CInput
id="notes-input"
name="text-input"
value={currentNote}
onChange={(e) => setCurrentNote(e.target.value)}
/>
</CModalBody>
</CModal>
</div>
);
};
CompactNotesTable.propTypes = {
t: PropTypes.func.isRequired,
notes: PropTypes.instanceOf(Array).isRequired,
addNote: PropTypes.func.isRequired,
loading: PropTypes.bool.isRequired,
editable: PropTypes.bool,
switchToDetailed: PropTypes.func,
};
CompactNotesTable.defaultProps = {
editable: true,
switchToDetailed: null,
};
export default CompactNotesTable;

View File

@@ -1,13 +1,21 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { CButtonToolbar, CDataTable, CPopover, CButton } from '@coreui/react';
import { cilPencil, cilMagnifyingGlass } from '@coreui/icons';
import { cilFilterSquare, cilMagnifyingGlass, cilSpreadsheet } from '@coreui/icons';
import CIcon from '@coreui/icons-react';
import ReactTooltip from 'react-tooltip';
import DeleteButton from './DeleteButton';
import FormattedDate from '../FormattedDate';
const ConfigurationTable = ({ t, history, loading, configs, toggleInUse, deleteConfig }) => {
const ConfigurationTable = ({
t,
history,
loading,
configs,
toggleInUse,
deleteConfig,
toggleEffects,
}) => {
const columns = [
{ key: 'name', label: t('user.name'), _style: { width: '20%' } },
{ key: 'description', label: t('user.description'), _style: { width: '20%' } },
@@ -35,7 +43,7 @@ const ConfigurationTable = ({ t, history, loading, configs, toggleInUse, deleteC
return (
<CDataTable
addTableClasses="ignore-overflow"
addTableClasses="ignore-overflow table-sm"
items={configs ?? []}
fields={columns}
hover
@@ -60,7 +68,7 @@ const ConfigurationTable = ({ t, history, loading, configs, toggleInUse, deleteC
<CButtonToolbar
role="group"
className="justify-content-flex-end"
style={{ width: '150px' }}
style={{ width: '200px' }}
>
<CPopover content={t('configuration.view_in_use')}>
<CButton
@@ -73,7 +81,21 @@ const ConfigurationTable = ({ t, history, loading, configs, toggleInUse, deleteC
onClick={() => toggleInUse(item)}
style={{ width: '33px', height: '30px' }}
>
<CIcon name="cil-magnifying-glass" content={cilMagnifyingGlass} size="sm" />
<CIcon name="cil-spreadsheet" content={cilSpreadsheet} size="sm" />
</CButton>
</CPopover>
<CPopover content={t('configuration.view_affected_devices')}>
<CButton
disabled={item.inUse.length === 0}
color="primary"
variant="outline"
shape="square"
size="sm"
className="mx-2"
onClick={() => toggleEffects(item)}
style={{ width: '33px', height: '30px' }}
>
<CIcon name="cil-filter-square" content={cilFilterSquare} size="sm" />
</CButton>
</CPopover>
<DeleteButton
@@ -82,7 +104,7 @@ const ConfigurationTable = ({ t, history, loading, configs, toggleInUse, deleteC
deleteConfig={deleteConfig}
hideTooltips={hideTooltips}
/>
<CPopover content={t('configuration.edit_configuration')}>
<CPopover content={t('configuration.view_config')}>
<CButton
color="primary"
variant="outline"
@@ -92,7 +114,7 @@ const ConfigurationTable = ({ t, history, loading, configs, toggleInUse, deleteC
onClick={() => history.push(`/configuration/${item.id}`)}
style={{ width: '33px', height: '30px' }}
>
<CIcon name="cil-pencil" content={cilPencil} size="sm" />
<CIcon name="cil-magnifying-glass" content={cilMagnifyingGlass} size="sm" />
</CButton>
</CPopover>
</CButtonToolbar>
@@ -110,6 +132,7 @@ ConfigurationTable.propTypes = {
configs: PropTypes.instanceOf(Array).isRequired,
toggleInUse: PropTypes.func.isRequired,
deleteConfig: PropTypes.func.isRequired,
toggleEffects: PropTypes.func.isRequired,
};
export default ConfigurationTable;

View File

@@ -20,7 +20,7 @@ const DeleteButton = ({ t, contact, deleteContact, hideTooltips }) => {
variant="outline"
shape="square"
size="sm"
className="mx-2"
className="mx-1"
data-tip
data-for={tooltipId}
data-event="click"

View File

@@ -55,7 +55,7 @@ const ContactTable = ({
return (
<>
<CDataTable
addTableClasses="ignore-overflow"
addTableClasses="ignore-overflow table-sm"
items={contacts}
fields={columns}
hover
@@ -94,7 +94,7 @@ const ContactTable = ({
<CButtonToolbar
role="group"
className="justify-content-flex-end"
style={{ width: '140px' }}
style={{ width: '125px' }}
>
<CPopover content={t('inventory.assign_ent_ven')}>
<div>
@@ -104,7 +104,7 @@ const ContactTable = ({
variant="outline"
shape="square"
size="sm"
className="mx-2"
className="mx-1"
onClick={() => assignToEntity(item.id)}
style={{ width: '33px', height: '30px' }}
>
@@ -124,7 +124,7 @@ const ContactTable = ({
variant="outline"
shape="square"
size="sm"
className="ml-2"
className="mx-1"
onClick={() => toggleEditModal(item.id)}
style={{ width: '33px', height: '30px' }}
>

View File

@@ -0,0 +1,97 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { CDataTable, CRow, CCol, CButton, CInput } from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilPlus } from '@coreui/icons';
import { prettyDate } from '../../utils/formatting';
const DetailedNotesTable = ({ t, notes, addNote, loading, editable }) => {
const [currentNote, setCurrentNote] = useState('');
const detailedColumns = [
{ key: 'created', label: t('common.date'), _style: { width: '1%' } },
{ key: 'note', label: t('configuration.note'), _style: { width: '50%' } },
{ key: 'createdBy', label: t('common.by'), _style: { width: '1%' } },
];
const addNewNote = () => {
addNote(currentNote);
};
useEffect(() => {
setCurrentNote('');
}, [notes]);
return (
<div>
<CRow>
<CCol>
{editable ? (
<div className="d-flex flex-row mt-2 mb-2">
<CInput
id="notes-input"
name="text-input"
value={currentNote}
onChange={(e) => setCurrentNote(e.target.value)}
/>
<CButton
className="ml-3"
onClick={addNewNote}
disabled={loading || currentNote === ''}
color="primary"
>
<CIcon content={cilPlus} />
</CButton>
</div>
) : null}
<div className="overflow-auto border mb-2" style={{ height: '200px' }}>
<CDataTable
responsive
border
addTableClasses="m-0 p-0 table-sm"
loading={loading}
fields={detailedColumns}
items={notes || []}
noItemsView={{ noItems: t('common.no_items') }}
sorterValue={{ column: 'created', desc: 'true' }}
scopedSlots={{
created: (item) => (
<td>
<div style={{ width: '150px' }}>
{item.created && item.created !== 0
? prettyDate(item.created)
: t('common.na')}
</div>
</td>
),
createdBy: (item) => (
<td>
<div style={{ width: '300px' }}>{item.createdBy}</div>
</td>
),
note: (item) => (
<td>
<div style={{ minWidth: '200px' }}>{item.note}</div>
</td>
),
}}
/>
</div>
</CCol>
</CRow>
</div>
);
};
DetailedNotesTable.propTypes = {
t: PropTypes.func.isRequired,
notes: PropTypes.instanceOf(Array).isRequired,
addNote: PropTypes.func.isRequired,
loading: PropTypes.bool.isRequired,
editable: PropTypes.bool,
};
DetailedNotesTable.defaultProps = {
editable: true,
};
export default DetailedNotesTable;

View File

@@ -45,7 +45,10 @@ const DeviceBadge = ({ t, device, deviceIcons }) => {
return (
<CPopover content={device.verifiedCertificate} placement="top">
<div className={`c-avatar c-avatar-lg ${getCertColor()}`}>
<div
style={{ height: '35px', width: '35px' }}
className={`c-avatar c-avatar-lg ${getCertColor()}`}
>
<img src={src} className={styles.icon} alt={device.deviceType} />
<CPopover content={device.connected ? t('common.connected') : t('common.not_connected')}>
<span

View File

@@ -1,4 +1,4 @@
.icon {
height: 36px;
width: 36px;
height: 25px;
width: 25px;
}

View File

@@ -22,7 +22,7 @@ const getIcon = (health) => {
const DeviceDashboard = ({ t, data }) => (
<div>
<CRow>
<CRow className="mt-3">
<CCol>
<CWidgetIcon
text={t('common.last_dashboard_refresh')}
@@ -57,7 +57,7 @@ const DeviceDashboard = ({ t, data }) => (
<CRow>
<CCol>
<CCard>
<CCardHeader>{t('common.device_status')}</CCardHeader>
<CCardHeader className="dark-header">{t('common.device_status')}</CCardHeader>
<CCardBody>
<CChartPie
datasets={data.status.datasets}
@@ -80,7 +80,7 @@ const DeviceDashboard = ({ t, data }) => (
</CCol>
<CCol>
<CCard>
<CCardHeader>{t('common.device_health')}</CCardHeader>
<CCardHeader className="dark-header">{t('common.device_health')}</CCardHeader>
<CCardBody>
<CChartPie
datasets={data.healths.datasets}
@@ -104,7 +104,7 @@ const DeviceDashboard = ({ t, data }) => (
</CCol>
<CCol>
<CCard>
<CCardHeader>
<CCardHeader className="dark-header">
{data.totalAssociations}{' '}
{data.totalAssociations === 1
? t('wifi_analysis.association')
@@ -137,7 +137,7 @@ const DeviceDashboard = ({ t, data }) => (
<CRow>
<CCol>
<CCard>
<CCardHeader>{t('common.vendors')}</CCardHeader>
<CCardHeader className="dark-header">{t('common.vendors')}</CCardHeader>
<CCardBody>
<CChartBar
datasets={data.vendors.datasets}
@@ -162,7 +162,7 @@ const DeviceDashboard = ({ t, data }) => (
</CCol>
<CCol>
<CCard>
<CCardHeader>{t('firmware.device_types')}</CCardHeader>
<CCardHeader className="dark-header">{t('firmware.device_types')}</CCardHeader>
<CCardBody>
<CChartPie
datasets={data.deviceType.datasets}
@@ -186,7 +186,7 @@ const DeviceDashboard = ({ t, data }) => (
</CCol>
<CCol>
<CCard>
<CCardHeader>{t('common.uptimes')}</CCardHeader>
<CCardHeader className="dark-header">{t('common.uptimes')}</CCardHeader>
<CCardBody>
<CChartBar
datasets={data.upTimes.datasets}
@@ -213,7 +213,7 @@ const DeviceDashboard = ({ t, data }) => (
<CRow>
<CCol>
<CCard>
<CCardHeader>{t('common.certificates')}</CCardHeader>
<CCardHeader className="dark-header">{t('common.certificates')}</CCardHeader>
<CCardBody>
<CChartPie
datasets={data.certificates.datasets}
@@ -236,7 +236,7 @@ const DeviceDashboard = ({ t, data }) => (
</CCol>
<CCol>
<CCard>
<CCardHeader>{t('common.commands')}</CCardHeader>
<CCardHeader className="dark-header">{t('common.commands')}</CCardHeader>
<CCardBody>
<CChartBar
datasets={data.commands.datasets}
@@ -261,7 +261,7 @@ const DeviceDashboard = ({ t, data }) => (
</CCol>
<CCol>
<CCard>
<CCardHeader>{t('common.memory_used')}</CCardHeader>
<CCardHeader className="dark-header">{t('common.memory_used')}</CCardHeader>
<CCardBody>
<CChartBar
datasets={data.memoryUsed.datasets}

View File

@@ -54,11 +54,11 @@ const DeviceDetails = ({ t, loading, getData, status, deviceConfig, lastStats })
return (
<CCard className="m-0">
<CCardHeader className="p-1">
<CCardHeader className="dark-header">
<div className="d-flex flex-row-reverse align-items-center">
<div className="text-right">
<CPopover content={t('common.refresh')}>
<CButton size="sm" color="primary" variant="outline" onClick={getData}>
<CButton size="sm" color="info" onClick={getData}>
<CIcon content={cilSync} />
</CButton>
</CPopover>

View File

@@ -76,6 +76,7 @@ const DeviceFirmwareModal = ({
<CCol>
<div className="overflow-auto" style={{ height: '600px' }}>
<CDataTable
addTableClasses="table-sm"
items={firmwareVersions}
fields={fields}
loading={loading}

View File

@@ -92,14 +92,14 @@ const DeviceListTable = ({
const tooltipId = createUuid();
let text = t('firmware.unknown_firmware_status');
let upgradeText = t('firmware.upgrade_to_latest');
let icon = <CIcon size="lg" name="cil-arrow-circle-top" content={cilArrowCircleTop} />;
let icon = <CIcon size="md" name="cil-arrow-circle-top" content={cilArrowCircleTop} />;
let color = 'secondary';
if (latest !== undefined) {
text = t('firmware.newer_firmware_available');
color = 'warning';
if (latest) {
icon = <CIcon size="lg" name="cil-check-circle" content={cilCheckCircle} />;
icon = <CIcon size="md" name="cil-check-circle" content={cilCheckCircle} />;
text = t('firmware.latest_version_installed');
upgradeText = t('firmware.reinstall_latest');
color = 'success';
@@ -107,7 +107,7 @@ const DeviceListTable = ({
}
return (
<div>
<CButton color={color} data-tip data-for={tooltipId} data-event="click">
<CButton size="sm" color={color} data-tip data-for={tooltipId} data-event="click">
{icon}
</CButton>
<ReactTooltip
@@ -252,15 +252,15 @@ const DeviceListTable = ({
return (
<>
<CCard>
<CCardHeader>
<CCard className="m-0 p-0">
<CCardHeader className="dark-header">
<div className="float-left" style={{ width: '400px' }}>
{searchBar}
</div>
</CCardHeader>
<CCardBody className="p-0">
<CDataTable
addTableClasses="ignore-overflow"
addTableClasses="ignore-overflow table-sm"
items={devices ?? []}
fields={columns}
hover
@@ -268,7 +268,7 @@ const DeviceListTable = ({
loading={loading}
scopedSlots={{
deviceType: (item) => (
<td className="pt-3 text-center">
<td className="align-middle text-center">
<DeviceBadge t={t} device={item} deviceIcons={deviceIcons} />
</td>
),

View File

@@ -37,11 +37,11 @@ const DeviceStatusCard = ({
lastStats,
}) => (
<CCard>
<CCardHeader className="p-1">
<CCardHeader className="dark-header">
<div className="d-flex flex-row-reverse align-items-center">
<div className="text-right">
<CPopover content={t('common.refresh')}>
<CButton size="sm" color="primary" variant="outline" onClick={getData}>
<CButton size="sm" color="info" onClick={getData}>
<CIcon content={cilSync} />
</CButton>
</CPopover>

View File

@@ -7,13 +7,11 @@ import {
CFormGroup,
CInvalidFeedback,
CFormText,
CRow,
CButton,
CSwitch,
} from '@coreui/react';
import PropTypes from 'prop-types';
import Select from 'react-select';
import NotesTable from '../NotesTable';
import FormattedDate from '../FormattedDate';
import selectStyles from '../../utils/selectStyles';
@@ -23,7 +21,6 @@ const EditConfigurationForm = ({
fields,
updateField,
updateFieldWithKey,
addNote,
editing,
toggleInUseModal,
deviceTypes,
@@ -84,188 +81,159 @@ const EditConfigurationForm = ({
return (
<CForm>
<CFormGroup row>
<CCol>
<CRow className="pb-0">
<CLabel lg="5" xl="3" col htmlFor="name">
<div>{t('user.name')}:</div>
</CLabel>
<CCol lg="7" xl="9">
{editing ? (
<div>
<CInput
id="name"
type="text"
required
value={fields.name.value}
onChange={updateField}
invalid={fields.name.error}
disabled={disable}
maxLength="50"
/>
<CFormText hidden={!fields.name.error} color={fields.name.error ? 'danger' : ''}>
{t('common.required')}
</CFormText>
</div>
) : (
<p className="mt-2 mb-0">{fields.name.value}</p>
)}
</CCol>
</CRow>
<CRow className="pb-0">
<CLabel lg="5" xl="3" col htmlFor="name">
<div>{t('user.description')}:</div>
</CLabel>
<CCol lg="7" xl="9">
{editing ? (
<div>
<CInput
id="description"
type="text"
required
value={fields.description.value}
onChange={updateField}
invalid={fields.description.error}
disabled={disable}
maxLength="50"
/>
<CInvalidFeedback>{t('common.required')}</CInvalidFeedback>
</div>
) : (
<p className="mt-2 mb-0">{fields.description.value}</p>
)}
</CCol>
</CRow>
<CRow className="pt-1">
<CLabel lg="5" xl="3" col htmlFor="name">
<div>{t('configuration.device_types')}:</div>
</CLabel>
<CCol lg="7" xl="9">
<Select
isMulti
styles={selectStyles}
closeMenuOnSelect={false}
name="Device Types"
options={typeOptions}
onChange={typeOnChange}
value={chosenTypes}
className={`basic-multi-select ${fields.deviceTypes.error ? 'border-danger' : ''}`}
classNamePrefix="select"
isDisabled={!editing}
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
<div>{t('user.name')}:</div>
</CLabel>
<CCol md="7" lg="4" xl="4" xxl="5">
{editing ? (
<div>
<CInput
id="name"
type="text"
required
value={fields.name.value}
onChange={updateField}
invalid={fields.name.error}
disabled={disable}
maxLength="50"
/>
<CFormText hidden={!fields.deviceTypes.error} color="danger">
{t('configuration.need_device_type')}
</CFormText>
</CCol>
</CRow>
<CRow className="pt-1 pb-0">
<CLabel lg="5" xl="3" col htmlFor="name">
<div>RRM:</div>
</CLabel>
<CCol lg="7" xl="9">
<div style={{ width: '120px' }}>
<Select
id="rrm"
styles={selectStyles}
value={{ value: fields.rrm.value, label: fields.rrm.value }}
onChange={(v) => updateFieldWithKey('rrm', { value: v.value })}
options={[
{ label: 'on', value: 'on' },
{ label: 'off', value: 'off' },
{ label: 'inherit', value: 'inherit' },
]}
isDisabled={!editing}
/>
</div>
<CFormText color="danger" hidden={!fields.rrm.error}>
<CFormText hidden={!fields.name.error} color={fields.name.error ? 'danger' : ''}>
{t('common.required')}
</CFormText>
</CCol>
</CRow>
<CRow className="py-1">
<CLabel col lg="5" xl="3" htmlFor="firmwareUpgrade">
Firmware Upgrade
</CLabel>
<CCol lg="7" xl="9">
<div style={{ width: '120px' }}>
<Select
id="rrm"
styles={selectStyles}
value={{
value: fields.firmwareUpgrade.value,
label: fields.firmwareUpgrade.value,
}}
onChange={(v) => updateFieldWithKey('firmwareUpgrade', { value: v.value })}
options={[
{ label: 'yes', value: 'yes' },
{ label: 'no', value: 'no' },
{ label: 'inherit', value: 'inherit' },
]}
isDisabled={!editing}
/>
</div>
</CCol>
</CRow>
<CRow className="py-1">
<CLabel col lg="5" xl="3" htmlFor="firmwareRCOnly">
Only Release Candidates
</CLabel>
<CCol lg="7" xl="9">
<CSwitch
id="firmwareRCOnly"
color="primary"
defaultChecked={fields.firmwareRCOnly.value}
onClick={() =>
updateFieldWithKey('firmwareRCOnly', { value: !fields.firmwareRCOnly.value })
}
size="lg"
disabled={!editing || fields.firmwareUpgrade.value === 'no'}
/>
</CCol>
</CRow>
<CRow className="pb-0">
<CLabel lg="5" xl="3" col htmlFor="name">
<div>{t('configuration.used_by')}:</div>
</CLabel>
<CCol lg="7" xl="9">
<CButton
disabled={fields.inUse.value.length === 0}
className="ml-0 pl-0"
color="link"
onClick={toggleInUseModal}
>
{config?.parsedInUse}
</CButton>
</CCol>
</CRow>
</div>
) : (
<p className="mt-2 mb-0">{fields.name.value}</p>
)}
</CCol>
<CCol className="mt-2">
<CRow className="pb-0">
<CLabel lg="5" xl="3" col htmlFor="name">
<div>{t('common.created')}:</div>
</CLabel>
<CCol lg="7" xl="9">
<p className="mt-2 mb-0">
<FormattedDate date={fields.created.value} />
</p>
</CCol>
</CRow>
<CRow className="pb-0">
<CLabel lg="5" xl="3" col htmlFor="name">
<div>{t('common.modified')}:</div>
</CLabel>
<CCol lg="7" xl="9">
<p className="mt-2 mb-0">
<FormattedDate date={fields.modified.value} />
</p>
</CCol>
</CRow>
<NotesTable
t={t}
notes={fields.notes.value}
addNote={addNote}
loading={disable}
editable={editing}
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
<div>{t('user.description')}:</div>
</CLabel>
<CCol md="7" lg="4" xl="4" xxl="5">
{editing ? (
<div>
<CInput
id="description"
type="text"
required
value={fields.description.value}
onChange={updateField}
invalid={fields.description.error}
disabled={disable}
maxLength="50"
/>
<CInvalidFeedback>{t('common.required')}</CInvalidFeedback>
</div>
) : (
<p className="mt-2 mb-0">{fields.description.value}</p>
)}
</CCol>
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
<div>{t('configuration.device_types')}:</div>
</CLabel>
<CCol md="7" lg="4" xl="4" xxl="5">
<Select
isMulti
styles={selectStyles}
closeMenuOnSelect={false}
name="Device Types"
options={typeOptions}
onChange={typeOnChange}
value={chosenTypes}
className={`basic-multi-select ${fields.deviceTypes.error ? 'border-danger' : ''}`}
classNamePrefix="select"
isDisabled={!editing}
/>
<CFormText hidden={!fields.deviceTypes.error} color="danger">
{t('configuration.need_device_type')}
</CFormText>
</CCol>
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
<div>RRM:</div>
</CLabel>
<CCol md="7" lg="4" xl="4" xxl="5">
<div style={{ width: '120px' }}>
<Select
id="rrm"
styles={selectStyles}
value={{ value: fields.rrm.value, label: fields.rrm.value }}
onChange={(v) => updateFieldWithKey('rrm', { value: v.value })}
options={[
{ label: 'on', value: 'on' },
{ label: 'off', value: 'off' },
{ label: 'inherit', value: 'inherit' },
]}
isDisabled={!editing}
/>
</div>
<CFormText color="danger" hidden={!fields.rrm.error}>
{t('common.required')}
</CFormText>
</CCol>
<CLabel col className="mb-2" md="5" lg="2" xl="2" xxl="1" htmlFor="firmwareUpgrade">
Firmware Upgrade
</CLabel>
<CCol md="7" lg="4" xl="4" xxl="5">
<div style={{ width: '120px' }}>
<Select
id="rrm"
styles={selectStyles}
value={{
value: fields.firmwareUpgrade.value,
label: fields.firmwareUpgrade.value,
}}
onChange={(v) => updateFieldWithKey('firmwareUpgrade', { value: v.value })}
options={[
{ label: 'yes', value: 'yes' },
{ label: 'no', value: 'no' },
{ label: 'inherit', value: 'inherit' },
]}
isDisabled={!editing}
/>
</div>
</CCol>
<CLabel col className="mb-2" md="5" lg="2" xl="2" xxl="1" htmlFor="firmwareRCOnly">
Only Release Candidates
</CLabel>
<CCol md="7" lg="4" xl="4" xxl="5">
<CSwitch
id="firmwareRCOnly"
color="primary"
defaultChecked={fields.firmwareRCOnly.value}
onClick={() =>
updateFieldWithKey('firmwareRCOnly', { value: !fields.firmwareRCOnly.value })
}
size="lg"
disabled={!editing || fields.firmwareUpgrade.value === 'no'}
/>
</CCol>
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
<div>{t('common.created')}:</div>
</CLabel>
<CCol md="7" lg="4" xl="4" xxl="5">
<p className="mt-2 mb-0">
<FormattedDate date={fields.created.value} />
</p>
</CCol>
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
<div>{t('common.modified')}:</div>
</CLabel>
<CCol md="7" lg="4" xl="4" xxl="5">
<p className="mt-2 mb-0">
<FormattedDate date={fields.modified.value} />
</p>
</CCol>
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="inUse">
<div>{t('configuration.used_by')}:</div>
</CLabel>
<CCol md="7" lg="4" xl="4" xxl="5">
<CButton
disabled={fields.inUse.value.length === 0}
className="ml-0 pl-0"
color="link"
onClick={toggleInUseModal}
>
{config?.parsedInUse}
</CButton>
</CCol>
</CFormGroup>
</CForm>
@@ -278,7 +246,6 @@ EditConfigurationForm.propTypes = {
fields: PropTypes.instanceOf(Object).isRequired,
updateField: PropTypes.func.isRequired,
updateFieldWithKey: PropTypes.func.isRequired,
addNote: PropTypes.func.isRequired,
editing: PropTypes.bool.isRequired,
toggleInUseModal: PropTypes.func.isRequired,
deviceTypes: PropTypes.instanceOf(Array).isRequired,

View File

@@ -16,7 +16,6 @@ import {
CButton,
} from '@coreui/react';
import FormattedDate from '../FormattedDate';
import NotesTable from '../NotesTable';
import RequiredAsterisk from '../RequiredAsterisk';
import selectStyles from '../../utils/selectStyles';
@@ -27,9 +26,9 @@ const EditContactForm = ({
updateField,
updateFieldWithKey,
entities,
addNote,
batchSetField,
hideEntities,
editing,
}) => {
const [filter, setFilter] = useState('');
@@ -83,7 +82,7 @@ const EditContactForm = ({
{ label: 'TECHNICIAN', value: 'TECHNICIAN' },
{ label: 'CORPORATE', value: 'CORPORATE' },
]}
isDisabled={disable}
isDisabled={disable || !editing}
/>
</div>
<CFormText hidden={!fields.type.error} color={fields.type.error ? 'danger' : ''}>
@@ -95,34 +94,46 @@ const EditContactForm = ({
<RequiredAsterisk />
</CLabel>
<CCol sm="4">
<CInput
id="name"
type="text"
required
value={fields.name.value}
onChange={updateField}
invalid={fields.name.error}
disabled={disable}
maxLength="50"
/>
<CFormText hidden={!fields.name.error} color={fields.name.error ? 'danger' : ''}>
{t('common.required')}
</CFormText>
{editing ? (
<div>
<CInput
id="name"
type="text"
required
value={fields.name.value}
onChange={updateField}
invalid={fields.name.error}
disabled={disable}
maxLength="50"
/>
<CFormText hidden={!fields.name.error} color={fields.name.error ? 'danger' : ''}>
{t('common.required')}
</CFormText>
</div>
) : (
<p className="mt-2 mb-0">{fields.name.value}</p>
)}
</CCol>
<CLabel className="mb-2" sm="2" col htmlFor="title">
{t('contact.user_title')}
</CLabel>
<CCol sm="4">
<CInput
id="title"
type="text"
required
value={fields.title.value}
onChange={updateField}
invalid={fields.title.error}
disabled={disable}
maxLength="50"
/>
{editing ? (
<div>
<CInput
id="title"
type="text"
required
value={fields.title.value}
onChange={updateField}
invalid={fields.title.error}
disabled={disable}
maxLength="50"
/>
</div>
) : (
<p className="mt-2 mb-0">{fields.title.value}</p>
)}
</CCol>
<CLabel className="mb-2" sm="2" col htmlFor="salutation">
{t('contact.salutation')}
@@ -144,7 +155,7 @@ const EditContactForm = ({
{ label: 'Mx.', value: 'Mx.' },
{ label: 'Dr.', value: 'Dr.' },
]}
isDisabled={disable}
isDisabled={disable || !editing}
/>
</div>
</CCol>
@@ -153,119 +164,184 @@ const EditContactForm = ({
<RequiredAsterisk />
</CLabel>
<CCol sm="4">
<CInput
id="firstname"
type="text"
required
value={fields.firstname.value}
onChange={updateField}
invalid={fields.firstname.error}
disabled={disable}
maxLength="50"
/>
<CFormText
hidden={!fields.firstname.error}
color={fields.firstname.error ? 'danger' : ''}
>
{t('common.required')}
</CFormText>
{editing ? (
<div>
<CInput
id="firstname"
type="text"
required
value={fields.firstname.value}
onChange={updateField}
invalid={fields.firstname.error}
disabled={disable}
maxLength="50"
/>
<CFormText
hidden={!fields.firstname.error}
color={fields.firstname.error ? 'danger' : ''}
>
{t('common.required')}
</CFormText>
</div>
) : (
<p className="mt-2 mb-0">{fields.firstname.value}</p>
)}
</CCol>
<CLabel className="mb-2" sm="2" col htmlFor="lastname">
{t('contact.last_name')}
<RequiredAsterisk />
</CLabel>
<CCol sm="4">
<CInput
id="lastname"
type="text"
required
value={fields.lastname.value}
onChange={updateField}
invalid={fields.lastname.error}
disabled={disable}
maxLength="50"
/>
<CFormText hidden={!fields.lastname.error} color={fields.lastname.error ? 'danger' : ''}>
{t('common.required')}
</CFormText>
{editing ? (
<div>
<CInput
id="lastname"
type="text"
required
value={fields.lastname.value}
onChange={updateField}
invalid={fields.lastname.error}
disabled={disable}
maxLength="50"
/>
<CFormText
hidden={!fields.lastname.error}
color={fields.lastname.error ? 'danger' : ''}
>
{t('common.required')}
</CFormText>
</div>
) : (
<p className="mt-2 mb-0">{fields.lastname.value}</p>
)}
</CCol>
<CLabel className="mb-2" sm="2" col htmlFor="initials">
{t('contact.initials')}
</CLabel>
<CCol sm="4">
<CInput
id="initials"
type="text"
required
value={fields.initials.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
{editing ? (
<div>
<CInput
id="initials"
type="text"
required
value={fields.initials.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
</div>
) : (
<p className="mt-2 mb-0">{fields.initials.value}</p>
)}
</CCol>
<CLabel sm="2" col htmlFor="visual">
{t('contact.visual')}
</CLabel>
<CCol sm="4">
<CInput
id="visual"
type="text"
required
value={fields.visual.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
{editing ? (
<div>
<CInput
id="visual"
type="text"
required
value={fields.visual.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
</div>
) : (
<p className="mt-2 mb-0">{fields.visual.value}</p>
)}
</CCol>
<CLabel className="mb-2" sm="2" col htmlFor="primaryEmail">
{t('contact.primary_email')}
<RequiredAsterisk />
</CLabel>
<CCol sm="4">
<CInput
id="primaryEmail"
type="text"
required
value={fields.primaryEmail.value}
onChange={updateField}
invalid={fields.primaryEmail.error}
disabled={disable}
maxLength="50"
/>
<CFormText
hidden={!fields.primaryEmail.error}
color={fields.primaryEmail.error ? 'danger' : ''}
>
{t('common.required')}
</CFormText>
{editing ? (
<div>
<CInput
id="primaryEmail"
type="text"
required
value={fields.primaryEmail.value}
onChange={updateField}
invalid={fields.primaryEmail.error}
disabled={disable}
maxLength="50"
/>
<CFormText
hidden={!fields.primaryEmail.error}
color={fields.primaryEmail.error ? 'danger' : ''}
>
{t('common.required')}
</CFormText>
</div>
) : (
<p className="mt-2 mb-0">{fields.primaryEmail.value}</p>
)}
</CCol>
<CLabel className="mb-2" sm="2" col htmlFor="secondaryEmail">
{t('contact.secondary_email')}
</CLabel>
<CCol sm="4">
<CInput
id="secondaryEmail"
type="text"
required
value={fields.secondaryEmail.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
{editing ? (
<div>
<CInput
id="secondaryEmail"
type="text"
required
value={fields.secondaryEmail.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
</div>
) : (
<p className="mt-2 mb-0">{fields.secondaryEmail.value}</p>
)}
</CCol>
<CLabel className="mb-2" sm="2" col htmlFor="accessPIN">
{t('contact.access_pin')}
</CLabel>
<CCol sm="4">
<CInput
id="accessPIN"
type="text"
required
value={fields.accessPIN.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
{editing ? (
<div>
<CInput
id="accessPIN"
type="text"
required
value={fields.accessPIN.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
</div>
) : (
<p className="mt-2 mb-0">{fields.accessPIN.value}</p>
)}
</CCol>
<CLabel sm="2" col htmlFor="description">
{t('user.description')}
</CLabel>
<CCol sm="4">
{editing ? (
<div>
<CInput
id="description"
type="text"
required
value={fields.description.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
</div>
) : (
<p className="mt-2 mb-0">{fields.description.value}</p>
)}
</CCol>
<CLabel className="mb-2" sm="2" col htmlFor="phones">
Landlines
@@ -274,7 +350,7 @@ const EditContactForm = ({
<CreatableSelect
isMulti
id="phones"
isDisabled={disable}
isDisabled={disable || !editing}
onChange={onPhonesChange}
components={{ NoOptionsMessage }}
options={[]}
@@ -289,7 +365,7 @@ const EditContactForm = ({
<CreatableSelect
id="mobiles"
isMulti
isDisabled={disable}
isDisabled={disable || !editing}
onChange={onMobilesChange}
components={{ NoOptionsMessage }}
options={[]}
@@ -297,20 +373,6 @@ const EditContactForm = ({
placeholder={t('common.type_for_options')}
/>
</CCol>
<CLabel sm="2" col htmlFor="description">
{t('user.description')}
</CLabel>
<CCol sm="4">
<CInput
id="description"
type="text"
required
value={fields.description.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
</CCol>
</CRow>
<CFormGroup row className="pb-1">
<CLabel sm="2" col htmlFor="title">
@@ -362,6 +424,7 @@ const EditContactForm = ({
color="primary"
variant="outline"
onClick={() => selectEntity(item)}
disabled={!editing}
>
{t('common.select')}
</CButton>
@@ -372,7 +435,6 @@ const EditContactForm = ({
/>
</div>
)}
<NotesTable t={t} notes={fields.notes.value} addNote={addNote} loading={disable} editable />
</CForm>
);
};
@@ -384,9 +446,9 @@ EditContactForm.propTypes = {
updateField: PropTypes.func.isRequired,
updateFieldWithKey: PropTypes.func.isRequired,
entities: PropTypes.instanceOf(Array).isRequired,
addNote: PropTypes.func.isRequired,
batchSetField: PropTypes.func.isRequired,
hideEntities: PropTypes.bool,
editing: PropTypes.bool.isRequired,
};
EditContactForm.defaultProps = {

View File

@@ -1,18 +1,7 @@
import React from 'react';
import {
CForm,
CInput,
CLabel,
CCol,
CFormGroup,
CInvalidFeedback,
CRow,
CButton,
CLink,
} from '@coreui/react';
import { CForm, CInput, CLabel, CCol, CInvalidFeedback, CRow, CButton, CLink } from '@coreui/react';
import PropTypes from 'prop-types';
import Select from 'react-select';
import NotesTable from '../NotesTable';
import FormattedDate from '../FormattedDate';
import selectStyles from '../../utils/selectStyles';
@@ -22,7 +11,6 @@ const EditEntityForm = ({
fields,
updateField,
updateFieldDirectly,
addNote,
editing,
toggleAssociate,
toggleContact,
@@ -30,209 +18,184 @@ const EditEntityForm = ({
toggleLocation,
}) => (
<CForm>
<CFormGroup row>
<CCol>
<CRow className="pb-0">
<CLabel lg="5" xl="3" col htmlFor="name">
<div>{t('user.name')}:</div>
<CRow>
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
<div>{t('user.name')}:</div>
</CLabel>
<CCol md="7" lg="4" xl="4" xxl="5">
{editing ? (
<div>
<CInput
id="name"
type="text"
required
value={fields.name.value}
onChange={updateField}
invalid={fields.name.error}
disabled={disable}
maxLength="50"
/>
<CInvalidFeedback>{t('common.required')}</CInvalidFeedback>
</div>
) : (
<p className="mt-2 mb-0">{fields.name.value}</p>
)}
</CCol>
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
<div>{t('user.description')}:</div>
</CLabel>
<CCol md="7" lg="4" xl="4" xxl="5">
{editing ? (
<div>
<CInput
id="description"
type="text"
required
value={fields.description.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
</div>
) : (
<p className="mt-2 mb-0">{fields.description.value}</p>
)}
</CCol>
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
<div>RRM:</div>
</CLabel>
<CCol md="7" lg="4" xl="4" xxl="5">
<div style={{ width: '120px' }}>
<Select
id="rrm"
styles={selectStyles}
value={{ value: fields.rrm.value, label: fields.rrm.value }}
onChange={(v) => updateFieldDirectly('rrm', { value: v.value })}
options={[
{ label: 'on', value: 'on' },
{ label: 'off', value: 'off' },
{ label: 'inherit', value: 'inherit' },
]}
isDisabled={!editing}
/>
</div>
</CCol>
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="sourceIp">
<div>{t('entity.ip_detection')}:</div>
</CLabel>
<CCol md="7" lg="4" xl="4" xxl="5">
{editing ? (
<CButton className="pl-0 text-left" color="link" onClick={toggleIpModal}>
{fields.sourceIP.value.length === 0
? t('entity.add_ips')
: fields.sourceIP.value.join(', ')}
</CButton>
) : (
<div className="mt-2 mb-0">
<p className="mb-0">
{fields.sourceIP.value.length === 0
? t('entity.no_ips')
: fields.sourceIP.value.join(', ')}
</p>
</div>
)}
</CCol>
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
<div>{t('common.created')}:</div>
</CLabel>
<CCol md="7" lg="4" xl="4" xxl="5">
<div className="mt-2 mb-0">
<FormattedDate date={fields.created.value} />
</div>
</CCol>
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
<div>{t('common.modified')}:</div>
</CLabel>
<CCol md="7" lg="4" xl="4" xxl="5">
<div className="mt-2 mb-0">
<FormattedDate date={fields.modified.value} />
</div>
</CCol>
{fields.contact ? (
<>
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
<div>{t('contact.title')}:</div>
</CLabel>
<CCol lg="7" xxl="9">
<CCol md="7" lg="4" xl="4" xxl="5">
{editing ? (
<div>
<CInput
id="name"
type="text"
required
value={fields.name.value}
onChange={updateField}
invalid={fields.name.error}
disabled={disable}
maxLength="50"
/>
<CInvalidFeedback>{t('common.required')}</CInvalidFeedback>
</div>
) : (
<p className="mt-2 mb-0">{fields.name.value}</p>
)}
</CCol>
</CRow>
<CRow className="pb-0">
<CLabel lg="5" xl="3" col htmlFor="name">
<div>{t('user.description')}:</div>
</CLabel>
<CCol lg="7" xxl="9">
{editing ? (
<div>
<CInput
id="description"
type="text"
required
value={fields.description.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
</div>
) : (
<p className="mt-2 mb-0">{fields.description.value}</p>
)}
</CCol>
</CRow>
<CRow className="pb-0">
<CLabel lg="5" xl="3" col htmlFor="name">
<div>RRM:</div>
</CLabel>
<CCol lg="7" xxl="9">
<div style={{ width: '120px' }}>
<Select
id="rrm"
styles={selectStyles}
value={{ value: fields.rrm.value, label: fields.rrm.value }}
onChange={(v) => updateFieldDirectly('rrm', { value: v.value })}
options={[
{ label: 'on', value: 'on' },
{ label: 'off', value: 'off' },
{ label: 'inherit', value: 'inherit' },
]}
isDisabled={!editing}
/>
</div>
</CCol>
</CRow>
<CRow className="pb-0">
<CLabel lg="5" xl="3" col htmlFor="name">
<div>{t('configuration.title')}:</div>
</CLabel>
<CCol lg="7" xxl="9">
{editing ? (
<CButton className="pl-0 text-left" color="link" onClick={toggleAssociate}>
{fields.deviceConfiguration.value.length === 0
? t('configuration.add_configuration')
: fields.deviceConfiguration.value.map((c) => c.name).join(', ')}
<CButton className="pl-0 text-left" color="link" onClick={toggleContact}>
{fields.contact.value === '' ? t('contact.add_contact') : fields.contact.value}
</CButton>
) : (
<div className="mt-2 mb-0">
{fields.deviceConfiguration.value.length === 0 ? (
<p className="mb-0">{t('configuration.no_associated_config')}</p>
{fields.contact.uuid === '' ? (
<p className="mb-0">{t('contact.no_associated_contact')}</p>
) : (
<p className="mb-0">
{' '}
{fields.deviceConfiguration.value.map((c) => c.name).join(', ')}
</p>
<CLink
className="c-subheader-nav-link"
aria-current="page"
to={() => `/contacts`}
>
{fields.contact.value}
</CLink>
)}
</div>
)}
</CCol>
</CRow>
{fields.contact ? (
<CRow className="pb-0">
<CLabel lg="5" xl="3" col htmlFor="name">
<div>{t('contact.title')}:</div>
</CLabel>
<CCol lg="7" xxl="9">
{editing ? (
<CButton className="pl-0 text-left" color="link" onClick={toggleContact}>
{fields.contact.value === '' ? t('contact.add_contact') : fields.contact.value}
</CButton>
) : (
<div className="mt-2 mb-0">
{fields.contact.uuid === '' ? (
<p className="mb-0">{t('contact.no_associated_contact')}</p>
) : (
<CLink
className="c-subheader-nav-link"
aria-current="page"
to={() => `/contacts`}
>
{fields.contact.value}
</CLink>
)}
</div>
)}
</CCol>
</CRow>
) : null}
{fields.location ? (
<CRow className="pb-0">
<CLabel lg="5" xl="3" col htmlFor="name">
<div>{t('location.title')}:</div>
</CLabel>
<CCol lg="7" xxl="9">
{editing ? (
<CButton className="pl-0 text-left" color="link" onClick={toggleLocation}>
{fields.location.value === '' ? t('location.add') : fields.location.value}
</CButton>
) : (
<div className="mt-2 mb-0">
{fields.location.uuid === '' ? (
<p className="mb-0">{t('location.no_associated')}</p>
) : (
<CLink
className="c-subheader-nav-link"
aria-current="page"
to={() => `/location`}
>
{fields.location.value}
</CLink>
)}
</div>
)}
</CCol>
</CRow>
) : null}
<CRow className="pb-0">
<CLabel lg="5" xl="3" col htmlFor="sourceIp">
<div>{t('entity.ip_detection')}:</div>
</>
) : null}
{fields.location ? (
<>
<CLabel className="mb-2" md="5" lg="2" xl="2" xxl="1" col htmlFor="name">
<div>{t('location.title')}:</div>
</CLabel>
<CCol lg="7" xxl="9">
<CCol md="7" lg="4" xl="4" xxl="5">
{editing ? (
<CButton className="pl-0 text-left" color="link" onClick={toggleIpModal}>
{fields.sourceIP.value.length === 0
? t('entity.add_ips')
: fields.sourceIP.value.join(', ')}
<CButton className="pl-0 text-left" color="link" onClick={toggleLocation}>
{fields.location.value === '' ? t('location.add') : fields.location.value}
</CButton>
) : (
<div className="mt-2 mb-0">
<p className="mb-0">
{fields.sourceIP.value.length === 0
? t('entity.no_ips')
: fields.sourceIP.value.join(', ')}
</p>
{fields.location.uuid === '' ? (
<p className="mb-0">{t('location.no_associated')}</p>
) : (
<CLink
className="c-subheader-nav-link"
aria-current="page"
to={() => `/location`}
>
{fields.location.value}
</CLink>
)}
</div>
)}
</CCol>
</CRow>
<CRow className="pb-0">
<CLabel lg="5" xl="3" col htmlFor="name">
<div>{t('common.created')}:</div>
</CLabel>
<CCol lg="7" xxl="9">
<div className="mt-2 mb-0">
<FormattedDate date={fields.created.value} />
</div>
</CCol>
</CRow>
</>
) : null}
<CLabel className="mb-2" sm="2" col htmlFor="name">
<div>{t('configuration.configurations')}:</div>
</CLabel>
<CCol sm="10">
{editing ? (
<CButton className="pl-0 text-left" color="link" onClick={toggleAssociate}>
{fields.deviceConfiguration.value.length === 0
? t('configuration.add_configuration')
: fields.deviceConfiguration.value.map((c) => c.name).join(', ')}
</CButton>
) : (
<div className="mt-2 mb-0">
{fields.deviceConfiguration.value.length === 0 ? (
<p className="mb-0">{t('configuration.no_associated_config')}</p>
) : (
<p className="mb-0">
{' '}
{fields.deviceConfiguration.value.map((c) => c.name).join(', ')}
</p>
)}
</div>
)}
</CCol>
<CCol className="mt-1">
<CRow className="pb-0">
<CLabel lg="5" xl="3" col htmlFor="name">
<div>{t('common.modified')}:</div>
</CLabel>
<CCol lg="7" xxl="9">
<div className="mt-2 mb-0">
<FormattedDate date={fields.modified.value} />
</div>
</CCol>
</CRow>
<NotesTable
t={t}
notes={fields.notes.value}
addNote={addNote}
loading={disable}
editable={editing}
/>
</CCol>
</CFormGroup>
</CRow>
</CForm>
);
@@ -242,7 +205,6 @@ EditEntityForm.propTypes = {
fields: PropTypes.instanceOf(Object).isRequired,
updateField: PropTypes.func.isRequired,
updateFieldDirectly: PropTypes.func.isRequired,
addNote: PropTypes.func.isRequired,
editing: PropTypes.bool.isRequired,
toggleAssociate: PropTypes.func.isRequired,
toggleContact: PropTypes.func,

View File

@@ -2,7 +2,6 @@ import React from 'react';
import { CForm, CInput, CLabel, CCol, CFormGroup, CFormText, CRow } from '@coreui/react';
import Select from 'react-select';
import PropTypes from 'prop-types';
import NotesTable from '../NotesTable';
import RequiredAsterisk from '../RequiredAsterisk';
import selectStyles from '../../utils/selectStyles';
@@ -12,8 +11,8 @@ const EditInventoryTagForm = ({
fields,
updateField,
updateFieldDirectly,
addNote,
deviceTypes,
editing,
}) => (
<CForm>
<CFormGroup row className="mb-1">
@@ -28,19 +27,25 @@ const EditInventoryTagForm = ({
<RequiredAsterisk />
</CLabel>
<CCol sm="8">
<CInput
id="name"
type="text"
required
value={fields.name.value}
onChange={updateField}
invalid={fields.name.error}
disabled={disable}
maxLength="50"
/>
<CFormText hidden={!fields.name.error} color={fields.name.error ? 'danger' : ''}>
{t('common.required')}
</CFormText>
{editing ? (
<div>
<CInput
id="name"
type="text"
required
value={fields.name.value}
onChange={updateField}
invalid={fields.name.error}
disabled={disable}
maxLength="50"
/>
<CFormText hidden={!fields.name.error} color={fields.name.error ? 'danger' : ''}>
{t('common.required')}
</CFormText>
</div>
) : (
<p className="mt-2 mb-0">{fields.name.value}</p>
)}
</CCol>
</CFormGroup>
<CFormGroup row className="mb-1">
@@ -48,15 +53,21 @@ const EditInventoryTagForm = ({
{t('user.description')}
</CLabel>
<CCol sm="8">
<CInput
id="description"
type="text"
required
value={fields.description.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
{editing ? (
<div>
<CInput
id="description"
type="text"
required
value={fields.description.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
</div>
) : (
<p className="mt-2 mb-0">{fields.description.value}</p>
)}
</CCol>
</CFormGroup>
<CFormGroup row className="mb-1">
@@ -72,7 +83,7 @@ const EditInventoryTagForm = ({
value={{ value: fields.deviceType.value, label: fields.deviceType.value }}
onChange={(v) => updateFieldDirectly('deviceType', { value: v.value })}
options={deviceTypes.map((v) => ({ value: v, label: v }))}
isDisabled={disable}
isDisabled={disable || !editing}
/>
</div>
<CFormText
@@ -100,7 +111,7 @@ const EditInventoryTagForm = ({
{ label: 'off', value: 'off' },
{ label: 'inherit', value: 'inherit' },
]}
isDisabled={disable}
isDisabled={disable || !editing}
/>
</div>
<CFormText hidden={!fields.rrm.error} color={fields.rrm.error ? 'danger' : ''}>
@@ -108,11 +119,6 @@ const EditInventoryTagForm = ({
</CFormText>
</CCol>
</CRow>
<CRow>
<CCol>
<NotesTable t={t} notes={fields.notes.value} addNote={addNote} loading={disable} />
</CCol>
</CRow>
</CForm>
);
@@ -123,7 +129,7 @@ EditInventoryTagForm.propTypes = {
updateField: PropTypes.func.isRequired,
updateFieldDirectly: PropTypes.func.isRequired,
deviceTypes: PropTypes.instanceOf(Array).isRequired,
addNote: PropTypes.func.isRequired,
editing: PropTypes.bool.isRequired,
};
export default EditInventoryTagForm;

View File

@@ -17,7 +17,6 @@ import {
} from '@coreui/react';
import countryList from 'utils/countryList';
import FormattedDate from '../FormattedDate';
import NotesTable from '../NotesTable';
import RequiredAsterisk from '../RequiredAsterisk';
const EditLocationForm = ({
@@ -27,9 +26,9 @@ const EditLocationForm = ({
updateField,
updateFieldWithKey,
entities,
addNote,
locationSearch,
batchSetField,
editing,
}) => {
const [filter, setFilter] = useState('');
@@ -63,33 +62,45 @@ const EditLocationForm = ({
<RequiredAsterisk />
</CLabel>
<CCol sm="4">
<CInput
id="name"
type="text"
required
value={fields.name.value}
onChange={updateField}
invalid={fields.name.error}
disabled={disable}
maxLength="50"
/>
<CFormText hidden={!fields.name.error} color={fields.name.error ? 'danger' : ''}>
{t('common.required')}
</CFormText>
{editing ? (
<div>
<CInput
id="name"
type="text"
required
value={fields.name.value}
onChange={updateField}
invalid={fields.name.error}
disabled={disable}
maxLength="50"
/>
<CFormText hidden={!fields.name.error} color={fields.name.error ? 'danger' : ''}>
{t('common.required')}
</CFormText>
</div>
) : (
<p className="mt-2 mb-0">{fields.name.value}</p>
)}
</CCol>
<CLabel sm="2" col htmlFor="description">
{t('user.description')}
</CLabel>
<CCol sm="4">
<CInput
id="description"
type="text"
required
value={fields.description.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
{editing ? (
<div>
<CInput
id="description"
type="text"
required
value={fields.description.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
</div>
) : (
<p className="mt-2 mb-0">{fields.description.value}</p>
)}
</CCol>
<CLabel className="mb-2" sm="2" col htmlFor="type">
{t('contact.type')}
@@ -114,7 +125,7 @@ const EditLocationForm = ({
{ label: 'UNKNOWN', value: 'UNKNOWN' },
{ label: 'CORPORATE', value: 'CORPORATE' },
]}
isDisabled={disable}
isDisabled={disable || !editing}
/>
</div>
<CFormText hidden={!fields.type.error} color={fields.type.error ? 'danger' : ''}>
@@ -125,15 +136,21 @@ const EditLocationForm = ({
{t('location.building_name')}
</CLabel>
<CCol sm="4">
<CInput
id="buildingName"
type="text"
required
value={fields.buildingName.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
{editing ? (
<div>
<CInput
id="buildingName"
type="text"
required
value={fields.buildingName.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
</div>
) : (
<p className="mt-2 mb-0">{fields.buildingName.value}</p>
)}
</CCol>
<CLabel className="mb-3" sm="2" col htmlFor="phones">
Landlines
@@ -142,7 +159,7 @@ const EditLocationForm = ({
<CreatableSelect
isMulti
id="phones"
isDisabled={disable}
isDisabled={disable || !editing}
onChange={onPhonesChange}
components={{ NoOptionsMessage }}
options={[]}
@@ -157,7 +174,7 @@ const EditLocationForm = ({
<CreatableSelect
id="mobiles"
isMulti
isDisabled={disable}
isDisabled={disable || !editing}
onChange={onMobilesChange}
components={{ NoOptionsMessage }}
options={[]}
@@ -175,24 +192,30 @@ const EditLocationForm = ({
<RequiredAsterisk />
</CLabel>
<CCol sm="4">
<CInput
id="addressLines"
type="text"
required
value={fields.addressLines.value[0]}
onChange={(e) =>
updateFieldWithKey('addressLines', { value: [e.target.value], error: false })
}
disabled={disable}
invalid={fields.addressLines.error}
maxLength="50"
/>
<CFormText
hidden={!fields.addressLines.error}
color={fields.addressLines.error ? 'danger' : ''}
>
{t('common.required')}
</CFormText>
{editing ? (
<div>
<CInput
id="addressLines"
type="text"
required
value={fields.addressLines.value[0]}
onChange={(e) =>
updateFieldWithKey('addressLines', { value: [e.target.value], error: false })
}
disabled={disable}
invalid={fields.addressLines.error}
maxLength="50"
/>
<CFormText
hidden={!fields.addressLines.error}
color={fields.addressLines.error ? 'danger' : ''}
>
{t('common.required')}
</CFormText>
</div>
) : (
<p className="mt-2 mb-0">{fields.addressLines.value[0]}</p>
)}
</CCol>
<CLabel className="mb-2" sm="2" col htmlFor="country">
{t('location.country')}
@@ -211,7 +234,7 @@ const EditLocationForm = ({
}}
onChange={(v) => updateFieldWithKey('country', { value: v.value })}
options={countryList}
isDisabled={disable}
isDisabled={disable || !editing}
/>
<CFormText hidden={!fields.country.error} color={fields.country.error ? 'danger' : ''}>
{t('common.required')}
@@ -223,71 +246,95 @@ const EditLocationForm = ({
<RequiredAsterisk />
</CLabel>
<CCol sm="4">
<CInput
id="city"
type="text"
required
value={fields.city.value}
onChange={updateField}
invalid={fields.city.error}
disabled={disable}
maxLength="50"
/>
<CFormText hidden={!fields.city.error} color={fields.city.error ? 'danger' : ''}>
{t('common.required')}
</CFormText>
{editing ? (
<div>
<CInput
id="city"
type="text"
required
value={fields.city.value}
onChange={updateField}
invalid={fields.city.error}
disabled={disable}
maxLength="50"
/>
<CFormText hidden={!fields.city.error} color={fields.city.error ? 'danger' : ''}>
{t('common.required')}
</CFormText>
</div>
) : (
<p className="mt-2 mb-0">{fields.city.value}</p>
)}
</CCol>
<CLabel className="mb-2" sm="2" col htmlFor="state">
{t('location.state')}
<RequiredAsterisk />
</CLabel>
<CCol sm="4">
<CInput
id="state"
type="text"
required
value={fields.state.value}
onChange={updateField}
invalid={fields.state.error}
disabled={disable}
maxLength="50"
/>
<CFormText hidden={!fields.state.error} color={fields.state.error ? 'danger' : ''}>
{t('common.required')}
</CFormText>
{editing ? (
<div>
<CInput
id="state"
type="text"
required
value={fields.state.value}
onChange={updateField}
invalid={fields.state.error}
disabled={disable}
maxLength="50"
/>
<CFormText hidden={!fields.state.error} color={fields.state.error ? 'danger' : ''}>
{t('common.required')}
</CFormText>
</div>
) : (
<p className="mt-2 mb-0">{fields.state.value}</p>
)}
</CCol>
<CLabel className="mb-2" sm="2" col htmlFor="postal">
{t('location.postal')}
<RequiredAsterisk />
</CLabel>
<CCol sm="4">
<CInput
id="postal"
type="text"
required
value={fields.postal.value}
onChange={updateField}
invalid={fields.postal.error}
disabled={disable}
maxLength="50"
/>
<CFormText hidden={!fields.postal.error} color={fields.postal.error ? 'danger' : ''}>
{t('common.required')}
</CFormText>
{editing ? (
<div>
<CInput
id="postal"
type="text"
required
value={fields.postal.value}
onChange={updateField}
invalid={fields.postal.error}
disabled={disable}
maxLength="50"
/>
<CFormText hidden={!fields.postal.error} color={fields.postal.error ? 'danger' : ''}>
{t('common.required')}
</CFormText>
</div>
) : (
<p className="mt-2 mb-0">{fields.postal.value}</p>
)}
</CCol>
<CLabel sm="2" col htmlFor="geoCode">
{t('location.geocode')}
</CLabel>
<CCol sm="4">
<CInput
id="geoCode"
type="text"
required
value={fields.geoCode.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
{editing ? (
<div>
<CInput
id="geoCode"
type="text"
required
value={fields.geoCode.value}
onChange={updateField}
disabled={disable}
maxLength="50"
/>
</div>
) : (
<p className="mt-2 mb-0">{fields.geoCode.value}</p>
)}
</CCol>
</CRow>
<CFormGroup row className="pt-2 pb-1">
@@ -335,6 +382,7 @@ const EditLocationForm = ({
<td className="align-middle p-1">
<CPopover content={t('entity.select_entity')}>
<CButton
disabled={!editing}
size="sm"
color="primary"
variant="outline"
@@ -348,7 +396,6 @@ const EditLocationForm = ({
}}
/>
</div>
<NotesTable t={t} notes={fields.notes.value} addNote={addNote} loading={disable} editable />
</CForm>
);
};
@@ -360,9 +407,9 @@ EditLocationForm.propTypes = {
updateField: PropTypes.func.isRequired,
updateFieldWithKey: PropTypes.func.isRequired,
entities: PropTypes.instanceOf(Array).isRequired,
addNote: PropTypes.func.isRequired,
locationSearch: PropTypes.node.isRequired,
batchSetField: PropTypes.func.isRequired,
editing: PropTypes.bool.isRequired,
};
export default EditLocationForm;

View File

@@ -3,7 +3,6 @@ import {
CButton,
CCol,
CForm,
CFormGroup,
CInput,
CInputGroup,
CInputGroupAppend,
@@ -19,8 +18,6 @@ import PropTypes from 'prop-types';
import parsePhoneNumber from 'libphonenumber-js';
import CIcon from '@coreui/icons-react';
import ValidatePhoneNumberModal from 'components/ValidatePhoneNumberModal';
import NotesTable from '../NotesTable';
import LoadingButton from '../LoadingButton';
import Avatar from '../Avatar';
import useToggle from '../../hooks/useToggle';
@@ -31,7 +28,6 @@ const EditMyProfile = ({
updateWithKey,
loading,
policies,
addNote,
avatar,
newAvatar,
deleteAvatar,
@@ -40,6 +36,7 @@ const EditMyProfile = ({
sendPhoneNumberTest,
testVerificationCode,
editing,
avatarDeleted,
}) => {
const [showPhoneModal, togglePhoneModal] = useToggle(false);
const [showPassword, setShowPassword] = useState(false);
@@ -70,175 +67,174 @@ const EditMyProfile = ({
return (
<CForm>
<CFormGroup row>
<CLabel lg="2" xxl="1" col htmlFor="name">
{t('user.name')}
</CLabel>
<CCol lg="4" xxl="5">
{editing ? (
<CInput id="name" value={user.name.value} onChange={updateUserWithId} maxLength="20" />
) : (
<p className="mt-2 mb-0">{user.name.value}</p>
)}
</CCol>
<CLabel lg="2" xxl="1" col htmlFor="description">
{t('user.description')}
</CLabel>
<CCol lg="4" xxl="5">
{editing ? (
<CInput
id="description"
value={user.description.value}
onChange={updateUserWithId}
maxLength="50"
<CRow>
<CCol md="3" xl="2">
<Avatar
src={(avatar !== newAvatar && newAvatar !== '') || avatarDeleted ? newAvatar : avatar}
fallback={user.email.value}
size="lg"
/>
<CRow className="my-2">
<CCol>
<CButton
className="ml-2"
color="primary"
disabled={
!editing || loading || !avatar || avatar === '' || avatar === 'data:;base64,'
}
onClick={deleteAvatar}
>
{t('user.delete_avatar')}
</CButton>
</CCol>
</CRow>
<CRow className="ml-2">
<CInputFile
disabled={!editing}
id="file-input"
name="file-input"
accept="image/*"
onChange={showPreview}
key={fileInputKey}
/>
) : (
<p className="mt-2 mb-0">{user.description.value}</p>
)}
</CRow>
</CCol>
</CFormGroup>
<CFormGroup row>
<CLabel lg="2" xxl="1" col htmlFor="mfaMethod">
MFA
</CLabel>
<CCol lg="4" xxl="5">
<CSelect
disabled={!editing}
custom
id="mfaMethod"
onChange={updateUserWithId}
value={user.mfaMethod.value}
style={{ width: '100px' }}
>
<option value="">Off</option>
<option value="sms">SMS</option>
<option value="email">Email</option>
</CSelect>
</CCol>
<CLabel lg="2" xxl="1" col htmlFor="name">
{t('user.phone_number')}
</CLabel>
<CCol lg="4" xxl="5">
{editing ? (
<CButton color="link" onClick={togglePhoneModal} className="pl-0">
{parseNumber()}
</CButton>
) : (
<p className="mt-2 mb-0">{parseNumber()}</p>
)}
</CCol>
</CFormGroup>
<CFormGroup row>
<CLabel lg="2" xxl="1" col htmlFor="newPassword">
{t('login.new_password')}
</CLabel>
<CCol lg="4" xxl="5">
{editing ? (
<CInputGroup>
<CInput
type={showPassword ? 'text' : 'password'}
id="newPassword"
value={user.newPassword.value}
onChange={updateUserWithId}
invalid={user.newPassword.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.make_sure_same_password')}</CInvalidFeedback>
</CInputGroup>
) : null}
</CCol>
<CLabel lg="2" xxl="1" col htmlFor="confirmNewPassword">
{t('user.confirm_new_password')}
</CLabel>
<CCol lg="4" xxl="5">
{editing ? (
<CInputGroup>
<CInput
type={showPassword ? 'text' : 'password'}
id="confirmNewPassword"
value={user.confirmNewPassword.value}
onChange={updateUserWithId}
invalid={user.newPassword.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.make_sure_same_password')}</CInvalidFeedback>
</CInputGroup>
) : null}
</CCol>
</CFormGroup>
<CFormGroup row>
<CLabel lg="2" xl="1" col htmlFor="avatar" className="pt-2">
{t('user.avatar')}
</CLabel>
<CCol lg="10" xl="5" className="pt-2">
<CCol md="9" xl="10">
<CRow>
<CCol lg="2" xl="2" className="pt-2">
{t('common.current')}
<div className="pt-5">Preview</div>
<CCol sm="12" md="6">
<CRow>
<CLabel className="mb-2" md="6" xl="4" col htmlFor="name">
{t('user.name')}
</CLabel>
<CCol md="6" xl="8">
{editing ? (
<CInput
id="name"
value={user.name.value}
onChange={updateUserWithId}
maxLength="20"
/>
) : (
<p className="mt-2 mb-0">{user.name.value}</p>
)}
</CCol>
</CRow>
<CRow>
<CLabel className="mb-2" md="6" xl="4" col htmlFor="mfaMethod">
MFA
</CLabel>
<CCol md="6" xl="8">
<CSelect
disabled={!editing}
custom
id="mfaMethod"
onChange={updateUserWithId}
value={user.mfaMethod.value}
style={{ width: '100px' }}
>
<option value="">Off</option>
<option value="sms">SMS</option>
<option value="email">Email</option>
</CSelect>
</CCol>
</CRow>
<CRow>
<CLabel className="mb-2" md="6" xl="4" col htmlFor="name">
{t('user.phone_number')}
</CLabel>
<CCol md="6" xl="8">
{editing ? (
<CButton color="link" onClick={togglePhoneModal} className="pl-0">
{parseNumber()}
</CButton>
) : (
<p className="mt-2 mb-0">{parseNumber()}</p>
)}
</CCol>
</CRow>
</CCol>
<CCol lg="1" xl="1">
<Avatar src={avatar} fallback={user.email.value} />
<div className="pt-3">
<Avatar src={newAvatar} fallback={user.email.value} />
</div>
</CCol>
<CCol className="pt-2">
<div className="mt-1 mb-4">
<LoadingButton
label={t('user.delete_avatar')}
isLoadingLabel={t('user.deleting')}
isLoading={loading}
action={deleteAvatar}
block={false}
disabled={
!editing || loading || !avatar || avatar === '' || avatar === 'data:;base64,'
}
/>
</div>
<div className="pt-1">
<CInputFile
disabled={!editing}
id="file-input"
name="file-input"
accept="image/*"
onChange={showPreview}
key={fileInputKey}
/>
</div>
<CCol sm="12" md="6">
<CRow>
<CLabel className="mb-2" md="6" xl="4" col htmlFor="description">
{t('user.description')}
</CLabel>
<CCol md="6" xl="8">
{editing ? (
<CInput
id="description"
value={user.description.value}
onChange={updateUserWithId}
maxLength="50"
/>
) : (
<p className="mt-2 mb-0">{user.description.value}</p>
)}
</CCol>
</CRow>
<CRow>
<CLabel className="mb-2" md="6" xl="4" col htmlFor="newPassword">
{t('login.new_password')}
</CLabel>
<CCol md="6" xl="8">
{editing ? (
<CInputGroup>
<CInput
type={showPassword ? 'text' : 'password'}
id="newPassword"
value={user.newPassword.value}
onChange={updateUserWithId}
invalid={user.newPassword.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.make_sure_same_password')}</CInvalidFeedback>
</CInputGroup>
) : null}
</CCol>
</CRow>
<CRow>
<CLabel className="mb-2" md="6" xl="4" col htmlFor="confirmNewPassword">
{t('user.confirm_new_password')}
</CLabel>
<CCol md="6" xl="8">
{editing ? (
<CInputGroup>
<CInput
type={showPassword ? 'text' : 'password'}
id="confirmNewPassword"
value={user.confirmNewPassword.value}
onChange={updateUserWithId}
invalid={user.newPassword.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.make_sure_same_password')}</CInvalidFeedback>
</CInputGroup>
) : null}
</CCol>
</CRow>
</CCol>
</CRow>
</CCol>
<CCol lg="12" xl="6" className="pt-2">
<NotesTable
t={t}
notes={user.notes.value}
addNote={addNote}
loading={loading}
size="lg"
editable={editing}
/>
</CCol>
</CFormGroup>
</CRow>
<CRow>
<CCol />
<CCol xs={2} className="mt-2 text-right">
@@ -271,7 +267,6 @@ EditMyProfile.propTypes = {
updateUserWithId: PropTypes.func.isRequired,
loading: PropTypes.bool.isRequired,
policies: PropTypes.instanceOf(Object).isRequired,
addNote: PropTypes.func.isRequired,
avatar: PropTypes.string,
newAvatar: PropTypes.string,
showPreview: PropTypes.func.isRequired,
@@ -281,6 +276,7 @@ EditMyProfile.propTypes = {
testVerificationCode: PropTypes.func.isRequired,
updateWithKey: PropTypes.func.isRequired,
editing: PropTypes.bool.isRequired,
avatarDeleted: PropTypes.bool.isRequired,
};
EditMyProfile.defaultProps = {

View File

@@ -17,9 +17,8 @@ import {
} from '@coreui/react';
import PropTypes from 'prop-types';
import CIcon from '@coreui/icons-react';
import NotesTable from '../NotesTable';
const EditUserForm = ({ t, user, updateUserWithId, loading, policies, addNote }) => {
const EditUserForm = ({ t, user, updateUserWithId, policies, editing }) => {
const [showPassword, setShowPassword] = useState(false);
const toggleShowPassword = () => {
@@ -33,18 +32,26 @@ const EditUserForm = ({ t, user, updateUserWithId, loading, policies, addNote })
{t('user.name')}
</CLabel>
<CCol sm="4">
<CInput id="name" value={user.name.value} onChange={updateUserWithId} maxLength="20" />
{editing ? (
<CInput id="name" value={user.name.value} onChange={updateUserWithId} maxLength="20" />
) : (
<p className="mt-2 mb-0">{user.name.value}</p>
)}
</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"
/>
{editing ? (
<CInput
id="description"
value={user.description.value}
onChange={updateUserWithId}
maxLength="50"
/>
) : (
<p className="mt-2 mb-0">{user.description.value}</p>
)}
</CCol>
</CFormGroup>
<CFormGroup row>
@@ -52,7 +59,13 @@ const EditUserForm = ({ t, user, updateUserWithId, loading, policies, addNote })
{t('user.user_role')}
</CLabel>
<CCol sm="4">
<CSelect custom id="userRole" onChange={updateUserWithId} value={user.userRole.value}>
<CSelect
custom
id="userRole"
onChange={updateUserWithId}
value={user.userRole.value}
disabled={!editing}
>
<option value="accounting">Accounting</option>
<option value="admin">Admin</option>
<option value="csr">CSR</option>
@@ -68,27 +81,31 @@ const EditUserForm = ({ t, user, updateUserWithId, loading, policies, addNote })
{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>
{editing ? (
<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>
) : (
<p className="mt-2 mb-0" />
)}
</CCol>
</CFormGroup>
<CFormGroup row>
@@ -98,6 +115,7 @@ const EditUserForm = ({ t, user, updateUserWithId, loading, policies, addNote })
<CCol sm="1">
<CInputGroup>
<CSwitch
disabled={!editing}
id="changePassword"
color="success"
defaultChecked={user.changePassword.value}
@@ -106,9 +124,6 @@ const EditUserForm = ({ t, user, updateUserWithId, loading, policies, addNote })
/>
</CInputGroup>
</CCol>
<CCol sm="8">
<NotesTable t={t} notes={user.notes.value} addNote={addNote} loading={loading} />
</CCol>
</CFormGroup>
<CRow>
<CCol />
@@ -132,9 +147,8 @@ EditUserForm.propTypes = {
t: PropTypes.func.isRequired,
user: PropTypes.instanceOf(Object).isRequired,
updateUserWithId: PropTypes.func.isRequired,
loading: PropTypes.bool.isRequired,
policies: PropTypes.instanceOf(Object).isRequired,
addNote: PropTypes.func.isRequired,
editing: PropTypes.bool.isRequired,
};
export default React.memo(EditUserForm);

View File

@@ -1,9 +1,21 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { CButton, CModal, CModalBody, CModalHeader, CModalTitle, CPopover } from '@coreui/react';
import {
CButton,
CModal,
CModalBody,
CModalHeader,
CModalTitle,
CPopover,
CNav,
CNavLink,
CTabPane,
CTabContent,
} from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilSave, cilX } from '@coreui/icons';
import { cilPencil, cilSave, cilX } from '@coreui/icons';
import EditUserForm from '../EditUserForm';
import DetailedNotesTable from '../DetailedNotesTable';
const EditUserModal = ({
t,
@@ -13,39 +25,99 @@ const EditUserModal = ({
loading,
policies,
show,
editing,
toggleEditing,
toggle,
addNote,
}) => (
<CModal show={show} onClose={toggle} size="xl">
<CModalHeader className="p-1">
<CModalTitle className="pl-1 pt-1">
{t('user.edit')} {user.email.value}
</CModalTitle>
<div className="text-right">
<CPopover content={t('common.save')}>
<CButton color="primary" variant="outline" onClick={saveUser} disabled={loading}>
<CIcon content={cilSave} />
</CButton>
</CPopover>
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggle}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader>
<CModalBody>
<EditUserForm
t={t}
user={user}
updateUserWithId={updateUserWithId}
loading={loading}
policies={policies}
addNote={addNote}
/>
</CModalBody>
</CModal>
);
}) => {
const [index, setIndex] = useState(0);
const toggleModal = () => {
toggleEditing();
toggle();
};
useEffect(() => {
if (show) setIndex(0);
}, [show]);
return (
<CModal show={show} onClose={toggle} size="xl">
<CModalHeader className="p-1">
<CModalTitle className="pl-1 pt-1">
{t('user.edit')} {user.email.value}
</CModalTitle>
<div className="text-right">
<CPopover content={t('common.save')}>
<CButton color="primary" variant="outline" onClick={saveUser} disabled={loading}>
<CIcon content={cilSave} />
</CButton>
</CPopover>
<CPopover content={t('common.edit')}>
<CButton
disabled={editing}
color="primary"
variant="outline"
onClick={toggleEditing}
className="ml-2"
>
<CIcon name="cil-pencil" content={cilPencil} />
</CButton>
</CPopover>
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader>
<CModalBody className="px-3 pt-0">
<CNav variant="tabs" className="mb-0 p-0">
<CNavLink
className="font-weight-bold"
href="#"
active={index === 0}
onClick={() => setIndex(0)}
>
{t('common.main')}
</CNavLink>
<CNavLink
className="font-weight-bold"
href="#"
active={index === 1}
onClick={() => setIndex(1)}
>
{t('configuration.notes')}
</CNavLink>
</CNav>
<CTabContent>
<CTabPane active={index === 0} className="pt-2">
{index === 0 ? (
<EditUserForm
t={t}
user={user}
updateUserWithId={updateUserWithId}
policies={policies}
editing={editing}
/>
) : null}
</CTabPane>
<CTabPane active={index === 1}>
{index === 1 ? (
<DetailedNotesTable
t={t}
notes={user.notes.value}
addNote={addNote}
loading={loading}
editable={editing}
/>
) : null}
</CTabPane>
</CTabContent>
</CModalBody>
</CModal>
);
};
EditUserModal.propTypes = {
t: PropTypes.func.isRequired,
@@ -55,6 +127,8 @@ EditUserModal.propTypes = {
saveUser: PropTypes.func.isRequired,
policies: PropTypes.instanceOf(Object).isRequired,
show: PropTypes.bool.isRequired,
editing: PropTypes.bool.isRequired,
toggleEditing: PropTypes.func.isRequired,
toggle: PropTypes.func.isRequired,
addNote: PropTypes.func.isRequired,
};

View File

@@ -29,7 +29,7 @@ const FirmwareDashboard = ({ t, data }) => {
return (
<div>
<CRow>
<CRow className="mt-3">
<CCol>
<CWidgetIcon
text={t('common.last_dashboard_refresh')}
@@ -79,7 +79,7 @@ const FirmwareDashboard = ({ t, data }) => {
<CRow>
<CCol>
<CCard>
<CCardHeader>{t('common.firmware_installed')}</CCardHeader>
<CCardHeader className="dark-header">{t('common.firmware_installed')}</CCardHeader>
<CCardBody>
<CChartPie
datasets={data.firmwareDistribution.datasets}
@@ -96,7 +96,7 @@ const FirmwareDashboard = ({ t, data }) => {
</CCol>
<CCol>
<CCard>
<CCardHeader>{t('common.devices_using_latest')}</CCardHeader>
<CCardHeader className="dark-header">{t('common.devices_using_latest')}</CCardHeader>
<CCardBody>
<CChartBar
datasets={data.latest.datasets}
@@ -121,7 +121,7 @@ const FirmwareDashboard = ({ t, data }) => {
</CCol>
<CCol>
<CCard>
<CCardHeader>Unknown Firmware</CCardHeader>
<CCardHeader className="dark-header">Unknown Firmware</CCardHeader>
<CCardBody>
<CChartBar
datasets={data.unknownFirmwares.datasets}
@@ -148,7 +148,7 @@ const FirmwareDashboard = ({ t, data }) => {
<CRow>
<CCol>
<CCard>
<CCardHeader>{t('common.device_status')}</CCardHeader>
<CCardHeader className="dark-header">{t('common.device_status')}</CCardHeader>
<CCardBody>
<CChartPie
datasets={data.status.datasets}
@@ -171,7 +171,7 @@ const FirmwareDashboard = ({ t, data }) => {
</CCol>
<CCol>
<CCard>
<CCardHeader>{t('firmware.device_types')}</CCardHeader>
<CCardHeader className="dark-header">{t('firmware.device_types')}</CCardHeader>
<CCardBody>
<CChartPie
datasets={data.deviceType.datasets}
@@ -195,7 +195,7 @@ const FirmwareDashboard = ({ t, data }) => {
</CCol>
<CCol>
<CCard>
<CCardHeader>OUIs</CCardHeader>
<CCardHeader className="dark-header">OUIs</CCardHeader>
<CCardBody>
<CChartBar
datasets={data.ouis.datasets}
@@ -222,9 +222,15 @@ const FirmwareDashboard = ({ t, data }) => {
<CRow>
<CCol>
<CCard>
<CCardHeader>{t('common.endpoints')}</CCardHeader>
<CCardHeader className="dark-header">{t('common.endpoints')}</CCardHeader>
<CCardBody>
<CDataTable items={data.endpoints ?? []} fields={columns} hover border />
<CDataTable
addTableClasses="table-sm"
items={data.endpoints ?? []}
fields={columns}
hover
border
/>
</CCardBody>
</CCard>
</CCol>

View File

@@ -1,98 +0,0 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { CCard, CCardBody, CCardHeader, CCol, CCollapse, CInput, CRow } from '@coreui/react';
import { prettyDate, cleanBytesString } from '../../utils/formatting';
import NotesTable from '../NotesTable';
import LoadingButton from '../LoadingButton';
const FirmwareDetails = ({
t,
show,
item,
addNote,
addNoteLoading,
updateDescription,
updateDescriptionLoading,
}) => {
const [description, setDescription] = useState('');
const saveDescription = () => {
updateDescription(description, item.id);
};
return (
<CCollapse show={show}>
<CCard className="mt-3 mx-3">
<CCardHeader>{t('firmware.details_title', { image: item.image })}</CCardHeader>
<CCardBody>
<CRow>
<CCol sm="1">Created</CCol>
<CCol sm="5">{prettyDate(item.created)}</CCol>
<CCol sm="1">Release</CCol>
<CCol sm="5">{item.release}</CCol>
</CRow>
<CRow className="my-3">
<CCol sm="1">Image</CCol>
<CCol sm="5">{item.image}</CCol>
<CCol sm="1">Image Date</CCol>
<CCol sm="5">{prettyDate(item.imageDate)}</CCol>
</CRow>
<CRow className="my-3">
<CCol sm="1">Revision</CCol>
<CCol sm="5">{item.revision}</CCol>
<CCol sm="1">Size</CCol>
<CCol sm="5">{cleanBytesString(item.size)}</CCol>
</CRow>
<CRow className="my-3">
<CCol sm="1">URI</CCol>
<CCol sm="5">{item.uri}</CCol>
<CCol sm="1">Owner</CCol>
<CCol sm="5">{item.owner === '' ? t('common.unknown') : item.owner}</CCol>
</CRow>
<CRow className="my-3">
<CCol sm="1" className="mt-2">
Description
</CCol>
<CCol sm="4">
<CInput
id="description"
defaultValue={item.description}
maxLength="50"
onChange={(e) => setDescription(e.target.value)}
/>
</CCol>
<CCol sm="1">
<LoadingButton
label={t('common.save')}
isLoadingLabel={t('common.saving')}
isLoading={updateDescriptionLoading}
action={saveDescription}
disabled={updateDescriptionLoading}
/>
</CCol>
<CCol>
<NotesTable
t={t}
notes={item.notes}
addNote={addNote}
loading={addNoteLoading}
extraFunctionParameter={item.id}
/>
</CCol>
</CRow>
</CCardBody>
</CCard>
</CCollapse>
);
};
FirmwareDetails.propTypes = {
t: PropTypes.func.isRequired,
show: PropTypes.bool.isRequired,
item: PropTypes.instanceOf(Object).isRequired,
addNoteLoading: PropTypes.bool.isRequired,
addNote: PropTypes.func.isRequired,
updateDescription: PropTypes.func.isRequired,
updateDescriptionLoading: PropTypes.bool.isRequired,
};
export default React.memo(FirmwareDetails);

View File

@@ -0,0 +1,63 @@
import React from 'react';
import PropTypes from 'prop-types';
import { CCardBody, CCol, CInput, CRow } from '@coreui/react';
import { prettyDate, cleanBytesString } from '../../utils/formatting';
import NotesTable from '../NotesTable';
const FirmwareDetailsForm = ({ t, fields, updateFieldsWithId, addNote, editing }) => (
<CCardBody className="p-1">
<CRow>
<CCol sm="2">Created</CCol>
<CCol sm="4">{prettyDate(fields.created.value)}</CCol>
<CCol sm="2">Release</CCol>
<CCol sm="4">{fields.release.value}</CCol>
</CRow>
<CRow className="my-3">
<CCol sm="2">Image</CCol>
<CCol sm="4">{fields.image.value}</CCol>
<CCol sm="2">Image Date</CCol>
<CCol sm="4">{prettyDate(fields.imageDate.value)}</CCol>
</CRow>
<CRow className="my-3">
<CCol sm="2">Revision</CCol>
<CCol sm="4">{fields.revision.value}</CCol>
<CCol sm="2">Size</CCol>
<CCol sm="4">{cleanBytesString(fields.size.value)}</CCol>
</CRow>
<CRow className="my-3">
<CCol sm="2">URI</CCol>
<CCol sm="4">{fields.uri.value}</CCol>
<CCol sm="2">Owner</CCol>
<CCol sm="4">{fields.owner.value === '' ? t('common.unknown') : fields.owner.value}</CCol>
</CRow>
<CRow className="my-3">
<CCol sm="2" className="mt-2">
Description
</CCol>
<CCol sm="4">
{editing ? (
<CInput
id="description"
value={fields.description.value}
onChange={updateFieldsWithId}
maxLength="50"
/>
) : (
<p className="mt-2 mb-0">{fields.description.value}</p>
)}
</CCol>
<CCol>
<NotesTable t={t} notes={fields.notes.value} addNote={addNote} editable={editing} />
</CCol>
</CRow>
</CCardBody>
);
FirmwareDetailsForm.propTypes = {
t: PropTypes.func.isRequired,
fields: PropTypes.instanceOf(Object).isRequired,
updateFieldsWithId: PropTypes.func.isRequired,
addNote: PropTypes.func.isRequired,
editing: PropTypes.bool.isRequired,
};
export default FirmwareDetailsForm;

View File

@@ -12,7 +12,7 @@ const FirmwareHistoryModal = ({ t, loading, data }) => {
return (
<CDataTable
addTableClasses="ignore-overflow"
addTableClasses="ignore-overflow table-sm"
fields={columns}
items={data}
hover

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import ReactPaginate from 'react-paginate';
import { v4 as createUuid } from 'uuid';
@@ -12,8 +12,9 @@ import {
CSelect,
CSwitch,
} from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilSearch } from '@coreui/icons';
import { prettyDate, cleanBytesString } from '../../utils/formatting';
import FirmwareDetails from '../FirmwareDetails';
import CopyToClipboardButton from '../CopyToClipboardButton';
const FirmwareList = ({
@@ -23,19 +24,15 @@ const FirmwareList = ({
pageCount,
setPage,
data,
toggleEditModal,
firmwarePerPage,
setFirmwarePerPage,
selectedDeviceType,
deviceTypes,
setSelectedDeviceType,
addNote,
addNoteLoading,
updateDescription,
updateDescriptionLoading,
displayDev,
toggleDevDisplay,
}) => {
const [detailsShown, setDetailsShown] = useState([]);
const fields = [
{ key: 'imageDate', label: t('firmware.image_date'), _style: { width: '1%' } },
{ key: 'size', label: t('firmware.size'), _style: { width: '1%' } },
@@ -44,18 +41,6 @@ const FirmwareList = ({
{ key: 'show_details', label: '', _style: { width: '5%' } },
];
const toggleDetails = (index) => {
const position = detailsShown.indexOf(index);
let newDetails = detailsShown.slice();
if (position !== -1) {
newDetails.splice(position, 1);
} else {
newDetails = [...newDetails, index];
}
setDetailsShown(newDetails);
};
const getShortRevision = (revision) => {
if (revision.includes(' / ')) {
return revision.split(' / ')[1];
@@ -64,17 +49,12 @@ const FirmwareList = ({
};
const changePage = (newValue) => {
setDetailsShown([]);
setPage(newValue);
};
useEffect(() => {
setDetailsShown([]);
}, [selectedDeviceType]);
return (
<CCard>
<CCardHeader className="py-2 px-3">
<CCard className="m-0">
<CCardHeader className="dark-header">
<div className="d-flex flex-row-reverse">
<div className="px-3">
<CSwitch
@@ -105,6 +85,7 @@ const FirmwareList = ({
</CCardHeader>
<CCardBody className="p-0">
<CDataTable
addTableClasses="table-sm"
items={data}
fields={fields}
loading={loading}
@@ -134,7 +115,7 @@ const FirmwareList = ({
<td className="align-middle">
<div style={{ width: 'calc(45vw)' }}>
<div className="text-truncate align-middle">
<CopyToClipboardButton key={item.uri} t={t} size="md" content={item.uri} />
<CopyToClipboardButton key={item.uri} t={t} size="sm" content={item.uri} />
<CPopover content={item.uri}>
<span>{item.uri}</span>
</CPopover>
@@ -142,28 +123,18 @@ const FirmwareList = ({
</div>
</td>
),
show_details: (item, index) => (
show_details: (item) => (
<td className="text-center align-middle">
<CButton
size="sm"
color="primary"
variant={detailsShown.includes(index) ? '' : 'outline'}
onClick={() => toggleDetails(index)}
variant="outline"
onClick={() => toggleEditModal(item.id)}
>
{detailsShown.includes(index) ? t('common.hide') : t('common.details')}
<CIcon content={cilSearch} />
</CButton>
</td>
),
details: (item, index) => (
<FirmwareDetails
t={t}
show={detailsShown.includes(index)}
item={item}
addNote={addNote}
addNoteLoading={addNoteLoading}
updateDescription={updateDescription}
updateDescriptionLoading={updateDescriptionLoading}
/>
),
}}
/>
<div className="d-flex flex-row pl-3">
@@ -217,12 +188,9 @@ FirmwareList.propTypes = {
selectedDeviceType: PropTypes.string.isRequired,
deviceTypes: PropTypes.instanceOf(Array).isRequired,
setSelectedDeviceType: PropTypes.func.isRequired,
addNote: PropTypes.func.isRequired,
addNoteLoading: PropTypes.bool.isRequired,
updateDescription: PropTypes.func.isRequired,
updateDescriptionLoading: PropTypes.bool.isRequired,
displayDev: PropTypes.bool.isRequired,
toggleDevDisplay: PropTypes.func.isRequired,
toggleEditModal: PropTypes.func.isRequired,
};
export default React.memo(FirmwareList);

View File

@@ -21,7 +21,7 @@ const DeleteButton = ({ t, tag, deleteTag, hideTooltips }) => {
variant="outline"
shape="square"
size="sm"
className="mx-2"
className="mx-1"
data-tip
data-for={tooltipId}
data-event="click"

View File

@@ -21,7 +21,7 @@ const UnassignButton = ({ t, tag, unassignTag, hideTooltips }) => {
variant="outline"
shape="square"
size="sm"
className="mx-2"
className="mx-1"
data-tip
data-for={tooltipId}
data-event="click"

View File

@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import ReactPaginate from 'react-paginate';
import { CButton, CDataTable, CLink, CPopover, CButtonToolbar, CSelect } from '@coreui/react';
import { cilPencil, cilPlus, cilRouter, cilSpreadsheet } from '@coreui/icons';
import { cilMagnifyingGlass, cilPlus, cilRouter, cilSpreadsheet, cilZoomIn } from '@coreui/icons';
import CIcon from '@coreui/icons-react';
import ReactTooltip from 'react-tooltip';
import DeleteButton from './DeleteButton';
@@ -31,6 +31,7 @@ const InventoryTable = ({
pushConfig,
}) => {
const [gwUi] = useState(localStorage.getItem('owgw-ui'));
const columns =
onlyEntity || onlyUnassigned
? [
@@ -70,7 +71,7 @@ const InventoryTable = ({
return (
<>
<CDataTable
addTableClasses="ignore-overflow"
addTableClasses="ignore-overflow table-sm"
items={tags ?? []}
fields={columns}
hover
@@ -79,15 +80,9 @@ const InventoryTable = ({
scopedSlots={{
serialNumber: (item) => (
<td className="align-middle">
<CLink
className="c-subheader-nav-link align-self-center"
aria-current="page"
href={`${gwUi}/#/devices/${item.serialNumber}`}
target="_blank"
disabled={!gwUi || gwUi === ''}
>
<CButton color="link" onClick={() => toggleEditModal(item.serialNumber)}>
{item.serialNumber}
</CLink>
</CButton>
</td>
),
name: (item) => <td className="align-middle">{item.name}</td>,
@@ -107,7 +102,7 @@ const InventoryTable = ({
toggleAssociate({
serialNumber: item.serialNumber,
uuid: item.deviceConfiguration,
value: item.deviceConfigurationName,
value: item.extendedInfo?.deviceConfiguration?.name ?? '',
})
}
>
@@ -152,7 +147,7 @@ const InventoryTable = ({
<CButtonToolbar
role="group"
className="justify-content-flex-end"
style={{ width: '300px' }}
style={{ width: '290px' }}
>
<CPopover content={t('inventory.assign_ent_ven')}>
<div>
@@ -162,7 +157,7 @@ const InventoryTable = ({
variant="outline"
shape="square"
size="sm"
className="mx-2"
className="mx-1"
onClick={() =>
entity !== null
? assignToEntity(item.serialNumber)
@@ -187,7 +182,7 @@ const InventoryTable = ({
variant="outline"
shape="square"
size="sm"
className="mx-2"
className="mx-1 d"
onClick={() => pushConfig(item.serialNumber)}
style={{ width: '33px', height: '30px' }}
disabled={item.deviceConfigurationName === ''}
@@ -201,24 +196,38 @@ const InventoryTable = ({
variant="outline"
shape="square"
size="sm"
className="mx-2"
className="mx-1"
onClick={() => toggleComputed(item.serialNumber)}
style={{ width: '33px', height: '30px' }}
>
<CIcon name="cil-spreadsheet" content={cilSpreadsheet} size="sm" />
</CButton>
</CPopover>
<CPopover content="Edit Tag">
<CPopover content="View Tag">
<CButton
color="primary"
variant="outline"
shape="square"
size="sm"
className="mx-2"
className="mx-1"
onClick={() => toggleEditModal(item.serialNumber)}
style={{ width: '33px', height: '30px' }}
>
<CIcon name="cil-pencil" content={cilPencil} size="sm" />
<CIcon name="cil-magnifying-glass" content={cilMagnifyingGlass} size="sm" />
</CButton>
</CPopover>
<CPopover content={t('inventory.view_in_gateway')}>
<CButton
color="primary"
variant="outline"
shape="square"
size="sm"
className="mx-1"
onClick={() => window.open(`${gwUi}/#/devices/${item.serialNumber}`, '_blank')}
disabled={!gwUi || gwUi === ''}
style={{ width: '33px', height: '30px' }}
>
<CIcon content={cilZoomIn} />
</CButton>
</CPopover>
</CButtonToolbar>

View File

@@ -20,7 +20,7 @@ const DeleteButton = ({ t, location, deleteLocation, hideTooltips }) => {
variant="outline"
shape="square"
size="sm"
className="mx-2"
className="mx-1"
data-tip
data-for={tooltipId}
data-event="click"

View File

@@ -55,7 +55,7 @@ const LocationTable = ({
return (
<>
<CDataTable
addTableClasses="ignore-overflow"
addTableClasses="ignore-overflow table-sm"
items={locations}
fields={columns}
hover
@@ -94,7 +94,7 @@ const LocationTable = ({
<CButtonToolbar
role="group"
className="justify-content-flex-end"
style={{ width: '140px' }}
style={{ width: '125px' }}
>
<CPopover content={t('inventory.assign_ent_ven')}>
<div>
@@ -104,7 +104,7 @@ const LocationTable = ({
variant="outline"
shape="square"
size="sm"
className="mx-2"
className="mx-1"
onClick={() => assignToEntity(item.id)}
style={{ width: '33px', height: '30px' }}
>
@@ -124,7 +124,7 @@ const LocationTable = ({
variant="outline"
shape="square"
size="sm"
className="ml-2"
className="mx-1"
onClick={() => toggleEditModal(item.id)}
style={{ width: '33px', height: '30px' }}
>

View File

@@ -56,9 +56,9 @@ const NotesTable = ({
<CCol>
<div className="overflow-auto" style={{ height: '200px' }}>
<CDataTable
striped
responsive
border
addTableClasses="table-sm"
loading={loading}
fields={columns}
items={notes || []}
@@ -106,15 +106,14 @@ const NotesTable = ({
/>
</CCol>
</CRow>
<CRow className="pt-3">
<CRow className="pt-1">
<CCol sm="2" />
<CCol>
<div className="overflow-auto border" style={{ height: '200px' }}>
<CDataTable
striped
responsive
border
addTableClasses="m-0 p-0"
addTableClasses="m-0 p-0 table-sm"
loading={loading}
fields={columns}
items={notes || []}
@@ -146,10 +145,9 @@ const NotesTable = ({
<CCol>
<div className="overflow-auto border" style={{ height: '200px' }}>
<CDataTable
striped
responsive
border
addTableClasses="m-0 p-0"
addTableClasses="m-0 p-0 table-sm"
loading={loading}
fields={columns}
items={notes || []}

View File

@@ -20,6 +20,7 @@ const RadioAnalysisTable = ({ data, loading }) => {
return (
<CDataTable
addTableClasses="table-sm"
fields={columns}
items={data}
hover

View File

@@ -70,7 +70,7 @@ const UserListTable = ({
return (
<div>
<CCard className="my-0 py-0">
<CCardHeader className="my-0 p-1 text-light" style={{ backgroundColor: '#2f3d54' }}>
<CCardHeader className="dark-header">
<div style={{ fontWeight: '600' }} className=" text-value-lg float-left">
{t('user.users')}
</div>
@@ -93,6 +93,7 @@ const UserListTable = ({
</CCardHeader>
<CCardBody className="p-0">
<CDataTable
addTableClasses="table-sm"
items={users}
fields={fields}
loading={loading}

View File

@@ -85,7 +85,7 @@ const VenueTable = ({
</CCardHeader>
<CCardBody className="p-0">
<CDataTable
addTableClasses="ignore-overflow"
addTableClasses="ignore-overflow table-sm"
items={venues ?? []}
fields={columns}
hover
@@ -120,7 +120,7 @@ const VenueTable = ({
<CButtonToolbar
role="group"
className="justify-content-flex-end pl-2"
style={{ width: '100px' }}
style={{ width: '90px' }}
>
<CPopover content="Edit Tag">
<CButton
@@ -128,7 +128,7 @@ const VenueTable = ({
variant="outline"
shape="square"
size="sm"
className="mx-1"
className="mr-1"
onClick={() => history.push(`/venue/${item.id}`)}
style={{ width: '33px', height: '30px' }}
>

View File

@@ -61,7 +61,7 @@ const WifiAnalysisTable = ({ t, data, loading }) => {
return (
<div>
<CDataTable
addTableClasses="ignore-overflow mb-5"
addTableClasses="ignore-overflow mb-5 table-sm"
fields={columns}
items={data}
hover

View File

@@ -47,12 +47,50 @@ export const EntityProvider = ({ axiosInstance, selectedEntity, children }) => {
const resetEntity = () => setEntity(null);
const setEntityId = async (entityId, isVenue) => {
const newEntity = await getEntity(entityId, isVenue);
if (newEntity && !_.isEqual(newEntity, entity)) {
const newObj = {
...newEntity,
uuid: newEntity.id,
name: newEntity.name,
path: null,
isVenue,
childrenIds: newEntity.children,
childrenVenues: newEntity.venues,
extraData: newEntity,
refreshId: createUuid(),
};
setEntity({ ...newObj });
}
};
const setProviderEntity = async (entityToLoad) => {
const newEntity = await getEntity(entityToLoad.uuid, entityToLoad.isVenue);
if (newEntity) {
const newObj = {
...newEntity,
uuid: newEntity.id,
name: newEntity.name,
path: null,
isVenue: entityToLoad.isVenue,
childrenIds: newEntity.children,
childrenVenues: newEntity.venues,
extraData: newEntity,
refreshId: createUuid(),
};
setEntity({ ...newObj });
}
};
const selectEntity = (uuid, name, path, isVenue, childrenEntities, childrenVenues, extraData) => {
// If we have not yet gotten the information of this entity's children, we get them now
if (childrenEntities || childrenVenues) {
setEntityToRetrieve({ childrenEntities, childrenVenues, path, uuid, isVenue });
}
setEntity({
setProviderEntity({
uuid,
name,
isVenue,
@@ -104,25 +142,6 @@ export const EntityProvider = ({ axiosInstance, selectedEntity, children }) => {
});
};
const setProviderEntity = async (id, isVenue) => {
const newEntity = await getEntity(id, isVenue);
if (newEntity && !_.isEqual(newEntity, entity)) {
const newObj = {
...newEntity,
uuid: newEntity.id,
name: newEntity.name,
path: null,
isVenue,
childrenIds: newEntity.children,
childrenVenues: newEntity.venues,
extraData: newEntity,
refreshId: createUuid(),
};
setEntity({ ...newObj });
}
};
const getEntityChildren = async (parent) => {
const loadedParents = parentsWithChildrenLoaded;
const basePath = `${parent.path}._children.`;
@@ -435,6 +454,7 @@ export const EntityProvider = ({ axiosInstance, selectedEntity, children }) => {
deleteEntity,
deviceTypes,
resetEntity,
setEntityId,
}}
>
{children}

View File

@@ -16,6 +16,7 @@ export { default as AddLocationForm } from './components/AddLocationForm';
export { default as AddressEditor } from './components/AddressEditor';
export { default as ApiStatusCard } from './components/ApiStatusCard';
export { default as Avatar } from './components/Avatar';
export { default as CompactNotesTable } from './components/CompactNotesTable';
export { default as ConfigurationCustomMultiModal } from './components/Configuration/CustomMultiModal';
export { default as ConfigurationElement } from './components/Configuration/ConfigurationElement';
export { default as ConfigurationFileField } from './components/Configuration/FileField';
@@ -32,6 +33,7 @@ export { default as ContactTable } from './components/ContactTable';
export { default as CopyToClipboardButton } from './components/CopyToClipboardButton';
export { default as CreateUserForm } from './components/CreateUserForm';
export { default as ConfigurationTable } from './components/ConfigurationTable';
export { default as DetailedNotesTable } from './components/DetailedNotesTable';
export { default as DeviceDetails } from './components/DeviceDetails';
export { default as DeviceFirmwareModal } from './components/DeviceFirmwareModal';
export { default as DeviceListTable } from './components/DeviceListTable';
@@ -48,6 +50,7 @@ export { default as EditUserModal } from './components/EditUserModal';
export { default as EventQueueModal } from './components/EventQueueModal';
export { default as InventoryTable } from './components/InventoryTable';
export { default as FileToStringButton } from './components/FileToStringButton';
export { default as FirmwareDetailsForm } from './components/FirmwareDetailsForm';
export { default as FirmwareHistoryTable } from './components/FirmwareHistoryTable';
export { default as FirmwareList } from './components/FirmwareList';
export { default as FormattedDate } from './components/FormattedDate';