diff --git a/package-lock.json b/package-lock.json
index 85d05e5..f459794 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "ucentral-client",
- "version": "3.2.0",
+ "version": "4.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ucentral-client",
- "version": "3.2.0",
+ "version": "4.1.0",
"license": "ISC",
"dependencies": {
"@chakra-ui/anatomy": "^2.1.1",
@@ -3540,7 +3540,7 @@
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.0",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -3548,7 +3548,7 @@
},
"node_modules/@jridgewell/set-array": {
"version": "1.1.2",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -3556,7 +3556,7 @@
},
"node_modules/@jridgewell/source-map": {
"version": "0.3.2",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.0",
@@ -3565,7 +3565,7 @@
},
"node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": {
"version": "0.3.2",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@jridgewell/set-array": "^1.0.1",
@@ -3578,12 +3578,12 @@
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.14",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.17",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "3.1.0",
@@ -4374,7 +4374,7 @@
"version": "18.15.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
- "dev": true
+ "devOptional": true
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
@@ -4419,7 +4419,7 @@
"version": "18.0.11",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz",
"integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@types/react": "*"
}
@@ -4753,7 +4753,7 @@
},
"node_modules/acorn": {
"version": "8.8.0",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -5174,7 +5174,7 @@
},
"node_modules/buffer-from": {
"version": "1.1.2",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/builtin-modules": {
@@ -9781,7 +9781,7 @@
},
"node_modules/source-map-support": {
"version": "0.5.21",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
@@ -9790,7 +9790,7 @@
},
"node_modules/source-map-support/node_modules/source-map": {
"version": "0.6.1",
- "dev": true,
+ "devOptional": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -10080,7 +10080,7 @@
},
"node_modules/terser": {
"version": "5.15.1",
- "dev": true,
+ "devOptional": true,
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.2",
@@ -10097,7 +10097,7 @@
},
"node_modules/terser/node_modules/commander": {
"version": "2.20.3",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/text-table": {
diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json
index 49b02d9..ec7faac 100644
--- a/public/locales/en/translation.json
+++ b/public/locales/en/translation.json
@@ -514,6 +514,8 @@
"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}}",
"update_success": "Device updated!",
"updated_blacklist": "Updated Blacklist!"
},
diff --git a/src/components/Buttons/DeviceActionDropdown/index.tsx b/src/components/Buttons/DeviceActionDropdown/index.tsx
index d34eb40..487281c 100644
--- a/src/components/Buttons/DeviceActionDropdown/index.tsx
+++ b/src/components/Buttons/DeviceActionDropdown/index.tsx
@@ -32,6 +32,7 @@ interface Props {
onOpenTelemetryModal: (serialNumber: string) => void;
onOpenScriptModal: (device: GatewayDevice) => void;
onOpenRebootModal: (serialNumber: string) => void;
+ onReEnroll?: () => void;
size?: 'sm' | 'md' | 'lg';
isCompact?: boolean;
}
@@ -49,6 +50,7 @@ const DeviceActionDropdown = ({
onOpenConfigureModal,
onOpenScriptModal,
onOpenRebootModal,
+ onReEnroll,
size,
isCompact,
}: Props) => {
@@ -234,6 +236,7 @@ const DeviceActionDropdown = ({
+ {onReEnroll && }
diff --git a/src/hooks/Network/ReEnroll.ts b/src/hooks/Network/ReEnroll.ts
new file mode 100644
index 0000000..a7d46a3
--- /dev/null
+++ b/src/hooks/Network/ReEnroll.ts
@@ -0,0 +1,78 @@
+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(`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',
+ });
+ },
+ });
+};
diff --git a/src/pages/Device/Wrapper.tsx b/src/pages/Device/Wrapper.tsx
index 6be4097..b5e9f68 100644
--- a/src/pages/Device/Wrapper.tsx
+++ b/src/pages/Device/Wrapper.tsx
@@ -48,6 +48,7 @@ import { TelemetryModal } from 'components/Modals/TelemetryModal';
import { TraceModal } from 'components/Modals/TraceModal';
import { WifiScanModal } from 'components/Modals/WifiScanModal';
import { useDeleteDevice, useGetDevice, useGetDeviceHealthChecks, useGetDeviceStatus } from 'hooks/Network/Devices';
+import { useReEnroll } from 'hooks/Network/ReEnroll';
import SwitchPortExamination from './SwitchPortExamination';
type Props = {
@@ -77,6 +78,7 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
const traceModalProps = useDisclosure();
const rebootModalProps = useDisclosure();
const scriptModal = useScriptModal();
+ const reEnroll = useReEnroll({ serialNumber });
// Sticky-top styles
const isCompact = breakpoint === 'base' || breakpoint === 'sm' || breakpoint === 'md';
const boxShadow = useColorModeValue('0px 7px 23px rgba(0, 0, 0, 0.05)', 'none');
@@ -216,6 +218,7 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
onOpenTelemetryModal={telemetryModalProps.onOpen}
onOpenScriptModal={scriptModal.openModal}
onOpenRebootModal={rebootModalProps.onOpen}
+ onReEnroll={() => reEnroll.mutate({ serialNumber, when: 0 })}
size="md"
isCompact
/>
@@ -268,6 +271,7 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
onOpenTelemetryModal={telemetryModalProps.onOpen}
onOpenRebootModal={rebootModalProps.onOpen}
onOpenScriptModal={scriptModal.openModal}
+ onReEnroll={() => reEnroll.mutate({ serialNumber, when: 0 })}
size="md"
/>
)}