/** * 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 { HelmRelease } from "../../../common/k8s-api/endpoints/helm-releases.api"; import { withInjectables } from "@ogre-tools/injectable-react"; import namespaceStoreInjectable from "../+namespaces/store.injectable"; import type { ItemListStore } from "../item-object-list"; import { ItemListLayout } from "../item-object-list"; import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter"; import { kebabCase } from "lodash/fp"; import { HelmReleaseMenu } from "./release-menu"; import { ReleaseRollbackDialog } from "./dialog/dialog"; import removableReleasesInjectable from "./removable-releases.injectable"; import type { RemovableHelmRelease } from "./removable-releases"; import type { IComputedValue } from "mobx"; import releasesInjectable from "./releases.injectable"; import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; import helmReleasesRouteParametersInjectable from "./helm-releases-route-parameters.injectable"; import type { NavigateToHelmReleases } from "../../../common/front-end-routing/routes/cluster/helm/releases/navigate-to-helm-releases.injectable"; import navigateToHelmReleasesInjectable from "../../../common/front-end-routing/routes/cluster/helm/releases/navigate-to-helm-releases.injectable"; enum columnId { name = "name", namespace = "namespace", revision = "revision", chart = "chart", version = "version", appVersion = "app-version", status = "status", updated = "update", } interface Dependencies { releases: IComputedValue; releasesArePending: IComputedValue; selectNamespace: (namespace: string) => void; namespace: IComputedValue; navigateToHelmReleases: NavigateToHelmReleases; } class NonInjectedHelmReleases extends Component { // TODO: This side-effect in mount must go. componentDidMount() { const namespace = this.props.namespace.get(); if (namespace) { this.props.selectNamespace(namespace); } } onDetails = (item: HelmRelease) => { this.showDetails(item); }; showDetails = (item: HelmRelease) => { this.props.navigateToHelmReleases({ name: item.getName(), namespace: item.getNs(), }); }; hideDetails = () => { this.props.navigateToHelmReleases(); }; renderRemoveDialogMessage(selectedItems: HelmRelease[]) { const releaseNames = selectedItems.map(item => item.getName()).join(", "); return (
<> Remove {" "} {releaseNames} ?

Note: StatefulSet Volumes won't be deleted automatically

); } render() { const releases = this.props.releases; const releasesArePending = this.props.releasesArePending; // TODO: Implement ItemListLayout without stateful stores const legacyReleaseStore: ItemListStore = { get isLoaded() { return !releasesArePending.get(); }, failedLoading: false, getTotalCount: () => releases.get().length, toggleSelection: (release) => release.toggle(), isSelectedAll: (releases) => ( releases.length > 0 && releases.every((release) => release.isSelected) ), toggleSelectionAll: (releases) => { let selected = false; if (!legacyReleaseStore.isSelectedAll(releases)) { selected = true; } for (const release of releases) { if (release.isSelected !== selected) { release.toggle(); } } }, isSelected: (release) => release.isSelected, removeSelectedItems: async () => { await Promise.all( releases.get() .filter((release) => release.isSelected) .map(release => release.delete()), ); }, pickOnlySelected: (releases) => releases.filter(release => release.isSelected), }; return ( store={legacyReleaseStore} getItems={() => releases.get()} preloadStores={false} isConfigurable tableId="helm_releases" className="HelmReleases" customizeTableRowProps={(item) => ({ testId: `helm-release-row-for-${item.getId()}` })} 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} ), 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 => ( )} customizeRemoveDialog={selectedItems => ({ message: this.renderRemoveDialogMessage(selectedItems), })} onDetails={this.onDetails} spinnerTestId="helm-releases-spinner" /> ); } } export const HelmReleases = withInjectables( NonInjectedHelmReleases, { getProps: (di) => { const routeParameters = di.inject(helmReleasesRouteParametersInjectable); return { releases: di.inject(removableReleasesInjectable), releasesArePending: di.inject(releasesInjectable).pending, selectNamespace: di.inject(namespaceStoreInjectable).selectNamespaces, navigateToHelmReleases: di.inject(navigateToHelmReleasesInjectable), namespace: routeParameters.namespace, }; }, }, );