[ARVADOS-WORKBENCH2] created: 1.2.0-937-g3a3de86
Git user
git at public.curoverse.com
Sat Nov 24 16:25:08 EST 2018
at 3a3de86b86ef60fc86f1190d42bc8a2471ab5276 (commit)
commit 3a3de86b86ef60fc86f1190d42bc8a2471ab5276
Author: Janicki Artur <artur.janicki at contractors.roche.com>
Date: Sat Nov 24 22:24:54 2018 +0100
add table view, actions and dialogs
Feature #14528_table_view_and_actions
Arvados-DCO-1.1-Signed-off-by: Janicki Artur <artur.janicki at contractors.roche.com>
diff --git a/src/components/icon/icon.tsx b/src/components/icon/icon.tsx
index b46195d..a0f58be 100644
--- a/src/components/icon/icon.tsx
+++ b/src/components/icon/icon.tsx
@@ -50,6 +50,7 @@ import SettingsEthernet from '@material-ui/icons/SettingsEthernet';
import Star from '@material-ui/icons/Star';
import StarBorder from '@material-ui/icons/StarBorder';
import Warning from '@material-ui/icons/Warning';
+import VpnKey from '@material-ui/icons/VpnKey';
export type IconType = React.SFC<{ className?: string, style?: object }>;
@@ -74,6 +75,7 @@ export const HelpIcon: IconType = (props) => <Help {...props} />;
export const HelpOutlineIcon: IconType = (props) => <HelpOutline {...props} />;
export const ImportContactsIcon: IconType = (props) => <ImportContacts {...props} />;
export const InputIcon: IconType = (props) => <InsertDriveFile {...props} />;
+export const KeyIcon: IconType = (props) => <VpnKey {...props} />;
export const LogIcon: IconType = (props) => <SettingsEthernet {...props} />;
export const MailIcon: IconType = (props) => <Mail {...props} />;
export const MoreOptionsIcon: IconType = (props) => <MoreVert {...props} />;
diff --git a/src/index.tsx b/src/index.tsx
index 922720a..88fd229 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -49,6 +49,7 @@ import { DragDropContextProvider } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import { initAdvanceFormProjectsTree } from '~/store/search-bar/search-bar-actions';
import { repositoryActionSet } from '~/views-components/context-menu/action-sets/repository-action-set';
+import { sshKeyActionSet } from '~/views-components/context-menu/action-sets/ssh-key-action-set';
console.log(`Starting arvados [${getBuildInfo()}]`);
@@ -66,6 +67,7 @@ addMenuActionSet(ContextMenuKind.PROCESS, processActionSet);
addMenuActionSet(ContextMenuKind.PROCESS_RESOURCE, processResourceActionSet);
addMenuActionSet(ContextMenuKind.TRASH, trashActionSet);
addMenuActionSet(ContextMenuKind.REPOSITORY, repositoryActionSet);
+addMenuActionSet(ContextMenuKind.SSH_KEY, sshKeyActionSet);
fetchConfig()
.then(({ config, apiHost }) => {
diff --git a/src/models/resource.ts b/src/models/resource.ts
index 520520f..5fa6179 100644
--- a/src/models/resource.ts
+++ b/src/models/resource.ts
@@ -29,6 +29,7 @@ export enum ResourceKind {
PROCESS = "arvados#containerRequest",
PROJECT = "arvados#group",
REPOSITORY = "arvados#repository",
+ SSH_KEY = "arvados#authorizedKeys",
USER = "arvados#user",
WORKFLOW = "arvados#workflow",
NONE = "arvados#none"
diff --git a/src/store/auth/auth-action.ts b/src/store/auth/auth-action.ts
index 3658c58..28559b1 100644
--- a/src/store/auth/auth-action.ts
+++ b/src/store/auth/auth-action.ts
@@ -4,7 +4,7 @@
import { ofType, unionize, UnionOf } from '~/common/unionize';
import { Dispatch } from "redux";
-import { reset, stopSubmit } from 'redux-form';
+import { reset, stopSubmit, startSubmit } from 'redux-form';
import { AxiosInstance } from "axios";
import { RootState } from "../store";
import { snackbarActions } from '~/store/snackbar/snackbar-actions';
@@ -23,10 +23,14 @@ export const authActions = unionize({
USER_DETAILS_REQUEST: {},
USER_DETAILS_SUCCESS: ofType<User>(),
SET_SSH_KEYS: ofType<SshKeyResource[]>(),
- ADD_SSH_KEY: ofType<SshKeyResource>()
+ ADD_SSH_KEY: ofType<SshKeyResource>(),
+ REMOVE_SSH_KEY: ofType<string>()
});
export const SSH_KEY_CREATE_FORM_NAME = 'sshKeyCreateFormName';
+export const SSH_KEY_PUBLIC_KEY_DIALOG = 'sshKeyPublicKeyDialog';
+export const SSH_KEY_REMOVE_DIALOG = 'sshKeyRemoveDialog';
+export const SSH_KEY_ATTRIBUTES_DIALOG = 'sshKeyAttributesDialog';
export interface SshKeyCreateFormDialogData {
publicKey: string;
@@ -87,20 +91,51 @@ export const getUserDetails = () => (dispatch: Dispatch, getState: () => RootSta
export const openSshKeyCreateDialog = () => dialogActions.OPEN_DIALOG({ id: SSH_KEY_CREATE_FORM_NAME, data: {} });
+export const openPublicKeyDialog = (name: string, publicKey: string) =>
+ dialogActions.OPEN_DIALOG({ id: SSH_KEY_PUBLIC_KEY_DIALOG, data: { name, publicKey } });
+
+export const openSshKeyAttributesDialog = (index: number) =>
+ (dispatch: Dispatch, getState: () => RootState) => {
+ const sshKey = getState().auth.sshKeys[index];
+ dispatch(dialogActions.OPEN_DIALOG({ id: SSH_KEY_ATTRIBUTES_DIALOG, data: { sshKey } }));
+ };
+
+export const openSshKeyRemoveDialog = (uuid: string) =>
+ (dispatch: Dispatch, getState: () => RootState) => {
+ dispatch(dialogActions.OPEN_DIALOG({
+ id: SSH_KEY_REMOVE_DIALOG,
+ data: {
+ title: 'Remove public key',
+ text: 'Are you sure you want to remove this public key?',
+ confirmButtonLabel: 'Remove',
+ uuid
+ }
+ }));
+ };
+
+export const removeSshKey = (uuid: string) =>
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removing ...' }));
+ await services.authorizedKeysService.delete(uuid);
+ dispatch(authActions.REMOVE_SSH_KEY(uuid));
+ dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Public Key has been successfully removed.', hideDuration: 2000 }));
+ };
+
export const createSshKey = (data: SshKeyCreateFormDialogData) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const userUuid = getState().auth.user!.uuid;
+ const { name, publicKey } = data;
+ dispatch(startSubmit(SSH_KEY_CREATE_FORM_NAME));
try {
- const userUuid = getState().auth.user!.uuid;
- const { name, publicKey } = data;
const newSshKey = await services.authorizedKeysService.create({
- name,
+ name,
publicKey,
keyType: KeyType.SSH,
authorizedUserUuid: userUuid
});
+ dispatch(authActions.ADD_SSH_KEY(newSshKey));
dispatch(dialogActions.CLOSE_DIALOG({ id: SSH_KEY_CREATE_FORM_NAME }));
dispatch(reset(SSH_KEY_CREATE_FORM_NAME));
- dispatch(authActions.ADD_SSH_KEY(newSshKey));
dispatch(snackbarActions.OPEN_SNACKBAR({
message: "Public key has been successfully created.",
hideDuration: 2000
diff --git a/src/store/auth/auth-reducer.ts b/src/store/auth/auth-reducer.ts
index 8f234da..a8e4340 100644
--- a/src/store/auth/auth-reducer.ts
+++ b/src/store/auth/auth-reducer.ts
@@ -10,7 +10,7 @@ import { SshKeyResource } from '~/models/ssh-key';
export interface AuthState {
user?: User;
apiToken?: string;
- sshKeys?: SshKeyResource[];
+ sshKeys: SshKeyResource[];
}
const initialState: AuthState = {
@@ -19,13 +19,13 @@ const initialState: AuthState = {
sshKeys: []
};
-export const authReducer = (services: ServiceRepository) => (state: AuthState = initialState, action: AuthAction) => {
+export const authReducer = (services: ServiceRepository) => (state = initialState, action: AuthAction) => {
return authActions.match(action, {
SAVE_API_TOKEN: (token: string) => {
return {...state, apiToken: token};
},
INIT: ({ user, token }) => {
- return { user, apiToken: token };
+ return { ...state, user, apiToken: token };
},
LOGIN: () => {
return state;
@@ -40,7 +40,10 @@ export const authReducer = (services: ServiceRepository) => (state: AuthState =
return {...state, sshKeys};
},
ADD_SSH_KEY: (sshKey: SshKeyResource) => {
- return { ...state, sshKeys: state.sshKeys!.concat(sshKey) };
+ return { ...state, sshKeys: state.sshKeys.concat(sshKey) };
+ },
+ REMOVE_SSH_KEY: (uuid: string) => {
+ return { ...state, sshKeys: state.sshKeys.filter((sshKey) => sshKey.uuid !== uuid )};
},
default: () => state
});
diff --git a/src/store/context-menu/context-menu-actions.ts b/src/store/context-menu/context-menu-actions.ts
index 596ac87..0a6b5a8 100644
--- a/src/store/context-menu/context-menu-actions.ts
+++ b/src/store/context-menu/context-menu-actions.ts
@@ -14,6 +14,7 @@ import { isSidePanelTreeCategory } from '~/store/side-panel-tree/side-panel-tree
import { extractUuidKind, ResourceKind } from '~/models/resource';
import { Process } from '~/store/processes/process';
import { RepositoryResource } from '~/models/repositories';
+import { SshKeyResource } from '~/models/ssh-key';
export const contextMenuActions = unionize({
OPEN_CONTEXT_MENU: ofType<{ position: ContextMenuPosition, resource: ContextMenuResource }>(),
@@ -73,6 +74,18 @@ export const openRepositoryContextMenu = (event: React.MouseEvent<HTMLElement>,
}));
};
+export const openSshKeyContextMenu = (event: React.MouseEvent<HTMLElement>, index: number, sshKey: SshKeyResource) =>
+ (dispatch: Dispatch) => {
+ dispatch<any>(openContextMenu(event, {
+ name: '',
+ uuid: sshKey.uuid,
+ ownerUuid: sshKey.ownerUuid,
+ kind: ResourceKind.SSH_KEY,
+ menuKind: ContextMenuKind.SSH_KEY,
+ index
+ }));
+ };
+
export const openRootProjectContextMenu = (event: React.MouseEvent<HTMLElement>, projectUuid: string) =>
(dispatch: Dispatch, getState: () => RootState) => {
const res = getResource<UserResource>(projectUuid)(getState().resources);
diff --git a/src/views-components/context-menu/action-sets/ssh-key-action-set.ts b/src/views-components/context-menu/action-sets/ssh-key-action-set.ts
new file mode 100644
index 0000000..3fa2f16
--- /dev/null
+++ b/src/views-components/context-menu/action-sets/ssh-key-action-set.ts
@@ -0,0 +1,27 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { ContextMenuActionSet } from "~/views-components/context-menu/context-menu-action-set";
+import { AdvancedIcon, RemoveIcon, AttributesIcon } from "~/components/icon/icon";
+import { openSshKeyRemoveDialog, openSshKeyAttributesDialog } from '~/store/auth/auth-action';
+
+export const sshKeyActionSet: ContextMenuActionSet = [[{
+ name: "Attributes",
+ icon: AttributesIcon,
+ execute: (dispatch, { index }) => {
+ dispatch<any>(openSshKeyAttributesDialog(index!));
+ }
+}, {
+ name: "Advanced",
+ icon: AdvancedIcon,
+ execute: (dispatch, { uuid, index }) => {
+ // ToDo
+ }
+}, {
+ name: "Remove",
+ icon: RemoveIcon,
+ execute: (dispatch, { uuid }) => {
+ dispatch<any>(openSshKeyRemoveDialog(uuid));
+ }
+}]];
diff --git a/src/views-components/context-menu/context-menu.tsx b/src/views-components/context-menu/context-menu.tsx
index 30ecc98..af5aaa9 100644
--- a/src/views-components/context-menu/context-menu.tsx
+++ b/src/views-components/context-menu/context-menu.tsx
@@ -69,5 +69,6 @@ export enum ContextMenuKind {
PROCESS = "Process",
PROCESS_RESOURCE = 'ProcessResource',
PROCESS_LOGS = "ProcessLogs",
- REPOSITORY = "Repository"
+ REPOSITORY = "Repository",
+ SSH_KEY = "SshKey"
}
diff --git a/src/views-components/ssh-keys-dialog/attributes-dialog.tsx b/src/views-components/ssh-keys-dialog/attributes-dialog.tsx
new file mode 100644
index 0000000..0a2a681
--- /dev/null
+++ b/src/views-components/ssh-keys-dialog/attributes-dialog.tsx
@@ -0,0 +1,65 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from "react";
+import { compose } from 'redux';
+import { withStyles, Dialog, DialogTitle, DialogContent, DialogActions, Button, StyleRulesCallback, WithStyles, Typography, Grid } from '@material-ui/core';
+import { WithDialogProps, withDialog } from "~/store/dialog/with-dialog";
+import { SSH_KEY_ATTRIBUTES_DIALOG } from '~/store/auth/auth-action';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { SshKeyResource } from "~/models/ssh-key";
+
+type CssRules = 'root';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+ root: {
+ fontSize: '0.875rem',
+ '& div:nth-child(odd)': {
+ textAlign: 'right',
+ color: theme.palette.grey["500"]
+ }
+ }
+});
+
+interface AttributesSshKeyDialogDataProps {
+ sshKey: SshKeyResource;
+}
+
+export const AttributesSshKeyDialog = compose(
+ withDialog(SSH_KEY_ATTRIBUTES_DIALOG),
+ withStyles(styles))(
+ ({ open, closeDialog, data, classes }: WithDialogProps<AttributesSshKeyDialogDataProps> & WithStyles<CssRules>) =>
+ <Dialog open={open}
+ onClose={closeDialog}
+ fullWidth
+ maxWidth='sm'>
+ <DialogTitle>Attributes</DialogTitle>
+ <DialogContent>
+ {data.sshKey && <Grid container direction="row" spacing={16} className={classes.root}>
+ <Grid item xs={5}>Name</Grid>
+ <Grid item xs={7}>{data.sshKey.name}</Grid>
+ <Grid item xs={5}>Owner uuid</Grid>
+ <Grid item xs={7}>{data.sshKey.ownerUuid}</Grid>
+ <Grid item xs={5}>Created at</Grid>
+ <Grid item xs={7}>{data.sshKey.createdAt}</Grid>
+ <Grid item xs={5}>Modified at</Grid>
+ <Grid item xs={7}>{data.sshKey.modifiedAt}</Grid>
+ <Grid item xs={5}>Modified by user uuid</Grid>
+ <Grid item xs={7}>{data.sshKey.modifiedByUserUuid}</Grid>
+ <Grid item xs={5}>Modified by client uuid</Grid>
+ <Grid item xs={7}>{data.sshKey.modifiedByClientUuid}</Grid>
+ <Grid item xs={5}>uuid</Grid>
+ <Grid item xs={7}>{data.sshKey.uuid}</Grid>
+ </Grid>}
+ </DialogContent>
+ <DialogActions>
+ <Button
+ variant='flat'
+ color='primary'
+ onClick={closeDialog}>
+ Close
+ </Button>
+ </DialogActions>
+ </Dialog>
+ );
\ No newline at end of file
diff --git a/src/views-components/ssh-keys-dialog/public-key-dialog.tsx b/src/views-components/ssh-keys-dialog/public-key-dialog.tsx
new file mode 100644
index 0000000..77c6cfd
--- /dev/null
+++ b/src/views-components/ssh-keys-dialog/public-key-dialog.tsx
@@ -0,0 +1,55 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from "react";
+import { compose } from 'redux';
+import { withStyles, Dialog, DialogTitle, DialogContent, DialogActions, Button, StyleRulesCallback, WithStyles } from '@material-ui/core';
+import { WithDialogProps, withDialog } from "~/store/dialog/with-dialog";
+import { SSH_KEY_PUBLIC_KEY_DIALOG } from '~/store/auth/auth-action';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { DefaultCodeSnippet } from '~/components/default-code-snippet/default-code-snippet';
+
+type CssRules = 'codeSnippet';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+ codeSnippet: {
+ borderRadius: theme.spacing.unit * 0.5,
+ border: '1px solid',
+ borderColor: theme.palette.grey["400"],
+ '& pre': {
+ wordWrap: 'break-word',
+ whiteSpace: 'pre-wrap'
+ }
+ },
+});
+
+interface PublicKeyDialogDataProps {
+ name: string;
+ publicKey: string;
+}
+
+export const PublicKeyDialog = compose(
+ withDialog(SSH_KEY_PUBLIC_KEY_DIALOG),
+ withStyles(styles))(
+ ({ open, closeDialog, data, classes }: WithDialogProps<PublicKeyDialogDataProps> & WithStyles<CssRules>) =>
+ <Dialog open={open}
+ onClose={closeDialog}
+ fullWidth
+ maxWidth='sm'>
+ <DialogTitle>{data.name} - SSH Key</DialogTitle>
+ <DialogContent>
+ {data && data.publicKey && <DefaultCodeSnippet
+ className={classes.codeSnippet}
+ lines={data.publicKey.split(' ')} />}
+ </DialogContent>
+ <DialogActions>
+ <Button
+ variant='flat'
+ color='primary'
+ onClick={closeDialog}>
+ Close
+ </Button>
+ </DialogActions>
+ </Dialog>
+ );
\ No newline at end of file
diff --git a/src/views-components/ssh-keys-dialog/remove-dialog.tsx b/src/views-components/ssh-keys-dialog/remove-dialog.tsx
new file mode 100644
index 0000000..8077f21
--- /dev/null
+++ b/src/views-components/ssh-keys-dialog/remove-dialog.tsx
@@ -0,0 +1,20 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+import { Dispatch, compose } from 'redux';
+import { connect } from "react-redux";
+import { ConfirmationDialog } from "~/components/confirmation-dialog/confirmation-dialog";
+import { withDialog, WithDialogProps } from "~/store/dialog/with-dialog";
+import { SSH_KEY_REMOVE_DIALOG, removeSshKey } from '~/store/auth/auth-action';
+
+const mapDispatchToProps = (dispatch: Dispatch, props: WithDialogProps<any>) => ({
+ onConfirm: () => {
+ props.closeDialog();
+ dispatch<any>(removeSshKey(props.data.uuid));
+ }
+});
+
+export const RemoveSshKeyDialog = compose(
+ withDialog(SSH_KEY_REMOVE_DIALOG),
+ connect(null, mapDispatchToProps)
+)(ConfirmationDialog);
\ No newline at end of file
diff --git a/src/views/ssh-key-panel/ssh-key-panel-root.tsx b/src/views/ssh-key-panel/ssh-key-panel-root.tsx
index f752228..869662d 100644
--- a/src/views/ssh-key-panel/ssh-key-panel-root.tsx
+++ b/src/views/ssh-key-panel/ssh-key-panel-root.tsx
@@ -3,53 +3,114 @@
// SPDX-License-Identifier: AGPL-3.0
import * as React from 'react';
-import { StyleRulesCallback, WithStyles, withStyles, Card, CardContent, Button, Typography } from '@material-ui/core';
+import { StyleRulesCallback, WithStyles, withStyles, Card, CardContent, Button, Typography, Grid, Table, TableHead, TableRow, TableCell, TableBody, Tooltip, IconButton } from '@material-ui/core';
import { ArvadosTheme } from '~/common/custom-theme';
import { SshKeyResource } from '~/models/ssh-key';
+import { AddIcon, MoreOptionsIcon, KeyIcon } from '~/components/icon/icon';
-
-type CssRules = 'root' | 'link';
+type CssRules = 'root' | 'link' | 'buttonContainer' | 'table' | 'tableRow' | 'keyIcon';
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
root: {
- width: '100%'
+ width: '100%',
+ overflow: 'auto'
},
link: {
color: theme.palette.primary.main,
textDecoration: 'none',
margin: '0px 4px'
+ },
+ buttonContainer: {
+ textAlign: 'right'
+ },
+ table: {
+ marginTop: theme.spacing.unit
+ },
+ tableRow: {
+ '& td, th': {
+ whiteSpace: 'nowrap'
+ }
+ },
+ keyIcon: {
+ color: theme.palette.primary.main
}
});
export interface SshKeyPanelRootActionProps {
- onClick: () => void;
+ openSshKeyCreateDialog: () => void;
+ openRowOptions: (event: React.MouseEvent<HTMLElement>, index: number, sshKey: SshKeyResource) => void;
+ openPublicKeyDialog: (name: string, publicKey: string) => void;
}
export interface SshKeyPanelRootDataProps {
- sshKeys?: SshKeyResource[];
+ sshKeys: SshKeyResource[];
+ hasKeys: boolean;
}
type SshKeyPanelRootProps = SshKeyPanelRootDataProps & SshKeyPanelRootActionProps & WithStyles<CssRules>;
export const SshKeyPanelRoot = withStyles(styles)(
- ({ classes, sshKeys, onClick }: SshKeyPanelRootProps) =>
+ ({ classes, sshKeys, openSshKeyCreateDialog, openPublicKeyDialog, hasKeys, openRowOptions }: SshKeyPanelRootProps) =>
<Card className={classes.root}>
<CardContent>
- <Typography variant='body1' paragraph={true}>
- You have not yet set up an SSH public key for use with Arvados.
- <a href='https://doc.arvados.org/user/getting_started/ssh-access-unix.html' target='blank' className={classes.link}>
- Learn more.
- </a>
- </Typography>
- <Typography variant='body1' paragraph={true}>
- When you have an SSH key you would like to use, add it using button below.
- </Typography>
- <Button
- onClick={onClick}
- color="primary"
- variant="contained">
- Add New Ssh Key
- </Button>
+ <Grid container direction="row">
+ <Grid item xs={8}>
+ { !hasKeys && <Typography variant='body1' paragraph={true} >
+ You have not yet set up an SSH public key for use with Arvados.
+ <a href='https://doc.arvados.org/user/getting_started/ssh-access-unix.html'
+ target='blank' className={classes.link}>
+ Learn more.
+ </a>
+ </Typography>}
+ { !hasKeys && <Typography variant='body1' paragraph={true}>
+ When you have an SSH key you would like to use, add it using button below.
+ </Typography> }
+ </Grid>
+ <Grid item xs={4} className={classes.buttonContainer}>
+ <Button onClick={openSshKeyCreateDialog} color="primary" variant="contained">
+ <AddIcon /> Add New Ssh Key
+ </Button>
+ </Grid>
+ </Grid>
+ <Grid item xs={12}>
+ {hasKeys && <Table className={classes.table}>
+ <TableHead>
+ <TableRow className={classes.tableRow}>
+ <TableCell>Name</TableCell>
+ <TableCell>UUID</TableCell>
+ <TableCell>Authorized user</TableCell>
+ <TableCell>Expires at</TableCell>
+ <TableCell>Key type</TableCell>
+ <TableCell>Public Key</TableCell>
+ <TableCell />
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {sshKeys.map((sshKey, index) =>
+ <TableRow key={index} className={classes.tableRow}>
+ <TableCell>{sshKey.name}</TableCell>
+ <TableCell>{sshKey.uuid}</TableCell>
+ <TableCell>{sshKey.authorizedUserUuid}</TableCell>
+ <TableCell>{sshKey.expiresAt || '(none)'}</TableCell>
+ <TableCell>{sshKey.keyType}</TableCell>
+ <TableCell>
+ <Tooltip title="Public Key" disableFocusListener>
+ <IconButton onClick={() => openPublicKeyDialog(sshKey.name, sshKey.publicKey)}>
+ <KeyIcon className={classes.keyIcon} />
+ </IconButton>
+ </Tooltip>
+ </TableCell>
+ <TableCell>
+ <Tooltip title="More options" disableFocusListener>
+ <IconButton onClick={event => openRowOptions(event, index, sshKey)}>
+ <MoreOptionsIcon />
+ </IconButton>
+ </Tooltip>
+ </TableCell>
+ </TableRow>)}
+ </TableBody>
+ </Table>}
+ </Grid>
</CardContent>
</Card>
);
\ No newline at end of file
diff --git a/src/views/ssh-key-panel/ssh-key-panel.tsx b/src/views/ssh-key-panel/ssh-key-panel.tsx
index f600677..c7e3516 100644
--- a/src/views/ssh-key-panel/ssh-key-panel.tsx
+++ b/src/views/ssh-key-panel/ssh-key-panel.tsx
@@ -5,18 +5,26 @@
import { RootState } from '~/store/store';
import { Dispatch } from 'redux';
import { connect } from 'react-redux';
+import { openSshKeyCreateDialog, openPublicKeyDialog } from '~/store/auth/auth-action';
+import { openSshKeyContextMenu } from '~/store/context-menu/context-menu-actions';
import { SshKeyPanelRoot, SshKeyPanelRootDataProps, SshKeyPanelRootActionProps } from '~/views/ssh-key-panel/ssh-key-panel-root';
-import { openSshKeyCreateDialog } from '~/store/auth/auth-action';
const mapStateToProps = (state: RootState): SshKeyPanelRootDataProps => {
return {
- sshKeys: state.auth.sshKeys
+ sshKeys: state.auth.sshKeys,
+ hasKeys: state.auth.sshKeys!.length > 0
};
};
const mapDispatchToProps = (dispatch: Dispatch): SshKeyPanelRootActionProps => ({
- onClick: () => {
- dispatch(openSshKeyCreateDialog());
+ openSshKeyCreateDialog: () => {
+ dispatch<any>(openSshKeyCreateDialog());
+ },
+ openRowOptions: (event, index, sshKey) => {
+ dispatch<any>(openSshKeyContextMenu(event, index, sshKey));
+ },
+ openPublicKeyDialog: (name: string, publicKey: string) => {
+ dispatch<any>(openPublicKeyDialog(name, publicKey));
}
});
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index ebdf57c..84c8e24 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -55,6 +55,9 @@ import { RepositoryAttributesDialog } from '~/views-components/repository-attrib
import { CreateRepositoryDialog } from '~/views-components/dialog-forms/create-repository-dialog';
import { RemoveRepositoryDialog } from '~/views-components/repository-remove-dialog/repository-remove-dialog';
import { CreateSshKeyDialog } from '~/views-components/dialog-forms/create-ssh-key-dialog';
+import { PublicKeyDialog } from '~/views-components/ssh-keys-dialog/public-key-dialog';
+import { RemoveSshKeyDialog } from '~/views-components/ssh-keys-dialog/remove-dialog';
+import { AttributesSshKeyDialog } from '~/views-components/ssh-keys-dialog/attributes-dialog';
type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
@@ -135,6 +138,7 @@ export const WorkbenchPanel =
<DetailsPanel />
</Grid>
<AdvancedTabDialog />
+ <AttributesSshKeyDialog />
<ChangeWorkflowDialog />
<ContextMenu />
<CopyCollectionDialog />
@@ -150,12 +154,14 @@ export const WorkbenchPanel =
<MoveProcessDialog />
<MoveProjectDialog />
<MultipleFilesRemoveDialog />
+ <PublicKeyDialog />
<PartialCopyCollectionDialog />
<ProcessCommandDialog />
<ProcessInputDialog />
<ProjectPropertiesDialog />
<RemoveProcessDialog />
<RemoveRepositoryDialog />
+ <RemoveSshKeyDialog />
<RenameFileDialog />
<RepositoryAttributesDialog />
<RepositoriesSampleGitDialog />
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list