mirror of https://github.com/grafana/grafana.git
BootstrapStep: Enhancement resource manage card options (#110723)
* BootstrapStep: card reorganize * ConnectRepositoryButton: Move to Provisioning page actions spot
This commit is contained in:
parent
d5fca9a5fa
commit
fc0db985c6
|
@ -10,6 +10,7 @@ import { Page } from 'app/core/components/Page/Page';
|
||||||
|
|
||||||
import GettingStarted from './GettingStarted/GettingStarted';
|
import GettingStarted from './GettingStarted/GettingStarted';
|
||||||
import GettingStartedPage from './GettingStarted/GettingStartedPage';
|
import GettingStartedPage from './GettingStarted/GettingStartedPage';
|
||||||
|
import { ConnectRepositoryButton } from './Shared/ConnectRepositoryButton';
|
||||||
import { RepositoryList } from './Shared/RepositoryList';
|
import { RepositoryList } from './Shared/RepositoryList';
|
||||||
import { InlineSecureValueWarning } from './components/InlineSecureValueWarning';
|
import { InlineSecureValueWarning } from './components/InlineSecureValueWarning';
|
||||||
import { useRepositoryList } from './hooks/useRepositoryList';
|
import { useRepositoryList } from './hooks/useRepositoryList';
|
||||||
|
@ -67,6 +68,7 @@ export default function HomePage() {
|
||||||
<Page
|
<Page
|
||||||
navId="provisioning"
|
navId="provisioning"
|
||||||
subTitle={t('provisioning.home-page.subtitle', 'View and manage your configured repositories')}
|
subTitle={t('provisioning.home-page.subtitle', 'View and manage your configured repositories')}
|
||||||
|
actions={activeTab === TabSelection.Repositories && <ConnectRepositoryButton items={items} />}
|
||||||
>
|
>
|
||||||
<Page.Contents isLoading={isLoading}>
|
<Page.Contents isLoading={isLoading}>
|
||||||
{settings.data?.legacyStorage && (
|
{settings.data?.legacyStorage && (
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useNavigate } from 'react-router-dom-v5-compat';
|
import { useNavigate } from 'react-router-dom-v5-compat';
|
||||||
|
|
||||||
import { Trans } from '@grafana/i18n';
|
import { t, Trans } from '@grafana/i18n';
|
||||||
import { Alert, Button, Dropdown, Icon, Menu, Stack } from '@grafana/ui';
|
import { Button, Dropdown, Icon, Menu, Stack } from '@grafana/ui';
|
||||||
import { Repository } from 'app/api/clients/provisioning/v0alpha1';
|
import { Repository } from 'app/api/clients/provisioning/v0alpha1';
|
||||||
import { useGetFrontendSettingsQuery } from 'app/api/clients/provisioning/v0alpha1/endpoints.gen';
|
import { useGetFrontendSettingsQuery } from 'app/api/clients/provisioning/v0alpha1/endpoints.gen';
|
||||||
|
|
||||||
|
@ -18,22 +18,7 @@ export function ConnectRepositoryButton({ items }: Props) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { data: frontendSettings } = useGetFrontendSettingsQuery();
|
const { data: frontendSettings } = useGetFrontendSettingsQuery();
|
||||||
|
|
||||||
if (state.instanceConnected) {
|
const isButtonDisabled = state.instanceConnected || state.maxReposReached;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.maxReposReached) {
|
|
||||||
return (
|
|
||||||
<Alert title="" severity="info">
|
|
||||||
<Trans
|
|
||||||
i18nKey="provisioning.connect-repository-button.repository-limit-info-alert"
|
|
||||||
values={{ count: state.repoCount }}
|
|
||||||
>
|
|
||||||
Repository limit reached ({'{{count}}'})
|
|
||||||
</Trans>
|
|
||||||
</Alert>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const availableTypes = frontendSettings?.availableRepositoryTypes || DEFAULT_REPOSITORY_TYPES;
|
const availableTypes = frontendSettings?.availableRepositoryTypes || DEFAULT_REPOSITORY_TYPES;
|
||||||
const { orderedConfigs } = getOrderedRepositoryConfigs(availableTypes);
|
const { orderedConfigs } = getOrderedRepositoryConfigs(availableTypes);
|
||||||
|
@ -55,7 +40,15 @@ export function ConnectRepositoryButton({ items }: Props) {
|
||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Button variant="primary">
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
disabled={isButtonDisabled}
|
||||||
|
tooltip={getConfigureRepoTooltip({
|
||||||
|
instanceConnected: state.instanceConnected,
|
||||||
|
maxReposReached: state.maxReposReached,
|
||||||
|
count: state.repoCount,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<Stack alignItems="center">
|
<Stack alignItems="center">
|
||||||
<Trans i18nKey="provisioning.connect-repository-button.configure">Configure</Trans>
|
<Trans i18nKey="provisioning.connect-repository-button.configure">Configure</Trans>
|
||||||
<Icon name={'angle-down'} />
|
<Icon name={'angle-down'} />
|
||||||
|
@ -64,3 +57,32 @@ export function ConnectRepositoryButton({ items }: Props) {
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getConfigureRepoTooltip({
|
||||||
|
instanceConnected,
|
||||||
|
maxReposReached,
|
||||||
|
count,
|
||||||
|
}: {
|
||||||
|
instanceConnected: boolean;
|
||||||
|
maxReposReached: boolean;
|
||||||
|
count: number;
|
||||||
|
}) {
|
||||||
|
if (instanceConnected) {
|
||||||
|
return t(
|
||||||
|
'provisioning.connect-repository-button.instance-fully-managed-tooltip',
|
||||||
|
'Configuration is disabled because this instance is fully managed'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxReposReached) {
|
||||||
|
return t(
|
||||||
|
'provisioning.connect-repository-button.repository-limit-reached-tooltip',
|
||||||
|
'Repository limit reached {{count}}',
|
||||||
|
{
|
||||||
|
count,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
|
@ -7,8 +7,6 @@ import { Repository } from 'app/api/clients/provisioning/v0alpha1';
|
||||||
import { RepositoryCard } from '../Repository/RepositoryCard';
|
import { RepositoryCard } from '../Repository/RepositoryCard';
|
||||||
import { checkSyncSettings } from '../utils/checkSyncSettings';
|
import { checkSyncSettings } from '../utils/checkSyncSettings';
|
||||||
|
|
||||||
import { ConnectRepositoryButton } from './ConnectRepositoryButton';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
items: Repository[];
|
items: Repository[];
|
||||||
}
|
}
|
||||||
|
@ -27,7 +25,6 @@ export function RepositoryList({ items }: Props) {
|
||||||
value={query}
|
value={query}
|
||||||
onChange={setQuery}
|
onChange={setQuery}
|
||||||
/>
|
/>
|
||||||
<ConnectRepositoryButton items={items} />
|
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
<Stack direction={'column'} gap={2}>
|
<Stack direction={'column'} gap={2}>
|
||||||
|
|
|
@ -140,9 +140,8 @@ describe('BootstrapStep', () => {
|
||||||
|
|
||||||
it('should render correct info for GitHub repository type', async () => {
|
it('should render correct info for GitHub repository type', async () => {
|
||||||
setup();
|
setup();
|
||||||
expect(await screen.findByText('Grafana instance')).toBeInTheDocument();
|
expect(screen.getAllByText('External storage')).toHaveLength(2);
|
||||||
expect(screen.getByText('External storage')).toBeInTheDocument();
|
expect(screen.getAllByText('Empty')).toHaveLength(3); // Three elements should have the role "Empty" (2 external + 1 unmanaged)
|
||||||
expect(screen.getAllByText('Empty')).toHaveLength(2); // Both should show empty
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render correct info for local file repository type', async () => {
|
it('should render correct info for local file repository type', async () => {
|
||||||
|
@ -170,7 +169,7 @@ describe('BootstrapStep', () => {
|
||||||
|
|
||||||
setup();
|
setup();
|
||||||
|
|
||||||
expect(await screen.findByText('2 files')).toBeInTheDocument();
|
expect(await screen.getAllByText('2 files')).toHaveLength(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display resource counts when resources exist', async () => {
|
it('should display resource counts when resources exist', async () => {
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
|
import { css } from '@emotion/css';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { Controller, useFormContext } from 'react-hook-form';
|
import { Controller, useFormContext } from 'react-hook-form';
|
||||||
|
|
||||||
import { Trans, t } from '@grafana/i18n';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { Box, Card, Field, Input, LoadingPlaceholder, Stack, Text } from '@grafana/ui';
|
import { t } from '@grafana/i18n';
|
||||||
|
import { Box, Card, Field, Input, LoadingPlaceholder, Stack, Text, useStyles2 } from '@grafana/ui';
|
||||||
import { RepositoryViewList } from 'app/api/clients/provisioning/v0alpha1';
|
import { RepositoryViewList } from 'app/api/clients/provisioning/v0alpha1';
|
||||||
import { generateRepositoryTitle } from 'app/features/provisioning/utils/data';
|
import { generateRepositoryTitle } from 'app/features/provisioning/utils/data';
|
||||||
|
|
||||||
|
import { BootstrapStepCardIcons } from './BootstrapStepCardIcons';
|
||||||
|
import { BootstrapStepResourceCounting } from './BootstrapStepResourceCounting';
|
||||||
import { useStepStatus } from './StepStatusContext';
|
import { useStepStatus } from './StepStatusContext';
|
||||||
import { useModeOptions } from './hooks/useModeOptions';
|
import { useModeOptions } from './hooks/useModeOptions';
|
||||||
import { useResourceStats } from './hooks/useResourceStats';
|
import { useResourceStats } from './hooks/useResourceStats';
|
||||||
|
@ -28,9 +32,11 @@ export function BootstrapStep({ settingsData, repoName }: Props) {
|
||||||
} = useFormContext<WizardFormData>();
|
} = useFormContext<WizardFormData>();
|
||||||
|
|
||||||
const selectedTarget = watch('repository.sync.target');
|
const selectedTarget = watch('repository.sync.target');
|
||||||
|
const repositoryType = watch('repository.type');
|
||||||
const options = useModeOptions(repoName, settingsData);
|
const options = useModeOptions(repoName, settingsData);
|
||||||
const { target } = options[0];
|
const { target } = options[0];
|
||||||
const { resourceCountString, fileCountString, isLoading } = useResourceStats(repoName, settingsData?.legacyStorage);
|
const { resourceCountString, fileCountString, isLoading } = useResourceStats(repoName, settingsData?.legacyStorage);
|
||||||
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Pick a name nice name based on type+settings
|
// Pick a name nice name based on type+settings
|
||||||
|
@ -60,25 +66,6 @@ export function BootstrapStep({ settingsData, repoName }: Props) {
|
||||||
return (
|
return (
|
||||||
<Stack direction="column" gap={2}>
|
<Stack direction="column" gap={2}>
|
||||||
<Stack direction="column" gap={2}>
|
<Stack direction="column" gap={2}>
|
||||||
<Box alignItems="center" padding={4}>
|
|
||||||
<Stack direction="row" gap={4} alignItems="flex-start" justifyContent="center">
|
|
||||||
<Stack direction="column" gap={1} alignItems="center">
|
|
||||||
<Text color="secondary">
|
|
||||||
<Trans i18nKey="provisioning.bootstrap-step.grafana">Grafana instance</Trans>
|
|
||||||
</Text>
|
|
||||||
<Stack direction="row" gap={2}>
|
|
||||||
<Text variant="h4">{resourceCountString}</Text>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
<Stack direction="column" gap={1} alignItems="center">
|
|
||||||
<Text color="secondary">
|
|
||||||
<Trans i18nKey="provisioning.bootstrap-step.ext-storage">External storage</Trans>
|
|
||||||
</Text>
|
|
||||||
<Text variant="h4">{fileCountString}</Text>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Controller
|
<Controller
|
||||||
name="repository.sync.target"
|
name="repository.sync.target"
|
||||||
control={control}
|
control={control}
|
||||||
|
@ -94,12 +81,27 @@ export function BootstrapStep({ settingsData, repoName }: Props) {
|
||||||
noMargin
|
noMargin
|
||||||
{...field}
|
{...field}
|
||||||
>
|
>
|
||||||
<Card.Heading>{action.label}</Card.Heading>
|
<Card.Heading>
|
||||||
|
<Text variant="h5">{action.label}</Text>
|
||||||
|
</Card.Heading>
|
||||||
<Card.Description>
|
<Card.Description>
|
||||||
|
<div className={styles.divider} />
|
||||||
|
|
||||||
|
<Box paddingBottom={2}>
|
||||||
|
<BootstrapStepCardIcons target={action.target} repoType={repositoryType} />
|
||||||
|
</Box>
|
||||||
<Stack direction="column" gap={3}>
|
<Stack direction="column" gap={3}>
|
||||||
{action.description}
|
{action.description}
|
||||||
<Text color="primary">{action.subtitle}</Text>
|
<Text color="primary">{action.subtitle}</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
|
<div className={styles.divider} />
|
||||||
|
|
||||||
|
<BootstrapStepResourceCounting
|
||||||
|
target={action.target}
|
||||||
|
fileCountString={fileCountString}
|
||||||
|
resourceCountString={resourceCountString}
|
||||||
|
/>
|
||||||
</Card.Description>
|
</Card.Description>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
@ -138,3 +140,13 @@ export function BootstrapStep({ settingsData, repoName }: Props) {
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
|
divider: css({
|
||||||
|
height: 1,
|
||||||
|
width: '100%',
|
||||||
|
backgroundColor: theme.colors.border.medium,
|
||||||
|
marginTop: theme.spacing(2),
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { Icon, Stack } from '@grafana/ui';
|
||||||
|
|
||||||
|
import { RepoIcon } from '../Shared/RepoIcon';
|
||||||
|
|
||||||
|
import { RepoType, Target } from './types';
|
||||||
|
|
||||||
|
export function BootstrapStepCardIcons({ target, repoType }: { target: Target; repoType: RepoType }) {
|
||||||
|
if (target === 'instance') {
|
||||||
|
return (
|
||||||
|
<Stack direction="row">
|
||||||
|
<Icon name="grafana" size="xxl" />
|
||||||
|
<Icon name="arrows-h" size="xxl" />
|
||||||
|
<RepoIcon type="github" />
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target === 'folder') {
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
<Icon name="folder" size="xxl" />
|
||||||
|
<Icon name="arrow-left" size="xxl" />
|
||||||
|
<RepoIcon type={repoType} />
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { Trans } from '@grafana/i18n';
|
||||||
|
import { Stack, Text } from '@grafana/ui';
|
||||||
|
|
||||||
|
import { Target } from './types';
|
||||||
|
|
||||||
|
export function BootstrapStepResourceCounting({
|
||||||
|
target,
|
||||||
|
fileCountString,
|
||||||
|
resourceCountString,
|
||||||
|
}: {
|
||||||
|
target: Target;
|
||||||
|
fileCountString: string;
|
||||||
|
resourceCountString: string;
|
||||||
|
}) {
|
||||||
|
if (target === 'instance') {
|
||||||
|
return (
|
||||||
|
<Stack direction="row" gap={3}>
|
||||||
|
<Stack gap={1}>
|
||||||
|
<Trans i18nKey="provisioning.bootstrap-step.external-storage-label">External storage</Trans>
|
||||||
|
<Text color="primary">{fileCountString}</Text>
|
||||||
|
</Stack>
|
||||||
|
<Stack gap={1}>
|
||||||
|
<Trans i18nKey="provisioning.bootstrap-step.unmanaged-resources-label">Unmanaged resources</Trans>{' '}
|
||||||
|
<Text color="primary">{resourceCountString}</Text>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target === 'folder') {
|
||||||
|
return (
|
||||||
|
<Stack gap={1}>
|
||||||
|
<Trans i18nKey="provisioning.bootstrap-step.external-storage-label">External storage</Trans>{' '}
|
||||||
|
<Text color="primary">{fileCountString}</Text>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
|
@ -11215,15 +11215,15 @@
|
||||||
"description-clear-repository-connection": "Add a clear name for this repository connection",
|
"description-clear-repository-connection": "Add a clear name for this repository connection",
|
||||||
"empty": "Empty",
|
"empty": "Empty",
|
||||||
"error-field-required": "This field is required.",
|
"error-field-required": "This field is required.",
|
||||||
"ext-storage": "External storage",
|
"external-storage-label": "External storage",
|
||||||
"files-count_one": "{{count}} files",
|
"files-count_one": "{{count}} files",
|
||||||
"files-count_other": "{{count}} files",
|
"files-count_other": "{{count}} files",
|
||||||
"folders-count_one": "{{count}} folder",
|
"folders-count_one": "{{count}} folder",
|
||||||
"folders-count_other": "{{count}} folder",
|
"folders-count_other": "{{count}} folder",
|
||||||
"grafana": "Grafana instance",
|
|
||||||
"label-display-name": "Display name",
|
"label-display-name": "Display name",
|
||||||
"placeholder-my-repository-connection": "My repository connection",
|
"placeholder-my-repository-connection": "My repository connection",
|
||||||
"text-loading-resource-information": "Loading resource information..."
|
"text-loading-resource-information": "Loading resource information...",
|
||||||
|
"unmanaged-resources-label": "Unmanaged resources"
|
||||||
},
|
},
|
||||||
"check-repository": {
|
"check-repository": {
|
||||||
"check": "Check"
|
"check": "Check"
|
||||||
|
@ -11265,7 +11265,9 @@
|
||||||
},
|
},
|
||||||
"connect-repository-button": {
|
"connect-repository-button": {
|
||||||
"configure": "Configure",
|
"configure": "Configure",
|
||||||
"repository-limit-info-alert": "Repository limit reached ({{count}})"
|
"instance-fully-managed-tooltip": "Configuration is disabled because this instance is fully managed",
|
||||||
|
"repository-limit-reached-tooltip_one": "Repository limit reached {{count}}",
|
||||||
|
"repository-limit-reached-tooltip_other": "Repository limit reached {{count}}"
|
||||||
},
|
},
|
||||||
"delete-repository-button": {
|
"delete-repository-button": {
|
||||||
"button-delete": "Delete",
|
"button-delete": "Delete",
|
||||||
|
|
Loading…
Reference in New Issue