prefs: UserPrefs;
isAdmin: boolean;
isActive: boolean;
+ createdAt: string;
}
export const getUserFullname = (user?: User) => {
export const USER_IS_ACTIVE = 'isActive';
export const USER_USERNAME = 'username';
export const USER_PREFS = 'prefs';
+export const USER_CREATED_AT = 'createdAt';
export interface UserDetailsResponse {
email: string;
is_admin: boolean;
is_active: boolean;
username: string;
+ created_at: string;
prefs: UserPrefs;
}
const isAdmin = this.getIsAdmin();
const isActive = this.getIsActive();
const username = localStorage.getItem(USER_USERNAME);
+ const createdAt = localStorage.getItem(USER_CREATED_AT);
const prefs = JSON.parse(localStorage.getItem(USER_PREFS) || '{"profile": {}}');
- return email && firstName && lastName && uuid && ownerUuid && username && prefs
- ? { email, firstName, lastName, uuid, ownerUuid, isAdmin, isActive, username, prefs }
+ return email && firstName && lastName && uuid && ownerUuid && username && createdAt && prefs
+ ? { email, firstName, lastName, uuid, ownerUuid, isAdmin, isActive, username, createdAt, prefs }
: undefined;
}
localStorage.setItem(USER_IS_ADMIN, JSON.stringify(user.isAdmin));
localStorage.setItem(USER_IS_ACTIVE, JSON.stringify(user.isActive));
localStorage.setItem(USER_USERNAME, user.username);
+ localStorage.setItem(USER_CREATED_AT, user.createdAt);
localStorage.setItem(USER_PREFS, JSON.stringify(user.prefs));
}
localStorage.removeItem(USER_IS_ADMIN);
localStorage.removeItem(USER_IS_ACTIVE);
localStorage.removeItem(USER_USERNAME);
+ localStorage.removeItem(USER_CREATED_AT);
localStorage.removeItem(USER_PREFS);
}
isAdmin: resp.data.is_admin,
isActive: resp.data.is_active,
username: resp.data.username,
+ createdAt: resp.data.created_at,
prefs
};
})
isAdmin: user.is_admin,
isActive: user.is_active,
username: user.username,
+ createdAt: user.created_at,
prefs: user.prefs
},
token: saltedToken
username: "username",
prefs: {},
isAdmin: false,
- isActive: true
+ isActive: true,
+ createdAt: "createdAt"
};
const state = reducer(initialState, authActions.INIT({ user, token: "token" }));
expect(state).toEqual({
username: "username",
prefs: {},
isAdmin: false,
- isActive: true
+ isActive: true,
+ createdAt: "createdAt"
};
const state = reducer(initialState, authActions.USER_DETAILS_SUCCESS(user));
username: "username",
prefs: {},
isAdmin: false,
- isActive: true
+ isActive: true,
+ createdAt: "createdAt"
}
});
});
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'}]));
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import {
+ StyleRulesCallback,
+ WithStyles,
+ withStyles,
+ Card,
+ CardContent,
+ Button,
+ Typography,
+ Grid,
+} from '@material-ui/core';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { User } from "~/models/user";
+import { formatDate }from "~/common/formatters";
+
+type CssRules = 'root';// | 'gridItem' | 'label' | 'title' | 'actions';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+ root: {
+ width: '100%',
+ overflow: 'auto'
+ }
+});
+
+export interface LinkAccountPanelRootDataProps {
+ user?: User;
+}
+
+export interface LinkAccountPanelRootActionProps { }
+
+type LinkAccountPanelRootProps = LinkAccountPanelRootDataProps & LinkAccountPanelRootActionProps & WithStyles<CssRules>;
+
+export const LinkAccountPanelRoot = withStyles(styles) (
+ ({classes, user}: LinkAccountPanelRootProps) => {
+ return <Card className={classes.root}>
+ <CardContent>
+ <Grid container spacing={24}>
+ { user && <Grid container item direction="column" spacing={24}>
+ <Grid item>
+ You are currently logged in as <b>{user.email}</b> ({user.username}, {user.uuid}) created on <b>{formatDate(user.createdAt)}</b>
+ </Grid>
+ <Grid item>
+ You can link Arvados accounts. After linking, either login will take you to the same account.
+ </Grid>
+ </Grid> }
+ <Grid container item direction="row" spacing={24}>
+ <Grid item>
+ <Button color="primary" variant="contained">Add another login to this account</Button>
+ </Grid>
+ <Grid item>
+ <Button color="primary" variant="contained">Use this login to access another account</Button>
+ </Grid>
+ </Grid>
+ </Grid>
+ </CardContent>
+ </Card>;
+});
\ No newline at end of file
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { RootState } from '~/store/store';
+import { Dispatch } from 'redux';
+import { compose } from 'redux';
+import { reduxForm } from 'redux-form';
+import { connect } from 'react-redux';
+import { getResource, ResourcesState } from '~/store/resources/resources';
+import { Resource } from '~/models/resource';
+import { User, UserResource } from '~/models/user';
+import {
+ LinkAccountPanelRoot,
+ LinkAccountPanelRootDataProps,
+ LinkAccountPanelRootActionProps
+} from '~/views/link-account-panel/link-account-panel-root';
+
+const mapStateToProps = (state: RootState): LinkAccountPanelRootDataProps => {
+ return {
+ user: state.auth.user
+ };
+};
+
+const mapDispatchToProps = (dispatch: Dispatch): LinkAccountPanelRootActionProps => ({});
+
+export const LinkAccountPanel = connect(mapStateToProps, mapDispatchToProps)(LinkAccountPanelRoot);
import { AddGroupMembersDialog } from '~/views-components/dialog-forms/add-group-member-dialog';
import { PartialCopyToCollectionDialog } from '~/views-components/dialog-forms/partial-copy-to-collection-dialog';
import { PublicFavoritePanel } from '~/views/public-favorites-panel/public-favorites-panel';
+import { LinkAccountPanel } from '~/views/link-account-panel/link-account-panel';
type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
<Route path={Routes.GROUP_DETAILS} component={GroupDetailsPanel} />
<Route path={Routes.LINKS} component={LinkPanel} />
<Route path={Routes.PUBLIC_FAVORITES} component={PublicFavoritePanel} />
+ <Route path={Routes.LINK_ACCOUNT} component={LinkAccountPanel} />
</Switch>
</Grid>
</Grid>