Compare commits

...

13 Commits

Author SHA1 Message Date
Sebastian Rubina
c4aff418ed Merge pull request #233 from Telecominfraproject/WIFI-14521-set-correct-tag-for-main
Set correct tag for helm version
2025-08-05 13:06:11 -04:00
Carsten Schafer
dd5c894b03 Set correct tag for helm version
Signed-off-by: Carsten Schafer <Carsten.Schafer@kinarasystems.com>
2025-08-05 11:51:43 -04:00
Sebastian Rubina
c3256b93c7 Merge pull request #232 from Telecominfraproject/re-enroll-modal
Add device re-enrollment with confirmation modal
2025-07-14 15:50:59 -04:00
Sebastian Rubina
932f1f4a12 Change wording of translation.json.
Signed-off-by: Sebastian Rubina <sebastian.rubina@icloud.com>
2025-07-14 15:48:41 -04:00
Sebastian Rubina
db3cbb0b35 Add device re-enrollment with confirmation modal
- Add ReEnrollModal component for user confirmation before re-enrollment
  - Update DeviceActionDropdown to open modal instead of direct action
  - Add modal state management in Device Wrapper component
  - Add translation keys for re-enrollment UI with certificate renewal
  messaging
  - Remove direct useReEnroll hook usage in favor of modal pattern

Signed-off-by: Sebastian Rubina <sebastian.rubina@icloud.com>
2025-07-14 15:38:30 -04:00
Sebastian Rubina
c895274ebf Merge pull request #231 from Telecominfraproject/re-enroll-devices
Add device re-enrollment functionality
2025-07-14 13:31:58 -04:00
Sebastian Rubina
a3647bca08 Add device re-enrollment functionality
- Add re-enrollment API hook with mutation handling
  - Add re-enroll option to device action dropdown menu
  - Add translation keys for re-enrollment UI messages

Signed-off-by: Sebastian Rubina <sebastian.rubina@icloud.com>
2025-07-14 13:16:26 -04:00
Carsten Schafer
5fbf421d77 Merge pull request #230 from Telecominfraproject/display-certificate-issuer
Display certificate issuer
2025-07-02 13:49:24 -04:00
Carsten Schafer
e09b3ee5f4 Merge branch 'main' into display-certificate-issuer 2025-07-02 11:45:47 -04:00
Sebastian Rubina
855960559d Update package.json version 2025-07-02 11:33:03 -04:00
Sebastian Rubina
4cecfc6fc4 Display Certificate Issuer
- Add label on translation.json
- Add new key on DeviceStatus
- Add column label and data on Summary.txt

Signed-off-by: Sebastian Rubina <sebastian.rubina@icloud.com>
2025-07-02 09:49:49 -04:00
Sebastian Rubina
e62d1e4a98 Display Certificate Issuer
- Add label on translation.json
- Add new key on DeviceStatus
- Add column label and data on Summary.txt

Signed-off-by: Sebastian Rubina <sebastian.rubina@icloud.com>
2025-07-02 09:49:49 -04:00
Ivan Chvets
6dddba0848 fix: Version update - release 4.0.0
Signed-off-by: Ivan Chvets <ivan.chvets@kinarasystems.com>
Signed-off-by: Sebastian Rubina <sebastian.rubina@icloud.com>
2025-07-02 09:49:49 -04:00
10 changed files with 1400 additions and 1248 deletions

View File

@@ -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.2.0 tag: main
pullPolicy: Always pullPolicy: Always
services: services:

