28 Commits

Author SHA1 Message Date
Irtiza-h30
5a49a0301e get radius profile query 2020-08-05 12:02:08 -04:00
Sean Macfarlane
afc222bb4a fixed root path bug 2020-08-04 01:46:25 -04:00
Sean Macfarlane
c595424269 update version 2020-08-03 17:17:25 -04:00
Sean Macfarlane
69fbceefff Merge pull request #40 from Telecominfraproject/hotfix/docker
Hotfix/docker: made API URL Dynamic
2020-08-03 14:53:18 -04:00
Sean Macfarlane
f9c66adf00 update versiom 2020-08-03 14:51:02 -04:00
Sean Macfarlane
56c0329bdb Merge branch 'master' into hotfix/docker 2020-08-03 14:33:57 -04:00
Sean Macfarlane
06cb3feac3 fixed jq 2020-08-03 14:32:56 -04:00
Sean Macfarlane
a159705cae fixed client history 2020-08-03 13:21:13 -04:00
Sean Macfarlane
02c20ffd6e fixed Alarms error handling 2020-08-03 12:08:08 -04:00
Sean Macfarlane
dc8374300e make API url dynamic 2020-08-03 12:03:56 -04:00
Sean Macfarlane
c001ce21ba update version 2020-08-02 13:20:03 -04:00
Sean Macfarlane
04bbc5fbed Merge pull request #35 from Telecominfraproject/feature/TW-843-polling
Feature/TW-843-polling: Line charts with polling new data after an interval
2020-08-01 13:12:48 -04:00
Sean Macfarlane
902de8bcb0 renamed Capacity to Occupany 2020-08-01 13:12:12 -04:00
Sean Macfarlane
6c6635017a cleanup 2020-08-01 12:58:43 -04:00
Sean Macfarlane
8891b5b3f9 cleanup 2020-08-01 12:51:18 -04:00
Sean Macfarlane
8d2e5bb8a5 Merge pull request #38 from Telecominfraproject/feature/TW-957
feature/TW-957: Blocked List Page
2020-07-30 11:47:20 -04:00
Sean Macfarlane
f29f31558a update version 2020-07-30 11:28:54 -04:00
Sean Macfarlane
6e0cf15616 Merge branch 'master' into feature/TW-957 2020-07-30 11:01:14 -04:00
Alidev123
49d5771fca Merge branch 'master' of https://github.com/Telecominfraproject/wlan-cloud-ui into feature/TW-843-polling 2020-07-30 10:41:23 -04:00
Alidev123
3794869adb fixed Line Charts polling 2020-07-30 10:41:09 -04:00
Sean Macfarlane
3d105340af Merge pull request #39 from Telecominfraproject/hotfix/TW-968
hotfix/TW-968: seconds to hh:mm:ss format
2020-07-29 17:12:48 -04:00
Sean Macfarlane
c8ee2138ad added days 2020-07-29 17:12:12 -04:00
Irtiza-h30
2e9dd99f3a seconds to hh:mm:ss format 2020-07-29 16:36:26 -04:00
Alidev123
0d58a8bb6a fixed line charts polling 2020-07-29 15:12:01 -04:00
Alidev123
19c0bb4393 Merge branch 'master' of https://github.com/Telecominfraproject/wlan-cloud-ui into feature/TW-843-polling 2020-07-27 09:50:38 -04:00
Alidev123
2a521ce036 fixed line charts re-rendering issue 2020-07-27 09:48:55 -04:00
Alidev123
85e1ee16ee fixed line chart component 2020-07-24 15:23:49 -04:00
Alidev123
195518a137 updated linecharts with polling new data after every 5 min 2020-07-24 12:18:26 -04:00
14 changed files with 198 additions and 100 deletions

View File

@@ -24,7 +24,11 @@ RUN npm run build
# production environment # production environment
FROM nginx:stable-alpine FROM nginx:stable-alpine
RUN apk add --no-cache jq
COPY --from=build /app/dist /usr/share/nginx/html COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
COPY docker_entrypoint.sh generate_config_js.sh /
RUN chmod +x docker_entrypoint.sh generate_config_js.sh
EXPOSE 80 EXPOSE 80
CMD ["nginx", "-g", "daemon off;"] ENTRYPOINT ["/docker_entrypoint.sh"]

1
app/config.js Normal file
View File

@@ -0,0 +1 @@
window.REACT_APP_API = undefined;

View File

