@flightctl/ui-components 0.9.2 → 0.10.0-rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/components/DetailsPage/DetailsPage.d.ts +2 -1
- package/dist/src/components/DetailsPage/DetailsPage.d.ts.map +1 -1
- package/dist/src/components/DetailsPage/DetailsPage.js +2 -1
- package/dist/src/components/DetailsPage/DetailsPage.js.map +1 -1
- package/dist/src/components/DetailsPage/DetailsPageActions.d.ts +10 -0
- package/dist/src/components/DetailsPage/DetailsPageActions.d.ts.map +1 -1
- package/dist/src/components/DetailsPage/DetailsPageActions.js +23 -1
- package/dist/src/components/DetailsPage/DetailsPageActions.js.map +1 -1
- package/dist/src/components/Device/DeviceDetails/DeviceDetailsPage.d.ts.map +1 -1
- package/dist/src/components/Device/DeviceDetails/DeviceDetailsPage.js +29 -3
- package/dist/src/components/Device/DeviceDetails/DeviceDetailsPage.js.map +1 -1
- package/dist/src/components/Device/DeviceDetails/DeviceDetailsTab.d.ts.map +1 -1
- package/dist/src/components/Device/DeviceDetails/DeviceDetailsTab.js +0 -4
- package/dist/src/components/Device/DeviceDetails/DeviceDetailsTab.js.map +1 -1
- package/dist/src/components/Device/DeviceDetails/DeviceDetailsTabContent/StatusContent.d.ts.map +1 -1
- package/dist/src/components/Device/DeviceDetails/DeviceDetailsTabContent/StatusContent.js +8 -2
- package/dist/src/components/Device/DeviceDetails/DeviceDetailsTabContent/StatusContent.js.map +1 -1
- package/dist/src/components/Device/DevicesPage/DecommissionedDeviceTableRow.d.ts.map +1 -1
- package/dist/src/components/Device/DevicesPage/DecommissionedDeviceTableRow.js +1 -3
- package/dist/src/components/Device/DevicesPage/DecommissionedDeviceTableRow.js.map +1 -1
- package/dist/src/components/Device/DevicesPage/DecommissionedDevicesTable.d.ts.map +1 -1
- package/dist/src/components/Device/DevicesPage/DecommissionedDevicesTable.js +0 -3
- package/dist/src/components/Device/DevicesPage/DecommissionedDevicesTable.js.map +1 -1
- package/dist/src/components/Device/DevicesPage/DeviceToolbarFilters.d.ts.map +1 -1
- package/dist/src/components/Device/DevicesPage/DeviceToolbarFilters.js +3 -1
- package/dist/src/components/Device/DevicesPage/DeviceToolbarFilters.js.map +1 -1
- package/dist/src/components/Device/DevicesPage/DevicesPage.d.ts.map +1 -1
- package/dist/src/components/Device/DevicesPage/DevicesPage.js +1 -1
- package/dist/src/components/Device/DevicesPage/DevicesPage.js.map +1 -1
- package/dist/src/components/Device/DevicesPage/EnrolledDeviceTableRow.d.ts +3 -1
- package/dist/src/components/Device/DevicesPage/EnrolledDeviceTableRow.d.ts.map +1 -1
- package/dist/src/components/Device/DevicesPage/EnrolledDeviceTableRow.js +12 -4
- package/dist/src/components/Device/DevicesPage/EnrolledDeviceTableRow.js.map +1 -1
- package/dist/src/components/Device/DevicesPage/EnrolledDevicesTable.d.ts +2 -1
- package/dist/src/components/Device/DevicesPage/EnrolledDevicesTable.d.ts.map +1 -1
- package/dist/src/components/Device/DevicesPage/EnrolledDevicesTable.js +7 -6
- package/dist/src/components/Device/DevicesPage/EnrolledDevicesTable.js.map +1 -1
- package/dist/src/components/Device/SystemdUnitsModal/TrackSystemdUnitsForm.js +1 -1
- package/dist/src/components/Device/SystemdUnitsModal/TrackSystemdUnitsForm.js.map +1 -1
- package/dist/src/components/Events/useEvents.d.ts.map +1 -1
- package/dist/src/components/Events/useEvents.js +12 -0
- package/dist/src/components/Events/useEvents.js.map +1 -1
- package/dist/src/components/Fleet/CreateFleet/steps/UpdatePolicyStep.js +1 -1
- package/dist/src/components/Fleet/CreateFleet/steps/UpdatePolicyStep.js.map +1 -1
- package/dist/src/components/Fleet/FleetDetails/FleetDetailsPage.d.ts.map +1 -1
- package/dist/src/components/Fleet/FleetDetails/FleetDetailsPage.js +4 -3
- package/dist/src/components/Fleet/FleetDetails/FleetDetailsPage.js.map +1 -1
- package/dist/src/components/Fleet/FleetDetails/FleetDevicesCharts.d.ts.map +1 -1
- package/dist/src/components/Fleet/FleetDetails/FleetDevicesCharts.js +7 -1
- package/dist/src/components/Fleet/FleetDetails/FleetDevicesCharts.js.map +1 -1
- package/dist/src/components/Fleet/FleetDetails/FleetRestoreBanner.d.ts +8 -0
- package/dist/src/components/Fleet/FleetDetails/FleetRestoreBanner.d.ts.map +1 -0
- package/dist/src/components/Fleet/FleetDetails/FleetRestoreBanner.js +36 -0
- package/dist/src/components/Fleet/FleetDetails/FleetRestoreBanner.js.map +1 -0
- package/dist/src/components/Fleet/FleetsPage.d.ts.map +1 -1
- package/dist/src/components/Fleet/FleetsPage.js +2 -0
- package/dist/src/components/Fleet/FleetsPage.js.map +1 -1
- package/dist/src/components/ListPage/ListPageActions.d.ts +3 -2
- package/dist/src/components/ListPage/ListPageActions.d.ts.map +1 -1
- package/dist/src/components/ListPage/ListPageActions.js +27 -1
- package/dist/src/components/ListPage/ListPageActions.js.map +1 -1
- package/dist/src/components/Masthead/CommandLineToolsPage.d.ts.map +1 -1
- package/dist/src/components/Masthead/CommandLineToolsPage.js +18 -14
- package/dist/src/components/Masthead/CommandLineToolsPage.js.map +1 -1
- package/dist/src/components/OverviewPage/Cards/Alerts/AlertsCard.d.ts.map +1 -1
- package/dist/src/components/OverviewPage/Cards/Alerts/AlertsCard.js +15 -5
- package/dist/src/components/OverviewPage/Cards/Alerts/AlertsCard.js.map +1 -1
- package/dist/src/components/OverviewPage/Cards/Status/DeviceStatusChart.d.ts.map +1 -1
- package/dist/src/components/OverviewPage/Cards/Status/DeviceStatusChart.js +7 -1
- package/dist/src/components/OverviewPage/Cards/Status/DeviceStatusChart.js.map +1 -1
- package/dist/src/components/OverviewPage/Overview.d.ts.map +1 -1
- package/dist/src/components/OverviewPage/Overview.js +4 -2
- package/dist/src/components/OverviewPage/Overview.js.map +1 -1
- package/dist/src/components/Status/StatusDisplay.d.ts +3 -1
- package/dist/src/components/Status/StatusDisplay.d.ts.map +1 -1
- package/dist/src/components/Status/StatusDisplay.js +8 -8
- package/dist/src/components/Status/StatusDisplay.js.map +1 -1
- package/dist/src/components/SystemRestore/PendingSyncDevicesAlert.d.ts +7 -0
- package/dist/src/components/SystemRestore/PendingSyncDevicesAlert.d.ts.map +1 -0
- package/dist/src/components/SystemRestore/PendingSyncDevicesAlert.js +22 -0
- package/dist/src/components/SystemRestore/PendingSyncDevicesAlert.js.map +1 -0
- package/dist/src/components/SystemRestore/SuspendedDevicesAlert.d.ts +16 -0
- package/dist/src/components/SystemRestore/SuspendedDevicesAlert.d.ts.map +1 -0
- package/dist/src/components/SystemRestore/SuspendedDevicesAlert.js +75 -0
- package/dist/src/components/SystemRestore/SuspendedDevicesAlert.js.map +1 -0
- package/dist/src/components/SystemRestore/SystemRestoreBanners.css +6 -0
- package/dist/src/components/SystemRestore/SystemRestoreBanners.d.ts +28 -0
- package/dist/src/components/SystemRestore/SystemRestoreBanners.d.ts.map +1 -0
- package/dist/src/components/SystemRestore/SystemRestoreBanners.js +38 -0
- package/dist/src/components/SystemRestore/SystemRestoreBanners.js.map +1 -0
- package/dist/src/components/charts/utils.js +1 -1
- package/dist/src/components/charts/utils.js.map +1 -1
- package/dist/src/components/common/OrganizationGuard.d.ts +13 -0
- package/dist/src/components/common/OrganizationGuard.d.ts.map +1 -0
- package/dist/src/components/common/OrganizationGuard.js +106 -0
- package/dist/src/components/common/OrganizationGuard.js.map +1 -0
- package/dist/src/components/common/OrganizationSelector.d.ts +8 -0
- package/dist/src/components/common/OrganizationSelector.d.ts.map +1 -0
- package/dist/src/components/common/OrganizationSelector.js +92 -0
- package/dist/src/components/common/OrganizationSelector.js.map +1 -0
- package/dist/src/components/common/PageNavigation.d.ts +4 -0
- package/dist/src/components/common/PageNavigation.d.ts.map +1 -0
- package/dist/src/components/common/PageNavigation.js +46 -0
- package/dist/src/components/common/PageNavigation.js.map +1 -0
- package/dist/src/components/form/FilterSelect.css +3 -4
- package/dist/src/components/form/validations.d.ts +9 -0
- package/dist/src/components/form/validations.d.ts.map +1 -1
- package/dist/src/components/form/validations.js +12 -1
- package/dist/src/components/form/validations.js.map +1 -1
- package/dist/src/components/modals/ResumeDevicesModal/ResumeDevicesModal.d.ts +15 -0
- package/dist/src/components/modals/ResumeDevicesModal/ResumeDevicesModal.d.ts.map +1 -0
- package/dist/src/components/modals/ResumeDevicesModal/ResumeDevicesModal.js +56 -0
- package/dist/src/components/modals/ResumeDevicesModal/ResumeDevicesModal.js.map +1 -0
- package/dist/src/components/modals/massModals/ResumeDevicesModal/MassResumeDevicesModal.d.ts +7 -0
- package/dist/src/components/modals/massModals/ResumeDevicesModal/MassResumeDevicesModal.d.ts.map +1 -0
- package/dist/src/components/modals/massModals/ResumeDevicesModal/MassResumeDevicesModal.js +265 -0
- package/dist/src/components/modals/massModals/ResumeDevicesModal/MassResumeDevicesModal.js.map +1 -0
- package/dist/src/components/modals/massModals/ResumeDevicesModal/ResumeAllDevicesConfirmationDialog.d.ts +9 -0
- package/dist/src/components/modals/massModals/ResumeDevicesModal/ResumeAllDevicesConfirmationDialog.d.ts.map +1 -0
- package/dist/src/components/modals/massModals/ResumeDevicesModal/ResumeAllDevicesConfirmationDialog.js +27 -0
- package/dist/src/components/modals/massModals/ResumeDevicesModal/ResumeAllDevicesConfirmationDialog.js.map +1 -0
- package/dist/src/hooks/useAccessReview.d.ts.map +1 -1
- package/dist/src/hooks/useAccessReview.js +17 -4
- package/dist/src/hooks/useAccessReview.js.map +1 -1
- package/dist/src/hooks/useAlertsEnabled.d.ts +2 -0
- package/dist/src/hooks/useAlertsEnabled.d.ts.map +1 -0
- package/dist/src/hooks/useAlertsEnabled.js +50 -0
- package/dist/src/hooks/useAlertsEnabled.js.map +1 -0
- package/dist/src/hooks/useAppContext.d.ts +3 -6
- package/dist/src/hooks/useAppContext.d.ts.map +1 -1
- package/dist/src/hooks/useAppContext.js +1 -0
- package/dist/src/hooks/useAppContext.js.map +1 -1
- package/dist/src/hooks/useFetch.d.ts +6 -7
- package/dist/src/hooks/useFetch.d.ts.map +1 -1
- package/dist/src/hooks/useFetch.js +2 -3
- package/dist/src/hooks/useFetch.js.map +1 -1
- package/dist/src/hooks/useFetchPeriodically.d.ts.map +1 -1
- package/dist/src/hooks/useFetchPeriodically.js +4 -9
- package/dist/src/hooks/useFetchPeriodically.js.map +1 -1
- package/dist/src/hooks/useSystemRestoreContext.d.ts +16 -0
- package/dist/src/hooks/useSystemRestoreContext.d.ts.map +1 -0
- package/dist/src/hooks/useSystemRestoreContext.js +45 -0
- package/dist/src/hooks/useSystemRestoreContext.js.map +1 -0
- package/dist/src/types/extraTypes.d.ts +17 -18
- package/dist/src/types/extraTypes.d.ts.map +1 -1
- package/dist/src/types/extraTypes.js +1 -6
- package/dist/src/types/extraTypes.js.map +1 -1
- package/dist/src/types/rbac.d.ts +1 -0
- package/dist/src/types/rbac.d.ts.map +1 -1
- package/dist/src/types/rbac.js +1 -0
- package/dist/src/types/rbac.js.map +1 -1
- package/dist/src/utils/api.d.ts +2 -15
- package/dist/src/utils/api.d.ts.map +1 -1
- package/dist/src/utils/api.js +1 -40
- package/dist/src/utils/api.js.map +1 -1
- package/dist/src/utils/devices.d.ts +2 -0
- package/dist/src/utils/devices.d.ts.map +1 -1
- package/dist/src/utils/devices.js +11 -1
- package/dist/src/utils/devices.js.map +1 -1
- package/dist/src/utils/organizationStorage.d.ts +4 -0
- package/dist/src/utils/organizationStorage.d.ts.map +1 -0
- package/dist/src/utils/organizationStorage.js +18 -0
- package/dist/src/utils/organizationStorage.js.map +1 -0
- package/dist/src/utils/query.d.ts +2 -0
- package/dist/src/utils/query.d.ts.map +1 -1
- package/dist/src/utils/query.js +16 -0
- package/dist/src/utils/query.js.map +1 -1
- package/dist/src/utils/status/common.d.ts +1 -0
- package/dist/src/utils/status/common.d.ts.map +1 -1
- package/dist/src/utils/status/common.js.map +1 -1
- package/dist/src/utils/status/devices.d.ts +5 -0
- package/dist/src/utils/status/devices.d.ts.map +1 -1
- package/dist/src/utils/status/devices.js +44 -5
- package/dist/src/utils/status/devices.js.map +1 -1
- package/dist/src/utils/status/fleet.js +1 -1
- package/dist/src/utils/status/fleet.js.map +1 -1
- package/dist/src/utils/status/repository.js +1 -1
- package/dist/src/utils/status/repository.js.map +1 -1
- package/package.json +1 -1
- package/src/components/DetailsPage/DetailsPage.tsx +3 -0
- package/src/components/DetailsPage/DetailsPageActions.tsx +45 -0
- package/src/components/Device/DeviceDetails/DeviceDetailsPage.tsx +57 -5
- package/src/components/Device/DeviceDetails/DeviceDetailsTab.tsx +0 -5
- package/src/components/Device/DeviceDetails/DeviceDetailsTabContent/StatusContent.tsx +11 -3
- package/src/components/Device/DevicesPage/DecommissionedDeviceTableRow.tsx +0 -2
- package/src/components/Device/DevicesPage/DecommissionedDevicesTable.tsx +0 -3
- package/src/components/Device/DevicesPage/DeviceToolbarFilters.tsx +5 -1
- package/src/components/Device/DevicesPage/DevicesPage.tsx +1 -0
- package/src/components/Device/DevicesPage/EnrolledDeviceTableRow.tsx +15 -3
- package/src/components/Device/DevicesPage/EnrolledDevicesTable.tsx +11 -5
- package/src/components/Device/SystemdUnitsModal/TrackSystemdUnitsForm.tsx +1 -1
- package/src/components/Events/useEvents.ts +12 -0
- package/src/components/Fleet/CreateFleet/steps/UpdatePolicyStep.tsx +1 -1
- package/src/components/Fleet/FleetDetails/FleetDetailsPage.tsx +4 -5
- package/src/components/Fleet/FleetDetails/FleetDevicesCharts.tsx +9 -3
- package/src/components/Fleet/FleetDetails/FleetRestoreBanner.tsx +46 -0
- package/src/components/Fleet/FleetsPage.tsx +2 -0
- package/src/components/ListPage/ListPageActions.tsx +46 -3
- package/src/components/Masthead/CommandLineToolsPage.tsx +17 -14
- package/src/components/OverviewPage/Cards/Alerts/AlertsCard.tsx +19 -5
- package/src/components/OverviewPage/Cards/Status/DeviceStatusChart.tsx +8 -2
- package/src/components/OverviewPage/Overview.tsx +5 -2
- package/src/components/Status/StatusDisplay.tsx +32 -23
- package/src/components/SystemRestore/PendingSyncDevicesAlert.tsx +36 -0
- package/src/components/SystemRestore/SuspendedDevicesAlert.tsx +144 -0
- package/src/components/SystemRestore/SystemRestoreBanners.css +6 -0
- package/src/components/SystemRestore/SystemRestoreBanners.tsx +82 -0
- package/src/components/charts/utils.ts +1 -1
- package/src/components/common/OrganizationGuard.tsx +124 -0
- package/src/components/common/OrganizationSelector.tsx +192 -0
- package/src/components/common/PageNavigation.tsx +103 -0
- package/src/components/form/FilterSelect.css +3 -4
- package/src/components/form/validations.ts +14 -0
- package/src/components/modals/ResumeDevicesModal/ResumeDevicesModal.tsx +114 -0
- package/src/components/modals/massModals/ResumeDevicesModal/MassResumeDevicesModal.tsx +465 -0
- package/src/components/modals/massModals/ResumeDevicesModal/ResumeAllDevicesConfirmationDialog.tsx +55 -0
- package/src/hooks/useAccessReview.ts +20 -4
- package/src/hooks/useAlertsEnabled.ts +50 -0
- package/src/hooks/useAppContext.tsx +9 -7
- package/src/hooks/useFetch.ts +1 -3
- package/src/hooks/useFetchPeriodically.ts +4 -11
- package/src/hooks/useSystemRestoreContext.tsx +54 -0
- package/src/types/extraTypes.ts +17 -23
- package/src/types/rbac.ts +1 -0
- package/src/utils/api.ts +2 -51
- package/src/utils/devices.ts +11 -1
- package/src/utils/organizationStorage.ts +13 -0
- package/src/utils/query.ts +22 -0
- package/src/utils/status/common.ts +1 -0
- package/src/utils/status/devices.ts +49 -2
- package/src/utils/status/fleet.ts +1 -1
- package/src/utils/status/repository.ts +1 -1
- package/dist/src/hooks/useAlerts.d.ts +0 -26
- package/dist/src/hooks/useAlerts.d.ts.map +0 -1
- package/dist/src/hooks/useAlerts.js +0 -114
- package/dist/src/hooks/useAlerts.js.map +0 -1
- package/dist/src/utils/metrics.d.ts +0 -9
- package/dist/src/utils/metrics.d.ts.map +0 -1
- package/dist/src/utils/metrics.js +0 -48
- package/dist/src/utils/metrics.js.map +0 -1
- package/src/hooks/useAlerts.ts +0 -147
- package/src/utils/metrics.ts +0 -49
|
@@ -13,8 +13,9 @@ import NavItem from '../../NavItem/NavItem';
|
|
|
13
13
|
import DetailsPage from '../../DetailsPage/DetailsPage';
|
|
14
14
|
import DetailsPageActions from '../../DetailsPage/DetailsPageActions';
|
|
15
15
|
import DeleteFleetModal from '../DeleteFleetModal/DeleteFleetModal';
|
|
16
|
-
import FleetDetailsContent from './FleetDetailsContent';
|
|
17
16
|
import YamlEditor from '../../common/CodeEditor/YamlEditor';
|
|
17
|
+
import FleetDetailsContent from './FleetDetailsContent';
|
|
18
|
+
import FleetRestoreBanner from './FleetRestoreBanner';
|
|
18
19
|
|
|
19
20
|
const FleetDetailPage = () => {
|
|
20
21
|
const { t } = useTranslation();
|
|
@@ -45,6 +46,7 @@ const FleetDetailPage = () => {
|
|
|
45
46
|
resourceLink={ROUTE.FLEETS}
|
|
46
47
|
resourceType="Fleets"
|
|
47
48
|
resourceTypeLabel={t('Fleets')}
|
|
49
|
+
banner={<FleetRestoreBanner fleet={fleet} refetch={refetch} />}
|
|
48
50
|
nav={
|
|
49
51
|
<Nav variant="tertiary">
|
|
50
52
|
<NavList>
|
|
@@ -98,10 +100,7 @@ const FleetDetailPage = () => {
|
|
|
98
100
|
<Routes>
|
|
99
101
|
<Route index element={<Navigate to="details" replace />} />
|
|
100
102
|
<Route path="details" element={<FleetDetailsContent fleet={fleet} />} />
|
|
101
|
-
<Route
|
|
102
|
-
path="yaml"
|
|
103
|
-
element={<YamlEditor filename={fleet.metadata.name || 'fleet'} apiObj={fleet} refetch={refetch} />}
|
|
104
|
-
/>
|
|
103
|
+
<Route path="yaml" element={<YamlEditor filename={fleetId} apiObj={fleet} refetch={refetch} />} />
|
|
105
104
|
</Routes>
|
|
106
105
|
{isDeleteModalOpen && (
|
|
107
106
|
<DeleteFleetModal
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { Flex, FlexItem } from '@patternfly/react-core';
|
|
3
3
|
|
|
4
|
-
import { DevicesSummary } from '@flightctl/types';
|
|
4
|
+
import { DeviceSummaryStatusType, DevicesSummary } from '@flightctl/types';
|
|
5
5
|
import { useTranslation } from '../../../hooks/useTranslation';
|
|
6
|
-
import { FilterSearchParams,
|
|
6
|
+
import { FilterSearchParams, getOverviewDeviceStatusItems } from '../../../utils/status/devices';
|
|
7
7
|
import { getSystemUpdateStatusItems } from '../../../utils/status/system';
|
|
8
8
|
import {
|
|
9
9
|
getApplicationStatusHelperText,
|
|
@@ -19,6 +19,11 @@ interface FleetDevicesChartsProps {
|
|
|
19
19
|
devicesSummary: DevicesSummary;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
const systemRestoreStatuses = [
|
|
23
|
+
DeviceSummaryStatusType.DeviceSummaryStatusAwaitingReconnect,
|
|
24
|
+
DeviceSummaryStatusType.DeviceSummaryStatusConflictPaused,
|
|
25
|
+
];
|
|
26
|
+
|
|
22
27
|
const getBaseFleetQuery = (fleetId: string) => {
|
|
23
28
|
const baseQuery = new URLSearchParams();
|
|
24
29
|
baseQuery.set(FilterSearchParams.Fleet, fleetId);
|
|
@@ -78,7 +83,8 @@ const DevicesByDeviceStatusChart = ({
|
|
|
78
83
|
}) => {
|
|
79
84
|
const { t } = useTranslation();
|
|
80
85
|
|
|
81
|
-
const
|
|
86
|
+
const excludeStatuses = systemRestoreStatuses.filter((status) => !deviceStatus[status]);
|
|
87
|
+
const statusItems = getOverviewDeviceStatusItems(t, excludeStatuses);
|
|
82
88
|
|
|
83
89
|
const deviceStatusData = toChartData(
|
|
84
90
|
deviceStatus,
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Trans } from 'react-i18next';
|
|
3
|
+
import { DeviceSummaryStatusType, Fleet } from '@flightctl/types';
|
|
4
|
+
|
|
5
|
+
import { SystemRestoreBanners } from '../../SystemRestore/SystemRestoreBanners';
|
|
6
|
+
import { fromAPILabel, labelToExactApiMatchString } from '../../../utils/labels';
|
|
7
|
+
import { useTranslation } from '../../../hooks/useTranslation';
|
|
8
|
+
|
|
9
|
+
const FleetRestoreBanner = ({ fleet, refetch }: { fleet?: Fleet; refetch: VoidFunction }) => {
|
|
10
|
+
const { t } = useTranslation();
|
|
11
|
+
if (!fleet) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const fleetId = fleet.metadata.name as string;
|
|
16
|
+
|
|
17
|
+
const fleetDeviceStatuses = fleet?.status?.devicesSummary?.summaryStatus;
|
|
18
|
+
const suspendedDevicesCountNum =
|
|
19
|
+
fleetDeviceStatuses?.[DeviceSummaryStatusType.DeviceSummaryStatusConflictPaused] || 0;
|
|
20
|
+
const suspendedDevicesCount = suspendedDevicesCountNum.toString();
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<SystemRestoreBanners
|
|
24
|
+
mode="fleet"
|
|
25
|
+
summaryStatus={fleetDeviceStatuses}
|
|
26
|
+
resumeAction={{
|
|
27
|
+
actionText: t('Resume all'),
|
|
28
|
+
title: (
|
|
29
|
+
<Trans t={t} count={suspendedDevicesCountNum}>
|
|
30
|
+
You are about to resume all<strong>{suspendedDevicesCount}</strong> suspended devices in{' '}
|
|
31
|
+
<strong>{fleetId}</strong>
|
|
32
|
+
</Trans>
|
|
33
|
+
),
|
|
34
|
+
requestSelector: {
|
|
35
|
+
labelSelector: fromAPILabel(fleet.spec.selector?.matchLabels || {})
|
|
36
|
+
.map(labelToExactApiMatchString)
|
|
37
|
+
.join(','),
|
|
38
|
+
},
|
|
39
|
+
}}
|
|
40
|
+
onResumeComplete={refetch}
|
|
41
|
+
className="pf-v5-u-pt-0 pf-v5-u-px-lg"
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default FleetRestoreBanner;
|
|
@@ -37,6 +37,7 @@ import ButtonWithPermissions from '../common/ButtonWithPermissions';
|
|
|
37
37
|
import { RESOURCE, VERB } from '../../types/rbac';
|
|
38
38
|
import PageWithPermissions from '../common/PageWithPermissions';
|
|
39
39
|
import { useFleetImportAccessReview } from '../../hooks/useFleetImportAccessReview';
|
|
40
|
+
import { GlobalSystemRestoreBanners } from '../SystemRestore/SystemRestoreBanners';
|
|
40
41
|
|
|
41
42
|
const FleetPageActions = ({ createText }: { createText?: string }) => {
|
|
42
43
|
const { t } = useTranslation();
|
|
@@ -122,6 +123,7 @@ const FleetTable = () => {
|
|
|
122
123
|
|
|
123
124
|
return (
|
|
124
125
|
<ListPageBody error={error} loading={isLoading}>
|
|
126
|
+
<GlobalSystemRestoreBanners onResumeComplete={refetch} />
|
|
125
127
|
<Toolbar inset={{ default: 'insetNone' }}>
|
|
126
128
|
<ToolbarContent>
|
|
127
129
|
<ToolbarGroup>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { TFunction } from 'react-i18next';
|
|
2
|
+
import { TFunction, Trans } from 'react-i18next';
|
|
3
3
|
|
|
4
4
|
import { DeviceDecommissionTargetType } from '@flightctl/types';
|
|
5
5
|
import { ListAction, ListActionProps, ListActionResult } from './types';
|
|
@@ -8,9 +8,10 @@ import { useTranslation } from '../../hooks/useTranslation';
|
|
|
8
8
|
import { getDisabledTooltipProps } from '../../utils/tooltip';
|
|
9
9
|
import DeleteModal from '../modals/DeleteModal/DeleteModal';
|
|
10
10
|
import DecommissionModal from '../modals/DecommissionModal/DecommissionModal';
|
|
11
|
+
import ResumeDevicesModal from '../modals/ResumeDevicesModal/ResumeDevicesModal';
|
|
11
12
|
|
|
12
13
|
type DeleteResourceType = 'EnrollmentRequest' | 'ResourceSync' | 'Device';
|
|
13
|
-
type
|
|
14
|
+
type DeviceOnlyResourceType = 'Device';
|
|
14
15
|
|
|
15
16
|
type ResourceType = 'Device' | 'EnrollmentRequest' | 'ResourceSync';
|
|
16
17
|
|
|
@@ -70,7 +71,7 @@ export const useDeleteListAction = ({
|
|
|
70
71
|
|
|
71
72
|
export const useDecommissionListAction = ({
|
|
72
73
|
onConfirm,
|
|
73
|
-
}: ListActionProps<
|
|
74
|
+
}: ListActionProps<DeviceOnlyResourceType, { target: DeviceDecommissionTargetType }>): ListActionResult => {
|
|
74
75
|
const { t } = useTranslation();
|
|
75
76
|
const [decommissionDeviceId, setDecommissionDeviceId] = React.useState<string>();
|
|
76
77
|
|
|
@@ -100,3 +101,45 @@ export const useDecommissionListAction = ({
|
|
|
100
101
|
|
|
101
102
|
return { action: decommissionAction, modal: decommissionModal };
|
|
102
103
|
};
|
|
104
|
+
|
|
105
|
+
export const useResumeListAction = (onResumeComplete?: VoidFunction): ListActionResult => {
|
|
106
|
+
const { t } = useTranslation();
|
|
107
|
+
const [deviceId, setDeviceId] = React.useState<string>();
|
|
108
|
+
const [deviceName, setDeviceName] = React.useState<string>();
|
|
109
|
+
|
|
110
|
+
const resumeAction: ListAction = ({ resourceId, resourceName, disabledReason }) => {
|
|
111
|
+
const popperProps = getDisabledTooltipProps(disabledReason);
|
|
112
|
+
return {
|
|
113
|
+
title: t('Resume device'),
|
|
114
|
+
...popperProps,
|
|
115
|
+
onClick: () => {
|
|
116
|
+
setDeviceId(resourceId);
|
|
117
|
+
setDeviceName(resourceName || resourceId);
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const onClose = (hasResumed?: boolean) => {
|
|
123
|
+
setDeviceId(undefined);
|
|
124
|
+
setDeviceName(undefined);
|
|
125
|
+
if (hasResumed) {
|
|
126
|
+
onResumeComplete?.();
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const resumeModal = deviceId && (
|
|
131
|
+
<ResumeDevicesModal
|
|
132
|
+
mode="device"
|
|
133
|
+
title={
|
|
134
|
+
<Trans t={t}>
|
|
135
|
+
You are about to resume device <strong>{deviceName}</strong>
|
|
136
|
+
</Trans>
|
|
137
|
+
}
|
|
138
|
+
selector={{ fieldSelector: `metadata.name=${deviceId}` }}
|
|
139
|
+
expectedCount={1}
|
|
140
|
+
onClose={onClose}
|
|
141
|
+
/>
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
return { action: resumeAction, modal: resumeModal };
|
|
145
|
+
};
|
|
@@ -31,9 +31,6 @@ type CommandLineToolsContentProps = {
|
|
|
31
31
|
|
|
32
32
|
type CommandLineArtifact = CliArtifactsResponse['artifacts'][0];
|
|
33
33
|
|
|
34
|
-
// "Not implemented" response from the UI Proxy when artifact functionality is disabled
|
|
35
|
-
const cliArtifactsDisabledError = 'Error 501';
|
|
36
|
-
|
|
37
34
|
const getArtifactUrl = (baseUrl: string, artifact: CommandLineArtifact) => {
|
|
38
35
|
const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
|
|
39
36
|
return `${normalizedBaseUrl}/${artifact.arch}/${artifact.os}/${artifact.filename}`;
|
|
@@ -103,7 +100,8 @@ const CommandLineToolsContent = ({
|
|
|
103
100
|
|
|
104
101
|
const CommandLineToolsPage = () => {
|
|
105
102
|
const { t } = useTranslation();
|
|
106
|
-
const {
|
|
103
|
+
const { fetch, settings } = useAppContext();
|
|
104
|
+
const proxyFetch = fetch.proxyFetch;
|
|
107
105
|
|
|
108
106
|
const [loading, setLoading] = React.useState<boolean>(true);
|
|
109
107
|
const [loadError, setLoadError] = React.useState<string>();
|
|
@@ -113,23 +111,28 @@ const CommandLineToolsPage = () => {
|
|
|
113
111
|
React.useEffect(() => {
|
|
114
112
|
const getLinks = async () => {
|
|
115
113
|
try {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
const response = await proxyFetch('cli-artifacts', {
|
|
115
|
+
method: 'GET',
|
|
116
|
+
});
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
if (response.status === 501) {
|
|
119
|
+
// Response that indicatest that the feature is disabled
|
|
120
|
+
setArtifactsEnabled(false);
|
|
121
|
+
} else {
|
|
122
|
+
setLoadError(getErrorMessage(response.statusText));
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
119
125
|
}
|
|
126
|
+
const apiResponse = (await response.json()) as CliArtifactsResponse;
|
|
127
|
+
setCliArtifactsResponse(apiResponse);
|
|
120
128
|
} catch (e) {
|
|
121
|
-
|
|
122
|
-
if (msg.includes(cliArtifactsDisabledError)) {
|
|
123
|
-
setArtifactsEnabled(false);
|
|
124
|
-
} else {
|
|
125
|
-
setLoadError(msg);
|
|
126
|
-
}
|
|
129
|
+
setArtifactsEnabled(false);
|
|
127
130
|
} finally {
|
|
128
131
|
setLoading(false);
|
|
129
132
|
}
|
|
130
133
|
};
|
|
131
134
|
void getLinks();
|
|
132
|
-
}, [
|
|
135
|
+
}, [proxyFetch]);
|
|
133
136
|
|
|
134
137
|
const productName = settings.isRHEM ? t('Red Hat Edge Manager') : t('Flight Control');
|
|
135
138
|
|
|
@@ -15,7 +15,8 @@ import { TFunction } from 'react-i18next';
|
|
|
15
15
|
import { ExclamationCircleIcon } from '@patternfly/react-icons/dist/js/icons/exclamation-circle-icon';
|
|
16
16
|
|
|
17
17
|
import { Event, ResourceKind } from '@flightctl/types';
|
|
18
|
-
import {
|
|
18
|
+
import { AlertManagerAlert } from '../../../../types/extraTypes';
|
|
19
|
+
import { useFetchPeriodically } from '../../../../hooks/useFetchPeriodically';
|
|
19
20
|
import { useTranslation } from '../../../../hooks/useTranslation';
|
|
20
21
|
import { getDateDisplay } from '../../../../utils/dates';
|
|
21
22
|
import { getErrorMessage } from '../../../../utils/error';
|
|
@@ -23,6 +24,8 @@ import ResourceLink from '../../../common/ResourceLink';
|
|
|
23
24
|
|
|
24
25
|
import AlertsEmptyState from './AlertsEmptyState';
|
|
25
26
|
|
|
27
|
+
const ALERTS_TIMEOUT = 20000; // 20 seconds
|
|
28
|
+
|
|
26
29
|
// Define only the Event.reason values that correspond to alerts
|
|
27
30
|
type AlertEventReason =
|
|
28
31
|
| Event.reason.DEVICE_APPLICATION_DEGRADED
|
|
@@ -113,10 +116,21 @@ const resourceKindLabel = (t: TFunction, resourceKind: ResourceKind | undefined)
|
|
|
113
116
|
return t('Template version');
|
|
114
117
|
}
|
|
115
118
|
};
|
|
119
|
+
const getAlertTitle = (alert: AlertManagerAlert, defaultTitle: string) => {
|
|
120
|
+
if (alert.annotations.summary) {
|
|
121
|
+
return alert.annotations.summary;
|
|
122
|
+
}
|
|
123
|
+
return defaultTitle;
|
|
124
|
+
};
|
|
116
125
|
|
|
117
126
|
const AlertsCard = () => {
|
|
118
127
|
const { t } = useTranslation();
|
|
119
|
-
|
|
128
|
+
|
|
129
|
+
const [alerts, isLoading, error] = useFetchPeriodically<AlertManagerAlert[]>({
|
|
130
|
+
endpoint: 'alerts',
|
|
131
|
+
timeout: ALERTS_TIMEOUT,
|
|
132
|
+
});
|
|
133
|
+
|
|
120
134
|
const alertTypes = React.useMemo(() => getAlertTitles(t), [t]);
|
|
121
135
|
|
|
122
136
|
let alertsBody: React.ReactNode;
|
|
@@ -130,12 +144,12 @@ const AlertsCard = () => {
|
|
|
130
144
|
</Alert>
|
|
131
145
|
</CardBody>
|
|
132
146
|
);
|
|
133
|
-
} else if (alerts
|
|
147
|
+
} else if (alerts?.length === 0) {
|
|
134
148
|
alertsBody = <AlertsEmptyState />;
|
|
135
149
|
} else {
|
|
136
150
|
alertsBody = (
|
|
137
151
|
<List isPlain>
|
|
138
|
-
{alerts
|
|
152
|
+
{alerts?.map((alert) => {
|
|
139
153
|
const alertName = alert.labels.alertname as AlertEventReason;
|
|
140
154
|
const resourceKind = alertResourceKind[alertName];
|
|
141
155
|
const kindLabel = resourceKindLabel(t, resourceKind);
|
|
@@ -146,7 +160,7 @@ const AlertsCard = () => {
|
|
|
146
160
|
<Icon status="danger" size="md">
|
|
147
161
|
<ExclamationCircleIcon />
|
|
148
162
|
</Icon>{' '}
|
|
149
|
-
<strong>{alertTypes[alertName] || alertName}</strong>
|
|
163
|
+
<strong>{getAlertTitle(alert, alertTypes[alertName] || alertName)}</strong>
|
|
150
164
|
</StackItem>
|
|
151
165
|
<StackItem>
|
|
152
166
|
<TextContent>
|
|
@@ -5,11 +5,16 @@ import { FlightCtlLabel } from '../../../../types/extraTypes';
|
|
|
5
5
|
|
|
6
6
|
import { useTranslation } from '../../../../hooks/useTranslation';
|
|
7
7
|
import { getDeviceStatusHelperText } from '../../../Status/utils';
|
|
8
|
-
import {
|
|
8
|
+
import { getOverviewDeviceStatusItems } from '../../../../utils/status/devices';
|
|
9
9
|
import { FilterSearchParams } from '../../../../utils/status/devices';
|
|
10
10
|
import { toOverviewChartData } from './utils';
|
|
11
11
|
import DonutChart from '../../../charts/DonutChart';
|
|
12
12
|
|
|
13
|
+
const systemRestoreStatuses = [
|
|
14
|
+
DeviceSummaryStatusType.DeviceSummaryStatusAwaitingReconnect,
|
|
15
|
+
DeviceSummaryStatusType.DeviceSummaryStatusConflictPaused,
|
|
16
|
+
];
|
|
17
|
+
|
|
13
18
|
const DeviceStatusChart = ({
|
|
14
19
|
deviceStatus,
|
|
15
20
|
labels,
|
|
@@ -21,7 +26,8 @@ const DeviceStatusChart = ({
|
|
|
21
26
|
}) => {
|
|
22
27
|
const { t } = useTranslation();
|
|
23
28
|
|
|
24
|
-
const
|
|
29
|
+
const excludeStatuses = systemRestoreStatuses.filter((status) => !deviceStatus[status]);
|
|
30
|
+
const statusItems = getOverviewDeviceStatusItems(t, excludeStatuses);
|
|
25
31
|
|
|
26
32
|
const devStatusData = toOverviewChartData<DeviceSummaryStatusType>(
|
|
27
33
|
deviceStatus,
|
|
@@ -2,21 +2,24 @@ import * as React from 'react';
|
|
|
2
2
|
import { Grid, GridItem } from '@patternfly/react-core';
|
|
3
3
|
|
|
4
4
|
import { useAccessReview } from '../../hooks/useAccessReview';
|
|
5
|
+
import { useAlertsEnabled } from '../../hooks/useAlertsEnabled';
|
|
5
6
|
import { RESOURCE, VERB } from '../../types/rbac';
|
|
6
7
|
import PageWithPermissions from '../common/PageWithPermissions';
|
|
7
|
-
import {
|
|
8
|
+
import { GlobalSystemRestoreBanners } from '../SystemRestore/SystemRestoreBanners';
|
|
8
9
|
|
|
9
10
|
import AlertsCard from './Cards/Alerts/AlertsCard';
|
|
10
11
|
import StatusCard from './Cards/Status/StatusCard';
|
|
11
12
|
import TasksCard from './Cards/Tasks/TasksCard';
|
|
12
13
|
|
|
13
14
|
const Overview = () => {
|
|
15
|
+
const alertsEnabled = useAlertsEnabled();
|
|
14
16
|
const [canListDevices, devicesLoading] = useAccessReview(RESOURCE.DEVICE, VERB.LIST);
|
|
15
17
|
const [canListErs, erLoading] = useAccessReview(RESOURCE.ENROLLMENT_REQUEST, VERB.LIST);
|
|
16
|
-
const alertsEnabled = useAlertsEnabled();
|
|
17
18
|
|
|
18
19
|
return (
|
|
19
20
|
<PageWithPermissions allowed={canListDevices || canListErs} loading={devicesLoading || erLoading}>
|
|
21
|
+
<GlobalSystemRestoreBanners className="pf-v5-u-py-0" />
|
|
22
|
+
|
|
20
23
|
<Grid hasGutter>
|
|
21
24
|
<GridItem md={alertsEnabled ? 9 : 12}>
|
|
22
25
|
<Grid hasGutter>
|
|
@@ -14,11 +14,29 @@ type StatusLabelProps = {
|
|
|
14
14
|
messageTitle?: string;
|
|
15
15
|
level: StatusLevel;
|
|
16
16
|
customIcon?: React.ComponentClass<SVGIconProps>;
|
|
17
|
+
customColor?: string;
|
|
17
18
|
};
|
|
18
19
|
|
|
19
|
-
export const StatusDisplayContent = ({
|
|
20
|
-
|
|
20
|
+
export const StatusDisplayContent = ({
|
|
21
|
+
label,
|
|
22
|
+
messageTitle,
|
|
23
|
+
message,
|
|
24
|
+
level,
|
|
25
|
+
customIcon,
|
|
26
|
+
customColor,
|
|
27
|
+
}: StatusLabelProps) => {
|
|
28
|
+
const overrideStatus = level === 'unknown' || (level === 'custom' && customColor);
|
|
21
29
|
const IconComponent = customIcon || getDefaultStatusIcon(level);
|
|
30
|
+
const iconColor = customColor || getDefaultStatusColor(level);
|
|
31
|
+
|
|
32
|
+
const icon = (
|
|
33
|
+
<Icon
|
|
34
|
+
status={overrideStatus ? undefined : level}
|
|
35
|
+
style={{ '--pf-v5-c-icon__content--Color': iconColor } as React.CSSProperties}
|
|
36
|
+
>
|
|
37
|
+
<IconComponent />
|
|
38
|
+
</Icon>
|
|
39
|
+
);
|
|
22
40
|
|
|
23
41
|
if (message) {
|
|
24
42
|
return (
|
|
@@ -29,18 +47,7 @@ export const StatusDisplayContent = ({ label, messageTitle, message, level, cust
|
|
|
29
47
|
className="fctl-status-display-content__popover"
|
|
30
48
|
hasAutoWidth={false}
|
|
31
49
|
>
|
|
32
|
-
<Button
|
|
33
|
-
variant="link"
|
|
34
|
-
isInline
|
|
35
|
-
icon={
|
|
36
|
-
<Icon
|
|
37
|
-
status={iconStatus}
|
|
38
|
-
style={{ '--pf-v5-c-icon__content--Color': getDefaultStatusColor(level) } as React.CSSProperties}
|
|
39
|
-
>
|
|
40
|
-
<IconComponent />
|
|
41
|
-
</Icon>
|
|
42
|
-
}
|
|
43
|
-
>
|
|
50
|
+
<Button variant="link" isInline icon={icon}>
|
|
44
51
|
{label}
|
|
45
52
|
</Button>
|
|
46
53
|
</Popover>
|
|
@@ -49,14 +56,7 @@ export const StatusDisplayContent = ({ label, messageTitle, message, level, cust
|
|
|
49
56
|
|
|
50
57
|
return (
|
|
51
58
|
<Flex className="ftcl_status-label">
|
|
52
|
-
<FlexItem>
|
|
53
|
-
<Icon
|
|
54
|
-
status={iconStatus}
|
|
55
|
-
style={{ '--pf-v5-c-icon__content--Color': getDefaultStatusColor(level) } as React.CSSProperties}
|
|
56
|
-
>
|
|
57
|
-
<IconComponent />
|
|
58
|
-
</Icon>
|
|
59
|
-
</FlexItem>
|
|
59
|
+
<FlexItem>{icon}</FlexItem>
|
|
60
60
|
<FlexItem>{label}</FlexItem>
|
|
61
61
|
</Flex>
|
|
62
62
|
);
|
|
@@ -67,6 +67,7 @@ type StatusDisplayProps = {
|
|
|
67
67
|
label: string;
|
|
68
68
|
level: StatusLevel;
|
|
69
69
|
customIcon?: React.ComponentClass<SVGIconProps>;
|
|
70
|
+
customColor?: string;
|
|
70
71
|
};
|
|
71
72
|
message?: React.ReactNode;
|
|
72
73
|
};
|
|
@@ -77,7 +78,15 @@ const StatusDisplay = ({ item, message }: StatusDisplayProps) => {
|
|
|
77
78
|
return <StatusDisplayContent level="unknown" label={t('Unknown')} />;
|
|
78
79
|
}
|
|
79
80
|
|
|
80
|
-
return
|
|
81
|
+
return (
|
|
82
|
+
<StatusDisplayContent
|
|
83
|
+
level={item.level}
|
|
84
|
+
customIcon={item.customIcon}
|
|
85
|
+
customColor={item.customColor}
|
|
86
|
+
label={item.label}
|
|
87
|
+
message={message}
|
|
88
|
+
/>
|
|
89
|
+
);
|
|
81
90
|
};
|
|
82
91
|
|
|
83
92
|
export default StatusDisplay;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Alert } from '@patternfly/react-core';
|
|
3
|
+
|
|
4
|
+
import { useAppContext } from '../../hooks/useAppContext';
|
|
5
|
+
import { useTranslation } from '../../hooks/useTranslation';
|
|
6
|
+
|
|
7
|
+
interface PendingSyncDevicesAlertProps {
|
|
8
|
+
forSingleDevice?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const getBrand = (isRHEM: boolean | undefined) => (isRHEM ? 'RHEM' : 'Flight Control');
|
|
12
|
+
|
|
13
|
+
export const PendingSyncDevicesAlert = ({ forSingleDevice }: PendingSyncDevicesAlertProps) => {
|
|
14
|
+
const { t } = useTranslation();
|
|
15
|
+
const { settings } = useAppContext();
|
|
16
|
+
|
|
17
|
+
const getMessage = () => {
|
|
18
|
+
if (forSingleDevice) {
|
|
19
|
+
return t(
|
|
20
|
+
'{{brand}} is waiting for the device to connect and report its status. It will report a ʼPending syncʼ status until it is able to reconnect. If it has configuration conflicts, it will report a ʼSuspendedʼ status and require manual action to resume.',
|
|
21
|
+
{ brand: getBrand(settings.isRHEM) },
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return t(
|
|
26
|
+
'{{brand}} is waiting for devices to connect and report their status. Devices will report a ʼPending syncʼ status until they are able to connect. Devices with configuration conflicts will report a ʼSuspendedʼ status and require manual action to resume.',
|
|
27
|
+
{ brand: getBrand(settings.isRHEM) },
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<Alert variant="warning" isInline title={t('System recovery complete')}>
|
|
33
|
+
{getMessage()}
|
|
34
|
+
</Alert>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Trans } from 'react-i18next';
|
|
3
|
+
import { Alert, AlertActionLink, Stack, StackItem } from '@patternfly/react-core';
|
|
4
|
+
|
|
5
|
+
import { useTranslation } from '../../hooks/useTranslation';
|
|
6
|
+
import MassResumeDevicesModal from '../modals/massModals/ResumeDevicesModal/MassResumeDevicesModal';
|
|
7
|
+
import ResumeDevicesModal from '../modals/ResumeDevicesModal/ResumeDevicesModal';
|
|
8
|
+
import { DeviceResumeRequest } from '@flightctl/types';
|
|
9
|
+
|
|
10
|
+
export type ResumeMode = 'global' | 'device' | 'fleet';
|
|
11
|
+
|
|
12
|
+
type ModalType = 'mass' | 'confirm';
|
|
13
|
+
|
|
14
|
+
interface SuspendedDevicesAlertProps {
|
|
15
|
+
mode: ResumeMode;
|
|
16
|
+
suspendedCount?: number;
|
|
17
|
+
extraAction?: {
|
|
18
|
+
actionText: string;
|
|
19
|
+
title: React.ReactNode;
|
|
20
|
+
requestSelector: DeviceResumeRequest;
|
|
21
|
+
};
|
|
22
|
+
onResumeComplete?: VoidFunction;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const SuspendedDevicesAlert = ({
|
|
26
|
+
mode,
|
|
27
|
+
suspendedCount = 0,
|
|
28
|
+
extraAction,
|
|
29
|
+
onResumeComplete,
|
|
30
|
+
}: SuspendedDevicesAlertProps) => {
|
|
31
|
+
const { t } = useTranslation();
|
|
32
|
+
const [isMassModalOpen, setIsMassModalOpen] = React.useState(false);
|
|
33
|
+
const [isConfirmModalOpen, setIsConfirmModalOpen] = React.useState(false);
|
|
34
|
+
|
|
35
|
+
const openResumeModal = React.useCallback((modalType: ModalType) => {
|
|
36
|
+
if (modalType === 'mass') {
|
|
37
|
+
setIsMassModalOpen(true);
|
|
38
|
+
} else {
|
|
39
|
+
setIsConfirmModalOpen(true);
|
|
40
|
+
}
|
|
41
|
+
}, []);
|
|
42
|
+
|
|
43
|
+
const closeResumeModal = React.useCallback(
|
|
44
|
+
(modalType: ModalType, hasResumed?: boolean) => {
|
|
45
|
+
if (hasResumed) {
|
|
46
|
+
onResumeComplete?.();
|
|
47
|
+
}
|
|
48
|
+
if (modalType === 'mass') {
|
|
49
|
+
setIsMassModalOpen(false);
|
|
50
|
+
} else {
|
|
51
|
+
setIsConfirmModalOpen(false);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
[onResumeComplete],
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const getMainMessage = () => {
|
|
58
|
+
const suspendedCountStr = suspendedCount.toString();
|
|
59
|
+
switch (mode) {
|
|
60
|
+
case 'device':
|
|
61
|
+
return t(
|
|
62
|
+
"This device is suspended because its local configuration is newer than the server's record. It will not receive updates until it is resumed.",
|
|
63
|
+
);
|
|
64
|
+
case 'fleet':
|
|
65
|
+
return (
|
|
66
|
+
<Trans t={t} count={suspendedCount}>
|
|
67
|
+
<strong>{suspendedCountStr}</strong> <strong>devices in this fleet</strong> are suspended because their
|
|
68
|
+
local configuration is newer than the server's record. These devices will not receive updates until
|
|
69
|
+
they are resumed.
|
|
70
|
+
</Trans>
|
|
71
|
+
);
|
|
72
|
+
default:
|
|
73
|
+
return (
|
|
74
|
+
<Trans t={t} count={suspendedCount}>
|
|
75
|
+
<strong>{suspendedCountStr}</strong> devices are suspended because their local configuration is newer than
|
|
76
|
+
the server's record. These devices will not receive updates until they are resumed.
|
|
77
|
+
</Trans>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const getActions = () => {
|
|
83
|
+
const actionButtons: React.ReactElement[] = [];
|
|
84
|
+
|
|
85
|
+
if (extraAction) {
|
|
86
|
+
actionButtons.push(
|
|
87
|
+
<AlertActionLink key="extra-resume-action" onClick={() => openResumeModal('confirm')}>
|
|
88
|
+
{extraAction.actionText}
|
|
89
|
+
</AlertActionLink>,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
actionButtons.push(
|
|
94
|
+
<AlertActionLink key="resume-suspended-devices" onClick={() => openResumeModal('mass')}>
|
|
95
|
+
{t('Resume suspended devices')}
|
|
96
|
+
</AlertActionLink>,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
return actionButtons;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<>
|
|
104
|
+
<Alert variant="danger" isInline title={t('Suspended devices detected')} actionLinks={getActions()}>
|
|
105
|
+
<Stack hasGutter>
|
|
106
|
+
<StackItem>{getMainMessage()}</StackItem>
|
|
107
|
+
<StackItem>
|
|
108
|
+
{mode === 'fleet' ? (
|
|
109
|
+
<Trans t={t}>
|
|
110
|
+
<strong>Warning:</strong> Please review this fleet's configuration before taking action. Resuming a
|
|
111
|
+
device will cause it to apply the current specification, which may be older than what is on the device.
|
|
112
|
+
</Trans>
|
|
113
|
+
) : (
|
|
114
|
+
<Trans t={t}>
|
|
115
|
+
<strong>Warning:</strong> Please review device configurations before taking action. Resuming a device
|
|
116
|
+
will cause it to apply the current specification, which may be older than what is on the device.
|
|
117
|
+
</Trans>
|
|
118
|
+
)}
|
|
119
|
+
</StackItem>
|
|
120
|
+
</Stack>
|
|
121
|
+
</Alert>
|
|
122
|
+
{isMassModalOpen && (
|
|
123
|
+
<MassResumeDevicesModal
|
|
124
|
+
onClose={(hasResumed) => {
|
|
125
|
+
closeResumeModal('mass', hasResumed);
|
|
126
|
+
}}
|
|
127
|
+
/>
|
|
128
|
+
)}
|
|
129
|
+
{isConfirmModalOpen && extraAction && (
|
|
130
|
+
<ResumeDevicesModal
|
|
131
|
+
mode={mode === 'global' ? 'device' : mode}
|
|
132
|
+
title={extraAction.title}
|
|
133
|
+
selector={extraAction.requestSelector}
|
|
134
|
+
expectedCount={suspendedCount}
|
|
135
|
+
onClose={(hasResumed) => {
|
|
136
|
+
closeResumeModal('confirm', hasResumed);
|
|
137
|
+
}}
|
|
138
|
+
/>
|
|
139
|
+
)}
|
|
140
|
+
</>
|
|
141
|
+
);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export default SuspendedDevicesAlert;
|