mirror of
https://github.com/komodorio/helm-dashboard.git
synced 2026-08-31 08:39:24 +00:00
Enabled recommended-requiring-type-checking as result type fixes provided (#632)
* Enabled recommended-requiring-type-checking * from .cjs to .js * check * check * check * check * A lot of types aligned and refactored * More strict types * Improvement * Improvements * Improvements * Fixed routs * Fixed import types
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
* @see https://storybook.js.org/docs/react/writing-stories/introduction
|
||||
*/
|
||||
|
||||
import { Meta } from "@storybook/react-vite";
|
||||
import type { Meta } from "@storybook/react-vite";
|
||||
import Badge from "./Badge";
|
||||
|
||||
// We set the metadata for the story.
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*
|
||||
*
|
||||
*/
|
||||
import { JSX, ReactNode } from "react";
|
||||
import type { JSX, ReactNode } from "react";
|
||||
|
||||
export type BadgeCode = "success" | "warning" | "error" | "unknown";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import Button from "./Button";
|
||||
|
||||
const meta = {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
*
|
||||
*
|
||||
*/
|
||||
import { HTMLAttributes, JSX, ReactNode } from "react";
|
||||
import type { HTMLAttributes, JSX, ReactNode } from "react";
|
||||
|
||||
// this is a type declaration for the action prop.
|
||||
// it is a function that takes a string as an argument and returns void.
|
||||
|
||||
@@ -2,7 +2,8 @@ import { AppContextProvider } from "../context/AppContext";
|
||||
import ClustersList from "./ClustersList";
|
||||
import { BrowserRouter } from "react-router";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { Release } from "../data/types";
|
||||
import type { Release } from "../data/types";
|
||||
import { DeploymentStatus } from "./common/StatusLabel";
|
||||
|
||||
type ClustersListProps = {
|
||||
onClusterChange: (clusterName: string) => void;
|
||||
@@ -17,7 +18,7 @@ const generateTestReleaseData = (): Release => ({
|
||||
namespace: "default",
|
||||
revision: 1,
|
||||
updated: "2024-01-23T15:37:35.0992836+02:00",
|
||||
status: "deployed",
|
||||
status: DeploymentStatus.DEPLOYED,
|
||||
chart: "helm-dashboard-0.1.10",
|
||||
chart_name: "helm-dashboard",
|
||||
chart_ver: "0.1.10",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import ClustersList from "./ClustersList";
|
||||
|
||||
const meta = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useEffectEvent, useMemo, useState } from "react";
|
||||
import { Cluster, Release } from "../data/types";
|
||||
import { useEffect, useEffectEvent, useMemo } from "react";
|
||||
import type { Cluster, Release } from "../data/types";
|
||||
import apiService from "../API/apiService";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import useCustomSearchParams from "../hooks/useCustomSearchParams";
|
||||
@@ -43,21 +43,19 @@ function ClustersList({
|
||||
}: ClustersListProps) {
|
||||
const { upsertSearchParams, removeSearchParam } = useCustomSearchParams();
|
||||
const { clusterMode } = useAppContext();
|
||||
const [sortedClusters, setSortedClusters] = useState<Cluster[]>([]);
|
||||
|
||||
const { data: clusters, isSuccess } = useQuery<Cluster[]>({
|
||||
const { data: clusters = [], isSuccess } = useQuery<Cluster[]>({
|
||||
queryKey: ["clusters", selectedCluster],
|
||||
queryFn: apiService.getClusters,
|
||||
select: (data) =>
|
||||
data?.sort((a, b) =>
|
||||
getCleanClusterName(a.Name).localeCompare(getCleanClusterName(b.Name))
|
||||
),
|
||||
});
|
||||
|
||||
const onSuccess = useEffectEvent((clusters: Cluster[]) => {
|
||||
const sortedData = [...clusters].sort((a, b) =>
|
||||
getCleanClusterName(a.Name).localeCompare(getCleanClusterName(b.Name))
|
||||
);
|
||||
setSortedClusters(sortedData);
|
||||
|
||||
if (sortedData && sortedData.length > 0 && !selectedCluster) {
|
||||
onClusterChange(sortedData[0].Name);
|
||||
if (clusters && clusters.length && !selectedCluster) {
|
||||
onClusterChange(clusters[0].Name);
|
||||
}
|
||||
|
||||
if (selectedCluster) {
|
||||
@@ -111,10 +109,10 @@ function ClustersList({
|
||||
{!clusterMode ? (
|
||||
<>
|
||||
<label className="font-bold">Clusters</label>
|
||||
{sortedClusters?.map((cluster) => {
|
||||
{clusters?.map((cluster) => {
|
||||
return (
|
||||
<span
|
||||
key={cluster.Name}
|
||||
key={cluster.Name + cluster.Namespace}
|
||||
className="data-cy-clusterName mt-2 flex items-center text-xs"
|
||||
>
|
||||
<input
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { HD_RESOURCE_CONDITION_TYPE } from "../../API/releases";
|
||||
import { Tooltip } from "flowbite-react";
|
||||
import { ReleaseHealthStatus } from "../../data/types";
|
||||
import type { ReleaseHealthStatus } from "../../data/types";
|
||||
|
||||
interface Props {
|
||||
statusData: ReleaseHealthStatus[];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Meta } from "@storybook/react-vite";
|
||||
import type { Meta } from "@storybook/react-vite";
|
||||
import InstalledPackageCard from "./InstalledPackageCard";
|
||||
|
||||
const meta = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import { Release, ReleaseHealthStatus } from "../../data/types";
|
||||
import type { Release, ReleaseHealthStatus } from "../../data/types";
|
||||
import { BsArrowUpCircleFill, BsPlusCircleFill } from "react-icons/bs";
|
||||
import { getAge } from "../../timeUtils";
|
||||
import StatusLabel, {
|
||||
@@ -13,7 +13,7 @@ import HelmGrayIcon from "../../assets/helm-gray-50.svg";
|
||||
import Spinner from "../Spinner";
|
||||
import { useGetLatestVersion } from "../../API/releases";
|
||||
import { isNewerVersion } from "../../utils";
|
||||
import { LatestChartVersion } from "../../API/interfaces";
|
||||
import type { LatestChartVersion } from "../../API/interfaces";
|
||||
import useNavigateWithSearchParams from "../../hooks/useNavigateWithSearchParams";
|
||||
import { useInView } from "react-intersection-observer";
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function InstalledPackageCard({
|
||||
queryKey: ["chartName", release.chartName],
|
||||
});
|
||||
|
||||
const { data: statusData } = useQuery<ReleaseHealthStatus[] | null>({
|
||||
const { data: statusData = [], isLoading } = useQuery<ReleaseHealthStatus[]>({
|
||||
queryKey: ["resourceStatus", release],
|
||||
queryFn: () => apiService.getResourceStatus({ release }),
|
||||
enabled: inView,
|
||||
@@ -61,14 +61,21 @@ export default function InstalledPackageCard({
|
||||
setIsMouseOver(false);
|
||||
};
|
||||
|
||||
const handleOnClick = () => {
|
||||
const onClick = async () => {
|
||||
const { name, namespace } = release;
|
||||
navigate(`/${namespace}/${name}/installed/revision/${release.revision}`, {
|
||||
state: release,
|
||||
});
|
||||
await navigate(
|
||||
`/${namespace}/${name}/installed/revision/${release.revision}`,
|
||||
{
|
||||
state: release,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const statusColor = getStatusColor(release.status as DeploymentStatus);
|
||||
const handleClick = () => {
|
||||
void onClick();
|
||||
};
|
||||
|
||||
const statusColor = getStatusColor(release.status);
|
||||
const borderLeftColor: { [key: string]: string } = {
|
||||
[DeploymentStatus.DEPLOYED]: "border-l-border-deployed",
|
||||
[DeploymentStatus.FAILED]: "border-l-text-danger",
|
||||
@@ -85,7 +92,7 @@ export default function InstalledPackageCard({
|
||||
}`}
|
||||
onMouseOver={handleMouseOver}
|
||||
onMouseOut={handleMouseOut}
|
||||
onClick={handleOnClick}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<img
|
||||
src={release.icon || HelmGrayIcon}
|
||||
@@ -118,10 +125,10 @@ export default function InstalledPackageCard({
|
||||
{release.description}
|
||||
</div>
|
||||
<div className="col-span-3 mr-2">
|
||||
{statusData ? (
|
||||
<HealthStatus statusData={statusData} />
|
||||
) : (
|
||||
{isLoading ? (
|
||||
<Spinner size={4} />
|
||||
) : (
|
||||
<HealthStatus statusData={statusData} />
|
||||
)}
|
||||
</div>
|
||||
<div className="items col-span-2 flex flex-col text-muted">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Meta } from "@storybook/react-vite";
|
||||
import type { Meta } from "@storybook/react-vite";
|
||||
import InstalledPackagesHeader from "./InstalledPackagesHeader";
|
||||
|
||||
const meta = {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import HeaderLogo from "../../assets/packges-header.svg";
|
||||
import { Release } from "../../data/types";
|
||||
import type { Release } from "../../data/types";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
|
||||
type InstalledPackagesHeaderProps = {
|
||||
filteredReleases?: Release[];
|
||||
setFilterKey: React.Dispatch<React.SetStateAction<string>>;
|
||||
setFilterKey: Dispatch<SetStateAction<string>>;
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Meta } from "@storybook/react-vite";
|
||||
import type { Meta } from "@storybook/react-vite";
|
||||
import InstalledPackagesList from "./InstalledPackagesList";
|
||||
|
||||
const meta = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import InstalledPackageCard from "./InstalledPackageCard";
|
||||
import { Release } from "../../data/types";
|
||||
import type { Release } from "../../data/types";
|
||||
|
||||
type InstalledPackagesListProps = {
|
||||
filteredReleases: Release[];
|
||||
|
||||
@@ -27,13 +27,9 @@ const LinkWithSearchParams = ({
|
||||
prefixedUrl = `/${encodeURIComponent(context)}${to}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<NavLink
|
||||
data-cy="navigation-link"
|
||||
to={`${prefixedUrl}/?${params.toString()}`}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
const url = `${prefixedUrl}/?${params.toString()}`;
|
||||
|
||||
return <NavLink data-cy="navigation-link" to={url} {...props} />;
|
||||
};
|
||||
|
||||
export default LinkWithSearchParams;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* The default story renders the component with the default props.
|
||||
*/
|
||||
|
||||
import { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { action } from "storybook/actions";
|
||||
import SelectMenu, { SelectMenuItem } from "./SelectMenu";
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
*
|
||||
*
|
||||
*/
|
||||
import { JSX } from "react";
|
||||
import type { JSX, ReactNode } from "react";
|
||||
|
||||
// define the SelectMenuItem type:
|
||||
// This is an object with a label and id.
|
||||
@@ -39,7 +39,7 @@ export interface SelectMenuItemProps {
|
||||
|
||||
export interface SelectMenuProps {
|
||||
header: string;
|
||||
children: React.ReactNode;
|
||||
children: ReactNode;
|
||||
selected: number;
|
||||
onSelect: (id: number) => void;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { StoryFn, Meta } from "@storybook/react-vite";
|
||||
import type { StoryFn, Meta } from "@storybook/react-vite";
|
||||
import ShutDownButton from "./ShutDownButton";
|
||||
|
||||
const meta = {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { BsPower } from "react-icons/bs";
|
||||
|
||||
import Modal from "./modal/Modal";
|
||||
import { useShutdownHelmDashboard } from "../API/other";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Meta } from "@storybook/react-vite";
|
||||
import type { Meta } from "@storybook/react-vite";
|
||||
import Tabs from "./Tabs";
|
||||
|
||||
const meta = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactNode } from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import useCustomSearchParams from "../hooks/useCustomSearchParams";
|
||||
|
||||
export interface Tab {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Meta } from "@storybook/react-vite";
|
||||
import type { Meta } from "@storybook/react-vite";
|
||||
import TabsBar from "./TabsBar";
|
||||
|
||||
const meta = {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
*
|
||||
*
|
||||
*/
|
||||
import { JSX } from "react";
|
||||
import type { JSX } from "react";
|
||||
|
||||
interface TabsBarProps {
|
||||
tabs: Array<{ name: string; component: JSX.Element }>;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* the first story simply renders the component with the default props.
|
||||
*/
|
||||
|
||||
import { Meta } from "@storybook/react-vite";
|
||||
import type { Meta } from "@storybook/react-vite";
|
||||
import TextInput from "./TextInput";
|
||||
|
||||
const meta = {
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
* @return JSX.Element
|
||||
*
|
||||
*/
|
||||
import { JSX } from "react";
|
||||
import type { ChangeEvent, JSX } from "react";
|
||||
|
||||
export interface TextInputProps {
|
||||
label: string;
|
||||
placeholder: string;
|
||||
isMandatory?: boolean;
|
||||
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||
}
|
||||
|
||||
export default function TextInput(props: TextInputProps): JSX.Element {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type ReactElement, cloneElement, HTMLAttributes } from "react";
|
||||
import type { HTMLAttributes } from "react";
|
||||
import { type ReactElement, cloneElement } from "react";
|
||||
|
||||
export default function Tooltip({
|
||||
id,
|
||||
@@ -15,7 +16,7 @@ export default function Tooltip({
|
||||
element as ReactElement<HTMLAttributes<HTMLElement>>,
|
||||
{
|
||||
"data-tooltip-target": id,
|
||||
} as HTMLAttributes<HTMLElement>
|
||||
} as unknown as HTMLAttributes<HTMLElement>
|
||||
)}
|
||||
<div
|
||||
id={id}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Meta, StoryFn } from "@storybook/react-vite";
|
||||
import type { Meta, StoryFn } from "@storybook/react-vite";
|
||||
import { Troubleshoot } from "./Troubleshoot";
|
||||
|
||||
const meta = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Meta } from "@storybook/react-vite";
|
||||
import type { Meta } from "@storybook/react-vite";
|
||||
|
||||
import { Button } from "./Button";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Meta } from "@storybook/react-vite";
|
||||
import type { Meta } from "@storybook/react-vite";
|
||||
import { action } from "storybook/actions";
|
||||
import DropDown from "./DropDown";
|
||||
import { BsSlack, BsGithub } from "react-icons/bs";
|
||||
@@ -10,7 +10,7 @@ const meta = {
|
||||
*/
|
||||
title: "DropDown",
|
||||
component: DropDown,
|
||||
} as Meta<typeof DropDown>;
|
||||
} as unknown as Meta<typeof DropDown>;
|
||||
|
||||
export default meta;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Fragment, ReactNode, useEffect, useRef, useState } from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import { Fragment, useEffect, useRef, useState } from "react";
|
||||
import ArrowDownIcon from "../../assets/arrow-down-icon.svg";
|
||||
|
||||
export type DropDownItem = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Meta } from "@storybook/react-vite";
|
||||
import type { Meta } from "@storybook/react-vite";
|
||||
|
||||
import { Header } from "./Header";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Meta } from "@storybook/react-vite";
|
||||
import type { Meta } from "@storybook/react-vite";
|
||||
import StatusLabel, { DeploymentStatus } from "./StatusLabel";
|
||||
|
||||
const meta = {
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { AiOutlineReload } from "react-icons/ai";
|
||||
|
||||
type StatusLabelProps = {
|
||||
status: string;
|
||||
isRollback?: boolean;
|
||||
};
|
||||
|
||||
export enum DeploymentStatus {
|
||||
DEPLOYED = "deployed",
|
||||
FAILED = "failed",
|
||||
@@ -12,6 +7,11 @@ export enum DeploymentStatus {
|
||||
SUPERSEDED = "superseded",
|
||||
}
|
||||
|
||||
type StatusLabelProps = {
|
||||
status: DeploymentStatus;
|
||||
isRollback?: boolean;
|
||||
};
|
||||
|
||||
export function getStatusColor(status: DeploymentStatus) {
|
||||
if (status === DeploymentStatus.DEPLOYED) return "text-deployed";
|
||||
if (status === DeploymentStatus.FAILED) return "text-failed";
|
||||
@@ -20,7 +20,7 @@ export function getStatusColor(status: DeploymentStatus) {
|
||||
}
|
||||
|
||||
function StatusLabel({ status, isRollback }: StatusLabelProps) {
|
||||
const statusColor = getStatusColor(status as DeploymentStatus);
|
||||
const statusColor = getStatusColor(status);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { StoryFn, Meta } from "@storybook/react-vite";
|
||||
import type { StoryFn, Meta } from "@storybook/react-vite";
|
||||
import AddRepositoryModal from "./AddRepositoryModal";
|
||||
|
||||
const meta = {
|
||||
|
||||
@@ -36,7 +36,7 @@ function AddRepositoryModal({ isOpen, onClose }: AddRepositoryModalProps) {
|
||||
const navigate = useNavigate();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const addRepository = () => {
|
||||
const addRepository = async () => {
|
||||
const body = new FormData();
|
||||
body.append("name", formData.name ?? "");
|
||||
body.append("url", formData.url ?? "");
|
||||
@@ -45,32 +45,34 @@ function AddRepositoryModal({ isOpen, onClose }: AddRepositoryModalProps) {
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
apiService
|
||||
.fetchWithDefaults<void>("/api/helm/repositories", {
|
||||
try {
|
||||
await apiService.fetchWithDefaults<void>("/api/helm/repositories", {
|
||||
method: "POST",
|
||||
body,
|
||||
})
|
||||
.then(() => {
|
||||
setIsLoading(false);
|
||||
onClose();
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["helm", "repositories"],
|
||||
});
|
||||
setSelectedRepo(formData.name || "");
|
||||
navigate(`/repository/${formData.name}`, {
|
||||
replace: true,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
alertError.setShowErrorModal({
|
||||
title: "Failed to add repo",
|
||||
msg: error.message,
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
});
|
||||
|
||||
setIsLoading(false);
|
||||
onClose();
|
||||
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: ["helm", "repositories"],
|
||||
});
|
||||
setSelectedRepo(formData.name || "");
|
||||
await navigate(`/repository/${formData.name}`, {
|
||||
replace: true,
|
||||
});
|
||||
} catch (err) {
|
||||
alertError.setShowErrorModal({
|
||||
title: "Failed to add repo",
|
||||
msg: err instanceof Error ? err.message : String(err),
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddRepository = () => {
|
||||
void addRepository();
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -84,7 +86,7 @@ function AddRepositoryModal({ isOpen, onClose }: AddRepositoryModalProps) {
|
||||
<button
|
||||
data-cy="add-chart-repository-button"
|
||||
className="flex cursor-pointer items-center rounded-lg bg-primary px-3 py-1.5 text-center text-base font-medium text-white hover:bg-add-repo focus:ring-4 focus:ring-blue-300 focus:outline-hidden disabled:bg-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
|
||||
onClick={addRepository}
|
||||
onClick={handleAddRepository}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading && <Spinner size={4} />}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { action } from "storybook/actions";
|
||||
import { Meta } from "@storybook/react-vite";
|
||||
import type { Meta } from "@storybook/react-vite";
|
||||
import ErrorModal from "./ErrorModal";
|
||||
|
||||
const meta = {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useParams } from "react-router";
|
||||
import { useEffect, useEffectEvent, useMemo, useState } from "react";
|
||||
import type { VersionData } from "../../../API/releases";
|
||||
import {
|
||||
useChartReleaseValues,
|
||||
useGetReleaseManifest,
|
||||
useGetVersions,
|
||||
useVersionData,
|
||||
VersionData,
|
||||
} from "../../../API/releases";
|
||||
import Modal, { ModalButtonStyle } from "../Modal";
|
||||
import { GeneralDetails } from "./GeneralDetails";
|
||||
@@ -17,11 +17,11 @@ import { isNoneEmptyArray } from "../../../utils";
|
||||
import useCustomSearchParams from "../../../hooks/useCustomSearchParams";
|
||||
import { useChartRepoValues } from "../../../API/repositories";
|
||||
import { useDiffData } from "../../../API/shared";
|
||||
import { InstallChartModalProps } from "../../../data/types";
|
||||
import type { InstallChartModalProps } from "../../../data/types";
|
||||
import { DefinedValues } from "./DefinedValues";
|
||||
import apiService from "../../../API/apiService";
|
||||
import { InstallUpgradeTitle } from "./InstallUpgradeTitle";
|
||||
import { LatestChartVersion } from "../../../API/interfaces";
|
||||
import type { LatestChartVersion } from "../../../API/interfaces";
|
||||
|
||||
export const InstallReleaseChartModal = ({
|
||||
isOpen,
|
||||
@@ -129,7 +129,7 @@ export const InstallReleaseChartModal = ({
|
||||
});
|
||||
|
||||
// Confirm method (install)
|
||||
const setReleaseVersionMutation = useMutation<VersionData>({
|
||||
const setReleaseVersionMutation = useMutation<VersionData, Error>({
|
||||
mutationKey: [
|
||||
"setVersion",
|
||||
namespace,
|
||||
@@ -148,20 +148,23 @@ export const InstallReleaseChartModal = ({
|
||||
}
|
||||
formData.append("version", selectedVersion || "");
|
||||
formData.append("values", userValues || releaseValues || ""); // if userValues is empty, we use the release values
|
||||
return await apiService.fetchWithDefaults(
|
||||
`/api/helm/releases/${
|
||||
namespace ? namespace : "default"
|
||||
}${`/${releaseName}`}`,
|
||||
{
|
||||
const url = `/api/helm/releases/${
|
||||
namespace ? namespace : "default"
|
||||
}/${releaseName}`;
|
||||
|
||||
return await apiService.fetchWithSafeDefaults<VersionData>({
|
||||
url,
|
||||
options: {
|
||||
method: "post",
|
||||
body: formData,
|
||||
}
|
||||
);
|
||||
},
|
||||
fallback: { version: "", urls: [""] },
|
||||
});
|
||||
},
|
||||
onSuccess: async (response) => {
|
||||
onClose();
|
||||
setSelectedVersionData({ version: "", urls: [] }); //cleanup
|
||||
navigate(
|
||||
await navigate(
|
||||
`/${
|
||||
namespace ? namespace : "default"
|
||||
}/${releaseName}/installed/revision/${response.version}`
|
||||
|
||||
@@ -10,11 +10,11 @@ import useNavigateWithSearchParams from "../../../hooks/useNavigateWithSearchPar
|
||||
import { VersionToInstall } from "./VersionToInstall";
|
||||
import { isNoneEmptyArray } from "../../../utils";
|
||||
import { useDiffData } from "../../../API/shared";
|
||||
import { InstallChartModalProps } from "../../../data/types";
|
||||
import type { InstallChartModalProps } from "../../../data/types";
|
||||
import { DefinedValues } from "./DefinedValues";
|
||||
import apiService from "../../../API/apiService";
|
||||
import { InstallUpgradeTitle } from "./InstallUpgradeTitle";
|
||||
import { LatestChartVersion } from "../../../API/interfaces";
|
||||
import type { LatestChartVersion } from "../../../API/interfaces";
|
||||
|
||||
export const InstallRepoChartModal = ({
|
||||
isOpen,
|
||||
@@ -135,18 +135,21 @@ export const InstallRepoChartModal = ({
|
||||
formData.append("values", userValues);
|
||||
formData.append("name", releaseName || "");
|
||||
|
||||
return await apiService.fetchWithDefaults(
|
||||
`/api/helm/releases/${namespace ? namespace : "default"}`,
|
||||
{
|
||||
return await apiService.fetchWithSafeDefaults({
|
||||
url: `/api/helm/releases/${namespace ? namespace : "default"}`,
|
||||
options: {
|
||||
method: "post",
|
||||
body: formData,
|
||||
}
|
||||
);
|
||||
},
|
||||
fallback: { namespace: "", name: "" },
|
||||
});
|
||||
},
|
||||
|
||||
onSuccess: async (response: { namespace: string; name: string }) => {
|
||||
onClose();
|
||||
navigate(`/${response.namespace}/${response.name}/installed/revision/1`);
|
||||
await navigate(
|
||||
`/${response.namespace}/${response.name}/installed/revision/1`
|
||||
);
|
||||
},
|
||||
onError: (error) => {
|
||||
setInstallError(error?.message || "Failed to update");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FC } from "react";
|
||||
import type { FC } from "react";
|
||||
|
||||
interface InstallUpgradeProps {
|
||||
isUpgrade: boolean;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { FC, useMemo, useState } from "react";
|
||||
import Select, { components, GroupBase, SingleValueProps } from "react-select";
|
||||
import type { FC } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import type { GroupBase, SingleValueProps } from "react-select";
|
||||
import Select, { components } from "react-select";
|
||||
import { BsCheck2 } from "react-icons/bs";
|
||||
import { NonEmptyArray } from "../../../data/types";
|
||||
import type { NonEmptyArray } from "../../../data/types";
|
||||
|
||||
interface Version {
|
||||
repository: string;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { action } from "storybook/actions";
|
||||
import { StoryObj, StoryFn, Meta } from "@storybook/react-vite";
|
||||
import Modal, { ModalAction, ModalButtonStyle } from "./Modal";
|
||||
import type { StoryObj, StoryFn, Meta } from "@storybook/react-vite";
|
||||
import type { ModalAction } from "./Modal";
|
||||
import Modal, { ModalButtonStyle } from "./Modal";
|
||||
|
||||
const meta = {
|
||||
/* 👇 The title prop is optional.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PropsWithChildren, ReactNode } from "react";
|
||||
import type { PropsWithChildren, ReactNode } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import Spinner from "../Spinner";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Meta } from "@storybook/react-vite";
|
||||
import type { Meta } from "@storybook/react-vite";
|
||||
import ChartViewer from "./ChartViewer";
|
||||
|
||||
//👇 This default export determines where your story goes in the story list
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import { Chart } from "../../data/types";
|
||||
import type { Chart } from "../../data/types";
|
||||
import { InstallRepoChartModal } from "../modal/InstallChartModal/InstallRepoChartModal";
|
||||
|
||||
type ChartViewerProps = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { StoryFn, Meta } from "@storybook/react-vite";
|
||||
import type { StoryFn, Meta } from "@storybook/react-vite";
|
||||
import RepositoriesList from "./RepositoriesList";
|
||||
|
||||
const meta = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMemo } from "react";
|
||||
import AddRepositoryModal from "../modal/AddRepositoryModal";
|
||||
import { Repository } from "../../data/types";
|
||||
import type { Repository } from "../../data/types";
|
||||
import useCustomSearchParams from "../../hooks/useCustomSearchParams";
|
||||
|
||||
type RepositoriesListProps = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { StoryFn, Meta } from "@storybook/react-vite";
|
||||
import type { StoryFn, Meta } from "@storybook/react-vite";
|
||||
import RepositoryViewer from "./RepositoryViewer";
|
||||
|
||||
const meta = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BsTrash3, BsArrowRepeat } from "react-icons/bs";
|
||||
import { Chart, Repository } from "../../data/types";
|
||||
import type { Chart, Repository } from "../../data/types";
|
||||
import ChartViewer from "./ChartViewer";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import apiService from "../../API/apiService";
|
||||
@@ -52,9 +52,9 @@ function RepositoryViewer({ repository }: RepositoryViewerProps) {
|
||||
method: "DELETE",
|
||||
}
|
||||
);
|
||||
navigate("/repository", { replace: true });
|
||||
await navigate("/repository", { replace: true });
|
||||
setSelectedRepo("");
|
||||
queryClient.invalidateQueries({
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: ["helm", "repositories"],
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -104,7 +104,7 @@ function RepositoryViewer({ repository }: RepositoryViewerProps) {
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
removeRepository();
|
||||
void removeRepository();
|
||||
}}
|
||||
>
|
||||
<span className="flex h-8 items-center gap-2 rounded-sm border border-gray-300 bg-white px-5 py-1 text-sm font-semibold">
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
BsArrowUp,
|
||||
BsCheckCircle,
|
||||
} from "react-icons/bs";
|
||||
import { Release, ReleaseRevision } from "../../data/types";
|
||||
import type { ReleaseRevision } from "../../data/types";
|
||||
import StatusLabel, { DeploymentStatus } from "../common/StatusLabel";
|
||||
import { useNavigate, useParams, useSearchParams } from "react-router";
|
||||
import {
|
||||
@@ -39,7 +39,7 @@ type RevisionTagProps = {
|
||||
};
|
||||
|
||||
type RevisionDetailsProps = {
|
||||
release: Release;
|
||||
release: ReleaseRevision;
|
||||
installedRevision: ReleaseRevision;
|
||||
isLatest: boolean;
|
||||
latestRevision: number;
|
||||
@@ -105,7 +105,7 @@ export default function RevisionDetails({
|
||||
setShowTestResults(false);
|
||||
setShowErrorModal({
|
||||
title: "Failed to run tests for chart " + chart,
|
||||
msg: (error as Error).message,
|
||||
msg: error.message,
|
||||
});
|
||||
console.error("Failed to execute test for chart", error);
|
||||
},
|
||||
@@ -135,7 +135,7 @@ export default function RevisionDetails({
|
||||
};
|
||||
|
||||
const displayTestResults = () => {
|
||||
if (!testResults || (testResults as []).length === 0) {
|
||||
if (!testResults || !testResults.length) {
|
||||
return (
|
||||
<div>
|
||||
Tests executed successfully
|
||||
@@ -147,7 +147,7 @@ export default function RevisionDetails({
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
{(testResults as string).split("\n").map((line, index) => (
|
||||
{testResults.split("\n").map((line, index) => (
|
||||
<div key={index} className="mb-2">
|
||||
{line}
|
||||
<br />
|
||||
@@ -160,6 +160,13 @@ export default function RevisionDetails({
|
||||
|
||||
const Header = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const addRepo = async () => {
|
||||
await navigate(
|
||||
`/repository?add_repo=true&repo_url=${latestVerData?.[0]?.urls[0]}&repo_name=${latestVerData?.[0]?.repository}`
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<header className="flex flex-wrap justify-between">
|
||||
<h1 className="float-left mb-1 font-roboto-slab text-3xl font-semibold">
|
||||
@@ -206,9 +213,7 @@ export default function RevisionDetails({
|
||||
{latestVerData?.[0]?.isSuggestedRepo ? (
|
||||
<span
|
||||
onClick={() => {
|
||||
navigate(
|
||||
`/repository?add_repo=true&repo_url=${latestVerData[0].urls[0]}&repo_name=${latestVerData[0].repository}`
|
||||
);
|
||||
void addRepo();
|
||||
}}
|
||||
className="cursor-pointer text-sm text-blue-600 underline"
|
||||
>
|
||||
@@ -216,7 +221,7 @@ export default function RevisionDetails({
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
onClick={() => refetchLatestVersion()}
|
||||
onClick={() => void refetchLatestVersion()}
|
||||
className="cursor-pointer text-xs underline"
|
||||
>
|
||||
Check for new version
|
||||
@@ -317,7 +322,7 @@ const Rollback = ({
|
||||
release,
|
||||
installedRevision,
|
||||
}: {
|
||||
release: Release;
|
||||
release: ReleaseRevision;
|
||||
installedRevision: ReleaseRevision;
|
||||
}) => {
|
||||
const { chart, namespace, revision } = useParams();
|
||||
@@ -328,8 +333,8 @@ const Rollback = ({
|
||||
|
||||
const { mutate: rollbackRelease, isPending: isRollingBackRelease } =
|
||||
useRollbackRelease({
|
||||
onSuccess: () => {
|
||||
navigate(
|
||||
onSuccess: async () => {
|
||||
await navigate(
|
||||
`/${namespace}/${chart}/installed/revision/${revisionInt + 1}`
|
||||
);
|
||||
window.location.reload();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ChangeEvent, useMemo, useState, useRef, useEffect } from "react";
|
||||
import type { ChangeEvent } from "react";
|
||||
import { useMemo, useState, useRef, useEffect } from "react";
|
||||
import { Diff2HtmlUI } from "diff2html/lib/ui/js/diff2html-ui-slim.js";
|
||||
import { useGetReleaseInfoByType } from "../../API/releases";
|
||||
import { useParams } from "react-router";
|
||||
|
||||
@@ -3,11 +3,8 @@ import { useParams } from "react-router";
|
||||
import hljs from "highlight.js";
|
||||
import { RiExternalLinkLine } from "react-icons/ri";
|
||||
|
||||
import {
|
||||
StructuredResources,
|
||||
useGetResourceDescription,
|
||||
useGetResources,
|
||||
} from "../../API/releases";
|
||||
import type { StructuredResources } from "../../API/releases";
|
||||
import { useGetResourceDescription, useGetResources } from "../../API/releases";
|
||||
import closeIcon from "../../assets/close.png";
|
||||
|
||||
import Drawer from "react-modern-drawer";
|
||||
@@ -25,7 +22,6 @@ interface Props {
|
||||
export default function RevisionResource({ isLatest }: Props) {
|
||||
const { namespace = "", chart = "" } = useParams();
|
||||
const { data: resources, isLoading } = useGetResources(namespace, chart);
|
||||
const interestingResources = ["STATEFULSET", "DEAMONSET", "DEPLOYMENT"];
|
||||
|
||||
return (
|
||||
<table
|
||||
@@ -46,23 +42,15 @@ export default function RevisionResource({ isLatest }: Props) {
|
||||
) : (
|
||||
<tbody className="mt-4 h-8 w-full rounded-sm bg-white">
|
||||
{resources?.length ? (
|
||||
resources
|
||||
.sort(function (a, b) {
|
||||
return (
|
||||
interestingResources.indexOf(a.kind.toUpperCase()) -
|
||||
interestingResources.indexOf(b.kind.toUpperCase())
|
||||
);
|
||||
})
|
||||
.reverse()
|
||||
.map((resource: StructuredResources) => (
|
||||
<ResourceRow
|
||||
key={
|
||||
resource.apiVersion + resource.kind + resource.metadata.name
|
||||
}
|
||||
resource={resource}
|
||||
isLatest={isLatest}
|
||||
/>
|
||||
))
|
||||
resources?.map((resource: StructuredResources) => (
|
||||
<ResourceRow
|
||||
key={
|
||||
resource.apiVersion + resource.kind + resource.metadata.name
|
||||
}
|
||||
resource={resource}
|
||||
isLatest={isLatest}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<div className="display-none no-charts mt-3 rounded-sm bg-white p-4 text-sm shadow-sm">
|
||||
|
||||
@@ -2,7 +2,7 @@ import { BsArrowDownRight, BsArrowUpRight } from "react-icons/bs";
|
||||
import { useParams } from "react-router";
|
||||
import { compare } from "compare-versions";
|
||||
|
||||
import { ReleaseRevision } from "../../data/types";
|
||||
import type { ReleaseRevision } from "../../data/types";
|
||||
import { getAge } from "../../timeUtils";
|
||||
import StatusLabel from "../common/StatusLabel";
|
||||
import useNavigateWithSearchParams from "../../hooks/useNavigateWithSearchParams";
|
||||
@@ -20,8 +20,8 @@ export default function RevisionsList({
|
||||
const navigate = useNavigateWithSearchParams();
|
||||
const { namespace, chart } = useParams();
|
||||
|
||||
const changeRelease = (newRevision: number) => {
|
||||
navigate(`/${namespace}/${chart}/installed/revision/${newRevision}`);
|
||||
const changeRelease = async (newRevision: number) => {
|
||||
await navigate(`/${namespace}/${chart}/installed/revision/${newRevision}`);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -38,7 +38,7 @@ export default function RevisionsList({
|
||||
title={
|
||||
isRollback ? `Rollback to ${Number(release.revision) - 1}` : ""
|
||||
}
|
||||
onClick={() => changeRelease(release.revision)}
|
||||
onClick={() => void changeRelease(release.revision)}
|
||||
key={release.revision}
|
||||
className={`mx-5 flex cursor-pointer flex-col gap-4 rounded-md border border-gray-200 p-2 ${
|
||||
release.revision === selectedRevision
|
||||
|
||||
Reference in New Issue
Block a user