@kinvolk/headlamp-plugin
Version:
The needed infrastructure for building Headlamp plugins.
101 lines (100 loc) • 4.14 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/*
* Copyright 2025 The Kubernetes Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Icon } from '@iconify/react';
import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import Typography from '@mui/material/Typography';
import React from 'react';
import { useTranslation } from 'react-i18next';
const A8R_ICON_MAP = {
description: { icon: 'mdi:information-outline', labelKey: 'Description' },
owner: { icon: 'mdi:account', labelKey: 'Owner' },
dependencies: { icon: 'mdi:sitemap', labelKey: 'Dependencies' },
chat: { icon: 'mdi:chat', labelKey: 'Chat' },
bugs: { icon: 'mdi:bug', labelKey: 'Bugs' },
logs: { icon: 'mdi:chart-timeline-variant', labelKey: 'Logs' },
documentation: { icon: 'mdi:book-open-page-variant', labelKey: 'Documentation' },
repository: { icon: 'mdi:github', labelKey: 'Repository' },
support: { icon: 'mdi:help-circle', labelKey: 'Support' },
runbook: { icon: 'mdi:script-text', labelKey: 'Runbook' },
incidents: { icon: 'mdi:alert-circle', labelKey: 'Incidents' },
uptime: { icon: 'mdi:check-circle', labelKey: 'Uptime' },
performance: { icon: 'mdi:speedometer', labelKey: 'Performance' },
};
const PREFERRED_ORDER = [
'description',
'owner',
'dependencies',
'chat',
'documentation',
'repository',
'logs',
'bugs',
'support',
'runbook',
'incidents',
'uptime',
'performance',
];
function isValidHttpUrl(value) {
try {
const url = new URL(value);
return url.protocol === 'http:' || url.protocol === 'https:';
}
catch {
return false;
}
}
/**
* Parses Kubernetes resource annotations and extracts a8r.io metadata items.
*
* Filters annotations starting with 'a8r.io/', maps them to display-friendly
* metadata items with icons and labels, and sorts by preferred display order.
*
* @param annotations - Key-value pairs of Kubernetes annotations (defaults to empty object)
* @returns Array of parsed metadata items sorted by preferred display order
*/
export function getA8RMetadata(annotations = {}) {
return Object.entries(annotations)
.filter(([key]) => key.startsWith('a8r.io/'))
.map(([key, value]) => {
const shortKey = key.replace('a8r.io/', '');
const meta = A8R_ICON_MAP[shortKey];
if (!meta)
return null;
return {
key: shortKey,
labelKey: meta.labelKey,
value,
icon: meta.icon,
isLink: isValidHttpUrl(value),
};
})
.filter((v) => Boolean(v))
.sort((a, b) => {
const ai = PREFERRED_ORDER.indexOf(a.key);
const bi = PREFERRED_ORDER.indexOf(b.key);
return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi);
});
}
export default function A8RInfo({ annotations }) {
const { t } = useTranslation(['glossary', 'translation']);
const metadata = React.useMemo(() => getA8RMetadata(annotations), [annotations]);
if (metadata.length === 0)
return null;
return (_jsx(Box, { display: "flex", flexDirection: "column", gap: 1.5, children: metadata.map(item => (_jsxs(Box, { display: "flex", alignItems: "center", children: [_jsx(Icon, { icon: item.icon, width: "20", style: { marginRight: 8 } }), _jsxs(Typography, { variant: "body2", children: [_jsxs("strong", { children: [t(item.labelKey), ":"] }), ' ', item.isLink ? (_jsx(Link, { href: item.value, target: "_blank", rel: "noopener noreferrer", children: item.value })) : (item.value)] })] }, item.key))) }));
}