diff --git a/src/renderer/components/+workspaces/add-workspace-dialog.scss b/src/renderer/components/+workspaces/add-workspace-dialog.scss new file mode 100644 index 0000000000..25bfadf0f9 --- /dev/null +++ b/src/renderer/components/+workspaces/add-workspace-dialog.scss @@ -0,0 +1,2 @@ +.AddWorkspaceDialog { +} \ No newline at end of file diff --git a/src/renderer/components/+workspaces/add-workspace-dialog.tsx b/src/renderer/components/+workspaces/add-workspace-dialog.tsx new file mode 100644 index 0000000000..9b37944bc4 --- /dev/null +++ b/src/renderer/components/+workspaces/add-workspace-dialog.tsx @@ -0,0 +1,84 @@ +import "./add-workspace-dialog.scss"; + +import React from "react"; +import { observable } from "mobx"; +import { observer } from "mobx-react"; +import { Dialog, DialogProps } from "../dialog"; +import { Wizard, WizardStep } from "../wizard"; +//import { namespaceStore } from "./namespace.store"; +import { Workspace, WorkspaceId } from "../../../common/workspace-store"; +import { Input } from "../input"; +import { systemName } from "../input/input_validators"; +import { Notifications } from "../notifications"; + +interface Props extends DialogProps { + onSuccess?(ws: Workspace): void; + onError?(error: any): void; +} + +@observer +export class AddWorkspaceDialog extends React.Component { + @observable static isOpen = false; + @observable workspace = ""; + + static open() { + AddWorkspaceDialog.isOpen = true; + } + + static close() { + AddWorkspaceDialog.isOpen = false; + } + + reset = () => { + this.workspace = ""; + }; + + close = () => { + AddWorkspaceDialog.close(); + }; + + addWorkspace = async () => { + const { workspace } = this; + const { onSuccess, onError } = this.props; + + try { +// await namespaceStore.create({ name: workspace }).then(onSuccess); + this.close(); + } catch (err) { + Notifications.error(err); + onError && onError(err); + } + }; + + render() { + const { ...dialogProps } = this.props; + const { workspace } = this; + const header =
Create Workspace
; + + return ( + + + + this.workspace = v.toLowerCase()} + /> + + + + ); + } +} diff --git a/src/renderer/components/+workspaces/index.ts b/src/renderer/components/+workspaces/index.ts index 1305e70219..0496641d32 100644 --- a/src/renderer/components/+workspaces/index.ts +++ b/src/renderer/components/+workspaces/index.ts @@ -1,3 +1,4 @@ export * from "./workspaces.route"; +export * from "./workspace-list.route"; export * from "./workspaces"; export * from "./workspace-list"; diff --git a/src/renderer/components/+workspaces/workspace-details.scss b/src/renderer/components/+workspaces/workspace-details.scss new file mode 100644 index 0000000000..951e95dc25 --- /dev/null +++ b/src/renderer/components/+workspaces/workspace-details.scss @@ -0,0 +1,52 @@ +.WorkspaceDetails { + .intro-logo { + margin-right: $margin * 2; + background: $helmLogoBackground; + border-radius: $radius; + max-width: 150px; + max-height: 100px; + padding: $padding; + box-sizing: content-box; + } + + .intro-contents { + .description { + font-weight: bold; + color: $textColorAccent; + padding-bottom: $padding; + + .Button { + padding-left: $padding * 3; + padding-right: $padding * 3; + margin-left: $margin * 2; + align-self: flex-start; + } + } + + .version { + .Select { + min-width: 80px; + white-space: nowrap; + } + + .Icon { + margin-right: $margin; + } + } + + .maintainers { + a { + display: inline-block; + margin-right: $margin; + } + } + + .DrawerItem { + align-items: center; + } + } + + .chart-description { + margin-top: $margin * 2; + } +} diff --git a/src/renderer/components/+workspaces/workspace-details.tsx b/src/renderer/components/+workspaces/workspace-details.tsx new file mode 100644 index 0000000000..7cd1155fc1 --- /dev/null +++ b/src/renderer/components/+workspaces/workspace-details.tsx @@ -0,0 +1,62 @@ +import "./workspace-details.scss"; + +import React, { Component } from "react"; +import { WorkspaceItem } from "./workspace-list.store"; +import { clusterStore } from "../../../common/cluster-store"; +import { Cluster } from "../../../main/cluster"; +import { observable, autorun } from "mobx"; +import { observer } from "mobx-react"; +import { Drawer, DrawerItem, DrawerTitle } from "../drawer"; +import { autobind, stopPropagation } from "../../utils"; +import { MarkdownViewer } from "../markdown-viewer"; +import { Spinner } from "../spinner"; +import { Button } from "../button"; +import { Select, SelectOption } from "../select"; +import { Badge } from "../badge"; + +interface Props { + workspace: WorkspaceItem; + hideDetails(): void; +} + +@observer +export class WorkspaceDetails extends Component { + + renderClusters() { + const { workspace } = this.props; + const clusters = clusterStore.getByWorkspaceId(workspace.getId()); + return
+ {clusters.map(cluster =>
{cluster.contextName}
)} +
+ } + + render() { + const { workspace, hideDetails } = this.props; + const title = workspace ? <>Workspace: {workspace.getName()} : ""; + + return ( + + + {workspace.getDescription()} + + + {workspace.getId()} + + + {workspace.getOwnerRef()} + + + {workspace.getEnabled()} + + + {this.renderClusters()} + + ); + } +} diff --git a/src/renderer/components/+workspaces/workspace-list.route.ts b/src/renderer/components/+workspaces/workspace-list.route.ts new file mode 100644 index 0000000000..2830dce981 --- /dev/null +++ b/src/renderer/components/+workspaces/workspace-list.route.ts @@ -0,0 +1,12 @@ +import type { RouteProps } from "react-router"; +import { buildURL } from "../../../common/utils/buildUrl"; + +export const workspaceListRoute: RouteProps = { + path: `/workspaces/:workspaceName?` +}; + +export interface IWorkspaceListRouteParams { + workspaceName?: string; +} + +export const workspaceListURL = buildURL(workspaceListRoute.path); \ No newline at end of file diff --git a/src/renderer/components/+workspaces/workspace-list.store.ts b/src/renderer/components/+workspaces/workspace-list.store.ts index f6e930dc4b..f1b8364284 100644 --- a/src/renderer/components/+workspaces/workspace-list.store.ts +++ b/src/renderer/components/+workspaces/workspace-list.store.ts @@ -8,9 +8,21 @@ export class WorkspaceItem { return this.workspace.name; } + getDescription() { + return this.workspace.description; + } + getId() { return this.workspace.id; } + + getOwnerRef() { + return this.workspace.ownerRef; + } + + getEnabled() { + return this.workspace.enabled ? "True" : "False"; + } } export class WorkspaceListStore extends ItemStore { diff --git a/src/renderer/components/+workspaces/workspace-list.tsx b/src/renderer/components/+workspaces/workspace-list.tsx index 3ca5ea66b0..dde67f2372 100644 --- a/src/renderer/components/+workspaces/workspace-list.tsx +++ b/src/renderer/components/+workspaces/workspace-list.tsx @@ -1,22 +1,55 @@ import "./workspaces.scss"; import React from "react"; +import { RouteComponentProps } from "react-router"; +import { observer } from "mobx-react"; +import { navigation } from "../../navigation"; +import { workspaceListURL, IWorkspaceListRouteParams } from "./workspace-list.route"; import { TabLayout } from "../layout/tab-layout"; import { Badge } from "../badge"; import { ItemListLayout, ItemListLayoutProps } from "../item-object-list/item-list-layout"; +import { AddWorkspaceDialog } from "./add-workspace-dialog"; import { Workspace, WorkspaceId } from "../../../common/workspace-store"; import { WorkspaceItem, workspaceListStore } from "./workspace-list.store"; +import { WorkspaceDetails } from "./workspace-details"; enum sortBy { name = "name", - id = "id", + description = "description", } -export class WorkspaceList extends React.Component { +interface Props extends RouteComponentProps { +} + +@observer +export class WorkspaceList extends React.Component { componentDidMount() { workspaceListStore.loadAll(); } + get selectedWorkspace() { + const { match: { params: { workspaceName } } } = this.props; + + return workspaceListStore.getByName(workspaceName); + } + + showDetails = (workspace: WorkspaceItem) => { + if (!workspace) { + navigation.merge(workspaceListURL()); + } + else { + navigation.merge(workspaceListURL({ + params: { + workspaceName: workspace.getName(), + } + })); + } + }; + + hideDetails = () => { + this.showDetails(null); + }; + render() { return ( @@ -25,19 +58,32 @@ export class WorkspaceList extends React.Component { className="Workspaces" store={workspaceListStore} sortingCallbacks={{ [sortBy.name]: (ws: WorkspaceItem) => ws.getName(), - [sortBy.id]: (ws: WorkspaceItem) => ws.getId(), + [sortBy.description]: (ws: WorkspaceItem) => ws.getDescription(), }} searchFilters={[]} renderHeaderTitle="Workspaces" renderTableHeader={[ { title: "Name", className: "name", sortBy: sortBy.name }, - { title: "Id", className: "id", sortBy: sortBy.id }, + { title: "Description", className: "description", sortBy: sortBy.description }, ]} renderTableContents={(item: WorkspaceItem) => [ item.getName(), - item.getId(), + item.getDescription(), ]} + addRemoveButtons={{ + addTooltip: "Add Workspace", + onAdd: () => AddWorkspaceDialog.open(), + }} + detailsItem={this.selectedWorkspace} + onDetails={this.showDetails} /> + {this.selectedWorkspace && ( + + )} + ); } diff --git a/src/renderer/components/cluster-manager/cluster-manager.tsx b/src/renderer/components/cluster-manager/cluster-manager.tsx index 355bdb6b8a..38a52a06b6 100644 --- a/src/renderer/components/cluster-manager/cluster-manager.tsx +++ b/src/renderer/components/cluster-manager/cluster-manager.tsx @@ -8,7 +8,7 @@ import { ClustersMenu } from "./clusters-menu"; import { BottomBar } from "./bottom-bar"; import { LandingPage, landingRoute, landingURL } from "../+landing-page"; import { Preferences, preferencesRoute } from "../+preferences"; -import { WorkspaceList, workspacesRoute } from "../+workspaces"; +import { WorkspaceList, workspaceListRoute } from "../+workspaces"; import { AddCluster, addClusterRoute } from "../+add-cluster"; import { ClusterView } from "./cluster-view"; import { ClusterSettings, clusterSettingsRoute } from "../+cluster-settings"; @@ -67,7 +67,7 @@ export class ClusterManager extends React.Component { - +