| undefined,
rangeName: string,
): TimeSeriesMetrics {
let series: TimeSeriesMetrics = {};
if (ranges) {
Object.values(ranges[rangeName] ?? {}).forEach((r) => {
- series = r.range.reduce((a, v) => ({ ...a, [v[0]]: parseFloat(v[1]) }), series);
+ series = r.range.reduce(
+ (a, v) => ({ ...a, [v[0]]: parseFloat(v[1]) }),
+ series,
+ );
});
}
@@ -26,8 +29,17 @@ export async function ConnectedTopicChartsCard({
return (
);
}
diff --git a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/overview/page.tsx b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/overview/page.tsx
index f27f7e267..2adcd28c3 100644
--- a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/overview/page.tsx
+++ b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/overview/page.tsx
@@ -18,21 +18,32 @@ export async function generateMetadata() {
};
}
-export default async function OverviewPage({ params }: { params: KafkaParams }) {
+export default async function OverviewPage({
+ params,
+}: {
+ params: KafkaParams;
+}) {
const kafkaCluster = getKafkaCluster(params.kafkaId, {
- fields: 'name,namespace,creationTimestamp,status,kafkaVersion,nodes,listeners,conditions,metrics'
- }).then(r => r.payload ?? null);
+ fields:
+ "name,namespace,creationTimestamp,status,kafkaVersion,nodes,listeners,conditions,metrics",
+ }).then((r) => r.payload ?? null);
const topics = getTopics(params.kafkaId, { fields: "status", pageSize: 1 });
- const consumerGroups = getConsumerGroups(params.kafkaId, { fields: "groupId,state" });
+ const consumerGroups = getConsumerGroups(params.kafkaId, {
+ fields: "groupId,state",
+ });
const viewedTopics = getViewedTopics().then((topics) =>
topics.filter((t) => t.kafkaId === params.kafkaId),
);
+ console.log("kafkaCLuster", kafkaCluster);
return (
+
}
topicsPartitions={ }
clusterCharts={ }
diff --git a/ui/components/ClusterOverview/TopicChartsCard.tsx b/ui/components/ClusterOverview/TopicChartsCard.tsx
index a6d1c2ca7..23e1daa91 100644
--- a/ui/components/ClusterOverview/TopicChartsCard.tsx
+++ b/ui/components/ClusterOverview/TopicChartsCard.tsx
@@ -22,11 +22,16 @@ export function TopicChartsCard({
isLoading,
incoming,
outgoing,
+ isVirtualKafkaCluster,
}:
- | ({ isLoading: false } & TopicChartsCardProps)
| ({
- isLoading: true;
- } & Partial<{ [key in keyof TopicChartsCardProps]?: undefined }>)) {
+ isLoading: false;
+ isVirtualKafkaCluster: boolean;
+ } & TopicChartsCardProps)
+ | ({
+ isLoading: true;
+ isVirtualKafkaCluster?: boolean;
+ } & Partial<{ [key in keyof TopicChartsCardProps]?: undefined }>)) {
const t = useTranslations();
return (
@@ -42,14 +47,22 @@ export function TopicChartsCard({
{t("topicMetricsCard.topics_bytes_incoming_and_outgoing")}{" "}
-
+
{isLoading ? (
) : (
-
+
)}
diff --git a/ui/components/ClusterOverview/components/ChartIncomingOutgoing.tsx b/ui/components/ClusterOverview/components/ChartIncomingOutgoing.tsx
index 0f93739a2..bdc420eb8 100644
--- a/ui/components/ClusterOverview/components/ChartIncomingOutgoing.tsx
+++ b/ui/components/ClusterOverview/components/ChartIncomingOutgoing.tsx
@@ -19,6 +19,7 @@ import { formatDateTime } from "@/utils/dateTime";
type ChartIncomingOutgoingProps = {
incoming: TimeSeriesMetrics;
outgoing: TimeSeriesMetrics;
+ isVirtualKafkaCluster: boolean;
};
type Datum = {
@@ -31,6 +32,7 @@ type Datum = {
export function ChartIncomingOutgoing({
incoming,
outgoing,
+ isVirtualKafkaCluster,
}: ChartIncomingOutgoingProps) {
const t = useTranslations();
const formatBytes = useFormatBytes();
@@ -41,13 +43,17 @@ export function ChartIncomingOutgoing({
const hasMetrics =
Object.keys(incoming).length > 0 && Object.keys(outgoing).length > 0;
- if (!hasMetrics) {
+ if (!hasMetrics || isVirtualKafkaCluster) {
return (
);
}
diff --git a/ui/components/ClustersTable.tsx b/ui/components/ClustersTable.tsx
index e611efa38..e341084da 100644
--- a/ui/components/ClustersTable.tsx
+++ b/ui/components/ClustersTable.tsx
@@ -6,6 +6,7 @@ import { ButtonLink } from "./Navigation/ButtonLink";
import { Link } from "@/i18n/routing";
import { Truncate } from "@/libs/patternfly/react-core";
import { EmptyStateNoMatchFound } from "./Table/EmptyStateNoMatchFound";
+import { KroxyliciousClusterLabel } from "@/app/[locale]/(authorized)/kafka/[kafkaId]/KroxyliciousClusterLabel";
export const ClusterColumns = [
"name",
@@ -91,18 +92,28 @@ export function ClustersTable({
}}
renderCell={({ key, column, row, Td }) => {
switch (column) {
- case "name":
+ case "name": {
+ const isVirtualCluster =
+ row.meta?.kind === "virtualkafkaclusters.kroxylicious.io";
+
return (
{authenticated ? (
-
-
-
+ <>
+
+
+
+ {isVirtualCluster && }
+ >
) : (
-
+ <>
+ {row.attributes.name}
+ {isVirtualCluster && }
+ >
)}
);
+ }
case "version":
return (
diff --git a/ui/messages/en.json b/ui/messages/en.json
index 0d4f87c23..780e45e38 100644
--- a/ui/messages/en.json
+++ b/ui/messages/en.json
@@ -371,6 +371,7 @@
"Kafka_cluster_details": "Kafka cluster details"
},
"ClusterChartsCard": {
+ "virtual_cluster_metrics_unavailable": "Metrics unavailable for Virtual Kafka Clusters",
"data_unavailable": "Cluster metrics are not available for this cluster. Please check your configuration.",
"cluster_metrics": "Cluster metrics",
"used_disk_space": "Used disk space",
@@ -773,5 +774,9 @@
"permissionType": "Permission"
}
+ },
+ "KroxyliciousCluster": {
+ "label": "Virtual",
+ "tooltip": "A virtual cluster is a logical Kafka endpoint that clients connect to. The proxy forwards requests to a physical Kafka cluster through its filters."
}
}