From e3d13624759c128c326b64e9e0e60996bd28aadf Mon Sep 17 00:00:00 2001 From: Pawel Kowalczyk Date: Tue, 20 Nov 2018 15:46:19 +0100 Subject: [PATCH] added-attributes-dialog-and-init-creating-repos Feature #13865 Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk --- src/components/icon/icon.tsx | 2 + src/models/repositories.ts | 2 +- .../repositories-service.ts | 4 +- src/store/advanced-tab/advanced-tab.ts | 4 +- .../context-menu/context-menu-actions.ts | 4 +- .../repositories/repositories-actions.ts | 25 +++++- .../repositories/repositories-reducer.ts | 4 +- src/validators/validators.tsx | 4 +- .../action-sets/repository-action-set.ts | 7 +- .../dialog-repository-create.tsx | 23 +++++ .../dialog-forms/create-repository-dialog.ts | 19 ++++ .../form-fields/repository-form-fields.tsx | 20 +++++ .../repositories-sample-git-dialog.tsx | 4 +- .../repository-attributes-dialog.tsx | 89 +++++++++++++++++++ .../repositories-panel/repositories-panel.tsx | 19 ++-- src/views/workbench/workbench.tsx | 4 + 16 files changed, 208 insertions(+), 26 deletions(-) create mode 100644 src/views-components/dialog-create/dialog-repository-create.tsx create mode 100644 src/views-components/dialog-forms/create-repository-dialog.ts create mode 100644 src/views-components/form-fields/repository-form-fields.tsx create mode 100644 src/views-components/repository-attributes-dialog/repository-attributes-dialog.tsx diff --git a/src/components/icon/icon.tsx b/src/components/icon/icon.tsx index a0fbd6ef..b46195de 100644 --- a/src/components/icon/icon.tsx +++ b/src/components/icon/icon.tsx @@ -32,6 +32,7 @@ import Input from '@material-ui/icons/Input'; import InsertDriveFile from '@material-ui/icons/InsertDriveFile'; import LastPage from '@material-ui/icons/LastPage'; import LibraryBooks from '@material-ui/icons/LibraryBooks'; +import ListAlt from '@material-ui/icons/ListAlt'; import Menu from '@material-ui/icons/Menu'; import MoreVert from '@material-ui/icons/MoreVert'; import Mail from '@material-ui/icons/Mail'; @@ -55,6 +56,7 @@ export type IconType = React.SFC<{ className?: string, style?: object }>; export const AddIcon: IconType = (props) => ; export const AddFavoriteIcon: IconType = (props) => ; export const AdvancedIcon: IconType = (props) => ; +export const AttributesIcon: IconType = (props) => ; export const BackIcon: IconType = (props) => ; export const CustomizeTableIcon: IconType = (props) => ; export const CommandIcon: IconType = (props) => ; diff --git a/src/models/repositories.ts b/src/models/repositories.ts index 63494bc6..02b99beb 100644 --- a/src/models/repositories.ts +++ b/src/models/repositories.ts @@ -4,7 +4,7 @@ import { Resource } from "~/models/resource"; -export interface RepositoriesResource extends Resource { +export interface RepositoryResource extends Resource { name: string; cloneUrls: string[]; } diff --git a/src/services/repositories-service/repositories-service.ts b/src/services/repositories-service/repositories-service.ts index 4bc66020..34f7f3f5 100644 --- a/src/services/repositories-service/repositories-service.ts +++ b/src/services/repositories-service/repositories-service.ts @@ -4,10 +4,10 @@ import { AxiosInstance } from "axios"; import { CommonResourceService } from "~/services/common-service/common-resource-service"; -import { RepositoriesResource } from '~/models/repositories'; +import { RepositoryResource } from '~/models/repositories'; import { ApiActions } from '~/services/api/api-actions'; - export class RepositoriesService extends CommonResourceService { + export class RepositoriesService extends CommonResourceService { constructor(serverApi: AxiosInstance, actions: ApiActions) { super(serverApi, "repositories", actions); } diff --git a/src/store/advanced-tab/advanced-tab.ts b/src/store/advanced-tab/advanced-tab.ts index d845c468..6ad8af22 100644 --- a/src/store/advanced-tab/advanced-tab.ts +++ b/src/store/advanced-tab/advanced-tab.ts @@ -14,7 +14,7 @@ import { CollectionResource } from '~/models/collection'; import { ProjectResource } from '~/models/project'; import { ServiceRepository } from '~/services/services'; import { FilterBuilder } from '~/services/api/filter-builder'; -import { RepositoriesResource } from '~/models/repositories'; +import { RepositoryResource } from '~/models/repositories'; export const ADVANCED_TAB_DIALOG = 'advancedTabDialog'; @@ -243,7 +243,7 @@ const groupRequestApiResponse = (apiResponse: ProjectResource) => { return response; }; -const repositoryApiResponse = (apiResponse: RepositoriesResource) => { +const repositoryApiResponse = (apiResponse: RepositoryResource) => { const { uuid, ownerUuid, createdAt, modifiedAt, modifiedByClientUuid, modifiedByUserUuid, name } = apiResponse; const response = `"uuid": "${uuid}", "owner_uuid": "${ownerUuid}", diff --git a/src/store/context-menu/context-menu-actions.ts b/src/store/context-menu/context-menu-actions.ts index 5a7a6018..596ac87b 100644 --- a/src/store/context-menu/context-menu-actions.ts +++ b/src/store/context-menu/context-menu-actions.ts @@ -13,7 +13,7 @@ import { UserResource } from '~/models/user'; import { isSidePanelTreeCategory } from '~/store/side-panel-tree/side-panel-tree-actions'; import { extractUuidKind, ResourceKind } from '~/models/resource'; import { Process } from '~/store/processes/process'; -import { RepositoriesResource } from '~/models/repositories'; +import { RepositoryResource } from '~/models/repositories'; export const contextMenuActions = unionize({ OPEN_CONTEXT_MENU: ofType<{ position: ContextMenuPosition, resource: ContextMenuResource }>(), @@ -61,7 +61,7 @@ export const openCollectionFilesContextMenu = (event: React.MouseEvent, index: number, repository: RepositoriesResource) => +export const openRepositoryContextMenu = (event: React.MouseEvent, index: number, repository: RepositoryResource) => (dispatch: Dispatch, getState: () => RootState) => { dispatch(openContextMenu(event, { name: '', diff --git a/src/store/repositories/repositories-actions.ts b/src/store/repositories/repositories-actions.ts index 2660f7ac..2330f7b4 100644 --- a/src/store/repositories/repositories-actions.ts +++ b/src/store/repositories/repositories-actions.ts @@ -9,6 +9,7 @@ import { ServiceRepository } from "~/services/services"; import { navigateToRepositories } from "~/store/navigation/navigation-action"; import { unionize, ofType, UnionOf } from "~/common/unionize"; import { dialogActions } from '~/store/dialog/dialog-actions'; +import { RepositoryResource } from "~/models/repositories"; export const repositoriesActions = unionize({ SET_REPOSITORIES: ofType(), @@ -17,12 +18,32 @@ export const repositoriesActions = unionize({ export type RepositoriesActions = UnionOf; export const REPOSITORIES_PANEL = 'repositoriesPanel'; -export const REPOSITORIES_SAMPLE_GIT_NAME = 'repositoriesSampleGit'; +export const REPOSITORIES_SAMPLE_GIT_DIALOG = 'repositoriesSampleGitDialog'; +export const REPOSITORY_ATTRIBUTES_DIALOG = 'repositoryAttributesDialog'; +export const REPOSITORY_CREATE_FORM_NAME = 'repositoryCreateFormName'; export const openRepositoriesSampleGitDialog = () => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { const uuidPrefix = getState().properties.uuidPrefix; - dispatch(dialogActions.OPEN_DIALOG({ id: REPOSITORIES_SAMPLE_GIT_NAME, data: { uuidPrefix } })); + dispatch(dialogActions.OPEN_DIALOG({ id: REPOSITORIES_SAMPLE_GIT_DIALOG, data: { uuidPrefix } })); + }; + +export const openRepositoryAttributes = (index: number) => + (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + const repositoryData = getState().repositories.items[index]; + dispatch(dialogActions.OPEN_DIALOG({ id: REPOSITORY_ATTRIBUTES_DIALOG, data: { repositoryData } })); + }; + +export const openRepositoryCreateDialog = () => + async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + dispatch(dialogActions.OPEN_DIALOG({ id: REPOSITORY_CREATE_FORM_NAME, data: {} })); + }; + +export const createRepository = (repository: RepositoryResource) => + async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + const userUuid = await services.authService.getUuid(); + const user = await services.userService.get(userUuid!); + await services.repositoriesService.create({ name: `${user.username}/${repository.name}` }); }; const repositoriesBindedActions = bindDataExplorerActions(REPOSITORIES_PANEL); diff --git a/src/store/repositories/repositories-reducer.ts b/src/store/repositories/repositories-reducer.ts index f277c314..3ef82896 100644 --- a/src/store/repositories/repositories-reducer.ts +++ b/src/store/repositories/repositories-reducer.ts @@ -3,10 +3,10 @@ // SPDX-License-Identifier: AGPL-3.0 import { repositoriesActions, RepositoriesActions } from '~/store/repositories/repositories-actions'; -import { RepositoriesResource } from '~/models/repositories'; +import { RepositoryResource } from '~/models/repositories'; interface Repositories { - items: RepositoriesResource[]; + items: RepositoryResource[]; } const initialState: Repositories = { diff --git a/src/validators/validators.tsx b/src/validators/validators.tsx index edc47265..37c1bd37 100644 --- a/src/validators/validators.tsx +++ b/src/validators/validators.tsx @@ -19,4 +19,6 @@ export const COPY_FILE_VALIDATION = [require]; export const MOVE_TO_VALIDATION = [require]; -export const PROCESS_NAME_VALIDATION = [require, maxLength(255)]; \ No newline at end of file +export const PROCESS_NAME_VALIDATION = [require, maxLength(255)]; + +export const REPOSITORY_NAME_VALIDATION = [require, maxLength(255)]; \ No newline at end of file diff --git a/src/views-components/context-menu/action-sets/repository-action-set.ts b/src/views-components/context-menu/action-sets/repository-action-set.ts index 1332a67b..2341f9ac 100644 --- a/src/views-components/context-menu/action-sets/repository-action-set.ts +++ b/src/views-components/context-menu/action-sets/repository-action-set.ts @@ -3,15 +3,16 @@ // SPDX-License-Identifier: AGPL-3.0 import { ContextMenuActionSet } from "~/views-components/context-menu/context-menu-action-set"; -import { AdvancedIcon, RemoveIcon, ShareIcon } from "~/components/icon/icon"; +import { AdvancedIcon, RemoveIcon, ShareIcon, AttributesIcon } from "~/components/icon/icon"; import { openFileRemoveDialog, openRenameFileDialog } from '~/store/collection-panel/collection-panel-files/collection-panel-files-actions'; import { openAdvancedTabDialog } from "~/store/advanced-tab/advanced-tab"; +import { openRepositoryAttributes } from "~/store/repositories/repositories-actions"; export const repositoryActionSet: ContextMenuActionSet = [[{ name: "Attributes", - icon: AdvancedIcon, + icon: AttributesIcon, execute: (dispatch, resource) => { - dispatch(openRenameFileDialog({ name: resource.name, id: resource.uuid })); + dispatch(openRepositoryAttributes(resource.index!)); } }, { name: "Share", diff --git a/src/views-components/dialog-create/dialog-repository-create.tsx b/src/views-components/dialog-create/dialog-repository-create.tsx new file mode 100644 index 00000000..465f622e --- /dev/null +++ b/src/views-components/dialog-create/dialog-repository-create.tsx @@ -0,0 +1,23 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import * as React from 'react'; +import { InjectedFormProps } from 'redux-form'; +import { WithDialogProps } from '~/store/dialog/with-dialog'; +import { FormDialog } from '~/components/form-dialog/form-dialog'; +import { RepositoryNameField } from '~/views-components/form-fields/repository-form-fields'; + +type DialogRepositoryProps = WithDialogProps<{}> & InjectedFormProps; + +export const DialogRepositoryCreate = (props: DialogRepositoryProps) => + ; + +const RepositoryAddField = () => + ; + diff --git a/src/views-components/dialog-forms/create-repository-dialog.ts b/src/views-components/dialog-forms/create-repository-dialog.ts new file mode 100644 index 00000000..04a13c0f --- /dev/null +++ b/src/views-components/dialog-forms/create-repository-dialog.ts @@ -0,0 +1,19 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { compose } from "redux"; +import { reduxForm } from 'redux-form'; +import { withDialog } from "~/store/dialog/with-dialog"; +import { createRepository, REPOSITORY_CREATE_FORM_NAME } from "~/store/repositories/repositories-actions"; +import { DialogRepositoryCreate } from "~/views-components/dialog-create/dialog-repository-create"; + +export const CreateRepositoryDialog = compose( + withDialog(REPOSITORY_CREATE_FORM_NAME), + reduxForm({ + form: REPOSITORY_CREATE_FORM_NAME, + onSubmit: (repositoryName, dispatch) => { + dispatch(createRepository(repositoryName)); + } + }) +)(DialogRepositoryCreate); \ No newline at end of file diff --git a/src/views-components/form-fields/repository-form-fields.tsx b/src/views-components/form-fields/repository-form-fields.tsx new file mode 100644 index 00000000..56f1c1b2 --- /dev/null +++ b/src/views-components/form-fields/repository-form-fields.tsx @@ -0,0 +1,20 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import * as React from "react"; +import { Field } from "redux-form"; +import { TextField } from "~/components/text-field/text-field"; +import { REPOSITORY_NAME_VALIDATION } from "~/validators/validators"; + +export const RepositoryNameField = () => + + pawelkowalczyk/ + .git
+ It may take a minute or two before you can clone your new repository. +
; \ No newline at end of file diff --git a/src/views-components/repositories-sample-git-dialog/repositories-sample-git-dialog.tsx b/src/views-components/repositories-sample-git-dialog/repositories-sample-git-dialog.tsx index 0941b53e..1a00e977 100644 --- a/src/views-components/repositories-sample-git-dialog/repositories-sample-git-dialog.tsx +++ b/src/views-components/repositories-sample-git-dialog/repositories-sample-git-dialog.tsx @@ -6,7 +6,7 @@ import * as React from "react"; import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Typography } from "@material-ui/core"; import { WithDialogProps } from "~/store/dialog/with-dialog"; import { withDialog } from '~/store/dialog/with-dialog'; -import { REPOSITORIES_SAMPLE_GIT_NAME } from "~/store/repositories/repositories-actions"; +import { REPOSITORIES_SAMPLE_GIT_DIALOG } from "~/store/repositories/repositories-actions"; import { DefaultCodeSnippet } from '~/components/default-code-snippet/default-code-snippet'; import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles'; import { ArvadosTheme } from '~/common/custom-theme'; @@ -40,7 +40,7 @@ interface RepositoriesSampleGitDataProps { type RepositoriesSampleGitProps = RepositoriesSampleGitDataProps & WithStyles; export const RepositoriesSampleGitDialog = compose( - withDialog(REPOSITORIES_SAMPLE_GIT_NAME), + withDialog(REPOSITORIES_SAMPLE_GIT_DIALOG), withStyles(styles))( (props: WithDialogProps & RepositoriesSampleGitProps) => = (theme: ArvadosTheme) => ({ + rightContainer: { + textAlign: 'right', + paddingRight: theme.spacing.unit * 2, + color: theme.palette.grey["500"] + }, + leftContainer: { + textAlign: 'left', + paddingLeft: theme.spacing.unit * 2 + }, + spacing: { + paddingTop: theme.spacing.unit * 2 + }, +}); + +interface RepositoryAttributesDataProps { + repositoryData: RepositoryResource; +} + +type RepositoryAttributesProps = RepositoryAttributesDataProps & WithStyles; + +export const RepositoryAttributesDialog = compose( + withDialog(REPOSITORY_ATTRIBUTES_DIALOG), + withStyles(styles))( + (props: WithDialogProps & RepositoryAttributesProps) => + + Attributes + + + {props.data.repositoryData && attributes(props.data.repositoryData, props.classes)} + + + + + + + ); + +const attributes = (repositoryData: RepositoryResource, classes: any) => { + const { uuid, ownerUuid, createdAt, modifiedAt, modifiedByClientUuid, modifiedByUserUuid, name } = repositoryData; + return ( + + + + Name + Owner uuid + Created at + Modified at + Modified by user uuid + Modified by client uuid + uuid + + + {name} + {ownerUuid} + {createdAt} + {modifiedAt} + {modifiedByUserUuid} + {modifiedByClientUuid} + {uuid} + + + + ); +}; diff --git a/src/views/repositories-panel/repositories-panel.tsx b/src/views/repositories-panel/repositories-panel.tsx index cafbefd2..262f3cc3 100644 --- a/src/views/repositories-panel/repositories-panel.tsx +++ b/src/views/repositories-panel/repositories-panel.tsx @@ -11,8 +11,8 @@ import { Link } from 'react-router-dom'; import { Dispatch, compose } from 'redux'; import { RootState } from '~/store/store'; import { HelpIcon, AddIcon, MoreOptionsIcon } from '~/components/icon/icon'; -import { loadRepositoriesData, openRepositoriesSampleGitDialog } from '~/store/repositories/repositories-actions'; -import { RepositoriesResource } from '~/models/repositories'; +import { loadRepositoriesData, openRepositoriesSampleGitDialog, openRepositoryCreateDialog } from '~/store/repositories/repositories-actions'; +import { RepositoryResource } from '~/models/repositories'; import { openRepositoryContextMenu } from '~/store/context-menu/context-menu-actions'; @@ -63,22 +63,24 @@ const mapStateToProps = (state: RootState) => { }; }; -const mapDispatchToProps = (dispatch: Dispatch): Pick => ({ +const mapDispatchToProps = (dispatch: Dispatch): Pick => ({ loadRepositories: () => dispatch(loadRepositoriesData()), onOptionsMenuOpen: (event, index, repository) => { dispatch(openRepositoryContextMenu(event, index, repository)); }, - openRepositoriesSampleGitDialog: () => dispatch(openRepositoriesSampleGitDialog()) + openRepositoriesSampleGitDialog: () => dispatch(openRepositoriesSampleGitDialog()), + openRepositoryCreateDialog: () => dispatch(openRepositoryCreateDialog()) }); interface RepositoriesActionProps { loadRepositories: () => void; - onOptionsMenuOpen: (event: React.MouseEvent, index: number, repository: RepositoriesResource) => void; + onOptionsMenuOpen: (event: React.MouseEvent, index: number, repository: RepositoryResource) => void; openRepositoriesSampleGitDialog: () => void; + openRepositoryCreateDialog: () => void; } interface RepositoriesDataProps { - repositories: RepositoriesResource[]; + repositories: RepositoryResource[]; } @@ -92,8 +94,7 @@ export const RepositoriesPanel = compose( this.props.loadRepositories(); } render() { - const { classes, repositories, onOptionsMenuOpen, openRepositoriesSampleGitDialog } = this.props; - console.log(repositories); + const { classes, repositories, onOptionsMenuOpen, openRepositoriesSampleGitDialog, openRepositoryCreateDialog } = this.props; return ( @@ -105,7 +106,7 @@ export const RepositoriesPanel = compose( - diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx index c9bcd91b..cefd0dc6 100644 --- a/src/views/workbench/workbench.tsx +++ b/src/views/workbench/workbench.tsx @@ -50,6 +50,8 @@ import { ProcessInputDialog } from '~/views-components/process-input-dialog/proc import { ProjectPropertiesDialog } from '~/views-components/project-properties-dialog/project-properties-dialog'; import { RepositoriesPanel } from '~/views/repositories-panel/repositories-panel'; import { RepositoriesSampleGitDialog } from '~/views-components/repositories-sample-git-dialog/repositories-sample-git-dialog'; +import { RepositoryAttributesDialog } from '~/views-components/repository-attributes-dialog/repository-attributes-dialog'; +import { CreateRepositoryDialog } from '~/views-components/dialog-forms/create-repository-dialog'; type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content'; @@ -135,6 +137,7 @@ export const WorkbenchPanel = + @@ -148,6 +151,7 @@ export const WorkbenchPanel = + -- 2.30.2