mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Technical requirements for behavioural unit tests (#5084)
* Implement a lot of technical requirements for behavioural unit tests Note: the crux of this was to make routing env-agnostic, and not based on URLs as magic strings, but instead something type-enforced. Note: extension-based routes comply to same exact interface by "late-registering" their routes when installed. Routes are just injectables. Note: another chunk of global shared state is no more. Note: a lot of explicit side effects have been cornered to injectables. Note: a lot of stuff has become reactive as part if this. Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make a directory commonly available Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Require id for <Select /> to prevent non-deterministic renders This was caused by global state in a 3rd party lib: "react-select". Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Specify id for all <Select /> to satisfy previous commit Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Prevent explicit side effect in component by using existing dependency instead Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract instantiation of "conf" as injectables for causing side effects Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce a legacy-helper to make gradual refactoring of inheritors of Singleton easier Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make legacy unit tests for hotbar green and more simple by using the new legacy helper Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Temporarily kludge all unit tests green with a disclaimer about allowing side-effects Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove kludge in previous commit by explicitly permitting specific side effects where old unit tests require it Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Prevent old unit test with side effects from accessing file system Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Migrate to actual typing for di.permitSideEffects Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Prevent unit tests from failing because of non-standard method of HTML-element not present in js-dom Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Adapt integration tests to recent changes Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Fix code style Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Fix artifact from bad rebase Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Add a deprecation from a review comment Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove change that is not required Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove redundant comment Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix code style Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove redundant file Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix bad merge Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Improve variable name Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Tweak logger interface to be more descriptive Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make injecting legacy singleton always provide new instance Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove conditional typing when not needed Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Improve naming of variable Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove unnecessary code style changes Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove flag for causing side effects from too broad scope Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Override side-effects in unit test using injectable instead of monkey patching Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Flag some side-effects and add general overrides Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Fix unit tests in CI by removing explicit side-effect Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove explicit side-effect from getting default shell Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce abstraction for getting absolute paths Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using abstraction for getting absolute path to control explicit side effect Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce abstraction for joining paths Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using abstraction for joining paths to control explicit side effect Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix fake implementation for join paths Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix test after removing explicit side effect Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove explicit side effects from kubeconfig-syncs Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix arguments after removing explicit side effect Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make registrators not async for not being needed anymore Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make generalCatalogEntities non-observable, as there is no requirement Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove redundant code Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify logic for registering general catalog entity sources Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add TODO Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Replace function for getting application menu items with reactive solution Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix typo in interface name Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove global shared state usages of hot bar store Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove redundant enum Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
1 parent
ab2cd27a27
commit
a277cfcf02
583 files changed
+13073
-6556
No files matched your search
+25
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import ipcRendererInjectable from "./ipc-renderer/ipc-renderer.injectable";
|
||||
import type {
|
||||
IpcChannelListener,
|
||||
} from "../../ipc-channel-listeners/ipc-channel-listener-injection-token";
|
||||
|
||||
const registerIpcChannelListenerInjectable = getInjectable({
|
||||
id: "register-ipc-channel-listener",
|
||||
|
||||
instantiate: (di) => {
|
||||
const ipc = di.inject(ipcRendererInjectable);
|
||||
|
||||
return ({ channel, handle }: IpcChannelListener) => {
|
||||
ipc.on(channel.name, (_, data) => {
|
||||
handle(data);
|
||||
});
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default registerIpcChannelListenerInjectable;
|
||||
@@ -20,9 +20,7 @@ import { DefaultProps } from "./mui-base-theme";
|
||||
import configurePackages from "../common/configure-packages";
|
||||
import * as initializers from "./initializers";
|
||||
import logger from "../common/logger";
|
||||
import { HotbarStore } from "../common/hotbar-store";
|
||||
import { WeblinkStore } from "../common/weblink-store";
|
||||
import { ThemeStore } from "./theme.store";
|
||||
import { SentryInit } from "../common/sentry";
|
||||
import { registerCustomThemes } from "./components/monaco-editor";
|
||||
import { getDi } from "./getDi";
|
||||
@@ -36,6 +34,15 @@ import userStoreInjectable from "../common/user-store/user-store.injectable";
|
||||
import initRootFrameInjectable from "./frames/root-frame/init-root-frame/init-root-frame.injectable";
|
||||
import initClusterFrameInjectable from "./frames/cluster-frame/init-cluster-frame/init-cluster-frame.injectable";
|
||||
import commandOverlayInjectable from "./components/command-palette/command-overlay.injectable";
|
||||
import { Router } from "react-router";
|
||||
import historyInjectable from "./navigation/history.injectable";
|
||||
import themeStoreInjectable from "./theme-store.injectable";
|
||||
import navigateToAddClusterInjectable from "../common/front-end-routing/routes/add-cluster/navigate-to-add-cluster.injectable";
|
||||
import addSyncEntriesInjectable from "./initializers/add-sync-entries.injectable";
|
||||
import hotbarStoreInjectable from "../common/hotbar-store.injectable";
|
||||
import { bindEvents } from "./navigation/events";
|
||||
import deleteClusterDialogModelInjectable
|
||||
from "./components/delete-cluster-dialog/delete-cluster-dialog-model/delete-cluster-dialog-model.injectable";
|
||||
|
||||
if (process.isMainFrame) {
|
||||
SentryInit();
|
||||
@@ -58,6 +65,9 @@ async function attachChromeDebugger() {
|
||||
export async function bootstrap(di: DiContainer) {
|
||||
await di.runSetups();
|
||||
|
||||
// TODO: Consolidate import time side-effect to setup time
|
||||
bindEvents();
|
||||
|
||||
const rootElem = document.getElementById("app");
|
||||
const logPrefix = `[BOOTSTRAP-${process.isMainFrame ? "ROOT" : "CLUSTER"}-FRAME]:`;
|
||||
|
||||
@@ -79,12 +89,19 @@ export async function bootstrap(di: DiContainer) {
|
||||
logger.info(`${logPrefix} initializing CatalogEntityDetailRegistry`);
|
||||
initializers.initCatalogEntityDetailRegistry();
|
||||
|
||||
const navigateToAddCluster = di.inject(navigateToAddClusterInjectable);
|
||||
const addSyncEntries = di.inject(addSyncEntriesInjectable);
|
||||
|
||||
logger.info(`${logPrefix} initializing CatalogCategoryRegistryEntries`);
|
||||
initializers.initCatalogCategoryRegistryEntries();
|
||||
initializers.initCatalogCategoryRegistryEntries({
|
||||
navigateToAddCluster,
|
||||
addSyncEntries,
|
||||
});
|
||||
|
||||
logger.info(`${logPrefix} initializing Catalog`);
|
||||
initializers.initCatalog({
|
||||
openCommandDialog: di.inject(commandOverlayInjectable).open,
|
||||
deleteClusterDialogModel: di.inject(deleteClusterDialogModelInjectable),
|
||||
});
|
||||
|
||||
const extensionLoader = di.inject(extensionLoaderInjectable);
|
||||
@@ -104,10 +121,11 @@ export async function bootstrap(di: DiContainer) {
|
||||
await clusterStore.loadInitialOnRenderer();
|
||||
|
||||
// HotbarStore depends on: ClusterStore
|
||||
HotbarStore.createInstance();
|
||||
di.inject(hotbarStoreInjectable);
|
||||
|
||||
// ThemeStore depends on: UserStore
|
||||
ThemeStore.createInstance();
|
||||
// TODO: Remove temporal dependencies
|
||||
di.inject(themeStoreInjectable);
|
||||
|
||||
WeblinkStore.createInstance();
|
||||
|
||||
@@ -142,9 +160,13 @@ export async function bootstrap(di: DiContainer) {
|
||||
});
|
||||
}
|
||||
|
||||
const history = di.inject(historyInjectable);
|
||||
|
||||
render(
|
||||
<DiContextProvider value={{ di }}>
|
||||
{DefaultProps(App)}
|
||||
<Router history={history}>
|
||||
{DefaultProps(App)}
|
||||
</Router>
|
||||
</DiContextProvider>,
|
||||
|
||||
rootElem,
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { AddCluster } from "./add-cluster";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import addClusterRouteInjectable from "../../../common/front-end-routing/routes/add-cluster/add-cluster-route.injectable";
|
||||
|
||||
const addClusterRouteComponentInjectable = getInjectable({
|
||||
id: "add-cluster-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(addClusterRouteInjectable),
|
||||
Component: AddCluster,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default addClusterRouteComponentInjectable;
|
||||
@@ -13,20 +13,17 @@ import { observer } from "mobx-react";
|
||||
import path from "path";
|
||||
import React from "react";
|
||||
import * as uuid from "uuid";
|
||||
|
||||
import { catalogURL } from "../../../common/routes";
|
||||
import { appEventBus } from "../../../common/app-event-bus/event-bus";
|
||||
import { loadConfigFromString, splitConfig } from "../../../common/kube-helpers";
|
||||
import { docsUrl } from "../../../common/vars";
|
||||
import { navigate } from "../../navigation";
|
||||
import { iter } from "../../utils";
|
||||
import { Button } from "../button";
|
||||
import { Notifications } from "../notifications";
|
||||
import { SettingLayout } from "../layout/setting-layout";
|
||||
import { MonacoEditor } from "../monaco-editor";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import getCustomKubeConfigDirectoryInjectable
|
||||
from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
|
||||
import getCustomKubeConfigDirectoryInjectable from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
|
||||
import navigateToCatalogInjectable, { NavigateToCatalog } from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
|
||||
|
||||
interface Option {
|
||||
config: KubeConfig;
|
||||
@@ -35,6 +32,7 @@ interface Option {
|
||||
|
||||
interface Dependencies {
|
||||
getCustomKubeConfigDirectory: (directoryName: string) => string;
|
||||
navigateToCatalog: NavigateToCatalog;
|
||||
}
|
||||
|
||||
function getContexts(config: KubeConfig): Map<string, Option> {
|
||||
@@ -96,7 +94,7 @@ class NonInjectedAddCluster extends React.Component<Dependencies> {
|
||||
|
||||
Notifications.ok(`Successfully added ${this.kubeContexts.size} new cluster(s)`);
|
||||
|
||||
return navigate(catalogURL());
|
||||
return this.props.navigateToCatalog();
|
||||
} catch (error) {
|
||||
Notifications.error(`Failed to add clusters: ${error}`);
|
||||
}
|
||||
@@ -104,7 +102,7 @@ class NonInjectedAddCluster extends React.Component<Dependencies> {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SettingLayout className={styles.AddClusters}>
|
||||
<SettingLayout className={styles.AddClusters} data-testid="add-cluster-page">
|
||||
<h2>Add Clusters from Kubeconfig</h2>
|
||||
<p>
|
||||
Clusters added here are <b>not</b> merged into the <code>~/.kube/config</code> file.{" "}
|
||||
@@ -149,5 +147,7 @@ export const AddCluster = withInjectables<Dependencies>(NonInjectedAddCluster, {
|
||||
getCustomKubeConfigDirectory: di.inject(
|
||||
getCustomKubeConfigDirectoryInjectable,
|
||||
),
|
||||
|
||||
navigateToCatalog: di.inject(navigateToCatalogInjectable),
|
||||
}),
|
||||
});
|
||||
@@ -12,6 +12,7 @@ import { CatalogCategory } from "../../../api/catalog-entity";
|
||||
import { getDiForUnitTesting } from "../../../getDiForUnitTesting";
|
||||
import type { AdditionalCategoryColumnRegistration, CategoryColumnRegistration } from "../custom-category-columns";
|
||||
import getCategoryColumnsInjectable, { CategoryColumns, GetCategoryColumnsParams } from "../get-category-columns.injectable";
|
||||
import hotbarStoreInjectable from "../../../../common/hotbar-store.injectable";
|
||||
|
||||
class TestCategory extends CatalogCategory {
|
||||
apiVersion = "catalog.k8slens.dev/v1alpha1";
|
||||
@@ -39,6 +40,8 @@ describe("Custom Category Columns", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
di = getDiForUnitTesting();
|
||||
|
||||
di.override(hotbarStoreInjectable, () => ({}));
|
||||
});
|
||||
|
||||
describe("without extensions", () => {
|
||||
|
||||
+1
-2
@@ -3,5 +3,4 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
.Network {
|
||||
}
|
||||
export const browseCatalogTab = "browse";
|
||||
+1
-1
@@ -3,8 +3,8 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { browseCatalogTab } from "../../../../common/routes";
|
||||
import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable";
|
||||
import { browseCatalogTab } from "../catalog-browse-tab";
|
||||
|
||||
const catalogPreviousActiveTabStorageInjectable = getInjectable({
|
||||
id: "catalog-previous-active-tab-storage",
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { Catalog } from "./catalog";
|
||||
import catalogRouteInjectable from "../../../common/front-end-routing/routes/catalog/catalog-route.injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const catalogRouteComponentInjectable = getInjectable({
|
||||
id: "catalog-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(catalogRouteInjectable),
|
||||
Component: Catalog,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default catalogRouteComponentInjectable;
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import routePathParametersInjectable from "../../routes/route-path-parameters.injectable";
|
||||
import catalogRouteInjectable from "../../../common/front-end-routing/routes/catalog/catalog-route.injectable";
|
||||
|
||||
const catalogRouteParametersInjectable = getInjectable({
|
||||
id: "catalog-route-parameters",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(catalogRouteInjectable);
|
||||
const pathParameters = di.inject(routePathParametersInjectable, route);
|
||||
|
||||
return {
|
||||
group: computed(() => pathParameters.get().group),
|
||||
kind: computed(() => pathParameters.get().kind),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default catalogRouteParametersInjectable;
|
||||
@@ -7,7 +7,6 @@ import React from "react";
|
||||
import { screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { Catalog } from "./catalog";
|
||||
import { createMemoryHistory } from "history";
|
||||
import { mockWindow } from "../../../../__mocks__/windowMock";
|
||||
import { CatalogCategoryRegistry, CatalogEntity, CatalogEntityActionContext, CatalogEntityData } from "../../../common/catalog";
|
||||
import { CatalogEntityRegistry } from "../../api/catalog-entity-registry";
|
||||
@@ -16,15 +15,15 @@ import type { CatalogEntityStore } from "./catalog-entity-store/catalog-entity.s
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import catalogEntityStoreInjectable from "./catalog-entity-store/catalog-entity-store.injectable";
|
||||
import catalogEntityRegistryInjectable
|
||||
from "../../api/catalog-entity-registry/catalog-entity-registry.injectable";
|
||||
import catalogEntityRegistryInjectable from "../../api/catalog-entity-registry/catalog-entity-registry.injectable";
|
||||
import type { DiRender } from "../test-utils/renderFor";
|
||||
import { renderFor } from "../test-utils/renderFor";
|
||||
import { ThemeStore } from "../../theme.store";
|
||||
import { UserStore } from "../../../common/user-store";
|
||||
import mockFs from "mock-fs";
|
||||
import directoryForUserDataInjectable
|
||||
from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
|
||||
import appVersionInjectable from "../../../common/get-configuration-file-model/app-version/app-version.injectable";
|
||||
import type { AppEvent } from "../../../common/app-event-bus/event-bus";
|
||||
import appEventBusInjectable from "../../../common/app-event-bus/app-event-bus.injectable";
|
||||
|
||||
@@ -66,25 +65,6 @@ class MockCatalogEntity extends CatalogEntity {
|
||||
}
|
||||
|
||||
describe("<Catalog />", () => {
|
||||
const history = createMemoryHistory();
|
||||
const mockLocation = {
|
||||
pathname: "",
|
||||
search: "",
|
||||
state: "",
|
||||
hash: "",
|
||||
};
|
||||
const mockMatch = {
|
||||
params: {
|
||||
// will be used to match activeCategory
|
||||
// need to be the same as property values in kubernetesClusterCategory
|
||||
group: "entity.k8slens.dev",
|
||||
kind: "KubernetesCluster",
|
||||
},
|
||||
isExact: true,
|
||||
path: "",
|
||||
url: "",
|
||||
};
|
||||
|
||||
function createMockCatalogEntity(onRun: (context: CatalogEntityActionContext) => void | Promise<void>) {
|
||||
return new MockCatalogEntity({
|
||||
metadata: {
|
||||
@@ -112,6 +92,9 @@ describe("<Catalog />", () => {
|
||||
|
||||
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
|
||||
|
||||
di.permitSideEffects(getConfigurationFileModelInjectable);
|
||||
di.permitSideEffects(appVersionInjectable);
|
||||
|
||||
await di.runSetups();
|
||||
|
||||
mockFs();
|
||||
@@ -169,11 +152,7 @@ describe("<Catalog />", () => {
|
||||
);
|
||||
|
||||
render(
|
||||
<Catalog
|
||||
history={history}
|
||||
location={mockLocation}
|
||||
match={mockMatch}
|
||||
/>,
|
||||
<Catalog />,
|
||||
);
|
||||
|
||||
userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon"));
|
||||
@@ -198,13 +177,7 @@ describe("<Catalog />", () => {
|
||||
},
|
||||
);
|
||||
|
||||
render(
|
||||
<Catalog
|
||||
history={history}
|
||||
location={mockLocation}
|
||||
match={mockMatch}
|
||||
/>,
|
||||
);
|
||||
render(<Catalog />);
|
||||
|
||||
userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon"));
|
||||
});
|
||||
@@ -229,13 +202,7 @@ describe("<Catalog />", () => {
|
||||
},
|
||||
);
|
||||
|
||||
render(
|
||||
<Catalog
|
||||
history={history}
|
||||
location={mockLocation}
|
||||
match={mockMatch}
|
||||
/>,
|
||||
);
|
||||
render(<Catalog />);
|
||||
|
||||
userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon"));
|
||||
});
|
||||
@@ -255,13 +222,7 @@ describe("<Catalog />", () => {
|
||||
},
|
||||
);
|
||||
|
||||
render(
|
||||
<Catalog
|
||||
history={history}
|
||||
location={mockLocation}
|
||||
match={mockMatch}
|
||||
/>,
|
||||
);
|
||||
render(<Catalog />);
|
||||
|
||||
userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon"));
|
||||
});
|
||||
@@ -288,13 +249,7 @@ describe("<Catalog />", () => {
|
||||
},
|
||||
);
|
||||
|
||||
render(
|
||||
<Catalog
|
||||
history={history}
|
||||
location={mockLocation}
|
||||
match={mockMatch}
|
||||
/>,
|
||||
);
|
||||
render(<Catalog />);
|
||||
|
||||
userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon"));
|
||||
});
|
||||
@@ -319,24 +274,14 @@ describe("<Catalog />", () => {
|
||||
},
|
||||
);
|
||||
|
||||
render(
|
||||
<Catalog
|
||||
history={history}
|
||||
location={mockLocation}
|
||||
match={mockMatch}
|
||||
/>,
|
||||
);
|
||||
render(<Catalog />);
|
||||
|
||||
userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon"));
|
||||
});
|
||||
|
||||
it("emits catalog open AppEvent", () => {
|
||||
render(
|
||||
<Catalog
|
||||
history={history}
|
||||
location={mockLocation}
|
||||
match={mockMatch}
|
||||
/>,
|
||||
<Catalog />,
|
||||
);
|
||||
|
||||
expect(emitEvent).toHaveBeenCalledWith( {
|
||||
@@ -347,11 +292,7 @@ describe("<Catalog />", () => {
|
||||
|
||||
it("emits catalog change AppEvent when changing the category", () => {
|
||||
render(
|
||||
<Catalog
|
||||
history={history}
|
||||
location={mockLocation}
|
||||
match={mockMatch}
|
||||
/>,
|
||||
<Catalog />,
|
||||
);
|
||||
|
||||
userEvent.click(screen.getByText("Web Links"));
|
||||
|
||||
@@ -8,21 +8,18 @@ import styles from "./catalog.module.scss";
|
||||
import React from "react";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import { ItemListLayout } from "../item-object-list";
|
||||
import { action, IComputedValue, makeObservable, observable, reaction, runInAction, when } from "mobx";
|
||||
import { action, computed, IComputedValue, makeObservable, observable, reaction, runInAction, when } from "mobx";
|
||||
import type { CatalogEntityStore } from "./catalog-entity-store/catalog-entity.store";
|
||||
import { navigate } from "../../navigation";
|
||||
import { MenuItem, MenuActions } from "../menu";
|
||||
import type { CatalogEntityContextMenu, CatalogEntityContextMenuContext } from "../../api/catalog-entity";
|
||||
import { HotbarStore } from "../../../common/hotbar-store";
|
||||
import { ConfirmDialog } from "../confirm-dialog";
|
||||
import { catalogCategoryRegistry, CatalogEntity } from "../../../common/catalog";
|
||||
import { CatalogAddButton } from "./catalog-add-button";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { Notifications } from "../notifications";
|
||||
import { MainLayout } from "../layout/main-layout";
|
||||
import { prevDefault } from "../../utils";
|
||||
import { CatalogEntityDetails } from "./catalog-entity-details";
|
||||
import { browseCatalogTab, catalogURL, CatalogViewRouteParam } from "../../../common/routes";
|
||||
import { CatalogMenu } from "./catalog-menu";
|
||||
import { RenderDelay } from "../render-delay/render-delay";
|
||||
import { Icon } from "../icon";
|
||||
@@ -36,10 +33,13 @@ import getCategoryColumnsInjectable from "./get-category-columns.injectable";
|
||||
import type { RegisteredCustomCategoryViewDecl } from "./custom-views.injectable";
|
||||
import customCategoryViewsInjectable from "./custom-views.injectable";
|
||||
import type { CustomCategoryViewComponents } from "./custom-views";
|
||||
import navigateToCatalogInjectable, { NavigateToCatalog } from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
|
||||
import catalogRouteParametersInjectable from "./catalog-route-parameters.injectable";
|
||||
import { browseCatalogTab } from "./catalog-browse-tab";
|
||||
import type { AppEvent } from "../../../common/app-event-bus/event-bus";
|
||||
import appEventBusInjectable from "../../../common/app-event-bus/app-event-bus.injectable";
|
||||
|
||||
export interface CatalogProps extends RouteComponentProps<CatalogViewRouteParam> {}
|
||||
import hotbarStoreInjectable from "../../../common/hotbar-store.injectable";
|
||||
import type { HotbarStore } from "../../../common/hotbar-store";
|
||||
|
||||
interface Dependencies {
|
||||
catalogPreviousActiveTabStorage: { set: (value: string ) => void };
|
||||
@@ -47,23 +47,35 @@ interface Dependencies {
|
||||
getCategoryColumns: (params: GetCategoryColumnsParams) => CategoryColumns;
|
||||
customCategoryViews: IComputedValue<Map<string, Map<string, RegisteredCustomCategoryViewDecl>>>;
|
||||
emitEvent: (event: AppEvent) => void;
|
||||
|
||||
routeParameters: {
|
||||
group: IComputedValue<string>;
|
||||
kind: IComputedValue<string>;
|
||||
};
|
||||
|
||||
navigateToCatalog: NavigateToCatalog;
|
||||
hotbarStore: HotbarStore;
|
||||
}
|
||||
|
||||
@observer
|
||||
class NonInjectedCatalog extends React.Component<CatalogProps & Dependencies> {
|
||||
class NonInjectedCatalog extends React.Component<Dependencies> {
|
||||
@observable private contextMenu: CatalogEntityContextMenuContext;
|
||||
@observable activeTab?: string;
|
||||
|
||||
constructor(props: CatalogProps & Dependencies) {
|
||||
constructor(props: Dependencies) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@computed
|
||||
get routeActiveTab(): string {
|
||||
const { group, kind } = this.props.match.params ?? {};
|
||||
const { group, kind } = this.props.routeParameters;
|
||||
|
||||
if (group && kind) {
|
||||
return `${group}/${kind}`;
|
||||
const dereferencedGroup = group.get();
|
||||
const dereferencedKind = kind.get();
|
||||
|
||||
if (dereferencedGroup && dereferencedKind) {
|
||||
return `${dereferencedGroup}/${dereferencedKind}`;
|
||||
}
|
||||
|
||||
return browseCatalogTab;
|
||||
@@ -115,11 +127,11 @@ class NonInjectedCatalog extends React.Component<CatalogProps & Dependencies> {
|
||||
}
|
||||
|
||||
addToHotbar(entity: CatalogEntity): void {
|
||||
HotbarStore.getInstance().addToHotbar(entity);
|
||||
this.props.hotbarStore.addToHotbar(entity);
|
||||
}
|
||||
|
||||
removeFromHotbar(entity: CatalogEntity): void {
|
||||
HotbarStore.getInstance().removeFromHotbar(entity.getId());
|
||||
this.props.hotbarStore.removeFromHotbar(entity.getId());
|
||||
}
|
||||
|
||||
onDetails = (entity: CatalogEntity) => {
|
||||
@@ -163,9 +175,10 @@ class NonInjectedCatalog extends React.Component<CatalogProps & Dependencies> {
|
||||
});
|
||||
|
||||
if (activeCategory) {
|
||||
navigate(catalogURL({ params: { group: activeCategory.spec.group, kind: activeCategory.spec.names.kind }}));
|
||||
|
||||
this.props.navigateToCatalog({ group: activeCategory.spec.group, kind: activeCategory.spec.names.kind });
|
||||
} else {
|
||||
navigate(catalogURL({ params: { group: browseCatalogTab }}));
|
||||
this.props.navigateToCatalog({ group: browseCatalogTab });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -205,7 +218,7 @@ class NonInjectedCatalog extends React.Component<CatalogProps & Dependencies> {
|
||||
};
|
||||
|
||||
renderName(entity: CatalogEntity) {
|
||||
const isItemInHotbar = HotbarStore.getInstance().isAddedToActive(entity);
|
||||
const isItemInHotbar = this.props.hotbarStore.isAddedToActive(entity);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -321,13 +334,15 @@ class NonInjectedCatalog extends React.Component<CatalogProps & Dependencies> {
|
||||
}
|
||||
}
|
||||
|
||||
export const Catalog = withInjectables<Dependencies, CatalogProps>( NonInjectedCatalog, {
|
||||
getProps: (di, props) => ({
|
||||
export const Catalog = withInjectables<Dependencies>( NonInjectedCatalog, {
|
||||
getProps: (di) => ({
|
||||
catalogEntityStore: di.inject(catalogEntityStoreInjectable),
|
||||
catalogPreviousActiveTabStorage: di.inject(catalogPreviousActiveTabStorageInjectable),
|
||||
getCategoryColumns: di.inject(getCategoryColumnsInjectable),
|
||||
customCategoryViews: di.inject(customCategoryViewsInjectable),
|
||||
routeParameters: di.inject(catalogRouteParametersInjectable),
|
||||
navigateToCatalog: di.inject(navigateToCatalogInjectable),
|
||||
emitEvent: di.inject(appEventBusInjectable).emit,
|
||||
...props,
|
||||
hotbarStore: di.inject(hotbarStoreInjectable),
|
||||
}),
|
||||
});
|
||||
@@ -9,10 +9,12 @@ import type { CatalogCategory, CatalogEntity } from "../../../common/catalog";
|
||||
import type { ItemListLayoutProps } from "../item-object-list";
|
||||
import type { RegisteredAdditionalCategoryColumn } from "./custom-category-columns";
|
||||
import categoryColumnsInjectable from "./custom-category-columns.injectable";
|
||||
import { defaultCategoryColumns, browseAllColumns, nameCategoryColumn } from "./internal-category-columns";
|
||||
import { defaultCategoryColumns, browseAllColumns } from "./internal-category-columns";
|
||||
import nameCategoryColumnInjectable from "./name-category-column.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
extensionColumns: IComputedValue<Map<string, Map<string, RegisteredAdditionalCategoryColumn[]>>>;
|
||||
nameCategoryColumn: RegisteredAdditionalCategoryColumn;
|
||||
}
|
||||
|
||||
export interface GetCategoryColumnsParams {
|
||||
@@ -21,7 +23,7 @@ export interface GetCategoryColumnsParams {
|
||||
|
||||
export type CategoryColumns = Required<Pick<ItemListLayoutProps<CatalogEntity>, "sortingCallbacks" | "searchFilters" | "renderTableContents" | "renderTableHeader">>;
|
||||
|
||||
function getSpecificCategoryColumns(activeCategory: CatalogCategory, extensionColumns: IComputedValue<Map<string, Map<string, RegisteredAdditionalCategoryColumn[]>>>): RegisteredAdditionalCategoryColumn[] {
|
||||
function getSpecificCategoryColumns(activeCategory: CatalogCategory, extensionColumns: IComputedValue<Map<string, Map<string, RegisteredAdditionalCategoryColumn[]>>>, nameCategoryColumn: RegisteredAdditionalCategoryColumn): RegisteredAdditionalCategoryColumn[] {
|
||||
const fromExtensions = (
|
||||
extensionColumns
|
||||
.get()
|
||||
@@ -41,7 +43,7 @@ function getSpecificCategoryColumns(activeCategory: CatalogCategory, extensionCo
|
||||
];
|
||||
}
|
||||
|
||||
function getBrowseAllColumns(): RegisteredAdditionalCategoryColumn[] {
|
||||
function getBrowseAllColumns(nameCategoryColumn: RegisteredAdditionalCategoryColumn): RegisteredAdditionalCategoryColumn[] {
|
||||
return [
|
||||
...browseAllColumns,
|
||||
nameCategoryColumn,
|
||||
@@ -49,11 +51,11 @@ function getBrowseAllColumns(): RegisteredAdditionalCategoryColumn[] {
|
||||
];
|
||||
}
|
||||
|
||||
const getCategoryColumns = ({ extensionColumns }: Dependencies) => ({ activeCategory }: GetCategoryColumnsParams): CategoryColumns => {
|
||||
const getCategoryColumns = ({ extensionColumns, nameCategoryColumn }: Dependencies) => ({ activeCategory }: GetCategoryColumnsParams): CategoryColumns => {
|
||||
const allRegistrations = orderBy(
|
||||
activeCategory
|
||||
? getSpecificCategoryColumns(activeCategory, extensionColumns)
|
||||
: getBrowseAllColumns(),
|
||||
? getSpecificCategoryColumns(activeCategory, extensionColumns, nameCategoryColumn)
|
||||
: getBrowseAllColumns(nameCategoryColumn),
|
||||
"priority",
|
||||
"asc",
|
||||
);
|
||||
@@ -89,6 +91,7 @@ const getCategoryColumnsInjectable = getInjectable({
|
||||
|
||||
instantiate: (di) => getCategoryColumns({
|
||||
extensionColumns: di.inject(categoryColumnsInjectable),
|
||||
nameCategoryColumn: di.inject(nameCategoryColumnInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -4,26 +4,54 @@
|
||||
*/
|
||||
import React, { ReactNode, useState } from "react";
|
||||
|
||||
import { HotbarStore } from "../../../common/hotbar-store";
|
||||
import { MenuItem } from "../menu";
|
||||
|
||||
import type { CatalogEntity } from "../../api/catalog-entity";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import hotbarStoreInjectable from "../../../common/hotbar-store.injectable";
|
||||
import type { HotbarStore } from "../../../common/hotbar-store";
|
||||
|
||||
export function HotbarToggleMenuItem(props: { entity: CatalogEntity; addContent: ReactNode; removeContent: ReactNode }) {
|
||||
const store = HotbarStore.getInstance();
|
||||
const [itemInHotbar, setItemInHotbar] = useState(store.isAddedToActive(props.entity));
|
||||
interface Dependencies {
|
||||
hotbarStore: HotbarStore;
|
||||
}
|
||||
|
||||
interface HotbarToggleMenuItemProps {
|
||||
entity: CatalogEntity;
|
||||
addContent: ReactNode;
|
||||
removeContent: ReactNode;
|
||||
}
|
||||
|
||||
function NonInjectedHotbarToggleMenuItem({
|
||||
addContent,
|
||||
entity,
|
||||
hotbarStore,
|
||||
removeContent,
|
||||
}: Dependencies & HotbarToggleMenuItemProps) {
|
||||
const [itemInHotbar, setItemInHotbar] = useState(hotbarStore.isAddedToActive(entity));
|
||||
|
||||
return (
|
||||
<MenuItem onClick={() => {
|
||||
if (itemInHotbar) {
|
||||
store.removeFromHotbar(props.entity.getId());
|
||||
hotbarStore.removeFromHotbar(entity.getId());
|
||||
setItemInHotbar(false);
|
||||
} else {
|
||||
store.addToHotbar(props.entity);
|
||||
hotbarStore.addToHotbar(entity);
|
||||
setItemInHotbar(true);
|
||||
}
|
||||
}}>
|
||||
{itemInHotbar ? props.removeContent : props.addContent }
|
||||
{itemInHotbar ? removeContent : addContent }
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
|
||||
export const HotbarToggleMenuItem = withInjectables<Dependencies, HotbarToggleMenuItemProps>(
|
||||
NonInjectedHotbarToggleMenuItem,
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
hotbarStore: di.inject(hotbarStoreInjectable),
|
||||
...props,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -6,49 +6,10 @@
|
||||
import styles from "./catalog.module.scss";
|
||||
|
||||
import React from "react";
|
||||
import { HotbarStore } from "../../../common/hotbar-store";
|
||||
import type { CatalogEntity } from "../../api/catalog-entity";
|
||||
import { Avatar } from "../avatar";
|
||||
import type { RegisteredAdditionalCategoryColumn } from "./custom-category-columns";
|
||||
import { Icon } from "../icon";
|
||||
import { prevDefault } from "../../utils";
|
||||
import { getLabelBadges } from "./helpers";
|
||||
import { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
|
||||
function renderEntityName(entity: CatalogEntity) {
|
||||
const hotbarStore = HotbarStore.getInstance();
|
||||
const isItemInHotbar = hotbarStore.isAddedToActive(entity);
|
||||
const onClick = prevDefault(
|
||||
isItemInHotbar
|
||||
? () => hotbarStore.removeFromHotbar(entity.getId())
|
||||
: () => hotbarStore.addToHotbar(entity),
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Avatar
|
||||
title={entity.getName()}
|
||||
colorHash={`${entity.getName()}-${entity.getSource()}`}
|
||||
src={entity.spec.icon?.src}
|
||||
background={entity.spec.icon?.background}
|
||||
className={styles.catalogAvatar}
|
||||
size={24}
|
||||
>
|
||||
{entity.spec.icon?.material && <Icon material={entity.spec.icon?.material} small/>}
|
||||
</Avatar>
|
||||
<span>{entity.getName()}</span>
|
||||
<Icon
|
||||
small
|
||||
className={styles.pinIcon}
|
||||
material={!isItemInHotbar && "push_pin"}
|
||||
svg={isItemInHotbar ? "push_off" : "push_pin"}
|
||||
tooltip={isItemInHotbar ? "Remove from Hotbar" : "Add to Hotbar"}
|
||||
onClick={onClick}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const browseAllColumns: RegisteredAdditionalCategoryColumn[] = [
|
||||
{
|
||||
id: "kind",
|
||||
@@ -63,20 +24,6 @@ export const browseAllColumns: RegisteredAdditionalCategoryColumn[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export const nameCategoryColumn: RegisteredAdditionalCategoryColumn = {
|
||||
id: "name",
|
||||
priority: 0,
|
||||
renderCell: renderEntityName,
|
||||
titleProps: {
|
||||
title: "Name",
|
||||
className: styles.entityName,
|
||||
id: "name",
|
||||
sortBy: "name",
|
||||
},
|
||||
searchFilter: entity => entity.getName(),
|
||||
sortCallback: entity => `name=${entity.getName()}`,
|
||||
};
|
||||
|
||||
export const defaultCategoryColumns: RegisteredAdditionalCategoryColumn[] = [
|
||||
{
|
||||
id: "source",
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import styles from "./catalog.module.scss";
|
||||
import type { CatalogEntity } from "../../../common/catalog";
|
||||
import { prevDefault } from "../../utils";
|
||||
import { Avatar } from "../avatar";
|
||||
import { Icon } from "../icon";
|
||||
import React from "react";
|
||||
import type { RegisteredAdditionalCategoryColumn } from "./custom-category-columns";
|
||||
import hotbarStoreInjectable from "../../../common/hotbar-store.injectable";
|
||||
import type { HotbarStore } from "../../../common/hotbar-store";
|
||||
|
||||
const renderEntityName = (hotbarStore: HotbarStore) => (entity: CatalogEntity) => {
|
||||
const isItemInHotbar = hotbarStore.isAddedToActive(entity);
|
||||
const onClick = prevDefault(
|
||||
isItemInHotbar
|
||||
? () => hotbarStore.removeFromHotbar(entity.getId())
|
||||
: () => hotbarStore.addToHotbar(entity),
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Avatar
|
||||
title={entity.getName()}
|
||||
colorHash={`${entity.getName()}-${entity.getSource()}`}
|
||||
src={entity.spec.icon?.src}
|
||||
background={entity.spec.icon?.background}
|
||||
className={styles.catalogAvatar}
|
||||
size={24}
|
||||
>
|
||||
{entity.spec.icon?.material && <Icon material={entity.spec.icon?.material} small/>}
|
||||
</Avatar>
|
||||
<span>{entity.getName()}</span>
|
||||
<Icon
|
||||
small
|
||||
className={styles.pinIcon}
|
||||
material={!isItemInHotbar && "push_pin"}
|
||||
svg={isItemInHotbar ? "push_off" : "push_pin"}
|
||||
tooltip={isItemInHotbar ? "Remove from Hotbar" : "Add to Hotbar"}
|
||||
onClick={onClick}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const nameCategoryColumnInjectable = getInjectable({
|
||||
id: "name-category-column",
|
||||
instantiate: (di): RegisteredAdditionalCategoryColumn => ({
|
||||
id: "name",
|
||||
priority: 0,
|
||||
renderCell: renderEntityName(di.inject(hotbarStoreInjectable)),
|
||||
titleProps: {
|
||||
title: "Name",
|
||||
className: styles.entityName,
|
||||
id: "name",
|
||||
sortBy: "name",
|
||||
},
|
||||
searchFilter: (entity) => entity.getName(),
|
||||
sortCallback: (entity) => `name=${entity.getName()}`,
|
||||
}),
|
||||
});
|
||||
|
||||
export default nameCategoryColumnInjectable;
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import { ClusterOverview } from "./cluster-overview";
|
||||
import clusterOverviewRouteInjectable from "../../../common/front-end-routing/routes/cluster/overview/cluster-overview-route.injectable";
|
||||
|
||||
const clusterOverviewRouteComponentInjectable = getInjectable({
|
||||
id: "cluster-overview-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(clusterOverviewRouteInjectable),
|
||||
Component: ClusterOverview,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default clusterOverviewRouteComponentInjectable;
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { Icon } from "../icon";
|
||||
import React from "react";
|
||||
import {
|
||||
SidebarItemRegistration,
|
||||
sidebarItemsInjectionToken,
|
||||
} from "../layout/sidebar-items.injectable";
|
||||
import { computed } from "mobx";
|
||||
import clusterOverviewRouteInjectable from "../../../common/front-end-routing/routes/cluster/overview/cluster-overview-route.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToClusterOverviewInjectable from "../../../common/front-end-routing/routes/cluster/overview/navigate-to-cluster-overview.injectable";
|
||||
|
||||
const clusterOverviewSidebarItemsInjectable = getInjectable({
|
||||
id: "cluster-overview-sidebar-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(clusterOverviewRouteInjectable);
|
||||
const navigateToClusterOverview = di.inject(navigateToClusterOverviewInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return computed((): SidebarItemRegistration[] => [
|
||||
{
|
||||
id: "cluster-overview",
|
||||
parentId: null,
|
||||
title: "Cluster",
|
||||
getIcon: () => <Icon svg="kube" />,
|
||||
onClick: navigateToClusterOverview,
|
||||
isActive: routeIsActive,
|
||||
isVisible: route.isEnabled,
|
||||
orderNumber: 10,
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default clusterOverviewSidebarItemsInjectable;
|
||||
@@ -11,7 +11,7 @@ import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import { nodesStore } from "../+nodes/nodes.store";
|
||||
import { podsStore } from "../+workloads-pods/pods.store";
|
||||
import { Disposer, getHostedClusterId, interval } from "../../utils";
|
||||
import { TabLayout } from "../layout/tab-layout";
|
||||
import { TabLayout } from "../layout/tab-layout-2";
|
||||
import { Spinner } from "../spinner";
|
||||
import { ClusterIssues } from "./cluster-issues";
|
||||
import { ClusterMetrics } from "./cluster-metrics";
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { clusterRoute, clusterURL } from "../../../common/routes";
|
||||
import type { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import isAllowedResourceInjectable from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
|
||||
export interface ClusterSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
isAllowedResource: IsAllowedResource;
|
||||
}
|
||||
|
||||
const NonInjectedClusterSidebarItem = observer(({ isAllowedResource }: Dependencies & ClusterSidebarItemProps) => (
|
||||
<SidebarItem
|
||||
id="cluster"
|
||||
text="Cluster"
|
||||
isActive={isActiveRoute(clusterRoute)}
|
||||
isHidden={!isAllowedResource("nodes")}
|
||||
url={clusterURL()}
|
||||
icon={<Icon svg="kube"/>}
|
||||
/>
|
||||
));
|
||||
|
||||
export const ClusterSidebarItem = withInjectables<Dependencies, ClusterSidebarItemProps>(NonInjectedClusterSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import horizontalPodAutoscalersRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable";
|
||||
import { configSidebarItemId } from "../+config/config-sidebar-items.injectable";
|
||||
import { SidebarItemRegistration, sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToHorizontalPodAutoscalersInjectable from "../../../common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/navigate-to-horizontal-pod-autoscalers.injectable";
|
||||
|
||||
const horizontalPodAutoScalersSidebarItemsInjectable = getInjectable({
|
||||
id: "horizontal-pod-auto-scalers-sidebar-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(horizontalPodAutoscalersRouteInjectable);
|
||||
const navigateToHorizontalPodAutoscalers = di.inject(navigateToHorizontalPodAutoscalersInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return computed((): SidebarItemRegistration[] => [
|
||||
{
|
||||
id: "horizontal-pod-auto-scalers",
|
||||
parentId: configSidebarItemId,
|
||||
title: "HPA",
|
||||
onClick: navigateToHorizontalPodAutoscalers,
|
||||
isActive: routeIsActive,
|
||||
isVisible: route.isEnabled,
|
||||
orderNumber: 50,
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default horizontalPodAutoScalersSidebarItemsInjectable;
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import horizontalPodAutoscalersRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable";
|
||||
import { HorizontalPodAutoscalers } from "./hpa";
|
||||
|
||||
const horizontalPodAutoscalersRouteComponentInjectable = getInjectable({
|
||||
id: "horizontal-pod-autoscalers-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(horizontalPodAutoscalersRouteInjectable),
|
||||
Component: HorizontalPodAutoscalers,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default horizontalPodAutoscalersRouteComponentInjectable;
|
||||
@@ -7,14 +7,13 @@ import "./hpa.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import type { HorizontalPodAutoscaler } from "../../../common/k8s-api/endpoints/hpa.api";
|
||||
import { hpaStore } from "./hpa.store";
|
||||
import { Badge } from "../badge";
|
||||
import { cssNames } from "../../utils";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { HpaRouteParams } from "../../../common/routes";
|
||||
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
|
||||
import { KubeObjectAge } from "../kube-object/age";
|
||||
|
||||
enum columnId {
|
||||
@@ -28,11 +27,8 @@ enum columnId {
|
||||
status = "status",
|
||||
}
|
||||
|
||||
export interface HorizontalPodAutoscalersProps extends RouteComponentProps<HpaRouteParams> {
|
||||
}
|
||||
|
||||
@observer
|
||||
export class HorizontalPodAutoscalers extends React.Component<HorizontalPodAutoscalersProps> {
|
||||
export class HorizontalPodAutoscalers extends React.Component {
|
||||
getTargets(hpa: HorizontalPodAutoscaler) {
|
||||
const metrics = hpa.getMetrics();
|
||||
|
||||
@@ -47,57 +43,59 @@ export class HorizontalPodAutoscalers extends React.Component<HorizontalPodAutos
|
||||
|
||||
render() {
|
||||
return (
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="configuration_hpa"
|
||||
className="HorizontalPodAutoscalers"
|
||||
store={hpaStore}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: hpa => hpa.getName(),
|
||||
[columnId.namespace]: hpa => hpa.getNs(),
|
||||
[columnId.minPods]: hpa => hpa.getMinPods(),
|
||||
[columnId.maxPods]: hpa => hpa.getMaxPods(),
|
||||
[columnId.replicas]: hpa => hpa.getReplicas(),
|
||||
[columnId.age]: hpa => -hpa.getCreationTimestamp(),
|
||||
}}
|
||||
searchFilters={[
|
||||
hpa => hpa.getSearchFields(),
|
||||
]}
|
||||
renderHeaderTitle="Horizontal Pod Autoscalers"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name },
|
||||
{ className: "warning", showWithColumn: columnId.name },
|
||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
{ title: "Metrics", className: "metrics", id: columnId.metrics },
|
||||
{ title: "Min Pods", className: "min-pods", sortBy: columnId.minPods, id: columnId.minPods },
|
||||
{ title: "Max Pods", className: "max-pods", sortBy: columnId.maxPods, id: columnId.maxPods },
|
||||
{ title: "Replicas", className: "replicas", sortBy: columnId.replicas, id: columnId.replicas },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
{ title: "Status", className: "status scrollable", id: columnId.status },
|
||||
]}
|
||||
renderTableContents={hpa => [
|
||||
hpa.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={hpa} />,
|
||||
hpa.getNs(),
|
||||
this.getTargets(hpa),
|
||||
hpa.getMinPods(),
|
||||
hpa.getMaxPods(),
|
||||
hpa.getReplicas(),
|
||||
<KubeObjectAge key="age" object={hpa} />,
|
||||
hpa.getConditions()
|
||||
.filter(({ isReady }) => isReady)
|
||||
.map(({ type, tooltip }) => (
|
||||
<Badge
|
||||
key={type}
|
||||
label={type}
|
||||
tooltip={tooltip}
|
||||
className={cssNames(type.toLowerCase())}
|
||||
expandable={false}
|
||||
scrollable={true}
|
||||
/>
|
||||
)),
|
||||
]}
|
||||
/>
|
||||
<SiblingsInTabLayout>
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="configuration_hpa"
|
||||
className="HorizontalPodAutoscalers"
|
||||
store={hpaStore}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: hpa => hpa.getName(),
|
||||
[columnId.namespace]: hpa => hpa.getNs(),
|
||||
[columnId.minPods]: hpa => hpa.getMinPods(),
|
||||
[columnId.maxPods]: hpa => hpa.getMaxPods(),
|
||||
[columnId.replicas]: hpa => hpa.getReplicas(),
|
||||
[columnId.age]: hpa => -hpa.getCreationTimestamp(),
|
||||
}}
|
||||
searchFilters={[
|
||||
hpa => hpa.getSearchFields(),
|
||||
]}
|
||||
renderHeaderTitle="Horizontal Pod Autoscalers"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name },
|
||||
{ className: "warning", showWithColumn: columnId.name },
|
||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
{ title: "Metrics", className: "metrics", id: columnId.metrics },
|
||||
{ title: "Min Pods", className: "min-pods", sortBy: columnId.minPods, id: columnId.minPods },
|
||||
{ title: "Max Pods", className: "max-pods", sortBy: columnId.maxPods, id: columnId.maxPods },
|
||||
{ title: "Replicas", className: "replicas", sortBy: columnId.replicas, id: columnId.replicas },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
{ title: "Status", className: "status scrollable", id: columnId.status },
|
||||
]}
|
||||
renderTableContents={hpa => [
|
||||
hpa.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={hpa} />,
|
||||
hpa.getNs(),
|
||||
this.getTargets(hpa),
|
||||
hpa.getMinPods(),
|
||||
hpa.getMaxPods(),
|
||||
hpa.getReplicas(),
|
||||
<KubeObjectAge key="age" object={hpa} />,
|
||||
hpa.getConditions()
|
||||
.filter(({ isReady }) => isReady)
|
||||
.map(({ type, tooltip }) => (
|
||||
<Badge
|
||||
key={type}
|
||||
label={type}
|
||||
tooltip={tooltip}
|
||||
className={cssNames(type.toLowerCase())}
|
||||
expandable={false}
|
||||
scrollable={true}
|
||||
/>
|
||||
)),
|
||||
]}
|
||||
/>
|
||||
</SiblingsInTabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { LimitRanges } from "./limit-ranges";
|
||||
import limitRangesRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/limit-ranges/limit-ranges-route.injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const limitRangesRouteComponentInjectable = getInjectable({
|
||||
id: "limit-ranges-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(limitRangesRouteInjectable),
|
||||
Component: LimitRanges,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default limitRangesRouteComponentInjectable;
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
|
||||
import limitRangesRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/limit-ranges/limit-ranges-route.injectable";
|
||||
import { configSidebarItemId } from "../+config/config-sidebar-items.injectable";
|
||||
import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToLimitRangesInjectable from "../../../common/front-end-routing/routes/cluster/config/limit-ranges/navigate-to-limit-ranges.injectable";
|
||||
|
||||
const limitRangesSidebarItemsInjectable = getInjectable({
|
||||
id: "limit-ranges-sidebar-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(limitRangesRouteInjectable);
|
||||
const navigateToLimitRanges = di.inject(navigateToLimitRangesInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return computed(() => [
|
||||
{
|
||||
id: "limit-ranges",
|
||||
parentId: configSidebarItemId,
|
||||
title: "Limit Ranges",
|
||||
onClick: navigateToLimitRanges,
|
||||
isActive: routeIsActive,
|
||||
isVisible: route.isEnabled,
|
||||
orderNumber: 40,
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default limitRangesSidebarItemsInjectable;
|
||||
@@ -5,13 +5,12 @@
|
||||
|
||||
import "./limit-ranges.scss";
|
||||
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { observer } from "mobx-react";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { limitRangeStore } from "./limit-ranges.store";
|
||||
import React from "react";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { LimitRangeRouteParams } from "../../../common/routes";
|
||||
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
|
||||
import { KubeObjectAge } from "../kube-object/age";
|
||||
|
||||
enum columnId {
|
||||
@@ -20,41 +19,40 @@ enum columnId {
|
||||
age = "age",
|
||||
}
|
||||
|
||||
export interface LimitRangesProps extends RouteComponentProps<LimitRangeRouteParams> {
|
||||
}
|
||||
|
||||
@observer
|
||||
export class LimitRanges extends React.Component<LimitRangesProps> {
|
||||
export class LimitRanges extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="configuration_limitranges"
|
||||
className="LimitRanges"
|
||||
store={limitRangeStore}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: limitRange => limitRange.getName(),
|
||||
[columnId.namespace]: limitRange => limitRange.getNs(),
|
||||
[columnId.age]: limitRange => -limitRange.getCreationTimestamp(),
|
||||
}}
|
||||
searchFilters={[
|
||||
item => item.getName(),
|
||||
item => item.getNs(),
|
||||
]}
|
||||
renderHeaderTitle="Limit Ranges"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
{ className: "warning", showWithColumn: columnId.name },
|
||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
]}
|
||||
renderTableContents={limitRange => [
|
||||
limitRange.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={limitRange}/>,
|
||||
limitRange.getNs(),
|
||||
<KubeObjectAge key="age" object={limitRange} />,
|
||||
]}
|
||||
/>
|
||||
<SiblingsInTabLayout>
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="configuration_limitranges"
|
||||
className="LimitRanges"
|
||||
store={limitRangeStore}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: limitRange => limitRange.getName(),
|
||||
[columnId.namespace]: limitRange => limitRange.getNs(),
|
||||
[columnId.age]: limitRange => -limitRange.getCreationTimestamp(),
|
||||
}}
|
||||
searchFilters={[
|
||||
item => item.getName(),
|
||||
item => item.getNs(),
|
||||
]}
|
||||
renderHeaderTitle="Limit Ranges"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
{ className: "warning", showWithColumn: columnId.name },
|
||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
]}
|
||||
renderTableContents={limitRange => [
|
||||
limitRange.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={limitRange}/>,
|
||||
limitRange.getNs(),
|
||||
<KubeObjectAge key="age" object={limitRange} />,
|
||||
]}
|
||||
/>
|
||||
</SiblingsInTabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { ConfigMaps } from "./config-maps";
|
||||
import configMapsRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/config-maps/config-maps-route.injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const configMapsRouteComponentInjectable = getInjectable({
|
||||
id: "config-maps-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(configMapsRouteInjectable),
|
||||
Component: ConfigMaps,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default configMapsRouteComponentInjectable;
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
|
||||
import configMapsRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/config-maps/config-maps-route.injectable";
|
||||
import { configSidebarItemId } from "../+config/config-sidebar-items.injectable";
|
||||
import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToConfigMapsInjectable from "../../../common/front-end-routing/routes/cluster/config/config-maps/navigate-to-config-maps.injectable";
|
||||
|
||||
const configMapsSidebarItemsInjectable = getInjectable({
|
||||
id: "config-maps-sidebar-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(configMapsRouteInjectable);
|
||||
const navigateToConfigMaps = di.inject(navigateToConfigMapsInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return computed(() => [
|
||||
{
|
||||
id: "config-maps",
|
||||
parentId: configSidebarItemId,
|
||||
title: "ConfigMaps",
|
||||
onClick: navigateToConfigMaps,
|
||||
isActive: routeIsActive,
|
||||
isVisible: route.isEnabled,
|
||||
orderNumber: 10,
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default configMapsSidebarItemsInjectable;
|
||||
@@ -7,11 +7,10 @@ import "./config-maps.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { configMapsStore } from "./config-maps.store";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { ConfigMapsRouteParams } from "../../../common/routes";
|
||||
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
|
||||
import { KubeObjectAge } from "../kube-object/age";
|
||||
|
||||
enum columnId {
|
||||
@@ -21,44 +20,43 @@ enum columnId {
|
||||
age = "age",
|
||||
}
|
||||
|
||||
export interface ConfigMapsProps extends RouteComponentProps<ConfigMapsRouteParams> {
|
||||
}
|
||||
|
||||
@observer
|
||||
export class ConfigMaps extends React.Component<ConfigMapsProps> {
|
||||
export class ConfigMaps extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="configuration_configmaps"
|
||||
className="ConfigMaps"
|
||||
store={configMapsStore}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: configMap => configMap.getName(),
|
||||
[columnId.namespace]: configMap => configMap.getNs(),
|
||||
[columnId.keys]: configMap => configMap.getKeys(),
|
||||
[columnId.age]: configMap => -configMap.getCreationTimestamp(),
|
||||
}}
|
||||
searchFilters={[
|
||||
configMap => configMap.getSearchFields(),
|
||||
configMap => configMap.getKeys(),
|
||||
]}
|
||||
renderHeaderTitle="Config Maps"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
{ className: "warning", showWithColumn: columnId.name },
|
||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
{ title: "Keys", className: "keys", sortBy: columnId.keys, id: columnId.keys },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
]}
|
||||
renderTableContents={configMap => [
|
||||
configMap.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={configMap}/>,
|
||||
configMap.getNs(),
|
||||
configMap.getKeys().join(", "),
|
||||
<KubeObjectAge key="age" object={configMap} />,
|
||||
]}
|
||||
/>
|
||||
<SiblingsInTabLayout>
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="configuration_configmaps"
|
||||
className="ConfigMaps"
|
||||
store={configMapsStore}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: configMap => configMap.getName(),
|
||||
[columnId.namespace]: configMap => configMap.getNs(),
|
||||
[columnId.keys]: configMap => configMap.getKeys(),
|
||||
[columnId.age]: configMap => -configMap.getCreationTimestamp(),
|
||||
}}
|
||||
searchFilters={[
|
||||
configMap => configMap.getSearchFields(),
|
||||
configMap => configMap.getKeys(),
|
||||
]}
|
||||
renderHeaderTitle="Config Maps"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
{ className: "warning", showWithColumn: columnId.name },
|
||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
{ title: "Keys", className: "keys", sortBy: columnId.keys, id: columnId.keys },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
]}
|
||||
renderTableContents={configMap => [
|
||||
configMap.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={configMap} />,
|
||||
configMap.getNs(),
|
||||
configMap.getKeys().join(", "),
|
||||
<KubeObjectAge key="age" object={configMap} />,
|
||||
]}
|
||||
/>
|
||||
</SiblingsInTabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { PodDisruptionBudgets } from "./pod-disruption-budgets";
|
||||
import podDisruptionBudgetsRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/pod-disruption-budgets/pod-disruption-budgets-route.injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const podDisruptionBudgetsRouteComponentInjectable = getInjectable({
|
||||
id: "pod-disruption-budgets-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(podDisruptionBudgetsRouteInjectable),
|
||||
Component: PodDisruptionBudgets,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default podDisruptionBudgetsRouteComponentInjectable;
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
|
||||
import podDisruptionBudgetsRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/pod-disruption-budgets/pod-disruption-budgets-route.injectable";
|
||||
import { configSidebarItemId } from "../+config/config-sidebar-items.injectable";
|
||||
import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToPodDisruptionBudgetsInjectable from "../../../common/front-end-routing/routes/cluster/config/pod-disruption-budgets/navigate-to-pod-disruption-budgets.injectable";
|
||||
|
||||
const podDisruptionBudgetsSidebarItemsInjectable = getInjectable({
|
||||
id: "pod-disruption-budgets-sidebar-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(podDisruptionBudgetsRouteInjectable);
|
||||
const navigateToPodDisruptionBudgets = di.inject(navigateToPodDisruptionBudgetsInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return computed(() => [
|
||||
{
|
||||
id: "pod-disruption-budgets",
|
||||
parentId: configSidebarItemId,
|
||||
title: "Pod Disruption Budgets",
|
||||
onClick: navigateToPodDisruptionBudgets,
|
||||
isActive: routeIsActive,
|
||||
isVisible: route.isEnabled,
|
||||
orderNumber: 60,
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default podDisruptionBudgetsSidebarItemsInjectable;
|
||||
@@ -12,6 +12,7 @@ import type { PodDisruptionBudget } from "../../../common/k8s-api/endpoints/podd
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
|
||||
import { KubeObjectAge } from "../kube-object/age";
|
||||
|
||||
enum columnId {
|
||||
@@ -31,45 +32,47 @@ export interface PodDisruptionBudgetsProps extends KubeObjectDetailsProps<PodDis
|
||||
export class PodDisruptionBudgets extends React.Component<PodDisruptionBudgetsProps> {
|
||||
render() {
|
||||
return (
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="configuration_distribution_budgets"
|
||||
className="PodDisruptionBudgets"
|
||||
store={podDisruptionBudgetsStore}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: pdb => pdb.getName(),
|
||||
[columnId.namespace]: pdb => pdb.getNs(),
|
||||
[columnId.minAvailable]: pdb => pdb.getMinAvailable(),
|
||||
[columnId.maxUnavailable]: pdb => pdb.getMaxUnavailable(),
|
||||
[columnId.currentHealthy]: pdb => pdb.getCurrentHealthy(),
|
||||
[columnId.desiredHealthy]: pdb => pdb.getDesiredHealthy(),
|
||||
[columnId.age]: pdb => -pdb.getCreationTimestamp(),
|
||||
}}
|
||||
searchFilters={[
|
||||
pdb => pdb.getSearchFields(),
|
||||
]}
|
||||
renderHeaderTitle="Pod Disruption Budgets"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
{ className: "warning", showWithColumn: columnId.name },
|
||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
{ title: "Min Available", className: "min-available", sortBy: columnId.minAvailable, id: columnId.minAvailable },
|
||||
{ title: "Max Unavailable", className: "max-unavailable", sortBy: columnId.maxUnavailable, id: columnId.maxUnavailable },
|
||||
{ title: "Current Healthy", className: "current-healthy", sortBy: columnId.currentHealthy, id: columnId.currentHealthy },
|
||||
{ title: "Desired Healthy", className: "desired-healthy", sortBy: columnId.desiredHealthy, id: columnId.desiredHealthy },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
]}
|
||||
renderTableContents={pdb => [
|
||||
pdb.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={pdb} />,
|
||||
pdb.getNs(),
|
||||
pdb.getMinAvailable(),
|
||||
pdb.getMaxUnavailable(),
|
||||
pdb.getCurrentHealthy(),
|
||||
pdb.getDesiredHealthy(),
|
||||
<KubeObjectAge key="age" object={pdb} />,
|
||||
]}
|
||||
/>
|
||||
<SiblingsInTabLayout>
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="configuration_distribution_budgets"
|
||||
className="PodDisruptionBudgets"
|
||||
store={podDisruptionBudgetsStore}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: pdb => pdb.getName(),
|
||||
[columnId.namespace]: pdb => pdb.getNs(),
|
||||
[columnId.minAvailable]: pdb => pdb.getMinAvailable(),
|
||||
[columnId.maxUnavailable]: pdb => pdb.getMaxUnavailable(),
|
||||
[columnId.currentHealthy]: pdb => pdb.getCurrentHealthy(),
|
||||
[columnId.desiredHealthy]: pdb => pdb.getDesiredHealthy(),
|
||||
[columnId.age]: pdb => -pdb.getCreationTimestamp(),
|
||||
}}
|
||||
searchFilters={[
|
||||
pdb => pdb.getSearchFields(),
|
||||
]}
|
||||
renderHeaderTitle="Pod Disruption Budgets"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
{ className: "warning", showWithColumn: columnId.name },
|
||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
{ title: "Min Available", className: "min-available", sortBy: columnId.minAvailable, id: columnId.minAvailable },
|
||||
{ title: "Max Unavailable", className: "max-unavailable", sortBy: columnId.maxUnavailable, id: columnId.maxUnavailable },
|
||||
{ title: "Current Healthy", className: "current-healthy", sortBy: columnId.currentHealthy, id: columnId.currentHealthy },
|
||||
{ title: "Desired Healthy", className: "desired-healthy", sortBy: columnId.desiredHealthy, id: columnId.desiredHealthy },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
]}
|
||||
renderTableContents={pdb => [
|
||||
pdb.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={pdb} />,
|
||||
pdb.getNs(),
|
||||
pdb.getMinAvailable(),
|
||||
pdb.getMaxUnavailable(),
|
||||
pdb.getCurrentHealthy(),
|
||||
pdb.getDesiredHealthy(),
|
||||
<KubeObjectAge key="age" object={pdb} />,
|
||||
]}
|
||||
/>
|
||||
</SiblingsInTabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -168,6 +168,7 @@ export class AddQuotaDialog extends React.Component<AddQuotaDialogProps> {
|
||||
|
||||
<SubTitle title="Namespace" />
|
||||
<NamespaceSelect
|
||||
id="namespace-input"
|
||||
value={this.namespace}
|
||||
placeholder="Namespace"
|
||||
themeName="light"
|
||||
@@ -178,6 +179,7 @@ export class AddQuotaDialog extends React.Component<AddQuotaDialogProps> {
|
||||
<SubTitle title="Values" />
|
||||
<div className="flex gaps align-center">
|
||||
<Select
|
||||
id="quota-input"
|
||||
className="quota-select"
|
||||
themeName="light"
|
||||
placeholder="Select a quota.."
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { ResourceQuotas } from "./resource-quotas";
|
||||
import resourceQuotasRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/resource-quotas/resource-quotas-route.injectable";
|
||||
import {
|
||||
routeSpecificComponentInjectionToken,
|
||||
} from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const resourceQuotasRouteComponentInjectable = getInjectable({
|
||||
id: "resource-quotas-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(resourceQuotasRouteInjectable),
|
||||
Component: ResourceQuotas,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default resourceQuotasRouteComponentInjectable;
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
|
||||
import resourceQuotasRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/resource-quotas/resource-quotas-route.injectable";
|
||||
import { configSidebarItemId } from "../+config/config-sidebar-items.injectable";
|
||||
import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToResourceQuotasInjectable from "../../../common/front-end-routing/routes/cluster/config/resource-quotas/navigate-to-resource-quotas.injectable";
|
||||
|
||||
const resourceQuotasSidebarItemsInjectable = getInjectable({
|
||||
id: "resource-quotas-sidebar-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(resourceQuotasRouteInjectable);
|
||||
const navigateToResourceQuotas = di.inject(navigateToResourceQuotasInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return computed(() => [
|
||||
{
|
||||
id: "resource-quotas",
|
||||
parentId: configSidebarItemId,
|
||||
title: "Resource Quotas",
|
||||
onClick: navigateToResourceQuotas,
|
||||
isActive: routeIsActive,
|
||||
isVisible: route.isEnabled,
|
||||
orderNumber: 30,
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default resourceQuotasSidebarItemsInjectable;
|
||||
@@ -7,12 +7,11 @@ import "./resource-quotas.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { AddQuotaDialog } from "./add-quota-dialog";
|
||||
import { resourceQuotaStore } from "./resource-quotas.store";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { ResourceQuotaRouteParams } from "../../../common/routes";
|
||||
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
|
||||
import { KubeObjectAge } from "../kube-object/age";
|
||||
|
||||
enum columnId {
|
||||
@@ -21,14 +20,11 @@ enum columnId {
|
||||
age = "age",
|
||||
}
|
||||
|
||||
export interface ResourceQuotasProps extends RouteComponentProps<ResourceQuotaRouteParams> {
|
||||
}
|
||||
|
||||
@observer
|
||||
export class ResourceQuotas extends React.Component<ResourceQuotasProps> {
|
||||
export class ResourceQuotas extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<SiblingsInTabLayout>
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="configuration_quotas"
|
||||
@@ -62,7 +58,7 @@ export class ResourceQuotas extends React.Component<ResourceQuotasProps> {
|
||||
}}
|
||||
/>
|
||||
<AddQuotaDialog/>
|
||||
</>
|
||||
</SiblingsInTabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -216,6 +216,7 @@ export class AddSecretDialog extends React.Component<AddSecretDialogProps> {
|
||||
<div className="secret-namespace">
|
||||
<SubTitle title="Namespace" />
|
||||
<NamespaceSelect
|
||||
id="secret-namespace-input"
|
||||
themeName="light"
|
||||
value={namespace}
|
||||
onChange={({ value }) => this.namespace = value}
|
||||
@@ -224,6 +225,7 @@ export class AddSecretDialog extends React.Component<AddSecretDialogProps> {
|
||||
<div className="secret-type">
|
||||
<SubTitle title="Secret type" />
|
||||
<Select
|
||||
id="secret-input"
|
||||
themeName="light"
|
||||
options={this.types}
|
||||
value={type} onChange={({ value }: SelectOption) => this.type = value}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { Secrets } from "./secrets";
|
||||
import secretsRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/secrets/secrets-route.injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const secretsRouteComponentInjectable = getInjectable({
|
||||
id: "secrets-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(secretsRouteInjectable),
|
||||
Component: Secrets,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default secretsRouteComponentInjectable;
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
|
||||
import secretsRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/secrets/secrets-route.injectable";
|
||||
import { configSidebarItemId } from "../+config/config-sidebar-items.injectable";
|
||||
import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToSecretsInjectable from "../../../common/front-end-routing/routes/cluster/config/secrets/navigate-to-secrets.injectable";
|
||||
|
||||
const secretsSidebarItemsInjectable = getInjectable({
|
||||
id: "secrets-sidebar-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(secretsRouteInjectable);
|
||||
const navigateToSecrets = di.inject(navigateToSecretsInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return computed(() => [
|
||||
{
|
||||
id: "secrets",
|
||||
parentId: configSidebarItemId,
|
||||
title: "Secrets",
|
||||
onClick: navigateToSecrets,
|
||||
isActive: routeIsActive,
|
||||
isVisible: route.isEnabled,
|
||||
orderNumber: 20,
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default secretsSidebarItemsInjectable;
|
||||
@@ -7,13 +7,12 @@ import "./secrets.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { AddSecretDialog } from "./add-secret-dialog";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { Badge } from "../badge";
|
||||
import { secretsStore } from "./secrets.store";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { SecretsRouteParams } from "../../../common/routes";
|
||||
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
|
||||
import { KubeObjectAge } from "../kube-object/age";
|
||||
|
||||
enum columnId {
|
||||
@@ -25,14 +24,11 @@ enum columnId {
|
||||
age = "age",
|
||||
}
|
||||
|
||||
export interface SecretsProps extends RouteComponentProps<SecretsRouteParams> {
|
||||
}
|
||||
|
||||
@observer
|
||||
export class Secrets extends React.Component<SecretsProps> {
|
||||
export class Secrets extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<SiblingsInTabLayout>
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="configuration_secrets"
|
||||
@@ -75,7 +71,7 @@ export class Secrets extends React.Component<SecretsProps> {
|
||||
}}
|
||||
/>
|
||||
<AddSecretDialog/>
|
||||
</>
|
||||
</SiblingsInTabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { Icon } from "../icon";
|
||||
import React from "react";
|
||||
import {
|
||||
SidebarItemRegistration,
|
||||
sidebarItemsInjectionToken,
|
||||
} from "../layout/sidebar-items.injectable";
|
||||
import { computed } from "mobx";
|
||||
import { noop } from "lodash/fp";
|
||||
|
||||
export const configSidebarItemId = "config";
|
||||
|
||||
const configSidebarItemsInjectable = getInjectable({
|
||||
id: "config-sidebar-items",
|
||||
|
||||
instantiate: () =>
|
||||
computed((): SidebarItemRegistration[] => [
|
||||
{
|
||||
id: configSidebarItemId,
|
||||
parentId: null,
|
||||
title: "Config",
|
||||
getIcon: () => <Icon material="list" />,
|
||||
onClick: noop,
|
||||
orderNumber: 40,
|
||||
},
|
||||
]),
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default configSidebarItemsInjectable;
|
||||
@@ -1,91 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import { HorizontalPodAutoscalers } from "../+config-autoscalers";
|
||||
import { LimitRanges } from "../+config-limit-ranges";
|
||||
import { ConfigMaps } from "../+config-maps";
|
||||
import { PodDisruptionBudgets } from "../+config-pod-disruption-budgets";
|
||||
import { ResourceQuotas } from "../+config-resource-quotas";
|
||||
import { Secrets } from "../+config-secrets";
|
||||
import isAllowedResourceInjectable, { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import * as routes from "../../../common/routes";
|
||||
|
||||
interface Dependencies {
|
||||
isAllowedResource: IsAllowedResource;
|
||||
}
|
||||
|
||||
function getRouteTabs({ isAllowedResource }: Dependencies) {
|
||||
return computed(() => {
|
||||
const tabs: TabLayoutRoute[] = [];
|
||||
|
||||
if (isAllowedResource("configmaps")) {
|
||||
tabs.push({
|
||||
title: "ConfigMaps",
|
||||
component: ConfigMaps,
|
||||
url: routes.configMapsURL(),
|
||||
routePath: routes.configMapsRoute.path.toString(),
|
||||
});
|
||||
}
|
||||
|
||||
if (isAllowedResource("secrets")) {
|
||||
tabs.push({
|
||||
title: "Secrets",
|
||||
component: Secrets,
|
||||
url: routes.secretsURL(),
|
||||
routePath: routes.secretsRoute.path.toString(),
|
||||
});
|
||||
}
|
||||
|
||||
if (isAllowedResource("resourcequotas")) {
|
||||
tabs.push({
|
||||
title: "Resource Quotas",
|
||||
component: ResourceQuotas,
|
||||
url: routes.resourceQuotaURL(),
|
||||
routePath: routes.resourceQuotaRoute.path.toString(),
|
||||
});
|
||||
}
|
||||
|
||||
if (isAllowedResource("limitranges")) {
|
||||
tabs.push({
|
||||
title: "Limit Ranges",
|
||||
component: LimitRanges,
|
||||
url: routes.limitRangeURL(),
|
||||
routePath: routes.limitRangesRoute.path.toString(),
|
||||
});
|
||||
}
|
||||
|
||||
if (isAllowedResource("horizontalpodautoscalers")) {
|
||||
tabs.push({
|
||||
title: "HPA",
|
||||
component: HorizontalPodAutoscalers,
|
||||
url: routes.hpaURL(),
|
||||
routePath: routes.hpaRoute.path.toString(),
|
||||
});
|
||||
}
|
||||
|
||||
if (isAllowedResource("poddisruptionbudgets")) {
|
||||
tabs.push({
|
||||
title: "Pod Disruption Budgets",
|
||||
component: PodDisruptionBudgets,
|
||||
url: routes.pdbURL(),
|
||||
routePath: routes.pdbRoute.path.toString(),
|
||||
});
|
||||
}
|
||||
|
||||
return tabs;
|
||||
});
|
||||
}
|
||||
|
||||
const configRoutesInjectable = getInjectable({
|
||||
id: "config-routes",
|
||||
|
||||
instantiate: (di) => getRouteTabs({
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
export default configRoutesInjectable;
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import configRoutesInjectable from "./route-tabs.injectable";
|
||||
|
||||
export interface ConfigRouteProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<TabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedConfigRoute = observer(({ routes }: Dependencies & ConfigRouteProps) => (
|
||||
<TabLayout
|
||||
className="Config"
|
||||
tabs={routes.get()}
|
||||
/>
|
||||
));
|
||||
|
||||
export const ConfigRoute = withInjectables<Dependencies, ConfigRouteProps>(NonInjectedConfigRoute, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(configRoutesInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { configRoute, configURL } from "../../../common/routes";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { renderTabRoutesSidebarItems } from "../layout/tab-routes-sidebar-items";
|
||||
import configRoutesInjectable from "./route-tabs.injectable";
|
||||
|
||||
export interface ConfigSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<TabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedConfigSidebarItem = observer(({ routes }: Dependencies & ConfigSidebarItemProps) => {
|
||||
const tabRoutes = routes.get();
|
||||
|
||||
return (
|
||||
<SidebarItem
|
||||
id="config"
|
||||
text="Configuration"
|
||||
isActive={isActiveRoute(configRoute)}
|
||||
isHidden={tabRoutes.length == 0}
|
||||
url={configURL()}
|
||||
icon={<Icon material="list"/>}
|
||||
>
|
||||
{renderTabRoutesSidebarItems(tabRoutes)}
|
||||
</SidebarItem>
|
||||
);
|
||||
});
|
||||
|
||||
export const ConfigSidebarItem = withInjectables<Dependencies, ConfigSidebarItemProps>(NonInjectedConfigSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(configRoutesInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import crdListRouteInjectable from "../../../common/front-end-routing/routes/cluster/custom-resources/crd-list/crd-list-route.injectable";
|
||||
import { CustomResourceDefinitions } from "./crd-list";
|
||||
|
||||
const crdListRouteComponentInjectable = getInjectable({
|
||||
id: "crd-list-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(crdListRouteInjectable),
|
||||
Component: CustomResourceDefinitions,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default crdListRouteComponentInjectable;
|
||||
@@ -16,6 +16,7 @@ import { Select, SelectOption } from "../select";
|
||||
import { createPageParam } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { KubeObjectAge } from "../kube-object/age";
|
||||
import { TabLayout } from "../layout/tab-layout-2";
|
||||
|
||||
export const crdGroupsUrlParam = createPageParam<string[]>({
|
||||
name: "groups",
|
||||
@@ -64,81 +65,84 @@ export class CustomResourceDefinitions extends React.Component {
|
||||
const { items, selectedGroups } = this;
|
||||
|
||||
return (
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="crd"
|
||||
className="CrdList"
|
||||
store={crdStore}
|
||||
// Don't subscribe the `crdStore` because <Sidebar> already has and is always mounted
|
||||
subscribeStores={false}
|
||||
items={items}
|
||||
sortingCallbacks={{
|
||||
[columnId.kind]: crd => crd.getResourceKind(),
|
||||
[columnId.group]: crd => crd.getGroup(),
|
||||
[columnId.version]: crd => crd.getVersion(),
|
||||
[columnId.scope]: crd => crd.getScope(),
|
||||
[columnId.age]: crd => -crd.getCreationTimestamp(),
|
||||
}}
|
||||
searchFilters={[
|
||||
crd => crd.getResourceKind(),
|
||||
crd => crd.getGroup(),
|
||||
crd => crd.getVersion(),
|
||||
crd => crd.getScope(),
|
||||
crd => -crd.getCreationTimestamp(),
|
||||
]}
|
||||
renderHeaderTitle="Custom Resources"
|
||||
customizeHeader={({ filters, ...headerPlaceholders }) => {
|
||||
let placeholder = <>All groups</>;
|
||||
<TabLayout>
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="crd"
|
||||
className="CrdList"
|
||||
store={crdStore}
|
||||
// Don't subscribe the `crdStore` because <Sidebar> already has and is always mounted
|
||||
subscribeStores={false}
|
||||
items={items}
|
||||
sortingCallbacks={{
|
||||
[columnId.kind]: crd => crd.getResourceKind(),
|
||||
[columnId.group]: crd => crd.getGroup(),
|
||||
[columnId.version]: crd => crd.getVersion(),
|
||||
[columnId.scope]: crd => crd.getScope(),
|
||||
[columnId.age]: crd => -crd.getCreationTimestamp(),
|
||||
}}
|
||||
searchFilters={[
|
||||
crd => crd.getResourceKind(),
|
||||
crd => crd.getGroup(),
|
||||
crd => crd.getVersion(),
|
||||
crd => crd.getScope(),
|
||||
crd => -crd.getCreationTimestamp(),
|
||||
]}
|
||||
renderHeaderTitle="Custom Resources"
|
||||
customizeHeader={({ filters, ...headerPlaceholders }) => {
|
||||
let placeholder = <>All groups</>;
|
||||
|
||||
if (selectedGroups.length == 1) placeholder = <>Group: {selectedGroups[0]}</>;
|
||||
if (selectedGroups.length >= 2) placeholder = <>Groups: {selectedGroups.join(", ")}</>;
|
||||
if (selectedGroups.length == 1) placeholder = <>Group: {selectedGroups[0]}</>;
|
||||
if (selectedGroups.length >= 2) placeholder = <>Groups: {selectedGroups.join(", ")}</>;
|
||||
|
||||
return {
|
||||
// todo: move to global filters
|
||||
filters: (
|
||||
<>
|
||||
{filters}
|
||||
<Select
|
||||
className="group-select"
|
||||
placeholder={placeholder}
|
||||
options={Object.keys(crdStore.groups)}
|
||||
onChange={({ value: group }: SelectOption) => this.toggleSelection(group)}
|
||||
closeMenuOnSelect={false}
|
||||
controlShouldRenderValue={false}
|
||||
formatOptionLabel={({ value: group }: SelectOption) => {
|
||||
const isSelected = selectedGroups.includes(group);
|
||||
return {
|
||||
// todo: move to global filters
|
||||
filters: (
|
||||
<>
|
||||
{filters}
|
||||
<Select
|
||||
id="crd-input"
|
||||
className="group-select"
|
||||
placeholder={placeholder}
|
||||
options={Object.keys(crdStore.groups)}
|
||||
onChange={({ value: group }: SelectOption) => this.toggleSelection(group)}
|
||||
closeMenuOnSelect={false}
|
||||
controlShouldRenderValue={false}
|
||||
formatOptionLabel={({ value: group }: SelectOption) => {
|
||||
const isSelected = selectedGroups.includes(group);
|
||||
|
||||
return (
|
||||
<div className="flex gaps align-center">
|
||||
<Icon small material="folder"/>
|
||||
<span>{group}</span>
|
||||
{isSelected && <Icon small material="check" className="box right"/>}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
...headerPlaceholders,
|
||||
};
|
||||
}}
|
||||
renderTableHeader={[
|
||||
{ title: "Resource", className: "kind", sortBy: columnId.kind, id: columnId.kind },
|
||||
{ title: "Group", className: "group", sortBy: columnId.group, id: columnId.group },
|
||||
{ title: "Version", className: "version", sortBy: columnId.version, id: columnId.version },
|
||||
{ title: "Scope", className: "scope", sortBy: columnId.scope, id: columnId.scope },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
]}
|
||||
renderTableContents={crd => [
|
||||
<Link key="link" to={crd.getResourceUrl()} onClick={stopPropagation}>
|
||||
{crd.getResourceKind()}
|
||||
</Link>,
|
||||
crd.getGroup(),
|
||||
crd.getVersion(),
|
||||
crd.getScope(),
|
||||
<KubeObjectAge key="age" object={crd} />,
|
||||
]}
|
||||
/>
|
||||
return (
|
||||
<div className="flex gaps align-center">
|
||||
<Icon small material="folder"/>
|
||||
<span>{group}</span>
|
||||
{isSelected && <Icon small material="check" className="box right"/>}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
...headerPlaceholders,
|
||||
};
|
||||
}}
|
||||
renderTableHeader={[
|
||||
{ title: "Resource", className: "kind", sortBy: columnId.kind, id: columnId.kind },
|
||||
{ title: "Group", className: "group", sortBy: columnId.group, id: columnId.group },
|
||||
{ title: "Version", className: "version", sortBy: columnId.version, id: columnId.version },
|
||||
{ title: "Scope", className: "scope", sortBy: columnId.scope, id: columnId.scope },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
]}
|
||||
renderTableContents={crd => [
|
||||
<Link key="link" to={crd.getResourceUrl()} onClick={stopPropagation}>
|
||||
{crd.getResourceKind()}
|
||||
</Link>,
|
||||
crd.getGroup(),
|
||||
crd.getVersion(),
|
||||
crd.getScope(),
|
||||
<KubeObjectAge key="age" object={crd} />,
|
||||
]}
|
||||
/>
|
||||
</TabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -8,35 +8,36 @@ import "./crd-resources.scss";
|
||||
import React from "react";
|
||||
import { value } from "jsonpath";
|
||||
import { observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { computed, makeObservable } from "mobx";
|
||||
import { computed, IComputedValue, makeObservable } from "mobx";
|
||||
import { crdStore } from "./crd.store";
|
||||
import { apiManager } from "../../../common/k8s-api/api-manager";
|
||||
import { parseJsonPath } from "../../utils/jsonPath";
|
||||
import type { CRDRouteParams } from "../../../common/routes";
|
||||
import { TabLayout } from "../layout/tab-layout-2";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import customResourcesRouteParametersInjectable from "./custom-resources-route-parameters.injectable";
|
||||
import { KubeObjectAge } from "../kube-object/age";
|
||||
|
||||
export interface CustomResourceDefinitionResourcesProps extends RouteComponentProps<CRDRouteParams> {
|
||||
}
|
||||
|
||||
enum columnId {
|
||||
name = "name",
|
||||
namespace = "namespace",
|
||||
age = "age",
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
group: IComputedValue<string>;
|
||||
name: IComputedValue<string>;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class CustomResourceDefinitionResources extends React.Component<CustomResourceDefinitionResourcesProps> {
|
||||
constructor(props: CustomResourceDefinitionResourcesProps) {
|
||||
class NonInjectedCrdResources extends React.Component<Dependencies> {
|
||||
constructor(props: Dependencies) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@computed get crd() {
|
||||
const { group, name } = this.props.match.params;
|
||||
|
||||
return crdStore.getByGroup(group, name);
|
||||
return crdStore.getByGroup(this.props.group.get(), this.props.name.get());
|
||||
}
|
||||
|
||||
@computed get store() {
|
||||
@@ -55,69 +56,87 @@ export class CustomResourceDefinitionResources extends React.Component<CustomRes
|
||||
const version = crd.getPreferedVersion();
|
||||
|
||||
return (
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
key={`crd_resources_${crd.getResourceApiBase()}`}
|
||||
tableId="crd_resources"
|
||||
className="CrdResources"
|
||||
store={store}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: customResource => customResource.getName(),
|
||||
[columnId.namespace]: customResource => customResource.getNs(),
|
||||
[columnId.age]: customResource => -customResource.getCreationTimestamp(),
|
||||
...Object.fromEntries(extraColumns.map(({ name, jsonPath }) => [
|
||||
name,
|
||||
customResource => value(customResource, parseJsonPath(jsonPath.slice(1))),
|
||||
])),
|
||||
}}
|
||||
searchFilters={[
|
||||
customResource => customResource.getSearchFields(),
|
||||
]}
|
||||
renderHeaderTitle={crd.getResourceKind()}
|
||||
customizeHeader={({ searchProps, ...headerPlaceholders }) => ({
|
||||
searchProps: {
|
||||
...searchProps,
|
||||
placeholder: `${crd.getResourceKind()} search ...`,
|
||||
},
|
||||
...headerPlaceholders,
|
||||
})}
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
isNamespaced && { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
...extraColumns.map(({ name }) => ({
|
||||
title: name,
|
||||
className: name.toLowerCase(),
|
||||
sortBy: name,
|
||||
id: name,
|
||||
})),
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
]}
|
||||
renderTableContents={crdInstance => [
|
||||
crdInstance.getName(),
|
||||
isNamespaced && crdInstance.getNs(),
|
||||
...extraColumns.map((column) => {
|
||||
let rawValue = value(crdInstance, parseJsonPath(column.jsonPath.slice(1)));
|
||||
<TabLayout>
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
key={`crd_resources_${crd.getResourceApiBase()}`}
|
||||
tableId="crd_resources"
|
||||
className="CrdResources"
|
||||
store={store}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: customResource => customResource.getName(),
|
||||
[columnId.namespace]: customResource => customResource.getNs(),
|
||||
[columnId.age]: customResource => -customResource.getCreationTimestamp(),
|
||||
...Object.fromEntries(extraColumns.map(({ name, jsonPath }) => [
|
||||
name,
|
||||
customResource => value(customResource, parseJsonPath(jsonPath.slice(1))),
|
||||
])),
|
||||
}}
|
||||
searchFilters={[
|
||||
customResource => customResource.getSearchFields(),
|
||||
]}
|
||||
renderHeaderTitle={crd.getResourceKind()}
|
||||
customizeHeader={({ searchProps, ...headerPlaceholders }) => ({
|
||||
searchProps: {
|
||||
...searchProps,
|
||||
placeholder: `${crd.getResourceKind()} search ...`,
|
||||
},
|
||||
...headerPlaceholders,
|
||||
})}
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
isNamespaced && { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
...extraColumns.map(({ name }) => ({
|
||||
title: name,
|
||||
className: name.toLowerCase(),
|
||||
sortBy: name,
|
||||
id: name,
|
||||
})),
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
]}
|
||||
renderTableContents={crdInstance => [
|
||||
crdInstance.getName(),
|
||||
isNamespaced && crdInstance.getNs(),
|
||||
...extraColumns.map((column) => {
|
||||
let rawValue = value(crdInstance, parseJsonPath(column.jsonPath.slice(1)));
|
||||
|
||||
if (Array.isArray(rawValue) || typeof rawValue === "object") {
|
||||
rawValue = JSON.stringify(rawValue);
|
||||
}
|
||||
if (Array.isArray(rawValue) || typeof rawValue === "object") {
|
||||
rawValue = JSON.stringify(rawValue);
|
||||
}
|
||||
|
||||
return {
|
||||
renderBoolean: true,
|
||||
children: rawValue,
|
||||
};
|
||||
}),
|
||||
<KubeObjectAge key="age" object={crdInstance} />,
|
||||
]}
|
||||
failedToLoadMessage={(
|
||||
<>
|
||||
<p>Failed to load {crd.getPluralName()}</p>
|
||||
{!version.served && (
|
||||
<p>Prefered version ({crd.getGroup()}/{version.name}) is not served</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
return {
|
||||
renderBoolean: true,
|
||||
children: rawValue,
|
||||
};
|
||||
}),
|
||||
<KubeObjectAge key="age" object={crdInstance} />,
|
||||
]}
|
||||
failedToLoadMessage={(
|
||||
<>
|
||||
<p>Failed to load {crd.getPluralName()}</p>
|
||||
{!version.served && (
|
||||
<p>Prefered version ({crd.getGroup()}/{version.name}) is not served</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</TabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const CrdResources = withInjectables<Dependencies>(
|
||||
NonInjectedCrdResources,
|
||||
|
||||
{
|
||||
getProps: (di) => {
|
||||
const routeParameters = di.inject(customResourcesRouteParametersInjectable);
|
||||
|
||||
return {
|
||||
group: routeParameters.group,
|
||||
name: routeParameters.name,
|
||||
};
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { noop, some } from "lodash/fp";
|
||||
import { computed } from "mobx";
|
||||
|
||||
import {
|
||||
SidebarItemRegistration,
|
||||
sidebarItemsInjectionToken,
|
||||
} from "../layout/sidebar-items.injectable";
|
||||
import { Icon } from "../icon";
|
||||
import React from "react";
|
||||
|
||||
import crdListRouteInjectable from "../../../common/front-end-routing/routes/cluster/custom-resources/crd-list/crd-list-route.injectable";
|
||||
import sidebarItemsForDefinitionGroupsInjectable from "./sidebar-items-for-definition-groups.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToCrdListInjectable from "../../../common/front-end-routing/routes/cluster/custom-resources/crd-list/navigate-to-crd-list.injectable";
|
||||
|
||||
const customResourceSidebarItemsInjectable = getInjectable({
|
||||
id: "custom-resource-sidebar-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const navigateToCrdList = di.inject(navigateToCrdListInjectable);
|
||||
const crdListRoute = di.inject(crdListRouteInjectable);
|
||||
const crdListRouteIsActive = di.inject(routeIsActiveInjectable, crdListRoute);
|
||||
|
||||
const definitionGroupSidebarItems = di.inject(
|
||||
sidebarItemsForDefinitionGroupsInjectable,
|
||||
);
|
||||
|
||||
return computed((): SidebarItemRegistration[] => {
|
||||
const definitionsItem = {
|
||||
id: "definitions",
|
||||
parentId: "custom-resources",
|
||||
title: "Definitions",
|
||||
onClick: navigateToCrdList,
|
||||
isActive: crdListRouteIsActive,
|
||||
isVisible: crdListRoute.isEnabled,
|
||||
orderNumber: 10,
|
||||
};
|
||||
|
||||
const definitionGroupItems = definitionGroupSidebarItems.get();
|
||||
|
||||
const childrenAndGrandChildren = [
|
||||
definitionsItem,
|
||||
...definitionGroupItems,
|
||||
];
|
||||
|
||||
const parentItem: SidebarItemRegistration = {
|
||||
id: "custom-resources",
|
||||
parentId: null,
|
||||
title: "Custom Resources",
|
||||
getIcon: () => <Icon material="extension" />,
|
||||
onClick: noop,
|
||||
isVisible: computed(() => some(item => item.isVisible.get(), childrenAndGrandChildren)),
|
||||
orderNumber: 110,
|
||||
};
|
||||
|
||||
return [parentItem, definitionsItem, ...definitionGroupItems];
|
||||
});
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default customResourceSidebarItemsInjectable;
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { CrdResources } from "./crd-resources";
|
||||
import customResourcesRouteInjectable from "../../../common/front-end-routing/routes/cluster/custom-resources/custom-resources/custom-resources-route.injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const customResourcesRouteComponentInjectable = getInjectable({
|
||||
id: "custom-resources-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(customResourcesRouteInjectable),
|
||||
Component: CrdResources,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default customResourcesRouteComponentInjectable;
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import routePathParametersInjectable from "../../routes/route-path-parameters.injectable";
|
||||
import customResourcesRouteInjectable from "../../../common/front-end-routing/routes/cluster/custom-resources/custom-resources/custom-resources-route.injectable";
|
||||
|
||||
const customResourcesRouteParametersInjectable = getInjectable({
|
||||
id: "custom-resources-route-parameters",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(customResourcesRouteInjectable);
|
||||
const pathParameters = di.inject(routePathParametersInjectable, route);
|
||||
|
||||
return {
|
||||
group: computed(() => pathParameters.get().group),
|
||||
name: computed(() => pathParameters.get().name),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default customResourcesRouteParametersInjectable;
|
||||
@@ -6,11 +6,23 @@
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import { crdStore } from "./crd.store";
|
||||
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
|
||||
import currentlyInClusterFrameInjectable from "../../routes/currently-in-cluster-frame.injectable";
|
||||
|
||||
const customResourceDefinitionsInjectable = getInjectable({
|
||||
id: "custom-resource-definitions",
|
||||
|
||||
instantiate: () => computed(() => [...crdStore.items]),
|
||||
instantiate: (di) => {
|
||||
const currentlyInClusterFrame = di.inject(currentlyInClusterFrameInjectable);
|
||||
|
||||
if (currentlyInClusterFrame) {
|
||||
const subscribeStores = di.inject(subscribeStoresInjectable);
|
||||
|
||||
subscribeStores([crdStore]);
|
||||
}
|
||||
|
||||
return computed(() => [...crdStore.items]);
|
||||
},
|
||||
});
|
||||
|
||||
export default customResourceDefinitionsInjectable;
|
||||
@@ -1,35 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed, IComputedValue } from "mobx";
|
||||
import type { CustomResourceDefinition } from "../../../common/k8s-api/endpoints";
|
||||
import { getOrInsert } from "../../utils";
|
||||
import customResourceDefinitionsInjectable from "./custom-resources.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
definitions: IComputedValue<CustomResourceDefinition[]>;
|
||||
}
|
||||
|
||||
function getGroupedCustomResourceDefinitions({ definitions }: Dependencies) {
|
||||
return computed(() => {
|
||||
const groups = new Map<string, CustomResourceDefinition[]>();
|
||||
|
||||
for (const crd of definitions.get()) {
|
||||
getOrInsert(groups, crd.getGroup(), []).push(crd);
|
||||
}
|
||||
|
||||
return groups;
|
||||
});
|
||||
}
|
||||
|
||||
const groupedCustomResourceDefinitionsInjectable = getInjectable({
|
||||
id: "grouped-custom-resource-definitions",
|
||||
|
||||
instantiate: (di) => getGroupedCustomResourceDefinitions({
|
||||
definitions: di.inject(customResourceDefinitionsInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
export default groupedCustomResourceDefinitionsInjectable;
|
||||
@@ -1,61 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed, IComputedValue } from "mobx";
|
||||
import type { CustomResourceDefinition } from "../../../common/k8s-api/endpoints";
|
||||
import { crdURL, crdDefinitionsRoute } from "../../../common/routes";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import groupedCustomResourceDefinitionsInjectable from "./grouped-custom-resources.injectable";
|
||||
|
||||
export interface CustomResourceTabLayoutRoute extends Omit<TabLayoutRoute, "component"> {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface CustomResourceGroupTabLayoutRoute extends CustomResourceTabLayoutRoute {
|
||||
subRoutes?: CustomResourceTabLayoutRoute[];
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
customResourcesDefinitions: IComputedValue<Map<string, CustomResourceDefinition[]>>;
|
||||
}
|
||||
|
||||
function getRouteTabs({ customResourcesDefinitions }: Dependencies) {
|
||||
return computed(() => {
|
||||
const tabs: CustomResourceGroupTabLayoutRoute[] = [
|
||||
{
|
||||
id: "definitions",
|
||||
title: "Definitions",
|
||||
url: crdURL(),
|
||||
routePath: String(crdDefinitionsRoute.path),
|
||||
exact: true,
|
||||
},
|
||||
];
|
||||
|
||||
for (const [group, definitions] of customResourcesDefinitions.get()) {
|
||||
tabs.push({
|
||||
id: `crd-group:${group}`,
|
||||
title: group,
|
||||
routePath: crdURL({ query: { groups: group }}),
|
||||
subRoutes: definitions.map(crd => ({
|
||||
id: `crd-resource:${crd.getResourceApiBase()}`,
|
||||
title: crd.getResourceKind(),
|
||||
routePath: crd.getResourceUrl(),
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
return tabs;
|
||||
});
|
||||
}
|
||||
|
||||
const customResourcesRouteTabsInjectable = getInjectable({
|
||||
id: "custom-resources-route-tabs",
|
||||
|
||||
instantiate: (di) => getRouteTabs({
|
||||
customResourcesDefinitions: di.inject(groupedCustomResourceDefinitionsInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
export default customResourcesRouteTabsInjectable;
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { Redirect, Route, Switch } from "react-router";
|
||||
import { TabLayout } from "../layout/tab-layout";
|
||||
import { crdDefinitionsRoute, crdResourcesRoute, crdURL } from "../../../common/routes";
|
||||
import { CustomResourceDefinitions } from "./crd-list";
|
||||
import { CustomResourceDefinitionResources } from "./crd-resources";
|
||||
|
||||
export const CustomResourcesRoute = () => (
|
||||
<TabLayout>
|
||||
<Switch>
|
||||
<Route component={CustomResourceDefinitions} {...crdDefinitionsRoute} exact/>
|
||||
<Route component={CustomResourceDefinitionResources} {...crdResourcesRoute}/>
|
||||
<Redirect to={crdURL()}/>
|
||||
</Switch>
|
||||
</TabLayout>
|
||||
);
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import React, { useEffect } from "react";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import customResourcesRouteTabsInjectable, { type CustomResourceGroupTabLayoutRoute } from "./route-tabs.injectable";
|
||||
import type { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import isAllowedResourceInjectable from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import { crdURL, crdRoute } from "../../../common/routes";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
|
||||
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
|
||||
import { crdStore } from "./crd.store";
|
||||
import { Spinner } from "../spinner";
|
||||
|
||||
export interface CustomResourcesSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<CustomResourceGroupTabLayoutRoute[]>;
|
||||
isAllowedResource: IsAllowedResource;
|
||||
subscribeStores: SubscribeStores;
|
||||
}
|
||||
|
||||
const NonInjectedCustomResourcesSidebarItem = observer(({ routes, isAllowedResource, subscribeStores }: Dependencies & CustomResourcesSidebarItemProps) => {
|
||||
useEffect(() => subscribeStores([
|
||||
crdStore,
|
||||
]), []);
|
||||
|
||||
return (
|
||||
<SidebarItem
|
||||
id="custom-resources"
|
||||
text="Custom Resources"
|
||||
url={crdURL()}
|
||||
isActive={isActiveRoute(crdRoute)}
|
||||
isHidden={!isAllowedResource("customresourcedefinitions")}
|
||||
icon={<Icon material="extension"/>}
|
||||
>
|
||||
{routes.get().map((route) => (
|
||||
<SidebarItem
|
||||
key={route.id}
|
||||
id={route.id}
|
||||
text={route.title}
|
||||
url={route.routePath}
|
||||
>
|
||||
{route.subRoutes?.map((subRoute) => (
|
||||
<SidebarItem
|
||||
key={subRoute.id}
|
||||
id={subRoute.id}
|
||||
url={subRoute.routePath}
|
||||
text={subRoute.title}
|
||||
/>
|
||||
))}
|
||||
</SidebarItem>
|
||||
))}
|
||||
{crdStore.isLoading && (
|
||||
<div className="flex justify-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
)}
|
||||
</SidebarItem>
|
||||
);
|
||||
});
|
||||
|
||||
export const CustomResourcesSidebarItem = withInjectables<Dependencies, CustomResourcesSidebarItemProps>(NonInjectedCustomResourcesSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(customResourcesRouteTabsInjectable),
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
subscribeStores: di.inject(subscribeStoresInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import crdListRouteInjectable from "../../../common/front-end-routing/routes/cluster/custom-resources/crd-list/crd-list-route.injectable";
|
||||
import customResourceDefinitionsInjectable from "./custom-resources.injectable";
|
||||
import { groupBy, matches, noop, some, toPairs } from "lodash/fp";
|
||||
import customResourcesRouteInjectable from "../../../common/front-end-routing/routes/cluster/custom-resources/custom-resources/custom-resources-route.injectable";
|
||||
import currentPathParametersInjectable from "../../routes/current-path-parameters.injectable";
|
||||
import type { SidebarItemRegistration } from "../layout/sidebar-items.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToCustomResourcesInjectable from "../../../common/front-end-routing/routes/cluster/custom-resources/custom-resources/navigate-to-custom-resources.injectable";
|
||||
|
||||
const sidebarItemsForDefinitionGroupsInjectable = getInjectable({
|
||||
id: "sidebar-items-for-definition-groups",
|
||||
|
||||
instantiate: (di) => {
|
||||
const customResourceDefinitions = di.inject(
|
||||
customResourceDefinitionsInjectable,
|
||||
);
|
||||
|
||||
const crdRoute = di.inject(customResourcesRouteInjectable);
|
||||
const crdRouteIsActive = di.inject(routeIsActiveInjectable, crdRoute);
|
||||
const crdListRoute = di.inject(crdListRouteInjectable);
|
||||
const pathParameters = di.inject(currentPathParametersInjectable);
|
||||
const navigateToCustomResources = di.inject(navigateToCustomResourcesInjectable);
|
||||
|
||||
return computed((): SidebarItemRegistration[] => {
|
||||
const definitions = customResourceDefinitions.get();
|
||||
|
||||
const groupedCrds = toPairs(
|
||||
groupBy((crd) => crd.getGroup(), definitions),
|
||||
);
|
||||
|
||||
return groupedCrds.flatMap(([group, definitions]) => {
|
||||
const childItems = definitions.map((crd) => {
|
||||
const title = crd.getResourceKind();
|
||||
|
||||
const crdPathParameters = {
|
||||
group: crd.getGroup(),
|
||||
name: crd.getPluralName(),
|
||||
};
|
||||
|
||||
return {
|
||||
id: `custom-resource-definition-group-${group}-crd-${crd.getId()}`,
|
||||
parentId: `custom-resource-definition-group-${group}`,
|
||||
title,
|
||||
|
||||
onClick: () => navigateToCustomResources(crdPathParameters),
|
||||
|
||||
isActive: computed(
|
||||
() =>
|
||||
crdRouteIsActive.get() &&
|
||||
matches(crdPathParameters, pathParameters.get()),
|
||||
),
|
||||
|
||||
isVisible: crdListRoute.isEnabled,
|
||||
orderNumber: 10,
|
||||
};
|
||||
});
|
||||
|
||||
return [
|
||||
{
|
||||
id: `custom-resource-definition-group-${group}`,
|
||||
parentId: "custom-resources",
|
||||
title: group,
|
||||
onClick: noop,
|
||||
isVisible: computed(() => some(item => item.isVisible.get(), childItems)),
|
||||
orderNumber: 10,
|
||||
},
|
||||
|
||||
...childItems,
|
||||
];
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default sidebarItemsForDefinitionGroupsInjectable;
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { EntitySettings } from "./entity-settings";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import entitySettingsRouteInjectable from "../../../common/front-end-routing/routes/entity-settings/entity-settings-route.injectable";
|
||||
|
||||
const entitySettingsRouteComponentInjectable = getInjectable({
|
||||
id: "entity-settings-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(entitySettingsRouteInjectable),
|
||||
Component: EntitySettings,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default entitySettingsRouteComponentInjectable;
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import routePathParametersInjectable from "../../routes/route-path-parameters.injectable";
|
||||
import entitySettingsRouteInjectable from "../../../common/front-end-routing/routes/entity-settings/entity-settings-route.injectable";
|
||||
|
||||
const entitySettingsRouteParametersInjectable = getInjectable({
|
||||
id: "entity-settings-route-parameters",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(entitySettingsRouteInjectable);
|
||||
const pathParameters = di.inject(routePathParametersInjectable, route);
|
||||
|
||||
return {
|
||||
entityId: computed(() => pathParameters.get().entityId),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default entitySettingsRouteParametersInjectable;
|
||||
@@ -6,28 +6,29 @@
|
||||
import styles from "./entity-settings.module.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observable, makeObservable } from "mobx";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { observable, makeObservable, IComputedValue, computed } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { navigation } from "../../navigation";
|
||||
import { Tabs, Tab } from "../tabs";
|
||||
import type { CatalogEntity } from "../../api/catalog-entity";
|
||||
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
||||
import { EntitySettingRegistry } from "../../../extensions/registries";
|
||||
import type { EntitySettingsRouteParams } from "../../../common/routes";
|
||||
import { groupBy } from "lodash";
|
||||
import { SettingLayout } from "../layout/setting-layout";
|
||||
import logger from "../../../common/logger";
|
||||
import { Avatar } from "../avatar";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import entitySettingsRouteParametersInjectable from "./entity-settings-route-parameters.injectable";
|
||||
|
||||
export interface EntitySettingsProps extends RouteComponentProps<EntitySettingsRouteParams> {
|
||||
interface Dependencies {
|
||||
entityId: IComputedValue<string>;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class EntitySettings extends React.Component<EntitySettingsProps> {
|
||||
class NonInjectedEntitySettings extends React.Component<Dependencies> {
|
||||
@observable activeTab: string;
|
||||
|
||||
constructor(props: EntitySettingsProps) {
|
||||
constructor(props: Dependencies) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
|
||||
@@ -43,8 +44,9 @@ export class EntitySettings extends React.Component<EntitySettingsProps> {
|
||||
}
|
||||
}
|
||||
|
||||
@computed
|
||||
get entityId() {
|
||||
return this.props.match.params.entityId;
|
||||
return this.props.entityId.get();
|
||||
}
|
||||
|
||||
get entity(): CatalogEntity {
|
||||
@@ -120,7 +122,6 @@ export class EntitySettings extends React.Component<EntitySettingsProps> {
|
||||
|
||||
const { activeSetting } = this;
|
||||
|
||||
|
||||
return (
|
||||
<SettingLayout
|
||||
navigation={this.renderNavigation()}
|
||||
@@ -140,3 +141,17 @@ export class EntitySettings extends React.Component<EntitySettingsProps> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const EntitySettings = withInjectables<Dependencies>(
|
||||
NonInjectedEntitySettings,
|
||||
|
||||
{
|
||||
getProps: (di) => {
|
||||
const routeParameters = di.inject(entitySettingsRouteParametersInjectable);
|
||||
|
||||
return {
|
||||
entityId: routeParameters.entityId,
|
||||
};
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { Events } from "./events";
|
||||
import eventsRouteInjectable from "../../../common/front-end-routing/routes/cluster/events/events-route.injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const eventsRouteComponentInjectable = getInjectable({
|
||||
id: "events-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(eventsRouteInjectable),
|
||||
Component: Events,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default eventsRouteComponentInjectable;
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import React from "react";
|
||||
import {
|
||||
SidebarItemRegistration,
|
||||
sidebarItemsInjectionToken,
|
||||
} from "../layout/sidebar-items.injectable";
|
||||
import { Icon } from "../icon";
|
||||
|
||||
import eventsRouteInjectable from "../../../common/front-end-routing/routes/cluster/events/events-route.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToEventsInjectable from "../../../common/front-end-routing/routes/cluster/events/navigate-to-events.injectable";
|
||||
|
||||
const eventsSidebarItemsInjectable = getInjectable({
|
||||
id: "events-sidebar-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(eventsRouteInjectable);
|
||||
const navigateToEvents = di.inject(navigateToEventsInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return computed((): SidebarItemRegistration[] => [
|
||||
{
|
||||
id: "events",
|
||||
parentId: null,
|
||||
getIcon: () => <Icon material="access_time" />,
|
||||
title: "Events",
|
||||
onClick: navigateToEvents,
|
||||
isActive: routeIsActive,
|
||||
isVisible: route.isEnabled,
|
||||
orderNumber: 80,
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default eventsSidebarItemsInjectable;
|
||||
@@ -9,7 +9,7 @@ import React, { Fragment } from "react";
|
||||
import { computed, observable, makeObservable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { orderBy } from "lodash";
|
||||
import { TabLayout } from "../layout/tab-layout";
|
||||
import { TabLayout } from "../layout/tab-layout-2";
|
||||
import { EventStore, eventStore } from "./event.store";
|
||||
import { KubeObjectListLayout, KubeObjectListLayoutProps } from "../kube-object-list-layout";
|
||||
import type { KubeEvent } from "../../../common/k8s-api/endpoints/events.api";
|
||||
@@ -19,9 +19,10 @@ import { Tooltip } from "../tooltip";
|
||||
import { Link } from "react-router-dom";
|
||||
import { cssNames, IClassName, stopPropagation } from "../../utils";
|
||||
import { Icon } from "../icon";
|
||||
import { eventsURL } from "../../../common/routes";
|
||||
import { getDetailsUrl } from "../kube-detail-params";
|
||||
import { apiManager } from "../../../common/k8s-api/api-manager";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import navigateToEventsInjectable from "../../../common/front-end-routing/routes/cluster/events/navigate-to-events.injectable";
|
||||
import { KubeObjectAge } from "../kube-object/age";
|
||||
import { ReactiveDuration } from "../duration/reactive-duration";
|
||||
|
||||
@@ -46,8 +47,12 @@ const defaultProps: Partial<EventsProps> = {
|
||||
compactLimit: 10,
|
||||
};
|
||||
|
||||
interface Dependencies {
|
||||
navigateToEvents: () => void;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class Events extends React.Component<EventsProps> {
|
||||
class NonInjectedEvents extends React.Component<Dependencies & EventsProps> {
|
||||
static defaultProps = defaultProps as object;
|
||||
|
||||
@observable sorting: TableSortParams = {
|
||||
@@ -64,7 +69,7 @@ export class Events extends React.Component<EventsProps> {
|
||||
[columnId.lastSeen]: event => -new Date(event.lastTimestamp).getTime(),
|
||||
};
|
||||
|
||||
constructor(props: EventsProps) {
|
||||
constructor(props: Dependencies & EventsProps) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
@@ -105,7 +110,7 @@ export class Events extends React.Component<EventsProps> {
|
||||
|
||||
return {
|
||||
title,
|
||||
info: <span> ({visibleItems.length} of <Link to={eventsURL()}>{items.length}</Link>)</span>,
|
||||
info: <span> ({visibleItems.length} of <a onClick={this.props.navigateToEvents}>{items.length}</a>)</span>,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -204,3 +209,14 @@ export class Events extends React.Component<EventsProps> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const Events = withInjectables<Dependencies, EventsProps>(
|
||||
NonInjectedEvents,
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
navigateToEvents: di.inject(navigateToEventsInjectable),
|
||||
...props,
|
||||
}),
|
||||
},
|
||||
);
|
||||
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { eventRoute, eventsURL } from "../../../common/routes";
|
||||
import type { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import isAllowedResourceInjectable from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
|
||||
export interface EventsSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
isAllowedResource: IsAllowedResource;
|
||||
}
|
||||
|
||||
const NonInjectedEventsSidebarItem = observer(({ isAllowedResource }: Dependencies & EventsSidebarItemProps) => (
|
||||
<SidebarItem
|
||||
id="events"
|
||||
text="Events"
|
||||
isActive={isActiveRoute(eventRoute)}
|
||||
isHidden={!isAllowedResource("events")}
|
||||
url={eventsURL()}
|
||||
icon={<Icon material="access_time"/>}
|
||||
/>
|
||||
));
|
||||
|
||||
export const EventsSidebarItem = withInjectables<Dependencies, EventsSidebarItemProps>(NonInjectedEventsSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -20,6 +20,10 @@ import { DiRender, renderFor } from "../../test-utils/renderFor";
|
||||
import extensionDiscoveryInjectable from "../../../../extensions/extension-discovery/extension-discovery.injectable";
|
||||
import directoryForUserDataInjectable from "../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import directoryForDownloadsInjectable from "../../../../common/app-paths/directory-for-downloads/directory-for-downloads.injectable";
|
||||
import getConfigurationFileModelInjectable
|
||||
from "../../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
|
||||
import appVersionInjectable
|
||||
from "../../../../common/get-configuration-file-model/app-version/app-version.injectable";
|
||||
|
||||
mockWindow();
|
||||
|
||||
@@ -53,6 +57,9 @@ describe("Extensions", () => {
|
||||
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
|
||||
di.override(directoryForDownloadsInjectable, () => "some-directory-for-downloads");
|
||||
|
||||
di.permitSideEffects(getConfigurationFileModelInjectable);
|
||||
di.permitSideEffects(appVersionInjectable);
|
||||
|
||||
mockFs({
|
||||
"some-directory-for-user-data": {},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { Extensions } from "./extensions";
|
||||
import extensionsRouteInjectable from "../../../common/front-end-routing/routes/extensions/extensions-route.injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const extensionsRouteComponentInjectable = getInjectable({
|
||||
id: "extensions-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(extensionsRouteInjectable),
|
||||
Component: Extensions,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default extensionsRouteComponentInjectable;
|
||||
@@ -71,7 +71,7 @@ class NonInjectedExtensions extends React.Component<Dependencies> {
|
||||
|
||||
return (
|
||||
<DropFileInput onDropFiles={this.props.installOnDrop}>
|
||||
<SettingLayout className="Extensions" contentGaps={false}>
|
||||
<SettingLayout className="Extensions" contentGaps={false} data-testid="extensions-page">
|
||||
<section>
|
||||
<h1>Extensions</h1>
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ class NonInjectedHelmChartDetails extends Component<HelmChartDetailsProps & Depe
|
||||
</div>
|
||||
<DrawerItem name="Version" className="version" onClick={stopPropagation}>
|
||||
<Select
|
||||
id="chart-version-input"
|
||||
themeName="outlined"
|
||||
menuPortalTarget={null}
|
||||
options={chartVersions.map(chart => ({
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { HelmCharts } from "./helm-charts";
|
||||
import helmChartsRouteInjectable from "../../../common/front-end-routing/routes/cluster/helm/charts/helm-charts-route.injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const helmChartsRouteComponentInjectable = getInjectable({
|
||||
id: "helm-charts-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(helmChartsRouteInjectable),
|
||||
Component: HelmCharts,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default helmChartsRouteComponentInjectable;
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import routePathParametersInjectable from "../../routes/route-path-parameters.injectable";
|
||||
import helmChartsRouteInjectable from "../../../common/front-end-routing/routes/cluster/helm/charts/helm-charts-route.injectable";
|
||||
|
||||
const helmChartsRouteParametersInjectable = getInjectable({
|
||||
id: "helm-charts-route-parameters",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(helmChartsRouteInjectable);
|
||||
const pathParameters = di.inject(routePathParametersInjectable, route);
|
||||
|
||||
return {
|
||||
chartName: computed(() => pathParameters.get().chartName),
|
||||
repo: computed(() => pathParameters.get().repo),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default helmChartsRouteParametersInjectable;
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import helmChartsRouteInjectable from "../../../common/front-end-routing/routes/cluster/helm/charts/helm-charts-route.injectable";
|
||||
import { helmSidebarItemId } from "../+helm/helm-sidebar-items.injectable";
|
||||
import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToHelmChartsInjectable from "../../../common/front-end-routing/routes/cluster/helm/charts/navigate-to-helm-charts.injectable";
|
||||
|
||||
const helmChartsSidebarItemsInjectable = getInjectable({
|
||||
id: "helm-charts-sidebar-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(helmChartsRouteInjectable);
|
||||
const navigateToHelmCharts = di.inject(navigateToHelmChartsInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return computed(() => [
|
||||
{
|
||||
id: "charts",
|
||||
parentId: helmSidebarItemId,
|
||||
title: "Charts",
|
||||
onClick: navigateToHelmCharts,
|
||||
isActive: routeIsActive,
|
||||
isVisible: route.isEnabled,
|
||||
orderNumber: 10,
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default helmChartsSidebarItemsInjectable;
|
||||
@@ -6,15 +6,17 @@
|
||||
import "./helm-charts.scss";
|
||||
|
||||
import React, { Component } from "react";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { observer } from "mobx-react";
|
||||
import { helmChartStore } from "./helm-chart.store";
|
||||
import type { HelmChart } from "../../../common/k8s-api/endpoints/helm-charts.api";
|
||||
import { HelmChartDetails } from "./helm-chart-details";
|
||||
import { navigation } from "../../navigation";
|
||||
import { ItemListLayout } from "../item-object-list/list-layout";
|
||||
import { helmChartsURL } from "../../../common/routes";
|
||||
import type { HelmChartsRouteParams } from "../../../common/routes";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
|
||||
import helmChartsRouteParametersInjectable from "./helm-charts-route-parameters.injectable";
|
||||
import type { NavigateToHelmCharts } from "../../../common/front-end-routing/routes/cluster/helm/charts/navigate-to-helm-charts.injectable";
|
||||
import navigateToHelmChartsInjectable from "../../../common/front-end-routing/routes/cluster/helm/charts/navigate-to-helm-charts.injectable";
|
||||
|
||||
enum columnId {
|
||||
name = "name",
|
||||
@@ -24,19 +26,25 @@ enum columnId {
|
||||
repo = "repo",
|
||||
}
|
||||
|
||||
export interface HelmChartsProps extends RouteComponentProps<HelmChartsRouteParams> {
|
||||
interface Dependencies {
|
||||
routeParameters: {
|
||||
chartName: IComputedValue<string>;
|
||||
repo: IComputedValue<string>;
|
||||
};
|
||||
|
||||
navigateToHelmCharts: NavigateToHelmCharts;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class HelmCharts extends Component<HelmChartsProps> {
|
||||
class NonInjectedHelmCharts extends Component<Dependencies> {
|
||||
componentDidMount() {
|
||||
helmChartStore.loadAll();
|
||||
}
|
||||
|
||||
get selectedChart() {
|
||||
const { match: { params: { chartName, repo }}} = this.props;
|
||||
const { chartName, repo } = this.props.routeParameters;
|
||||
|
||||
return helmChartStore.getByName(chartName, repo);
|
||||
return helmChartStore.getByName(chartName.get(), repo.get());
|
||||
}
|
||||
|
||||
onDetails = (chart: HelmChart) => {
|
||||
@@ -49,15 +57,13 @@ export class HelmCharts extends Component<HelmChartsProps> {
|
||||
|
||||
showDetails = (chart: HelmChart) => {
|
||||
if (!chart) {
|
||||
navigation.push(helmChartsURL());
|
||||
this.props.navigateToHelmCharts();
|
||||
}
|
||||
else {
|
||||
navigation.push(helmChartsURL({
|
||||
params: {
|
||||
chartName: chart.getName(),
|
||||
repo: chart.getRepository(),
|
||||
},
|
||||
}));
|
||||
this.props.navigateToHelmCharts({
|
||||
chartName: chart.getName(),
|
||||
repo: chart.getRepository(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -67,7 +73,7 @@ export class HelmCharts extends Component<HelmChartsProps> {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<SiblingsInTabLayout>
|
||||
<ItemListLayout
|
||||
isConfigurable
|
||||
tableId="helm_charts"
|
||||
@@ -122,7 +128,19 @@ export class HelmCharts extends Component<HelmChartsProps> {
|
||||
hideDetails={this.hideDetails}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</SiblingsInTabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const HelmCharts = withInjectables<Dependencies>(
|
||||
NonInjectedHelmCharts,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
routeParameters: di.inject(helmChartsRouteParametersInjectable),
|
||||
navigateToHelmCharts: di.inject(navigateToHelmChartsInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { HelmReleases } from "./releases";
|
||||
import helmReleasesRouteInjectable from "../../../common/front-end-routing/routes/cluster/helm/releases/helm-releases-route.injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const helmReleasesRouteComponentInjectable = getInjectable({
|
||||
id: "helm-releases-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(helmReleasesRouteInjectable),
|
||||
Component: HelmReleases,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default helmReleasesRouteComponentInjectable;
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import helmReleasesRouteInjectable from "../../../common/front-end-routing/routes/cluster/helm/releases/helm-releases-route.injectable";
|
||||
import routePathParametersInjectable from "../../routes/route-path-parameters.injectable";
|
||||
|
||||
const helmReleasesRouteParametersInjectable = getInjectable({
|
||||
id: "helm-releases-route-parameters",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(helmReleasesRouteInjectable);
|
||||
const pathParameters = di.inject(routePathParametersInjectable, route);
|
||||
|
||||
return {
|
||||
namespace: computed(() => pathParameters.get().namespace),
|
||||
name: computed(() => pathParameters.get().name),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default helmReleasesRouteParametersInjectable;
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
|
||||
import helmReleasesRouteInjectable from "../../../common/front-end-routing/routes/cluster/helm/releases/helm-releases-route.injectable";
|
||||
import { helmSidebarItemId } from "../+helm/helm-sidebar-items.injectable";
|
||||
import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToHelmReleasesInjectable from "../../../common/front-end-routing/routes/cluster/helm/releases/navigate-to-helm-releases.injectable";
|
||||
|
||||
const helmReleasesSidebarItemsInjectable = getInjectable({
|
||||
id: "helm-releases-sidebar-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(helmReleasesRouteInjectable);
|
||||
const navigateToHelmReleases = di.inject(navigateToHelmReleasesInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return computed(() => [
|
||||
{
|
||||
id: "releases",
|
||||
parentId: helmSidebarItemId,
|
||||
title: "Releases",
|
||||
onClick: navigateToHelmReleases,
|
||||
isActive: routeIsActive,
|
||||
isVisible: route.isEnabled,
|
||||
orderNumber: 20,
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default helmReleasesSidebarItemsInjectable;
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import { matchPath } from "react-router";
|
||||
import observableHistoryInjectable from "../../../navigation/observable-history.injectable";
|
||||
import { releaseRoute, ReleaseRouteParams } from "../../../../common/routes";
|
||||
|
||||
const releaseRouteParametersInjectable = getInjectable({
|
||||
id: "release-route-parameters",
|
||||
|
||||
instantiate: (di) => {
|
||||
const observableHistory = di.inject(observableHistoryInjectable);
|
||||
|
||||
return computed(() => {
|
||||
const releasePathParameters = matchPath<ReleaseRouteParams>(observableHistory.location.pathname, {
|
||||
path: releaseRoute.path,
|
||||
});
|
||||
|
||||
if (!releasePathParameters) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return releasePathParameters.params;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default releaseRouteParametersInjectable;
|
||||
@@ -5,18 +5,19 @@
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { matches } from "lodash/fp";
|
||||
import releasesInjectable from "../releases.injectable";
|
||||
import releaseRouteParametersInjectable from "./release-route-parameters.injectable";
|
||||
import { computed } from "mobx";
|
||||
import helmReleasesRouteParametersInjectable from "../helm-releases-route-parameters.injectable";
|
||||
|
||||
const releaseInjectable = getInjectable({
|
||||
id: "release",
|
||||
|
||||
instantiate: (di) => {
|
||||
const releases = di.inject(releasesInjectable);
|
||||
const releaseRouteParameters = di.inject(releaseRouteParametersInjectable);
|
||||
const routeParameters = di.inject(helmReleasesRouteParametersInjectable);
|
||||
|
||||
return computed(() => {
|
||||
const { name, namespace } = releaseRouteParameters.get();
|
||||
const name = routeParameters.name.get();
|
||||
const namespace = routeParameters.namespace.get();
|
||||
|
||||
if (!name || !namespace) {
|
||||
return null;
|
||||
|
||||
@@ -75,6 +75,7 @@ class NonInjectedReleaseRollbackDialog extends React.Component<ReleaseRollbackDi
|
||||
<div className="flex gaps align-center">
|
||||
<b>Revision</b>
|
||||
<Select
|
||||
id="revision-input"
|
||||
themeName="light"
|
||||
value={revision}
|
||||
options={revisions}
|
||||
|
||||
@@ -7,11 +7,7 @@ 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";
|
||||
@@ -25,6 +21,10 @@ 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",
|
||||
@@ -37,18 +37,18 @@ enum columnId {
|
||||
updated = "update",
|
||||
}
|
||||
|
||||
export interface HelmReleasesProps extends RouteComponentProps<ReleaseRouteParams> {
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
releases: IComputedValue<RemovableHelmRelease[]>;
|
||||
releasesArePending: IComputedValue<boolean>;
|
||||
selectNamespace: (namespace: string) => void;
|
||||
namespace: IComputedValue<string>;
|
||||
navigateToHelmReleases: NavigateToHelmReleases;
|
||||
}
|
||||
|
||||
class NonInjectedHelmReleases extends Component<Dependencies & HelmReleasesProps> {
|
||||
class NonInjectedHelmReleases extends Component<Dependencies> {
|
||||
// TODO: This side-effect in mount must go.
|
||||
componentDidMount() {
|
||||
const { match: { params: { namespace }}} = this.props;
|
||||
const namespace = this.props.namespace.get();
|
||||
|
||||
if (namespace) {
|
||||
this.props.selectNamespace(namespace);
|
||||
@@ -60,16 +60,14 @@ class NonInjectedHelmReleases extends Component<Dependencies & HelmReleasesProps
|
||||
};
|
||||
|
||||
showDetails = (item: HelmRelease) => {
|
||||
navigation.push(releaseURL({
|
||||
params: {
|
||||
name: item.getName(),
|
||||
namespace: item.getNs(),
|
||||
},
|
||||
}));
|
||||
this.props.navigateToHelmReleases({
|
||||
name: item.getName(),
|
||||
namespace: item.getNs(),
|
||||
});
|
||||
};
|
||||
|
||||
hideDetails = () => {
|
||||
navigation.push(releaseURL());
|
||||
this.props.navigateToHelmReleases();
|
||||
};
|
||||
|
||||
renderRemoveDialogMessage(selectedItems: HelmRelease[]) {
|
||||
@@ -144,7 +142,7 @@ class NonInjectedHelmReleases extends Component<Dependencies & HelmReleasesProps
|
||||
} as ItemStore<RemovableHelmRelease>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SiblingsInTabLayout>
|
||||
<ItemListLayout
|
||||
store={legacyReleaseStore}
|
||||
getItems={() => legacyReleaseStore.items}
|
||||
@@ -171,7 +169,7 @@ class NonInjectedHelmReleases extends Component<Dependencies & HelmReleasesProps
|
||||
filters: (
|
||||
<>
|
||||
{filters}
|
||||
<NamespaceSelectFilter />
|
||||
<NamespaceSelectFilter id="namespace-select-filter" />
|
||||
</>
|
||||
),
|
||||
searchProps: {
|
||||
@@ -218,20 +216,25 @@ class NonInjectedHelmReleases extends Component<Dependencies & HelmReleasesProps
|
||||
/>
|
||||
|
||||
<ReleaseRollbackDialog/>
|
||||
</>
|
||||
</SiblingsInTabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const HelmReleases = withInjectables<Dependencies, HelmReleasesProps>(
|
||||
export const HelmReleases = withInjectables<Dependencies>(
|
||||
NonInjectedHelmReleases,
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
releases: di.inject(removableReleasesInjectable),
|
||||
releasesArePending: di.inject(releasesInjectable).pending,
|
||||
selectNamespace: di.inject(namespaceStoreInjectable).selectNamespaces,
|
||||
...props,
|
||||
}),
|
||||
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,
|
||||
};
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import React from "react";
|
||||
import {
|
||||
SidebarItemRegistration,
|
||||
sidebarItemsInjectionToken,
|
||||
} from "../layout/sidebar-items.injectable";
|
||||
import { Icon } from "../icon";
|
||||
import { noop } from "lodash/fp";
|
||||
|
||||
export const helmSidebarItemId = "helm";
|
||||
|
||||
const helmSidebarItemsInjectable = getInjectable({
|
||||
id: "helm-sidebar-items",
|
||||
|
||||
instantiate: () =>
|
||||
computed((): SidebarItemRegistration[] => [
|
||||
{
|
||||
id: helmSidebarItemId,
|
||||
parentId: null,
|
||||
getIcon: () => <Icon svg="helm" />,
|
||||
title: "Helm",
|
||||
onClick: noop,
|
||||
orderNumber: 90,
|
||||
},
|
||||
]),
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default helmSidebarItemsInjectable;
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { HelmCharts } from "../+helm-charts";
|
||||
import { HelmReleases } from "../+helm-releases";
|
||||
import { helmChartsURL, helmChartsRoute, releaseURL, releaseRoute } from "../../../common/routes";
|
||||
|
||||
function getRouteTabs() {
|
||||
return computed((): TabLayoutRoute[] => [
|
||||
{
|
||||
title: "Charts",
|
||||
component: HelmCharts,
|
||||
url: helmChartsURL(),
|
||||
routePath: helmChartsRoute.path.toString(),
|
||||
},
|
||||
{
|
||||
title: "Releases",
|
||||
component: HelmReleases,
|
||||
url: releaseURL(),
|
||||
routePath: releaseRoute.path.toString(),
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
const helmRoutesInjectable = getInjectable({
|
||||
id: "helm-routes",
|
||||
instantiate: () => getRouteTabs(),
|
||||
});
|
||||
|
||||
export default helmRoutesInjectable;
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import helmRoutesInjectable from "./route-tabs.injectable";
|
||||
|
||||
export interface HelmRouteProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<TabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedHelmRoute = observer(({ routes }: Dependencies & HelmRouteProps) => (
|
||||
<TabLayout
|
||||
className="Apps"
|
||||
tabs={routes.get()}
|
||||
/>
|
||||
));
|
||||
|
||||
export const HelmRoute = withInjectables<Dependencies, HelmRouteProps>(NonInjectedHelmRoute, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(helmRoutesInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import React from "react";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
import type { TabLayoutRoute } from "../layout/tab-layout";
|
||||
import { renderTabRoutesSidebarItems } from "../layout/tab-routes-sidebar-items";
|
||||
import { helmRoute, helmURL } from "../../../common/routes";
|
||||
import networkRouteTabsInjectable from "./route-tabs.injectable";
|
||||
|
||||
export interface HelmSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
routes: IComputedValue<TabLayoutRoute[]>;
|
||||
}
|
||||
|
||||
const NonInjectedHelmSidebarItem = observer(({ routes }: Dependencies & HelmSidebarItemProps) => {
|
||||
const tabRoutes = routes.get();
|
||||
|
||||
return (
|
||||
<SidebarItem
|
||||
id="helm"
|
||||
text="Helm"
|
||||
isActive={isActiveRoute(helmRoute)}
|
||||
url={helmURL()}
|
||||
icon={<Icon svg="helm" />}
|
||||
>
|
||||
{renderTabRoutesSidebarItems(tabRoutes)}
|
||||
</SidebarItem>
|
||||
);
|
||||
});
|
||||
|
||||
export const HelmSidebarItem = withInjectables<Dependencies, HelmSidebarItemProps>(NonInjectedHelmSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
routes: di.inject(networkRouteTabsInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -30,6 +30,7 @@ const NonInjectedNamespaceSelectFilter = observer(({ model }: SelectProps & Depe
|
||||
onClick={model.onClick}
|
||||
>
|
||||
<NamespaceSelect
|
||||
id="namespace-select-filter-input"
|
||||
isMulti={true}
|
||||
menuIsOpen={model.menuIsOpen}
|
||||
components={{ Placeholder }}
|
||||
|
||||
@@ -77,6 +77,7 @@ class NonInjectedNamespaceSelect extends React.Component<NamespaceSelectProps &
|
||||
|
||||
return (
|
||||
<Select
|
||||
id="namespace-input"
|
||||
className={cssNames("NamespaceSelect", className)}
|
||||
menuClass="NamespaceSelectMenu"
|
||||
formatOptionLabel={this.formatOptionLabel}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { NamespacesRoute } from "./route";
|
||||
import namespacesRouteInjectable from "../../../common/front-end-routing/routes/cluster/namespaces/namespaces-route.injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const namespacesRouteComponentInjectable = getInjectable({
|
||||
id: "namespaces-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(namespacesRouteInjectable),
|
||||
Component: NamespacesRoute,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default namespacesRouteComponentInjectable;
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import {
|
||||
SidebarItemRegistration,
|
||||
sidebarItemsInjectionToken,
|
||||
} from "../layout/sidebar-items.injectable";
|
||||
import { Icon } from "../icon";
|
||||
import React from "react";
|
||||
import namespacesRouteInjectable from "../../../common/front-end-routing/routes/cluster/namespaces/namespaces-route.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToNamespacesInjectable from "../../../common/front-end-routing/routes/cluster/namespaces/navigate-to-namespaces.injectable";
|
||||
|
||||
const namespacesSidebarItemsInjectable = getInjectable({
|
||||
id: "namespaces",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(namespacesRouteInjectable);
|
||||
const navigateToNamespaces = di.inject(navigateToNamespacesInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return computed((): SidebarItemRegistration[] => [
|
||||
{
|
||||
id: "namespaces",
|
||||
parentId: null,
|
||||
getIcon: () => <Icon material="layers" />,
|
||||
title: "Namespaces",
|
||||
onClick: navigateToNamespaces,
|
||||
isActive: routeIsActive,
|
||||
isVisible: route.isEnabled,
|
||||
orderNumber: 70,
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default namespacesSidebarItemsInjectable;
|
||||
@@ -8,17 +8,14 @@ import "./namespaces.scss";
|
||||
import React from "react";
|
||||
import { NamespaceStatus } from "../../../common/k8s-api/endpoints";
|
||||
import { AddNamespaceDialog } from "./add-namespace-dialog";
|
||||
import { TabLayout } from "../layout/tab-layout";
|
||||
import { TabLayout } from "../layout/tab-layout-2";
|
||||
import { Badge } from "../badge";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import type { NamespaceStore } from "./namespace-store/namespace.store";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { NamespacesRouteParams } from "../../../common/routes";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import namespaceStoreInjectable from "./namespace-store/namespace-store.injectable";
|
||||
import addNamespaceDialogModelInjectable
|
||||
from "./add-namespace-dialog-model/add-namespace-dialog-model.injectable";
|
||||
import addNamespaceDialogModelInjectable from "./add-namespace-dialog-model/add-namespace-dialog-model.injectable";
|
||||
import { KubeObjectAge } from "../kube-object/age";
|
||||
|
||||
enum columnId {
|
||||
@@ -28,15 +25,12 @@ enum columnId {
|
||||
status = "status",
|
||||
}
|
||||
|
||||
export interface NamespacesRouteProps extends RouteComponentProps<NamespacesRouteParams> {
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
namespaceStore: NamespaceStore;
|
||||
openAddNamespaceDialog: () => void;
|
||||
}
|
||||
|
||||
export const NonInjectedNamespacesRoute = ({ namespaceStore, openAddNamespaceDialog }: Dependencies & NamespacesRouteProps) => (
|
||||
export const NonInjectedNamespacesRoute = ({ namespaceStore, openAddNamespaceDialog }: Dependencies) => (
|
||||
<TabLayout>
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
@@ -81,10 +75,9 @@ export const NonInjectedNamespacesRoute = ({ namespaceStore, openAddNamespaceDia
|
||||
);
|
||||
|
||||
|
||||
export const NamespacesRoute = withInjectables<Dependencies, NamespacesRouteProps>(NonInjectedNamespacesRoute, {
|
||||
getProps: (di, props) => ({
|
||||
export const NamespacesRoute = withInjectables<Dependencies>(NonInjectedNamespacesRoute, {
|
||||
getProps: (di) => ({
|
||||
namespaceStore: di.inject(namespaceStoreInjectable),
|
||||
openAddNamespaceDialog: di.inject(addNamespaceDialogModelInjectable).open,
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { namespacesRoute, namespacesURL } from "../../../common/routes";
|
||||
import type { IsAllowedResource } from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import isAllowedResourceInjectable from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import { Icon } from "../icon";
|
||||
import { SidebarItem } from "../layout/sidebar-item";
|
||||
|
||||
export interface NamespacesSidebarItemProps {}
|
||||
|
||||
interface Dependencies {
|
||||
isAllowedResource: IsAllowedResource;
|
||||
}
|
||||
|
||||
const NonInjectedNamespacesSidebarItem = observer(({ isAllowedResource }: Dependencies & NamespacesSidebarItemProps) => (
|
||||
<SidebarItem
|
||||
id="namespaces"
|
||||
text="Namespaces"
|
||||
isActive={isActiveRoute(namespacesRoute)}
|
||||
isHidden={!isAllowedResource("namespaces")}
|
||||
url={namespacesURL()}
|
||||
icon={<Icon material="layers"/>}
|
||||
/>
|
||||
));
|
||||
|
||||
export const NamespacesSidebarItem = withInjectables<Dependencies, NamespacesSidebarItemProps>(NonInjectedNamespacesSidebarItem, {
|
||||
getProps: (di, props) => ({
|
||||
isAllowedResource: di.inject(isAllowedResourceInjectable),
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { Endpoints } from "./endpoints";
|
||||
import endpointsRouteInjectable from "../../../common/front-end-routing/routes/cluster/network/endpoints/endpoints-route.injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const endpointsRouteComponentInjectable = getInjectable({
|
||||
id: "endpoints-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(endpointsRouteInjectable),
|
||||
Component: Endpoints,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default endpointsRouteComponentInjectable;
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import endpointsRouteInjectable from "../../../common/front-end-routing/routes/cluster/network/endpoints/endpoints-route.injectable";
|
||||
import { networkSidebarItemId } from "../+network/network-sidebar-items.injectable";
|
||||
import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToEndpointsInjectable from "../../../common/front-end-routing/routes/cluster/network/endpoints/navigate-to-endpoints.injectable";
|
||||
|
||||
const endpointsSidebarItemsInjectable = getInjectable({
|
||||
id: "endpoints-sidebar-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(endpointsRouteInjectable);
|
||||
const navigateToEndpoints = di.inject(navigateToEndpointsInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return computed(() => [
|
||||
{
|
||||
id: "endpoints",
|
||||
parentId: networkSidebarItemId,
|
||||
title: "Endpoints",
|
||||
onClick: navigateToEndpoints,
|
||||
isActive: routeIsActive,
|
||||
isVisible: route.isEnabled,
|
||||
orderNumber: 20,
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default endpointsSidebarItemsInjectable;
|
||||
@@ -7,11 +7,10 @@ import "./endpoints.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router-dom";
|
||||
import { endpointStore } from "./endpoints.store";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { EndpointRouteParams } from "../../../common/routes";
|
||||
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
|
||||
import { KubeObjectAge } from "../kube-object/age";
|
||||
|
||||
enum columnId {
|
||||
@@ -21,48 +20,47 @@ enum columnId {
|
||||
age = "age",
|
||||
}
|
||||
|
||||
export interface EndpointsProps extends RouteComponentProps<EndpointRouteParams> {
|
||||
}
|
||||
|
||||
@observer
|
||||
export class Endpoints extends React.Component<EndpointsProps> {
|
||||
export class Endpoints extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="network_endpoints"
|
||||
className="Endpoints" store={endpointStore}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: endpoint => endpoint.getName(),
|
||||
[columnId.namespace]: endpoint => endpoint.getNs(),
|
||||
[columnId.age]: endpoint => -endpoint.getCreationTimestamp(),
|
||||
}}
|
||||
searchFilters={[
|
||||
endpoint => endpoint.getSearchFields(),
|
||||
]}
|
||||
renderHeaderTitle="Endpoints"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
{ className: "warning", showWithColumn: columnId.name },
|
||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
{ title: "Endpoints", className: "endpoints", id: columnId.endpoints },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
]}
|
||||
renderTableContents={endpoint => [
|
||||
endpoint.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={endpoint} />,
|
||||
endpoint.getNs(),
|
||||
endpoint.toString(),
|
||||
<KubeObjectAge key="age" object={endpoint} />,
|
||||
]}
|
||||
tableProps={{
|
||||
customRowHeights: (item, lineHeight, paddings) => {
|
||||
const lines = item.getEndpointSubsets().length || 1;
|
||||
<SiblingsInTabLayout>
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="network_endpoints"
|
||||
className="Endpoints" store={endpointStore}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: endpoint => endpoint.getName(),
|
||||
[columnId.namespace]: endpoint => endpoint.getNs(),
|
||||
[columnId.age]: endpoint => -endpoint.getCreationTimestamp(),
|
||||
}}
|
||||
searchFilters={[
|
||||
endpoint => endpoint.getSearchFields(),
|
||||
]}
|
||||
renderHeaderTitle="Endpoints"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
{ className: "warning", showWithColumn: columnId.name },
|
||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
{ title: "Endpoints", className: "endpoints", id: columnId.endpoints },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
]}
|
||||
renderTableContents={endpoint => [
|
||||
endpoint.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={endpoint} />,
|
||||
endpoint.getNs(),
|
||||
endpoint.toString(),
|
||||
<KubeObjectAge key="age" object={endpoint} />,
|
||||
]}
|
||||
tableProps={{
|
||||
customRowHeights: (item, lineHeight, paddings) => {
|
||||
const lines = item.getEndpointSubsets().length || 1;
|
||||
|
||||
return lines * lineHeight + paddings;
|
||||
},
|
||||
}}
|
||||
/>
|
||||
return lines * lineHeight + paddings;
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</SiblingsInTabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { Ingresses } from "./ingresses";
|
||||
import ingressesRouteInjectable from "../../../common/front-end-routing/routes/cluster/network/ingresses/ingresses-route.injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const ingressesRouteComponentInjectable = getInjectable({
|
||||
id: "ingresses-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(ingressesRouteInjectable),
|
||||
Component: Ingresses,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default ingressesRouteComponentInjectable;
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
|
||||
import ingressesRouteInjectable from "../../../common/front-end-routing/routes/cluster/network/ingresses/ingresses-route.injectable";
|
||||
import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable";
|
||||
import { networkSidebarItemId } from "../+network/network-sidebar-items.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToIngressesInjectable from "../../../common/front-end-routing/routes/cluster/network/ingresses/navigate-to-ingresses.injectable";
|
||||
|
||||
const ingressesSidebarItemsInjectable = getInjectable({
|
||||
id: "ingresses-sidebar-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(ingressesRouteInjectable);
|
||||
const navigateToIngresses = di.inject(navigateToIngressesInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return computed(() => [
|
||||
{
|
||||
id: "ingresses",
|
||||
parentId: networkSidebarItemId,
|
||||
title: "Ingresses",
|
||||
onClick: navigateToIngresses,
|
||||
isActive: routeIsActive,
|
||||
isVisible: route.isEnabled,
|
||||
orderNumber: 30,
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default ingressesSidebarItemsInjectable;
|
||||
@@ -7,11 +7,10 @@ import "./ingresses.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router-dom";
|
||||
import { ingressStore } from "./ingress.store";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { IngressRouteParams } from "../../../common/routes";
|
||||
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
|
||||
import { KubeObjectAge } from "../kube-object/age";
|
||||
import { computeRouteDeclarations } from "../../../common/k8s-api/endpoints";
|
||||
|
||||
@@ -23,59 +22,65 @@ enum columnId {
|
||||
age = "age",
|
||||
}
|
||||
|
||||
export interface IngressesProps extends RouteComponentProps<IngressRouteParams> {
|
||||
}
|
||||
|
||||
@observer
|
||||
export class Ingresses extends React.Component<IngressesProps> {
|
||||
export class Ingresses extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="network_ingresses"
|
||||
className="Ingresses" store={ingressStore}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: ingress => ingress.getName(),
|
||||
[columnId.namespace]: ingress => ingress.getNs(),
|
||||
[columnId.age]: ingress => -ingress.getCreationTimestamp(),
|
||||
}}
|
||||
searchFilters={[
|
||||
ingress => ingress.getSearchFields(),
|
||||
ingress => ingress.getPorts(),
|
||||
]}
|
||||
renderHeaderTitle="Ingresses"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
{ className: "warning", showWithColumn: columnId.name },
|
||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
{ title: "LoadBalancers", className: "loadbalancers", id: columnId.loadBalancers },
|
||||
{ title: "Rules", className: "rules", id: columnId.rules },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
]}
|
||||
renderTableContents={ingress => [
|
||||
ingress.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={ingress} />,
|
||||
ingress.getNs(),
|
||||
ingress.getLoadBalancers().map(lb => <p key={lb}>{lb}</p>),
|
||||
computeRouteDeclarations(ingress).map(decl => (
|
||||
decl.displayAsLink
|
||||
? (
|
||||
<span key={decl.url}>
|
||||
<a
|
||||
href={decl.url}
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
{decl.url}
|
||||
</a> ⇢ {decl.service}
|
||||
</span>
|
||||
)
|
||||
: <span key={decl.url}>{decl.url} ⇢ {decl.service}</span>
|
||||
)),
|
||||
<KubeObjectAge key="age" object={ingress} />,
|
||||
]}
|
||||
/>
|
||||
<SiblingsInTabLayout>
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="network_ingresses"
|
||||
className="Ingresses" store={ingressStore}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: ingress => ingress.getName(),
|
||||
[columnId.namespace]: ingress => ingress.getNs(),
|
||||
[columnId.age]: ingress => -ingress.getCreationTimestamp(),
|
||||
}}
|
||||
searchFilters={[
|
||||
ingress => ingress.getSearchFields(),
|
||||
ingress => ingress.getPorts(),
|
||||
]}
|
||||
renderHeaderTitle="Ingresses"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
{ className: "warning", showWithColumn: columnId.name },
|
||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
{ title: "LoadBalancers", className: "loadbalancers", id: columnId.loadBalancers },
|
||||
{ title: "Rules", className: "rules", id: columnId.rules },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
]}
|
||||
renderTableContents={ingress => [
|
||||
ingress.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={ingress} />,
|
||||
ingress.getNs(),
|
||||
ingress.getLoadBalancers().map(lb => <p key={lb}>{lb}</p>),
|
||||
computeRouteDeclarations(ingress).map(decl => (
|
||||
decl.displayAsLink
|
||||
? (
|
||||
<span key={decl.url}>
|
||||
<a
|
||||
href={decl.url}
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
{decl.url}
|
||||
</a> ⇢ {decl.service}
|
||||
</span>
|
||||
)
|
||||
: <span key={decl.url}>{decl.url} ⇢ {decl.service}</span>
|
||||
)),
|
||||
<KubeObjectAge key="age" object={ingress} />,
|
||||
]}
|
||||
tableProps={{
|
||||
customRowHeights: (item, lineHeight, paddings) => {
|
||||
const lines = item.getRoutes().length || 1;
|
||||
|
||||
return lines * lineHeight + paddings;
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</SiblingsInTabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { NetworkPolicies } from "./network-policies";
|
||||
import networkPoliciesRouteInjectable from "../../../common/front-end-routing/routes/cluster/network/network-policies/network-policies-route.injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const networkPoliciesRouteComponentInjectable = getInjectable({
|
||||
id: "network-policies-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(networkPoliciesRouteInjectable),
|
||||
Component: NetworkPolicies,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default networkPoliciesRouteComponentInjectable;
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
|
||||
import networkPoliciesRouteInjectable from "../../../common/front-end-routing/routes/cluster/network/network-policies/network-policies-route.injectable";
|
||||
import { networkSidebarItemId } from "../+network/network-sidebar-items.injectable";
|
||||
import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToNetworkPoliciesInjectable from "../../../common/front-end-routing/routes/cluster/network/network-policies/navigate-to-network-policies.injectable";
|
||||
|
||||
const networkPoliciesSidebarItemsInjectable = getInjectable({
|
||||
id: "network-policies-sidebar-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(networkPoliciesRouteInjectable);
|
||||
const navigateToNetworkPolicies = di.inject(navigateToNetworkPoliciesInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return computed(() => [
|
||||
{
|
||||
id: "network-policies",
|
||||
parentId: networkSidebarItemId,
|
||||
title: "Network Policies",
|
||||
onClick: navigateToNetworkPolicies,
|
||||
isActive: routeIsActive,
|
||||
isVisible: route.isEnabled,
|
||||
orderNumber: 40,
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default networkPoliciesSidebarItemsInjectable;
|
||||
@@ -7,11 +7,10 @@ import "./network-policies.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router-dom";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { networkPolicyStore } from "./network-policy.store";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { NetworkPoliciesRouteParams } from "../../../common/routes";
|
||||
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
|
||||
import { KubeObjectAge } from "../kube-object/age";
|
||||
|
||||
enum columnId {
|
||||
@@ -21,42 +20,41 @@ enum columnId {
|
||||
age = "age",
|
||||
}
|
||||
|
||||
export interface NetworkPoliciesProps extends RouteComponentProps<NetworkPoliciesRouteParams> {
|
||||
}
|
||||
|
||||
@observer
|
||||
export class NetworkPolicies extends React.Component<NetworkPoliciesProps> {
|
||||
export class NetworkPolicies extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="network_policies"
|
||||
className="NetworkPolicies"
|
||||
store={networkPolicyStore}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: networkPolicy => networkPolicy.getName(),
|
||||
[columnId.namespace]: networkPolicy => networkPolicy.getNs(),
|
||||
[columnId.age]: networkPolicy => -networkPolicy.getCreationTimestamp(),
|
||||
}}
|
||||
searchFilters={[
|
||||
networkPolicy => networkPolicy.getSearchFields(),
|
||||
]}
|
||||
renderHeaderTitle="Network Policies"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
{ className: "warning", showWithColumn: columnId.name },
|
||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
{ title: "Policy Types", className: "type", id: columnId.types },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
]}
|
||||
renderTableContents={networkPolicy => [
|
||||
networkPolicy.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={networkPolicy} />,
|
||||
networkPolicy.getNs(),
|
||||
networkPolicy.getTypes().join(", "),
|
||||
<KubeObjectAge key="age" object={networkPolicy} />,
|
||||
]}
|
||||
/>
|
||||
<SiblingsInTabLayout>
|
||||
<KubeObjectListLayout
|
||||
isConfigurable
|
||||
tableId="network_policies"
|
||||
className="NetworkPolicies"
|
||||
store={networkPolicyStore}
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: networkPolicy => networkPolicy.getName(),
|
||||
[columnId.namespace]: networkPolicy => networkPolicy.getNs(),
|
||||
[columnId.age]: networkPolicy => -networkPolicy.getCreationTimestamp(),
|
||||
}}
|
||||
searchFilters={[
|
||||
networkPolicy => networkPolicy.getSearchFields(),
|
||||
]}
|
||||
renderHeaderTitle="Network Policies"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||
{ className: "warning", showWithColumn: columnId.name },
|
||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||
{ title: "Policy Types", className: "type", id: columnId.types },
|
||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||
]}
|
||||
renderTableContents={networkPolicy => [
|
||||
networkPolicy.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={networkPolicy} />,
|
||||
networkPolicy.getNs(),
|
||||
networkPolicy.getTypes().join(", "),
|
||||
<KubeObjectAge key="age" object={networkPolicy} />,
|
||||
]}
|
||||
/>
|
||||
</SiblingsInTabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { PortForwards } from "./port-forwards";
|
||||
import portForwardsRouteInjectable from "../../../common/front-end-routing/routes/cluster/network/port-forwards/port-forwards-route.injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
|
||||
const portForwardsRouteComponentInjectable = getInjectable({
|
||||
id: "port-forwards-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(portForwardsRouteInjectable),
|
||||
Component: PortForwards,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default portForwardsRouteComponentInjectable;
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import routePathParametersInjectable from "../../routes/route-path-parameters.injectable";
|
||||
import portForwardsRouteInjectable from "../../../common/front-end-routing/routes/cluster/network/port-forwards/port-forwards-route.injectable";
|
||||
|
||||
const portForwardsRouteParametersInjectable = getInjectable({
|
||||
id: "port-forwards-route-parameters",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(portForwardsRouteInjectable);
|
||||
const pathParameters = di.inject(routePathParametersInjectable, route);
|
||||
|
||||
return {
|
||||
forwardport: computed(() => pathParameters.get().forwardport),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default portForwardsRouteParametersInjectable;
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
|
||||
import portForwardsRouteInjectable from "../../../common/front-end-routing/routes/cluster/network/port-forwards/port-forwards-route.injectable";
|
||||
import { networkSidebarItemId } from "../+network/network-sidebar-items.injectable";
|
||||
import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable";
|
||||
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import navigateToPortForwardsInjectable from "../../../common/front-end-routing/routes/cluster/network/port-forwards/navigate-to-port-forwards.injectable";
|
||||
|
||||
const portForwardsSidebarItemsInjectable = getInjectable({
|
||||
id: "port-forwards-sidebar-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(portForwardsRouteInjectable);
|
||||
const navigateToPortForwards = di.inject(navigateToPortForwardsInjectable);
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return computed(() => [
|
||||
{
|
||||
id: "port-forwards",
|
||||
parentId: networkSidebarItemId,
|
||||
|
||||
title: "Port Forwarding",
|
||||
onClick: navigateToPortForwards,
|
||||
isActive: routeIsActive,
|
||||
isVisible: route.isEnabled,
|
||||
orderNumber: 50,
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
injectionToken: sidebarItemsInjectionToken,
|
||||
});
|
||||
|
||||
export default portForwardsSidebarItemsInjectable;
|
||||
@@ -7,15 +7,16 @@ import "./port-forwards.scss";
|
||||
|
||||
import React from "react";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router-dom";
|
||||
import { ItemListLayout } from "../item-object-list/list-layout";
|
||||
import type { PortForwardItem, PortForwardStore } from "../../port-forward";
|
||||
import { PortForwardMenu } from "./port-forward-menu";
|
||||
import { PortForwardsRouteParams, portForwardsURL } from "../../../common/routes";
|
||||
import { PortForwardDetails } from "./port-forward-details";
|
||||
import { navigation } from "../../navigation";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import portForwardStoreInjectable from "../../port-forward/port-forward-store/port-forward-store.injectable";
|
||||
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
|
||||
import { computed, IComputedValue, makeObservable } from "mobx";
|
||||
import portForwardsRouteParametersInjectable from "./port-forwards-route-parameters.injectable";
|
||||
import navigateToPortForwardsInjectable, { NavigateToPortForwards } from "../../../common/front-end-routing/routes/cluster/network/port-forwards/navigate-to-port-forwards.injectable";
|
||||
|
||||
enum columnId {
|
||||
name = "name",
|
||||
@@ -27,26 +28,30 @@ enum columnId {
|
||||
status = "status",
|
||||
}
|
||||
|
||||
export interface PortForwardsProps extends RouteComponentProps<PortForwardsRouteParams> {
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
portForwardStore: PortForwardStore;
|
||||
forwardport: IComputedValue<string>;
|
||||
navigateToPortForwards: NavigateToPortForwards;
|
||||
}
|
||||
|
||||
@observer
|
||||
class NonInjectedPortForwards extends React.Component<PortForwardsProps & Dependencies> {
|
||||
class NonInjectedPortForwards extends React.Component<Dependencies> {
|
||||
constructor(props: Dependencies) {
|
||||
super(props);
|
||||
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
||||
disposeOnUnmount(this, [
|
||||
this.props.portForwardStore.watch(),
|
||||
]);
|
||||
}
|
||||
|
||||
@computed
|
||||
get selectedPortForward() {
|
||||
const { match: { params: { forwardport }}} = this.props;
|
||||
|
||||
return this.props.portForwardStore.getById(forwardport);
|
||||
return this.props.portForwardStore.getById(this.props.forwardport.get());
|
||||
}
|
||||
|
||||
onDetails = (item: PortForwardItem) => {
|
||||
@@ -58,15 +63,13 @@ class NonInjectedPortForwards extends React.Component<PortForwardsProps & Depend
|
||||
};
|
||||
|
||||
showDetails = (item: PortForwardItem) => {
|
||||
navigation.push(portForwardsURL({
|
||||
params: {
|
||||
forwardport: item.getId(),
|
||||
},
|
||||
}));
|
||||
this.props.navigateToPortForwards({
|
||||
forwardport: item.getId(),
|
||||
});
|
||||
};
|
||||
|
||||
hideDetails = () => {
|
||||
navigation.push(portForwardsURL());
|
||||
this.props.navigateToPortForwards();
|
||||
};
|
||||
|
||||
renderRemoveDialogMessage(selectedItems: PortForwardItem[]) {
|
||||
@@ -82,7 +85,7 @@ class NonInjectedPortForwards extends React.Component<PortForwardsProps & Depend
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<SiblingsInTabLayout>
|
||||
<ItemListLayout
|
||||
isConfigurable
|
||||
tableId="port_forwards"
|
||||
@@ -138,19 +141,24 @@ class NonInjectedPortForwards extends React.Component<PortForwardsProps & Depend
|
||||
hideDetails={this.hideDetails}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</SiblingsInTabLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const PortForwards = withInjectables<Dependencies, PortForwardsProps>(
|
||||
export const PortForwards = withInjectables<Dependencies>(
|
||||
NonInjectedPortForwards,
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
portForwardStore: di.inject(portForwardStoreInjectable),
|
||||
...props,
|
||||
}),
|
||||
getProps: (di) => {
|
||||
const routeParameters = di.inject(portForwardsRouteParametersInjectable);
|
||||
|
||||
return {
|
||||
portForwardStore: di.inject(portForwardStoreInjectable),
|
||||
forwardport: routeParameters.forwardport,
|
||||
navigateToPortForwards: di.inject(navigateToPortForwardsInjectable),
|
||||
};
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
Loaded 100 of 583 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user