mirror of
				https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui.git
				synced 2025-10-31 02:38:01 +00:00 
			
		
		
		
	Radio analysis table added
This commit is contained in:
		
							
								
								
									
										18
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										18
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1,12 +1,12 @@ | |||||||
| { | { | ||||||
|   "name": "ucentral-client", |   "name": "ucentral-client", | ||||||
|   "version": "0.9.22", |   "version": "0.9.23", | ||||||
|   "lockfileVersion": 2, |   "lockfileVersion": 2, | ||||||
|   "requires": true, |   "requires": true, | ||||||
|   "packages": { |   "packages": { | ||||||
|     "": { |     "": { | ||||||
|       "name": "ucentral-client", |       "name": "ucentral-client", | ||||||
|       "version": "0.9.22", |       "version": "0.9.23", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@coreui/coreui": "^3.4.0", |         "@coreui/coreui": "^3.4.0", | ||||||
|         "@coreui/icons": "^2.0.1", |         "@coreui/icons": "^2.0.1", | ||||||
| @@ -27,7 +27,7 @@ | |||||||
|         "react-router-dom": "^5.2.0", |         "react-router-dom": "^5.2.0", | ||||||
|         "react-widgets": "^5.1.1", |         "react-widgets": "^5.1.1", | ||||||
|         "sass": "^1.35.1", |         "sass": "^1.35.1", | ||||||
|         "ucentral-libs": "^0.8.19", |         "ucentral-libs": "^0.8.20", | ||||||
|         "uuid": "^8.3.2" |         "uuid": "^8.3.2" | ||||||
|       }, |       }, | ||||||
|       "devDependencies": { |       "devDependencies": { | ||||||
| @@ -14218,9 +14218,9 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/ucentral-libs": { |     "node_modules/ucentral-libs": { | ||||||
|       "version": "0.8.19", |       "version": "0.8.20", | ||||||
|       "resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.8.19.tgz", |       "resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.8.20.tgz", | ||||||
|       "integrity": "sha512-Ul4jemUHvOWlQPmGblQPe8zLd2g5O38zFpNG5GF0tSNc4Ckxgb+KKDwzmTDlJGZo298/BpJGZ0q5gYUbhWp3oQ==", |       "integrity": "sha512-/Ut/E3t9mqgK3Tds9zpfQSdD1sBssd/b4U4LLwD22HBCsAkQtc7VicZ6nJAbtoXmxYMD++64fz6Sa+xjgv/GWA==", | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=10" |         "node": ">=10" | ||||||
|       }, |       }, | ||||||
| @@ -26465,9 +26465,9 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "ucentral-libs": { |     "ucentral-libs": { | ||||||
|       "version": "0.8.19", |       "version": "0.8.20", | ||||||
|       "resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.8.19.tgz", |       "resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.8.20.tgz", | ||||||
|       "integrity": "sha512-Ul4jemUHvOWlQPmGblQPe8zLd2g5O38zFpNG5GF0tSNc4Ckxgb+KKDwzmTDlJGZo298/BpJGZ0q5gYUbhWp3oQ==", |       "integrity": "sha512-/Ut/E3t9mqgK3Tds9zpfQSdD1sBssd/b4U4LLwD22HBCsAkQtc7VicZ6nJAbtoXmxYMD++64fz6Sa+xjgv/GWA==", | ||||||
|       "requires": {} |       "requires": {} | ||||||
|     }, |     }, | ||||||
|     "unbox-primitive": { |     "unbox-primitive": { | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "ucentral-client", |   "name": "ucentral-client", | ||||||
|   "version": "0.9.22", |   "version": "0.9.23", | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@coreui/coreui": "^3.4.0", |     "@coreui/coreui": "^3.4.0", | ||||||
|     "@coreui/icons": "^2.0.1", |     "@coreui/icons": "^2.0.1", | ||||||
| @@ -21,7 +21,7 @@ | |||||||
|     "react-router-dom": "^5.2.0", |     "react-router-dom": "^5.2.0", | ||||||
|     "react-widgets": "^5.1.1", |     "react-widgets": "^5.1.1", | ||||||
|     "sass": "^1.35.1", |     "sass": "^1.35.1", | ||||||
|     "ucentral-libs": "^0.8.19", |     "ucentral-libs": "^0.8.20", | ||||||
|     "uuid": "^8.3.2" |     "uuid": "^8.3.2" | ||||||
|   }, |   }, | ||||||
|   "main": "index.js", |   "main": "index.js", | ||||||
|   | |||||||
| @@ -104,7 +104,7 @@ | |||||||
| 		"submit": "Absenden", | 		"submit": "Absenden", | ||||||
| 		"submitted": "Eingereicht", | 		"submitted": "Eingereicht", | ||||||
| 		"success": "Erfolg", | 		"success": "Erfolg", | ||||||
| 		"timestamp": "Zeitstempel", | 		"timestamp": "Zeit", | ||||||
| 		"to": "zu", | 		"to": "zu", | ||||||
| 		"unknown": "unbekannte", | 		"unknown": "unbekannte", | ||||||
| 		"uuid": "UUID", | 		"uuid": "UUID", | ||||||
| @@ -290,7 +290,9 @@ | |||||||
| 		"validated": "Bestätigt" | 		"validated": "Bestätigt" | ||||||
| 	}, | 	}, | ||||||
| 	"wifi_analysis": { | 	"wifi_analysis": { | ||||||
|  | 		"associations": "Verbände", | ||||||
| 		"mode": "Modus", | 		"mode": "Modus", | ||||||
|  | 		"radios": "Radios", | ||||||
| 		"title": "WLAN-Analyse" | 		"title": "WLAN-Analyse" | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -104,7 +104,7 @@ | |||||||
| 		"submit": "Submit", | 		"submit": "Submit", | ||||||
| 		"submitted": "Submitted", | 		"submitted": "Submitted", | ||||||
| 		"success": "Success", | 		"success": "Success", | ||||||
| 		"timestamp": "Timestamp", | 		"timestamp": "Time", | ||||||
| 		"to": "To", | 		"to": "To", | ||||||
| 		"unknown": "Unknown", | 		"unknown": "Unknown", | ||||||
| 		"uuid": "UUID", | 		"uuid": "UUID", | ||||||
| @@ -290,7 +290,9 @@ | |||||||
| 		"validated": "Validated" | 		"validated": "Validated" | ||||||
| 	}, | 	}, | ||||||
| 	"wifi_analysis": { | 	"wifi_analysis": { | ||||||
|  | 		"associations": "Associations", | ||||||
| 		"mode": "Mode", | 		"mode": "Mode", | ||||||
|  | 		"radios": "Radios", | ||||||
| 		"title": "WiFi Analysis" | 		"title": "WiFi Analysis" | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -104,7 +104,7 @@ | |||||||
| 		"submit": "Enviar", | 		"submit": "Enviar", | ||||||
| 		"submitted": "Presentado", | 		"submitted": "Presentado", | ||||||
| 		"success": "Éxito", | 		"success": "Éxito", | ||||||
| 		"timestamp": "marca de tiempo", | 		"timestamp": "hora", | ||||||
| 		"to": "a", | 		"to": "a", | ||||||
| 		"unknown": "Desconocido", | 		"unknown": "Desconocido", | ||||||
| 		"uuid": "UUID", | 		"uuid": "UUID", | ||||||
| @@ -290,7 +290,9 @@ | |||||||
| 		"validated": "Validado" | 		"validated": "Validado" | ||||||
| 	}, | 	}, | ||||||
| 	"wifi_analysis": { | 	"wifi_analysis": { | ||||||
|  | 		"associations": "Asociaciones", | ||||||
| 		"mode": "Modo", | 		"mode": "Modo", | ||||||
|  | 		"radios": "Radios", | ||||||
| 		"title": "Análisis WiFi" | 		"title": "Análisis WiFi" | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -104,7 +104,7 @@ | |||||||
| 		"submit": "Soumettre", | 		"submit": "Soumettre", | ||||||
| 		"submitted": "Soumis", | 		"submitted": "Soumis", | ||||||
| 		"success": "Succès", | 		"success": "Succès", | ||||||
| 		"timestamp": "horodatage", | 		"timestamp": "Temps", | ||||||
| 		"to": "à", | 		"to": "à", | ||||||
| 		"unknown": "Inconnu", | 		"unknown": "Inconnu", | ||||||
| 		"uuid": "UUID", | 		"uuid": "UUID", | ||||||
| @@ -290,7 +290,9 @@ | |||||||
| 		"validated": "Validé" | 		"validated": "Validé" | ||||||
| 	}, | 	}, | ||||||
| 	"wifi_analysis": { | 	"wifi_analysis": { | ||||||
|  | 		"associations": "Les associations", | ||||||
| 		"mode": "Mode", | 		"mode": "Mode", | ||||||
|  | 		"radios": "Radios", | ||||||
| 		"title": "Analyse Wi-Fi" | 		"title": "Analyse Wi-Fi" | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -104,7 +104,7 @@ | |||||||
| 		"submit": "Enviar", | 		"submit": "Enviar", | ||||||
| 		"submitted": "Submetido", | 		"submitted": "Submetido", | ||||||
| 		"success": "Sucesso", | 		"success": "Sucesso", | ||||||
| 		"timestamp": "timestamp", | 		"timestamp": "tempo", | ||||||
| 		"to": "Para", | 		"to": "Para", | ||||||
| 		"unknown": "Desconhecido", | 		"unknown": "Desconhecido", | ||||||
| 		"uuid": "UUID", | 		"uuid": "UUID", | ||||||
| @@ -290,7 +290,9 @@ | |||||||
| 		"validated": "Validado" | 		"validated": "Validado" | ||||||
| 	}, | 	}, | ||||||
| 	"wifi_analysis": { | 	"wifi_analysis": { | ||||||
|  | 		"associations": "Associações", | ||||||
| 		"mode": "Modo", | 		"mode": "Modo", | ||||||
|  | 		"radios": "Rádios", | ||||||
| 		"title": "Análise WiFi" | 		"title": "Análise WiFi" | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -50,7 +50,7 @@ const TheLayout = () => { | |||||||
|         <div className="c-body"> |         <div className="c-body"> | ||||||
|           <PageContainer t={t} routes={routes} redirectTo="/devices" /> |           <PageContainer t={t} routes={routes} redirectTo="/devices" /> | ||||||
|         </div> |         </div> | ||||||
|         <Footer t={t} version="0.9.22" /> |         <Footer t={t} version="0.9.23" /> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -1,32 +1,35 @@ | |||||||
| import React, { useState, useEffect } from 'react'; | import React, { useState, useEffect } from 'react'; | ||||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||||
| import { useParams } from 'react-router-dom'; | import { useParams } from 'react-router-dom'; | ||||||
| import { WifiAnalysisTable } from 'ucentral-libs'; | import { WifiAnalysisTable, RadioAnalysisTable } from 'ucentral-libs'; | ||||||
| import { useAuth } from 'contexts/AuthProvider'; | import { useAuth } from 'contexts/AuthProvider'; | ||||||
| import axiosInstance from 'utils/axiosInstance'; | import axiosInstance from 'utils/axiosInstance'; | ||||||
| import { cleanBytesString, prettyDate } from 'utils/helper'; | import { cleanBytesString, prettyDate, secondsToDetailed } from 'utils/helper'; | ||||||
| import { CCard, CCardBody } from '@coreui/react'; | import { CCard, CCardBody, CCardHeader, CCol, CRow } from '@coreui/react'; | ||||||
|  |  | ||||||
| const WifiAnalysisPage = () => { | const WifiAnalysisPage = () => { | ||||||
|   const { t } = useTranslation(); |   const { t } = useTranslation(); | ||||||
|   const { deviceId } = useParams(); |   const { deviceId } = useParams(); | ||||||
|   const { currentToken, endpoints } = useAuth(); |   const { currentToken, endpoints } = useAuth(); | ||||||
|   const [loading, setLoading] = useState(false); |   const [loading, setLoading] = useState(false); | ||||||
|   const [parsedStats, setParsedStats] = useState([]); |   const [tableTime, setTableTime] = useState(''); | ||||||
|   const [selectedStats, setSelectedStats] = useState([]); |   const [parsedAssociationStats, setParsedAssociationStats] = useState([]); | ||||||
|  |   const [selectedAssociationStats, setSelectedAssociationStats] = useState([]); | ||||||
|  |   const [parsedRadioStats, setParsedRadioStats] = useState([]); | ||||||
|  |   const [selectedRadioStats, setSelectedRadioStats] = useState([]); | ||||||
|   const [range, setRange] = useState(19); |   const [range, setRange] = useState(19); | ||||||
|  |  | ||||||
|   const extractIp = (json, bssid) => { |   const extractIp = (json, bssid) => { | ||||||
|     const ips = { |     const ips = { | ||||||
|       ipV4: '', |       ipV4: '-', | ||||||
|       ipV6: '', |       ipV6: '-', | ||||||
|     }; |     }; | ||||||
|     for (const obj of json.interfaces) { |     for (const obj of json.interfaces) { | ||||||
|       if ('clients' in obj) { |       if ('clients' in obj) { | ||||||
|         for (const client of obj.clients) { |         for (const client of obj.clients) { | ||||||
|           if (client.mac === bssid) { |           if (client.mac === bssid) { | ||||||
|             ips.ipV4 = client.ipv4_addresses ?? ''; |             ips.ipV4 = client.ipv4_addresses ?? '-'; | ||||||
|             ips.ipV6 = client.ipv6_addresses ?? ''; |             ips.ipV6 = client.ipv6_addresses ?? '-'; | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| @@ -34,14 +37,62 @@ const WifiAnalysisPage = () => { | |||||||
|     return ips; |     return ips; | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   const parseStats = (json) => { |   const parseAssociationStats = (json) => { | ||||||
|     const dbmNumber = 4294967295; |     const dbmNumber = 4294967295; | ||||||
|     const newParsedStats = []; |     const newParsedAssociationStats = []; | ||||||
|  |     const newParsedRadioStats = []; | ||||||
|  |  | ||||||
|     for (const stat of json.data) { |     for (const stat of json.data) { | ||||||
|       const associations = []; |       const associations = []; | ||||||
|  |       const radios = []; | ||||||
|       const timeStamp = prettyDate(stat.recorded); |       const timeStamp = prettyDate(stat.recorded); | ||||||
|  |  | ||||||
|  |       for (let i = 0; i < stat.data.radios.length; i += 1) { | ||||||
|  |         const radio = stat.data.radios[i]; | ||||||
|  |         radios.push({ | ||||||
|  |           timeStamp, | ||||||
|  |           radio: i, | ||||||
|  |           channel: radio.channel, | ||||||
|  |           channelWidth: radio.channel_width, | ||||||
|  |           noise: radio.noise ? (dbmNumber - radio.noise) * -1 : '-', | ||||||
|  |           txPower: radio.tx_power, | ||||||
|  |           activeMs: secondsToDetailed( | ||||||
|  |             radio?.active_ms ? Math.floor(radio.active_ms / 1000) : 0, | ||||||
|  |             t('common.day'), | ||||||
|  |             t('common.days'), | ||||||
|  |             t('common.hour'), | ||||||
|  |             t('common.hours'), | ||||||
|  |             t('common.minute'), | ||||||
|  |             t('common.minutes'), | ||||||
|  |             t('common.second'), | ||||||
|  |             t('common.seconds'), | ||||||
|  |           ), | ||||||
|  |           busyMs: secondsToDetailed( | ||||||
|  |             radio?.busy_ms ? Math.floor(radio.busy_ms / 1000) : 0, | ||||||
|  |             t('common.day'), | ||||||
|  |             t('common.days'), | ||||||
|  |             t('common.hour'), | ||||||
|  |             t('common.hours'), | ||||||
|  |             t('common.minute'), | ||||||
|  |             t('common.minutes'), | ||||||
|  |             t('common.second'), | ||||||
|  |             t('common.seconds'), | ||||||
|  |           ), | ||||||
|  |           receiveMs: secondsToDetailed( | ||||||
|  |             radio?.receive_ms ? Math.floor(radio.receive_ms / 1000) : 0, | ||||||
|  |             t('common.day'), | ||||||
|  |             t('common.days'), | ||||||
|  |             t('common.hour'), | ||||||
|  |             t('common.hours'), | ||||||
|  |             t('common.minute'), | ||||||
|  |             t('common.minutes'), | ||||||
|  |             t('common.second'), | ||||||
|  |             t('common.seconds'), | ||||||
|  |           ), | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |       newParsedRadioStats.push(radios); | ||||||
|  |  | ||||||
|       // Looping through the interfaces |       // Looping through the interfaces | ||||||
|       for (const deviceInterface of stat.data.interfaces) { |       for (const deviceInterface of stat.data.interfaces) { | ||||||
|         if ('counters' in deviceInterface && 'ssids' in deviceInterface) { |         if ('counters' in deviceInterface && 'ssids' in deviceInterface) { | ||||||
| @@ -61,13 +112,14 @@ const WifiAnalysisPage = () => { | |||||||
|                 const data = { |                 const data = { | ||||||
|                   ...radioInfo, |                   ...radioInfo, | ||||||
|                   ...extractIp(stat.data, association.bssid), |                   ...extractIp(stat.data, association.bssid), | ||||||
|  |                   bssid: association.bssid, | ||||||
|                   ssid: ssid.ssid, |                   ssid: ssid.ssid, | ||||||
|                   rssi: (dbmNumber - association.rssi) * -1, |                   rssi: (dbmNumber - association.rssi) * -1, | ||||||
|                   mode: ssid.mode, |                   mode: ssid.mode, | ||||||
|                   rxBytes: cleanBytesString(association.rx_bytes, 0), |                   rxBytes: cleanBytesString(association.rx_bytes, 0), | ||||||
|                   rxRate: association.rx_rate.bitrate, |                   rxRate: association.rx_rate.bitrate, | ||||||
|                   rxMcs: association.rx_rate.mcs ?? '', |                   rxMcs: association.rx_rate.mcs ?? '-', | ||||||
|                   rxNss: association.rx_rate.nss ?? '', |                   rxNss: association.rx_rate.nss ?? '-', | ||||||
|                   txBytes: cleanBytesString(association.tx_bytes, 0), |                   txBytes: cleanBytesString(association.tx_bytes, 0), | ||||||
|                   txMcs: association.tx_rate.mcs, |                   txMcs: association.tx_rate.mcs, | ||||||
|                   txNss: association.tx_rate.nss, |                   txNss: association.tx_rate.nss, | ||||||
| @@ -80,16 +132,21 @@ const WifiAnalysisPage = () => { | |||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|       newParsedStats.push(associations); |       newParsedAssociationStats.push(associations); | ||||||
|     } |     } | ||||||
|     const ascOrderedStats = newParsedStats.reverse(); |  | ||||||
|     setRange(ascOrderedStats.length - 1); |     const ascOrderedRadioStats = newParsedRadioStats.reverse(); | ||||||
|     setParsedStats(ascOrderedStats); |     setParsedRadioStats(ascOrderedRadioStats); | ||||||
|     setSelectedStats(ascOrderedStats[ascOrderedStats.length - 1]); |     setSelectedRadioStats(ascOrderedRadioStats[ascOrderedRadioStats.length - 1]); | ||||||
|  |     setRange(ascOrderedRadioStats.length - 1); | ||||||
|  |     setTableTime(ascOrderedRadioStats[ascOrderedRadioStats.length - 1][0].timeStamp); | ||||||
|  |     const ascOrderedAssociationStats = newParsedAssociationStats.reverse(); | ||||||
|  |     setParsedAssociationStats(ascOrderedAssociationStats); | ||||||
|  |     setSelectedAssociationStats(ascOrderedAssociationStats[ascOrderedAssociationStats.length - 1]); | ||||||
|     setLoading(false); |     setLoading(false); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   const getLatestStats = () => { |   const getLatestAssociationStats = () => { | ||||||
|     setLoading(true); |     setLoading(true); | ||||||
|     setRange(19); |     setRange(19); | ||||||
|  |  | ||||||
| @@ -106,33 +163,57 @@ const WifiAnalysisPage = () => { | |||||||
|         options, |         options, | ||||||
|       ) |       ) | ||||||
|       .then((response) => { |       .then((response) => { | ||||||
|         parseStats(response.data); |         parseAssociationStats(response.data); | ||||||
|       }) |       }) | ||||||
|       .catch(() => { |       .catch((e) => { | ||||||
|  |         console.log(e); | ||||||
|         setLoading(false); |         setLoading(false); | ||||||
|       }); |       }); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   const updateSelectedStats = (index) => { |   const updateSelectedStats = (index) => { | ||||||
|     setSelectedStats(parsedStats[index]); |     setTableTime(parsedRadioStats[index][0].timeStamp); | ||||||
|  |     setSelectedAssociationStats(parsedAssociationStats[index]); | ||||||
|  |     setSelectedRadioStats(parsedRadioStats[index]); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     if (deviceId && deviceId.length > 0) { |     if (deviceId && deviceId.length > 0) { | ||||||
|       getLatestStats(); |       getLatestAssociationStats(); | ||||||
|     } |     } | ||||||
|   }, [deviceId]); |   }, [deviceId]); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <div> |     <div> | ||||||
|       <CCard> |       <CCard> | ||||||
|  |         <CCardHeader> | ||||||
|  |           <CRow> | ||||||
|  |             <CCol className="text-center"> | ||||||
|  |               <input | ||||||
|  |                 type="range" | ||||||
|  |                 style={{ width: '80%' }} | ||||||
|  |                 className="form-range" | ||||||
|  |                 min="0" | ||||||
|  |                 max={range} | ||||||
|  |                 step="1" | ||||||
|  |                 onChange={(e) => updateSelectedStats(e.target.value)} | ||||||
|  |                 defaultValue={range} | ||||||
|  |               /> | ||||||
|  |               <h5> | ||||||
|  |                 {t('common.timestamp')}: {tableTime} | ||||||
|  |               </h5> | ||||||
|  |             </CCol> | ||||||
|  |           </CRow> | ||||||
|  |         </CCardHeader> | ||||||
|         <CCardBody> |         <CCardBody> | ||||||
|  |           <h5 className="pb-3 text-center">{t('wifi_analysis.radios')}</h5> | ||||||
|  |           <RadioAnalysisTable data={selectedRadioStats} loading={loading} range={range} /> | ||||||
|  |           <h5 className="pt-5 pb-3 text-center">{t('wifi_analysis.associations')}</h5> | ||||||
|           <WifiAnalysisTable |           <WifiAnalysisTable | ||||||
|             t={t} |             t={t} | ||||||
|             data={selectedStats} |             data={selectedAssociationStats} | ||||||
|             loading={loading} |             loading={loading} | ||||||
|             range={range} |             range={range} | ||||||
|             updateSelectedStats={updateSelectedStats} |  | ||||||
|           /> |           /> | ||||||
|         </CCardBody> |         </CCardBody> | ||||||
|       </CCard> |       </CCard> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 bourquecharles
					bourquecharles