32
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "ucentral-client", "name": "ucentral-client",
"version": "3.2.0", "version": "4.1.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ucentral-client", "name": "ucentral-client",
"version": "3.2.0", "version": "4.1.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",
"dev": true, "devOptional": 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",
"dev": true, "devOptional": 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",
"dev": true, "devOptional": 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",
"dev": true, "devOptional": 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",
"dev": true, "devOptional": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@jridgewell/trace-mapping": { "node_modules/@jridgewell/trace-mapping": {
"version": "0.3.17", "version": "0.3.17",
"dev": true, "devOptional": 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==",
"dev": true "devOptional": 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==",
"dev": true, "devOptional": 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",
"dev": true, "devOptional": 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",
"dev": true, "devOptional": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/builtin-modules": { "node_modules/builtin-modules": {
@@ -9781,7 +9781,7 @@
}, },
"node_modules/source-map-support": { "node_modules/source-map-support": {
"version": "0.5.21", "version": "0.5.21",
"dev": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"buffer-from": "^1.0.0", "buffer-from": "^1.0.0",
@@ -9790,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",
"dev": true, "devOptional": true,
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@@ -10080,7 +10080,7 @@
}, },
"node_modules/terser": { "node_modules/terser": {
"version": "5.15.1", "version": "5.15.1",
"dev": true, "devOptional": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"@jridgewell/source-map": "^0.3.2", "@jridgewell/source-map": "^0.3.2",
@@ -10097,7 +10097,7 @@
}, },
"node_modules/terser/node_modules/commander": { "node_modules/terser/node_modules/commander": {
"version": "2.20.3", "version": "2.20.3",
"dev": true, "devOptional": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/text-table": { "node_modules/text-table": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "ucentral-client", "name": "ucentral-client",
"version": "4.0.0", "version": "4.1.0",
"description": "", "description": "",
"private": true, "private": true,
"main": "index.tsx", "main": "index.tsx",

File diff suppressed because it is too large Load Diff

View File

@@ -32,6 +32,7 @@ interface Props {
onOpenTelemetryModal: (serialNumber: string) => void; onOpenTelemetryModal: (serialNumber: string) => void;
onOpenScriptModal: (device: GatewayDevice) => void; onOpenScriptModal: (device: GatewayDevice) => void;
onOpenRebootModal: (serialNumber: string) => void; onOpenRebootModal: (serialNumber: string) => void;
onOpenReEnrollModal?: (serialNumber: string) => void;
size?: 'sm' | 'md' | 'lg'; size?: 'sm' | 'md' | 'lg';
isCompact?: boolean; isCompact?: boolean;
} }
@@ -49,6 +50,7 @@ const DeviceActionDropdown = ({
onOpenConfigureModal, onOpenConfigureModal,
onOpenScriptModal, onOpenScriptModal,
onOpenRebootModal, onOpenRebootModal,
onOpenReEnrollModal,
size, size,
isCompact, isCompact,
}: Props) => { }: Props) => {
@@ -234,6 +236,11 @@ const DeviceActionDropdown = ({
<MenuItem onClick={handleRebootClick} hidden={!isCompact}> <MenuItem onClick={handleRebootClick} hidden={!isCompact}>
{t('commands.reboot')} {t('commands.reboot')}
</MenuItem> </MenuItem>
{onOpenReEnrollModal && (
<MenuItem onClick={() => onOpenReEnrollModal(device.serialNumber)}>
{t('controller.devices.re_enroll')}
</MenuItem>
)}
<MenuItem onClick={handleOpenTelemetry}>{t('controller.telemetry.title')}</MenuItem> <MenuItem onClick={handleOpenTelemetry}>{t('controller.telemetry.title')}</MenuItem>
<MenuItem onClick={handleOpenScript}>{t('script.one')}</MenuItem> <MenuItem onClick={handleOpenScript}>{t('script.one')}</MenuItem>
<MenuItem onClick={handleOpenTrace}>{t('controller.devices.trace')}</MenuItem> <MenuItem onClick={handleOpenTrace}>{t('controller.devices.trace')}</MenuItem>

View File

@@ -0,0 +1,50 @@
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;

View File

@@ -166,6 +166,7 @@ export type DeviceStatus = {
connected: boolean; connected: boolean;
connectReason?: string; connectReason?: string;
certificateExpiryDate: number; certificateExpiryDate: number;
certificateIssuerName?: string;
connectionCompletionTime: number; connectionCompletionTime: number;
firmware: string; firmware: string;
ipAddress: string; ipAddress: string;

View File

@@ -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<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',
});
},
});
};

