mirror of
				https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui.git
				synced 2025-10-31 18:57:46 +00:00 
			
		
		
		
	Compare commits
	
		
			24 Commits
		
	
	
		
			release/v3
			...
			v4.0.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | a4743b6db5 | ||
|   | b3880f7e7e | ||
|   | 30fffdfe52 | ||
|   | c8d6540ca6 | ||
|   | 2b2f08c231 | ||
|   | 0cfed90a7b | ||
|   | 01008dc1aa | ||
|   | 26b90cfdba | ||
|   | b218051104 | ||
|   | a2fa93938f | ||
|   | c220d11dd0 | ||
|   | 40d533ecc5 | ||
|   | d1a1c96e74 | ||
|   | 1a18985c0d | ||
|   | 8eede7b559 | ||
|   | caab40b08e | ||
|   | 18fa320b19 | ||
|   | 6f9f6638d6 | ||
|   | 5688e2f7bc | ||
|   | 4738097178 | ||
|   | 591ecc3664 | ||
|   | b9089a39ac | ||
|   | b7bdf89d37 | ||
|   | 849ea9f7b2 | 
							
								
								
									
										2
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -20,7 +20,7 @@ defaults: | |||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   docker: |   docker: | ||||||
|     runs-on: ubuntu-20.04 |     runs-on: ubuntu-latest | ||||||
|     env: |     env: | ||||||
|       DOCKER_REGISTRY_URL: tip-tip-wlan-cloud-ucentral.jfrog.io |       DOCKER_REGISTRY_URL: tip-tip-wlan-cloud-ucentral.jfrog.io | ||||||
|       DOCKER_REGISTRY_USERNAME: ucentral |       DOCKER_REGISTRY_USERNAME: ucentral | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -11,7 +11,7 @@ defaults: | |||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   helm-package: |   helm-package: | ||||||
|     runs-on: ubuntu-20.04 |     runs-on: ubuntu-latest | ||||||
|     env: |     env: | ||||||
|       HELM_REPO_URL: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/ |       HELM_REPO_URL: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/ | ||||||
|       HELM_REPO_USERNAME: ucentral |       HELM_REPO_USERNAME: ucentral | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ fullnameOverride: "" | |||||||
| images: | images: | ||||||
|   owgwui: |   owgwui: | ||||||
|     repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owgw-ui |     repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owgw-ui | ||||||
|     tag: v3.0.2-RC1 |     tag: v4.0.0 | ||||||
|     pullPolicy: Always |     pullPolicy: Always | ||||||
|  |  | ||||||
| services: | services: | ||||||
|   | |||||||
							
								
								
									
										49
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										49
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1,12 +1,12 @@ | |||||||
| { | { | ||||||
|   "name": "ucentral-client", |   "name": "ucentral-client", | ||||||
|   "version": "3.0.2(9)", |   "version": "3.2.0", | ||||||
|   "lockfileVersion": 3, |   "lockfileVersion": 3, | ||||||
|   "requires": true, |   "requires": true, | ||||||
|   "packages": { |   "packages": { | ||||||
|     "": { |     "": { | ||||||
|       "name": "ucentral-client", |       "name": "ucentral-client", | ||||||
|       "version": "3.0.2(9)", |       "version": "3.2.0", | ||||||
|       "license": "ISC", |       "license": "ISC", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@chakra-ui/anatomy": "^2.1.1", |         "@chakra-ui/anatomy": "^2.1.1", | ||||||
| @@ -3540,7 +3540,7 @@ | |||||||
|     }, |     }, | ||||||
|     "node_modules/@jridgewell/resolve-uri": { |     "node_modules/@jridgewell/resolve-uri": { | ||||||
|       "version": "3.1.0", |       "version": "3.1.0", | ||||||
|       "devOptional": true, |       "dev": true, | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=6.0.0" |         "node": ">=6.0.0" | ||||||
| @@ -3548,7 +3548,7 @@ | |||||||
|     }, |     }, | ||||||
|     "node_modules/@jridgewell/set-array": { |     "node_modules/@jridgewell/set-array": { | ||||||
|       "version": "1.1.2", |       "version": "1.1.2", | ||||||
|       "devOptional": true, |       "dev": true, | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=6.0.0" |         "node": ">=6.0.0" | ||||||
| @@ -3556,7 +3556,7 @@ | |||||||
|     }, |     }, | ||||||
|     "node_modules/@jridgewell/source-map": { |     "node_modules/@jridgewell/source-map": { | ||||||
|       "version": "0.3.2", |       "version": "0.3.2", | ||||||
|       "devOptional": true, |       "dev": true, | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@jridgewell/gen-mapping": "^0.3.0", |         "@jridgewell/gen-mapping": "^0.3.0", | ||||||
| @@ -3565,7 +3565,7 @@ | |||||||
|     }, |     }, | ||||||
|     "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { |     "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { | ||||||
|       "version": "0.3.2", |       "version": "0.3.2", | ||||||
|       "devOptional": true, |       "dev": true, | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@jridgewell/set-array": "^1.0.1", |         "@jridgewell/set-array": "^1.0.1", | ||||||
| @@ -3578,12 +3578,12 @@ | |||||||
|     }, |     }, | ||||||
|     "node_modules/@jridgewell/sourcemap-codec": { |     "node_modules/@jridgewell/sourcemap-codec": { | ||||||
|       "version": "1.4.14", |       "version": "1.4.14", | ||||||
|       "devOptional": true, |       "dev": true, | ||||||
|       "license": "MIT" |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|     "node_modules/@jridgewell/trace-mapping": { |     "node_modules/@jridgewell/trace-mapping": { | ||||||
|       "version": "0.3.17", |       "version": "0.3.17", | ||||||
|       "devOptional": true, |       "dev": true, | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@jridgewell/resolve-uri": "3.1.0", |         "@jridgewell/resolve-uri": "3.1.0", | ||||||
| @@ -4374,7 +4374,7 @@ | |||||||
|       "version": "18.15.11", |       "version": "18.15.11", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", |       "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", | ||||||
|       "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", |       "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", | ||||||
|       "devOptional": true |       "dev": true | ||||||
|     }, |     }, | ||||||
|     "node_modules/@types/parse-json": { |     "node_modules/@types/parse-json": { | ||||||
|       "version": "4.0.0", |       "version": "4.0.0", | ||||||
| @@ -4419,7 +4419,7 @@ | |||||||
|       "version": "18.0.11", |       "version": "18.0.11", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz", |       "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz", | ||||||
|       "integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==", |       "integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==", | ||||||
|       "devOptional": true, |       "dev": true, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@types/react": "*" |         "@types/react": "*" | ||||||
|       } |       } | ||||||
| @@ -4753,7 +4753,7 @@ | |||||||
|     }, |     }, | ||||||
|     "node_modules/acorn": { |     "node_modules/acorn": { | ||||||
|       "version": "8.8.0", |       "version": "8.8.0", | ||||||
|       "devOptional": true, |       "dev": true, | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|       "bin": { |       "bin": { | ||||||
|         "acorn": "bin/acorn" |         "acorn": "bin/acorn" | ||||||
| @@ -5174,7 +5174,7 @@ | |||||||
|     }, |     }, | ||||||
|     "node_modules/buffer-from": { |     "node_modules/buffer-from": { | ||||||
|       "version": "1.1.2", |       "version": "1.1.2", | ||||||
|       "devOptional": true, |       "dev": true, | ||||||
|       "license": "MIT" |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|     "node_modules/builtin-modules": { |     "node_modules/builtin-modules": { | ||||||
| @@ -5671,8 +5671,9 @@ | |||||||
|       "license": "MIT" |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|     "node_modules/ejs": { |     "node_modules/ejs": { | ||||||
|       "version": "3.1.8", |       "version": "3.1.10", | ||||||
|       "license": "Apache-2.0", |       "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", | ||||||
|  |       "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "jake": "^10.8.5" |         "jake": "^10.8.5" | ||||||
|       }, |       }, | ||||||
| @@ -6682,9 +6683,9 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/follow-redirects": { |     "node_modules/follow-redirects": { | ||||||
|       "version": "1.15.4", |       "version": "1.15.6", | ||||||
|       "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", |       "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", | ||||||
|       "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", |       "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", | ||||||
|       "funding": [ |       "funding": [ | ||||||
|         { |         { | ||||||
|           "type": "individual", |           "type": "individual", | ||||||
| @@ -9780,7 +9781,7 @@ | |||||||
|     }, |     }, | ||||||
|     "node_modules/source-map-support": { |     "node_modules/source-map-support": { | ||||||
|       "version": "0.5.21", |       "version": "0.5.21", | ||||||
|       "devOptional": true, |       "dev": true, | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "buffer-from": "^1.0.0", |         "buffer-from": "^1.0.0", | ||||||
| @@ -9789,7 +9790,7 @@ | |||||||
|     }, |     }, | ||||||
|     "node_modules/source-map-support/node_modules/source-map": { |     "node_modules/source-map-support/node_modules/source-map": { | ||||||
|       "version": "0.6.1", |       "version": "0.6.1", | ||||||
|       "devOptional": true, |       "dev": true, | ||||||
|       "license": "BSD-3-Clause", |       "license": "BSD-3-Clause", | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=0.10.0" |         "node": ">=0.10.0" | ||||||
| @@ -10079,7 +10080,7 @@ | |||||||
|     }, |     }, | ||||||
|     "node_modules/terser": { |     "node_modules/terser": { | ||||||
|       "version": "5.15.1", |       "version": "5.15.1", | ||||||
|       "devOptional": true, |       "dev": true, | ||||||
|       "license": "BSD-2-Clause", |       "license": "BSD-2-Clause", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@jridgewell/source-map": "^0.3.2", |         "@jridgewell/source-map": "^0.3.2", | ||||||
| @@ -10096,7 +10097,7 @@ | |||||||
|     }, |     }, | ||||||
|     "node_modules/terser/node_modules/commander": { |     "node_modules/terser/node_modules/commander": { | ||||||
|       "version": "2.20.3", |       "version": "2.20.3", | ||||||
|       "devOptional": true, |       "dev": true, | ||||||
|       "license": "MIT" |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|     "node_modules/text-table": { |     "node_modules/text-table": { | ||||||
| @@ -10442,9 +10443,9 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/vite": { |     "node_modules/vite": { | ||||||
|       "version": "4.5.2", |       "version": "4.5.3", | ||||||
|       "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz", |       "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz", | ||||||
|       "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==", |       "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "esbuild": "^0.18.10", |         "esbuild": "^0.18.10", | ||||||
|         "postcss": "^8.4.27", |         "postcss": "^8.4.27", | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "ucentral-client", |   "name": "ucentral-client", | ||||||
|   "version": "3.0.2(9)", |   "version": "4.0.0", | ||||||
|   "description": "", |   "description": "", | ||||||
|   "private": true, |   "private": true, | ||||||
|   "main": "index.tsx", |   "main": "index.tsx", | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								public/devices/asterfusion_CX204Y-24GT-M-SWP4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/devices/asterfusion_CX204Y-24GT-M-SWP4.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 294 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/devices/asterfusion_CX204Y-48GT-M-SWP4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/devices/asterfusion_CX204Y-48GT-M-SWP4.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 394 KiB | 
| @@ -173,7 +173,7 @@ const DeviceActionDropdown = ({ | |||||||
|           isLoading={isRtty} |           isLoading={isRtty} | ||||||
|           onClick={handleConnectClick} |           onClick={handleConnectClick} | ||||||
|           colorScheme={connectColor} |           colorScheme={connectColor} | ||||||
|           hidden={isCompact || deviceType !== 'ap'} |           hidden={isCompact} | ||||||
|         /> |         /> | ||||||
|       </Tooltip> |       </Tooltip> | ||||||
|       <Tooltip label={t('controller.configure.title')}> |       <Tooltip label={t('controller.configure.title')}> | ||||||
|   | |||||||
							
								
								
									
										257
									
								
								src/components/Modals/CableDiagnosticsModal/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										257
									
								
								src/components/Modals/CableDiagnosticsModal/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,257 @@ | |||||||
|  | import React from 'react'; | ||||||
|  | import { | ||||||
|  |   Modal, | ||||||
|  |   Text, | ||||||
|  |   ModalOverlay, | ||||||
|  |   ModalContent, | ||||||
|  |   ModalBody, | ||||||
|  |   Center, | ||||||
|  |   Spinner, | ||||||
|  |   Checkbox, | ||||||
|  |   Stack, | ||||||
|  |   Table, | ||||||
|  |   Thead, | ||||||
|  |   Tbody, | ||||||
|  |   Tr, | ||||||
|  |   Th, | ||||||
|  |   Td, | ||||||
|  | } from '@chakra-ui/react'; | ||||||
|  | import { PlugsConnected } from '@phosphor-icons/react'; | ||||||
|  | import { useTranslation } from 'react-i18next'; | ||||||
|  | import { CloseButton } from 'components/Buttons/CloseButton'; | ||||||
|  | import { ResponsiveButton } from 'components/Buttons/ResponsiveButton'; | ||||||
|  | import { ModalHeader } from 'components/Containers/Modal/ModalHeader'; | ||||||
|  | import { useCableDiagnostics } from 'hooks/Network/Devices'; | ||||||
|  | import { ModalProps } from 'models/Modal'; | ||||||
|  | import Button from 'theme/components/button'; | ||||||
|  | import { DataGridColumn, useDataGrid } from 'components/DataTables/DataGrid/useDataGrid'; | ||||||
|  | import { DataGrid } from 'components/DataTables/DataGrid'; | ||||||
|  |  | ||||||
|  | export type CableDiagnosticsModalProps = { | ||||||
|  |   modalProps: ModalProps; | ||||||
|  |   serialNumber: string; | ||||||
|  |   port: string; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | type DiagnosticsRow = { | ||||||
|  |   port: string; | ||||||
|  |   linkStatus: string; | ||||||
|  |   pairA: string; | ||||||
|  |   pairB: string; | ||||||
|  |   pairC: string; | ||||||
|  |   pairD: string; | ||||||
|  |   type: string; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | type OpticalRow = { | ||||||
|  |   port: string; | ||||||
|  |   vendorName: string; | ||||||
|  |   formFactor: string; | ||||||
|  |   partNumber: string; | ||||||
|  |   serialNumber: string; | ||||||
|  |   temperature: string; | ||||||
|  |   txPower: string; | ||||||
|  |   rxPower: string; | ||||||
|  |   revision: string; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const CableDiagnosticsModal = ({ | ||||||
|  |   modalProps: { isOpen, onClose }, | ||||||
|  |   serialNumber, | ||||||
|  |   port, | ||||||
|  | }: CableDiagnosticsModalProps) => { | ||||||
|  |   const { t } = useTranslation(); | ||||||
|  |   const [selectedPorts, setSelectedPorts] = React.useState<string[]>([]); | ||||||
|  |   const [diagnosticsResult, setDiagnosticsResult] = React.useState<any>(null); | ||||||
|  |   const { mutateAsync: diagnose, isLoading } = useCableDiagnostics({ serialNumber }); | ||||||
|  |  | ||||||
|  |   const handlePortToggle = (port: string) => { | ||||||
|  |     setSelectedPorts((prev) => (prev.includes(port) ? prev.filter((p) => p !== port) : [...prev, port])); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   const handleDiagnose = async () => { | ||||||
|  |     if (port) { | ||||||
|  |       try { | ||||||
|  |         const result = await diagnose([port]); | ||||||
|  |         setDiagnosticsResult(result); | ||||||
|  |       } catch (error) { | ||||||
|  |         console.error('Error diagnosing cable:', error); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   const tableController = useDataGrid({ | ||||||
|  |     tableSettingsId: 'cable.diagnostics.table', | ||||||
|  |     defaultOrder: ['port', 'linkStatus', 'pairA', 'pairB', 'pairC', 'pairD', 'type'], | ||||||
|  |     showAllRows: true, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   const columns: DataGridColumn<DiagnosticsRow | OpticalRow>[] = React.useMemo(() => { | ||||||
|  |     const data = diagnosticsResult?.results?.status?.text?.[port]; | ||||||
|  |     const isOpticalData = data && 'form-factor' in data; | ||||||
|  |  | ||||||
|  |     return isOpticalData | ||||||
|  |       ? [ | ||||||
|  |           { | ||||||
|  |             id: 'vendorName', | ||||||
|  |             header: 'Vendor Name', | ||||||
|  |             accessorKey: 'vendorName', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             id: 'formFactor', | ||||||
|  |             header: 'Form Factor', | ||||||
|  |             accessorKey: 'formFactor', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             id: 'partNumber', | ||||||
|  |             header: 'Part Number', | ||||||
|  |             accessorKey: 'partNumber', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             id: 'serialNumber', | ||||||
|  |             header: 'Serial Number', | ||||||
|  |             accessorKey: 'serialNumber', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             id: 'temperature', | ||||||
|  |             header: 'Temperature', | ||||||
|  |             accessorKey: 'temperature', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             id: 'txPower', | ||||||
|  |             header: 'TX Power', | ||||||
|  |             accessorKey: 'txPower', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             id: 'rxPower', | ||||||
|  |             header: 'RX Power', | ||||||
|  |             accessorKey: 'rxPower', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             id: 'revision', | ||||||
|  |             header: 'Revision', | ||||||
|  |             accessorKey: 'revision', | ||||||
|  |           }, | ||||||
|  |         ] | ||||||
|  |       : [ | ||||||
|  |           { | ||||||
|  |             id: 'port', | ||||||
|  |             header: 'Port', | ||||||
|  |             accessorKey: 'port', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             id: 'linkStatus', | ||||||
|  |             header: 'Link Status', | ||||||
|  |             accessorKey: 'linkStatus', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             id: 'pairA', | ||||||
|  |             header: 'Pair A', | ||||||
|  |             accessorKey: 'pairA', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             id: 'pairB', | ||||||
|  |             header: 'Pair B', | ||||||
|  |             accessorKey: 'pairB', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             id: 'pairC', | ||||||
|  |             header: 'Pair C', | ||||||
|  |             accessorKey: 'pairC', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             id: 'pairD', | ||||||
|  |             header: 'Pair D', | ||||||
|  |             accessorKey: 'pairD', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             id: 'type', | ||||||
|  |             header: 'Type', | ||||||
|  |             accessorKey: 'type', | ||||||
|  |           }, | ||||||
|  |         ]; | ||||||
|  |   }, [diagnosticsResult]); | ||||||
|  |  | ||||||
|  |   const formatDiagnosticsData = (result: any): (DiagnosticsRow | OpticalRow)[] => { | ||||||
|  |     if (!result?.results?.status?.text?.[port]) return []; | ||||||
|  |  | ||||||
|  |     const data = result.results.status.text[port]; | ||||||
|  |  | ||||||
|  |     if (data['form-factor']) { | ||||||
|  |       return [ | ||||||
|  |         { | ||||||
|  |           port, | ||||||
|  |           vendorName: data['vendor-name'] || 'N/A', | ||||||
|  |           formFactor: data['form-factor'] || 'N/A', | ||||||
|  |           partNumber: data['part-number'] || 'N/A', | ||||||
|  |           serialNumber: data['serial-number'] || 'N/A', | ||||||
|  |           temperature: data.temperature ? `${data.temperature.toFixed(2)}` : 'N/A', | ||||||
|  |           txPower: data['tx-optical-power'] ? `${data['tx-optical-power']}` : 'N/A', | ||||||
|  |           rxPower: data['rx-optical-power'] ? `${data['rx-optical-power']}` : 'N/A', | ||||||
|  |           revision: data.revision || 'N/A', | ||||||
|  |         }, | ||||||
|  |       ]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return [ | ||||||
|  |       { | ||||||
|  |         port, | ||||||
|  |         linkStatus: data['link-status'], | ||||||
|  |         pairA: `${data['pair-A'].meters} (${data['pair-A'].status})`, | ||||||
|  |         pairB: `${data['pair-B'].meters} (${data['pair-B'].status})`, | ||||||
|  |         pairC: `${data['pair-C'].meters} (${data['pair-C'].status})`, | ||||||
|  |         pairD: `${data['pair-D'].meters} (${data['pair-D'].status})`, | ||||||
|  |         type: data.type, | ||||||
|  |       }, | ||||||
|  |     ]; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <Modal onClose={onClose} isOpen={isOpen} size="xl"> | ||||||
|  |       <ModalOverlay /> | ||||||
|  |       <ModalContent maxW="50vw"> | ||||||
|  |         <ModalHeader title={t('commands.cable_diagnostics')} right={<CloseButton onClick={onClose} />} /> | ||||||
|  |         <ModalBody pb={6}> | ||||||
|  |           {isLoading ? ( | ||||||
|  |             <Center my={4} flexDirection="column" gap={4}> | ||||||
|  |               <Spinner size="lg" /> | ||||||
|  |               <Text>Please wait...</Text> | ||||||
|  |               <Text fontSize="sm" color="gray.500"> | ||||||
|  |                 Please do not close this window. This may take a few seconds. | ||||||
|  |               </Text> | ||||||
|  |             </Center> | ||||||
|  |           ) : ( | ||||||
|  |             <Center flexDirection="column" gap={4}> | ||||||
|  |               <ResponsiveButton | ||||||
|  |                 color="blue" | ||||||
|  |                 icon={<PlugsConnected size={20} />} | ||||||
|  |                 label={`${ | ||||||
|  |                   diagnosticsResult && formatDiagnosticsData(diagnosticsResult).length > 0 ? 'Retake' : 'Start' | ||||||
|  |                 } Test for Port ${port}`} | ||||||
|  |                 onClick={handleDiagnose} | ||||||
|  |                 isLoading={isLoading} | ||||||
|  |                 isDisabled={!port} | ||||||
|  |                 isCompact={false} | ||||||
|  |               /> | ||||||
|  |               {diagnosticsResult && formatDiagnosticsData(diagnosticsResult).length > 0 && ( | ||||||
|  |                 <DataGrid<DiagnosticsRow | OpticalRow> | ||||||
|  |                   controller={tableController} | ||||||
|  |                   header={{ | ||||||
|  |                     title: '', | ||||||
|  |                     objectListed: 'Cable Diagnostics', | ||||||
|  |                   }} | ||||||
|  |                   columns={columns} | ||||||
|  |                   isLoading={isLoading} | ||||||
|  |                   data={formatDiagnosticsData(diagnosticsResult)} | ||||||
|  |                   options={{ | ||||||
|  |                     isHidingControls: true, | ||||||
|  |                   }} | ||||||
|  |                 /> | ||||||
|  |               )} | ||||||
|  |             </Center> | ||||||
|  |           )} | ||||||
|  |         </ModalBody> | ||||||
|  |       </ModalContent> | ||||||
|  |     </Modal> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
| @@ -25,6 +25,7 @@ import { useTranslation } from 'react-i18next'; | |||||||
| import { Modal } from '../Modal'; | import { Modal } from '../Modal'; | ||||||
| import { lowercaseFirstLetter } from 'helpers/stringHelper'; | import { lowercaseFirstLetter } from 'helpers/stringHelper'; | ||||||
| import { useTelemetry } from 'hooks/Network/Telemetry'; | import { useTelemetry } from 'hooks/Network/Telemetry'; | ||||||
|  | import { secondsDuration } from 'helpers/dateFormatting'; | ||||||
|  |  | ||||||
| export type TelemetryModalProps = { | export type TelemetryModalProps = { | ||||||
|   serialNumber: string; |   serialNumber: string; | ||||||
| @@ -146,8 +147,7 @@ const _TelemetryModal = ({ serialNumber, modalProps }: TelemetryModalProps) => { | |||||||
|               {t('controller.telemetry.interval')}: {form.interval} {lowercaseFirstLetter(t('common.seconds'))} |               {t('controller.telemetry.interval')}: {form.interval} {lowercaseFirstLetter(t('common.seconds'))} | ||||||
|             </p> |             </p> | ||||||
|             <p> |             <p> | ||||||
|               {t('controller.telemetry.duration')}: {form.interval}{' '} |               {t('controller.telemetry.duration')}: {secondsDuration(form.lifetime, t)} | ||||||
|               {lowercaseFirstLetter(t('controller.telemetry.minutes'))} |  | ||||||
|             </p> |             </p> | ||||||
|             <p> |             <p> | ||||||
|               {t('controller.telemetry.types')}: {form.types.join(', ')} |               {t('controller.telemetry.types')}: {form.types.join(', ')} | ||||||
|   | |||||||
| @@ -377,6 +377,40 @@ export const useWifiScanDevice = ({ serialNumber }: { serialNumber: string }) => | |||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | export const useCableDiagnostics = ({ serialNumber }: { serialNumber: string }) => { | ||||||
|  |   const toast = useToast(); | ||||||
|  |   const { t } = useTranslation(); | ||||||
|  |  | ||||||
|  |   return useMutation( | ||||||
|  |     (ports: string[]): Promise<unknown> => | ||||||
|  |       axiosGw | ||||||
|  |         .post(`device/${serialNumber}/cable-diagnostics`, { | ||||||
|  |           serial: serialNumber, | ||||||
|  |           ports, | ||||||
|  |           when: 0, | ||||||
|  |         }) | ||||||
|  |         .then(({ data }) => data), | ||||||
|  |     { | ||||||
|  |       onSuccess: (data) => { | ||||||
|  |         console.log('Success data: ', data); | ||||||
|  |       }, | ||||||
|  |       onError: (e: AxiosError) => { | ||||||
|  |         toast({ | ||||||
|  |           id: uuid(), | ||||||
|  |           title: t('common.error'), | ||||||
|  |           description: t('commands.cablediagnostics_error', { | ||||||
|  |             e: e?.response?.data?.ErrorDescription, | ||||||
|  |           }), | ||||||
|  |           status: 'error', | ||||||
|  |           duration: 5000, | ||||||
|  |           isClosable: true, | ||||||
|  |           position: 'top-right', | ||||||
|  |         }); | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
| export const useGetDeviceRtty = ({ serialNumber, extraId }: { serialNumber: string; extraId: string | number }) => { | export const useGetDeviceRtty = ({ serialNumber, extraId }: { serialNumber: string; extraId: string | number }) => { | ||||||
|   const { t } = useTranslation(); |   const { t } = useTranslation(); | ||||||
|   const toast = useToast(); |   const toast = useToast(); | ||||||
|   | |||||||
| @@ -59,6 +59,7 @@ export type DeviceInterfaceStatistics = { | |||||||
|       dynamic_vlan?: number; |       dynamic_vlan?: number; | ||||||
|       inactive: number; |       inactive: number; | ||||||
|       ipaddr_v4: string; |       ipaddr_v4: string; | ||||||
|  |       fingerprint?: object; | ||||||
|       rssi: number; |       rssi: number; | ||||||
|       rx_bytes: number; |       rx_bytes: number; | ||||||
|       rx_duration: number; |       rx_duration: number; | ||||||
|   | |||||||
| @@ -65,6 +65,8 @@ const DeviceSummary = ({ serialNumber }: Props) => { | |||||||
|   const getDeviceCompatible = () => { |   const getDeviceCompatible = () => { | ||||||
|     if (!getDevice.data?.compatible) return undefined; |     if (!getDevice.data?.compatible) return undefined; | ||||||
|  |  | ||||||
|  |     if (getDevice.data.compatible.includes(' ')) return getDevice.data.compatible.replaceAll(' ', '_'); | ||||||
|  |  | ||||||
|     return getDevice.data?.compatible; |     return getDevice.data?.compatible; | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,17 +1,19 @@ | |||||||
| import * as React from 'react'; | import * as React from 'react'; | ||||||
| import { IconButton, Tooltip, useToast } from '@chakra-ui/react'; | import { IconButton, Tooltip, useToast } from '@chakra-ui/react'; | ||||||
| import { Power } from '@phosphor-icons/react'; | import { Power, PlugsConnected } from '@phosphor-icons/react'; | ||||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||||
| import { usePowerCycle } from 'hooks/Network/Devices'; | import { usePowerCycle } from 'hooks/Network/Devices'; | ||||||
| import { useNotification } from 'hooks/useNotification'; | import { useNotification } from 'hooks/useNotification'; | ||||||
| import { DeviceLinkState } from 'hooks/Network/Statistics'; | import { DeviceLinkState } from 'hooks/Network/Statistics'; | ||||||
|  | import { CableDiagnosticsModalProps } from 'components/Modals/CableDiagnosticsModal'; | ||||||
|  |  | ||||||
| type Props = { | type Props = { | ||||||
|   state: DeviceLinkState & { name: string }; |   state: DeviceLinkState & { name: string }; | ||||||
|   deviceSerialNumber: string; |   deviceSerialNumber: string; | ||||||
|  |   onOpenCableDiagnostics: (port: string) => void; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const LinkStateTableActions = ({ state, deviceSerialNumber }: Props) => { | const LinkStateTableActions = ({ state, deviceSerialNumber, onOpenCableDiagnostics }: Props) => { | ||||||
|   const { t } = useTranslation(); |   const { t } = useTranslation(); | ||||||
|   const powerCycle = usePowerCycle(); |   const powerCycle = usePowerCycle(); | ||||||
|   const toast = useToast(); |   const toast = useToast(); | ||||||
| @@ -54,6 +56,7 @@ const LinkStateTableActions = ({ state, deviceSerialNumber }: Props) => { | |||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|  |     <> | ||||||
|       <Tooltip label="Power Cycle" placement="auto-start"> |       <Tooltip label="Power Cycle" placement="auto-start"> | ||||||
|         <IconButton |         <IconButton | ||||||
|           aria-label="Power Cycle" |           aria-label="Power Cycle" | ||||||
| @@ -64,6 +67,16 @@ const LinkStateTableActions = ({ state, deviceSerialNumber }: Props) => { | |||||||
|           size="xs" |           size="xs" | ||||||
|         /> |         /> | ||||||
|       </Tooltip> |       </Tooltip> | ||||||
|  |       <Tooltip label="Cable Diagnostics" placement="auto-start"> | ||||||
|  |         <IconButton | ||||||
|  |           aria-label="Cable Diagnostics" | ||||||
|  |           icon={<PlugsConnected size={20} />} | ||||||
|  |           colorScheme="blue" | ||||||
|  |           onClick={() => onOpenCableDiagnostics(state.name)} | ||||||
|  |           size="xs" | ||||||
|  |         /> | ||||||
|  |       </Tooltip> | ||||||
|  |     </> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,8 +9,12 @@ import LinkStateTableActions from './Actions'; | |||||||
|  |  | ||||||
| type Row = DeviceLinkState & { name: string }; | type Row = DeviceLinkState & { name: string }; | ||||||
| const dataCell = (v: number) => <DataCell bytes={v} />; | const dataCell = (v: number) => <DataCell bytes={v} />; | ||||||
| const actionCell = (row: Row, serialNumber: string) => ( | const actionCell = (row: Row, serialNumber: string, onOpenCableDiagnostics: (port: string) => void) => ( | ||||||
|   <LinkStateTableActions state={row} deviceSerialNumber={serialNumber} /> |   <LinkStateTableActions | ||||||
|  |     state={row} | ||||||
|  |     deviceSerialNumber={serialNumber} | ||||||
|  |     onOpenCableDiagnostics={onOpenCableDiagnostics} | ||||||
|  |   /> | ||||||
| ); | ); | ||||||
|  |  | ||||||
| type Props = { | type Props = { | ||||||
| @@ -19,9 +23,10 @@ type Props = { | |||||||
|   isFetching: boolean; |   isFetching: boolean; | ||||||
|   type: 'upstream' | 'downstream'; |   type: 'upstream' | 'downstream'; | ||||||
|   serialNumber: string; |   serialNumber: string; | ||||||
|  |   onOpenCableDiagnostics: (port: string) => void; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const LinkStateTable = ({ statistics, refetch, isFetching, type, serialNumber }: Props) => { | const LinkStateTable = ({ statistics, refetch, isFetching, type, serialNumber, onOpenCableDiagnostics }: Props) => { | ||||||
|   const tableController = useDataGrid({ |   const tableController = useDataGrid({ | ||||||
|     tableSettingsId: 'switch.link-state.table', |     tableSettingsId: 'switch.link-state.table', | ||||||
|     defaultOrder: [ |     defaultOrder: [ | ||||||
| @@ -157,10 +162,16 @@ const LinkStateTable = ({ statistics, refetch, isFetching, type, serialNumber }: | |||||||
|         id: 'actions', |         id: 'actions', | ||||||
|         header: '', |         header: '', | ||||||
|         accessorKey: '', |         accessorKey: '', | ||||||
|         cell: ({ cell }) => actionCell(cell.row.original, serialNumber), |         cell: ({ cell }) => ( | ||||||
|  |           <LinkStateTableActions | ||||||
|  |             state={cell.row.original} | ||||||
|  |             deviceSerialNumber={serialNumber} | ||||||
|  |             onOpenCableDiagnostics={onOpenCableDiagnostics} | ||||||
|  |           /> | ||||||
|  |         ), | ||||||
|       }, |       }, | ||||||
|     ], |     ], | ||||||
|     [], |     [onOpenCableDiagnostics], | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   if (!statistics || statistics?.length === 0) { |   if (!statistics || statistics?.length === 0) { | ||||||
|   | |||||||
| @@ -5,6 +5,8 @@ import SwitchInterfaceTable from './SwitchInterfaceTable'; | |||||||
| import { DeviceLinkState, useGetDeviceLastStats } from 'hooks/Network/Statistics'; | import { DeviceLinkState, useGetDeviceLastStats } from 'hooks/Network/Statistics'; | ||||||
| import { Card } from 'components/Containers/Card'; | import { Card } from 'components/Containers/Card'; | ||||||
| import { CardBody } from 'components/Containers/Card/CardBody'; | import { CardBody } from 'components/Containers/Card/CardBody'; | ||||||
|  | import { CableDiagnosticsModal } from 'components/Modals/CableDiagnosticsModal'; | ||||||
|  | import { useDisclosure } from '@chakra-ui/react'; | ||||||
|  |  | ||||||
| type Props = { | type Props = { | ||||||
|   serialNumber: string; |   serialNumber: string; | ||||||
| @@ -12,6 +14,8 @@ type Props = { | |||||||
|  |  | ||||||
| const SwitchPortExamination = ({ serialNumber }: Props) => { | const SwitchPortExamination = ({ serialNumber }: Props) => { | ||||||
|   const [tabIndex, setTabIndex] = React.useState(0); |   const [tabIndex, setTabIndex] = React.useState(0); | ||||||
|  |   const [selectedPort, setSelectedPort] = React.useState<string>(''); | ||||||
|  |   const cableDiagnosticsModalProps = useDisclosure(); | ||||||
|  |  | ||||||
|   const handleTabsChange = React.useCallback((index: number) => { |   const handleTabsChange = React.useCallback((index: number) => { | ||||||
|     setTabIndex(index); |     setTabIndex(index); | ||||||
| @@ -35,7 +39,13 @@ const SwitchPortExamination = ({ serialNumber }: Props) => { | |||||||
|     })); |     })); | ||||||
|   }, [getStats.data]); |   }, [getStats.data]); | ||||||
|  |  | ||||||
|  |   const handleOpenCableDiagnostics = React.useCallback((port: string) => { | ||||||
|  |     setSelectedPort(port); | ||||||
|  |     cableDiagnosticsModalProps.onOpen(); | ||||||
|  |   }, []); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|  |     <> | ||||||
|       <Card p={0} mb={4}> |       <Card p={0} mb={4}> | ||||||
|         <CardBody p={0} display="block"> |         <CardBody p={0} display="block"> | ||||||
|           <Tabs index={tabIndex} onChange={handleTabsChange} variant="enclosed" w="100%"> |           <Tabs index={tabIndex} onChange={handleTabsChange} variant="enclosed" w="100%"> | ||||||
| @@ -70,6 +80,7 @@ const SwitchPortExamination = ({ serialNumber }: Props) => { | |||||||
|                     isFetching={getStats.isFetching} |                     isFetching={getStats.isFetching} | ||||||
|                     type="upstream" |                     type="upstream" | ||||||
|                     serialNumber={serialNumber} |                     serialNumber={serialNumber} | ||||||
|  |                     onOpenCableDiagnostics={handleOpenCableDiagnostics} | ||||||
|                   /> |                   /> | ||||||
|                 ) : ( |                 ) : ( | ||||||
|                   <Spinner size="xl" /> |                   <Spinner size="xl" /> | ||||||
| @@ -83,6 +94,7 @@ const SwitchPortExamination = ({ serialNumber }: Props) => { | |||||||
|                     isFetching={getStats.isFetching} |                     isFetching={getStats.isFetching} | ||||||
|                     type="downstream" |                     type="downstream" | ||||||
|                     serialNumber={serialNumber} |                     serialNumber={serialNumber} | ||||||
|  |                     onOpenCableDiagnostics={handleOpenCableDiagnostics} | ||||||
|                   /> |                   /> | ||||||
|                 ) : ( |                 ) : ( | ||||||
|                   <Spinner size="xl" /> |                   <Spinner size="xl" /> | ||||||
| @@ -92,6 +104,8 @@ const SwitchPortExamination = ({ serialNumber }: Props) => { | |||||||
|           </Tabs> |           </Tabs> | ||||||
|         </CardBody> |         </CardBody> | ||||||
|       </Card> |       </Card> | ||||||
|  |       <CableDiagnosticsModal modalProps={cableDiagnosticsModalProps} serialNumber={serialNumber} port={selectedPort} /> | ||||||
|  |     </> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -28,6 +28,7 @@ export type ParsedAssociation = { | |||||||
|   txNss: number | string; |   txNss: number | string; | ||||||
|   recorded: number; |   recorded: number; | ||||||
|   dynamicVlan?: number; |   dynamicVlan?: number; | ||||||
|  |   fingerprint?: object; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| type Props = { | type Props = { | ||||||
| @@ -77,6 +78,14 @@ const WifiAnalysisAssociationsTable = ({ data, ouis, isSingle }: Props) => { | |||||||
|         isMonospace: true, |         isMonospace: true, | ||||||
|         alwaysShow: true, |         alwaysShow: true, | ||||||
|       }, |       }, | ||||||
|  |       { | ||||||
|  |         id: 'ssid', | ||||||
|  |         Header: 'SSID', | ||||||
|  |         Footer: '', | ||||||
|  |         accessor: 'ssid', | ||||||
|  |         customWidth: '35px', | ||||||
|  |         alwaysShow: true, | ||||||
|  |       }, | ||||||
|       { |       { | ||||||
|         id: 'ips', |         id: 'ips', | ||||||
|         Header: 'IPs', |         Header: 'IPs', | ||||||
| @@ -84,6 +93,13 @@ const WifiAnalysisAssociationsTable = ({ data, ouis, isSingle }: Props) => { | |||||||
|         Cell: (v) => ipCell(v.cell.row.original), |         Cell: (v) => ipCell(v.cell.row.original), | ||||||
|         disableSortBy: true, |         disableSortBy: true, | ||||||
|       }, |       }, | ||||||
|  |       { | ||||||
|  |         id: 'fingerprint', | ||||||
|  |         Header: 'Fingerprint', | ||||||
|  |         Footer: '', | ||||||
|  |         Cell: (v) => Object.values(v.cell.row.original.fingerprint ?? {}).join(', '), | ||||||
|  |         disableSortBy: true, | ||||||
|  |       }, | ||||||
|       { |       { | ||||||
|         id: 'vendor', |         id: 'vendor', | ||||||
|         Header: t('controller.wifi.vendor'), |         Header: t('controller.wifi.vendor'), | ||||||
|   | |||||||
| @@ -105,6 +105,7 @@ const parseAssociations = (data: { data: DeviceStatistics; recorded: number }, r | |||||||
|           txNss: association.tx_rate.nss ?? '-', |           txNss: association.tx_rate.nss ?? '-', | ||||||
|           recorded: data.recorded, |           recorded: data.recorded, | ||||||
|           dynamicVlan: association.dynamic_vlan, |           dynamicVlan: association.dynamic_vlan, | ||||||
|  |           fingerprint: association.fingerprint, | ||||||
|         }); |         }); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -68,6 +68,7 @@ const DevicePageWrapper = ({ serialNumber }: Props) => { | |||||||
|   const getHealth = useGetDeviceHealthChecks({ serialNumber, limit: 1 }); |   const getHealth = useGetDeviceHealthChecks({ serialNumber, limit: 1 }); | ||||||
|   const { isOpen: isDeleteOpen, onOpen: onDeleteOpen, onClose: onDeleteClose } = useDisclosure(); |   const { isOpen: isDeleteOpen, onOpen: onDeleteOpen, onClose: onDeleteClose } = useDisclosure(); | ||||||
|   const scanModalProps = useDisclosure(); |   const scanModalProps = useDisclosure(); | ||||||
|  |   const cableDiagnosticsModalProps = useDisclosure(); | ||||||
|   const resetModalProps = useDisclosure(); |   const resetModalProps = useDisclosure(); | ||||||
|   const eventQueueProps = useDisclosure(); |   const eventQueueProps = useDisclosure(); | ||||||
|   const configureModalProps = useDisclosure(); |   const configureModalProps = useDisclosure(); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user