diff --git a/src/main/cluster.ts b/src/main/cluster.ts
index 956164e10c..223deb8227 100644
--- a/src/main/cluster.ts
+++ b/src/main/cluster.ts
@@ -245,7 +245,7 @@ export class Cluster implements ClusterModel, ClusterState {
* Kubernetes version
*/
get version(): string {
- return String(this.metadata?.version) || "";
+ return String(this.metadata?.version || "");
}
constructor(model: ClusterModel) {
diff --git a/src/renderer/components/+landing-page/landing-page.scss b/src/renderer/components/+landing-page/landing-page.scss
index 4874b37c72..2e482d2b49 100644
--- a/src/renderer/components/+landing-page/landing-page.scss
+++ b/src/renderer/components/+landing-page/landing-page.scss
@@ -3,58 +3,4 @@
height: 100%;
text-align: center;
z-index: 0;
-
- &::after {
- content: "";
- background: url(../../components/icon/crane.svg) no-repeat;
- background-position: 0 35%;
- background-size: 85%;
- background-clip: content-box;
- opacity: .75;
- top: 0;
- left: 0;
- bottom: 0;
- right: 0;
- position: absolute;
- z-index: -1;
-
- .theme-light & {
- opacity: 0.2;
- }
- }
-
- .startup-hint {
- $bgc: $mainBackground;
- $arrowSize: 10px;
-
- position: absolute;
- left: 0;
- top: 25px;
- margin: $padding;
- padding: $padding * 2;
- width: 320px;
- background: $bgc;
- color: $textColorAccent;
- filter: drop-shadow(0 0px 2px #ffffff33);
-
- &:before {
- content: "";
- position: absolute;
- width: 0;
- height: 0;
- border-top: $arrowSize solid transparent;
- border-bottom: $arrowSize solid transparent;
- border-right: $arrowSize solid $bgc;
- right: 100%;
- }
-
- .theme-light & {
- filter: drop-shadow(0 0px 2px #777);
- background: white;
-
- &:before {
- border-right-color: white;
- }
- }
- }
}
\ No newline at end of file
diff --git a/src/renderer/components/+landing-page/landing-page.tsx b/src/renderer/components/+landing-page/landing-page.tsx
index ea0b24bb87..853d1e3486 100644
--- a/src/renderer/components/+landing-page/landing-page.tsx
+++ b/src/renderer/components/+landing-page/landing-page.tsx
@@ -1,40 +1,23 @@
import "./landing-page.scss";
import React from "react";
-import { observable } from "mobx";
import { observer } from "mobx-react";
-import { clusterStore } from "../../../common/cluster-store";
-import { workspaceStore } from "../../../common/workspace-store";
-
+import { Workspace, workspaceStore } from "../../../common/workspace-store";
+import { WorkspaceOverview } from "./workspace-overview";
+import { PageLayout } from "../layout/page-layout";
@observer
export class LandingPage extends React.Component {
- @observable showHint = true;
+ get workspace(): Workspace {
+ return workspaceStore.currentWorkspace;
+ }
+
render() {
- const clusters = clusterStore.getByWorkspaceId(workspaceStore.currentWorkspaceId);
- const noClustersInScope = !clusters.length;
- const showStartupHint = this.showHint && noClustersInScope;
+ const header =
Workspace: {this.workspace.name}
;
return (
-
- {showStartupHint && (
-
this.showHint = false}>
-
This is the quick launch menu.
-
- Associate clusters and choose the ones you want to access via quick launch menu by clicking the + button.
-
-
- )}
- {noClustersInScope && (
-
-
- Welcome!
-
-
- Get started by associating one or more clusters to Lens.
-
-
- )}
-
+
+
+
);
}
}
diff --git a/src/renderer/components/+landing-page/workspace-cluster-menu.tsx b/src/renderer/components/+landing-page/workspace-cluster-menu.tsx
new file mode 100644
index 0000000000..83d542f258
--- /dev/null
+++ b/src/renderer/components/+landing-page/workspace-cluster-menu.tsx
@@ -0,0 +1,76 @@
+import React from "react";
+import { ClusterItem, WorkspaceClusterStore } from "./workspace-cluster.store";
+import { autobind, cssNames } from "../../utils";
+import { MenuActions, MenuActionsProps } from "../menu/menu-actions";
+import { MenuItem } from "../menu";
+import { Icon } from "../icon";
+import { Workspace } from "../../../common/workspace-store";
+import { clusterSettingsURL } from "../+cluster-settings";
+import { navigate } from "../../navigation";
+
+interface Props extends MenuActionsProps {
+ clusterItem: ClusterItem;
+ workspace: Workspace;
+ workspaceClusterStore: WorkspaceClusterStore;
+}
+
+export class WorkspaceClusterMenu extends React.Component {
+
+ @autobind()
+ remove() {
+ const { clusterItem, workspaceClusterStore } = this.props;
+
+ return workspaceClusterStore.remove(clusterItem);
+ }
+
+ @autobind()
+ settings() {
+ const { clusterItem } = this.props;
+
+ navigate(clusterSettingsURL({
+ params: {
+ clusterId: clusterItem.getId()
+ }
+ }));
+ }
+
+ @autobind()
+ renderRemoveMessage() {
+ const { clusterItem, workspace } = this.props;
+
+ return (
+ Remove cluster {clusterItem.getName()} from workspace {workspace.name}?
+ );
+ }
+
+
+ renderContent() {
+ const { toolbar } = this.props;
+
+ return (
+ <>
+ {
+
+ }
+ >
+ );
+ }
+
+ render() {
+ const { clusterItem, className, ...menuProps } = this.props;
+
+ return (
+
+ {this.renderContent()}
+
+ );
+ }
+}
diff --git a/src/renderer/components/+landing-page/workspace-cluster.store.ts b/src/renderer/components/+landing-page/workspace-cluster.store.ts
new file mode 100644
index 0000000000..810683e128
--- /dev/null
+++ b/src/renderer/components/+landing-page/workspace-cluster.store.ts
@@ -0,0 +1,64 @@
+import { WorkspaceId } from "../../../common/workspace-store";
+import { Cluster } from "../../../main/cluster";
+import { clusterStore } from "../../../common/cluster-store";
+import { ItemObject, ItemStore } from "../../item.store";
+import { autobind } from "../../utils";
+
+export class ClusterItem implements ItemObject {
+ cluster: Cluster;
+
+ getName() {
+ return this.cluster.name;
+ }
+
+ getId() {
+ return this.cluster.id;
+ }
+}
+
+/** an ItemStore of the clusters belonging to a given workspace */
+@autobind()
+export class WorkspaceClusterStore extends ItemStore {
+
+ workspaceId: WorkspaceId;
+
+ constructor(workspaceId: WorkspaceId) {
+ super();
+ this.workspaceId = workspaceId;
+ }
+
+ loadAll() {
+ return this.loadItems(() => clusterStore.getByWorkspaceId(this.workspaceId).map(cluster => {
+ const clusterItem = new ClusterItem();
+
+ clusterItem.cluster = cluster;
+
+ return clusterItem;
+ }));
+ }
+
+ async remove(clusterItem: ClusterItem) {
+ const { cluster } = clusterItem;
+
+ if (cluster.isManaged) {
+ return;
+ }
+
+ const clusterId = cluster.id;
+
+ return super.removeItem(clusterItem, async () => {
+ if (clusterStore.activeClusterId === clusterId) {
+ clusterStore.setActive(null);
+ }
+ clusterStore.removeById(clusterId);
+ });
+ }
+
+ async removeSelectedItems() {
+ if (!this.selectedItems.length) {
+ return;
+ }
+
+ return Promise.all(this.selectedItems.map(this.remove));
+ }
+}
diff --git a/src/renderer/components/+landing-page/workspace-overview.scss b/src/renderer/components/+landing-page/workspace-overview.scss
new file mode 100644
index 0000000000..1d6e16a383
--- /dev/null
+++ b/src/renderer/components/+landing-page/workspace-overview.scss
@@ -0,0 +1,12 @@
+.WorkspaceOverview {
+ .TableCell {
+ display: flex;
+ align-items: left;
+
+ &.cluster-icon {
+ align-items: center;
+ flex-grow: 0.2;
+ padding: 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/renderer/components/+landing-page/workspace-overview.tsx b/src/renderer/components/+landing-page/workspace-overview.tsx
new file mode 100644
index 0000000000..d6bca05d49
--- /dev/null
+++ b/src/renderer/components/+landing-page/workspace-overview.tsx
@@ -0,0 +1,69 @@
+import "./workspace-overview.scss";
+
+import React, { Component } from "react";
+import { Workspace } from "../../../common/workspace-store";
+import { observer } from "mobx-react";
+import { ItemListLayout } from "../item-object-list/item-list-layout";
+import { ClusterItem, WorkspaceClusterStore } from "./workspace-cluster.store";
+import { navigate } from "../../navigation";
+import { clusterViewURL } from "../cluster-manager/cluster-view.route";
+import { WorkspaceClusterMenu } from "./workspace-cluster-menu";
+
+interface Props {
+ workspace: Workspace;
+}
+
+enum sortBy {
+ name = "name",
+ contextName = "contextName",
+ version = "version",
+}
+
+@observer
+export class WorkspaceOverview extends Component {
+
+ showCluster = (clusterItem: ClusterItem) => {
+ const clusterId = clusterItem.getId();
+
+ navigate(clusterViewURL({ params: { clusterId } }));
+ };
+
+ render() {
+ const { workspace } = this.props;
+ const workspaceClusterStore = new WorkspaceClusterStore(workspace.id);
+
+ workspaceClusterStore.loadAll();
+
+ return (
+ Clusters}
+ isClusterScoped
+ isSearchable={false}
+ isSelectable={false}
+ className="WorkspaceOverview"
+ store={workspaceClusterStore}
+ sortingCallbacks={{
+ [sortBy.name]: (item: ClusterItem) => item.getName(),
+ [sortBy.contextName]: (item: ClusterItem) => item.cluster.contextName,
+ [sortBy.version]: (item: ClusterItem) => item.cluster.version,
+ }}
+ renderTableHeader={[
+ { title: "Name", className: "name", sortBy: sortBy.name },
+ { title: "Context", className: "context", sortBy: sortBy.contextName },
+ { title: "Version", className: "version", sortBy: sortBy.version },
+ { title: "Status", className: "status" },
+ ]}
+ renderTableContents={(item: ClusterItem) => [
+ item.getName(),
+ item.cluster.contextName,
+ item.cluster.version,
+ item.cluster.online ? "online" : "offline"
+ ]}
+ onDetails={this.showCluster}
+ renderItemMenu={(clusterItem: ClusterItem) => {
+ return ;
+ }}
+ />
+ );
+ }
+}
diff --git a/src/renderer/components/item-object-list/item-list-layout.tsx b/src/renderer/components/item-object-list/item-list-layout.tsx
index b13d496064..afdf57c73e 100644
--- a/src/renderer/components/item-object-list/item-list-layout.tsx
+++ b/src/renderer/components/item-object-list/item-list-layout.tsx
@@ -322,6 +322,7 @@ export class ItemListLayout extends React.Component {
}
renderHeaderContent(placeholders: IHeaderPlaceholders): ReactNode {
+ const { isSearchable, searchFilters } = this.props;
const { title, filters, search, info } = placeholders;
return (
@@ -331,7 +332,7 @@ export class ItemListLayout extends React.Component {
{this.isReady && info}
{filters}
- {search}
+ {isSearchable && searchFilters && search}
>
);
}