Fix/chart-link-cluster-mode (#474)

This commit is contained in:
Nir Parisian
2023-10-02 19:10:04 +03:00
committed by GitHub
parent 88ea89a5ba
commit c251e6c697
19 changed files with 157 additions and 170 deletions

View File

@@ -1,91 +1,39 @@
# Helm dashboard V2 # Helm dashboard React
Welcome to our new project, upgrading helm dashbord, we call it, Helm Dashboard version... 2! 🤩 Welcome to the frontend of the helm dashboard.
We care most about keeping the project:
Helm dashboard V2 is an open source effort to modernize the helm-dashboard. 1. Maintainable
Our goals are to create a version which is more:
1. Maintable
2. Extendable 2. Extendable
3. Contributor friendly 3. Contributor friendly
## What is helm? # The FE Stack
First thing first, if you are new here please check out these resources to see what helm is all about
[Video](https://www.youtube.com/watch?v=fy8SHvNZGeE)
[Article](https://kruschecompany.com/helm-kubernetes/)
# Legacy dashboard vs dashboard V2
The legacy dashboard found [here](https://github.com/komodorio/helm-dashboard/tree/main/pkg/dashboard/static) is a static webapp and was written vanilla css, jquery, javascript and html. If you inspect the code abit you may notice that its relitvly hard to extend and maintain such a project.
Our goal with dashboard V2 is to improve the ability to maintain and extend our dashboard app. To achive this we are using a more modern frontend stack.
- Vite, as our build tool. - Vite, as our build tool.
- React will be used to make this project more inviting for developers to contribute too. - React, as our UI library.
- TypeScript and ESLint will keep the project safe, please keep them clean. - TypeScript and ESLint will keep the project safe, please keep them clean.
- Tailwind will be used for styling. - Tailwind for styling.
- React-Query will be used to fetch data from the backend. - React-Query for fetching data from the backend.
- Storybook is utilized to develop a component library. - Storybook is utilized to develop a component library.
Please follow through the file structure to understand how things are structured and should be used. Please follow through the file structure to understand how things are structured and should be used.
# Contribution guide # Contribution guide
## Running legacy dashboard
The legacy dashboard is great for refrence and checking that you have implemented the UI correctly.
1. Install [helm](https://helm.sh/docs/intro/install/) and [kubectl](https://kubernetes.io/docs/tasks/tools/).
2. `git clone https://github.com/komodorio/helm-dashboard.git`.
3. `go build -o bin/dashboard .`
4. `bin/dashboard`
The UI should now be running on http://localhost:8080/
If you're having issues with that please follow the main README in the main folder.
If you still having troubles please contact us on our [Slack community channel](https://join.slack.com/t/komodorkommunity/shared_invite/zt-1lz4cme86-2zIKTRtTFnzL_UNxaUS9yw)
## Setting up your development environment ## Setting up your development environment
1. First you should fork this repositroy. 1. First you should fork this repository.
2. Clone your new repository using `git clone <https_or_ssh_url>`. 2. Clone your new repository using `git clone <https_or_ssh_url>`.
3. Make sure to checkout branch `helm-dashboard-v2`.
- `git fetch`
- `git checkout helm-dashboard-v2`
## Running dashboard V2 ## Running helm dashboard
1. Make sure you cloned the project correctly. This is explained in this [stage](https://github.com/komodorio/helm-dashboard/blob/helm-dashboard-v2/dashboard/README.md#setting-up-your-development-environment). 1. Make sure you cloned the project correctly. This is explained in this [stage](https://github.com/komodorio/helm-dashboard/blob/helm-dashboard-v2/dashboard/README.md#setting-up-your-development-environment).
2. go to `helm-dashboard-v2/dashboard` in your local project. 2. run the backend server. This is also explained in the above link.
3. inorder to install dependncies and start the development server 2. go to `frontend` in your local project.
3. in order to install dependencies and start the development server
- `npm i` - `npm i`
- `npm run dev` - `npm run dev`
4. with the default integration the dashboard should run on http://localhost:5173/ 4. with the default integration the dashboard should run on http://localhost:5173/
## Setting up a local cluster with ease
1. Install [Docker](https://docs.docker.com/engine/install/ubuntu/)
2. Install [Minikube](https://minikube.sigs.k8s.io/docs/start/)
3. Start your cluster `minikube start`
You should now be able to follow the [Helm tutorial](https://helm.sh/docs/intro/quickstart/) and interact with Helm normally.
## Choosing a task
If you are completely new to the project its recommended to look for tasks labled: `good first issue`.
These tasks should be simple enough for a begginer or for someone looking to learn the code base.
You are also free to reachout to us on [Slack](https://join.slack.com/t/komodorkommunity/shared_invite/zt-1lz4cme86-2zIKTRtTFnzL_UNxaUS9yw), we can help you find a task that suits your perfectly.
## Opening a pull request
Inorder to open a pull request with your changes. \
1. make sure you are synced with `helm-dashboard-v2` and that all conflicts are resolved. \
2. commit your changes and push to your fork. \
3. then navigate to https://github.com/komodorio/helm-dashboard and open a pull request. Make sure you are merging from your branch to `helm-dashboard-v2`. \
4. you should now tag a main developer (@chad11111
for example) and get your pull request reviewed.
# Component library # Component library

View File

@@ -116,7 +116,9 @@ class ApiService {
}): Promise<ReleaseHealthStatus[] | null> => { }): Promise<ReleaseHealthStatus[] | null> => {
if (!release) return null; if (!release) return null;
const data = await this.fetchWithDefaults( const data = await this.fetchWithDefaults<
Promise<ReleaseHealthStatus[] | null>
>(
`/api/helm/releases/${release.namespace}/${release.name}/resources?health=true` `/api/helm/releases/${release.namespace}/${release.name}/resources?health=true`
); );
return data; return data;
@@ -129,14 +131,21 @@ class ApiService {
if (!params.namespace || !params.chart) return []; if (!params.namespace || !params.chart) return [];
const data = await this.fetchWithDefaults( const data = await this.fetchWithDefaults<ReleaseRevision[]>(
`/api/helm/releases/${params.namespace}/${params.chart}/history` `/api/helm/releases/${params.namespace}/${params.chart}/history`
); );
return data; return data;
}; };
getValues = async ({ queryKey }: any) => { getValues = async ({
queryKey,
}: {
queryKey: [
string,
{ namespace: string; chart: { name: string }; version: number }
];
}) => {
const [, params] = queryKey; const [, params] = queryKey;
const { namespace, chart, version } = params; const { namespace, chart, version } = params;

View File

@@ -26,7 +26,7 @@ export interface Scanner {
export interface ScanResult { export interface ScanResult {
scannerType: string; scannerType: string;
result: any; result: string;
} }
export interface ScannersList { export interface ScannersList {

View File

@@ -16,16 +16,43 @@ export function useGetInstalledReleases(
) { ) {
return useQuery<Release[]>( return useQuery<Release[]>(
["installedReleases", context], ["installedReleases", context],
() => () => apiService.fetchWithDefaults<Release[]>("/api/helm/releases"),
apiService.fetchWithDefaults<Release[]>("/api/helm/releases", {
headers: {
"X-Kubecontext": context,
},
}),
options options
); );
} }
export interface ReleaseManifest {
apiVersion: string;
kind: string;
metadata: {
name: string;
namespace: string;
labels: Record<string, string>;
};
spec: {
replicas: number;
selector: Record<string, string>;
template: {
metadata: {
labels: Record<string, string>;
};
spec: {
containers: {
name: string;
image: string;
ports: {
containerPort: number;
}[];
env: {
name: string;
value: string;
}[];
}[];
};
};
};
}
export function useGetReleaseManifest({ export function useGetReleaseManifest({
namespace, namespace,
chartName, chartName,
@@ -33,12 +60,12 @@ export function useGetReleaseManifest({
}: { }: {
namespace: string; namespace: string;
chartName: string; chartName: string;
options?: UseQueryOptions<any>; options?: UseQueryOptions<ReleaseManifest[]>;
}) { }) {
return useQuery<any>( return useQuery<ReleaseManifest[]>(
["manifest", namespace, chartName], ["manifest", namespace, chartName],
() => () =>
apiService.fetchWithDefaults<any>( apiService.fetchWithDefaults<ReleaseManifest[]>(
`/api/helm/releases/${namespace}/${chartName}/manifests` `/api/helm/releases/${namespace}/${chartName}/manifests`
), ),
options options
@@ -219,12 +246,12 @@ export function useChartReleaseValues({
userDefinedValue?: string; userDefinedValue?: string;
revision?: number; revision?: number;
version?: string; version?: string;
options?: UseQueryOptions<any>; options?: UseQueryOptions<unknown>;
}) { }) {
return useQuery<any>( return useQuery<unknown>(
["values", namespace, release, userDefinedValue, version], ["values", namespace, release, userDefinedValue, version],
() => () =>
apiService.fetchWithDefaults<any>( apiService.fetchWithDefaults<unknown>(
`/api/helm/releases/${namespace}/${release}/values?${"userDefined=true"}${ `/api/helm/releases/${namespace}/${release}/values?${"userDefined=true"}${
revision ? `&revision=${revision}` : "" revision ? `&revision=${revision}` : ""
}`, }`,
@@ -253,7 +280,7 @@ export const useVersionData = ({
namespace: string; namespace: string;
releaseName: string; releaseName: string;
isInstallRepoChart?: boolean; isInstallRepoChart?: boolean;
options?: UseQueryOptions<any>; options?: UseQueryOptions;
}) => { }) => {
return useQuery( return useQuery(
[ [
@@ -287,7 +314,7 @@ export const useVersionData = ({
return data; return data;
}, },
// @ts-ignore
options options
); );
}; };
@@ -311,12 +338,12 @@ export interface StructuredResources {
export interface Metadata { export interface Metadata {
name: string; name: string;
namespace: string; namespace: string;
creationTimestamp: any; creationTimestamp: Date;
labels: any; labels: string[];
} }
export interface Spec { export interface Spec {
[key: string]: any; [key: string]: string;
} }
export interface Status { export interface Status {
@@ -326,8 +353,8 @@ export interface Status {
export interface Condition { export interface Condition {
type: string; type: string;
status: string; status: string;
lastProbeTime: any; lastProbeTime: Date;
lastTransitionTime: any; lastTransitionTime: Date;
reason: string; reason: string;
message: string; message: string;
} }

View File

@@ -56,10 +56,10 @@ export function useChartRepoValues({
version: string; version: string;
chart: string; chart: string;
}) { }) {
return useQuery<any>( return useQuery<string>(
["helm", "repositories", "values", chart, version], ["helm", "repositories", "values", chart, version],
() => () =>
apiService.fetchWithDefaults<any>( apiService.fetchWithDefaults<string>(
`/api/helm/repositories/values?chart=${chart}&version=${version}`, `/api/helm/repositories/values?chart=${chart}&version=${version}`,
{ {
headers: { "Content-Type": "text/plain; charset=utf-8" }, headers: { "Content-Type": "text/plain; charset=utf-8" },

View File

@@ -40,7 +40,7 @@ export const useDiffData = ({
selectedRepo: string; selectedRepo: string;
versionsError: string; versionsError: string;
currentVerManifest: string; currentVerManifest: string;
selectedVerData: any; selectedVerData: { [key: string]: string };
chart: string; chart: string;
}) => { }) => {
return useQuery( return useQuery(

View File

@@ -55,7 +55,7 @@ export default function App() {
<Routes> <Routes>
<Route path="docs/" element={<DocsPage />} /> <Route path="docs/" element={<DocsPage />} />
<Route path="*" element={<PageLayout />}> <Route path="*" element={<PageLayout />}>
<Route path=":context/*" element={<SyncContext />}> <Route path=":context?/*" element={<SyncContext />}>
<Route path="installed/?" element={<Installed />} /> <Route path="installed/?" element={<Installed />} />
<Route <Route
path=":namespace/:chart/installed/revision/:revision" path=":namespace/:chart/installed/revision/:revision"

View File

@@ -15,7 +15,6 @@ import { useGetLatestVersion } from "../../API/releases";
import { isNewerVersion } from "../../utils"; import { isNewerVersion } from "../../utils";
import { LatestChartVersion } from "../../API/interfaces"; import { LatestChartVersion } from "../../API/interfaces";
import useNavigateWithSearchParams from "../../hooks/useNavigateWithSearchParams"; import useNavigateWithSearchParams from "../../hooks/useNavigateWithSearchParams";
import { useParams } from "react-router-dom";
type InstalledPackageCardProps = { type InstalledPackageCardProps = {
release: Release; release: Release;
@@ -26,7 +25,6 @@ export default function InstalledPackageCard({
}: InstalledPackageCardProps) { }: InstalledPackageCardProps) {
const navigate = useNavigateWithSearchParams(); const navigate = useNavigateWithSearchParams();
const { context: selectedCluster } = useParams();
const [isMouseOver, setIsMouseOver] = useState(false); const [isMouseOver, setIsMouseOver] = useState(false);
const { data: latestVersionResult } = useGetLatestVersion(release.chartName, { const { data: latestVersionResult } = useGetLatestVersion(release.chartName, {
@@ -34,7 +32,7 @@ export default function InstalledPackageCard({
cacheTime: 0, cacheTime: 0,
}); });
const { data: statusData } = useQuery<any>({ const { data: statusData } = useQuery<unknown>({
queryKey: ["resourceStatus", release], queryKey: ["resourceStatus", release],
queryFn: () => apiService.getResourceStatus({ release }), queryFn: () => apiService.getResourceStatus({ release }),
}); });
@@ -61,10 +59,9 @@ export default function InstalledPackageCard({
const handleOnClick = () => { const handleOnClick = () => {
const { name, namespace } = release; const { name, namespace } = release;
navigate( navigate(`/${namespace}/${name}/installed/revision/${release.revision}`, {
`/${selectedCluster}/${namespace}/${name}/installed/revision/${release.revision}`, state: release,
{ state: release } });
);
}; };
const statusColor = getStatusColor(release.status as DeploymentStatus); const statusColor = getStatusColor(release.status as DeploymentStatus);

View File

@@ -1,4 +1,5 @@
import { NavLink, useLocation } from "react-router-dom"; import { NavLink, useLocation, useParams } from "react-router-dom";
import { useAppContext } from "../context/AppContext";
const LinkWithSearchParams = ({ const LinkWithSearchParams = ({
to, to,
@@ -11,14 +12,22 @@ const LinkWithSearchParams = ({
children: React.ReactNode; children: React.ReactNode;
}) => { }) => {
const { search } = useLocation(); const { search } = useLocation();
const params = new URLSearchParams(search); const { context } = useParams();
const {clusterMode} = useAppContext();
const params = new URLSearchParams(search);
// For state we don't want to keep while navigating // For state we don't want to keep while navigating
props.exclude?.forEach((key) => { props.exclude?.forEach((key) => {
params.delete(key); params.delete(key);
}); });
return <NavLink to={`${to}/?${params.toString()}`} {...props} />; let prefixedUrl = to;
if (!clusterMode) {
prefixedUrl = `/${context}${to}`;
}
return <NavLink to={`${prefixedUrl}/?${params.toString()}`} {...props} />;
}; };
export default LinkWithSearchParams; export default LinkWithSearchParams;

View File

@@ -5,7 +5,7 @@ import useAlertError from "../../hooks/useAlertError";
import useCustomSearchParams from "../../hooks/useCustomSearchParams"; import useCustomSearchParams from "../../hooks/useCustomSearchParams";
import { useAppContext } from "../../context/AppContext"; import { useAppContext } from "../../context/AppContext";
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import apiService from "../../API/apiService"; import apiService from "../../API/apiService";
interface FormKeys { interface FormKeys {
@@ -27,7 +27,6 @@ function AddRepositoryModal({ isOpen, onClose }: AddRepositoryModalProps) {
const { searchParamsObject } = useCustomSearchParams(); const { searchParamsObject } = useCustomSearchParams();
const { repo_url, repo_name } = searchParamsObject; const { repo_url, repo_name } = searchParamsObject;
const { setSelectedRepo } = useAppContext(); const { setSelectedRepo } = useAppContext();
const { context } = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@@ -45,7 +44,8 @@ function AddRepositoryModal({ isOpen, onClose }: AddRepositoryModalProps) {
setIsLoading(true); setIsLoading(true);
apiService.fetchWithDefaults<void>("/api/helm/repositories", { apiService
.fetchWithDefaults<void>("/api/helm/repositories", {
method: "POST", method: "POST",
body, body,
}) })
@@ -57,7 +57,7 @@ function AddRepositoryModal({ isOpen, onClose }: AddRepositoryModalProps) {
queryKey: ["helm", "repositories"], queryKey: ["helm", "repositories"],
}); });
setSelectedRepo(formData.name || ""); setSelectedRepo(formData.name || "");
navigate(`/${context}/repository/${formData.name}`, { navigate(`/repository/${formData.name}`, {
replace: true, replace: true,
}); });
}) })

View File

@@ -1,5 +1,4 @@
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import useAlertError from "../../../hooks/useAlertError";
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import { import {
useChartReleaseValues, useChartReleaseValues,
@@ -19,6 +18,7 @@ import { useChartRepoValues } from "../../../API/repositories";
import { useDiffData } from "../../../API/shared"; import { useDiffData } from "../../../API/shared";
import { InstallChartModalProps } from "../../../data/types"; import { InstallChartModalProps } from "../../../data/types";
import { DefinedValues } from "./DefinedValues"; import { DefinedValues } from "./DefinedValues";
import apiService from "../../../API/apiService";
export const InstallReleaseChartModal = ({ export const InstallReleaseChartModal = ({
isOpen, isOpen,
@@ -30,7 +30,6 @@ export const InstallReleaseChartModal = ({
latestRevision, latestRevision,
}: InstallChartModalProps) => { }: InstallChartModalProps) => {
const navigate = useNavigateWithSearchParams(); const navigate = useNavigateWithSearchParams();
const { setShowErrorModal } = useAlertError();
const [userValues, setUserValues] = useState<string>(); const [userValues, setUserValues] = useState<string>();
const [installError, setInstallError] = useState(""); const [installError, setInstallError] = useState("");
@@ -150,35 +149,23 @@ export const InstallReleaseChartModal = ({
formData.append("version", selectedVersion || ""); formData.append("version", selectedVersion || "");
formData.append("values", userValues || releaseValues || ""); // if userValues is empty, we use the release values formData.append("values", userValues || releaseValues || ""); // if userValues is empty, we use the release values
const res = await fetch( const data = await apiService.fetchWithDefaults(
// Todo: Change to BASE_URL from env
`/api/helm/releases/${ `/api/helm/releases/${
namespace ? namespace : "default" namespace ? namespace : "default"
}${`/${releaseName}`}`, }${`/${releaseName}`}`,
{ {
method: "post", method: "post",
body: formData, body: formData,
headers: {
"X-Kubecontext": selectedCluster as string,
},
} }
); );
return data;
if (!res.ok) {
setShowErrorModal({
title: "Failed to upgrade the chart",
msg: String(await res.text()),
});
}
return res.json();
}, },
{ {
onSuccess: async (response) => { onSuccess: async (response) => {
onClose(); onClose();
setSelectedVersionData({ version: "", urls: [] }); //cleanup setSelectedVersionData({ version: "", urls: [] }); //cleanup
navigate( navigate(
`/${selectedCluster}/${ `/${
namespace ? namespace : "default" namespace ? namespace : "default"
}/${releaseName}/installed/revision/${response.version}` }/${releaseName}/installed/revision/${response.version}`
); );

View File

@@ -1,5 +1,4 @@
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import useAlertError from "../../../hooks/useAlertError";
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import { useGetVersions, useVersionData } from "../../../API/releases"; import { useGetVersions, useVersionData } from "../../../API/releases";
import Modal, { ModalButtonStyle } from "../Modal"; import Modal, { ModalButtonStyle } from "../Modal";
@@ -13,6 +12,7 @@ import { isNewerVersion, isNoneEmptyArray } from "../../../utils";
import { useDiffData } from "../../../API/shared"; import { useDiffData } from "../../../API/shared";
import { InstallChartModalProps } from "../../../data/types"; import { InstallChartModalProps } from "../../../data/types";
import { DefinedValues } from "./DefinedValues"; import { DefinedValues } from "./DefinedValues";
import apiService from "../../../API/apiService";
export const InstallRepoChartModal = ({ export const InstallRepoChartModal = ({
isOpen, isOpen,
@@ -22,7 +22,6 @@ export const InstallRepoChartModal = ({
latestVersion, latestVersion,
}: InstallChartModalProps) => { }: InstallChartModalProps) => {
const navigate = useNavigateWithSearchParams(); const navigate = useNavigateWithSearchParams();
const { setShowErrorModal } = useAlertError();
const [userValues, setUserValues] = useState(""); const [userValues, setUserValues] = useState("");
const [installError, setInstallError] = useState(""); const [installError, setInstallError] = useState("");
@@ -130,32 +129,20 @@ export const InstallRepoChartModal = ({
formData.append("version", selectedVersion || ""); formData.append("version", selectedVersion || "");
formData.append("values", userValues); formData.append("values", userValues);
formData.append("name", releaseName || ""); formData.append("name", releaseName || "");
const res = await fetch( const data = await apiService.fetchWithDefaults(
// Todo: Change to BASE_URL from env
`/api/helm/releases/${namespace ? namespace : "default"}`, `/api/helm/releases/${namespace ? namespace : "default"}`,
{ {
method: "post", method: "post",
body: formData, body: formData,
headers: {
"X-Kubecontext": selectedCluster as string,
},
} }
); );
return data;
if (!res.ok) {
setShowErrorModal({
title: "Failed to install the chart",
msg: String(await res.text()),
});
}
return res.json();
}, },
{ {
onSuccess: async (response) => { onSuccess: async (response) => {
onClose(); onClose();
navigate( navigate(
`/${selectedCluster}/${response.namespace}/${response.name}/installed/revision/1` `/${response.namespace}/${response.name}/installed/revision/1`
); );
}, },
onError: (error) => { onError: (error) => {

View File

@@ -6,7 +6,7 @@ import apiService from "../../API/apiService";
import Spinner from "../Spinner"; import Spinner from "../Spinner";
import { useUpdateRepo } from "../../API/repositories"; import { useUpdateRepo } from "../../API/repositories";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { useAppContext } from "../../context/AppContext"; import { useAppContext } from "../../context/AppContext";
type RepositoryViewerProps = { type RepositoryViewerProps = {
@@ -16,7 +16,6 @@ type RepositoryViewerProps = {
function RepositoryViewer({ repository }: RepositoryViewerProps) { function RepositoryViewer({ repository }: RepositoryViewerProps) {
const [searchValue, setSearchValue] = useState(""); const [searchValue, setSearchValue] = useState("");
const [isRemoveLoading, setIsRemove] = useState(false); const [isRemoveLoading, setIsRemove] = useState(false);
const { context } = useParams();
const { setSelectedRepo, selectedRepo } = useAppContext(); const { setSelectedRepo, selectedRepo } = useAppContext();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@@ -54,7 +53,7 @@ function RepositoryViewer({ repository }: RepositoryViewerProps) {
method: "DELETE", method: "DELETE",
} }
); );
navigate(`/${context}/repository`, { replace: true }); navigate("/repository", { replace: true });
setSelectedRepo(""); setSelectedRepo("");
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: ["helm", "repositories"], queryKey: ["helm", "repositories"],

View File

@@ -23,7 +23,7 @@ import {
import RevisionDiff from "./RevisionDiff"; import RevisionDiff from "./RevisionDiff";
import RevisionResource from "./RevisionResource"; import RevisionResource from "./RevisionResource";
import Tabs from "../Tabs"; import Tabs from "../Tabs";
import { useMutation } from "@tanstack/react-query"; import { type UseQueryResult, useMutation } from "@tanstack/react-query";
import Modal, { ModalButtonStyle } from "../modal/Modal"; import Modal, { ModalButtonStyle } from "../modal/Modal";
import Spinner from "../Spinner"; import Spinner from "../Spinner";
import useAlertError from "../../hooks/useAlertError"; import useAlertError from "../../hooks/useAlertError";
@@ -125,10 +125,10 @@ export default function RevisionDetails({
ns: namespace, ns: namespace,
name: chart, name: chart,
}); });
} catch (error: any) { } catch (error: unknown) {
setShowErrorModal({ setShowErrorModal({
title: "Test failed to run", title: "Test failed to run",
msg: error.message, msg: (error as Error).message,
}); });
} }
setShowTestResults(true); setShowTestResults(true);
@@ -207,7 +207,7 @@ export default function RevisionDetails({
<span <span
onClick={() => { onClick={() => {
navigate( navigate(
`/${context}/repository?add_repo=true&repo_url=${latestVerData[0].urls[0]}&repo_name=${latestVerData[0].repository}` `/repository?add_repo=true&repo_url=${latestVerData[0].urls[0]}&repo_name=${latestVerData[0].repository}`
); );
}} }}
className="underline text-sm cursor-pointer text-blue-600" className="underline text-sm cursor-pointer text-blue-600"
@@ -320,7 +320,7 @@ const Rollback = ({
release: Release; release: Release;
installedRevision: ReleaseRevision; installedRevision: ReleaseRevision;
}) => { }) => {
const { chart, namespace, revision, context } = useParams(); const { chart, namespace, revision } = useParams();
const navigate = useNavigateWithSearchParams(); const navigate = useNavigateWithSearchParams();
const [showRollbackDiff, setShowRollbackDiff] = useState(false); const [showRollbackDiff, setShowRollbackDiff] = useState(false);
@@ -330,9 +330,7 @@ const Rollback = ({
useRollbackRelease({ useRollbackRelease({
onSuccess: () => { onSuccess: () => {
navigate( navigate(
`/${context}/${namespace}/${chart}/installed/revision/${ `/${namespace}/${chart}/installed/revision/${revisionInt + 1}`
revisionInt + 1
}`
); );
window.location.reload(); window.location.reload();
}, },
@@ -398,7 +396,11 @@ const Rollback = ({
); );
}; };
const RollbackModalContent = ({ dataResponse }: { dataResponse: any }) => { const RollbackModalContent = ({
dataResponse,
}: {
dataResponse: UseQueryResult<string, unknown>;
}) => {
const { const {
data, data,
isLoading, isLoading,

View File

@@ -18,10 +18,11 @@ export default function RevisionsList({
selectedRevision, selectedRevision,
}: RevisionsListProps) { }: RevisionsListProps) {
const navigate = useNavigateWithSearchParams(); const navigate = useNavigateWithSearchParams();
const { context, namespace, chart } = useParams(); const { namespace, chart } = useParams();
const changeRelease = (newRevision: number) => { const changeRelease = (newRevision: number) => {
navigate( navigate(
`/${context}/${namespace}/${chart}/installed/revision/${newRevision}` `/${namespace}/${chart}/installed/revision/${newRevision}`
); );
}; };

View File

@@ -1,10 +1,27 @@
import { useLocation, useNavigate } from "react-router-dom"; import {
type NavigateOptions,
useLocation,
useNavigate,
useParams,
} from "react-router-dom";
import { useAppContext } from "../context/AppContext";
const useNavigateWithSearchParams = () => { const useNavigateWithSearchParams = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const { clusterMode } = useAppContext();
const { context } = useParams();
const { search } = useLocation(); const { search } = useLocation();
const navigateWithSearchParams = (url: string, ...restArgs: any[]) => { const navigateWithSearchParams = (
navigate(url + search, ...restArgs); url: string,
...restArgs: NavigateOptions[]
) => {
let prefixedUrl = url;
if (!clusterMode) {
prefixedUrl = `/${context}${url}`;
}
navigate(`${prefixedUrl}${search}`, ...restArgs);
}; };
return navigateWithSearchParams; return navigateWithSearchParams;

View File

@@ -1,4 +1,4 @@
import { useLocation, useParams } from "react-router-dom"; import { useLocation } from "react-router-dom";
import LogoHeader from "../assets/logo-header.svg"; import LogoHeader from "../assets/logo-header.svg";
import DropDown from "../components/common/DropDown"; import DropDown from "../components/common/DropDown";
import WatcherIcon from "../assets/k8s-watcher.svg"; import WatcherIcon from "../assets/k8s-watcher.svg";
@@ -22,7 +22,7 @@ export default function Header() {
setClusterMode(data.ClusterMode); setClusterMode(data.ClusterMode);
}, },
}); });
const { context } = useParams();
const location = useLocation(); const location = useLocation();
const openSupportChat = () => { const openSupportChat = () => {
@@ -58,7 +58,7 @@ export default function Header() {
return ( return (
<div className="h-16 flex items-center justify-between bg-white custom-shadow"> <div className="h-16 flex items-center justify-between bg-white custom-shadow">
<div className="h-16 flex items-center gap-6 min-w-fit "> <div className="h-16 flex items-center gap-6 min-w-fit ">
<LinkWithSearchParams to={`/${context}/installed`} exclude={["tab"]}> <LinkWithSearchParams to={"/installed"} exclude={["tab"]}>
<img <img
src={LogoHeader} src={LogoHeader}
alt="helm dashboard logo" alt="helm dashboard logo"
@@ -70,7 +70,7 @@ export default function Header() {
<ul className="w-full items-center flex md:flex-row md:justify-between md:mt-0 md:text-sm md:font-normal md:border-0 "> <ul className="w-full items-center flex md:flex-row md:justify-between md:mt-0 md:text-sm md:font-normal md:border-0 ">
<li> <li>
<LinkWithSearchParams <LinkWithSearchParams
to={`/${context}/installed`} to={"/installed"}
exclude={["tab"]} exclude={["tab"]}
className={getBtnStyle("installed")} className={getBtnStyle("installed")}
> >
@@ -79,7 +79,7 @@ export default function Header() {
</li> </li>
<li> <li>
<LinkWithSearchParams <LinkWithSearchParams
to={`/${context}/repository`} to={"/repository"}
exclude={["tab"]} exclude={["tab"]}
end={false} end={false}
className={getBtnStyle("repository")} className={getBtnStyle("repository")}

View File

@@ -15,7 +15,7 @@ function RepositoryPage() {
const { setSelectedRepo, selectedRepo } = useAppContext(); const { setSelectedRepo, selectedRepo } = useAppContext();
const handleRepositoryChanged = (selectedRepository: Repository) => { const handleRepositoryChanged = (selectedRepository: Repository) => {
navigate(`/${context}/repository/${selectedRepository.name}`, { navigate(`/repository/${selectedRepository.name}`, {
replace: true, replace: true,
}); });
}; };
@@ -28,7 +28,7 @@ function RepositoryPage() {
useEffect(() => { useEffect(() => {
if (selectedRepo && !repoFromParams) { if (selectedRepo && !repoFromParams) {
navigate(`/${context}/repository/${selectedRepo}`, { navigate(`/repository/${selectedRepo}`, {
replace: true, replace: true,
}); });
} }

View File

@@ -1461,10 +1461,10 @@
resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz" resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz"
integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==
"@esbuild/darwin-arm64@0.17.19": "@esbuild/darwin-x64@0.17.19":
version "0.17.19" version "0.17.19"
resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz" resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz"
integrity sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg== integrity sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
version "4.4.0" version "4.4.0"
@@ -5621,6 +5621,10 @@ fs.realpath@^1.0.0:
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@^2.3.2, fsevents@~2.3.2:
version "2.3.2"
resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
function-bind@^1.1.1: function-bind@^1.1.1:
version "1.1.1" version "1.1.1"