mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
show catalog categories
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
parent
a946d7989a
commit
f42cac6b3b
@ -1 +1,2 @@
|
|||||||
export * from "./kubernetes-cluster";
|
export * from "./kubernetes-cluster";
|
||||||
|
export * from "./web-link";
|
||||||
|
|||||||
@ -96,6 +96,10 @@ export class KubernetesClusterCategory extends EventEmitter implements CatalogCa
|
|||||||
kind: "KubernetesCluster"
|
kind: "KubernetesCluster"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return `${this.spec.group}/${this.spec.names.kind}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
catalogCategoryRegistry.add(new KubernetesClusterCategory());
|
catalogCategoryRegistry.add(new KubernetesClusterCategory());
|
||||||
|
|||||||
65
src/common/catalog-entities/web-link.ts
Normal file
65
src/common/catalog-entities/web-link.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { observable } from "mobx";
|
||||||
|
import { CatalogCategory, CatalogEntity, CatalogEntityActionContext, CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog-entity";
|
||||||
|
import { catalogCategoryRegistry } from "../catalog-category-registry";
|
||||||
|
|
||||||
|
export interface WebLinkStatus extends CatalogEntityStatus {
|
||||||
|
phase: "valid" | "invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WebLinkSpec = {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class WebLink implements CatalogEntity {
|
||||||
|
public readonly apiVersion = "entity.k8slens.dev/v1alpha1";
|
||||||
|
public readonly kind = "KubernetesCluster";
|
||||||
|
@observable public metadata: CatalogEntityMetadata;
|
||||||
|
@observable public status: WebLinkStatus;
|
||||||
|
@observable public spec: WebLinkSpec;
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.metadata.uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return this.metadata.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
async onRun(context: CatalogEntityActionContext) {
|
||||||
|
window.open(this.spec.url, "_blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
async onDetailsOpen() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
async onContextMenuOpen(context: CatalogEntityContextMenuContext) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WebLinkCategory implements CatalogCategory {
|
||||||
|
public readonly apiVersion = "catalog.k8slens.dev/v1alpha1";
|
||||||
|
public readonly kind = "CatalogCategory";
|
||||||
|
public metadata = {
|
||||||
|
name: "Web Link"
|
||||||
|
};
|
||||||
|
public spec = {
|
||||||
|
group: "entity.k8slens.dev",
|
||||||
|
versions: [
|
||||||
|
{
|
||||||
|
name: "v1alpha1",
|
||||||
|
entityClass: WebLink
|
||||||
|
}
|
||||||
|
],
|
||||||
|
names: {
|
||||||
|
kind: "WebLink"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return `${this.spec.group}/${this.spec.names.kind}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
catalogCategoryRegistry.add(new WebLinkCategory());
|
||||||
@ -16,6 +16,7 @@ export interface CatalogCategory {
|
|||||||
kind: string;
|
kind: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
getId: () => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CatalogEntityMetadata = {
|
export type CatalogEntityMetadata = {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { action, observable } from "mobx";
|
import { action, observable } from "mobx";
|
||||||
import { broadcastMessage, subscribeToBroadcast } from "../../common/ipc";
|
import { broadcastMessage, subscribeToBroadcast } from "../../common/ipc";
|
||||||
import { CatalogEntity, CatalogEntityData } from "../../common/catalog-entity";
|
import { CatalogCategory, CatalogEntity, CatalogEntityData } from "../../common/catalog-entity";
|
||||||
import { catalogCategoryRegistry, CatalogCategoryRegistry } from "../../common/catalog-category-registry";
|
import { catalogCategoryRegistry, CatalogCategoryRegistry } from "../../common/catalog-category-registry";
|
||||||
import "../../common/catalog-entities";
|
import "../../common/catalog-entities";
|
||||||
|
|
||||||
@ -49,6 +49,13 @@ export class CatalogEntityRegistry {
|
|||||||
|
|
||||||
return items as T[];
|
return items as T[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getItemsForCategory<T extends CatalogEntity>(category: CatalogCategory): T[] {
|
||||||
|
const supportedVersions = category.spec.versions.map((v) => `${category.spec.group}/${v.name}`);
|
||||||
|
const items = this._items.filter((item) => supportedVersions.includes(item.apiVersion) && item.kind === category.spec.names.kind);
|
||||||
|
|
||||||
|
return items as T[];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
|
export const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import { action, computed, reaction } from "mobx";
|
import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
|
||||||
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
||||||
import { CatalogEntity, CatalogEntityActionContext } from "../../api/catalog-entity";
|
import { CatalogEntity, CatalogEntityActionContext } from "../../api/catalog-entity";
|
||||||
import { ItemObject, ItemStore } from "../../item.store";
|
import { ItemObject, ItemStore } from "../../item.store";
|
||||||
import { autobind } from "../../utils";
|
import { autobind } from "../../utils";
|
||||||
|
import { CatalogCategory } from "../../../common/catalog-entity";
|
||||||
|
|
||||||
export class CatalogEntityItem implements ItemObject {
|
export class CatalogEntityItem implements ItemObject {
|
||||||
constructor(public entity: CatalogEntity) {}
|
constructor(public entity: CatalogEntity) {}
|
||||||
@ -55,13 +56,23 @@ export class CatalogEntityItem implements ItemObject {
|
|||||||
|
|
||||||
@autobind()
|
@autobind()
|
||||||
export class CatalogEntityStore extends ItemStore<CatalogEntityItem> {
|
export class CatalogEntityStore extends ItemStore<CatalogEntityItem> {
|
||||||
|
@observable activeCategory: CatalogCategory;
|
||||||
|
|
||||||
@computed get entities() {
|
@computed get entities() {
|
||||||
return catalogEntityRegistry.items.map(entity => new CatalogEntityItem(entity));
|
if (!this.activeCategory) return [];
|
||||||
|
|
||||||
|
console.log("computing entities", this.activeCategory);
|
||||||
|
|
||||||
|
return catalogEntityRegistry.getItemsForCategory(this.activeCategory).map(entity => new CatalogEntityItem(entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
watch() {
|
watch() {
|
||||||
return reaction(() => this.entities, () => this.loadAll());
|
const disposers: IReactionDisposer[] = [
|
||||||
|
reaction(() => this.entities, () => this.loadAll()),
|
||||||
|
reaction(() => this.activeCategory, () => this.loadAll(), { delay: 100})
|
||||||
|
];
|
||||||
|
|
||||||
|
return () => disposers.forEach((dispose) => dispose());
|
||||||
}
|
}
|
||||||
|
|
||||||
loadAll() {
|
loadAll() {
|
||||||
|
|||||||
@ -1,13 +1,16 @@
|
|||||||
.CatalogPage {
|
.CatalogPage {
|
||||||
--width: 100%;
|
--width: 100%;
|
||||||
--height: 100%;
|
--height: 100%;
|
||||||
|
--nav-column-width: 230px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
.content-wrapper {
|
.sidebarRegion {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentRegion {
|
||||||
.content {
|
.content {
|
||||||
margin: unset;
|
padding: 20px 20px;
|
||||||
max-width: unset;
|
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import "./catalog.scss";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { disposeOnUnmount, observer } from "mobx-react";
|
import { disposeOnUnmount, observer } from "mobx-react";
|
||||||
import { ItemListLayout } from "../item-object-list";
|
import { ItemListLayout } from "../item-object-list";
|
||||||
import { observable } from "mobx";
|
import { observable, reaction } from "mobx";
|
||||||
import { CatalogEntityItem, CatalogEntityStore } from "./catalog-entity.store";
|
import { CatalogEntityItem, CatalogEntityStore } from "./catalog-entity.store";
|
||||||
import { navigate } from "../../navigation";
|
import { navigate } from "../../navigation";
|
||||||
import { kebabCase } from "lodash";
|
import { kebabCase } from "lodash";
|
||||||
@ -16,17 +16,19 @@ import { addClusterURL } from "../+add-cluster";
|
|||||||
import { autobind } from "../../utils";
|
import { autobind } from "../../utils";
|
||||||
import { Notifications } from "../notifications";
|
import { Notifications } from "../notifications";
|
||||||
import { ConfirmDialog } from "../confirm-dialog";
|
import { ConfirmDialog } from "../confirm-dialog";
|
||||||
|
import { Tab, Tabs } from "../tabs";
|
||||||
|
import { catalogCategoryRegistry } from "../../../common/catalog-category-registry";
|
||||||
|
|
||||||
enum sortBy {
|
enum sortBy {
|
||||||
name = "name",
|
name = "name",
|
||||||
source = "source",
|
source = "source",
|
||||||
status = "status"
|
status = "status"
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class Catalog extends React.Component {
|
export class Catalog extends React.Component {
|
||||||
@observable private catalogEntityStore?: CatalogEntityStore;
|
@observable private catalogEntityStore?: CatalogEntityStore;
|
||||||
@observable.deep private contextMenu: CatalogEntityContextMenuContext;
|
@observable.deep private contextMenu: CatalogEntityContextMenuContext;
|
||||||
|
@observable activeTab: string;
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
this.contextMenu = {
|
this.contextMenu = {
|
||||||
@ -35,7 +37,13 @@ export class Catalog extends React.Component {
|
|||||||
};
|
};
|
||||||
this.catalogEntityStore = new CatalogEntityStore();
|
this.catalogEntityStore = new CatalogEntityStore();
|
||||||
disposeOnUnmount(this, [
|
disposeOnUnmount(this, [
|
||||||
this.catalogEntityStore.watch()
|
this.catalogEntityStore.watch(),
|
||||||
|
reaction(() => catalogCategoryRegistry.items, (items) => {
|
||||||
|
if (!this.activeTab && items.length > 0) {
|
||||||
|
this.activeTab = items[0].getId();
|
||||||
|
this.catalogEntityStore.activeCategory = items[0];
|
||||||
|
}
|
||||||
|
}, { fireImmediately: true })
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -89,6 +97,31 @@ export class Catalog extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get categories() {
|
||||||
|
return catalogCategoryRegistry.items;
|
||||||
|
}
|
||||||
|
|
||||||
|
onTabChange = (tabId: string) => {
|
||||||
|
this.activeTab = tabId;
|
||||||
|
|
||||||
|
const activeCategory = this.categories.find((category) => category.getId() === tabId);
|
||||||
|
|
||||||
|
if (activeCategory) {
|
||||||
|
this.catalogEntityStore.activeCategory = activeCategory;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
renderNavigation() {
|
||||||
|
return (
|
||||||
|
<Tabs className="flex column" scrollable={false} onChange={this.onTabChange} value={this.activeTab}>
|
||||||
|
<div className="header">Catalog</div>
|
||||||
|
{ this.categories.map((category, index) => {
|
||||||
|
return <Tab value={category.getId()} key={index} label={category.metadata.name} data-testid={`${category.getId()}-tab`} />;
|
||||||
|
})}
|
||||||
|
</Tabs>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@autobind()
|
@autobind()
|
||||||
renderItemMenu(item: CatalogEntityItem) {
|
renderItemMenu(item: CatalogEntityItem) {
|
||||||
const onOpen = async () => {
|
const onOpen = async () => {
|
||||||
@ -120,14 +153,18 @@ export class Catalog extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout className="CatalogPage" provideBackButtonNavigation={false}>
|
<PageLayout
|
||||||
|
className="CatalogPage"
|
||||||
|
navigation={this.renderNavigation()}
|
||||||
|
provideBackButtonNavigation={false}
|
||||||
|
contentGaps={false}>
|
||||||
<ItemListLayout
|
<ItemListLayout
|
||||||
renderHeaderTitle="Catalog"
|
|
||||||
isClusterScoped
|
isClusterScoped
|
||||||
isSearchable={true}
|
isSearchable={true}
|
||||||
isSelectable={false}
|
isSelectable={false}
|
||||||
className="CatalogItemList"
|
className="CatalogItemList"
|
||||||
store={this.catalogEntityStore}
|
store={this.catalogEntityStore}
|
||||||
|
tableId="catalog-items"
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[sortBy.name]: (item: CatalogEntityItem) => item.name,
|
[sortBy.name]: (item: CatalogEntityItem) => item.name,
|
||||||
[sortBy.source]: (item: CatalogEntityItem) => item.source,
|
[sortBy.source]: (item: CatalogEntityItem) => item.source,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user