name: string;
groupClass: GroupClass | null;
description: string;
- properties: string;
+ properties: any;
writeableBy: string[];
}
// SPDX-License-Identifier: AGPL-3.0
import { Dispatch } from "redux";
-import { reset, startSubmit, stopSubmit, initialize, FormErrors } from 'redux-form';
+import { reset, startSubmit, stopSubmit, initialize, FormErrors, formValueSelector, change } from 'redux-form';
import { RootState } from '~/store/store';
import { dialogActions } from "~/store/dialog/dialog-actions";
import { getCommonResourceServiceError, CommonResourceServiceError } from '~/services/common-service/common-resource-service';
import { ProjectResource } from '~/models/project';
import { ServiceRepository } from '~/services/services';
import { matchProjectRoute, matchRunProcessRoute } from '~/routes/routes';
+import { ResourcePropertiesFormData } from '~/views-components/resource-properties-form/resource-properties-form';
export interface ProjectCreateFormDialogData {
ownerUuid: string;
name: string;
description: string;
+ properties: ProjectProperties;
+}
+
+export interface ProjectProperties {
+ [key: string]: string;
}
export const PROJECT_CREATE_FORM_NAME = 'projectCreateFormName';
+export const PROJECT_CREATE_PROPERTIES_FORM_NAME = 'projectCreatePropertiesFormName';
+export const CREATE_FORM_SELECTOR = formValueSelector(PROJECT_CREATE_FORM_NAME);
export const isProjectOrRunProcessRoute = ({ router }: RootState) => {
const pathname = router.location ? router.location.pathname : '';
return undefined;
}
};
+
+export const addPropertyToCreateProjectForm = (data: ResourcePropertiesFormData) =>
+ (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const properties = { ...CREATE_FORM_SELECTOR(getState(), 'properties') };
+ properties[data.key] = data.value;
+ dispatch(change(PROJECT_CREATE_FORM_NAME, 'properties', properties ));
+ };
+
+export const removePropertyFromCreateProjectForm = (key: string) =>
+ (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const properties = { ...CREATE_FORM_SELECTOR(getState(), 'properties') };
+ delete properties[key];
+ dispatch(change(PROJECT_CREATE_FORM_NAME, 'properties', properties ));
+ };
\ No newline at end of file
import { ProjectCreateFormDialogData } from '~/store/projects/project-create-actions';
import { FormDialog } from '~/components/form-dialog/form-dialog';
import { ProjectNameField, ProjectDescriptionField } from '~/views-components/form-fields/project-form-fields';
+import { CreateProjectPropertiesForm } from '~/views-components/project-properties/create-project-properties-form';
+import { CreateProjectPropertiesList } from '~/views-components/project-properties/create-project-properties-list';
-type DialogCollectionProps = WithDialogProps<{}> & InjectedFormProps<ProjectCreateFormDialogData>;
+type DialogProjectProps = WithDialogProps<{}> & InjectedFormProps<ProjectCreateFormDialogData>;
-export const DialogProjectCreate = (props: DialogCollectionProps) =>
+export const DialogProjectCreate = (props: DialogProjectProps) =>
<FormDialog
dialogTitle='New project'
formFields={ProjectAddFields}
const ProjectAddFields = () => <span>
<ProjectNameField />
<ProjectDescriptionField />
+ <CreateProjectPropertiesForm />
+ <CreateProjectPropertiesList />
</span>;
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { reduxForm, reset, InjectedFormProps } from 'redux-form';
+import { PROJECT_CREATE_PROPERTIES_FORM_NAME, addPropertyToCreateProjectForm } from '~/store/projects/project-create-actions';
+import { ResourcePropertiesFormData } from '~/views-components/resource-properties-form/resource-properties-form';
+import { StyleRulesCallback, WithStyles, withStyles, Grid } from '@material-ui/core';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { PropertyKeyField } from '~/views-components/resource-properties-form/property-key-field';
+import { PropertyValueField } from '~/views-components/resource-properties-form/property-value-field';
+import { Button } from '~/views-components/resource-properties-form/resource-properties-form';
+
+type CssRules = 'root';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+ root: {
+ paddingTop: theme.spacing.unit,
+ margin: 0
+ }
+});
+
+type CreateProjectPropertiesFormProps = InjectedFormProps<ResourcePropertiesFormData> & WithStyles<CssRules>;
+
+const Form = withStyles(styles)(
+ ({ handleSubmit, submitting, invalid, classes }: CreateProjectPropertiesFormProps) =>
+ <Grid container spacing={16} className={classes.root}>
+ <Grid item xs={5}>
+ <PropertyKeyField />
+ </Grid>
+ <Grid item xs={5}>
+ <PropertyValueField />
+ </Grid>
+ <Grid item xs={2}>
+ <Button
+ disabled={invalid}
+ loading={submitting}
+ color='primary'
+ variant='contained'
+ onClick={handleSubmit}>
+ Add
+ </Button>
+ </Grid>
+ </Grid>
+);
+
+export const CreateProjectPropertiesForm = reduxForm<ResourcePropertiesFormData>({
+ form: PROJECT_CREATE_PROPERTIES_FORM_NAME,
+ onSubmit: (data, dispatch) => {
+ dispatch(addPropertyToCreateProjectForm(data));
+ dispatch(reset(PROJECT_CREATE_PROPERTIES_FORM_NAME));
+ }
+})(Form);
\ No newline at end of file
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { connect } from 'react-redux';
+import { Dispatch } from 'redux';
+import { withStyles, StyleRulesCallback, WithStyles, Chip } from '@material-ui/core';
+import { RootState } from '~/store/store';
+import { removePropertyFromCreateProjectForm, CREATE_FORM_SELECTOR, ProjectProperties } from '~/store/projects/project-create-actions';
+import { ArvadosTheme } from '~/common/custom-theme';
+
+type CssRules = 'tag';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+ tag: {
+ marginRight: theme.spacing.unit,
+ marginBottom: theme.spacing.unit
+ }
+});
+
+interface CreateProjectPropertiesListDataProps {
+ properties: ProjectProperties;
+}
+
+interface CreateProjectPropertiesListActionProps {
+ handleDelete: (key: string) => void;
+}
+
+const mapStateToProps = (state: RootState): CreateProjectPropertiesListDataProps => {
+ const properties = CREATE_FORM_SELECTOR(state, 'properties');
+ return { properties };
+};
+
+const mapDispatchToProps = (dispatch: Dispatch): CreateProjectPropertiesListActionProps => ({
+ handleDelete: (key: string) => dispatch<any>(removePropertyFromCreateProjectForm(key))
+});
+
+type CreateProjectPropertiesListProps = CreateProjectPropertiesListDataProps &
+ CreateProjectPropertiesListActionProps & WithStyles<CssRules>;
+
+const List = withStyles(styles)(
+ ({ classes, handleDelete, properties }: CreateProjectPropertiesListProps) =>
+ <div>
+ {properties &&
+ Object.keys(properties).map(k => {
+ return <Chip key={k} className={classes.tag}
+ onDelete={() => handleDelete(k)}
+ label={`${k}: ${properties[k]}`} />;
+ })}
+ </div>
+);
+
+export const CreateProjectPropertiesList = connect(mapStateToProps, mapDispatchToProps)(List);
\ No newline at end of file
</Grid>
</form>;
-const Button = withStyles(theme => ({
+export const Button = withStyles(theme => ({
root: { marginTop: theme.spacing.unit }
}))(ProgressButton);