View File

@@ -171,6 +171,12 @@ const DeviceSummary = ({ serialNumber }: Props) => {
'-' '-'
)} )}
</GridItem> </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"> <GridItem colSpan={1} alignContent="center" alignItems="center">
<Heading size="sm">Connect Reason:</Heading> <Heading size="sm">Connect Reason:</Heading>
</GridItem> </GridItem>

View File

@@ -41,6 +41,7 @@ import { EventQueueModal } from 'components/Modals/EventQueueModal';
import FactoryResetModal from 'components/Modals/FactoryResetModal'; import FactoryResetModal from 'components/Modals/FactoryResetModal';
import { FirmwareUpgradeModal } from 'components/Modals/FirmwareUpgradeModal'; import { FirmwareUpgradeModal } from 'components/Modals/FirmwareUpgradeModal';
import { RebootModal } from 'components/Modals/RebootModal'; import { RebootModal } from 'components/Modals/RebootModal';
import ReEnrollModal from 'components/Modals/ReEnrollModal';
import { useScriptModal } from 'components/Modals/ScriptModal/useScriptModal'; import { useScriptModal } from 'components/Modals/ScriptModal/useScriptModal';
import ethernetConnected from './ethernetIconConnected.svg?react'; import ethernetConnected from './ethernetIconConnected.svg?react';
import ethernetDisconnected from './ethernetIconDisconnected.svg?react'; import ethernetDisconnected from './ethernetIconDisconnected.svg?react';
@@ -76,6 +77,7 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
const telemetryModalProps = useDisclosure(); const telemetryModalProps = useDisclosure();
const traceModalProps = useDisclosure(); const traceModalProps = useDisclosure();
const rebootModalProps = useDisclosure(); const rebootModalProps = useDisclosure();
const reEnrollModalProps = useDisclosure();
const scriptModal = useScriptModal(); const scriptModal = useScriptModal();
// Sticky-top styles // Sticky-top styles
const isCompact = breakpoint === 'base' || breakpoint === 'sm' || breakpoint === 'md'; const isCompact = breakpoint === 'base' || breakpoint === 'sm' || breakpoint === 'md';
@@ -216,6 +218,7 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
onOpenTelemetryModal={telemetryModalProps.onOpen} onOpenTelemetryModal={telemetryModalProps.onOpen}
onOpenScriptModal={scriptModal.openModal} onOpenScriptModal={scriptModal.openModal}
onOpenRebootModal={rebootModalProps.onOpen} onOpenRebootModal={rebootModalProps.onOpen}
onOpenReEnrollModal={reEnrollModalProps.onOpen}
size="md" size="md"
isCompact isCompact
/> />
@@ -268,6 +271,7 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
onOpenTelemetryModal={telemetryModalProps.onOpen} onOpenTelemetryModal={telemetryModalProps.onOpen}
onOpenRebootModal={rebootModalProps.onOpen} onOpenRebootModal={rebootModalProps.onOpen}
onOpenScriptModal={scriptModal.openModal} onOpenScriptModal={scriptModal.openModal}
onOpenReEnrollModal={reEnrollModalProps.onOpen}
size="md" size="md"
/> />
)} )}
@@ -311,6 +315,7 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
<ConfigureModal serialNumber={serialNumber} modalProps={configureModalProps} /> <ConfigureModal serialNumber={serialNumber} modalProps={configureModalProps} />
<TelemetryModal serialNumber={serialNumber} modalProps={telemetryModalProps} /> <TelemetryModal serialNumber={serialNumber} modalProps={telemetryModalProps} />
<RebootModal serialNumber={serialNumber} modalProps={rebootModalProps} /> <RebootModal serialNumber={serialNumber} modalProps={rebootModalProps} />
<ReEnrollModal serialNumber={serialNumber} modalProps={reEnrollModalProps} />
{scriptModal.modal} {scriptModal.modal}
<Box mt={isCompact ? '0px' : '68px'}> <Box mt={isCompact ? '0px' : '68px'}>
<Masonry <Masonry