mirror of
				https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui.git
				synced 2025-11-03 20:27:59 +00:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			v4.1.0-RC1
			...
			release/v3
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					f23abf0f4a | ||
| 
						 | 
					eccff13fb2 | 
							
								
								
									
										1
									
								
								.env
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.env
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
VITE_UCENTRALSEC_URL="https://ucentral.dpaas.arilia.com:16001"
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -20,7 +20,7 @@ defaults:
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  docker:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    runs-on: ubuntu-20.04
 | 
			
		||||
    env:
 | 
			
		||||
      DOCKER_REGISTRY_URL: tip-tip-wlan-cloud-ucentral.jfrog.io
 | 
			
		||||
      DOCKER_REGISTRY_USERNAME: ucentral
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -11,7 +11,7 @@ defaults:
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  helm-package:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    runs-on: ubuntu-20.04
 | 
			
		||||
    env:
 | 
			
		||||
      HELM_REPO_URL: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/
 | 
			
		||||
      HELM_REPO_USERNAME: ucentral
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ fullnameOverride: ""
 | 
			
		||||
images:
 | 
			
		||||
  owgwui:
 | 
			
		||||
    repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owgw-ui
 | 
			
		||||
    tag: v4.1.0-RC1
 | 
			
		||||
    tag: v3.1.0
 | 
			
		||||
    pullPolicy: Always
 | 
			
		||||
 | 
			
		||||
services:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -1,12 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "ucentral-client",
 | 
			
		||||
  "version": "4.1.0",
 | 
			
		||||
  "version": "3.1.0(5)",
 | 
			
		||||
  "lockfileVersion": 3,
 | 
			
		||||
  "requires": true,
 | 
			
		||||
  "packages": {
 | 
			
		||||
    "": {
 | 
			
		||||
      "name": "ucentral-client",
 | 
			
		||||
      "version": "4.1.0",
 | 
			
		||||
      "version": "3.1.0(5)",
 | 
			
		||||
      "license": "ISC",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@chakra-ui/anatomy": "^2.1.1",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "ucentral-client",
 | 
			
		||||
  "version": "4.1.0",
 | 
			
		||||
  "version": "3.1.0(5)",
 | 
			
		||||
  "description": "",
 | 
			
		||||
  "private": true,
 | 
			
		||||
  "main": "index.tsx",
 | 
			
		||||
 
 | 
			
		||||
@@ -514,10 +514,6 @@
 | 
			
		||||
			"started_upgrade": "{{serialNumber}} just shut down to start the upgrade!",
 | 
			
		||||
			"trace": "Trace",
 | 
			
		||||
			"trace_description": "Launch a remote trace of this device for either a specific duration or a number of packets",
 | 
			
		||||
      "re_enroll": "Re-enroll",
 | 
			
		||||
      "re_enroll_initiated": "Re-enrollment initiated for device {{serialNumber}}",
 | 
			
		||||
      "re_enroll_warning": "This will renew the operational certificate for device {{serialNumber}}.",
 | 
			
		||||
      "confirm_re_enroll": "Renew Certificate for {{serialNumber}}",
 | 
			
		||||
			"update_success": "Device updated!",
 | 
			
		||||
			"updated_blacklist": "Updated Blacklist!"
 | 
			
		||||
		},
 | 
			
		||||
@@ -626,7 +622,6 @@
 | 
			
		||||
		"all": "All",
 | 
			
		||||
		"associations": "Associations",
 | 
			
		||||
		"certificate_expires_in": "Certificate Expiry",
 | 
			
		||||
    "certificate_issuer": "Certificate Issuer",
 | 
			
		||||
		"certificate_expiry": "Cert. Expires In",
 | 
			
		||||
		"connected": "Connected",
 | 
			
		||||
		"crash_logs": "Crash Logs",
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,6 @@ interface Props {
 | 
			
		||||
  onOpenTelemetryModal: (serialNumber: string) => void;
 | 
			
		||||
  onOpenScriptModal: (device: GatewayDevice) => void;
 | 
			
		||||
  onOpenRebootModal: (serialNumber: string) => void;
 | 
			
		||||
  onOpenReEnrollModal?: (serialNumber: string) => void;
 | 
			
		||||
  size?: 'sm' | 'md' | 'lg';
 | 
			
		||||
  isCompact?: boolean;
 | 
			
		||||
}
 | 
			
		||||
@@ -50,7 +49,6 @@ const DeviceActionDropdown = ({
 | 
			
		||||
  onOpenConfigureModal,
 | 
			
		||||
  onOpenScriptModal,
 | 
			
		||||
  onOpenRebootModal,
 | 
			
		||||
  onOpenReEnrollModal,
 | 
			
		||||
  size,
 | 
			
		||||
  isCompact,
 | 
			
		||||
}: Props) => {
 | 
			
		||||
@@ -236,11 +234,6 @@ const DeviceActionDropdown = ({
 | 
			
		||||
            <MenuItem onClick={handleRebootClick} hidden={!isCompact}>
 | 
			
		||||
              {t('commands.reboot')}
 | 
			
		||||
            </MenuItem>
 | 
			
		||||
            {onOpenReEnrollModal && (
 | 
			
		||||
              <MenuItem onClick={() => onOpenReEnrollModal(device.serialNumber)}>
 | 
			
		||||
                {t('controller.devices.re_enroll')}
 | 
			
		||||
              </MenuItem>
 | 
			
		||||
            )}
 | 
			
		||||
            <MenuItem onClick={handleOpenTelemetry}>{t('controller.telemetry.title')}</MenuItem>
 | 
			
		||||
            <MenuItem onClick={handleOpenScript}>{t('script.one')}</MenuItem>
 | 
			
		||||
            <MenuItem onClick={handleOpenTrace}>{t('controller.devices.trace')}</MenuItem>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,257 +0,0 @@
 | 
			
		||||
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>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
@@ -1,50 +0,0 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { Center, Spinner, Alert, Button } from '@chakra-ui/react';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import { Modal } from '../Modal';
 | 
			
		||||
import { useReEnroll } from 'hooks/Network/ReEnroll';
 | 
			
		||||
import { ModalProps } from 'models/Modal';
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  modalProps: ModalProps;
 | 
			
		||||
  serialNumber: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ReEnrollModal = ({ modalProps: { isOpen, onClose }, serialNumber }: Props) => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const { mutate: reEnroll, isLoading } = useReEnroll({ serialNumber });
 | 
			
		||||
 | 
			
		||||
  const submit = () => {
 | 
			
		||||
    reEnroll(
 | 
			
		||||
      { serialNumber, when: 0 },
 | 
			
		||||
      {
 | 
			
		||||
        onSuccess: () => {
 | 
			
		||||
          onClose();
 | 
			
		||||
        },
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Modal isOpen={isOpen} onClose={onClose} title={t('controller.devices.re_enroll')}>
 | 
			
		||||
      {isLoading ? (
 | 
			
		||||
        <Center>
 | 
			
		||||
          <Spinner size="lg" />
 | 
			
		||||
        </Center>
 | 
			
		||||
      ) : (
 | 
			
		||||
        <>
 | 
			
		||||
          <Alert colorScheme="blue" mb={6}>
 | 
			
		||||
            {t('controller.devices.re_enroll_warning', { serialNumber })}
 | 
			
		||||
          </Alert>
 | 
			
		||||
          <Center mb={6}>
 | 
			
		||||
            <Button size="lg" colorScheme="blue" onClick={submit} fontWeight="bold">
 | 
			
		||||
              {t('controller.devices.confirm_re_enroll', { serialNumber })}
 | 
			
		||||
            </Button>
 | 
			
		||||
          </Center>
 | 
			
		||||
        </>
 | 
			
		||||
      )}
 | 
			
		||||
    </Modal>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default ReEnrollModal;
 | 
			
		||||
@@ -166,7 +166,6 @@ export type DeviceStatus = {
 | 
			
		||||
  connected: boolean;
 | 
			
		||||
  connectReason?: string;
 | 
			
		||||
  certificateExpiryDate: number;
 | 
			
		||||
  certificateIssuerName?: string;
 | 
			
		||||
  connectionCompletionTime: number;
 | 
			
		||||
  firmware: string;
 | 
			
		||||
  ipAddress: string;
 | 
			
		||||
@@ -378,40 +377,6 @@ 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 }) => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const toast = useToast();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,78 +0,0 @@
 | 
			
		||||
import { useToast } from '@chakra-ui/react';
 | 
			
		||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import { axiosGw } from 'constants/axiosInstances';
 | 
			
		||||
 | 
			
		||||
export type ReEnrollRequest = {
 | 
			
		||||
  serialNumber: string;
 | 
			
		||||
  when?: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type ReEnrollResponse = {
 | 
			
		||||
  UUID: string;
 | 
			
		||||
  command: 're-enroll' | 'reenroll';
 | 
			
		||||
  completed: number;
 | 
			
		||||
  custom: number;
 | 
			
		||||
  details: {
 | 
			
		||||
    serial: string;
 | 
			
		||||
    when: number;
 | 
			
		||||
  };
 | 
			
		||||
  errorCode: number;
 | 
			
		||||
  errorText: string;
 | 
			
		||||
  executed: number;
 | 
			
		||||
  executionTime: number;
 | 
			
		||||
  results: {
 | 
			
		||||
    serial: string;
 | 
			
		||||
    status: {
 | 
			
		||||
      error: number;
 | 
			
		||||
      resultCode: number;
 | 
			
		||||
      resultText: string;
 | 
			
		||||
      text: string;
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
  serialNumber: string;
 | 
			
		||||
  status: string;
 | 
			
		||||
  submitted: number;
 | 
			
		||||
  submittedBy: string;
 | 
			
		||||
  when: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const reEnrollDevice = async ({ serialNumber, when = 0 }: ReEnrollRequest) =>
 | 
			
		||||
  axiosGw.post<ReEnrollResponse>(`device/${serialNumber}/reenroll`, {
 | 
			
		||||
    serial: serialNumber,
 | 
			
		||||
    when,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
export const useReEnroll = ({ serialNumber }: { serialNumber: string }) => {
 | 
			
		||||
  const queryClient = useQueryClient();
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const toast = useToast();
 | 
			
		||||
 | 
			
		||||
  return useMutation(reEnrollDevice, {
 | 
			
		||||
    onSuccess: () => {
 | 
			
		||||
      queryClient.invalidateQueries(['commands', serialNumber]);
 | 
			
		||||
      queryClient.invalidateQueries(['device', serialNumber]);
 | 
			
		||||
      queryClient.invalidateQueries(['device-status', serialNumber]);
 | 
			
		||||
      toast({
 | 
			
		||||
        id: `re-enroll-success-${serialNumber}`,
 | 
			
		||||
        title: t('common.success'),
 | 
			
		||||
        description: t('controller.devices.re_enroll_initiated', { serialNumber }),
 | 
			
		||||
        status: 'success',
 | 
			
		||||
        duration: 5000,
 | 
			
		||||
        isClosable: true,
 | 
			
		||||
        position: 'top-right',
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    onError: (error: any) => {
 | 
			
		||||
      toast({
 | 
			
		||||
        id: `re-enroll-error-${serialNumber}`,
 | 
			
		||||
        title: t('common.error'),
 | 
			
		||||
        description: error?.response?.data?.ErrorDescription || t('common.error'),
 | 
			
		||||
        status: 'error',
 | 
			
		||||
        duration: 5000,
 | 
			
		||||
        isClosable: true,
 | 
			
		||||
        position: 'top-right',
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
@@ -171,12 +171,6 @@ const DeviceSummary = ({ serialNumber }: Props) => {
 | 
			
		||||
                '-'
 | 
			
		||||
              )}
 | 
			
		||||
            </GridItem>
 | 
			
		||||
            <GridItem colSpan={1} alignContent="center" alignItems="center">
 | 
			
		||||
              <Heading size="sm">{t('devices.certificate_issuer')}:</Heading>
 | 
			
		||||
            </GridItem>
 | 
			
		||||
            <GridItem colSpan={1}>
 | 
			
		||||
              {getStatus.data?.certificateIssuerName ? getStatus.data.certificateIssuerName.split('CN=')[1] : '-'}
 | 
			
		||||
            </GridItem>
 | 
			
		||||
            <GridItem colSpan={1} alignContent="center" alignItems="center">
 | 
			
		||||
              <Heading size="sm">Connect Reason:</Heading>
 | 
			
		||||
            </GridItem>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,17 @@
 | 
			
		||||
import * as React from 'react';
 | 
			
		||||
import { IconButton, Tooltip, useToast } from '@chakra-ui/react';
 | 
			
		||||
import { Power, PlugsConnected } from '@phosphor-icons/react';
 | 
			
		||||
import { Power } from '@phosphor-icons/react';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import { usePowerCycle } from 'hooks/Network/Devices';
 | 
			
		||||
import { useNotification } from 'hooks/useNotification';
 | 
			
		||||
import { DeviceLinkState } from 'hooks/Network/Statistics';
 | 
			
		||||
import { CableDiagnosticsModalProps } from 'components/Modals/CableDiagnosticsModal';
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
  state: DeviceLinkState & { name: string };
 | 
			
		||||
  deviceSerialNumber: string;
 | 
			
		||||
  onOpenCableDiagnostics: (port: string) => void;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const LinkStateTableActions = ({ state, deviceSerialNumber, onOpenCableDiagnostics }: Props) => {
 | 
			
		||||
const LinkStateTableActions = ({ state, deviceSerialNumber }: Props) => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const powerCycle = usePowerCycle();
 | 
			
		||||
  const toast = useToast();
 | 
			
		||||
@@ -56,7 +54,6 @@ const LinkStateTableActions = ({ state, deviceSerialNumber, onOpenCableDiagnosti
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
    <Tooltip label="Power Cycle" placement="auto-start">
 | 
			
		||||
      <IconButton
 | 
			
		||||
        aria-label="Power Cycle"
 | 
			
		||||
@@ -67,16 +64,6 @@ const LinkStateTableActions = ({ state, deviceSerialNumber, onOpenCableDiagnosti
 | 
			
		||||
        size="xs"
 | 
			
		||||
      />
 | 
			
		||||
    </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,12 +9,8 @@ import LinkStateTableActions from './Actions';
 | 
			
		||||
 | 
			
		||||
type Row = DeviceLinkState & { name: string };
 | 
			
		||||
const dataCell = (v: number) => <DataCell bytes={v} />;
 | 
			
		||||
const actionCell = (row: Row, serialNumber: string, onOpenCableDiagnostics: (port: string) => void) => (
 | 
			
		||||
  <LinkStateTableActions
 | 
			
		||||
    state={row}
 | 
			
		||||
    deviceSerialNumber={serialNumber}
 | 
			
		||||
    onOpenCableDiagnostics={onOpenCableDiagnostics}
 | 
			
		||||
  />
 | 
			
		||||
const actionCell = (row: Row, serialNumber: string) => (
 | 
			
		||||
  <LinkStateTableActions state={row} deviceSerialNumber={serialNumber} />
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
@@ -23,10 +19,9 @@ type Props = {
 | 
			
		||||
  isFetching: boolean;
 | 
			
		||||
  type: 'upstream' | 'downstream';
 | 
			
		||||
  serialNumber: string;
 | 
			
		||||
  onOpenCableDiagnostics: (port: string) => void;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const LinkStateTable = ({ statistics, refetch, isFetching, type, serialNumber, onOpenCableDiagnostics }: Props) => {
 | 
			
		||||
const LinkStateTable = ({ statistics, refetch, isFetching, type, serialNumber }: Props) => {
 | 
			
		||||
  const tableController = useDataGrid({
 | 
			
		||||
    tableSettingsId: 'switch.link-state.table',
 | 
			
		||||
    defaultOrder: [
 | 
			
		||||
@@ -162,16 +157,10 @@ const LinkStateTable = ({ statistics, refetch, isFetching, type, serialNumber, o
 | 
			
		||||
        id: 'actions',
 | 
			
		||||
        header: '',
 | 
			
		||||
        accessorKey: '',
 | 
			
		||||
        cell: ({ cell }) => (
 | 
			
		||||
          <LinkStateTableActions
 | 
			
		||||
            state={cell.row.original}
 | 
			
		||||
            deviceSerialNumber={serialNumber}
 | 
			
		||||
            onOpenCableDiagnostics={onOpenCableDiagnostics}
 | 
			
		||||
          />
 | 
			
		||||
        ),
 | 
			
		||||
        cell: ({ cell }) => actionCell(cell.row.original, serialNumber),
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    [onOpenCableDiagnostics],
 | 
			
		||||
    [],
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  if (!statistics || statistics?.length === 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,8 +5,6 @@ import SwitchInterfaceTable from './SwitchInterfaceTable';
 | 
			
		||||
import { DeviceLinkState, useGetDeviceLastStats } from 'hooks/Network/Statistics';
 | 
			
		||||
import { Card } from 'components/Containers/Card';
 | 
			
		||||
import { CardBody } from 'components/Containers/Card/CardBody';
 | 
			
		||||
import { CableDiagnosticsModal } from 'components/Modals/CableDiagnosticsModal';
 | 
			
		||||
import { useDisclosure } from '@chakra-ui/react';
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
  serialNumber: string;
 | 
			
		||||
@@ -14,8 +12,6 @@ type Props = {
 | 
			
		||||
 | 
			
		||||
const SwitchPortExamination = ({ serialNumber }: Props) => {
 | 
			
		||||
  const [tabIndex, setTabIndex] = React.useState(0);
 | 
			
		||||
  const [selectedPort, setSelectedPort] = React.useState<string>('');
 | 
			
		||||
  const cableDiagnosticsModalProps = useDisclosure();
 | 
			
		||||
 | 
			
		||||
  const handleTabsChange = React.useCallback((index: number) => {
 | 
			
		||||
    setTabIndex(index);
 | 
			
		||||
@@ -39,13 +35,7 @@ const SwitchPortExamination = ({ serialNumber }: Props) => {
 | 
			
		||||
    }));
 | 
			
		||||
  }, [getStats.data]);
 | 
			
		||||
 | 
			
		||||
  const handleOpenCableDiagnostics = React.useCallback((port: string) => {
 | 
			
		||||
    setSelectedPort(port);
 | 
			
		||||
    cableDiagnosticsModalProps.onOpen();
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
    <Card p={0} mb={4}>
 | 
			
		||||
      <CardBody p={0} display="block">
 | 
			
		||||
        <Tabs index={tabIndex} onChange={handleTabsChange} variant="enclosed" w="100%">
 | 
			
		||||
@@ -80,7 +70,6 @@ const SwitchPortExamination = ({ serialNumber }: Props) => {
 | 
			
		||||
                  isFetching={getStats.isFetching}
 | 
			
		||||
                  type="upstream"
 | 
			
		||||
                  serialNumber={serialNumber}
 | 
			
		||||
                    onOpenCableDiagnostics={handleOpenCableDiagnostics}
 | 
			
		||||
                />
 | 
			
		||||
              ) : (
 | 
			
		||||
                <Spinner size="xl" />
 | 
			
		||||
@@ -94,7 +83,6 @@ const SwitchPortExamination = ({ serialNumber }: Props) => {
 | 
			
		||||
                  isFetching={getStats.isFetching}
 | 
			
		||||
                  type="downstream"
 | 
			
		||||
                  serialNumber={serialNumber}
 | 
			
		||||
                    onOpenCableDiagnostics={handleOpenCableDiagnostics}
 | 
			
		||||
                />
 | 
			
		||||
              ) : (
 | 
			
		||||
                <Spinner size="xl" />
 | 
			
		||||
@@ -104,8 +92,6 @@ const SwitchPortExamination = ({ serialNumber }: Props) => {
 | 
			
		||||
        </Tabs>
 | 
			
		||||
      </CardBody>
 | 
			
		||||
    </Card>
 | 
			
		||||
      <CableDiagnosticsModal modalProps={cableDiagnosticsModalProps} serialNumber={serialNumber} port={selectedPort} />
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,6 @@ import { EventQueueModal } from 'components/Modals/EventQueueModal';
 | 
			
		||||
import FactoryResetModal from 'components/Modals/FactoryResetModal';
 | 
			
		||||
import { FirmwareUpgradeModal } from 'components/Modals/FirmwareUpgradeModal';
 | 
			
		||||
import { RebootModal } from 'components/Modals/RebootModal';
 | 
			
		||||
import ReEnrollModal from 'components/Modals/ReEnrollModal';
 | 
			
		||||
import { useScriptModal } from 'components/Modals/ScriptModal/useScriptModal';
 | 
			
		||||
import ethernetConnected from './ethernetIconConnected.svg?react';
 | 
			
		||||
import ethernetDisconnected from './ethernetIconDisconnected.svg?react';
 | 
			
		||||
@@ -69,7 +68,6 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
 | 
			
		||||
  const getHealth = useGetDeviceHealthChecks({ serialNumber, limit: 1 });
 | 
			
		||||
  const { isOpen: isDeleteOpen, onOpen: onDeleteOpen, onClose: onDeleteClose } = useDisclosure();
 | 
			
		||||
  const scanModalProps = useDisclosure();
 | 
			
		||||
  const cableDiagnosticsModalProps = useDisclosure();
 | 
			
		||||
  const resetModalProps = useDisclosure();
 | 
			
		||||
  const eventQueueProps = useDisclosure();
 | 
			
		||||
  const configureModalProps = useDisclosure();
 | 
			
		||||
@@ -77,7 +75,6 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
 | 
			
		||||
  const telemetryModalProps = useDisclosure();
 | 
			
		||||
  const traceModalProps = useDisclosure();
 | 
			
		||||
  const rebootModalProps = useDisclosure();
 | 
			
		||||
  const reEnrollModalProps = useDisclosure();
 | 
			
		||||
  const scriptModal = useScriptModal();
 | 
			
		||||
  // Sticky-top styles
 | 
			
		||||
  const isCompact = breakpoint === 'base' || breakpoint === 'sm' || breakpoint === 'md';
 | 
			
		||||
@@ -218,7 +215,6 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
 | 
			
		||||
                  onOpenTelemetryModal={telemetryModalProps.onOpen}
 | 
			
		||||
                  onOpenScriptModal={scriptModal.openModal}
 | 
			
		||||
                  onOpenRebootModal={rebootModalProps.onOpen}
 | 
			
		||||
                  onOpenReEnrollModal={reEnrollModalProps.onOpen}
 | 
			
		||||
                  size="md"
 | 
			
		||||
                  isCompact
 | 
			
		||||
                />
 | 
			
		||||
@@ -271,7 +267,6 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
 | 
			
		||||
                    onOpenTelemetryModal={telemetryModalProps.onOpen}
 | 
			
		||||
                    onOpenRebootModal={rebootModalProps.onOpen}
 | 
			
		||||
                    onOpenScriptModal={scriptModal.openModal}
 | 
			
		||||
                    onOpenReEnrollModal={reEnrollModalProps.onOpen}
 | 
			
		||||
                    size="md"
 | 
			
		||||
                  />
 | 
			
		||||
                )}
 | 
			
		||||
@@ -315,7 +310,6 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
 | 
			
		||||
      <ConfigureModal serialNumber={serialNumber} modalProps={configureModalProps} />
 | 
			
		||||
      <TelemetryModal serialNumber={serialNumber} modalProps={telemetryModalProps} />
 | 
			
		||||
      <RebootModal serialNumber={serialNumber} modalProps={rebootModalProps} />
 | 
			
		||||
      <ReEnrollModal serialNumber={serialNumber} modalProps={reEnrollModalProps} />
 | 
			
		||||
      {scriptModal.modal}
 | 
			
		||||
      <Box mt={isCompact ? '0px' : '68px'}>
 | 
			
		||||
        <Masonry
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user