const computeNodesMatch = Routes.matchComputeNodesRoute(pathname);
const apiClientAuthorizationsMatch = Routes.matchApiClientAuthorizationsRoute(pathname);
const myAccountMatch = Routes.matchMyAccountRoute(pathname);
+ const linkAccountMatch = Routes.matchLinkAccountRoute(pathname);
const userMatch = Routes.matchUsersRoute(pathname);
const groupsMatch = Routes.matchGroupsRoute(pathname);
const groupDetailsMatch = Routes.matchGroupDetailsRoute(pathname);
store.dispatch(WorkbenchActions.loadApiClientAuthorizations);
} else if (myAccountMatch) {
store.dispatch(WorkbenchActions.loadMyAccount);
+ } else if (linkAccountMatch) {
+ store.dispatch(WorkbenchActions.loadLinkAccount);
} else if (userMatch) {
store.dispatch(WorkbenchActions.loadUsers);
} else if (groupsMatch) {
SSH_KEYS_USER: `/ssh-keys-user`,
SITE_MANAGER: `/site-manager`,
MY_ACCOUNT: '/my-account',
+ LINK_ACCOUNT: '/link_account',
KEEP_SERVICES: `/keep-services`,
COMPUTE_NODES: `/nodes`,
USERS: '/users',
export const matchMyAccountRoute = (route: string) =>
matchPath(route, { path: Routes.MY_ACCOUNT });
+export const matchLinkAccountRoute = (route: string) =>
+ matchPath(route, { path: Routes.LINK_ACCOUNT });
+
export const matchKeepServicesRoute = (route: string) =>
matchPath(route, { path: Routes.KEEP_SERVICES });
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from "redux";
+import { RootState } from "~/store/store";
+import { initialize } from "redux-form";
+import { ServiceRepository } from "~/services/services";
+import { setBreadcrumbs } from "~/store/breadcrumbs/breadcrumbs-actions";
+import { authActions } from "~/store/auth/auth-action";
+import { snackbarActions, SnackbarKind } from "~/store/snackbar/snackbar-actions";
+
+export const LINK_ACCOUNT_FORM = 'linkAccountForm';
+
+export const loadLinkAccountPanel = () =>
+ (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+ dispatch(setBreadcrumbs([{ label: 'Link account'}]));
+ };
import { setBreadcrumbs } from "~/store/breadcrumbs/breadcrumbs-actions";
import { authActions } from "~/store/auth/auth-action";
import { snackbarActions, SnackbarKind } from "~/store/snackbar/snackbar-actions";
+import { navigateToLinkAccount } from '~/store/navigation/navigation-action';
export const MY_ACCOUNT_FORM = 'myAccountForm';
dispatch(setBreadcrumbs([{ label: 'User profile'}]));
};
+export const openLinkAccount = () =>
+ (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+ dispatch<any>(navigateToLinkAccount);
+ };
+
export const saveEditedUser = (resource: any) =>
async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
try {
export const navigateToMyAccount = push(Routes.MY_ACCOUNT);
+export const navigateToLinkAccount = push(Routes.LINK_ACCOUNT);
+
export const navigateToKeepServices = push(Routes.KEEP_SERVICES);
export const navigateToComputeNodes = push(Routes.COMPUTE_NODES);
import { loadWorkflowPanel, workflowPanelActions } from '~/store/workflow-panel/workflow-panel-actions';
import { loadSshKeysPanel } from '~/store/auth/auth-action-ssh';
import { loadMyAccountPanel } from '~/store/my-account/my-account-panel-actions';
+import { loadLinkAccountPanel } from '~/store/link-account/link-account-panel-actions';
import { loadSiteManagerPanel } from '~/store/auth/auth-action-session';
import { workflowPanelColumns } from '~/views/workflow-panel/workflow-panel-view';
import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions';
dispatch(loadMyAccountPanel());
});
+export const loadLinkAccount = handleFirstTimeLoad(
+ (dispatch: Dispatch<any>) => {
+ dispatch(loadLinkAccountPanel());
+ });
+
export const loadKeepServices = handleFirstTimeLoad(
async (dispatch: Dispatch<any>) => {
await dispatch(loadKeepServicesPanel());
import { Field, InjectedFormProps, WrappedFieldProps } from "redux-form";
import { TextField } from "~/components/text-field/text-field";
import { NativeSelectField } from "~/components/select-field/select-field";
+import { DetailsAttribute } from '~/components/details-attribute/details-attribute';
import {
StyleRulesCallback,
WithStyles,
import { User } from "~/models/user";
import { MY_ACCOUNT_VALIDATION } from "~/validators/validators";
-type CssRules = 'root' | 'gridItem' | 'label' | 'title' | 'actions';
+type CssRules = 'root' | 'gridItem' | 'label' | 'title' | 'actions' | 'link';
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
root: {
marginBottom: theme.spacing.unit * 3,
color: theme.palette.grey["600"]
},
+ link: {
+ lineHeight:'2.1',
+ whiteSpace: 'nowrap',
+ fontSize: '1rem',
+ color: theme.palette.primary.main,
+ '&:hover': {
+ cursor: 'pointer'
+ }
+ },
actions: {
display: 'flex',
justifyContent: 'flex-end'
}
});
-export interface MyAccountPanelRootActionProps { }
+export interface MyAccountPanelRootActionProps {
+ openLinkAccount: () => void;
+ }
export interface MyAccountPanelRootDataProps {
isPristine: boolean;
{ key: 'Other', value: 'Other' }
];
-type MyAccountPanelRootProps = InjectedFormProps<MyAccountPanelRootActionProps> & MyAccountPanelRootDataProps & WithStyles<CssRules>;
+type MyAccountPanelRootProps = InjectedFormProps & MyAccountPanelRootActionProps & MyAccountPanelRootDataProps & WithStyles<CssRules>;
type LocalClusterProp = { localCluster: string };
const renderField: React.ComponentType<WrappedFieldProps & LocalClusterProp> = ({ input, localCluster }) => (
);
export const MyAccountPanelRoot = withStyles(styles)(
- ({ classes, isValid, handleSubmit, reset, isPristine, invalid, submitting, localCluster }: MyAccountPanelRootProps) => {
+ ({ classes, isValid, handleSubmit, reset, isPristine, invalid, submitting, localCluster, openLinkAccount}: MyAccountPanelRootProps) => {
return <Card className={classes.root}>
<CardContent>
- <Typography variant="title" className={classes.title}>
- Logged in as <Field name="uuid" component={renderField} localCluster={localCluster} />
- </Typography>
+ <Grid container spacing={24}>
+ <Grid item className={classes.gridItem}>
+ <Typography variant="title" className={classes.title}>
+ Logged in as <Field name="uuid" component={renderField} localCluster={localCluster} />
+ </Typography>
+ </Grid>
+ <Grid item className={classes.gridItem}>
+ <span onClick={() => openLinkAccount()}>
+ <DetailsAttribute classLabel={classes.link} label='Link account' />
+ </span>
+ </Grid>
+ </Grid>
<form onSubmit={handleSubmit}>
<Grid container spacing={24}>
<Grid item className={classes.gridItem} sm={6} xs={12}>
// SPDX-License-Identifier: AGPL-3.0
import { RootState } from '~/store/store';
+import { Dispatch } from 'redux';
import { compose } from 'redux';
import { reduxForm, isPristine, isValid } from 'redux-form';
import { connect } from 'react-redux';
-import { saveEditedUser } from '~/store/my-account/my-account-panel-actions';
-import { MyAccountPanelRoot, MyAccountPanelRootDataProps } from '~/views/my-account-panel/my-account-panel-root';
+import { saveEditedUser, openLinkAccount } from '~/store/my-account/my-account-panel-actions';
+import { MyAccountPanelRoot, MyAccountPanelRootDataProps, MyAccountPanelRootActionProps } from '~/views/my-account-panel/my-account-panel-root';
import { MY_ACCOUNT_FORM } from "~/store/my-account/my-account-panel-actions";
const mapStateToProps = (state: RootState): MyAccountPanelRootDataProps => ({
localCluster: state.auth.localCluster
});
+const mapDispatchToProps = (dispatch: Dispatch): MyAccountPanelRootActionProps => ({
+ openLinkAccount: () => dispatch<any>(openLinkAccount())
+});
+
export const MyAccountPanel = compose(
- connect(mapStateToProps),
+ connect(mapStateToProps, mapDispatchToProps),
reduxForm({
form: MY_ACCOUNT_FORM,
onSubmit: (data, dispatch) => {