From 5293c9dd84ae37d12dc2bbecc488c9935c09d102 Mon Sep 17 00:00:00 2001 From: Shavonn Brown Date: Mon, 8 Jun 2020 11:19:00 -0400 Subject: [PATCH] Convert remaining profile bits to React (#24310) * reactify user sessions * all reactified * more cleanup * comment edit * Profile: Fix casing * Profile: Add Page wrapper * Profile: New form styles for UserProfileEditForm * Profile: Use new form styles for SharedPreferences * Profile: Use radioButtonGroup for SharedPreferences * Grafana UI: Add FieldSet * Grafana UI: Add story * Grafana UI: Add docs * Grafana UI: Export FieldSet * Profile: USe FieldSet * Profile: Sort sessions Co-authored-by: Clarity-89 --- public/app/core/angular_wrappers.ts | 3 - .../SharedPreferences/SharedPreferences.tsx | 133 +++++++++------- public/app/core/utils/UserProvider.tsx | 82 ++++++++-- .../features/profile/ReactProfileWrapper.tsx | 41 ----- .../features/profile/UserOrganizations.tsx | 3 +- .../app/features/profile/UserProfileEdit.tsx | 69 +++++++++ .../features/profile/UserProfileEditForm.tsx | 142 +++++++----------- public/app/features/profile/UserSessions.tsx | 67 +++++++++ public/app/features/profile/all.ts | 1 - .../features/profile/partials/profile.html | 36 ----- public/app/routes/routes.ts | 12 +- 11 files changed, 337 insertions(+), 252 deletions(-) delete mode 100644 public/app/features/profile/ReactProfileWrapper.tsx create mode 100644 public/app/features/profile/UserProfileEdit.tsx create mode 100644 public/app/features/profile/UserSessions.tsx delete mode 100644 public/app/features/profile/partials/profile.html diff --git a/public/app/core/angular_wrappers.ts b/public/app/core/angular_wrappers.ts index 483e362e085..81c77afbbee 100644 --- a/public/app/core/angular_wrappers.ts +++ b/public/app/core/angular_wrappers.ts @@ -20,7 +20,6 @@ import { } from '@grafana/ui'; const { SecretFormField } = LegacyForms; import { FunctionEditor } from 'app/plugins/datasource/graphite/FunctionEditor'; -import ReactProfileWrapper from 'app/features/profile/ReactProfileWrapper'; import { LokiAnnotationsQueryEditor } from '../plugins/datasource/loki/components/AnnotationsQueryEditor'; import { HelpModal } from './components/help/HelpModal'; import { Footer } from './components/Footer/Footer'; @@ -164,8 +163,6 @@ export function registerAngularDirectives() { ['onChange', { watchDepth: 'reference', wrapApply: true }], ]); - react2AngularDirective('reactProfileWrapper', ReactProfileWrapper, []); - react2AngularDirective('lokiAnnotationsQueryEditor', LokiAnnotationsQueryEditor, [ 'expr', 'onChange', diff --git a/public/app/core/components/SharedPreferences/SharedPreferences.tsx b/public/app/core/components/SharedPreferences/SharedPreferences.tsx index 85dbf9306cf..f82076ac980 100644 --- a/public/app/core/components/SharedPreferences/SharedPreferences.tsx +++ b/public/app/core/components/SharedPreferences/SharedPreferences.tsx @@ -1,12 +1,23 @@ import React, { PureComponent } from 'react'; +import { css } from 'emotion'; -import { InlineFormLabel, LegacyForms } from '@grafana/ui'; -const { Select } = LegacyForms; +import { + Select, + Field, + Form, + Tooltip, + Icon, + stylesFactory, + Label, + Button, + RadioButtonGroup, + FieldSet, +} from '@grafana/ui'; +import { getTimeZoneGroups, SelectableValue } from '@grafana/data'; +import { selectors } from '@grafana/e2e-selectors'; import { DashboardSearchHit, DashboardSearchItemType } from 'app/features/search/types'; import { backendSrv } from 'app/core/services/backend_srv'; -import { getTimeZoneGroups, SelectableValue } from '@grafana/data'; -import { selectors } from '@grafana/e2e-selectors'; export interface Props { resourceUri: string; @@ -19,7 +30,7 @@ export interface State { dashboards: DashboardSearchHit[]; } -const themes = [ +const themes: SelectableValue[] = [ { value: '', label: 'Default' }, { value: 'dark', label: 'Dark' }, { value: 'light', label: 'Light' }, @@ -86,9 +97,7 @@ export class SharedPreferences extends PureComponent { }); } - onSubmitForm = async (event: React.SyntheticEvent) => { - event.preventDefault(); - + onSubmitForm = async () => { const { homeDashboardId, theme, timezone } = this.state; await backendSrv.put(`/api/${this.props.resourceUri}/preferences`, { @@ -99,11 +108,8 @@ export class SharedPreferences extends PureComponent { window.location.reload(); }; - onThemeChanged = (theme: SelectableValue) => { - if (!theme || typeof theme.value !== 'string') { - return; - } - this.setState({ theme: theme.value }); + onThemeChanged = (value: string) => { + this.setState({ theme: value }); }; onTimeZoneChanged = (timezone: SelectableValue) => { @@ -126,55 +132,66 @@ export class SharedPreferences extends PureComponent { render() { const { theme, timezone, homeDashboardId, dashboards } = this.state; + const styles = getStyles(); return ( -
-

Preferences

-
- UI Theme - dashboard.id === homeDashboardId)} - getOptionValue={i => i.id} - getOptionLabel={this.getFullDashName} - onChange={(dashboard: DashboardSearchHit) => this.onHomeDashboardChanged(dashboard.id)} - options={dashboards} - placeholder="Choose default dashboard" - width={20} - /> -
-
- - dashboard.id === homeDashboardId)} + getOptionValue={i => i.id} + getOptionLabel={this.getFullDashName} + onChange={(dashboard: DashboardSearchHit) => this.onHomeDashboardChanged(dashboard.id)} + options={dashboards} + placeholder="Choose default dashboard" + /> + + + + -
-
- Email - - {disableLoginForm && ( - - - - )} -
-
- Username - - {disableLoginForm && ( - - - - )} -
-
- -
-
- - ); - } -} + return ( +
+ {({ register, errors }) => { + return ( +
+ + + + + } + /> + + + } + /> + +
+ +
+
+ ); + }} +
+ ); +}; export default UserProfileEditForm; + +const InputSuffix: FC = () => { + return disableLoginForm ? ( + + + + ) : null; +}; diff --git a/public/app/features/profile/UserSessions.tsx b/public/app/features/profile/UserSessions.tsx new file mode 100644 index 00000000000..b220e9df92f --- /dev/null +++ b/public/app/features/profile/UserSessions.tsx @@ -0,0 +1,67 @@ +import React, { PureComponent } from 'react'; +import { User, UserSession } from 'app/types'; +import { LoadingPlaceholder, Button, Icon } from '@grafana/ui'; + +export interface Props { + user: User; + sessions: UserSession[]; + isLoading: boolean; + loadSessions: () => void; + revokeUserSession: (tokenId: number) => void; +} + +export class UserSessions extends PureComponent { + componentDidMount() { + this.props.loadSessions(); + } + + render() { + const { isLoading, sessions, revokeUserSession } = this.props; + + if (isLoading) { + return ; + } + + return ( + <> + {sessions.length > 0 && ( + <> +

Sessions

+
+ + + + + + + + + + + + {sessions.map((session: UserSession, index) => ( + + {session.isActive ? : } + + + + + + ))} + +
Last seenLogged onIP addressBrowser & OS
Now{session.seenAt}{session.createdAt}{session.clientIp} + {session.browser} on {session.os} {session.osVersion} + + +
+
+ + )} + + ); + } +} + +export default UserSessions; diff --git a/public/app/features/profile/all.ts b/public/app/features/profile/all.ts index dfe5812f5d2..e69de29bb2d 100644 --- a/public/app/features/profile/all.ts +++ b/public/app/features/profile/all.ts @@ -1 +0,0 @@ -import './ProfileCtrl'; diff --git a/public/app/features/profile/partials/profile.html b/public/app/features/profile/partials/profile.html deleted file mode 100644 index 5fcb038fd22..00000000000 --- a/public/app/features/profile/partials/profile.html +++ /dev/null @@ -1,36 +0,0 @@ - - -
- - -

Sessions

-
- - - - - - - - - - - - - - - - - - - - -
Last seenLogged onIP addressBrowser & OS
Now{{ session.seenAt }}{{ session.createdAt }}{{ session.clientIp }}{{ session.browser }} on {{ session.os }} {{ session.osVersion }} - -
-
-
- -