@@ -31,6 +31,7 @@ const Alarms = () => {
const { customerId } = useContext(UserContext); const { customerId } = useContext(UserContext);
const { loading, error, data, refetch, fetchMore } = useQuery(GET_ALL_ALARMS, { const { loading, error, data, refetch, fetchMore } = useQuery(GET_ALL_ALARMS, {
variables: { customerId }, variables: { customerId },
errorPolicy: 'all',
}); });
const handleOnReload = () => { const handleOnReload = () => {
@@ -73,9 +74,10 @@ const Alarms = () => {
return <Loading />; return <Loading />;
} }
if (error) { if (error && !data?.getAllAlarms?.items) {
return <Alert message="Error" description="Failed to load alarms." type="error" showIcon />; return <Alert message="Error" description="Failed to load alarms." type="error" showIcon />;
} }
return ( return (
<AlarmsPage <AlarmsPage
data={data.getAllAlarms.items} data={data.getAllAlarms.items}

View File

@@ -1,4 +1,4 @@
import React, { useContext, useMemo, useState } from 'react'; import React, { useContext, useEffect, useMemo, useState, useRef } from 'react';
import { Alert } from 'antd'; import { Alert } from 'antd';
import moment from 'moment'; import moment from 'moment';
import { useQuery } from '@apollo/react-hooks'; import { useQuery } from '@apollo/react-hooks';
@@ -36,104 +36,164 @@ const USER_FRIENDLY_RADIOS = {
is5GHz: '5GHz', is5GHz: '5GHz',
}; };
const lineChartConfig = [
{ key: 'inservicesAPs', title: 'Inservice APs (24 hours)' },
{ key: 'clientDevices', title: 'Client Devices (24 hours)' },
{
key: 'traffic',
title: 'Traffic (24 hours)',
options: { formatter: trafficLabelFormatter, tooltipFormatter: trafficTooltipFormatter },
},
];
const Dashboard = () => { const Dashboard = () => {
const initialGraphTime = useRef({
toTime: moment()
.valueOf()
.toString(),
fromTime: moment()
.subtract(24, 'hours')
.valueOf()
.toString(),
});
const { customerId } = useContext(UserContext); const { customerId } = useContext(UserContext);
const { loading, error, data } = useQuery(GET_ALL_STATUS, { const { loading, error, data } = useQuery(GET_ALL_STATUS, {
variables: { customerId, statusDataTypes: ['CUSTOMER_DASHBOARD'] }, variables: { customerId, statusDataTypes: ['CUSTOMER_DASHBOARD'] },
}); });
const [toTime] = useState(
moment()
.valueOf()
.toString()
);
const [fromTime] = useState(
moment()
.subtract(24, 'hours')
.valueOf()
.toString()
);
const { const [lineChartData, setLineChartData] = useState({
loading: metricsLoading, inservicesAPs: {
error: metricsError, key: 'Inservice APs',
data: metricsData, value: [],
// refetch: metricsRefetch, },
} = useQuery(FILTER_SYSTEM_EVENTS, { clientDevices: {
variables: { is2dot4GHz: {
customerId, key: USER_FRIENDLY_RADIOS.is2dot4GHz,
fromTime, value: [],
toTime, },
equipmentIds: [0], is5GHz: {
dataTypes: ['StatusChangedEvent'], key: USER_FRIENDLY_RADIOS.is5GHz,
limit: 1000, value: [],
},
},
traffic: {
trafficBytesDownstream: {
key: 'Down Stream',
value: [],
},
trafficBytesUpstream: {
key: 'Up Stream',
value: [],
},
}, },
}); });
const formatLineChartData = (list = []) => { const { loading: metricsLoading, error: metricsError, data: metricsData, fetchMore } = useQuery(
const lineChartData = { FILTER_SYSTEM_EVENTS,
inservicesAPs: { {
title: 'Inservice APs (24 hours)', variables: {
data: { key: 'Inservice APs', value: [] }, customerId,
fromTime: initialGraphTime.current.fromTime,
toTime: initialGraphTime.current.toTime,
equipmentIds: [0],
dataTypes: ['StatusChangedEvent'],
limit: 3000, // TODO: make get all in GraphQL
}, },
clientDevices: { title: 'Client Devices (24 hours)' }, }
traffic: { );
title: 'Traffic (24 hours)',
formatter: trafficLabelFormatter,
tooltipFormatter: trafficTooltipFormatter,
data: {
trafficBytesDownstream: { key: 'Down Stream', value: [] },
trafficBytesUpstream: { key: 'Up Stream', value: [] },
},
},
};
const clientDevicesData = {};
list.forEach( const formatLineChartData = (list = []) => {
({ if (list.length) {
eventTimestamp, setLineChartData(prev => {
details: { const inservicesAPs = [];
payload: { const clientDevices2dot4GHz = [];
const clientDevices5GHz = [];
const trafficBytesDownstreamData = [];
const trafficBytesUpstreamData = [];
list.forEach(
({
eventTimestamp,
details: { details: {
equipmentInServiceCount, payload: {
associatedClientsCountPerRadio: radios, details: {
trafficBytesDownstream, equipmentInServiceCount,
trafficBytesUpstream, associatedClientsCountPerRadio: radios,
trafficBytesDownstream,
trafficBytesUpstream,
},
},
},
}) => {
inservicesAPs.push([eventTimestamp, equipmentInServiceCount]);
let total5GHz = 0;
total5GHz += (radios?.is5GHz || 0) + (radios?.is5GHzL || 0) + (radios?.is5GHzU || 0); // combine all 5GHz radios
clientDevices2dot4GHz.push([eventTimestamp, radios.is2dot4GHz || 0]);
clientDevices5GHz.push([eventTimestamp, total5GHz]);
trafficBytesDownstreamData.push([eventTimestamp, trafficBytesDownstream]);
trafficBytesUpstreamData.push([eventTimestamp, trafficBytesUpstream]);
}
);
return {
inservicesAPs: {
...prev.inservicesAPs,
value: [...prev.inservicesAPs.value, ...inservicesAPs],
},
clientDevices: {
is2dot4GHz: {
...prev.clientDevices.is2dot4GHz,
value: [...prev.clientDevices.is2dot4GHz.value, ...clientDevices2dot4GHz],
},
is5GHz: {
...prev.clientDevices.is5GHz,
value: [...prev.clientDevices.is5GHz.value, ...clientDevices5GHz],
}, },
}, },
}, traffic: {
}) => { trafficBytesDownstream: {
lineChartData.inservicesAPs.data.value.push([eventTimestamp, equipmentInServiceCount]); ...prev.traffic.trafficBytesDownstream,
Object.keys(radios).forEach(key => { value: [...prev.traffic.trafficBytesDownstream.value, ...trafficBytesDownstreamData],
if (!clientDevicesData[key]) { },
clientDevicesData[key] = { trafficBytesUpstream: {
key: USER_FRIENDLY_RADIOS[key] || key, ...prev.traffic.trafficBytesUpstream,
value: [], value: [...prev.traffic.trafficBytesUpstream.value, ...trafficBytesUpstreamData],
}; },
} },
clientDevicesData[key].value.push([eventTimestamp, radios[key]]); };
}); });
}
lineChartData.traffic.data.trafficBytesDownstream.value.push([
eventTimestamp,
trafficBytesDownstream,
]);
lineChartData.traffic.data.trafficBytesUpstream.value.push([
eventTimestamp,
trafficBytesUpstream,
]);
}
);
return {
...lineChartData,
clientDevices: { ...lineChartData.clientDevices, data: { ...clientDevicesData } },
};
}; };
const lineChartsData = useMemo( useEffect(() => {
() => formatLineChartData(metricsData?.filterSystemEvents?.items), const interval = setInterval(() => {
[metricsData] const toTime = moment()
); .valueOf()
.toString();
const fromTime = moment()
.subtract(5, 'minutes')
.valueOf()
.toString();
fetchMore({
variables: {
fromTime,
toTime,
},
updateQuery: (_, { fetchMoreResult }) => {
formatLineChartData(fetchMoreResult?.filterSystemEvents?.items);
},
});
}, 300000);
return () => clearInterval(interval);
}, []);
useEffect(() => {
formatLineChartData(metricsData?.filterSystemEvents?.items);
}, [metricsData]);
const statsArr = useMemo(() => { const statsArr = useMemo(() => {
const status = data?.getAllStatus?.items[0]?.detailsJSON || {}; const status = data?.getAllStatus?.items[0]?.detailsJSON || {};
@@ -205,7 +265,8 @@ const Dashboard = () => {
<DashboardPage <DashboardPage
statsCardDetails={statsArr} statsCardDetails={statsArr}
pieChartDetails={pieChartsData} pieChartDetails={pieChartsData}
lineChartDetails={lineChartsData} lineChartData={lineChartData}
lineChartConfig={lineChartConfig}
lineChartLoading={metricsLoading} lineChartLoading={metricsLoading}
lineChartError={metricsError} lineChartError={metricsError}
/> />

View File

@@ -1,11 +1,14 @@
import React, { useEffect, useContext } from 'react'; import React, { useEffect, useContext } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import moment from 'moment';
import { useLazyQuery } from '@apollo/react-hooks'; import { useLazyQuery } from '@apollo/react-hooks';
import { Alert } from 'antd'; import { Alert } from 'antd';
import { floor, padStart } from 'lodash';
import { NetworkTable, Loading } from '@tip-wlan/wlan-cloud-ui-library'; import { NetworkTable, Loading } from '@tip-wlan/wlan-cloud-ui-library';
import UserContext from 'contexts/UserContext'; import UserContext from 'contexts/UserContext';
import { FILTER_EQUIPMENT } from 'graphql/queries'; import { FILTER_EQUIPMENT } from 'graphql/queries';
import styles from './index.module.scss'; import styles from './index.module.scss';
const renderTableCell = tabCell => { const renderTableCell = tabCell => {
@@ -22,6 +25,13 @@ const renderTableCell = tabCell => {
return tabCell; return tabCell;
}; };
const durationToString = duration =>
`${floor(duration.asDays())}d ${floor(duration.hours())}h ${padStart(
duration.minutes(),
2,
0
)}m ${padStart(duration.seconds(), 2, 0)}s`;
const accessPointsTableColumns = [ const accessPointsTableColumns = [
{ {
title: 'NAME', title: 'NAME',
@@ -61,7 +71,7 @@ const accessPointsTableColumns = [
{ {
title: 'UP TIME', title: 'UP TIME',
dataIndex: ['status', 'osPerformance', 'details', 'uptimeInSeconds'], dataIndex: ['status', 'osPerformance', 'details', 'uptimeInSeconds'],
render: renderTableCell, render: upTimeInSeconds => durationToString(moment.duration(upTimeInSeconds, 'seconds')),
}, },
{ {
title: 'PROFILE', title: 'PROFILE',
@@ -74,7 +84,7 @@ const accessPointsTableColumns = [
render: renderTableCell, render: renderTableCell,
}, },
{ {
title: 'CAPACITY', title: 'OCCUPANCY',
dataIndex: ['status', 'radioUtilization', 'details', 'capacityDetails'], dataIndex: ['status', 'radioUtilization', 'details', 'capacityDetails'],
render: renderTableCell, render: renderTableCell,
}, },

View File

@@ -12,7 +12,7 @@ import UserContext from 'contexts/UserContext';
import { GET_CLIENT_SESSION, FILTER_SERVICE_METRICS } from 'graphql/queries'; import { GET_CLIENT_SESSION, FILTER_SERVICE_METRICS } from 'graphql/queries';
const toTime = moment(); const toTime = moment();
const fromTime = moment().subtract(1, 'hour'); const fromTime = moment().subtract(4, 'hours');
const ClientDeviceDetails = () => { const ClientDeviceDetails = () => {
const { id } = useParams(); const { id } = useParams();
@@ -33,7 +33,7 @@ const ClientDeviceDetails = () => {
toTime: toTime.valueOf().toString(), toTime: toTime.valueOf().toString(),
clientMacs: [id], clientMacs: [id],
dataTypes: ['Client'], dataTypes: ['Client'],
limit: 100, limit: 1000,
}, },
}); });
@@ -73,7 +73,7 @@ const ClientDeviceDetails = () => {
metricsData={ metricsData={
metricsData && metricsData.filterServiceMetrics && metricsData.filterServiceMetrics.items metricsData && metricsData.filterServiceMetrics && metricsData.filterServiceMetrics.items
} }
historyDate={toTime} historyDate={{ toTime, fromTime }}
/> />
); );
}; };

View File

@@ -77,9 +77,15 @@ const ProfileDetails = () => {
const { loading, error, data } = useQuery(GET_PROFILE, { const { loading, error, data } = useQuery(GET_PROFILE, {
variables: { id }, variables: { id },
}); });
const { data: ssidProfiles } = useQuery(GET_ALL_PROFILES, { const { data: ssidProfiles } = useQuery(GET_ALL_PROFILES, {
variables: { customerId, type: 'ssid' }, variables: { customerId, type: 'ssid' },
}); });
const { data: radiusProfiles } = useQuery(GET_ALL_PROFILES, {
variables: { customerId, type: 'radius' },
});
const [updateProfile] = useMutation(UPDATE_PROFILE); const [updateProfile] = useMutation(UPDATE_PROFILE);
const [deleteProfile] = useMutation(DELETE_PROFILE); const [deleteProfile] = useMutation(DELETE_PROFILE);
@@ -170,6 +176,7 @@ const ProfileDetails = () => {
ssidProfiles={ ssidProfiles={
(ssidProfiles && ssidProfiles.getAllProfiles && ssidProfiles.getAllProfiles.items) || [] (ssidProfiles && ssidProfiles.getAllProfiles && ssidProfiles.getAllProfiles.items) || []
} }
radiusProfiles={radiusProfiles?.getAllProfiles?.items}
fileUpload={handleFileUpload} fileUpload={handleFileUpload}
/> />
); );

View File

@@ -181,6 +181,7 @@ export const FILTER_SERVICE_METRICS = gql`
rssi rssi
rxBytes rxBytes
txBytes txBytes
detailsJSON
} }
context { context {
lastPage lastPage

View File

@@ -9,6 +9,7 @@
<!-- Allow installing the app to the homescreen --> <!-- Allow installing the app to the homescreen -->
<meta name="mobile-web-app-capable" content="yes" /> <meta name="mobile-web-app-capable" content="yes" />
<script type="text/javascript" src="/config.js"></script>
</head> </head>
<body> <body>

View File

@@ -18,9 +18,7 @@ import { getItem, setItem, removeItem } from 'utils/localStorage';
import history from 'utils/history'; import history from 'utils/history';
const API_URI = const API_URI =
process.env.NODE_ENV === 'development' process.env.NODE_ENV === 'development' ? 'http://localhost:4000/' : window.REACT_APP_API;
? 'http://localhost:4000/'
: 'https://wlan-graphql.zone3.lab.connectus.ai/';
const MOUNT_NODE = document.getElementById('root'); const MOUNT_NODE = document.getElementById('root');
const cache = new InMemoryCache(); const cache = new InMemoryCache();

3
docker_entrypoint.sh Normal file
View File

@@ -0,0 +1,3 @@
#!/bin/sh -eu
./generate_config_js.sh >/usr/share/nginx/html/config.js
nginx -g "daemon off;"

10
generate_config_js.sh Normal file
View File

@@ -0,0 +1,10 @@
#!/bin/sh -eu
if [ -z "${API:-}" ]; then
API_URL_JSON=undefined
else
API_URL_JSON=$(jq -n --arg api "$API" '$api')
fi
cat <<EOF
window.REACT_APP_API = $API_URL_JSON;
EOF

8
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "wlan-cloud-ui", "name": "wlan-cloud-ui",
"version": "0.2.3", "version": "0.2.7",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -1874,9 +1874,9 @@
} }
}, },
"@tip-wlan/wlan-cloud-ui-library": { "@tip-wlan/wlan-cloud-ui-library": {
"version": "0.2.3", "version": "0.2.7",
"resolved": "https://tip.jfrog.io/artifactory/api/npm/tip-wlan-cloud-npm-repo/@tip-wlan/wlan-cloud-ui-library/-/@tip-wlan/wlan-cloud-ui-library-0.2.3.tgz", "resolved": "https://tip.jfrog.io/artifactory/api/npm/tip-wlan-cloud-npm-repo/@tip-wlan/wlan-cloud-ui-library/-/@tip-wlan/wlan-cloud-ui-library-0.2.7.tgz",
"integrity": "sha1-m/aF8DLiaeGF8UDtECAabipk2P0=" "integrity": "sha1-GGCapOJxcgD36ml6ZU2j1AlzXio="
}, },
"@types/anymatch": { "@types/anymatch": {
"version": "1.3.1", "version": "1.3.1",

View File

@@ -1,6 +1,6 @@
{ {
"name": "wlan-cloud-ui", "name": "wlan-cloud-ui",
"version": "0.2.3", "version": "0.2.7",
"author": "ConnectUs", "author": "ConnectUs",
"description": "React Portal", "description": "React Portal",
"engines": { "engines": {
@@ -20,7 +20,7 @@
"@ant-design/icons": "^4.2.1", "@ant-design/icons": "^4.2.1",
"@apollo/react-hoc": "^3.1.4", "@apollo/react-hoc": "^3.1.4",
"@apollo/react-hooks": "^3.1.3", "@apollo/react-hooks": "^3.1.3",
"@tip-wlan/wlan-cloud-ui-library": "^0.2.3", "@tip-wlan/wlan-cloud-ui-library": "^0.2.7",
"antd": "^4.3.1", "antd": "^4.3.1",
"apollo-cache-inmemory": "^1.6.6", "apollo-cache-inmemory": "^1.6.6",
"apollo-client": "^2.6.10", "apollo-client": "^2.6.10",