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
44 changes: 44 additions & 0 deletions src/hooks/useOutdatedHelmCharts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useQuery, UseQueryResult } from "@tanstack/react-query";
import client from "@/api-client/apiClient";
import { API_ENDPOINTS } from "@/api-client/endpoints";
import { HelmReleaseReponse } from "@/types/helm.types";

interface OutdatedHelmChartsParams {
namespace: string;
name: string;
type: string;
}

// Formats each outdated release as "release (namespace): current -> latest" for the McpButton popover.
const formatOutdatedHelmCharts = (data: HelmReleaseReponse): string[] => {
return (data?.helmReleases || [])
.filter((release) => release.latestVersion || release.latestPatchVersion)
.map((release) => {
const newerVersion = release.latestPatchVersion || release.latestVersion;
return `${release.releaseName} (${release.namespace}): ${release.chartVersion} -> ${newerVersion} available`;
});
};

const fetchOutdatedHelmCharts = async (
params: OutdatedHelmChartsParams,
): Promise<string[]> => {
const { data } = await client.get(API_ENDPOINTS.HELM_CHART, {
params: {
namespace: params.namespace,
name: params.name,
type: params.type,
},
});
return formatOutdatedHelmCharts(data);
};

export const useOutdatedHelmCharts = (
params: OutdatedHelmChartsParams,
enabled: boolean = false,
): UseQueryResult<string[], Error> => {
return useQuery({
queryKey: ["outdatedHelmCharts", params],
queryFn: () => fetchOutdatedHelmCharts(params),
enabled,
});
};
5 changes: 5 additions & 0 deletions src/locales/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"paused": "متوقف مؤقتا",
"deployment_issues": "فشل نشر ملف تعريف واحد أو أكثر",
"deployment_in_progress": "الجاري النشر",
"helm_updates_available": "تتوفر تحديثات لواحد أو أكثر من مخططات Helm",
"labels": "الملصقات",
"kubernetes_version": "إصدار Kubernetes",
"event_trigger": "مشغل الحدث",
Expand Down Expand Up @@ -86,6 +87,10 @@
"error": "خطأ",
"no_debug_data": "لا تتوفر بيانات للتحقيق.",
"no_debug_data_relax": "استرخ، لا يوجد شيء للتحقيق فيه هنا!",
"mcp_unavailable": "تعذر الوصول إلى خادم MCP. حاول مرة أخرى لاحقًا.",
"updates": "التحديثات",
"update_available": "تحديث متوفر",
"no_updates_data_relax": "استرخ، كل شيء محدث!",
"verify_installation": "تحقق من التثبيت",
"no_verification_data": "لا تتوفر بيانات التحقق.",
"search_profile_name": "البحث حسب اسم البروفايل",
Expand Down
5 changes: 5 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"paused": "Paused",
"deployment_issues": "One or more profiles failed to deploy",
"deployment_in_progress": "Deployment in progress",
"helm_updates_available": "One or more Helm charts have updates available",
"labels": "Labels",
"kubernetes_version": "Kubernetes version",
"event_trigger": "Event Trigger",
Expand Down Expand Up @@ -86,6 +87,10 @@
"error": "Error",
"no_debug_data": "No debug data available.",
"no_debug_data_relax": "Relax, nothing to debug here!",
"mcp_unavailable": "Unable to reach the MCP server. Try again later.",
"updates": "Updates",
"update_available": "Update Available",
"no_updates_data_relax": "Relax, everything is up to date!",
"verify_installation": "Verify Installation",
"no_verification_data": "No verification data available.",
"search_profile_name": "Search by Name",
Expand Down
5 changes: 5 additions & 0 deletions src/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"paused": "En pause",
"deployment_issues": "Un ou plusieurs profils n'ont pas pu être déployés",
"deployment_in_progress": "Déploiement en cours",
"helm_updates_available": "Une ou plusieurs charts Helm ont des mises à jour disponibles",
"labels": "Étiquettes",
"kubernetes_version": "Version de Kubernetes",
"event_trigger": "Déclencheur d'événement",
Expand Down Expand Up @@ -87,6 +88,10 @@
"error": "Erreur",
"no_debug_data": "Aucune donnée de débogage disponible.",
"no_debug_data_relax": "Détendez-vous, rien à déboguer ici !",
"mcp_unavailable": "Impossible de contacter le serveur MCP. Réessayez plus tard.",
"updates": "Mises à jour",
"update_available": "Mise à jour disponible",
"no_updates_data_relax": "Détendez-vous, tout est à jour !",
"verify_installation": "Vérifier l'installation",
"no_verification_data": "Aucune donnée de vérification disponible.",
"filter_profile_namespace": "Filtrer par espace de noms...",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,11 @@ export const MatchingClusterCard = ({
onClick={() => McpQuery?.refetch()}
isLoading={McpQuery?.isRefetching}
mcpResponse={
McpQuery?.data && McpQuery.data.length > 0
? McpQuery.data
: t("common.relax_no_errors")
McpQuery?.isError
? t("common.mcp_unavailable")
: McpQuery?.data && McpQuery.data.length > 0
? McpQuery.data
: t("common.relax_no_errors")
}
>
{t("common.analyze_pipeline")}
Expand Down
7 changes: 7 additions & 0 deletions src/modules/clusters/cluster-information/ClusterInfoById.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { AddonTypes } from "@/types/addon.types";
import { ErrorQuery } from "@/modules/common/components/feedback/ErrorQuery";
import { useClusterTableInfo } from "@/modules/clusters/cluster-information/hooks/useClusterTableInfo";
import { useMcp } from "@/hooks/useMcp";
import { useOutdatedHelmCharts } from "@/hooks/useOutdatedHelmCharts";
import { getClusterInfoType } from "@/utils/GetClusterInfoType";
import { Button } from "@/lib/components/ui/inputs/button";
import { Tags } from "lucide-react";
Expand All @@ -25,6 +26,11 @@ export function ClusterInfoById() {
"",
"",
);
const outdatedHelmChartsQuery = useOutdatedHelmCharts({
namespace: namespace ?? "",
name: name ?? "",
type: getClusterInfoType(type as ClusterType),
});
const { queries, setPage } = useClusterTableInfo(
namespace as string,
name as string,
Expand Down Expand Up @@ -62,6 +68,7 @@ export function ClusterInfoById() {
InfoQuery.data.managedClusters[0]?.clusterInfo.failureMessage
}
mcpDebugQuery={debugClusterQuery}
outdatedHelmChartsQuery={outdatedHelmChartsQuery}
namespace={InfoQuery.data.managedClusters[0].namespace}
version={InfoQuery.data.managedClusters[0]?.clusterInfo.version}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ export const helmColumns: Column[] = [
className: "hidden sm:table-cell",
keys: "chartVersion/version",
},
{
label: "common.update_available",
className: "hidden sm:table-cell",
keys: AddonTableTypes.HELM_UPDATE,
},
{
label: "common.last_applied",
className: "hidden sm:table-cell",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,21 @@ export const HelmRow = ({ row, columns, onOpenRepo }: HelmRowProps) => {
</TableCell>
);
}
if (key === AddonTableTypes.HELM_UPDATE) {
const newerVersion = row.latestPatchVersion || row.latestVersion;
return (
<TableCell key={colIndex} className={column.className}>
{newerVersion && (
<Badge
variant="outline"
className="border-amber-400 text-amber-600"
>
{newerVersion}
</Badge>
)}
</TableCell>
);
}
if (key === AddonTableTypes.PROFILE) {
return (
<TableCell
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,11 @@ export const ProfileRow = ({ row, columns, onOpenRepo }: ProfileRowProps) => {
onClick={triggerMcp}
isLoading={debugProfileClusterQuery?.isFetching}
mcpResponse={
debugProfileClusterQuery?.data
? debugProfileClusterQuery.data
: t("common.no_debug_data")
debugProfileClusterQuery?.isError
? t("common.mcp_unavailable")
: debugProfileClusterQuery?.data
? debugProfileClusterQuery.data
: t("common.no_debug_data")
}
variant={"highlight"}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button } from "@/lib/components/ui/inputs/button";
import { Check, ChevronLeft } from "lucide-react";
import { ArrowUpCircle, Check, ChevronLeft } from "lucide-react";
import { Badge } from "@/lib/components/ui/data-display/badge";
import { useNavigate } from "react-router-dom";
import { Icons } from "@/lib/components/icons";
Expand All @@ -18,8 +18,15 @@ type ClusterHeadingProps = {
mcpDebugQuery?: {
refetch: () => void;
isFetching: boolean;
isError: boolean;
data?: DebugClusterResponse;
};
outdatedHelmChartsQuery?: {
refetch: () => void;
isFetching: boolean;
isError: boolean;
data?: string[];
};
};

export const ClusterHeading = ({
Expand All @@ -30,12 +37,16 @@ export const ClusterHeading = ({
namespace,
failureMsg,
mcpDebugQuery,
outdatedHelmChartsQuery,
}: ClusterHeadingProps) => {
const { t } = useTranslation();
const navigate = useNavigate();
function triggerMcpDebugQuery() {
mcpDebugQuery?.refetch();
}
function triggerOutdatedHelmChartsQuery() {
outdatedHelmChartsQuery?.refetch();
}
return (
<>
<div className="flex my-4 items-center gap-4">
Expand Down Expand Up @@ -85,13 +96,31 @@ export const ClusterHeading = ({
)}

<div className="hidden items-center gap-2 md:ml-auto md:flex">
<McpButton
onClick={triggerOutdatedHelmChartsQuery}
isLoading={outdatedHelmChartsQuery?.isFetching}
icon={
<ArrowUpCircle className="mr-2 h-4 w-4 mx-auto text-sky-500" />
}
mcpResponse={
outdatedHelmChartsQuery?.isError
? t("common.mcp_unavailable")
: outdatedHelmChartsQuery?.data?.length
? outdatedHelmChartsQuery.data
: t("common.no_updates_data_relax")
}
>
{t("common.updates")}
</McpButton>
<McpButton
onClick={triggerMcpDebugQuery}
isLoading={mcpDebugQuery?.isFetching}
mcpResponse={
mcpDebugQuery?.data?.formattedData?.length
? mcpDebugQuery.data.formattedData
: t("common.no_debug_data_relax")
mcpDebugQuery?.isError
? t("common.mcp_unavailable")
: mcpDebugQuery?.data?.formattedData?.length
? mcpDebugQuery.data.formattedData
: t("common.no_debug_data_relax")
}
>
{t("common.debug")}
Expand Down
14 changes: 13 additions & 1 deletion src/modules/clusters/clusters-list/components/ClusterCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ReadyFlag } from "@/lib/components/ui/data-display/ready-flag";
import { FailedFlag } from "@/lib/components/ui/data-display/failed-flag";
import { Label } from "@/types/cluster.types";
import { appConfig } from "@/config/app";
import { AlertTriangle, Clock, Tags } from "lucide-react";
import { AlertTriangle, ArrowUpCircle, Clock, Tags } from "lucide-react";
import { PauseIcon } from "@radix-ui/react-icons";

interface ClusterCardProps {
Expand All @@ -23,6 +23,7 @@ interface ClusterCardProps {
failureMsg?: string | null;
hasIssues?: boolean;
isProvisioning?: boolean;
hasOutdatedHelmCharts?: boolean;
}

import { useTranslation } from "react-i18next";
Expand All @@ -37,6 +38,7 @@ export const ClusterCard = ({
failureMsg,
hasIssues,
isProvisioning,
hasOutdatedHelmCharts,
}: ClusterCardProps) => {
const { t } = useTranslation();
const labelEntries = Object.entries(labels || {});
Expand Down Expand Up @@ -77,6 +79,16 @@ export const ClusterCard = ({
</TooltipContent>
</Tooltip>
)}
{hasOutdatedHelmCharts && (
<Tooltip>
<TooltipTrigger asChild>
<ArrowUpCircle className="h-3.5 w-3.5 text-sky-500 shrink-0 cursor-default" />
</TooltipTrigger>
<TooltipContent>
<p>{t("common.helm_updates_available")}</p>
</TooltipContent>
</Tooltip>
)}
</p>
<p className="text-sm text-muted-foreground py-1">
{t("common.version")}:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const ClusterList: FC<ClusterListProps> = ({
labels={cluster.clusterInfo.labels}
hasIssues={cluster.hasIssues}
isProvisioning={cluster.isProvisioning}
hasOutdatedHelmCharts={cluster.hasOutdatedHelmCharts}
/>
</div>
))}
Expand Down
4 changes: 3 additions & 1 deletion src/modules/common/components/actions/VerifyInstallation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export function VerifyInstallation() {
}
isLoading={installationQuery?.isFetching}
mcpResponse={
installationQuery?.data?.details || t("common.no_verification_data")
installationQuery?.isError
? t("common.mcp_unavailable")
: installationQuery?.data?.details || t("common.no_verification_data")
}
>
{installationQuery?.data?.is_correctly_installed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,11 @@ export const ClusterMatchCard = ({
onClick={() => McpQuery?.refetch()}
isLoading={McpQuery?.isRefetching}
mcpResponse={
McpQuery?.data && McpQuery.data.length > 0
? McpQuery.data
: t("common.relax_no_errors")
McpQuery?.isError
? t("common.mcp_unavailable")
: McpQuery?.data && McpQuery.data.length > 0
? McpQuery.data
: t("common.relax_no_errors")
}
>
{t("common.analyze_pipeline")}
Expand Down
3 changes: 3 additions & 0 deletions src/types/addon.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ export type AddonData = {
profileType?: string;
profileNames?: string[];
repoURL?: string;
latestVersion?: string;
latestPatchVersion?: string;
lastCheckedTime?: string;
helmReleases?: AddonTableTypes[];
totalHelmReleases?: number;
resources?: AddonTableTypes[];
Expand Down
1 change: 1 addition & 0 deletions src/types/addonTable.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export enum AddonTableTypes {
GROUP = "group/version",
KIND = "kind",
STATUS = "status",
HELM_UPDATE = "latestVersion/latestPatchVersion",
ACTION = "action",
ICON = "icon",
FAILURE_MESSAGE = "failureMessage",
Expand Down
1 change: 1 addition & 0 deletions src/types/cluster.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface ClusterInfoType {
kind?: string;
hasIssues?: boolean;
isProvisioning?: boolean;
hasOutdatedHelmCharts?: boolean;
clusterInfo: {
labels: Label;
version: string;
Expand Down
3 changes: 3 additions & 0 deletions src/types/helm.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ export interface HelmReleaseType {
icon: string;
lastAppliedTime: string;
profileName: string;
latestVersion?: string;
latestPatchVersion?: string;
lastCheckedTime?: string;
}

export type HelmReleaseReponse = {
Expand Down