grafana/public/app/features/provisioning/Wizard/BootstrapStep.tsx

153 lines
5.1 KiB
TypeScript

import { css } from '@emotion/css';
import { useEffect } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { GrafanaTheme2 } from '@grafana/data';
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 { generateRepositoryTitle } from 'app/features/provisioning/utils/data';
import { BootstrapStepCardIcons } from './BootstrapStepCardIcons';
import { BootstrapStepResourceCounting } from './BootstrapStepResourceCounting';
import { useStepStatus } from './StepStatusContext';
import { useModeOptions } from './hooks/useModeOptions';
import { useResourceStats } from './hooks/useResourceStats';
import { WizardFormData } from './types';
export interface Props {
settingsData?: RepositoryViewList;
repoName: string;
}
export function BootstrapStep({ settingsData, repoName }: Props) {
const { setStepStatusInfo } = useStepStatus();
const {
register,
control,
setValue,
watch,
getValues,
formState: { errors },
} = useFormContext<WizardFormData>();
const selectedTarget = watch('repository.sync.target');
const repositoryType = watch('repository.type');
const options = useModeOptions(repoName, settingsData);
const { target } = options[0];
const { resourceCountString, fileCountString, isLoading } = useResourceStats(repoName, settingsData?.legacyStorage);
const styles = useStyles2(getStyles);
useEffect(() => {
// Pick a name nice name based on type+settings
const repository = getValues('repository');
const title = generateRepositoryTitle(repository);
setValue('repository.title', title);
}, [getValues, setValue]);
useEffect(() => {
setStepStatusInfo({ status: isLoading ? 'running' : 'idle' });
}, [isLoading, setStepStatusInfo]);
useEffect(() => {
setValue('repository.sync.target', target);
}, [target, setValue]);
if (isLoading) {
return (
<Box padding={4}>
<LoadingPlaceholder
text={t('provisioning.bootstrap-step.text-loading-resource-information', 'Loading resource information...')}
/>
</Box>
);
}
return (
<Stack direction="column" gap={2}>
<Stack direction="column" gap={2}>
<Controller
name="repository.sync.target"
control={control}
render={({ field: { ref, onChange, ...field } }) => (
<>
{options.map((action) => (
<Card
key={action.target}
isSelected={action.target === selectedTarget}
onClick={() => {
onChange(action.target);
}}
noMargin
{...field}
>
<Card.Heading>
<Text variant="h5">{action.label}</Text>
</Card.Heading>
<Card.Description>
<div className={styles.divider} />
<Box paddingBottom={2}>
<BootstrapStepCardIcons target={action.target} repoType={repositoryType} />
</Box>
<Stack direction="column" gap={3}>
{action.description}
<Text color="primary">{action.subtitle}</Text>
</Stack>
<div className={styles.divider} />
<BootstrapStepResourceCounting
target={action.target}
fileCountString={fileCountString}
resourceCountString={resourceCountString}
/>
</Card.Description>
</Card>
))}
</>
)}
/>
{/* Only show title field if folder sync */}
{selectedTarget === 'folder' && (
<Field
label={t('provisioning.bootstrap-step.label-display-name', 'Display name')}
description={t(
'provisioning.bootstrap-step.description-clear-repository-connection',
'Add a clear name for this repository connection'
)}
error={errors.repository?.title?.message}
invalid={!!errors.repository?.title}
required
noMargin
>
<Input
id="repository-title"
{...register('repository.title', {
required: t('provisioning.bootstrap-step.error-field-required', 'This field is required.'),
})}
placeholder={t(
'provisioning.bootstrap-step.placeholder-my-repository-connection',
'My repository connection'
)}
// Autofocus the title field if it's the only available option
autoFocus={options.length === 1 && options[0].target === 'folder'}
/>
</Field>
)}
</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),
}),
});