mirror of
https://github.com/grafana/grafana.git
synced 2025-02-12 16:45:43 -06:00
Chore: Mark up User Profile page for translation (#43874)
* Mark up User profile page for translation * Extract new messages * updated selectors * update selectors * wip TestProvider * update tests * fix field labels * extract new messages * don't store date objects in redux state * don't store date objects in redux state
This commit is contained in:
parent
6d072ad84d
commit
36983d8d3b
@ -2,7 +2,8 @@
|
||||
"locales": [
|
||||
"en",
|
||||
"fr",
|
||||
"es"
|
||||
"es",
|
||||
"pseudo-LOCALE"
|
||||
],
|
||||
"catalogs": [
|
||||
{
|
||||
@ -11,14 +12,18 @@
|
||||
"public/app"
|
||||
],
|
||||
"exclude": [
|
||||
"**/*.d.ts",
|
||||
"**/*.test.ts",
|
||||
"**/node_modules/**",
|
||||
"public/app/plugins"
|
||||
]
|
||||
}
|
||||
],
|
||||
"fallbackLocales": {
|
||||
"pseudo-LOCALE": "en",
|
||||
"default": "en"
|
||||
},
|
||||
"pseudoLocale": "pseudo-LOCALE",
|
||||
"sourceLocale": "en",
|
||||
"format": "po",
|
||||
"formatOptions": {
|
||||
|
@ -322,4 +322,10 @@ export const Components = {
|
||||
DashboardRow: {
|
||||
title: (title: string) => `data-testid dashboard-row-title-${title}`,
|
||||
},
|
||||
UserProfile: {
|
||||
profileSaveButton: 'data-testid-user-profile-save',
|
||||
preferencesSaveButton: 'data-testid-shared-prefs-save',
|
||||
orgsTable: 'data-testid-user-orgs-table',
|
||||
sessionsTable: 'data-testid-user-sessions-table',
|
||||
},
|
||||
};
|
||||
|
@ -21,6 +21,7 @@ import { selectors } from '@grafana/e2e-selectors';
|
||||
import { DashboardSearchHit, DashboardSearchItemType } from 'app/features/search/types';
|
||||
import { backendSrv } from 'app/core/services/backend_srv';
|
||||
import { PreferencesService } from 'app/core/services/PreferencesService';
|
||||
import { t, Trans } from '@lingui/macro';
|
||||
|
||||
export interface Props {
|
||||
resourceUri: string;
|
||||
@ -36,9 +37,9 @@ export interface State {
|
||||
}
|
||||
|
||||
const themes: SelectableValue[] = [
|
||||
{ value: '', label: 'Default' },
|
||||
{ value: 'dark', label: 'Dark' },
|
||||
{ value: 'light', label: 'Light' },
|
||||
{ value: '', label: t({ id: 'shared-preferences.theme.default-label', message: 'Default' }) },
|
||||
{ value: 'dark', label: t({ id: 'shared-preferences.theme.dark-label', message: 'Dark' }) },
|
||||
{ value: 'light', label: t({ id: 'shared-preferences.theme.light-label', message: 'Light' }) },
|
||||
];
|
||||
|
||||
export class SharedPreferences extends PureComponent<Props, State> {
|
||||
@ -130,12 +131,24 @@ export class SharedPreferences extends PureComponent<Props, State> {
|
||||
const { disabled } = this.props;
|
||||
const styles = getStyles();
|
||||
|
||||
const homeDashboardTooltip = (
|
||||
<Tooltip
|
||||
content={
|
||||
<Trans id="shared-preferences.fields.home-dashboard-tooltip">
|
||||
Not finding the dashboard you want? Star it first, then it should appear in this select box.
|
||||
</Trans>
|
||||
}
|
||||
>
|
||||
<Icon name="info-circle" />
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
return (
|
||||
<Form onSubmit={this.onSubmitForm}>
|
||||
{() => {
|
||||
return (
|
||||
<FieldSet label="Preferences" disabled={disabled}>
|
||||
<Field label="UI Theme">
|
||||
<FieldSet label={<Trans id="shared-preferences.title">Preferences</Trans>} disabled={disabled}>
|
||||
<Field label={t({ id: 'shared-preferences.fields.theme-label', message: 'UI Theme' })}>
|
||||
<RadioButtonGroup
|
||||
options={themes}
|
||||
value={themes.find((item) => item.value === theme)?.value}
|
||||
@ -146,10 +159,11 @@ export class SharedPreferences extends PureComponent<Props, State> {
|
||||
<Field
|
||||
label={
|
||||
<Label htmlFor="home-dashboard-select">
|
||||
<span className={styles.labelText}>Home Dashboard</span>
|
||||
<Tooltip content="Not finding the dashboard you want? Star it first, then it should appear in this select box.">
|
||||
<Icon name="info-circle" />
|
||||
</Tooltip>
|
||||
<span className={styles.labelText}>
|
||||
<Trans id="shared-preferences.fields.home-dashboard-label">Home Dashboard</Trans>
|
||||
</span>
|
||||
|
||||
{homeDashboardTooltip}
|
||||
</Label>
|
||||
}
|
||||
data-testid="User preferences home dashboard drop down"
|
||||
@ -163,30 +177,40 @@ export class SharedPreferences extends PureComponent<Props, State> {
|
||||
this.onHomeDashboardChanged(dashboard.id)
|
||||
}
|
||||
options={dashboards}
|
||||
placeholder="Choose default dashboard"
|
||||
placeholder={t({
|
||||
id: 'shared-preferences.fields.home-dashboard-placeholder',
|
||||
message: 'Choose default dashboard',
|
||||
})}
|
||||
inputId="home-dashboard-select"
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Field label="Timezone" data-testid={selectors.components.TimeZonePicker.containerV2}>
|
||||
<Field
|
||||
label={t({ id: 'shared-dashboard.fields.timezone-label', message: 'Timezone' })}
|
||||
data-testid={selectors.components.TimeZonePicker.containerV2}
|
||||
>
|
||||
<TimeZonePicker
|
||||
includeInternal={true}
|
||||
value={timezone}
|
||||
onChange={this.onTimeZoneChanged}
|
||||
inputId={'shared-preferences-timezone-picker'}
|
||||
inputId="shared-preferences-timezone-picker"
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Field label="Week start" data-testid={selectors.components.WeekStartPicker.containerV2}>
|
||||
<Field
|
||||
label={t({ id: 'shared-preferences.fields.week-start-label', message: 'Week start' })}
|
||||
data-testid={selectors.components.WeekStartPicker.containerV2}
|
||||
>
|
||||
<WeekStartPicker
|
||||
value={weekStart}
|
||||
onChange={this.onWeekStartChanged}
|
||||
inputId={'shared-preferences-week-start-picker'}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<div className="gf-form-button-row">
|
||||
<Button variant="primary" aria-label="User preferences save button">
|
||||
Save
|
||||
<Button variant="primary" data-testid={selectors.components.UserProfile.preferencesSaveButton}>
|
||||
<Trans id="common.save">Save</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
</FieldSet>
|
||||
|
@ -3,8 +3,9 @@ import { css } from '@emotion/css';
|
||||
import { ConfirmButton, ConfirmModal, Button } from '@grafana/ui';
|
||||
import { AccessControlAction, UserSession } from 'app/types';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
import { withI18n, withI18nProps } from '@lingui/react';
|
||||
|
||||
interface Props {
|
||||
interface Props extends withI18nProps {
|
||||
sessions: UserSession[];
|
||||
|
||||
onSessionRevoke: (id: number) => void;
|
||||
@ -15,7 +16,7 @@ interface State {
|
||||
showLogoutModal: boolean;
|
||||
}
|
||||
|
||||
export class UserSessions extends PureComponent<Props, State> {
|
||||
class BaseUserSessions extends PureComponent<Props, State> {
|
||||
forceAllLogoutButton = React.createRef<HTMLButtonElement>();
|
||||
state: State = {
|
||||
showLogoutModal: false,
|
||||
@ -43,7 +44,7 @@ export class UserSessions extends PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { sessions } = this.props;
|
||||
const { sessions, i18n } = this.props;
|
||||
const { showLogoutModal } = this.state;
|
||||
|
||||
const logoutFromAllDevicesClass = css`
|
||||
@ -71,7 +72,7 @@ export class UserSessions extends PureComponent<Props, State> {
|
||||
sessions.map((session, index) => (
|
||||
<tr key={`${session.id}-${index}`}>
|
||||
<td>{session.isActive ? 'Now' : session.seenAt}</td>
|
||||
<td>{session.createdAt}</td>
|
||||
<td>{i18n.date(session.createdAt, { dateStyle: 'long' })}</td>
|
||||
<td>{session.clientIp}</td>
|
||||
<td>{`${session.browser} on ${session.os} ${session.osVersion}`}</td>
|
||||
<td>
|
||||
@ -113,3 +114,5 @@ export class UserSessions extends PureComponent<Props, State> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const UserSessions = withI18n()(BaseUserSessions);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import config from 'app/core/config';
|
||||
import { dateTimeFormat, dateTimeFormatTimeAgo } from '@grafana/data';
|
||||
import { dateTimeFormatTimeAgo } from '@grafana/data';
|
||||
import { featureEnabled, getBackendSrv, locationService } from '@grafana/runtime';
|
||||
import { ThunkResult, LdapUser, UserSession, UserDTO, AccessControlAction, UserFilter } from 'app/types';
|
||||
|
||||
@ -144,12 +144,13 @@ export function loadUserSessions(userId: number): ThunkResult<void> {
|
||||
|
||||
const tokens = await getBackendSrv().get(`/api/admin/users/${userId}/auth-tokens`);
|
||||
tokens.reverse();
|
||||
|
||||
const sessions = tokens.map((session: UserSession) => {
|
||||
return {
|
||||
id: session.id,
|
||||
isActive: session.isActive,
|
||||
seenAt: dateTimeFormatTimeAgo(session.seenAt),
|
||||
createdAt: dateTimeFormat(session.createdAt, { format: 'MMMM DD, YYYY' }),
|
||||
createdAt: session.createdAt,
|
||||
clientIp: session.clientIp,
|
||||
browser: session.browser,
|
||||
browserVersion: session.browserVersion,
|
||||
@ -158,6 +159,7 @@ export function loadUserSessions(userId: number): ThunkResult<void> {
|
||||
device: session.device,
|
||||
};
|
||||
});
|
||||
|
||||
dispatch(userSessionsLoadedAction(sessions));
|
||||
};
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { UserDTO, UserOrg } from 'app/types';
|
||||
import { Button, LoadingPlaceholder } from '@grafana/ui';
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
export interface Props {
|
||||
user: UserDTO | null;
|
||||
@ -23,13 +25,20 @@ export class UserOrganizations extends PureComponent<Props> {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3 className="page-sub-heading">Organizations</h3>
|
||||
<h3 className="page-sub-heading">
|
||||
<Trans id="user-orgs.title">Organizations</Trans>
|
||||
</h3>
|
||||
|
||||
<div className="gf-form-group">
|
||||
<table className="filter-table form-inline" aria-label="User organizations table">
|
||||
<table className="filter-table form-inline" data-testid={selectors.components.UserProfile.orgsTable}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Role</th>
|
||||
<th>
|
||||
<Trans id="user-orgs.name-column">Name</Trans>
|
||||
</th>
|
||||
<th>
|
||||
<Trans id="user-orgs.role-column">Role</Trans>
|
||||
</th>
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
@ -42,7 +51,7 @@ export class UserOrganizations extends PureComponent<Props> {
|
||||
<td className="text-right">
|
||||
{org.orgId === user?.orgId ? (
|
||||
<Button variant="secondary" size="sm" disabled>
|
||||
Current
|
||||
<Trans id="user-orgs.current-org-button">Current</Trans>
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
@ -51,9 +60,8 @@ export class UserOrganizations extends PureComponent<Props> {
|
||||
onClick={() => {
|
||||
this.props.setUserOrg(org);
|
||||
}}
|
||||
aria-label={`Switch to the organization named ${org.name}`}
|
||||
>
|
||||
Select
|
||||
<Trans id="user-orgs.select-org-button">Select organisation</Trans>
|
||||
</Button>
|
||||
)}
|
||||
</td>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { FC } from 'react';
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import { Button, Field, FieldSet, Form, Icon, Input, Tooltip } from '@grafana/ui';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { UserDTO } from 'app/types';
|
||||
import config from 'app/core/config';
|
||||
import { ProfileUpdateFields } from './types';
|
||||
@ -22,37 +23,57 @@ export const UserProfileEditForm: FC<Props> = ({ user, isSavingUser, updateProfi
|
||||
<Form onSubmit={onSubmitProfileUpdate} validateOn="onBlur">
|
||||
{({ register, errors }) => {
|
||||
return (
|
||||
<FieldSet label={<Trans id="edit-user-profile.title">Edit profile</Trans>}>
|
||||
<Field label="Name" invalid={!!errors.name} error="Name is required" disabled={disableLoginForm}>
|
||||
<FieldSet label={<Trans id="user-profile.title">Edit profile</Trans>}>
|
||||
<Field
|
||||
label={t({ id: 'user-profile.fields.name-label', message: 'Name' })}
|
||||
invalid={!!errors.name}
|
||||
error={<Trans id="user-profile.fields.name-error">Name is required</Trans>}
|
||||
disabled={disableLoginForm}
|
||||
>
|
||||
<Input
|
||||
{...register('name', { required: true })}
|
||||
id="edit-user-profile-name"
|
||||
placeholder="Name"
|
||||
placeholder={t({ id: 'user-profile.fields.name-label', message: 'Name' })}
|
||||
defaultValue={user?.name ?? ''}
|
||||
suffix={<InputSuffix />}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Email" invalid={!!errors.email} error="Email is required" disabled={disableLoginForm}>
|
||||
|
||||
<Field
|
||||
label={t({ id: 'user-profile.fields.email-label', message: 'Email' })}
|
||||
invalid={!!errors.email}
|
||||
error={<Trans id="user-profile.fields.email-error">Email is required</Trans>}
|
||||
disabled={disableLoginForm}
|
||||
>
|
||||
<Input
|
||||
{...register('email', { required: true })}
|
||||
id="edit-user-profile-email"
|
||||
placeholder="Email"
|
||||
placeholder={t({ id: 'user-profile.fields.email-label', message: 'Email' })}
|
||||
defaultValue={user?.email ?? ''}
|
||||
suffix={<InputSuffix />}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Username" disabled={disableLoginForm}>
|
||||
|
||||
<Field
|
||||
label={t({ id: 'user-profile.fields.username-label', message: 'Username' })}
|
||||
disabled={disableLoginForm}
|
||||
>
|
||||
<Input
|
||||
{...register('login')}
|
||||
id="edit-user-profile-username"
|
||||
defaultValue={user?.login ?? ''}
|
||||
placeholder="Username"
|
||||
placeholder={t({ id: 'user-profile.fields.username-label', message: 'Username' })}
|
||||
suffix={<InputSuffix />}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<div className="gf-form-button-row">
|
||||
<Button variant="primary" disabled={isSavingUser} aria-label="Edit user profile save button">
|
||||
Save
|
||||
<Button
|
||||
variant="primary"
|
||||
disabled={isSavingUser}
|
||||
data-testid={selectors.components.UserProfile.profileSaveButton}
|
||||
>
|
||||
<Trans id="common.save">Save</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
</FieldSet>
|
||||
|
@ -4,6 +4,7 @@ import userEvent from '@testing-library/user-event';
|
||||
import { within } from '@testing-library/dom';
|
||||
import { OrgRole } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import TestProvider from '../../../test/helpers/TestProvider';
|
||||
|
||||
import { Props, UserProfileEditPage } from './UserProfileEditPage';
|
||||
import { initialUserState } from './state/reducers';
|
||||
@ -90,28 +91,28 @@ function getSelectors() {
|
||||
const dashboardSelect = () => screen.getByTestId('User preferences home dashboard drop down');
|
||||
const timepickerSelect = () => screen.getByTestId(selectors.components.TimeZonePicker.containerV2);
|
||||
const teamsTable = () => screen.getByRole('table', { name: /user teams table/i });
|
||||
const orgsTable = () => screen.getByRole('table', { name: /user organizations table/i });
|
||||
const sessionsTable = () => screen.getByRole('table', { name: /user sessions table/i });
|
||||
const orgsTable = () => screen.getByTestId(selectors.components.UserProfile.orgsTable);
|
||||
const sessionsTable = () => screen.getByTestId(selectors.components.UserProfile.sessionsTable);
|
||||
return {
|
||||
name: () => screen.getByRole('textbox', { name: /^name$/i }),
|
||||
email: () => screen.getByRole('textbox', { name: /email/i }),
|
||||
username: () => screen.getByRole('textbox', { name: /username/i }),
|
||||
saveProfile: () => screen.getByRole('button', { name: /edit user profile save button/i }),
|
||||
saveProfile: () => screen.getByTestId(selectors.components.UserProfile.profileSaveButton),
|
||||
dashboardSelect,
|
||||
dashboardValue: () => within(dashboardSelect()).getByText(/default/i),
|
||||
timepickerSelect,
|
||||
timepickerValue: () => within(timepickerSelect()).getByText(/coordinated universal time/i),
|
||||
savePreferences: () => screen.getByRole('button', { name: /user preferences save button/i }),
|
||||
savePreferences: () => screen.getByTestId(selectors.components.UserProfile.preferencesSaveButton),
|
||||
teamsTable,
|
||||
teamsRow: () => within(teamsTable()).getByRole('row', { name: /team one team.one@test\.com 2000/i }),
|
||||
orgsTable,
|
||||
orgsEditorRow: () => within(orgsTable()).getByRole('row', { name: /main editor current/i }),
|
||||
orgsViewerRow: () => within(orgsTable()).getByRole('row', { name: /second viewer select/i }),
|
||||
orgsAdminRow: () => within(orgsTable()).getByRole('row', { name: /third admin select/i }),
|
||||
orgsViewerRow: () => within(orgsTable()).getByRole('row', { name: /second viewer select organisation/i }),
|
||||
orgsAdminRow: () => within(orgsTable()).getByRole('row', { name: /third admin select organisation/i }),
|
||||
sessionsTable,
|
||||
sessionsRow: () =>
|
||||
within(sessionsTable()).getByRole('row', {
|
||||
name: /now 2021-01-01 04:00:00 localhost chrome on mac os x 11/i,
|
||||
name: /now January 1, 2021 localhost chrome on mac os x 11/i,
|
||||
}),
|
||||
};
|
||||
}
|
||||
@ -125,7 +126,11 @@ async function getTestContext(overrides: Partial<Props> = {}) {
|
||||
const searchSpy = jest.spyOn(backendSrv, 'search').mockResolvedValue([]);
|
||||
|
||||
const props = { ...defaultProps, ...overrides };
|
||||
const { rerender } = render(<UserProfileEditPage {...props} />);
|
||||
const { rerender } = render(
|
||||
<TestProvider>
|
||||
<UserProfileEditPage {...props} />
|
||||
</TestProvider>
|
||||
);
|
||||
|
||||
await waitFor(() => expect(props.initUserProfilePage).toHaveBeenCalledTimes(1));
|
||||
|
||||
@ -253,7 +258,7 @@ describe('UserProfileEditPage', () => {
|
||||
const { props } = await getTestContext();
|
||||
const orgsAdminSelectButton = () =>
|
||||
within(getSelectors().orgsAdminRow()).getByRole('button', {
|
||||
name: /switch to the organization named Third/i,
|
||||
name: /select organisation/i,
|
||||
});
|
||||
|
||||
userEvent.click(orgsAdminSelectButton());
|
||||
|
@ -1,19 +1,22 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { UserSession } from 'app/types';
|
||||
import { Button, Icon, LoadingPlaceholder } from '@grafana/ui';
|
||||
import { withI18n, withI18nProps } from '@lingui/react';
|
||||
import { t, Trans } from '@lingui/macro';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
export interface Props {
|
||||
interface Props extends withI18nProps {
|
||||
sessions: UserSession[];
|
||||
isLoading: boolean;
|
||||
revokeUserSession: (tokenId: number) => void;
|
||||
}
|
||||
|
||||
export class UserSessions extends PureComponent<Props> {
|
||||
class UserSessions extends PureComponent<Props> {
|
||||
render() {
|
||||
const { isLoading, sessions, revokeUserSession } = this.props;
|
||||
const { isLoading, sessions, revokeUserSession, i18n } = this.props;
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingPlaceholder text="Loading sessions..." />;
|
||||
return <LoadingPlaceholder text={<Trans id="user-sessions.loading">Loading sessions...</Trans>} />;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -22,13 +25,21 @@ export class UserSessions extends PureComponent<Props> {
|
||||
<>
|
||||
<h3 className="page-sub-heading">Sessions</h3>
|
||||
<div className="gf-form-group">
|
||||
<table className="filter-table form-inline" aria-label="User sessions table">
|
||||
<table className="filter-table form-inline" data-testid={selectors.components.UserProfile.sessionsTable}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Last seen</th>
|
||||
<th>Logged on</th>
|
||||
<th>IP address</th>
|
||||
<th>Browser & OS</th>
|
||||
<th>
|
||||
<Trans id="user-session.seen-at-column">Last seen</Trans>
|
||||
</th>
|
||||
<th>
|
||||
<Trans id="user-session.created-at-column">Logged on</Trans>
|
||||
</th>
|
||||
<th>
|
||||
<Trans id="user-session.ip-column">IP address</Trans>
|
||||
</th>
|
||||
<th>
|
||||
<Trans id="user-session.browser-column">Browser & OS</Trans>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -36,7 +47,7 @@ export class UserSessions extends PureComponent<Props> {
|
||||
{sessions.map((session: UserSession, index) => (
|
||||
<tr key={index}>
|
||||
{session.isActive ? <td>Now</td> : <td>{session.seenAt}</td>}
|
||||
<td>{session.createdAt}</td>
|
||||
<td>{i18n.date(session.createdAt, { dateStyle: 'long' })}</td>
|
||||
<td>{session.clientIp}</td>
|
||||
<td>
|
||||
{session.browser} on {session.os} {session.osVersion}
|
||||
@ -46,7 +57,7 @@ export class UserSessions extends PureComponent<Props> {
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
onClick={() => revokeUserSession(session.id)}
|
||||
aria-label="Revoke user session"
|
||||
aria-label={t({ id: 'user-session.revoke', message: 'Revoke user session' })}
|
||||
>
|
||||
<Icon name="power" />
|
||||
</Button>
|
||||
@ -63,4 +74,4 @@ export class UserSessions extends PureComponent<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default UserSessions;
|
||||
export default withI18n()(UserSessions);
|
||||
|
@ -166,7 +166,7 @@ describe('userReducer', () => {
|
||||
browserVersion: '90',
|
||||
osVersion: '95',
|
||||
clientIp: '192.168.1.1',
|
||||
createdAt: 'December 31, 2020',
|
||||
createdAt: '2021-01-01 04:00:00',
|
||||
device: 'Computer',
|
||||
os: 'Windows',
|
||||
isActive: false,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { isEmpty, isString, set } from 'lodash';
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { dateTimeFormat, dateTimeFormatTimeAgo, setWeekStart, TimeZone } from '@grafana/data';
|
||||
import { dateTimeFormatTimeAgo, setWeekStart, TimeZone } from '@grafana/data';
|
||||
|
||||
import { Team, ThunkResult, UserDTO, UserOrg, UserSession } from 'app/types';
|
||||
import config from 'app/core/config';
|
||||
@ -78,7 +78,7 @@ export const slice = createSlice({
|
||||
id: session.id,
|
||||
isActive: session.isActive,
|
||||
seenAt: dateTimeFormatTimeAgo(session.seenAt),
|
||||
createdAt: dateTimeFormat(session.createdAt, { format: 'MMMM DD, YYYY' }),
|
||||
createdAt: session.createdAt,
|
||||
clientIp: session.clientIp,
|
||||
browser: session.browser,
|
||||
browserVersion: session.browserVersion,
|
||||
|
@ -13,6 +13,118 @@ msgstr ""
|
||||
"Language-Team: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "edit-user-profile.title"
|
||||
msgid "common.save"
|
||||
msgstr "Save"
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-dashboard.fields.timezone-label"
|
||||
msgstr "Timezone"
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.home-dashboard-label"
|
||||
msgstr "Home Dashboard"
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.home-dashboard-placeholder"
|
||||
msgstr "Choose default dashboard"
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.home-dashboard-tooltip"
|
||||
msgstr "Not finding the dashboard you want? Star it first, then it should appear in this select box."
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.theme-label"
|
||||
msgstr "UI Theme"
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.week-start-label"
|
||||
msgstr "Week start"
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.theme.dark-label"
|
||||
msgstr "Dark"
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.theme.default-label"
|
||||
msgstr "Default"
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.theme.light-label"
|
||||
msgstr "Light"
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.title"
|
||||
msgstr "Preferences"
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.current-org-button"
|
||||
msgstr "Current"
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.name-column"
|
||||
msgstr "Name"
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.role-column"
|
||||
msgstr "Role"
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.select-org-button"
|
||||
msgstr "Select"
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.title"
|
||||
msgstr "Organizations"
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.email-error"
|
||||
msgstr "Email is required"
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.email-label"
|
||||
msgstr "Email"
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.name-error"
|
||||
msgstr "Name is required"
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.name-label"
|
||||
msgstr "Name"
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.username-label"
|
||||
msgstr "Username"
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.title"
|
||||
msgstr "Edit profile"
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.browser-column"
|
||||
msgstr "Browser & OS"
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.created-at-column"
|
||||
msgstr "Logged on"
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.ip-column"
|
||||
msgstr "IP address"
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.revoke"
|
||||
msgstr "Revoke user session"
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.seen-at-column"
|
||||
msgstr "Last seen"
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-sessions.loading"
|
||||
msgstr "Loading sessions..."
|
||||
|
@ -13,6 +13,118 @@ msgstr ""
|
||||
"Language-Team: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "edit-user-profile.title"
|
||||
msgid "common.save"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-dashboard.fields.timezone-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.home-dashboard-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.home-dashboard-placeholder"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.home-dashboard-tooltip"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.theme-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.week-start-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.theme.dark-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.theme.default-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.theme.light-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.title"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.current-org-button"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.name-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.role-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.select-org-button"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.title"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.email-error"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.email-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.name-error"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.name-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.username-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.title"
|
||||
msgstr "Editar perfil"
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.browser-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.created-at-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.ip-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.revoke"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.seen-at-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-sessions.loading"
|
||||
msgstr ""
|
||||
|
@ -13,6 +13,118 @@ msgstr ""
|
||||
"Language-Team: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "edit-user-profile.title"
|
||||
msgid "common.save"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-dashboard.fields.timezone-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.home-dashboard-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.home-dashboard-placeholder"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.home-dashboard-tooltip"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.theme-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.week-start-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.theme.dark-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.theme.default-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.theme.light-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.title"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.current-org-button"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.name-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.role-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.select-org-button"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.title"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.email-error"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.email-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.name-error"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.name-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.username-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.title"
|
||||
msgstr "Editer le profil"
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.browser-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.created-at-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.ip-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.revoke"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.seen-at-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-sessions.loading"
|
||||
msgstr ""
|
||||
|
130
public/locales/pseudo-LOCALE/messages.po
Normal file
130
public/locales/pseudo-LOCALE/messages.po
Normal file
@ -0,0 +1,130 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"POT-Creation-Date: 2022-01-10 10:42+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: @lingui/cli\n"
|
||||
"Language: pseudo-LOCALE\n"
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "common.save"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-dashboard.fields.timezone-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.home-dashboard-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.home-dashboard-placeholder"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.home-dashboard-tooltip"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.theme-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.fields.week-start-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.theme.dark-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.theme.default-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.theme.light-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/core/components/SharedPreferences/SharedPreferences.tsx
|
||||
msgid "shared-preferences.title"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.current-org-button"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.name-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.role-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.select-org-button"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserOrganizations.tsx
|
||||
msgid "user-orgs.title"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.email-error"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.email-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.name-error"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.name-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.fields.username-label"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserProfileEditForm.tsx
|
||||
msgid "user-profile.title"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.browser-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.created-at-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.ip-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.revoke"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-session.seen-at-column"
|
||||
msgstr ""
|
||||
|
||||
#: public/app/features/profile/UserSessions.tsx
|
||||
msgid "user-sessions.loading"
|
||||
msgstr ""
|
8
public/test/helpers/TestProvider.tsx
Normal file
8
public/test/helpers/TestProvider.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
import { I18nProvider } from '../../app/core/localisation';
|
||||
|
||||
const TestProvider: React.FC = ({ children }) => {
|
||||
return <I18nProvider>{children}</I18nProvider>;
|
||||
};
|
||||
|
||||
export default TestProvider;
|
@ -15,3 +15,7 @@ export const Select: React.FC = () => {
|
||||
export const SelectOrdinal: React.FC = () => {
|
||||
throw new Error('SelectOrdinal mock not implemented yet');
|
||||
};
|
||||
|
||||
export const t = (msg: string | { message: string }) => {
|
||||
return typeof msg === 'string' ? msg : msg.message;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user