EmptyState: Apply call-to-action variant in core (#86448)
* apply empty state in a bunch of places * fix unit tests * put alert back on top * add data-testids so e2e tests keep working * remove info boxes * fix annotations empty state alignment with new maxWidth
This commit is contained in:
+2
-6
@@ -2513,9 +2513,6 @@ exports[`better eslint`] = {
|
||||
"public/app/features/dashboard-scene/settings/annotations/AnnotationSettingsEdit.tsx:5381": [
|
||||
[0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"]
|
||||
],
|
||||
"public/app/features/dashboard-scene/settings/annotations/AnnotationSettingsList.tsx:5381": [
|
||||
[0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"]
|
||||
],
|
||||
"public/app/features/dashboard-scene/settings/annotations/index.tsx:5381": [
|
||||
[0, 0, 0, "Do not re-export imported variable (\`./AnnotationSettingsEdit\`)", "0"],
|
||||
[0, 0, 0, "Do not re-export imported variable (\`./AnnotationSettingsList\`)", "1"]
|
||||
@@ -2565,8 +2562,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Styles should be written using objects.", "5"]
|
||||
],
|
||||
"public/app/features/dashboard/components/AnnotationSettings/AnnotationSettingsList.tsx:5381": [
|
||||
[0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "1"]
|
||||
[0, 0, 0, "Styles should be written using objects.", "0"]
|
||||
],
|
||||
"public/app/features/dashboard/components/AnnotationSettings/index.tsx:5381": [
|
||||
[0, 0, 0, "Do not re-export imported variable (\`./AnnotationSettingsEdit\`)", "0"],
|
||||
@@ -3923,7 +3919,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Styles should be written using objects.", "1"]
|
||||
],
|
||||
"public/app/features/manage-dashboards/components/PublicDashboardListTable/PublicDashboardListTable.tsx:5381": [
|
||||
[0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui/src\' is restricted from being used by a pattern. Use Stack component instead.", "0"],
|
||||
[0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "1"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "2"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "3"],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { CallToActionCard } from '@grafana/ui';
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
import { CallToActionCard, EmptyState, LinkButton } from '@grafana/ui';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
|
||||
import { getInstancesPermissions } from '../../utils/access-control';
|
||||
@@ -16,11 +16,14 @@ export const NoSilencesSplash = ({ alertManagerSourceName }: Props) => {
|
||||
|
||||
if (contextSrv.hasPermission(permissions.create)) {
|
||||
return (
|
||||
<EmptyListCTA
|
||||
title="You haven't created any silences yet"
|
||||
buttonIcon="bell-slash"
|
||||
buttonLink={makeAMLink('alerting/silence/new', alertManagerSourceName)}
|
||||
buttonTitle="Create silence"
|
||||
<EmptyState
|
||||
variant="call-to-action"
|
||||
button={
|
||||
<LinkButton href={makeAMLink('alerting/silence/new', alertManagerSourceName)} icon="bell-slash" size="lg">
|
||||
<Trans i18nKey="silences.empty-state.button-title">Create silence</Trans>
|
||||
</LinkButton>
|
||||
}
|
||||
message={t('silences.empty-state.title', "You haven't created any silences yet")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ describe('browse-dashboards BrowseView', () => {
|
||||
describe('when there is no item in the folder', () => {
|
||||
it('shows a CTA for creating a dashboard if the user has editor rights', async () => {
|
||||
render(<BrowseView canSelect={true} folderUID={folderB_empty.item.uid} width={WIDTH} height={HEIGHT} />);
|
||||
expect(await screen.findByText('Create Dashboard')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Create dashboard')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows a simple message if the user has viewer rights', async () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import { CallToActionCard } from '@grafana/ui';
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
import { CallToActionCard, EmptyState, LinkButton, TextLink } from '@grafana/ui';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
import { DashboardViewItem } from 'app/features/search/types';
|
||||
import { useDispatch } from 'app/types';
|
||||
|
||||
@@ -117,16 +117,32 @@ export function BrowseView({ folderUID, width, height, canSelect }: BrowseViewPr
|
||||
return (
|
||||
<div style={{ width }}>
|
||||
{canSelect ? (
|
||||
<EmptyListCTA
|
||||
title={folderUID ? "This folder doesn't have any dashboards yet" : 'No dashboards yet. Create your first!'}
|
||||
buttonIcon="plus"
|
||||
buttonTitle="Create Dashboard"
|
||||
buttonLink={folderUID ? `dashboard/new?folderUid=${folderUID}` : 'dashboard/new'}
|
||||
proTip={folderUID && 'Add/move dashboards to your folder at ->'}
|
||||
proTipLink={folderUID && 'dashboards'}
|
||||
proTipLinkTitle={folderUID && 'Browse dashboards'}
|
||||
proTipTarget=""
|
||||
/>
|
||||
<EmptyState
|
||||
variant="call-to-action"
|
||||
button={
|
||||
<LinkButton
|
||||
href={folderUID ? `dashboard/new?folderUid=${folderUID}` : 'dashboard/new'}
|
||||
icon="plus"
|
||||
size="lg"
|
||||
>
|
||||
<Trans i18nKey="browse-dashboards.empty-state.button-title">Create dashboard</Trans>
|
||||
</LinkButton>
|
||||
}
|
||||
message={
|
||||
folderUID
|
||||
? t('browse-dashboards.empty-state.title-folder', "This folder doesn't have any dashboards yet")
|
||||
: t('browse-dashboards.empty-state.title', "You haven't created any dashboards yet")
|
||||
}
|
||||
>
|
||||
{folderUID && (
|
||||
<Trans i18nKey="browse-dashboards.empty-state.pro-tip">
|
||||
Add/move dashboards to your folder at{' '}
|
||||
<TextLink external={false} href="/dashboards">
|
||||
Browse dashboards
|
||||
</TextLink>
|
||||
</Trans>
|
||||
)}
|
||||
</EmptyState>
|
||||
) : (
|
||||
<CallToActionCard callToActionElement={<span>This folder is empty</span>} />
|
||||
)}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Card } from '@grafana/ui';
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
import { Button, Card, EmptyState } from '@grafana/ui';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
|
||||
interface Props {
|
||||
onClick?: () => void;
|
||||
@@ -11,13 +11,19 @@ export const EmptyCorrelationsCTA = ({ onClick, canWriteCorrelations }: Props) =
|
||||
// TODO: if there are no datasources show a different message
|
||||
|
||||
return canWriteCorrelations ? (
|
||||
<EmptyListCTA
|
||||
title="You haven't defined any correlation yet."
|
||||
buttonIcon="gf-glue"
|
||||
onClick={onClick}
|
||||
buttonTitle="Add correlation"
|
||||
proTip="you can also define correlations via datasource provisioning"
|
||||
/>
|
||||
<EmptyState
|
||||
variant="call-to-action"
|
||||
button={
|
||||
<Button icon="gf-glue" onClick={onClick} size="lg">
|
||||
<Trans i18nKey="correlations.empty-state.button-title">Add correlation</Trans>
|
||||
</Button>
|
||||
}
|
||||
message={t('correlations.empty-state.title', "You haven't defined any correlations yet")}
|
||||
>
|
||||
<Trans i18nKey="correlations.empty-state.pro-tip">
|
||||
You can also define correlations via datasource provisioning
|
||||
</Trans>
|
||||
</EmptyState>
|
||||
) : (
|
||||
<Card>
|
||||
<Card.Heading>There are no correlations configured yet.</Card.Heading>
|
||||
|
||||
+4
-4
@@ -65,21 +65,21 @@ describe('AnnotationSettingsEdit', () => {
|
||||
|
||||
it('should render with empty list message', async () => {
|
||||
const {
|
||||
renderer: { getByTestId },
|
||||
renderer: { getByRole },
|
||||
} = await setup(true);
|
||||
|
||||
const emptyListBtn = getByTestId(selectors.components.CallToActionCard.buttonV2(BUTTON_TITLE));
|
||||
const emptyListBtn = getByRole('button', { name: BUTTON_TITLE });
|
||||
|
||||
expect(emptyListBtn).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should create new annotation when empty list button is pressed', async () => {
|
||||
const {
|
||||
renderer: { getByTestId },
|
||||
renderer: { getByRole },
|
||||
user,
|
||||
} = await setup(true);
|
||||
|
||||
const emptyListBtn = getByTestId(selectors.components.CallToActionCard.buttonV2(BUTTON_TITLE));
|
||||
const emptyListBtn = getByRole('button', { name: BUTTON_TITLE });
|
||||
|
||||
await user.click(emptyListBtn);
|
||||
|
||||
|
||||
+36
-23
@@ -4,8 +4,8 @@ import React from 'react';
|
||||
import { AnnotationQuery } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { getDataSourceSrv } from '@grafana/runtime';
|
||||
import { Button, DeleteButton, IconButton, useStyles2, VerticalGroup } from '@grafana/ui';
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
import { Button, DeleteButton, EmptyState, IconButton, Stack, TextLink, useStyles2 } from '@grafana/ui';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
import { ListNewButton } from 'app/features/dashboard/components/DashboardSettings/ListNewButton';
|
||||
|
||||
import { MoveDirection } from '../AnnotationsEditView';
|
||||
@@ -39,7 +39,7 @@ export const AnnotationSettingsList = ({ annotations, onNew, onEdit, onMove, onD
|
||||
|
||||
const dataSourceSrv = getDataSourceSrv();
|
||||
return (
|
||||
<VerticalGroup>
|
||||
<Stack direction="column">
|
||||
{annotations.length > 0 && (
|
||||
<div className={styles.table}>
|
||||
<table role="grid" className="filter-table filter-table--hover">
|
||||
@@ -99,25 +99,38 @@ export const AnnotationSettingsList = ({ annotations, onNew, onEdit, onMove, onD
|
||||
</div>
|
||||
)}
|
||||
{showEmptyListCTA && (
|
||||
<EmptyListCTA
|
||||
onClick={onNew}
|
||||
title="There are no custom annotation queries added yet"
|
||||
buttonIcon="comment-alt"
|
||||
buttonTitle={BUTTON_TITLE}
|
||||
infoBoxTitle="What are annotation queries?"
|
||||
infoBox={{
|
||||
__html: `<p>Annotations provide a way to integrate event data into your graphs. They are visualized as vertical lines
|
||||
and icons on all graph panels. When you hover over an annotation icon you can get event text & tags for
|
||||
the event. You can add annotation events directly from grafana by holding CTRL or CMD + click on graph (or
|
||||
drag region). These will be stored in Grafana's annotation database.
|
||||
</p>
|
||||
Checkout the
|
||||
<a class='external-link' target='_blank' href='http://docs.grafana.org/reference/annotations/'
|
||||
>Annotations documentation</a
|
||||
>
|
||||
for more information.`,
|
||||
}}
|
||||
/>
|
||||
<Stack direction="column">
|
||||
<EmptyState
|
||||
variant="call-to-action"
|
||||
button={
|
||||
<Button
|
||||
data-testid={selectors.components.CallToActionCard.buttonV2('Add annotation query')}
|
||||
icon="comment-alt"
|
||||
onClick={onNew}
|
||||
size="lg"
|
||||
>
|
||||
<Trans i18nKey="annotations.empty-state.button-title">Add annotation query</Trans>
|
||||
</Button>
|
||||
}
|
||||
message={t('annotations.empty-state.title', 'There are no custom annotation queries added yet')}
|
||||
>
|
||||
<Trans i18nKey="annotations.empty-state.info-box-content">
|
||||
<p>
|
||||
Annotations provide a way to integrate event data into your graphs. They are visualized as vertical
|
||||
lines and icons on all graph panels. When you hover over an annotation icon you can get event text &
|
||||
tags for the event. You can add annotation events directly from grafana by holding CTRL or CMD + click
|
||||
on graph (or drag region). These will be stored in Grafana's annotation database.
|
||||
</p>
|
||||
</Trans>
|
||||
<Trans i18nKey="annotations.empty-state.info-box-content-2">
|
||||
Checkout the{' '}
|
||||
<TextLink external href="http://docs.grafana.org/reference/annotations/">
|
||||
Annotations documentation
|
||||
</TextLink>{' '}
|
||||
for more information.
|
||||
</Trans>
|
||||
</EmptyState>
|
||||
</Stack>
|
||||
)}
|
||||
{!showEmptyListCTA && (
|
||||
<ListNewButton
|
||||
@@ -127,7 +140,7 @@ export const AnnotationSettingsList = ({ annotations, onNew, onEdit, onMove, onD
|
||||
New query
|
||||
</ListNewButton>
|
||||
)}
|
||||
</VerticalGroup>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,8 +3,19 @@ import React from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { DashboardLink } from '@grafana/schema';
|
||||
import { Button, DeleteButton, HorizontalGroup, Icon, IconButton, TagList, useStyles2 } from '@grafana/ui';
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
import {
|
||||
Button,
|
||||
DeleteButton,
|
||||
EmptyState,
|
||||
HorizontalGroup,
|
||||
Icon,
|
||||
IconButton,
|
||||
Stack,
|
||||
TagList,
|
||||
TextLink,
|
||||
useStyles2,
|
||||
} from '@grafana/ui';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
|
||||
interface DashboardLinkListProps {
|
||||
links: DashboardLink[];
|
||||
@@ -28,19 +39,28 @@ export function DashboardLinkList({
|
||||
|
||||
if (isEmptyList) {
|
||||
return (
|
||||
<div>
|
||||
<EmptyListCTA
|
||||
onClick={onNew}
|
||||
title="There are no dashboard links added yet"
|
||||
buttonIcon="link"
|
||||
buttonTitle="Add dashboard link"
|
||||
infoBoxTitle="What are dashboard links?"
|
||||
infoBox={{
|
||||
__html:
|
||||
'<p>Dashboard Links allow you to place links to other dashboards and web sites directly below the dashboard header.</p>',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Stack direction="column">
|
||||
<EmptyState
|
||||
variant="call-to-action"
|
||||
button={
|
||||
<Button onClick={onNew} size="lg">
|
||||
<Trans i18nKey="dashboard-links.empty-state.button-title">Add dashboard link</Trans>
|
||||
</Button>
|
||||
}
|
||||
message={t('dashboard-links.empty-state.title', 'There are no dashboard links added yet')}
|
||||
>
|
||||
<Trans i18nKey="dashboard-links.empty-state.info-box-content">
|
||||
Dashboard links allow you to place links to other dashboards and web sites directly below the dashboard
|
||||
header.{' '}
|
||||
<TextLink
|
||||
external
|
||||
href="https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/manage-dashboard-links/"
|
||||
>
|
||||
Learn more
|
||||
</TextLink>
|
||||
</Trans>
|
||||
</EmptyState>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { SceneVariable, SceneVariableState } from '@grafana/scenes';
|
||||
import { useStyles2, Stack, Button } from '@grafana/ui';
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
import { useStyles2, Stack, Button, EmptyState, TextLink } from '@grafana/ui';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
|
||||
import { VariableEditorListRow } from './VariableEditorListRow';
|
||||
|
||||
@@ -98,30 +98,38 @@ export function VariableEditorList({
|
||||
|
||||
function EmptyVariablesList({ onAdd }: { onAdd: () => void }): ReactElement {
|
||||
return (
|
||||
<div>
|
||||
<EmptyListCTA
|
||||
title="There are no variables yet"
|
||||
buttonIcon="calculator-alt"
|
||||
buttonTitle="Add variable"
|
||||
infoBox={{
|
||||
__html: ` <p>
|
||||
Variables enable more interactive and dynamic dashboards. Instead of hard-coding things like server
|
||||
or sensor names in your metric queries you can use variables in their place. Variables are shown as
|
||||
list boxes at the top of the dashboard. These drop-down lists make it easy to change the data
|
||||
being displayed in your dashboard. Check out the
|
||||
<a class="external-link" href="https://grafana.com/docs/grafana/latest/variables/" target="_blank">
|
||||
Templates and variables documentation
|
||||
</a>
|
||||
for more information.
|
||||
</p>`,
|
||||
}}
|
||||
infoBoxTitle="What do variables do?"
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
onAdd();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Stack direction="column">
|
||||
<EmptyState
|
||||
variant="call-to-action"
|
||||
button={
|
||||
<Button
|
||||
data-testid={selectors.components.CallToActionCard.buttonV2('Add variable')}
|
||||
icon="calculator-alt"
|
||||
onClick={onAdd}
|
||||
size="lg"
|
||||
>
|
||||
<Trans i18nKey="variables.empty-state.button-title">Add variable</Trans>
|
||||
</Button>
|
||||
}
|
||||
message={t('variables.empty-state.title', 'There are no variables added yet')}
|
||||
>
|
||||
<p>
|
||||
<Trans i18nKey="variables.empty-state.info-box-content">
|
||||
Variables enable more interactive and dynamic dashboards. Instead of hard-coding things like server or
|
||||
sensor names in your metric queries you can use variables in their place. Variables are shown as list boxes
|
||||
at the top of the dashboard. These drop-down lists make it easy to change the data being displayed in your
|
||||
dashboard.
|
||||
</Trans>
|
||||
</p>
|
||||
<Trans i18nKey="variables.empty-state.info-box-content-2">
|
||||
Check out the{' '}
|
||||
<TextLink external href="https://grafana.com/docs/grafana/latest/variables/">
|
||||
Templates and variables documentation
|
||||
</TextLink>{' '}
|
||||
for more information.
|
||||
</Trans>
|
||||
</EmptyState>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+36
-23
@@ -4,8 +4,8 @@ import React, { useState } from 'react';
|
||||
import { arrayUtils, AnnotationQuery } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { getDataSourceSrv } from '@grafana/runtime';
|
||||
import { Button, DeleteButton, IconButton, useStyles2, VerticalGroup } from '@grafana/ui';
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
import { Button, DeleteButton, EmptyState, IconButton, Stack, TextLink, useStyles2 } from '@grafana/ui';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
|
||||
import { DashboardModel } from '../../state/DashboardModel';
|
||||
import { ListNewButton } from '../DashboardSettings/ListNewButton';
|
||||
@@ -54,7 +54,7 @@ export const AnnotationSettingsList = ({ dashboard, onNew, onEdit }: Props) => {
|
||||
|
||||
const dataSourceSrv = getDataSourceSrv();
|
||||
return (
|
||||
<VerticalGroup>
|
||||
<Stack direction="column">
|
||||
{annotations.length > 0 && (
|
||||
<div className={styles.table}>
|
||||
<table role="grid" className="filter-table filter-table--hover">
|
||||
@@ -108,25 +108,38 @@ export const AnnotationSettingsList = ({ dashboard, onNew, onEdit }: Props) => {
|
||||
</div>
|
||||
)}
|
||||
{showEmptyListCTA && (
|
||||
<EmptyListCTA
|
||||
onClick={onNew}
|
||||
title="There are no custom annotation queries added yet"
|
||||
buttonIcon="comment-alt"
|
||||
buttonTitle="Add annotation query"
|
||||
infoBoxTitle="What are annotation queries?"
|
||||
infoBox={{
|
||||
__html: `<p>Annotations provide a way to integrate event data into your graphs. They are visualized as vertical lines
|
||||
and icons on all graph panels. When you hover over an annotation icon you can get event text & tags for
|
||||
the event. You can add annotation events directly from grafana by holding CTRL or CMD + click on graph (or
|
||||
drag region). These will be stored in Grafana's annotation database.
|
||||
</p>
|
||||
Checkout the
|
||||
<a class='external-link' target='_blank' href='http://docs.grafana.org/reference/annotations/'
|
||||
>Annotations documentation</a
|
||||
>
|
||||
for more information.`,
|
||||
}}
|
||||
/>
|
||||
<Stack direction="column">
|
||||
<EmptyState
|
||||
variant="call-to-action"
|
||||
button={
|
||||
<Button
|
||||
data-testid={selectors.components.CallToActionCard.buttonV2('Add annotation query')}
|
||||
icon="comment-alt"
|
||||
onClick={onNew}
|
||||
size="lg"
|
||||
>
|
||||
<Trans i18nKey="annotations.empty-state.button-title">Add annotation query</Trans>
|
||||
</Button>
|
||||
}
|
||||
message={t('annotations.empty-state.title', 'There are no custom annotation queries added yet')}
|
||||
>
|
||||
<Trans i18nKey="annotations.empty-state.info-box-content">
|
||||
<p>
|
||||
Annotations provide a way to integrate event data into your graphs. They are visualized as vertical
|
||||
lines and icons on all graph panels. When you hover over an annotation icon you can get event text &
|
||||
tags for the event. You can add annotation events directly from grafana by holding CTRL or CMD + click
|
||||
on graph (or drag region). These will be stored in Grafana's annotation database.
|
||||
</p>
|
||||
</Trans>
|
||||
<Trans i18nKey="annotations.empty-state.info-box-content-2">
|
||||
Checkout the{' '}
|
||||
<TextLink external href="http://docs.grafana.org/reference/annotations/">
|
||||
Annotations documentation
|
||||
</TextLink>{' '}
|
||||
for more information.
|
||||
</Trans>
|
||||
</EmptyState>
|
||||
</Stack>
|
||||
)}
|
||||
{!showEmptyListCTA && (
|
||||
<ListNewButton
|
||||
@@ -136,7 +149,7 @@ export const AnnotationSettingsList = ({ dashboard, onNew, onEdit }: Props) => {
|
||||
New query
|
||||
</ListNewButton>
|
||||
)}
|
||||
</VerticalGroup>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+3
-8
@@ -3,7 +3,6 @@ import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { TestProvider } from 'test/helpers/TestProvider';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { locationService, setAngularLoader, setDataSourceSrv } from '@grafana/runtime';
|
||||
import { mockDataSource, MockDataSourceSrv } from 'app/features/alerting/unified/mocks';
|
||||
|
||||
@@ -89,9 +88,7 @@ describe('AnnotationsSettings', () => {
|
||||
expect(screen.queryByRole('grid')).toBeInTheDocument();
|
||||
expect(screen.getByRole('row', { name: /annotations & alerts \(built-in\) -- grafana --/i })).toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query'))
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'Add annotation query' })).toBeInTheDocument();
|
||||
expect(screen.queryByRole('link', { name: /annotations documentation/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -99,9 +96,7 @@ describe('AnnotationsSettings', () => {
|
||||
dashboard.annotations.list = [];
|
||||
setup(dashboard);
|
||||
|
||||
expect(
|
||||
screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query'))
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'Add annotation query' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('it renders the annotation names or uid if annotation does not exist', async () => {
|
||||
@@ -185,7 +180,7 @@ describe('AnnotationsSettings', () => {
|
||||
test('Adding a new annotation', async () => {
|
||||
setup(dashboard);
|
||||
|
||||
await userEvent.click(screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query')));
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Add annotation query' }));
|
||||
|
||||
expect(locationService.getSearchObject().editIndex).toBe('1');
|
||||
expect(dashboard.annotations.list.length).toBe(2);
|
||||
|
||||
@@ -5,7 +5,6 @@ import { Provider } from 'react-redux';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import { GrafanaContext } from 'app/core/context/GrafanaContext';
|
||||
|
||||
@@ -93,9 +92,7 @@ describe('LinksSettings', () => {
|
||||
const linksTab = screen.getByRole('tab', { name: 'Tab Links' });
|
||||
expect(linksTab).toBeInTheDocument();
|
||||
expect(linksTab).toHaveAttribute('aria-selected', 'true');
|
||||
expect(
|
||||
screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add dashboard link'))
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'Add dashboard link' })).toBeInTheDocument();
|
||||
expect(screen.queryByRole('table')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -104,9 +101,7 @@ describe('LinksSettings', () => {
|
||||
setup(dashboard);
|
||||
|
||||
expect(getTableBodyRows().length).toBe(dashboard.links.length);
|
||||
expect(
|
||||
screen.queryByTestId(selectors.components.CallToActionCard.buttonV2('Add dashboard link'))
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Add dashboard link' })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('it rearranges the order of dashboard links', async () => {
|
||||
|
||||
@@ -4,10 +4,9 @@ import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { DataSourceSettings, GrafanaTheme2 } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { EmptyState, useStyles2 } from '@grafana/ui';
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
import { EmptyState, LinkButton, TextLink, useStyles2 } from '@grafana/ui';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
import { t } from 'app/core/internationalization';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
import { StoreState, AccessControlAction, useSelector } from 'app/types';
|
||||
|
||||
import { getDataSources, getDataSourcesCount, useDataSourcesRoutes, useLoadDataSources } from '../state';
|
||||
@@ -67,17 +66,25 @@ export function DataSourcesListView({
|
||||
|
||||
if (!isLoading && dataSourcesCount === 0) {
|
||||
return (
|
||||
<EmptyListCTA
|
||||
buttonDisabled={!hasCreateRights}
|
||||
title="No data sources defined"
|
||||
buttonIcon="database"
|
||||
buttonLink={dataSourcesRoutes.New}
|
||||
buttonTitle="Add data source"
|
||||
proTip="You can also define data sources through configuration files."
|
||||
proTipLink="http://docs.grafana.org/administration/provisioning/?utm_source=grafana_ds_list#data-sources"
|
||||
proTipLinkTitle="Learn more"
|
||||
proTipTarget="_blank"
|
||||
/>
|
||||
<EmptyState
|
||||
variant="call-to-action"
|
||||
button={
|
||||
<LinkButton disabled={!hasCreateRights} href={dataSourcesRoutes.New} icon="database" size="lg">
|
||||
<Trans i18nKey="data-source-list.empty-state.button-title">Add data source</Trans>
|
||||
</LinkButton>
|
||||
}
|
||||
message={t('data-source-list.empty-state.title', 'No data sources defined')}
|
||||
>
|
||||
<Trans i18nKey="data-source-list.empty-state.pro-tip">
|
||||
You can also define data sources through configuration files.{' '}
|
||||
<TextLink
|
||||
external
|
||||
href="http://docs.grafana.org/administration/provisioning/?utm_source=grafana_ds_list#data-sources"
|
||||
>
|
||||
Learn more
|
||||
</TextLink>
|
||||
</Trans>
|
||||
</EmptyState>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -105,7 +105,7 @@ describe('LibraryPanelsSearch', () => {
|
||||
await getTestContext();
|
||||
|
||||
expect(screen.getByPlaceholderText(/search by name/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/no library panels found/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/you haven\'t created any library panels yet/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('and user searches for library panel by name or description', () => {
|
||||
@@ -132,7 +132,7 @@ describe('LibraryPanelsSearch', () => {
|
||||
await getTestContext({ showSort: true });
|
||||
|
||||
expect(screen.getByPlaceholderText(/search by name/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/no library panels found/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/you haven\'t created any library panels yet/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/sort \(default a–z\)/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -160,7 +160,7 @@ describe('LibraryPanelsSearch', () => {
|
||||
await getTestContext({ showPanelFilter: true });
|
||||
|
||||
expect(screen.getByPlaceholderText(/search by name/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/no library panels found/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/you haven\'t created any library panels yet/i)).toBeInTheDocument();
|
||||
expect(screen.getByRole('combobox', { name: /panel type filter/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -188,7 +188,7 @@ describe('LibraryPanelsSearch', () => {
|
||||
await getTestContext({ showFolderFilter: true });
|
||||
|
||||
expect(screen.getByPlaceholderText(/search by name/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/no library panels found/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/you haven\'t created any library panels yet/i)).toBeInTheDocument();
|
||||
expect(screen.getByRole('combobox', { name: /folder filter/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -274,7 +274,7 @@ describe('LibraryPanelsSearch', () => {
|
||||
|
||||
const card = () => screen.getByLabelText(/plugin visualization item time series/i);
|
||||
|
||||
expect(screen.queryByText(/no library panels found/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/you haven\'t created any library panels yet/i)).not.toBeInTheDocument();
|
||||
expect(card()).toBeInTheDocument();
|
||||
expect(within(card()).getByText(/library panel name/i)).toBeInTheDocument();
|
||||
expect(within(card()).getByText(/library panel description/i)).toBeInTheDocument();
|
||||
@@ -315,7 +315,7 @@ describe('LibraryPanelsSearch', () => {
|
||||
|
||||
const card = () => screen.getByLabelText(/plugin visualization item time series/i);
|
||||
|
||||
expect(screen.queryByText(/no library panels found/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/you haven\'t created any library panels yet/i)).not.toBeInTheDocument();
|
||||
expect(card()).toBeInTheDocument();
|
||||
expect(within(card()).getByText(/library panel name/i)).toBeInTheDocument();
|
||||
expect(within(card()).getByText(/library panel description/i)).toBeInTheDocument();
|
||||
|
||||
+22
-2
@@ -3,8 +3,8 @@ import React, { useMemo, useReducer } from 'react';
|
||||
import { useDebounce } from 'react-use';
|
||||
|
||||
import { GrafanaTheme2, LoadingState } from '@grafana/data';
|
||||
import { EmptyState, Pagination, Stack, useStyles2 } from '@grafana/ui';
|
||||
import { t } from 'app/core/internationalization';
|
||||
import { EmptyState, Pagination, Stack, TextLink, useStyles2 } from '@grafana/ui';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
|
||||
import { LibraryElementDTO } from '../../types';
|
||||
import { LibraryPanelCard } from '../LibraryPanelCard/LibraryPanelCard';
|
||||
@@ -74,6 +74,26 @@ export const LibraryPanelsView = ({
|
||||
})
|
||||
);
|
||||
const onPageChange = (page: number) => asyncDispatch(changePage({ page }));
|
||||
const hasFilter = searchString || panelFilter?.length || folderFilter?.length;
|
||||
|
||||
if (!hasFilter && loadingState === LoadingState.Done && libraryPanels.length < 1) {
|
||||
return (
|
||||
<EmptyState
|
||||
variant="call-to-action"
|
||||
message={t('library-panel.empty-state.message', "You haven't created any library panels yet")}
|
||||
>
|
||||
<Trans i18nKey="library-panel.empty-state.more-info">
|
||||
Create a library panel from any existing dashboard panel through the panel context menu.{' '}
|
||||
<TextLink
|
||||
external
|
||||
href="https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/manage-library-panels/#create-a-library-panel"
|
||||
>
|
||||
Learn more
|
||||
</TextLink>
|
||||
</Trans>
|
||||
</EmptyState>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack direction="column" wrap="nowrap">
|
||||
|
||||
+2
-2
@@ -99,13 +99,13 @@ const renderPublicDashboardTable = async (waitForListRendering?: boolean) => {
|
||||
</TestProvider>
|
||||
);
|
||||
|
||||
waitForListRendering && (await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')[1], { timeout: 3000 }));
|
||||
waitForListRendering && (await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')[0], { timeout: 3000 }));
|
||||
};
|
||||
|
||||
describe('Show table', () => {
|
||||
it('renders loader spinner while loading', async () => {
|
||||
await renderPublicDashboardTable();
|
||||
const spinner = screen.getAllByTestId('Spinner')[1];
|
||||
const spinner = screen.getAllByTestId('Spinner')[0];
|
||||
expect(spinner).toBeInTheDocument();
|
||||
|
||||
await waitForElementToBeRemoved(spinner);
|
||||
|
||||
+43
-19
@@ -2,7 +2,7 @@ import { css } from '@emotion/css';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useMedia } from 'react-use';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data/src';
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import {
|
||||
@@ -16,7 +16,9 @@ import {
|
||||
Switch,
|
||||
Pagination,
|
||||
HorizontalGroup,
|
||||
} from '@grafana/ui/src';
|
||||
EmptyState,
|
||||
TextLink,
|
||||
} from '@grafana/ui';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
@@ -141,28 +143,50 @@ export const PublicDashboardListTable = () => {
|
||||
const [page, setPage] = useState(1);
|
||||
|
||||
const styles = useStyles2(getStyles);
|
||||
const { data: paginatedPublicDashboards, isLoading, isFetching, isError } = useListPublicDashboardsQuery(page);
|
||||
const { data: paginatedPublicDashboards, isLoading, isError } = useListPublicDashboardsQuery(page);
|
||||
|
||||
return (
|
||||
<Page navId="dashboards/public" actions={isFetching && <Spinner />}>
|
||||
<Page navId="dashboards/public">
|
||||
<Page.Contents isLoading={isLoading}>
|
||||
{!isLoading && !isError && !!paginatedPublicDashboards && (
|
||||
<div>
|
||||
<ul className={styles.list}>
|
||||
{paginatedPublicDashboards.publicDashboards.map((pd: PublicDashboardListResponse) => (
|
||||
<li key={pd.uid}>
|
||||
<PublicDashboardCard pd={pd} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<HorizontalGroup justify="flex-end">
|
||||
<Pagination
|
||||
onNavigate={setPage}
|
||||
currentPage={paginatedPublicDashboards.page}
|
||||
numberOfPages={paginatedPublicDashboards.totalPages}
|
||||
hideWhenSinglePage
|
||||
/>
|
||||
</HorizontalGroup>
|
||||
{paginatedPublicDashboards.publicDashboards.length === 0 ? (
|
||||
<EmptyState
|
||||
variant="call-to-action"
|
||||
message={t(
|
||||
'public-dashboard-list.empty-state.message',
|
||||
"You haven't created any public dashboards yet"
|
||||
)}
|
||||
>
|
||||
<Trans i18nKey="public-dashboard-list.empty-state.more-info">
|
||||
Create a public dashboard from any existing dashboard through the <b>Share</b> modal.{' '}
|
||||
<TextLink
|
||||
external
|
||||
href="https://grafana.com/docs/grafana/latest/dashboards/dashboard-public/#make-a-dashboard-public"
|
||||
>
|
||||
Learn more
|
||||
</TextLink>
|
||||
</Trans>
|
||||
</EmptyState>
|
||||
) : (
|
||||
<>
|
||||
<ul className={styles.list}>
|
||||
{paginatedPublicDashboards.publicDashboards.map((pd: PublicDashboardListResponse) => (
|
||||
<li key={pd.uid}>
|
||||
<PublicDashboardCard pd={pd} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<HorizontalGroup justify="flex-end">
|
||||
<Pagination
|
||||
onNavigate={setPage}
|
||||
currentPage={paginatedPublicDashboards.page}
|
||||
numberOfPages={paginatedPublicDashboards.totalPages}
|
||||
hideWhenSinglePage
|
||||
/>
|
||||
</HorizontalGroup>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Page.Contents>
|
||||
|
||||
@@ -2,13 +2,13 @@ import React, { useState, useCallback } from 'react';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
import { ConfirmModal } from '@grafana/ui';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
import { ConfirmModal, EmptyState, TextLink } from '@grafana/ui';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
import { getDashboardSnapshotSrv, Snapshot } from 'app/features/dashboard/services/SnapshotSrv';
|
||||
|
||||
import { SnapshotListTableRow } from './SnapshotListTableRow';
|
||||
|
||||
export function getSnapshots() {
|
||||
export async function getSnapshots() {
|
||||
return getDashboardSnapshotSrv()
|
||||
.getSnapshots()
|
||||
.then((result: Snapshot[]) => {
|
||||
@@ -42,6 +42,25 @@ export const SnapshotListTable = () => {
|
||||
[snapshots]
|
||||
);
|
||||
|
||||
if (!isFetching && snapshots.length === 0) {
|
||||
return (
|
||||
<EmptyState
|
||||
variant="call-to-action"
|
||||
message={t('snapshot.empty-state.message', "You haven't created any snapshots yet")}
|
||||
>
|
||||
<Trans i18nKey="snapshot.empty-state.more-info">
|
||||
You can create a snapshot of any dashboard through the <b>Share</b> modal.{' '}
|
||||
<TextLink
|
||||
external
|
||||
href="https://grafana.com/docs/grafana/latest/dashboards/share-dashboards-panels/#publish-a-snapshot"
|
||||
>
|
||||
Learn more
|
||||
</TextLink>
|
||||
</Trans>
|
||||
</EmptyState>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<table className="filter-table">
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { ConfirmModal, EmptyState, LinkButton } from '@grafana/ui';
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
import { ConfirmModal, EmptyState, LinkButton, TextLink } from '@grafana/ui';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import PageActionBar from 'app/core/components/PageActionBar/PageActionBar';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
@@ -35,20 +34,6 @@ export const PlaylistPage = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const emptyListBanner = (
|
||||
<EmptyListCTA
|
||||
title={t('playlist-page.empty.title', 'There are no playlists created yet')}
|
||||
buttonIcon="plus"
|
||||
buttonLink="playlists/new"
|
||||
buttonTitle={t('playlist-page.empty.button', 'Create Playlist')}
|
||||
buttonDisabled={!contextSrv.isEditor}
|
||||
proTip={t('playlist-page.empty.pro-tip', 'You can use playlists to cycle dashboards on TVs without user control')}
|
||||
proTipLink="http://docs.grafana.org/reference/playlist/"
|
||||
proTipLinkTitle={t('playlist-page.empty.pro-tip-link-title', 'Learn more')}
|
||||
proTipTarget="_blank"
|
||||
/>
|
||||
);
|
||||
|
||||
const showSearch = allPlaylists.loading || playlists.length > 0 || searchQuery.length > 0;
|
||||
|
||||
return (
|
||||
@@ -78,7 +63,24 @@ export const PlaylistPage = () => {
|
||||
setPlaylistToDelete={setPlaylistToDelete}
|
||||
/>
|
||||
)}
|
||||
{!showSearch && emptyListBanner}
|
||||
{!showSearch && (
|
||||
<EmptyState
|
||||
variant="call-to-action"
|
||||
button={
|
||||
<LinkButton disabled={!contextSrv.isEditor} href="playlists/new" icon="plus" size="lg">
|
||||
<Trans i18nKey="playlist-page.empty.button">Create playlist</Trans>
|
||||
</LinkButton>
|
||||
}
|
||||
message={t('playlist-page.empty.title', 'There are no playlists created yet')}
|
||||
>
|
||||
<Trans i18nKey="playlist-page.empty.pro-tip">
|
||||
You can use playlists to cycle dashboards on TVs without user control.{' '}
|
||||
<TextLink external href="https://docs.grafana.org/reference/playlist/">
|
||||
Learn more
|
||||
</TextLink>
|
||||
</Trans>
|
||||
</EmptyState>
|
||||
)}
|
||||
{playlistToDelete && (
|
||||
<ConfirmModal
|
||||
title={playlistToDelete.name}
|
||||
|
||||
@@ -4,11 +4,10 @@ import { connect, ConnectedProps } from 'react-redux';
|
||||
|
||||
import { OrgRole } from '@grafana/data';
|
||||
import { ConfirmModal, FilterInput, LinkButton, RadioButtonGroup, InlineField, EmptyState, Box } from '@grafana/ui';
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import config from 'app/core/config';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
import { t } from 'app/core/internationalization';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
import { StoreState, ServiceAccountDTO, AccessControlAction, ServiceAccountStateFilter } from 'app/types';
|
||||
|
||||
import { ServiceAccountTable } from './ServiceAccountTable';
|
||||
@@ -217,19 +216,24 @@ export const ServiceAccountsListPageUnconnected = ({
|
||||
/>
|
||||
)}
|
||||
{!isLoading && noServiceAccountsCreated && (
|
||||
<>
|
||||
<EmptyListCTA
|
||||
title="You haven't created any service accounts yet."
|
||||
buttonIcon="key-skeleton-alt"
|
||||
buttonLink="org/serviceaccounts/create"
|
||||
buttonTitle="Add service account"
|
||||
buttonDisabled={!contextSrv.hasPermission(AccessControlAction.ServiceAccountsCreate)}
|
||||
proTip="Remember, you can provide specific permissions for API access to other applications."
|
||||
proTipLink=""
|
||||
proTipLinkTitle=""
|
||||
proTipTarget="_blank"
|
||||
/>
|
||||
</>
|
||||
<EmptyState
|
||||
variant="call-to-action"
|
||||
button={
|
||||
<LinkButton
|
||||
disabled={!contextSrv.hasPermission(AccessControlAction.ServiceAccountsCreate)}
|
||||
href="org/serviceaccounts/create"
|
||||
icon="key-skeleton-alt"
|
||||
size="lg"
|
||||
>
|
||||
<Trans i18nKey="service-accounts.empty-state.button-title">Add service account</Trans>
|
||||
</LinkButton>
|
||||
}
|
||||
message={t('service-accounts.empty-state.title', "You haven't created any service accounts yet")}
|
||||
>
|
||||
<Trans i18nKey="service-accounts.empty-state.more-info">
|
||||
Remember, you can provide specific permissions for API access to other applications
|
||||
</Trans>
|
||||
</EmptyState>
|
||||
)}
|
||||
|
||||
{(isLoading || serviceAccounts.length !== 0) && (
|
||||
|
||||
@@ -19,10 +19,9 @@ import {
|
||||
TextLink,
|
||||
useStyles2,
|
||||
} from '@grafana/ui';
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { fetchRoleOptions } from 'app/core/components/RolePicker/api';
|
||||
import { t } from 'app/core/internationalization';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import { AccessControlAction, Role, StoreState, Team } from 'app/types';
|
||||
|
||||
@@ -208,24 +207,31 @@ export const TeamList = ({
|
||||
<Page
|
||||
navId="teams"
|
||||
actions={
|
||||
<LinkButton href={canCreate ? 'org/teams/new' : '#'} disabled={!canCreate}>
|
||||
New Team
|
||||
</LinkButton>
|
||||
!noTeams ? (
|
||||
<LinkButton href={canCreate ? 'org/teams/new' : '#'} disabled={!canCreate}>
|
||||
New Team
|
||||
</LinkButton>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
<Page.Contents>
|
||||
{noTeams ? (
|
||||
<EmptyListCTA
|
||||
title="You haven't created any teams yet."
|
||||
buttonIcon="users-alt"
|
||||
buttonLink="org/teams/new"
|
||||
buttonTitle=" New team"
|
||||
buttonDisabled={!contextSrv.hasPermission(AccessControlAction.ActionTeamsCreate)}
|
||||
proTip="Assign folder and dashboard permissions to teams instead of users to ease administration."
|
||||
proTipLink=""
|
||||
proTipLinkTitle=""
|
||||
proTipTarget="_blank"
|
||||
/>
|
||||
<EmptyState
|
||||
variant="call-to-action"
|
||||
button={
|
||||
<LinkButton disabled={!canCreate} href="org/teams/new" icon="users-alt" size="lg">
|
||||
<Trans i18nKey="teams.empty-state.button-title">New team</Trans>
|
||||
</LinkButton>
|
||||
}
|
||||
message={t('teams.empty-state.title', "You haven't created any teams yet")}
|
||||
>
|
||||
<Trans i18nKey="teams.empty-state.pro-tip">
|
||||
Assign folder and dashboard permissions to teams instead of users to ease administration.{' '}
|
||||
<TextLink external href="https://grafana.com/docs/grafana/latest/administration/team-management">
|
||||
Learn more
|
||||
</TextLink>
|
||||
</Trans>
|
||||
</EmptyState>
|
||||
) : (
|
||||
<>
|
||||
<div className="page-action-bar">
|
||||
|
||||
@@ -5,8 +5,8 @@ import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd';
|
||||
import { TypedVariableModel } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { Button, useStyles2, Stack } from '@grafana/ui';
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
import { Button, useStyles2, Stack, EmptyState, TextLink } from '@grafana/ui';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
|
||||
import { VariablesDependenciesButton } from '../inspect/VariablesDependenciesButton';
|
||||
import { UsagesToNetwork, VariableUsageTree } from '../inspect/utils';
|
||||
@@ -107,30 +107,38 @@ export function VariableEditorList({
|
||||
|
||||
function EmptyVariablesList({ onAdd }: { onAdd: () => void }): ReactElement {
|
||||
return (
|
||||
<div>
|
||||
<EmptyListCTA
|
||||
title="There are no variables yet"
|
||||
buttonIcon="calculator-alt"
|
||||
buttonTitle="Add variable"
|
||||
infoBox={{
|
||||
__html: ` <p>
|
||||
Variables enable more interactive and dynamic dashboards. Instead of hard-coding things like server
|
||||
or sensor names in your metric queries you can use variables in their place. Variables are shown as
|
||||
list boxes at the top of the dashboard. These drop-down lists make it easy to change the data
|
||||
being displayed in your dashboard. Check out the
|
||||
<a class="external-link" href="https://grafana.com/docs/grafana/latest/variables/" target="_blank">
|
||||
Templates and variables documentation
|
||||
</a>
|
||||
for more information.
|
||||
</p>`,
|
||||
}}
|
||||
infoBoxTitle="What do variables do?"
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
onAdd();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Stack direction="column">
|
||||
<EmptyState
|
||||
variant="call-to-action"
|
||||
button={
|
||||
<Button
|
||||
data-testid={selectors.components.CallToActionCard.buttonV2('Add variable')}
|
||||
icon="calculator-alt"
|
||||
onClick={onAdd}
|
||||
size="lg"
|
||||
>
|
||||
<Trans i18nKey="variables.empty-state.button-title">Add variable</Trans>
|
||||
</Button>
|
||||
}
|
||||
message={t('variables.empty-state.title', 'There are no variables added yet')}
|
||||
>
|
||||
<p>
|
||||
<Trans i18nKey="variables.empty-state.info-box-content">
|
||||
Variables enable more interactive and dynamic dashboards. Instead of hard-coding things like server or
|
||||
sensor names in your metric queries you can use variables in their place. Variables are shown as list boxes
|
||||
at the top of the dashboard. These drop-down lists make it easy to change the data being displayed in your
|
||||
dashboard.
|
||||
</Trans>
|
||||
</p>
|
||||
<Trans i18nKey="variables.empty-state.info-box-content-2">
|
||||
Check out the{' '}
|
||||
<TextLink external href="https://grafana.com/docs/grafana/latest/variables/">
|
||||
Templates and variables documentation
|
||||
</TextLink>{' '}
|
||||
for more information.
|
||||
</Trans>
|
||||
</EmptyState>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,14 @@
|
||||
"user": "User"
|
||||
}
|
||||
},
|
||||
"annotations": {
|
||||
"empty-state": {
|
||||
"button-title": "Add annotation query",
|
||||
"info-box-content": "<0>Annotations provide a way to integrate event data into your graphs. They are visualized as vertical lines and icons on all graph panels. When you hover over an annotation icon you can get event text & tags for the event. You can add annotation events directly from grafana by holding CTRL or CMD + click on graph (or drag region). These will be stored in Grafana's annotation database.</0>",
|
||||
"info-box-content-2": "Checkout the <2>Annotations documentation</2> for more information.",
|
||||
"title": "There are no custom annotation queries added yet"
|
||||
}
|
||||
},
|
||||
"api-keys": {
|
||||
"empty-state": {
|
||||
"message": "No API keys found"
|
||||
@@ -72,6 +80,12 @@
|
||||
"select-checkbox": "Select",
|
||||
"tags-column": "Tags"
|
||||
},
|
||||
"empty-state": {
|
||||
"button-title": "Create dashboard",
|
||||
"pro-tip": "Add/move dashboards to your folder at <2>Browse dashboards</2>",
|
||||
"title": "You haven't created any dashboards yet",
|
||||
"title-folder": "This folder doesn't have any dashboards yet"
|
||||
},
|
||||
"folder-actions-button": {
|
||||
"delete": "Delete",
|
||||
"folder-actions": "Folder actions",
|
||||
@@ -159,6 +173,11 @@
|
||||
"sub-text": "<0>Define text that will describe the correlation.</0>",
|
||||
"title": "Define correlation label (Step 1 of 3)"
|
||||
},
|
||||
"empty-state": {
|
||||
"button-title": "Add correlation",
|
||||
"pro-tip": "You can also define correlations via datasource provisioning",
|
||||
"title": "You haven't defined any correlations yet"
|
||||
},
|
||||
"list": {
|
||||
"delete": "delete correlation",
|
||||
"label": "Label",
|
||||
@@ -355,6 +374,13 @@
|
||||
"validation-required": "Need a dashboard JSON model"
|
||||
}
|
||||
},
|
||||
"dashboard-links": {
|
||||
"empty-state": {
|
||||
"button-title": "Add dashboard link",
|
||||
"info-box-content": "Dashboard links allow you to place links to other dashboards and web sites directly below the dashboard header. <2>Learn more</2>",
|
||||
"title": "There are no dashboard links added yet"
|
||||
}
|
||||
},
|
||||
"dashboard-settings": {
|
||||
"annotations": {
|
||||
"title": "Annotations"
|
||||
@@ -405,6 +431,13 @@
|
||||
"title": "Versions"
|
||||
}
|
||||
},
|
||||
"data-source-list": {
|
||||
"empty-state": {
|
||||
"button-title": "Add data source",
|
||||
"pro-tip": "You can also define data sources through configuration files. <2>Learn more</2>",
|
||||
"title": "No data sources defined"
|
||||
}
|
||||
},
|
||||
"data-source-picker": {
|
||||
"add-new-data-source": "Configure a new data source",
|
||||
"built-in-list": {
|
||||
@@ -657,6 +690,10 @@
|
||||
},
|
||||
"add-widget": {
|
||||
"title": "Add panel from panel library"
|
||||
},
|
||||
"empty-state": {
|
||||
"message": "You haven't created any library panels yet",
|
||||
"more-info": "Create a library panel from any existing dashboard panel through the panel context menu. <2>Learn more</2>"
|
||||
}
|
||||
},
|
||||
"library-panels": {
|
||||
@@ -1230,9 +1267,8 @@
|
||||
"confirm-text": "Delete"
|
||||
},
|
||||
"empty": {
|
||||
"button": "Create Playlist",
|
||||
"pro-tip": "You can use playlists to cycle dashboards on TVs without user control",
|
||||
"pro-tip-link-title": "Learn more",
|
||||
"button": "Create playlist",
|
||||
"pro-tip": "You can use playlists to cycle dashboards on TVs without user control. <2>Learn more</2>",
|
||||
"title": "There are no playlists created yet"
|
||||
}
|
||||
},
|
||||
@@ -1346,6 +1382,10 @@
|
||||
"orphaned-title": "<0>Orphaned public dashboard</0>",
|
||||
"orphaned-tooltip": "The linked dashboard has already been deleted"
|
||||
},
|
||||
"empty-state": {
|
||||
"message": "You haven't created any public dashboards yet",
|
||||
"more-info": "Create a public dashboard from any existing dashboard through the <1>Share</1> modal. <4>Learn more</4>"
|
||||
},
|
||||
"toggle": {
|
||||
"pause-sharing-toggle-text": "Pause sharing"
|
||||
}
|
||||
@@ -1450,7 +1490,10 @@
|
||||
},
|
||||
"service-accounts": {
|
||||
"empty-state": {
|
||||
"message": "No services accounts found"
|
||||
"button-title": "Add service account",
|
||||
"message": "No services accounts found",
|
||||
"more-info": "Remember, you can provide specific permissions for API access to other applications",
|
||||
"title": "You haven't created any service accounts yet"
|
||||
}
|
||||
},
|
||||
"share-modal": {
|
||||
@@ -1570,7 +1613,17 @@
|
||||
},
|
||||
"title": "Preferences"
|
||||
},
|
||||
"silences": {
|
||||
"empty-state": {
|
||||
"button-title": "Create silence",
|
||||
"title": "You haven't created any silences yet"
|
||||
}
|
||||
},
|
||||
"snapshot": {
|
||||
"empty-state": {
|
||||
"message": "You haven't created any snapshots yet",
|
||||
"more-info": "You can create a snapshot of any dashboard through the <1>Share</1> modal. <4>Learn more</4>"
|
||||
},
|
||||
"external-badge": "External",
|
||||
"name-column-header": "Name",
|
||||
"url-column-header": "Snapshot url",
|
||||
@@ -1583,7 +1636,10 @@
|
||||
},
|
||||
"teams": {
|
||||
"empty-state": {
|
||||
"message": "No teams found"
|
||||
"button-title": "New team",
|
||||
"message": "No teams found",
|
||||
"pro-tip": "Assign folder and dashboard permissions to teams instead of users to ease administration. <2>Learn more</2>",
|
||||
"title": "You haven't created any teams yet"
|
||||
}
|
||||
},
|
||||
"time-picker": {
|
||||
@@ -1705,5 +1761,13 @@
|
||||
"textbox": {
|
||||
"placeholder": "Enter variable value"
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"empty-state": {
|
||||
"button-title": "Add variable",
|
||||
"info-box-content": "Variables enable more interactive and dynamic dashboards. Instead of hard-coding things like server or sensor names in your metric queries you can use variables in their place. Variables are shown as list boxes at the top of the dashboard. These drop-down lists make it easy to change the data being displayed in your dashboard.",
|
||||
"info-box-content-2": "Check out the <2>Templates and variables documentation</2> for more information.",
|
||||
"title": "There are no variables added yet"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,14 @@
|
||||
"user": "Ůşęř"
|
||||
}
|
||||
},
|
||||
"annotations": {
|
||||
"empty-state": {
|
||||
"button-title": "Åđđ äʼnʼnőŧäŧįőʼn qūęřy",
|
||||
"info-box-content": "<0>Åʼnʼnőŧäŧįőʼnş přővįđę ä ŵäy ŧő įʼnŧęģřäŧę ęvęʼnŧ đäŧä įʼnŧő yőūř ģřäpĥş. Ŧĥęy äřę vįşūäľįžęđ äş vęřŧįčäľ ľįʼnęş äʼnđ įčőʼnş őʼn äľľ ģřäpĥ päʼnęľş. Ŵĥęʼn yőū ĥővęř ővęř äʼn äʼnʼnőŧäŧįőʼn įčőʼn yőū čäʼn ģęŧ ęvęʼnŧ ŧęχŧ & ŧäģş ƒőř ŧĥę ęvęʼnŧ. Ÿőū čäʼn äđđ äʼnʼnőŧäŧįőʼn ęvęʼnŧş đįřęčŧľy ƒřőm ģřäƒäʼnä þy ĥőľđįʼnģ CŦŖĿ őř CMĐ + čľįčĸ őʼn ģřäpĥ (őř đřäģ řęģįőʼn). Ŧĥęşę ŵįľľ þę şŧőřęđ įʼn Ğřäƒäʼnä'ş äʼnʼnőŧäŧįőʼn đäŧäþäşę.</0>",
|
||||
"info-box-content-2": "Cĥęčĸőūŧ ŧĥę <2>Åʼnʼnőŧäŧįőʼnş đőčūmęʼnŧäŧįőʼn</2> ƒőř mőřę įʼnƒőřmäŧįőʼn.",
|
||||
"title": "Ŧĥęřę äřę ʼnő čūşŧőm äʼnʼnőŧäŧįőʼn qūęřįęş äđđęđ yęŧ"
|
||||
}
|
||||
},
|
||||
"api-keys": {
|
||||
"empty-state": {
|
||||
"message": "Ńő ÅPĨ ĸęyş ƒőūʼnđ"
|
||||
@@ -72,6 +80,12 @@
|
||||
"select-checkbox": "Ŝęľęčŧ",
|
||||
"tags-column": "Ŧäģş"
|
||||
},
|
||||
"empty-state": {
|
||||
"button-title": "Cřęäŧę đäşĥþőäřđ",
|
||||
"pro-tip": "Åđđ/mővę đäşĥþőäřđş ŧő yőūř ƒőľđęř äŧ <2>ßřőŵşę đäşĥþőäřđş</2>",
|
||||
"title": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny đäşĥþőäřđş yęŧ",
|
||||
"title-folder": "Ŧĥįş ƒőľđęř đőęşʼn'ŧ ĥävę äʼny đäşĥþőäřđş yęŧ"
|
||||
},
|
||||
"folder-actions-button": {
|
||||
"delete": "Đęľęŧę",
|
||||
"folder-actions": "Főľđęř äčŧįőʼnş",
|
||||
@@ -159,6 +173,11 @@
|
||||
"sub-text": "<0>Đęƒįʼnę ŧęχŧ ŧĥäŧ ŵįľľ đęşčřįþę ŧĥę čőřřęľäŧįőʼn.</0>",
|
||||
"title": "Đęƒįʼnę čőřřęľäŧįőʼn ľäþęľ (Ŝŧęp 1 őƒ 3)"
|
||||
},
|
||||
"empty-state": {
|
||||
"button-title": "Åđđ čőřřęľäŧįőʼn",
|
||||
"pro-tip": "Ÿőū čäʼn äľşő đęƒįʼnę čőřřęľäŧįőʼnş vįä đäŧäşőūřčę přővįşįőʼnįʼnģ",
|
||||
"title": "Ÿőū ĥävęʼn'ŧ đęƒįʼnęđ äʼny čőřřęľäŧįőʼnş yęŧ"
|
||||
},
|
||||
"list": {
|
||||
"delete": "đęľęŧę čőřřęľäŧįőʼn",
|
||||
"label": "Ŀäþęľ",
|
||||
@@ -355,6 +374,13 @@
|
||||
"validation-required": "Ńęęđ ä đäşĥþőäřđ ĴŜØŃ mőđęľ"
|
||||
}
|
||||
},
|
||||
"dashboard-links": {
|
||||
"empty-state": {
|
||||
"button-title": "Åđđ đäşĥþőäřđ ľįʼnĸ",
|
||||
"info-box-content": "Đäşĥþőäřđ ľįʼnĸş äľľőŵ yőū ŧő pľäčę ľįʼnĸş ŧő őŧĥęř đäşĥþőäřđş äʼnđ ŵęþ şįŧęş đįřęčŧľy þęľőŵ ŧĥę đäşĥþőäřđ ĥęäđęř. <2>Ŀęäřʼn mőřę</2>",
|
||||
"title": "Ŧĥęřę äřę ʼnő đäşĥþőäřđ ľįʼnĸş äđđęđ yęŧ"
|
||||
}
|
||||
},
|
||||
"dashboard-settings": {
|
||||
"annotations": {
|
||||
"title": "Åʼnʼnőŧäŧįőʼnş"
|
||||
@@ -405,6 +431,13 @@
|
||||
"title": "Vęřşįőʼnş"
|
||||
}
|
||||
},
|
||||
"data-source-list": {
|
||||
"empty-state": {
|
||||
"button-title": "Åđđ đäŧä şőūřčę",
|
||||
"pro-tip": "Ÿőū čäʼn äľşő đęƒįʼnę đäŧä şőūřčęş ŧĥřőūģĥ čőʼnƒįģūřäŧįőʼn ƒįľęş. <2>Ŀęäřʼn mőřę</2>",
|
||||
"title": "Ńő đäŧä şőūřčęş đęƒįʼnęđ"
|
||||
}
|
||||
},
|
||||
"data-source-picker": {
|
||||
"add-new-data-source": "Cőʼnƒįģūřę ä ʼnęŵ đäŧä şőūřčę",
|
||||
"built-in-list": {
|
||||
@@ -657,6 +690,10 @@
|
||||
},
|
||||
"add-widget": {
|
||||
"title": "Åđđ päʼnęľ ƒřőm päʼnęľ ľįþřäřy"
|
||||
},
|
||||
"empty-state": {
|
||||
"message": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny ľįþřäřy päʼnęľş yęŧ",
|
||||
"more-info": "Cřęäŧę ä ľįþřäřy päʼnęľ ƒřőm äʼny ęχįşŧįʼnģ đäşĥþőäřđ päʼnęľ ŧĥřőūģĥ ŧĥę päʼnęľ čőʼnŧęχŧ męʼnū. <2>Ŀęäřʼn mőřę</2>"
|
||||
}
|
||||
},
|
||||
"library-panels": {
|
||||
@@ -1230,9 +1267,8 @@
|
||||
"confirm-text": "Đęľęŧę"
|
||||
},
|
||||
"empty": {
|
||||
"button": "Cřęäŧę Pľäyľįşŧ",
|
||||
"pro-tip": "Ÿőū čäʼn ūşę pľäyľįşŧş ŧő čyčľę đäşĥþőäřđş őʼn ŦVş ŵįŧĥőūŧ ūşęř čőʼnŧřőľ",
|
||||
"pro-tip-link-title": "Ŀęäřʼn mőřę",
|
||||
"button": "Cřęäŧę pľäyľįşŧ",
|
||||
"pro-tip": "Ÿőū čäʼn ūşę pľäyľįşŧş ŧő čyčľę đäşĥþőäřđş őʼn ŦVş ŵįŧĥőūŧ ūşęř čőʼnŧřőľ. <2>Ŀęäřʼn mőřę</2>",
|
||||
"title": "Ŧĥęřę äřę ʼnő pľäyľįşŧş čřęäŧęđ yęŧ"
|
||||
}
|
||||
},
|
||||
@@ -1346,6 +1382,10 @@
|
||||
"orphaned-title": "<0>Øřpĥäʼnęđ pūþľįč đäşĥþőäřđ</0>",
|
||||
"orphaned-tooltip": "Ŧĥę ľįʼnĸęđ đäşĥþőäřđ ĥäş äľřęäđy þęęʼn đęľęŧęđ"
|
||||
},
|
||||
"empty-state": {
|
||||
"message": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny pūþľįč đäşĥþőäřđş yęŧ",
|
||||
"more-info": "Cřęäŧę ä pūþľįč đäşĥþőäřđ ƒřőm äʼny ęχįşŧįʼnģ đäşĥþőäřđ ŧĥřőūģĥ ŧĥę <1>Ŝĥäřę</1> mőđäľ. <4>Ŀęäřʼn mőřę</4>"
|
||||
},
|
||||
"toggle": {
|
||||
"pause-sharing-toggle-text": "Päūşę şĥäřįʼnģ"
|
||||
}
|
||||
@@ -1450,7 +1490,10 @@
|
||||
},
|
||||
"service-accounts": {
|
||||
"empty-state": {
|
||||
"message": "Ńő şęřvįčęş äččőūʼnŧş ƒőūʼnđ"
|
||||
"button-title": "Åđđ şęřvįčę äččőūʼnŧ",
|
||||
"message": "Ńő şęřvįčęş äččőūʼnŧş ƒőūʼnđ",
|
||||
"more-info": "Ŗęmęmþęř, yőū čäʼn přővįđę şpęčįƒįč pęřmįşşįőʼnş ƒőř ÅPĨ äččęşş ŧő őŧĥęř äppľįčäŧįőʼnş",
|
||||
"title": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny şęřvįčę äččőūʼnŧş yęŧ"
|
||||
}
|
||||
},
|
||||
"share-modal": {
|
||||
@@ -1570,7 +1613,17 @@
|
||||
},
|
||||
"title": "Přęƒęřęʼnčęş"
|
||||
},
|
||||
"silences": {
|
||||
"empty-state": {
|
||||
"button-title": "Cřęäŧę şįľęʼnčę",
|
||||
"title": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny şįľęʼnčęş yęŧ"
|
||||
}
|
||||
},
|
||||
"snapshot": {
|
||||
"empty-state": {
|
||||
"message": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny şʼnäpşĥőŧş yęŧ",
|
||||
"more-info": "Ÿőū čäʼn čřęäŧę ä şʼnäpşĥőŧ őƒ äʼny đäşĥþőäřđ ŧĥřőūģĥ ŧĥę <1>Ŝĥäřę</1> mőđäľ. <4>Ŀęäřʼn mőřę</4>"
|
||||
},
|
||||
"external-badge": "Ēχŧęřʼnäľ",
|
||||
"name-column-header": "Ńämę",
|
||||
"url-column-header": "Ŝʼnäpşĥőŧ ūřľ",
|
||||
@@ -1583,7 +1636,10 @@
|
||||
},
|
||||
"teams": {
|
||||
"empty-state": {
|
||||
"message": "Ńő ŧęämş ƒőūʼnđ"
|
||||
"button-title": "Ńęŵ ŧęäm",
|
||||
"message": "Ńő ŧęämş ƒőūʼnđ",
|
||||
"pro-tip": "Åşşįģʼn ƒőľđęř äʼnđ đäşĥþőäřđ pęřmįşşįőʼnş ŧő ŧęämş įʼnşŧęäđ őƒ ūşęřş ŧő ęäşę äđmįʼnįşŧřäŧįőʼn. <2>Ŀęäřʼn mőřę</2>",
|
||||
"title": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny ŧęämş yęŧ"
|
||||
}
|
||||
},
|
||||
"time-picker": {
|
||||
@@ -1705,5 +1761,13 @@
|
||||
"textbox": {
|
||||
"placeholder": "Ēʼnŧęř väřįäþľę väľūę"
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"empty-state": {
|
||||
"button-title": "Åđđ väřįäþľę",
|
||||
"info-box-content": "Väřįäþľęş ęʼnäþľę mőřę įʼnŧęřäčŧįvę äʼnđ đyʼnämįč đäşĥþőäřđş. Ĩʼnşŧęäđ őƒ ĥäřđ-čőđįʼnģ ŧĥįʼnģş ľįĸę şęřvęř őř şęʼnşőř ʼnämęş įʼn yőūř męŧřįč qūęřįęş yőū čäʼn ūşę väřįäþľęş įʼn ŧĥęįř pľäčę. Väřįäþľęş äřę şĥőŵʼn äş ľįşŧ þőχęş äŧ ŧĥę ŧőp őƒ ŧĥę đäşĥþőäřđ. Ŧĥęşę đřőp-đőŵʼn ľįşŧş mäĸę įŧ ęäşy ŧő čĥäʼnģę ŧĥę đäŧä þęįʼnģ đįşpľäyęđ įʼn yőūř đäşĥþőäřđ.",
|
||||
"info-box-content-2": "Cĥęčĸ őūŧ ŧĥę <2>Ŧęmpľäŧęş äʼnđ väřįäþľęş đőčūmęʼnŧäŧįőʼn</2> ƒőř mőřę įʼnƒőřmäŧįőʼn.",
|
||||
"title": "Ŧĥęřę äřę ʼnő väřįäþľęş äđđęđ yęŧ"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user