Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions apps/ocp-plugin/src/components/Devices/DeviceDetailsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import * as React from 'react';
import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk';
import ExclamationCircleIcon from '@patternfly/react-icons/dist/js/icons/exclamation-circle-icon';
import { Icon, Popover, Stack, StackItem } from '@patternfly/react-core';
import {
DescriptionListDescription,
DescriptionListGroup,
DescriptionListTerm,
Icon,
Popover,
} from '@patternfly/react-core';
import { useAppContext } from '@flightctl/ui-components/src/hooks/useAppContext';
import { useTranslation } from '@flightctl/ui-components/src/hooks/useTranslation';
import DeviceDetails from '@flightctl/ui-components/src/components/Device/DeviceDetails/DeviceDetailsPage';
Expand Down Expand Up @@ -65,10 +71,10 @@ const DeviceDetailsPage = () => {
return (
<WithPageLayout>
<DeviceDetails>
<Stack>
<StackItem className="fctl-device-details-tab__label">{t('MicroShift cluster')}</StackItem>
<StackItem>{mcContent}</StackItem>
</Stack>
<DescriptionListGroup>
<DescriptionListTerm>{t('MicroShift cluster')}</DescriptionListTerm>
<DescriptionListDescription>{mcContent}</DescriptionListDescription>
</DescriptionListGroup>
</DeviceDetails>
</WithPageLayout>
);
Expand Down
51 changes: 32 additions & 19 deletions libs/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,8 @@
"Please try refreshing the page. If the problem persists, contact your administrator.": "Please try refreshing the page. If the problem persists, contact your administrator.",
"Error details": "Error details",
"Reload organizations": "Reload organizations",
"Image": "Image",
"Package": "Package",
"Image mode: OS uses bootable container images": "Image mode: OS uses bootable container images",
"Package mode: OS managed outside of Edge Manager": "Package mode: OS managed outside of Edge Manager",
"OS mode: Unknown": "OS mode: Unknown",
Expand Down Expand Up @@ -635,6 +637,9 @@
"Alias": "Alias",
"Edit alias": "Edit alias",
"Device alias could not be updated": "Device alias could not be updated",
"{{count}} application errors_one": "{{count}} application error",
"{{count}} application errors_other": "{{count}} application errors",
"Custom data": "Custom data",
"Failed to fetch owner fleet": "Failed to fetch owner fleet",
"Delete forever": "Delete forever",
"You are about to resume <1>{deviceNameOrAlias}</1>": "You are about to resume <1>{deviceNameOrAlias}</1>",
Expand All @@ -643,19 +648,12 @@
"Events": "Events",
"Edit device configurations": "Edit device configurations",
"Delete device forever": "Delete device forever",
"Fleet name": "Fleet name",
"Labels": "Labels",
"Custom data": "Custom data",
"Indicates whether the device is available to be managed and assigned to do work or is moving to an end-of-life state.": "Indicates whether the device is available to be managed and assigned to do work or is moving to an end-of-life state.",
"Configurations": "Configurations",
"Application status is not available for decommissioned devices": "Application status is not available for decommissioned devices",
"System image": "System image",
"This device uses package-based OS management and cannot satisfy the fleet's OS image requirement. The device will remain out of date and unable to apply any fleet updates. To resolve this, remove the device from the fleet or update it to a bootable container image.": "This device uses package-based OS management and cannot satisfy the fleet's OS image requirement. The device will remain out of date and unable to apply any fleet updates. To resolve this, remove the device from the fleet or update it to a bootable container image.",
"OS image status not fully determined": "OS image status not fully determined",
"The device is bound to a fleet, but its OS image status could not be determined.": "The device is bound to a fleet, but its OS image status could not be determined.",
"The device is bound to a fleet, and its OS image status could not be determined.": "The device is bound to a fleet, and its OS image status could not be determined.",
"System image (running)": "System image (running)",
"Configurations": "Configurations",
"Sources ({{size}})": "Sources ({{size}})",
"System status": "System status",
"Application status": "Application status",
"Indicates the overall status of application workloads on the device.": "Indicates the overall status of application workloads on the device.",
"Device status": "Device status",
Expand All @@ -664,15 +662,19 @@
"Indicates whether a system is running the latest target configuration or is updating towards it.": "Indicates whether a system is running the latest target configuration or is updating towards it.",
"Integrity status": "Integrity status",
"Indicates whether the device has been verified as secure and authentic.": "Indicates whether the device has been verified as secure and authentic.",
"Last seen": "Last seen",
"Resource status": "Resource status",
"CPU pressure": "CPU pressure",
"Disk pressure": "Disk pressure",
"Memory pressure": "Memory pressure",
"None": "None",
"Device labels don't match any fleet's selector labels": "Device labels don't match any fleet's selector labels",
"Ownership information": "Ownership information",
"Device labels match multiple fleets:": "Device labels match multiple fleets:",
"{{count}} status issues_one": "{{count}} status issue",
"{{count}} status issues_other": "{{count}} status issues",
"Issues detected": "Issues detected",
"Device information": "Device information",
"Labels": "Labels",
"Last seen {{time}}": "Last seen {{time}}",
"Log file not found": "Log file not found",
"The requested log file could not be found on the device. Verify the file path is correct and the logs exist on the device, then try again.": "The requested log file could not be found on the device. Verify the file path is correct and the logs exist on the device, then try again.",
"Path is a directory": "Path is a directory",
Expand Down Expand Up @@ -736,7 +738,14 @@
"Running system image is unknown": "Running system image is unknown",
"System image mismatch": "System image mismatch",
"Desired system image": "Desired system image",
"Device specifications": "Device specifications",
"Hide full system info": "Hide full system info",
"Show full system info": "Show full system info",
"Capabilities": "Capabilities",
"System status": "System status",
"Resource status": "Resource status",
"System services": "System services",
"OS mode": "OS mode",
"You do not have permission to access the device terminal.": "You do not have permission to access the device terminal.",
"Console type": "Console type",
"Device terminal": "Device terminal",
Expand All @@ -758,7 +767,6 @@
"Networking details": "Networking details",
"Toggle networking details": "Toggle networking details",
"VM details for {{name}}": "VM details for {{name}}",
"Image": "Image",
"Run as user": "Run as user",
"Embedded": "Embedded",
"Yes": "Yes",
Expand All @@ -777,7 +785,6 @@
"You can add devices and label them to match fleets": "You can add devices and label them to match fleets",
"No decommissioning or decommissioned devices here!": "No decommissioning or decommissioned devices here!",
"View and manage your enrolled edge devices. Monitor device status, configurations, and fleet assignments.": "View and manage your enrolled edge devices. Monitor device status, configurations, and fleet assignments.",
"OS mode": "OS mode",
"Clear all filters": "Clear all filters",
"Searching...": "Searching...",
"No results": "No results",
Expand Down Expand Up @@ -963,6 +970,7 @@
"view documentation": "view documentation",
"Using template variables": "Using template variables",
"Add a variable by using <1>{templateOption1}</1> or <3>{templateOption2}</3> and it will be applied based on each device&rsquo;s details. For example, you could set the following value to apply different files in a Git configuration:": "Add a variable by using <1>{templateOption1}</1> or <3>{templateOption2}</3> and it will be applied based on each device&rsquo;s details. For example, you could set the following value to apply different files in a Git configuration:",
"System image": "System image",
"The target system image for this fleet's devices.": "The target system image for this fleet's devices.",
"The target system image for this device.": "The target system image for this device.",
"System image is managed outside of Edge Manager": "System image is managed outside of Edge Manager",
Expand Down Expand Up @@ -1000,6 +1008,7 @@
"Enter image reference or choose from catalog": "Enter image reference or choose from catalog",
"Choose from catalog": "Choose from catalog",
"Approve": "Approve",
"Last seen": "Last seen",
"Certificate signing request": "Certificate signing request",
"A PEM-encoded PKCS#10 certificate signing request.": "A PEM-encoded PKCS#10 certificate signing request.",
"Certificate Signing Request": "Certificate Signing Request",
Expand Down Expand Up @@ -1096,6 +1105,7 @@
"{{ count }} devices matching the labels were selected._one": "{{ count }} device matching the labels was selected.",
"{{ count }} devices matching the labels were selected._other": "{{ count }} devices matching the labels were selected.",
"Labels used to select devices for your fleet. If not specified, no devices will be added.": "Labels used to select devices for your fleet. If not specified, no devices will be added.",
"Fleet name": "Fleet name",
"Fleet labels": "Fleet labels",
"Device selector": "Device selector",
"Rollout policy": "Rollout policy",
Expand Down Expand Up @@ -1788,7 +1798,9 @@
"Enter a valid HTTP service URL. Example: https://my-service-url": "Enter a valid HTTP service URL. Example: https://my-service-url",
"HTTP service URL is required": "HTTP service URL is required",
"Copy fingerprint": "Copy fingerprint",
"Last synced": "Last synced",
"Last synced {{syncTime}}": "Last synced {{syncTime}}",
"Sources": "Sources",
"Number of configurations": "Number of configurations",
"Deleting {{count}} resource sync_one": "Deleting {{count}} resource sync",
"Deleting {{count}} resource sync_other": "Deleting {{count}} resource syncs",
"{{count}} resource sync could not be deleted. Try deleting it manually._one": "{{count}} resource sync could not be deleted. Try deleting it manually.",
Expand Down Expand Up @@ -1830,11 +1842,13 @@
"Resource sync {{rsId}} could not be found": "Resource sync {{rsId}} could not be found",
"Resource sync {{rsId}}": "Resource sync {{rsId}}",
"Could not find the details for the resource sync <1>{rsId}</1>": "Could not find the details for the resource sync <1>{rsId}</1>",
"Toggle security details": "Toggle security details",
"Monitor security findings across your edge environment. Review vulnerabilities to identify affected devices and images.": "Monitor security findings across your edge environment. Review vulnerabilities to identify affected devices and images.",
"Vulnerability reporting is not enabled in this environment.": "Vulnerability reporting is not enabled in this environment.",
"Filter table by {{severity}} severity": "Filter table by {{severity}} severity",
"Vulnerability counts by severity": "Vulnerability counts by severity",
"Total active vulnerabilities.": "Total active vulnerabilities.",
"CVEs affecting images deployed across your managed fleet and devices.": "CVEs affecting images deployed across your managed fleet and devices.",
"Vulnerability counts by severity": "Vulnerability counts by severity",
"No vulnerabilities were found affecting the image currently deployed on this device. This reflects the most recent scan data available.": "No vulnerabilities were found affecting the image currently deployed on this device. This reflects the most recent scan data available.",
"No vulnerabilities were found affecting images currently deployed across all devices in this fleet. This reflects the most recent scan data available.": "No vulnerabilities were found affecting images currently deployed across all devices in this fleet. This reflects the most recent scan data available.",
"No vulnerabilities detected": "No vulnerabilities detected",
Expand Down Expand Up @@ -1937,10 +1951,11 @@
"End active session": "End active session",
"Console session unavailable": "Console session unavailable",
"Log retrieval failed with exit code {{code}}": "Log retrieval failed with exit code {{code}}",
"Architecture": "Architecture",
"Agent version": "Agent version",
"Distro": "Distro",
"Hostname": "Hostname",
"TPM vendor info": "TPM vendor info",
"Architecture": "Architecture",
"Distro": "Distro",
"Boot ID": "Boot ID",
"Kernel": "Kernel",
"Net interface default": "Net interface default",
Expand All @@ -1949,8 +1964,6 @@
"Product name": "Product name",
"Product serial": "Product serial",
"Product UUID": "Product UUID",
"TPM vendor info": "TPM vendor info",
"Package": "Package",
"Unknown error": "Unknown error",
"Failed to connect to device terminal: {{reason}}": "Failed to connect to device terminal: {{reason}}",
"Failed to connect to device terminal": "Failed to connect to device terminal",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
import * as React from 'react';
import { Card, type CardProps } from '@patternfly/react-core';
import { Card, type CardProps, CardTitle, Flex, FlexItem, Icon } from '@patternfly/react-core';

export const DetailsPageCardTitle = ({
icon,
title,
badge,
}: {
title: string;
icon: React.ReactNode;
badge?: React.ReactNode;
}) => (
<CardTitle>
<Flex spaceItems={{ default: 'spaceItemsSm' }} alignItems={{ default: 'alignItemsCenter' }}>
<FlexItem>
<Icon>{icon}</Icon>
</FlexItem>
<FlexItem>{title}</FlexItem>
<FlexItem>{badge}</FlexItem>
</Flex>
</CardTitle>
);

const DetailsPageCard = (props: CardProps) => <Card isFullHeight {...props} ref={undefined} />;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { Alert, Button, Grid, GridItem, Icon } from '@patternfly/react-core';
import { Alert, Button, Flex, FlexItem, Icon } from '@patternfly/react-core';
import { PencilAltIcon } from '@patternfly/react-icons/dist/js/icons/pencil-alt-icon';
import { Formik, useField } from 'formik';
import * as Yup from 'yup';
Expand Down Expand Up @@ -129,7 +129,7 @@ const DeviceAliasEdit = ({ deviceId, hasLabels, alias: originalAlias = '', onAli
);

return (
<Grid hasGutter>
<Flex direction={{ default: 'column' }} alignItems={{ default: 'alignItemsFlexStart' }} gap={{ default: 'gapSm' }}>
<Formik<DeviceAliasEditValues>
initialValues={{
alias: originalAlias,
Expand All @@ -140,7 +140,7 @@ const DeviceAliasEdit = ({ deviceId, hasLabels, alias: originalAlias = '', onAli
validateOnBlur
onSubmit={onSubmit}
>
<GridItem md={6}>
<FlexItem>
{isEditing ? (
<DeviceAliasInputField
alias={originalAlias}
Expand All @@ -149,31 +149,37 @@ const DeviceAliasEdit = ({ deviceId, hasLabels, alias: originalAlias = '', onAli
toggleIsEditing={toggleIsEditing}
/>
) : (
<>
{originalAlias || t('Untitled')}
<Button
variant="plain"
aria-label={t('Edit alias')}
onClick={toggleIsEditing}
isDisabled={isSubmitting}
icon={
<Icon size="md">
<PencilAltIcon />
</Icon>
}
/>
</>
<Flex
alignItems={{ default: 'alignItemsCenter' }}
gap={{ default: 'gapSm' }}
flexWrap={{ default: 'nowrap' }}
>
<FlexItem>{originalAlias || t('Untitled')}</FlexItem>
<FlexItem>
<Button
variant="plain"
aria-label={t('Edit alias')}
onClick={toggleIsEditing}
isDisabled={isSubmitting}
icon={
<Icon size="md">
<PencilAltIcon />
</Icon>
}
/>
</FlexItem>
</Flex>
)}
</GridItem>
</FlexItem>
</Formik>
<GridItem md={8}>
{submitError && (
{submitError && (
<FlexItem>
<Alert isInline variant="danger" title={t('Device alias could not be updated')}>
{getErrorMessage(submitError)}
</Alert>
)}
</GridItem>
</Grid>
</FlexItem>
)}
</Flex>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import * as React from 'react';
import { CardBody, CardTitle } from '@patternfly/react-core';
import { CardBody, Label } from '@patternfly/react-core';
import CubesIcon from '@patternfly/react-icons/dist/js/icons/cubes-icon';

import { type Device } from '@flightctl/types';
import type { Device } from '@flightctl/types';
import type { DeviceHealthItem } from '../../../hooks/useDeviceOverallHealth';
import { useTranslation } from '../../../hooks/useTranslation';
import { useAppContext } from '../../../hooks/useAppContext';
import { getLifecycleDisabledReason } from '../../../utils/devices';
import { getDeviceAppLifecycleOverrides } from '../../../utils/applicationLifecycle';
import ApplicationsTable from '../../DetailsPage/Tables/ApplicationsTable';
import DetailsPageCard from '../../DetailsPage/DetailsPageCard';
import DetailsPageCard, { DetailsPageCardTitle } from '../../DetailsPage/DetailsPageCard';

type DeviceDetailsTabProps = {
device: Required<Device>;
health: DeviceHealthItem;
refetch?: VoidFunction;
};

const DeviceApplications = ({ device, refetch = () => undefined }: DeviceDetailsTabProps) => {
const DeviceApplications = ({ device, health, refetch = () => undefined }: DeviceDetailsTabProps) => {
const { t } = useTranslation();
const {
router: { useNavigate: useRouterNavigate },
Expand All @@ -32,8 +35,16 @@ const DeviceApplications = ({ device, refetch = () => undefined }: DeviceDetails
);

return (
<DetailsPageCard isCompact>
<CardTitle>{t('Applications')}</CardTitle>
<DetailsPageCard id="device-applications-card" isCompact>
<DetailsPageCardTitle
title={t('Applications')}
icon={<CubesIcon />}
badge={
health.level !== null && (
<Label status={health.level}>{t('{{count}} application errors', { count: health.itemCount })}</Label>
)
}
/>
<CardBody>
<ApplicationsTable
deviceName={device.metadata.name as string}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as React from 'react';
import {
CardBody,
DescriptionList,
DescriptionListDescription,
DescriptionListGroup,
DescriptionListTerm,
} from '@patternfly/react-core';
import TagIcon from '@patternfly/react-icons/dist/js/icons/tag-icon';

import { useTranslation } from '../../../hooks/useTranslation';
import DetailsPageCard, { DetailsPageCardTitle } from '../../DetailsPage/DetailsPageCard';

import './DeviceDetailsTab.css';

const DeviceCustomDataCard = ({ customInfo }: { customInfo: [string, string][] }) => {
const { t } = useTranslation();

return (
<DetailsPageCard>
<DetailsPageCardTitle title={t('Custom data')} icon={<TagIcon />} />
<CardBody>
<DescriptionList isHorizontal isCompact horizontalTermWidthModifier={{ default: '12ch' }}>
{customInfo.map((entry, index) => (
<DescriptionListGroup key={index}>
<DescriptionListTerm>{entry[0]}</DescriptionListTerm>
<DescriptionListDescription>{entry[1]}</DescriptionListDescription>
</DescriptionListGroup>
))}
</DescriptionList>
</CardBody>
</DetailsPageCard>
);
};

export default DeviceCustomDataCard;
Loading
Loading