[ARVADOS-WORKBENCH2] created: 1.1.4-600-geebf512
Git user
git at public.curoverse.com
Thu Aug 16 03:04:23 EDT 2018
at eebf51242e597fd8430f1e92a5e9076b3d623ab5 (commit)
commit eebf51242e597fd8430f1e92a5e9076b3d623ab5
Author: Janicki Artur <artur.janicki at contractors.roche.com>
Date: Thu Aug 16 09:04:05 2018 +0200
init edit project feature, refactor forms - add textField component
Feature #13833
Arvados-DCO-1.1-Signed-off-by: Janicki Artur <artur.janicki at contractors.roche.com>
diff --git a/src/store/project/project-action.ts b/src/store/project/project-action.ts
index bef50d1..7080090 100644
--- a/src/store/project/project-action.ts
+++ b/src/store/project/project-action.ts
@@ -9,12 +9,16 @@ import { FilterBuilder } from "~/common/api/filter-builder";
import { RootState } from "../store";
import { checkPresenceInFavorites } from "../favorites/favorites-actions";
import { ServiceRepository } from "~/services/services";
+import { projectPanelActions } from "~/store/project-panel/project-panel-action";
export const projectActions = unionize({
OPEN_PROJECT_CREATOR: ofType<{ ownerUuid: string }>(),
CLOSE_PROJECT_CREATOR: ofType<{}>(),
CREATE_PROJECT: ofType<Partial<ProjectResource>>(),
CREATE_PROJECT_SUCCESS: ofType<ProjectResource>(),
+ OPEN_PROJECT_UPDATER: ofType<{ uuid: string}>(),
+ CLOSE_PROJECT_UPDATER: ofType<{}>(),
+ UPDATE_PROJECT_SUCCESS: ofType<ProjectResource>(),
REMOVE_PROJECT: ofType<string>(),
PROJECTS_REQUEST: ofType<string>(),
PROJECTS_SUCCESS: ofType<{ projects: ProjectResource[], parentItemId?: string }>(),
@@ -26,6 +30,8 @@ export const projectActions = unionize({
value: 'payload'
});
+export const PROJECT_FORM_NAME = 'projectEditDialog';
+
export const getProjectList = (parentUuid: string = '') =>
(dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
dispatch(projectActions.PROJECTS_REQUEST(parentUuid));
@@ -50,4 +56,17 @@ export const createProject = (project: Partial<ProjectResource>) =>
.then(project => dispatch(projectActions.CREATE_PROJECT_SUCCESS(project)));
};
+export const updateProject = (project: Partial<ProjectResource>) =>
+ (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const { uuid } = getState().projects.updater;
+ return services.projectService
+ .update(uuid, project)
+ .then(project => {
+ dispatch(projectActions.UPDATE_PROJECT_SUCCESS(project));
+ dispatch(projectPanelActions.REQUEST_ITEMS());
+ dispatch<any>(getProjectList(project.ownerUuid));
+ // ToDo: Update Panel Details
+ });
+ };
+
export type ProjectAction = UnionOf<typeof projectActions>;
diff --git a/src/store/project/project-reducer.test.ts b/src/store/project/project-reducer.test.ts
index cd96afc..d5963f3 100644
--- a/src/store/project/project-reducer.test.ts
+++ b/src/store/project/project-reducer.test.ts
@@ -35,6 +35,10 @@ describe('project-reducer', () => {
creator: {
opened: false,
ownerUuid: "",
+ },
+ updater: {
+ opened: false,
+ uuid: ''
}
});
});
@@ -50,6 +54,7 @@ describe('project-reducer', () => {
}],
currentItemId: "1",
creator: { opened: false, ownerUuid: "" },
+ updater: { opened: false, uuid: '' }
};
const project = {
items: [{
@@ -61,6 +66,7 @@ describe('project-reducer', () => {
}],
currentItemId: "",
creator: { opened: false, ownerUuid: "" },
+ updater: { opened: false, uuid: '' }
};
const state = projectsReducer(initialState, projectActions.RESET_PROJECT_TREE_ACTIVITY(initialState.items[0].id));
@@ -77,7 +83,8 @@ describe('project-reducer', () => {
status: TreeItemStatus.PENDING
}],
currentItemId: "1",
- creator: { opened: false, ownerUuid: "" }
+ creator: { opened: false, ownerUuid: "" },
+ updater: { opened: false, uuid: '' }
};
const project = {
items: [{
@@ -89,6 +96,7 @@ describe('project-reducer', () => {
}],
currentItemId: "1",
creator: { opened: false, ownerUuid: "" },
+ updater: { opened: false, uuid: '' }
};
const state = projectsReducer(initialState, projectActions.TOGGLE_PROJECT_TREE_ITEM_ACTIVE(initialState.items[0].id));
@@ -106,7 +114,8 @@ describe('project-reducer', () => {
status: TreeItemStatus.PENDING,
}],
currentItemId: "1",
- creator: { opened: false, ownerUuid: "" }
+ creator: { opened: false, ownerUuid: "" },
+ updater: { opened: false, uuid: '' }
};
const project = {
items: [{
@@ -118,6 +127,7 @@ describe('project-reducer', () => {
}],
currentItemId: "1",
creator: { opened: false, ownerUuid: "" },
+
};
const state = projectsReducer(initialState, projectActions.TOGGLE_PROJECT_TREE_ITEM_OPEN(initialState.items[0].id));
diff --git a/src/store/project/project-reducer.ts b/src/store/project/project-reducer.ts
index 4249007..bb07486 100644
--- a/src/store/project/project-reducer.ts
+++ b/src/store/project/project-reducer.ts
@@ -11,7 +11,8 @@ import { ProjectResource } from "~/models/project";
export type ProjectState = {
items: Array<TreeItem<ProjectResource>>,
currentItemId: string,
- creator: ProjectCreator
+ creator: ProjectCreator,
+ updater: ProjectUpdater
};
interface ProjectCreator {
@@ -20,6 +21,11 @@ interface ProjectCreator {
error?: string;
}
+interface ProjectUpdater {
+ opened: boolean;
+ uuid: string;
+}
+
export function findTreeItem<T>(tree: Array<TreeItem<T>>, itemId: string): TreeItem<T> | undefined {
let item;
for (const t of tree) {
@@ -100,12 +106,24 @@ const updateCreator = (state: ProjectState, creator: Partial<ProjectCreator>) =>
}
});
+const updateProject = (state: ProjectState, updater?: Partial<ProjectUpdater>) => ({
+ ...state,
+ updater: {
+ ...state.updater,
+ ...updater
+ }
+});
+
const initialState: ProjectState = {
items: [],
currentItemId: "",
creator: {
opened: false,
ownerUuid: ""
+ },
+ updater: {
+ opened: false,
+ uuid: ''
}
};
@@ -116,6 +134,9 @@ export const projectsReducer = (state: ProjectState = initialState, action: Proj
CLOSE_PROJECT_CREATOR: () => updateCreator(state, { opened: false }),
CREATE_PROJECT: () => updateCreator(state, { error: undefined }),
CREATE_PROJECT_SUCCESS: () => updateCreator(state, { opened: false, ownerUuid: "" }),
+ OPEN_PROJECT_UPDATER: ({ uuid }) => updateProject(state, { uuid, opened: true }),
+ CLOSE_PROJECT_UPDATER: () => updateProject(state, { opened: false, uuid: "" }),
+ UPDATE_PROJECT_SUCCESS: () => updateProject(state, { opened: false, uuid: "" }),
REMOVE_PROJECT: () => state,
PROJECTS_REQUEST: itemId => {
const items = _.cloneDeep(state.items);
diff --git a/src/validators/create-collection/create-collection-validator.tsx b/src/validators/create-collection/create-collection-validator.tsx
deleted file mode 100644
index 2d8e1f5..0000000
--- a/src/validators/create-collection/create-collection-validator.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { require } from '../require';
-import { maxLength } from '../max-length';
-
-export const COLLECTION_NAME_VALIDATION = [require, maxLength(255)];
-export const COLLECTION_DESCRIPTION_VALIDATION = [maxLength(255)];
\ No newline at end of file
diff --git a/src/validators/create-project/create-project-validator.tsx b/src/validators/create-project/create-project-validator.tsx
deleted file mode 100644
index ddea8be..0000000
--- a/src/validators/create-project/create-project-validator.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { require } from '../require';
-import { maxLength } from '../max-length';
-
-export const PROJECT_NAME_VALIDATION = [require, maxLength(255)];
-export const PROJECT_DESCRIPTION_VALIDATION = [maxLength(255)];
-export const COLLECTION_NAME_VALIDATION = [require, maxLength(255)];
-export const COLLECTION_DESCRIPTION_VALIDATION = [maxLength(255)];
-export const COLLECTION_PROJECT_VALIDATION = [require];
diff --git a/src/validators/validators.tsx b/src/validators/validators.tsx
index fdeb8fa..edd0782 100644
--- a/src/validators/validators.tsx
+++ b/src/validators/validators.tsx
@@ -6,4 +6,11 @@ import { require } from './require';
import { maxLength } from './max-length';
export const TAG_KEY_VALIDATION = [require, maxLength(255)];
-export const TAG_VALUE_VALIDATION = [require, maxLength(255)];
\ No newline at end of file
+export const TAG_VALUE_VALIDATION = [require, maxLength(255)];
+
+export const PROJECT_NAME_VALIDATION = [require, maxLength(255)];
+export const PROJECT_DESCRIPTION_VALIDATION = [maxLength(255)];
+
+export const COLLECTION_NAME_VALIDATION = [require, maxLength(255)];
+export const COLLECTION_DESCRIPTION_VALIDATION = [maxLength(255)];
+export const COLLECTION_PROJECT_VALIDATION = [require];
\ No newline at end of file
diff --git a/src/views-components/context-menu/action-sets/project-action-set.ts b/src/views-components/context-menu/action-sets/project-action-set.ts
index 8944685..1b000c8 100644
--- a/src/views-components/context-menu/action-sets/project-action-set.ts
+++ b/src/views-components/context-menu/action-sets/project-action-set.ts
@@ -2,28 +2,39 @@
//
// SPDX-License-Identifier: AGPL-3.0
-import { reset } from "redux-form";
+import { reset, initialize } from "redux-form";
import { ContextMenuActionSet } from "../context-menu-action-set";
-import { projectActions } from "~/store/project/project-action";
-import { NewProjectIcon } from "~/components/icon/icon";
+import { projectActions, PROJECT_FORM_NAME } from "~/store/project/project-action";
+import { NewProjectIcon, RenameIcon } from "~/components/icon/icon";
import { ToggleFavoriteAction } from "../actions/favorite-action";
import { toggleFavorite } from "~/store/favorites/favorites-actions";
import { favoritePanelActions } from "~/store/favorite-panel/favorite-panel-action";
import { PROJECT_CREATE_DIALOG } from "../../dialog-create/dialog-project-create";
-export const projectActionSet: ContextMenuActionSet = [[{
- icon: NewProjectIcon,
- name: "New project",
- execute: (dispatch, resource) => {
- dispatch(reset(PROJECT_CREATE_DIALOG));
- dispatch(projectActions.OPEN_PROJECT_CREATOR({ ownerUuid: resource.uuid }));
+export const projectActionSet: ContextMenuActionSet = [[
+ {
+ icon: NewProjectIcon,
+ name: "New project",
+ execute: (dispatch, resource) => {
+ dispatch(reset(PROJECT_CREATE_DIALOG));
+ dispatch(projectActions.OPEN_PROJECT_CREATOR({ ownerUuid: resource.uuid }));
+ }
+ },
+ {
+ icon: RenameIcon,
+ name: "Edit project",
+ execute: (dispatch, resource) => {
+ dispatch(projectActions.OPEN_PROJECT_UPDATER({ uuid: resource.uuid }));
+ dispatch(initialize(PROJECT_FORM_NAME, { name: resource.name, description: resource.description }));
+ }
+ },
+ {
+ component: ToggleFavoriteAction,
+ execute: (dispatch, resource) => {
+ dispatch<any>(toggleFavorite(resource)).then(() => {
+ dispatch<any>(favoritePanelActions.REQUEST_ITEMS());
+ });
+ }
}
-}, {
- component: ToggleFavoriteAction,
- execute: (dispatch, resource) => {
- dispatch<any>(toggleFavorite(resource)).then(() => {
- dispatch<any>(favoritePanelActions.REQUEST_ITEMS());
- });
- }
-}]];
+]];
diff --git a/src/views-components/dialog-create/dialog-collection-create-selected.tsx b/src/views-components/dialog-create/dialog-collection-create-selected.tsx
index 0dc590a..af2536d 100644
--- a/src/views-components/dialog-create/dialog-collection-create-selected.tsx
+++ b/src/views-components/dialog-create/dialog-collection-create-selected.tsx
@@ -7,7 +7,7 @@ import { InjectedFormProps, Field, WrappedFieldProps } from "redux-form";
import { Dialog, DialogTitle, DialogContent, DialogActions, Button, CircularProgress } from "@material-ui/core";
import { WithDialogProps } from "~/store/dialog/with-dialog";
import { TextField } from "~/components/text-field/text-field";
-import { COLLECTION_NAME_VALIDATION, COLLECTION_DESCRIPTION_VALIDATION, COLLECTION_PROJECT_VALIDATION } from "~/validators/create-project/create-project-validator";
+import { COLLECTION_NAME_VALIDATION, COLLECTION_DESCRIPTION_VALIDATION, COLLECTION_PROJECT_VALIDATION } from "~/validators/validators";
import { ProjectTreePicker } from "../project-tree-picker/project-tree-picker";
export const DialogCollectionCreateWithSelected = (props: WithDialogProps<string> & InjectedFormProps<{ name: string }>) =>
diff --git a/src/views-components/dialog-create/dialog-collection-create.tsx b/src/views-components/dialog-create/dialog-collection-create.tsx
index 7f2e411..af0e33f 100644
--- a/src/views-components/dialog-create/dialog-collection-create.tsx
+++ b/src/views-components/dialog-create/dialog-collection-create.tsx
@@ -9,13 +9,13 @@ import { TextField } from '~/components/text-field/text-field';
import { Dialog, DialogActions, DialogContent, DialogTitle } from '@material-ui/core/';
import { Button, StyleRulesCallback, WithStyles, withStyles, CircularProgress } from '@material-ui/core';
-import { COLLECTION_NAME_VALIDATION, COLLECTION_DESCRIPTION_VALIDATION } from '~/validators/create-collection/create-collection-validator';
+import { COLLECTION_NAME_VALIDATION, COLLECTION_DESCRIPTION_VALIDATION } from '~/validators/validators';
import { FileUpload } from "~/components/file-upload/file-upload";
import { connect, DispatchProp } from "react-redux";
import { RootState } from "~/store/store";
import { collectionUploaderActions, UploadFile } from "~/store/collections/uploader/collection-uploader-actions";
-type CssRules = "button" | "lastButton" | "formContainer" | "textField" | "createProgress" | "dialogActions";
+type CssRules = "button" | "lastButton" | "formContainer" | "createProgress" | "dialogActions";
const styles: StyleRulesCallback<CssRules> = theme => ({
button: {
@@ -29,9 +29,6 @@ const styles: StyleRulesCallback<CssRules> = theme => ({
display: "flex",
flexDirection: "column",
},
- textField: {
- marginBottom: theme.spacing.unit * 3
- },
createProgress: {
position: "absolute",
minWidth: "20px",
@@ -42,10 +39,8 @@ const styles: StyleRulesCallback<CssRules> = theme => ({
}
});
-interface DialogCollectionCreateProps {
+interface DialogCollectionDataProps {
open: boolean;
- handleClose: () => void;
- onSubmit: (data: { name: string, description: string }, files: UploadFile[]) => void;
handleSubmit: any;
submitting: boolean;
invalid: boolean;
@@ -53,6 +48,13 @@ interface DialogCollectionCreateProps {
files: UploadFile[];
}
+interface DialogCollectionActionProps {
+ handleClose: () => void;
+ onSubmit: (data: { name: string, description: string }, files: UploadFile[]) => void;
+}
+
+type DialogCollectionProps = DialogCollectionDataProps & DialogCollectionActionProps & DispatchProp & WithStyles<CssRules>;
+
export const COLLECTION_CREATE_DIALOG = "collectionCreateDialog";
export const DialogCollectionCreate = compose(
@@ -61,7 +63,7 @@ export const DialogCollectionCreate = compose(
})),
reduxForm({ form: COLLECTION_CREATE_DIALOG }),
withStyles(styles))(
- class DialogCollectionCreate extends React.Component<DialogCollectionCreateProps & DispatchProp & WithStyles<CssRules>> {
+ class DialogCollectionCreate extends React.Component<DialogCollectionProps> {
render() {
const { classes, open, handleClose, handleSubmit, onSubmit, submitting, invalid, pristine, files } = this.props;
const busy = submitting || files.reduce(
@@ -82,13 +84,11 @@ export const DialogCollectionCreate = compose(
disabled={submitting}
component={TextField}
validate={COLLECTION_NAME_VALIDATION}
- className={classes.textField}
label="Collection Name" />
<Field name="description"
disabled={submitting}
component={TextField}
validate={COLLECTION_DESCRIPTION_VALIDATION}
- className={classes.textField}
label="Description - optional" />
<FileUpload
files={files}
diff --git a/src/views-components/dialog-create/dialog-project-create.tsx b/src/views-components/dialog-create/dialog-project-create.tsx
index c3d8415..e77114b 100644
--- a/src/views-components/dialog-create/dialog-project-create.tsx
+++ b/src/views-components/dialog-create/dialog-project-create.tsx
@@ -9,9 +9,9 @@ import { TextField } from '~/components/text-field/text-field';
import { Dialog, DialogActions, DialogContent, DialogTitle } from '@material-ui/core/';
import { Button, StyleRulesCallback, WithStyles, withStyles, CircularProgress } from '@material-ui/core';
-import { PROJECT_NAME_VALIDATION, PROJECT_DESCRIPTION_VALIDATION } from '~/validators/create-project/create-project-validator';
+import { PROJECT_NAME_VALIDATION, PROJECT_DESCRIPTION_VALIDATION } from '~/validators/validators';
-type CssRules = "button" | "lastButton" | "formContainer" | "textField" | "dialog" | "dialogTitle" | "createProgress" | "dialogActions";
+type CssRules = "button" | "lastButton" | "formContainer" | "dialog" | "dialogTitle" | "createProgress" | "dialogActions";
const styles: StyleRulesCallback<CssRules> = theme => ({
button: {
@@ -29,9 +29,6 @@ const styles: StyleRulesCallback<CssRules> = theme => ({
dialogTitle: {
paddingBottom: "0"
},
- textField: {
- marginTop: "32px",
- },
dialog: {
minWidth: "600px",
minHeight: "320px"
@@ -78,12 +75,10 @@ export const DialogProjectCreate = compose(
<Field name="name"
component={TextField}
validate={PROJECT_NAME_VALIDATION}
- className={classes.textField}
label="Project Name"/>
<Field name="description"
component={TextField}
validate={PROJECT_DESCRIPTION_VALIDATION}
- className={classes.textField}
label="Description - optional"/>
</DialogContent>
<DialogActions className={classes.dialogActions}>
diff --git a/src/views-components/dialog-update/dialog-collection-update.tsx b/src/views-components/dialog-update/dialog-collection-update.tsx
index d97ff41..18c43f2 100644
--- a/src/views-components/dialog-update/dialog-collection-update.tsx
+++ b/src/views-components/dialog-update/dialog-collection-update.tsx
@@ -6,11 +6,12 @@ import * as React from 'react';
import { reduxForm, Field } from 'redux-form';
import { compose } from 'redux';
import { ArvadosTheme } from '~/common/custom-theme';
-import { Dialog, DialogActions, DialogContent, DialogTitle, TextField, StyleRulesCallback, withStyles, WithStyles, Button, CircularProgress } from '@material-ui/core';
-import { COLLECTION_NAME_VALIDATION, COLLECTION_DESCRIPTION_VALIDATION } from '~/validators/create-collection/create-collection-validator';
+import { Dialog, DialogActions, DialogContent, DialogTitle, StyleRulesCallback, withStyles, WithStyles, Button, CircularProgress } from '@material-ui/core';
+import { COLLECTION_NAME_VALIDATION, COLLECTION_DESCRIPTION_VALIDATION } from '~/validators/validators';
import { COLLECTION_FORM_NAME } from '~/store/collections/updater/collection-updater-action';
+import { TextField } from '~/components/text-field/text-field';
-type CssRules = 'content' | 'actions' | 'textField' | 'buttonWrapper' | 'saveButton' | 'circularProgress';
+type CssRules = 'content' | 'actions' | 'buttonWrapper' | 'saveButton' | 'circularProgress';
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
content: {
@@ -22,9 +23,6 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
padding: `${theme.spacing.unit}px ${theme.spacing.unit * 3 - theme.spacing.unit / 2}px
${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`
},
- textField: {
- marginBottom: theme.spacing.unit * 3
- },
buttonWrapper: {
position: 'relative'
},
@@ -56,14 +54,6 @@ interface DialogCollectionAction {
type DialogCollectionProps = DialogCollectionDataProps & DialogCollectionAction & WithStyles<CssRules>;
-interface TextFieldProps {
- label: string;
- floatinglabeltext: string;
- className?: string;
- input?: string;
- meta?: any;
-}
-
export const DialogCollectionUpdate = compose(
reduxForm({ form: COLLECTION_FORM_NAME }),
withStyles(styles))(
@@ -83,19 +73,15 @@ export const DialogCollectionUpdate = compose(
<form onSubmit={handleSubmit((data: any) => onSubmit(data))}>
<DialogTitle>Edit Collection</DialogTitle>
<DialogContent className={classes.content}>
- <Field name="name"
+ <Field name='name'
disabled={submitting}
- component={this.renderTextField}
- floatinglabeltext="Collection Name"
+ component={TextField}
validate={COLLECTION_NAME_VALIDATION}
- className={classes.textField}
label="Collection Name" />
- <Field name="description"
+ <Field name='description'
disabled={submitting}
- component={this.renderTextField}
- floatinglabeltext="Description - optional"
+ component={TextField}
validate={COLLECTION_DESCRIPTION_VALIDATION}
- className={classes.textField}
label="Description - optional" />
</DialogContent>
<DialogActions className={classes.actions}>
@@ -115,17 +101,5 @@ export const DialogCollectionUpdate = compose(
</Dialog>
);
}
-
- renderTextField = ({ input, label, meta: { touched, error }, ...custom }: TextFieldProps) => (
- <TextField
- helperText={touched && error}
- label={label}
- className={this.props.classes.textField}
- error={touched && !!error}
- autoComplete='off'
- {...input}
- {...custom}
- />
- )
}
);
diff --git a/src/views-components/dialog-update/dialog-project-update.tsx b/src/views-components/dialog-update/dialog-project-update.tsx
new file mode 100644
index 0000000..5dde00a
--- /dev/null
+++ b/src/views-components/dialog-update/dialog-project-update.tsx
@@ -0,0 +1,101 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { reduxForm, Field } from 'redux-form';
+import { compose } from 'redux';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { StyleRulesCallback, WithStyles, withStyles, Dialog, DialogTitle, DialogContent, DialogActions, CircularProgress, Button } from '../../../node_modules/@material-ui/core';
+import { TextField } from '~/components/text-field/text-field';
+import { PROJECT_FORM_NAME } from '~/store/project/project-action';
+import { PROJECT_NAME_VALIDATION, PROJECT_DESCRIPTION_VALIDATION } from '~/validators/validators';
+
+type CssRules = 'content' | 'actions' | 'buttonWrapper' | 'saveButton' | 'circularProgress';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+ content: {
+ display: 'flex',
+ flexDirection: 'column'
+ },
+ actions: {
+ margin: 0,
+ padding: `${theme.spacing.unit}px ${theme.spacing.unit * 3 - theme.spacing.unit / 2}px
+ ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`
+ },
+ buttonWrapper: {
+ position: 'relative'
+ },
+ saveButton: {
+ boxShadow: 'none'
+ },
+ circularProgress: {
+ position: 'absolute',
+ top: 0,
+ bottom: 0,
+ left: 0,
+ right: 0,
+ margin: 'auto'
+ }
+});
+
+interface DialogProjectDataProps {
+ open: boolean;
+ handleSubmit: any;
+ submitting: boolean;
+ invalid: boolean;
+ pristine: boolean;
+}
+
+interface DialogProjectActionProps {
+ handleClose: () => void;
+ onSubmit: (data: { name: string, description: string }) => void;
+}
+
+type DialogProjectProps = DialogProjectDataProps & DialogProjectActionProps & WithStyles<CssRules>;
+
+export const DialogProjectUpdate = compose(
+ reduxForm({ form: PROJECT_FORM_NAME }),
+ withStyles(styles))(
+
+ class DialogProjectUpdate extends React.Component<DialogProjectProps> {
+ render() {
+ const { handleSubmit, handleClose, onSubmit, open, classes, submitting, invalid, pristine } = this.props;
+ return <Dialog open={open}
+ onClose={handleClose}
+ fullWidth={true}
+ maxWidth='sm'
+ disableBackdropClick={true}
+ disableEscapeKeyDown={true}>
+ <form onSubmit={handleSubmit((data: any) => onSubmit(data))}>
+ <DialogTitle>Edit Collection</DialogTitle>
+ <DialogContent className={classes.content}>
+ <Field name='name'
+ disabled={submitting}
+ component={TextField}
+ validate={PROJECT_NAME_VALIDATION}
+ label="Project Name" />
+ <Field name='description'
+ disabled={submitting}
+ component={TextField}
+ validate={PROJECT_DESCRIPTION_VALIDATION}
+ label="Description - optional" />
+ </DialogContent>
+ <DialogActions className={classes.actions}>
+ <Button onClick={handleClose} color="primary"
+ disabled={submitting}>CANCEL</Button>
+ <div className={classes.buttonWrapper}>
+ <Button type="submit" className={classes.saveButton}
+ color="primary"
+ disabled={invalid || submitting || pristine}
+ variant="contained">
+ SAVE
+ </Button>
+ {submitting && <CircularProgress size={20} className={classes.circularProgress} />}
+ </div>
+ </DialogActions>
+ </form>
+ </Dialog>;
+ }
+ }
+ );
diff --git a/src/views-components/update-project-dialog/update-project-dialog.tsx b/src/views-components/update-project-dialog/update-project-dialog.tsx
new file mode 100644
index 0000000..c455842
--- /dev/null
+++ b/src/views-components/update-project-dialog/update-project-dialog.tsx
@@ -0,0 +1,42 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { connect } from "react-redux";
+import { Dispatch } from "redux";
+import { SubmissionError } from "redux-form";
+import { RootState } from "~/store/store";
+import { snackbarActions } from "~/store/snackbar/snackbar-actions";
+import { DialogProjectUpdate } from "../dialog-update/dialog-project-update";
+import { projectActions, updateProject } from "~/store/project/project-action";
+
+const mapStateToProps = (state: RootState) => ({
+ open: state.projects.updater.opened
+});
+
+const mapDispatchToProps = (dispatch: Dispatch) => ({
+ handleClose: () => {
+ dispatch(projectActions.CLOSE_PROJECT_UPDATER());
+ },
+ onSubmit: (data: { name: string, description: string }) => {
+ return dispatch<any>(editProject(data))
+ .catch((e: any) => {
+ if (e.errors) {
+ throw new SubmissionError({ name: e.errors.join("").includes("UniqueViolation") ? "CProject with this name already exists." : "" });
+ }
+ });
+ }
+});
+
+const editProject = (data: { name: string, description: string }) =>
+ (dispatch: Dispatch, getState: () => RootState) => {
+ const { uuid } = getState().projects.updater;
+ return dispatch<any>(updateProject(data)).then(() => {
+ dispatch(snackbarActions.OPEN_SNACKBAR({
+ message: "Project has been successfully updated.",
+ hideDuration: 2000
+ }));
+ });
+ };
+
+export const UpdateProjectDialog = connect(mapStateToProps, mapDispatchToProps)(DialogProjectUpdate);
diff --git a/src/views/collection-panel/collection-tag-form.tsx b/src/views/collection-panel/collection-tag-form.tsx
index 8f25404..83ad0ca 100644
--- a/src/views/collection-panel/collection-tag-form.tsx
+++ b/src/views/collection-panel/collection-tag-form.tsx
@@ -6,20 +6,15 @@ import * as React from 'react';
import { reduxForm, Field, reset } from 'redux-form';
import { compose, Dispatch } from 'redux';
import { ArvadosTheme } from '~/common/custom-theme';
-import { StyleRulesCallback, withStyles, WithStyles, TextField, Button, CircularProgress } from '@material-ui/core';
+import { StyleRulesCallback, withStyles, WithStyles, Button, CircularProgress, Grid } from '@material-ui/core';
import { TagProperty } from '~/models/tag';
+import { TextField } from '~/components/text-field/text-field';
import { createCollectionTag, COLLECTION_TAG_FORM_NAME } from '~/store/collection-panel/collection-panel-action';
import { TAG_VALUE_VALIDATION, TAG_KEY_VALIDATION } from '~/validators/validators';
-type CssRules = 'form' | 'textField' | 'buttonWrapper' | 'saveButton' | 'circularProgress';
+type CssRules = 'buttonWrapper' | 'saveButton' | 'circularProgress';
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
- form: {
- marginBottom: theme.spacing.unit * 4
- },
- textField: {
- marginRight: theme.spacing.unit
- },
buttonWrapper: {
position: 'relative',
display: 'inline-block'
@@ -47,14 +42,6 @@ interface CollectionTagFormActionProps {
handleSubmit: any;
}
-interface TextFieldProps {
- label: string;
- floatinglabeltext: string;
- className?: string;
- input?: string;
- meta?: any;
-}
-
type CollectionTagFormProps = CollectionTagFormDataProps & CollectionTagFormActionProps & WithStyles<CssRules>;
export const CollectionTagForm = compose(
@@ -67,52 +54,41 @@ export const CollectionTagForm = compose(
}),
withStyles(styles))(
- class CollectionTagForm extends React.Component<CollectionTagFormProps> {
+ class CollectionTagForm extends React.Component<CollectionTagFormProps> {
render() {
const { classes, submitting, pristine, invalid, handleSubmit } = this.props;
return (
- <form className={classes.form} onSubmit={handleSubmit}>
- <Field name="key"
- disabled={submitting}
- component={this.renderTextField}
- floatinglabeltext="Key"
- validate={TAG_KEY_VALIDATION}
- className={classes.textField}
- label="Key" />
- <Field name="value"
- disabled={submitting}
- component={this.renderTextField}
- floatinglabeltext="Value"
- validate={TAG_VALUE_VALIDATION}
- className={classes.textField}
- label="Value" />
- <div className={classes.buttonWrapper}>
- <Button type="submit" className={classes.saveButton}
- color="primary"
- size='small'
- disabled={invalid || submitting || pristine}
- variant="contained">
- ADD
- </Button>
- {submitting && <CircularProgress size={20} className={classes.circularProgress} />}
- </div>
+ <form onSubmit={handleSubmit}>
+ <Grid container justify="flex-start" alignItems="baseline" spacing={24}>
+ <Grid item xs={3} component={"span"}>
+ <Field name="key"
+ disabled={submitting}
+ component={TextField}
+ validate={TAG_KEY_VALIDATION}
+ label="Key" />
+ </Grid>
+ <Grid item xs={5} component={"span"}>
+ <Field name="value"
+ disabled={submitting}
+ component={TextField}
+ validate={TAG_VALUE_VALIDATION}
+ label="Value" />
+ </Grid>
+ <Grid item component={"span"} className={classes.buttonWrapper}>
+ <Button type="submit" className={classes.saveButton}
+ color="primary"
+ size='small'
+ disabled={invalid || submitting || pristine}
+ variant="contained">
+ ADD
+ </Button>
+ {submitting && <CircularProgress size={20} className={classes.circularProgress} />}
+ </Grid>
+ </Grid>
</form>
);
}
-
- renderTextField = ({ input, label, meta: { touched, error }, ...custom }: TextFieldProps) => (
- <TextField
- helperText={touched && error}
- label={label}
- className={this.props.classes.textField}
- error={touched && !!error}
- autoComplete='off'
- {...input}
- {...custom}
- />
- )
-
}
);
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index a0be372..a38afb7 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -42,6 +42,7 @@ import { CollectionPanel } from '../collection-panel/collection-panel';
import { loadCollection, loadCollectionTags } from '~/store/collection-panel/collection-panel-action';
import { getCollectionUrl } from '~/models/collection';
import { UpdateCollectionDialog } from '~/views-components/update-collection-dialog/update-collection-dialog.';
+import { UpdateProjectDialog } from '~/views-components/update-project-dialog/update-project-dialog';
import { AuthService } from "~/services/auth-service/auth-service";
import { RenameFileDialog } from '~/views-components/rename-file-dialog/rename-file-dialog';
import { FileRemoveDialog } from '~/views-components/file-remove-dialog/file-remove-dialog';
@@ -245,6 +246,7 @@ export const Workbench = withStyles(styles)(
<FileRemoveDialog />
<MultipleFilesRemoveDialog />
<UpdateCollectionDialog />
+ <UpdateProjectDialog />
<CurrentTokenDialog
currentToken={this.props.currentToken}
open={this.state.isCurrentTokenDialogOpen}
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list