workspaces -- part 2

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman committed 2020-07-22 14:26:17 +03:00
1 parent c70ed87da9
commit 117ad584d3
10 files changed
+363 -470

No files matched your search

+2 -2
View File
@@ -55,7 +55,7 @@ export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
}
@action
public saveWorkspace(workspace: Workspace) {
saveWorkspace(workspace: Workspace) {
const id = workspace.id;
const existingWorkspace = this.getById(id);
if (existingWorkspace) {
@@ -66,7 +66,7 @@ export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
}
@action
public removeWorkspace(id: WorkspaceId) {
removeWorkspace(id: WorkspaceId) {
const workspace = this.getById(id);
if (!workspace) return;
if (this.isDefault(id)) {
@@ -1,115 +0,0 @@
<template>
<div class="content">
<ClosePageButton />
<div class="container-fluid lens-workspaces">
<div class="header sticky-top">
<h2><i class="material-icons">layers</i> Workspaces</h2>
</div>
<div class="row">
<div class="col-3" />
<div class="col-6">
<h2>New Workspace</h2>
<b-form @submit.prevent="createWorkspace">
<b-form-group
label="Name:"
label-for="input-name"
>
<b-form-input
id="input-name"
v-model="workspace.name"
trim
/>
</b-form-group>
<b-form-group
label="Description:"
label-for="input-description"
>
<b-form-input
id="input-description"
v-model="workspace.description"
trim
/>
</b-form-group>
<b-form-row>
<b-col>
<b-button variant="primary" type="submit">
Create Workspace
</b-button>
</b-col>
</b-form-row>
</b-form>
</div>
<div class="col-3" />
</div>
</div>
</div>
</template>
<script>
import { v4 as uuid } from "uuid";
import ClosePageButton from "@/_vue/components/common/ClosePageButton";
export default {
name: 'AddWorkspacePage',
components: {
ClosePageButton
},
data() {
return {
workspace: {
id: uuid(),
name: "",
description: ""
},
errors: {
name: null,
description: null
}
}
},
computed: {
},
methods: {
createWorkspace: function() {
this.$store.commit("addWorkspace", this.workspace)
this.$router.push({
name: "workspaces-page"
})
}
},
mounted: function() {
this.$store.commit("hideMenu");
},
destroyed: function() {
this.$store.commit("showMenu");
}
}
</script>
<style lang="scss" scoped>
#app > .main-view > .content {
left: 70px;
right: 70px;
}
h2 {
i {
position: relative;
top: 6px;
}
}
.lens-workspaces {
height: 100%;
overflow-y: scroll;
& input {
background-color: #252729 !important;
border: 0px !important;
color: #87909c !important;
}
.header {
padding-top: 15px;
}
}
</style>
@@ -1,125 +0,0 @@
<template>
<div class="content">
<ClosePageButton />
<div class="container-fluid lens-workspaces">
<div class="header sticky-top">
<h2><i class="material-icons">layers</i> Workspaces</h2>
</div>
<div class="row">
<div class="col-3" />
<div class="col-6">
<h2>Edit Workspace</h2>
<b-form @submit.prevent="updateWorkspace">
<b-form-group
label="Name:"
label-for="input-name"
>
<b-form-input
id="input-name"
v-model="workspace.name"
trim
/>
</b-form-group>
<b-form-group
label="Description:"
label-for="input-description"
>
<b-form-input
id="input-description"
v-model="workspace.description"
trim
/>
</b-form-group>
<b-form-row>
<b-col>
<b-button variant="primary" type="submit">
Save
</b-button>
<b-button v-if="workspace.id !== 'default'" v-b-modal.bv-modal-confirm>
Delete
</b-button>
<b-modal id="bv-modal-confirm" @ok="deleteWorkspace" ok-title="Delete" ok-variant="danger" title-class="confirm-header" hide-backdrop title="Confirm workspace delete">
<p>Are you sure you want to delete <strong>{{ workspace.name }}</strong> workspace from Lens?</p>
</b-modal>
</b-col>
</b-form-row>
</b-form>
</div>
<div class="col-3" />
</div>
</div>
</div>
</template>
<script>
import ClosePageButton from "@/_vue/components/common/ClosePageButton";
import { workspaceStore } from "../../../common/workspace-store"
export default {
name: 'EditWorkspacePage',
components: {
ClosePageButton
},
data() {
return {
errors: {
name: null,
description: null
}
}
},
computed: {
workspace: function() {
return workspaceStore.getById(this.$route.params.id)
}
},
methods: {
updateWorkspace: function() {
this.$store.commit("updateWorkspace", this.workspace)
this.$router.push({
name: "workspaces-page"
})
},
deleteWorkspace: async function() {
await this.$store.commit("removeWorkspace", this.workspace)
this.$router.push({
name: "workspaces-page"
})
}
},
mounted: function() {
this.$store.commit("hideMenu");
},
destroyed: function() {
this.$store.commit("showMenu");
}
}
</script>
<style lang="scss" scoped>
#app > .main-view > .content {
left: 70px;
right: 70px;
}
h2 {
i {
position: relative;
top: 6px;
}
}
.lens-workspaces {
height: 100%;
overflow-y: scroll;
& input {
background-color: #252729 !important;
border: 0px !important;
color: #87909c !important;
}
.header {
padding-top: 15px;
}
}
</style>
@@ -1,156 +0,0 @@
<template>
<div class="content">
<ClosePageButton />
<div class="container-fluid lens-workspaces">
<div class="header sticky-top">
<h2><i class="material-icons">layers</i> Workspaces</h2>
</div>
<div class="row">
<div class="col-9 main-content">
<b-list-group>
<b-list-group-item @click="editWorkspace(workspace)" href="#" v-for="workspace in workspaces" :key="workspace.id" :workspace="workspace">
<div class="d-flex w-100 justify-content-between">
<h5>{{ workspace.name }}</h5>
<small v-if="workspace.id !== 'default'">
<a href="#" title="Switch to workspace" v-b-tooltip.hover.right @click.prevent.stop="switchWorkspace(workspace)"><i class="material-icons">keyboard_arrow_right</i></a>
</small>
</div>
<p class="mb-1">
{{ workspace.description }}
</p>
</b-list-group-item>
</b-list-group>
<b-button variant="primary" href="#" @click.prevent="goAdd">
Add Workspace
</b-button>
</div>
<div class="col-3 help">
<h3>What is a Workspace?</h3>
<p>
Workspaces are used to organize number of clusters into logical groups. A single workspaces contains a list of clusters and their full configuration.
</p>
</div>
</div>
</div>
</div>
</template>
<script>
import ClosePageButton from "@/_vue/components/common/ClosePageButton";
export default {
name: 'WorkspacesPage',
components: {
ClosePageButton
},
data(){
return {
fields: [
{
key: "name",
label: "Name"
},
{
key: "description",
label: "Description"
}
],
errors: {
}
}
},
computed: {
workspaces: function() {
return this.$store.getters.workspaces;
}
},
methods: {
switchWorkspace: function(workspace) {
this.$store.commit("setCurrentWorkspace", workspace);
this.$store.dispatch("clearClusters");
this.$store.dispatch("refreshClusters", workspace);
this.$router.push({
name: "landing-page"
});
},
editWorkspace: function(workspace) {
this.$router.push({
name: "edit-workspace-page",
params: {
id: workspace.id
}
});
},
goAdd: function() {
this.$router.push({
name: "add-workspace-page"
});
}
},
mounted: function() {
this.$store.commit("hideMenu");
},
destroyed: function() {
this.$store.commit("showMenu");
}
}
</script>
<style lang="scss" scoped>
#app > .main-view > .content {
left: 70px;
right: 70px;
}
h2 {
i {
position: relative;
top: 6px;
}
}
.main-content {
padding-left: 40px;
padding-top: 20px;
}
.lens-workspaces {
height: 100%;
overflow-y: scroll;
& input {
background-color: #252729 !important;
border: 0px !important;
color: #87909c !important;
}
.header {
padding-top: 15px;
}
.list-group {
padding-bottom: 20px;
}
.list-group-item {
background-color: inherit;
color: var(--lens-text-color-light);
border: 0;
border-bottom: 1px solid var(--lens-pane-bg);
padding-left: 0;
h5 {
color: var(--lens-primary)
}
h5:hover {
text-decoration: underline;
}
small {
a {
color: var(--lens-text-color-light);
z-index: 100;
}
i {
opacity: 0.3;
&:hover {
opacity: 1;
}
}
}
}
}
</style>
@@ -1,3 +1,14 @@
.Workspaces {
.workspace {
--flex-gap: #{$padding};
padding: $padding / 2;
&.default {
font-style: italic;
}
> .description {
flex: 1;
}
}
}
@@ -1,43 +1,173 @@
import "./workspaces.scss"
import React, { Fragment } from "react";
import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { computed, observable, toJS } from "mobx";
import { t, Trans } from "@lingui/macro";
import { WizardLayout } from "../layout/wizard-layout";
import { workspaceStore } from "../../../common/workspace-store";
import { Workspace, WorkspaceId, workspaceStore } from "../../../common/workspace-store";
import { v4 as uuid } from "uuid"
import { _i18n } from "../../i18n";
import { ConfirmDialog } from "../confirm-dialog";
import { Icon } from "../icon";
import { Input } from "../input";
import { cssNames, prevDefault } from "../../utils";
import { Button } from "../button";
@observer
export class Workspaces extends React.Component {
@observable editingWorkspaces = observable.map<WorkspaceId, Workspace>();
@computed get workspaces(): Workspace[] {
const allWorkspaces = new Map([
...workspaceStore.workspaces,
...this.editingWorkspaces,
]);
return Array.from(allWorkspaces.values());
}
renderInfo() {
return (
<Fragment>
<h2><Trans>What is a Workspace?</Trans></h2>
<p><Trans>Workspaces are used to organize number of clusters into logical groups.</Trans></p>
<p><Trans>A single workspaces contains a list of clusters and their full configuration.</Trans></p>
<p className="info">
<Trans>Workspaces are used to organize number of clusters into logical groups.</Trans>
</p>
<p>
<Trans>A single workspaces contains a list of clusters and their full configuration.</Trans>
</p>
</Fragment>
)
}
saveWorkspace = (id: WorkspaceId) => {
const draft = toJS(this.editingWorkspaces.get(id));
if (draft) {
this.clearEditing(id);
workspaceStore.saveWorkspace(draft);
}
}
addWorkspace = () => {
console.log('add workspace')
const workspaceId = uuid();
this.editingWorkspaces.set(workspaceId, {
id: workspaceId,
name: _i18n._(t`My Workspace`),
description: "",
})
}
editWorkspace = (id: WorkspaceId) => {
const workspace = workspaceStore.getById(id);
this.editingWorkspaces.set(id, toJS(workspace));
}
clearEditing = (id: WorkspaceId) => {
this.editingWorkspaces.delete(id);
}
removeWorkspace = (id: WorkspaceId) => {
const workspace = workspaceStore.getById(id);
ConfirmDialog.open({
okButtonProps: {
label: _i18n._(t`Remove Workspace`),
primary: false,
accent: true,
},
ok: () => {
this.clearEditing(id);
workspaceStore.removeWorkspace(id);
},
message: (
<div className="confirm flex column gaps">
<p>
<Trans>Are you sure you want remove workspace <b>{workspace.name}</b>?</Trans>
</p>
<p className="info">
<Trans>All clusters within workspace will be cleared as well</Trans>
</p>
</div>
),
})
}
render() {
const { workspacesList, currentWorkspace } = workspaceStore;
return (
<WizardLayout className="Workspaces" infoPanel={this.renderInfo()}>
<h2>
<Trans>Workspaces</Trans>
</h2>
<div className="workspaces">
{workspacesList.map(({ id: workspaceId, name, description }) => {
<div className="items flex column gaps">
{this.workspaces.map(({ id: workspaceId, name, description }) => {
const isActive = workspaceStore.currentWorkspaceId === workspaceId;
const isDefault = workspaceStore.isDefault(workspaceId);
const isEditing = this.editingWorkspaces.has(workspaceId);
const editingWorkspace = this.editingWorkspaces.get(workspaceId);
const className = cssNames("workspace flex gaps align-center", {
active: isActive,
editing: isEditing,
default: isDefault,
});
return (
<div key={workspaceId} className="workspace flex gaps">
<span className="name">{name}</span>
<span className="description">{description}</span>
<div key={workspaceId} className={className}>
{!isEditing && (
<Fragment>
<span className="name flex gaps align-center">
<a href="#" onClick={prevDefault(() => workspaceStore.setActive(workspaceId))}>{name}</a>
{isActive && <span> <Trans>(current)</Trans></span>}
</span>
<span className="description">{description}</span>
{!isDefault && (
<Fragment>
<Icon
material="edit"
tooltip={<Trans>Edit</Trans>}
onClick={() => this.editWorkspace(workspaceId)}
/>
<Icon
material="delete"
tooltip={<Trans>Delete</Trans>}
onClick={() => this.removeWorkspace(workspaceId)}
/>
</Fragment>
)}
</Fragment>
)}
{isEditing && (
<Fragment>
<Input
className="name"
placeholder={_i18n._(t`Name`)}
value={editingWorkspace.name}
onChange={v => editingWorkspace.name = v}
/>
<Input
className="description"
placeholder={_i18n._(t`Description`)}
value={editingWorkspace.description}
onChange={v => editingWorkspace.description = v}
/>
<Icon
material="cancel"
tooltip={<Trans>Cancel</Trans>}
onClick={() => this.clearEditing(workspaceId)}
/>
<Icon
material="save"
tooltip={<Trans>Save</Trans>}
onClick={() => this.saveWorkspace(workspaceId)}
/>
</Fragment>
)}
</div>
)
})}
</div>
<Button
primary
className="box left"
label={<Trans>Add Workspace</Trans>}
onClick={this.addWorkspace}
/>
</WizardLayout>
);
}
@@ -5,7 +5,7 @@ import { observable } from "mobx";
import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { cssNames, noop, prevDefault } from "../../utils";
import { Button } from "../button";
import { Button, ButtonProps } from "../button";
import { Dialog, DialogProps } from "../dialog";
import { Icon } from "../icon";
@@ -14,7 +14,9 @@ export interface IConfirmDialogParams {
labelOk?: ReactNode;
labelCancel?: ReactNode;
message?: ReactNode;
icon?: string;
icon?: ReactNode;
okButtonProps?: Partial<ButtonProps>
cancelButtonProps?: Partial<ButtonProps>
}
interface Props extends Partial<DialogProps> {
@@ -40,7 +42,7 @@ export class ConfirmDialog extends React.Component<Props> {
ok: noop,
labelOk: <Trans>Ok</Trans>,
labelCancel: <Trans>Cancel</Trans>,
icon: "warning",
icon: <Icon big material="warning"/>,
};
get params(): IConfirmDialogParams {
@@ -67,18 +69,21 @@ export class ConfirmDialog extends React.Component<Props> {
render() {
const { className, ...dialogProps } = this.props;
const { icon, labelOk, labelCancel, message } = this.params;
const {
icon, labelOk, labelCancel, message,
okButtonProps = {},
cancelButtonProps = {},
} = this.params;
return (
<Dialog
{...dialogProps}
className={cssNames("ConfirmDialog", className, icon)}
className={cssNames("ConfirmDialog", className)}
isOpen={ConfirmDialog.isOpen}
onClose={this.onClose}
close={this.close}
>
<div className="confirm-content">
<Icon big material={icon}/>
{message}
{icon} {message}
</div>
<div className="confirm-buttons">
<Button
@@ -86,6 +91,7 @@ export class ConfirmDialog extends React.Component<Props> {
className="cancel"
label={labelCancel}
onClick={prevDefault(this.close)}
{...cancelButtonProps}
/>
<Button
autoFocus primary
@@ -93,6 +99,7 @@ export class ConfirmDialog extends React.Component<Props> {
label={labelOk}
onClick={prevDefault(this.ok)}
waiting={this.isSaving}
{...okButtonProps}
/>
</div>
</Dialog>