mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
238 lines
7.6 KiB
TypeScript
238 lines
7.6 KiB
TypeScript
/**
|
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
*/
|
|
|
|
import "../item-object-list/item-list-layout.scss";
|
|
import "./releases.scss";
|
|
|
|
import React, { Component } from "react";
|
|
import type { RouteComponentProps } from "react-router";
|
|
import type { HelmRelease } from "../../../common/k8s-api/endpoints/helm-releases.api";
|
|
import { navigation } from "../../navigation";
|
|
import type { ReleaseRouteParams } from "../../../common/routes";
|
|
import { releaseURL } from "../../../common/routes";
|
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
|
import namespaceStoreInjectable from "../+namespaces/namespace-store/namespace-store.injectable";
|
|
import { ItemListLayout } from "../item-object-list";
|
|
import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter";
|
|
import { kebabCase } from "lodash/fp";
|
|
import { HelmReleaseMenu } from "./release-menu";
|
|
import type { ItemStore } from "../../../common/item.store";
|
|
import { ReleaseRollbackDialog } from "./release-rollback-dialog";
|
|
import { ReleaseDetails } from "./release-details/release-details";
|
|
import removableReleasesInjectable from "./removable-releases.injectable";
|
|
import type { RemovableHelmRelease } from "./removable-releases";
|
|
import type { IComputedValue } from "mobx";
|
|
import releasesInjectable from "./releases.injectable";
|
|
|
|
enum columnId {
|
|
name = "name",
|
|
namespace = "namespace",
|
|
revision = "revision",
|
|
chart = "chart",
|
|
version = "version",
|
|
appVersion = "app-version",
|
|
status = "status",
|
|
updated = "update",
|
|
}
|
|
|
|
export interface HelmReleasesProps extends RouteComponentProps<ReleaseRouteParams> {
|
|
}
|
|
|
|
interface Dependencies {
|
|
releases: IComputedValue<RemovableHelmRelease[]>;
|
|
releasesArePending: IComputedValue<boolean>;
|
|
selectNamespace: (namespace: string) => void;
|
|
}
|
|
|
|
class NonInjectedHelmReleases extends Component<Dependencies & HelmReleasesProps> {
|
|
componentDidMount() {
|
|
const { match: { params: { namespace }}} = this.props;
|
|
|
|
if (namespace) {
|
|
this.props.selectNamespace(namespace);
|
|
}
|
|
}
|
|
|
|
onDetails = (item: HelmRelease) => {
|
|
this.showDetails(item);
|
|
};
|
|
|
|
showDetails = (item: HelmRelease) => {
|
|
navigation.push(releaseURL({
|
|
params: {
|
|
name: item.getName(),
|
|
namespace: item.getNs(),
|
|
},
|
|
}));
|
|
};
|
|
|
|
hideDetails = () => {
|
|
navigation.push(releaseURL());
|
|
};
|
|
|
|
renderRemoveDialogMessage(selectedItems: HelmRelease[]) {
|
|
const releaseNames = selectedItems.map(item => item.getName()).join(", ");
|
|
|
|
return (
|
|
<div>
|
|
<>Remove <b>{releaseNames}</b>?</>
|
|
<p className="warning">
|
|
Note: StatefulSet Volumes won't be deleted automatically
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
render() {
|
|
const releases = this.props.releases;
|
|
const releasesArePending = this.props.releasesArePending;
|
|
|
|
// TODO: Implement ItemListLayout without stateful stores
|
|
const legacyReleaseStore = {
|
|
get items() {
|
|
return releases.get();
|
|
},
|
|
|
|
loadAll: () => Promise.resolve(),
|
|
|
|
get isLoaded() {
|
|
return !releasesArePending.get();
|
|
},
|
|
|
|
failedLoading: false,
|
|
|
|
getTotalCount: () => releases.get().length,
|
|
|
|
toggleSelection: (item) => {
|
|
item.toggle();
|
|
},
|
|
|
|
isSelectedAll: (visibleItems: RemovableHelmRelease[]) => (
|
|
visibleItems.length > 0
|
|
&& visibleItems.every((release) => release.isSelected)
|
|
),
|
|
|
|
toggleSelectionAll: (visibleItems: RemovableHelmRelease[]) => {
|
|
let selected = false;
|
|
|
|
if (!legacyReleaseStore.isSelectedAll(visibleItems)) {
|
|
selected = true;
|
|
}
|
|
|
|
visibleItems.forEach((release) => {
|
|
if (release.isSelected !== selected) {
|
|
release.toggle();
|
|
}
|
|
});
|
|
},
|
|
|
|
isSelected: (item) => item.isSelected,
|
|
|
|
get selectedItems() {
|
|
return releases.get().filter((release) => release.isSelected);
|
|
},
|
|
|
|
pickOnlySelected: (releases) => {
|
|
return releases.filter(release => release.isSelected);
|
|
},
|
|
|
|
removeItems: async (releases) => {
|
|
await Promise.all(releases.map(release => release.delete()));
|
|
},
|
|
} as ItemStore<RemovableHelmRelease>;
|
|
|
|
return (
|
|
<>
|
|
<ItemListLayout
|
|
store={legacyReleaseStore}
|
|
getItems={() => legacyReleaseStore.items}
|
|
preloadStores={false}
|
|
isConfigurable
|
|
tableId="helm_releases"
|
|
className="HelmReleases"
|
|
sortingCallbacks={{
|
|
[columnId.name]: release => release.getName(),
|
|
[columnId.namespace]: release => release.getNs(),
|
|
[columnId.revision]: release => release.getRevision(),
|
|
[columnId.chart]: release => release.getChart(),
|
|
[columnId.status]: release => release.getStatus(),
|
|
[columnId.updated]: release => release.getUpdated(false, false),
|
|
}}
|
|
searchFilters={[
|
|
release => release.getName(),
|
|
release => release.getNs(),
|
|
release => release.getChart(),
|
|
release => release.getStatus(),
|
|
release => release.getVersion(),
|
|
]}
|
|
customizeHeader={({ filters, searchProps, ...headerPlaceholders }) => ({
|
|
filters: (
|
|
<>
|
|
{filters}
|
|
<NamespaceSelectFilter />
|
|
</>
|
|
),
|
|
searchProps: {
|
|
...searchProps,
|
|
placeholder: "Search Releases...",
|
|
},
|
|
...headerPlaceholders,
|
|
})}
|
|
renderHeaderTitle="Releases"
|
|
renderTableHeader={[
|
|
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
|
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
|
{ title: "Chart", className: "chart", sortBy: columnId.chart, id: columnId.chart },
|
|
{ title: "Revision", className: "revision", sortBy: columnId.revision, id: columnId.revision },
|
|
{ title: "Version", className: "version", id: columnId.version },
|
|
{ title: "App Version", className: "app-version", id: columnId.appVersion },
|
|
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
|
{ title: "Updated", className: "updated", sortBy: columnId.updated, id: columnId.updated },
|
|
]}
|
|
renderTableContents={release => [
|
|
release.getName(),
|
|
release.getNs(),
|
|
release.getChart(),
|
|
release.getRevision(),
|
|
release.getVersion(),
|
|
release.appVersion,
|
|
{ title: release.getStatus(), className: kebabCase(release.getStatus()) },
|
|
release.getUpdated(),
|
|
]}
|
|
renderItemMenu={release => (
|
|
<HelmReleaseMenu
|
|
release={release}
|
|
removeConfirmationMessage={this.renderRemoveDialogMessage([release])}
|
|
/>
|
|
)}
|
|
customizeRemoveDialog={selectedItems => ({
|
|
message: this.renderRemoveDialogMessage(selectedItems),
|
|
})}
|
|
onDetails={this.onDetails}
|
|
/>
|
|
|
|
<ReleaseDetails
|
|
hideDetails={this.hideDetails}
|
|
/>
|
|
|
|
<ReleaseRollbackDialog/>
|
|
</>
|
|
);
|
|
}
|
|
}
|
|
|
|
export const HelmReleases = withInjectables<Dependencies, HelmReleasesProps>(
|
|
NonInjectedHelmReleases,
|
|
|
|
{
|
|
getProps: (di, props) => ({
|
|
releases: di.inject(removableReleasesInjectable),
|
|
releasesArePending: di.inject(releasesInjectable).pending,
|
|
selectNamespace: di.inject(namespaceStoreInjectable).selectNamespaces,
|
|
...props,
|
|
}),
|
|
},
|
|
);
|