BulkDeleteProvisionedResource: Move progress bar into a second step (#108417)

* Move progress bar into a second step

---------

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
This commit is contained in:
Yunwen Zheng
2025-07-22 10:31:08 -04:00
committed by GitHub
co-authored by Alex Khomenko
parent 0dbede9ba5
commit 2c86460fe3
4 changed files with 97 additions and 59 deletions
@@ -1,5 +1,5 @@
import { Trans } from '@grafana/i18n';
import { Box, Text } from '@grafana/ui';
import { Box, Icon, Text, Stack } from '@grafana/ui';
import ProgressBar from 'app/features/provisioning/Shared/ProgressBar';
export interface ProgressState {
@@ -13,16 +13,19 @@ export function BulkActionProgress({ progress }: { progress: ProgressState }) {
return (
<Box>
<Text>
<Trans
i18nKey="browse-dashboards.bulk-move-resources-form.progress"
defaults="Progress: {{current}} of {{total}}"
values={{ current: progress.current, total: progress.total }}
/>
</Text>
<ProgressBar progress={progressPercentage} />
<Stack direction="row" alignItems="center">
<Text>
<Trans
i18nKey="browse-dashboards.bulk-move-resources-form.progress"
defaults="Progress: {{current}} of {{total}}"
values={{ current: progress.current, total: progress.total }}
/>
</Text>
<Icon name="spinner" className="fa-spin" size="sm" />
</Stack>
<ProgressBar progress={progressPercentage} topBottomSpacing={1} />
<Text variant="bodySmall" color="secondary">
{progress.item}
<Trans i18nKey="browse-dashboards.bulk-move-resources-form.deleting" defaults="Deleting:" /> {progress.item}
</Text>
</Box>
);
@@ -2,10 +2,8 @@ import { useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom-v5-compat';
import { AppEvents } from '@grafana/data';
import { Trans, t } from '@grafana/i18n';
import { getAppEvents } from '@grafana/runtime';
import { Box, Button, Stack } from '@grafana/ui';
import { Alert, Box, Button, Stack } from '@grafana/ui';
import {
DeleteRepositoryFilesWithPathApiArg,
DeleteRepositoryFilesWithPathApiResponse,
@@ -56,6 +54,11 @@ type BulkSuccessResponse = Array<{
data: DeleteRepositoryFilesWithPathApiResponse;
}>;
type MoveResultSuccessState = {
allSuccess: boolean;
repoUrl?: string;
};
function FormContent({
initialValues,
selectedItems,
@@ -65,10 +68,17 @@ function FormContent({
isGitHub,
onDismiss,
}: FormProps) {
const [deleteRepoFile, request] = useDeleteRepositoryFilesWithPathMutation();
// States
const [progress, setProgress] = useState<ProgressState | null>(null);
const [failureResults, setFailureResults] = useState<MoveResultFailed[] | undefined>();
const [successState, setSuccessState] = useState<MoveResultSuccessState>({
allSuccess: false,
repoUrl: '',
});
const [hasSubmitted, setHasSubmitted] = useState(false);
// Hooks
const [deleteRepoFile, request] = useDeleteRepositoryFilesWithPathMutation();
const methods = useForm<BulkDeleteFormData>({ defaultValues: initialValues });
const childrenByParentUID = useChildrenByParentUIDState();
const rootItems = useSelector(rootItemsSelector);
@@ -84,21 +94,11 @@ function FormContent({
return isFolder ? `${folderPath}/${item.title}/` : fetchProvisionedDashboardPath(uid);
};
const handleSuccess = (successes: BulkSuccessResponse) => {
getAppEvents().publish({
type: AppEvents.alertSuccess.name,
payload: [
t('browse-dashboards.bulk-delete-resources-form.api-success', `Successfully deleted {{count}} items`, {
count: successes.length,
}),
],
});
const handleSuccess = () => {
if (workflow === 'branch') {
onDismiss?.();
const repoUrl = successes[0].data.urls?.repositoryURL;
if (repoUrl) {
navigate({ search: `?repo_url=${encodeURIComponent(repoUrl)}` });
if (successState.repoUrl) {
navigate({ search: `?repo_url=${encodeURIComponent(successState.repoUrl)}` });
return;
}
window.location.reload();
@@ -110,6 +110,7 @@ function FormContent({
const handleSubmitForm = async (data: BulkDeleteFormData) => {
setFailureResults(undefined);
setHasSubmitted(true);
const targets = collectSelectedItems(selectedItems, childrenByParentUID, rootItems?.items || []);
@@ -175,12 +176,43 @@ function FormContent({
setProgress(null);
if (successes.length > 0 && failures.length === 0) {
handleSuccess(successes);
// handleSuccess(successes);
setSuccessState({
allSuccess: true,
repoUrl: successes[0].data.urls?.repositoryURL,
});
} else if (failures.length > 0) {
setFailureResults(failures);
}
};
const getPostSubmitContent = () => {
if (progress) {
return <BulkActionProgress progress={progress} />;
}
if (successState.allSuccess) {
return (
<>
<Alert severity="success" title={t('browse-dashboards.bulk-delete-resources-form.progress-title', 'Success')}>
<Trans i18nKey="browse-dashboards.bulk-delete-resources-form.success-message">
All resources have been deleted successfully.
</Trans>
</Alert>
<Stack gap={2}>
<Button onClick={() => handleSuccess()}>
<Trans i18nKey="browse-dashboards.bulk-delete-resources-form.button-done">Done</Trans>
</Button>
</Stack>
</>
);
} else if (failureResults) {
return <BulkActionFailureBanner result={failureResults} onDismiss={() => setFailureResults(undefined)} />;
}
return null;
};
return (
<FormProvider {...methods}>
<form onSubmit={handleSubmit(handleSubmitForm)}>
@@ -192,31 +224,31 @@ function FormContent({
<DescendantCount selectedItems={{ ...selectedItems, panel: {}, $all: false }} />
</Box>
{failureResults && (
<BulkActionFailureBanner result={failureResults} onDismiss={() => setFailureResults(undefined)} />
{hasSubmitted ? (
getPostSubmitContent()
) : (
<>
<ResourceEditFormSharedFields
resourceType="folder"
isNew={false}
workflow={workflow}
workflowOptions={workflowOptions}
isGitHub={isGitHub}
hidePath
/>
<Stack gap={2}>
<Button type="submit" disabled={request.isLoading || !!failureResults} variant="destructive">
{request.isLoading
? t('browse-dashboards.bulk-delete-resources-form.button-deleting', 'Deleting...')
: t('browse-dashboards.bulk-delete-resources-form.button-delete', 'Delete')}
</Button>
<Button variant="secondary" fill="outline" onClick={onDismiss} disabled={request.isLoading}>
<Trans i18nKey="browse-dashboards.bulk-delete-resources-form.button-cancel">Cancel</Trans>
</Button>
</Stack>
</>
)}
{progress && <BulkActionProgress progress={progress} />}
<ResourceEditFormSharedFields
resourceType="folder"
isNew={false}
workflow={workflow}
workflowOptions={workflowOptions}
isGitHub={isGitHub}
hidePath
/>
<Stack gap={2}>
<Button type="submit" disabled={request.isLoading || !!failureResults} variant="destructive">
{request.isLoading
? t('browse-dashboards.bulk-delete-resources-form.button-deleting', 'Deleting...')
: t('browse-dashboards.bulk-delete-resources-form.button-delete', 'Delete')}
</Button>
<Button variant="secondary" fill="outline" onClick={onDismiss}>
<Trans i18nKey="browse-dashboards.bulk-delete-resources-form.button-cancel">Cancel</Trans>
</Button>
</Stack>
</Stack>
</form>
</FormProvider>
@@ -5,9 +5,10 @@ import { useStyles2 } from '@grafana/ui';
interface ProgressBarProps {
progress?: number;
topBottomSpacing?: number;
}
const ProgressBar = ({ progress }: ProgressBarProps) => {
const styles = useStyles2(getStyles);
const ProgressBar = ({ progress, topBottomSpacing }: ProgressBarProps) => {
const styles = useStyles2(getStyles, topBottomSpacing);
if (progress === undefined) {
return null;
@@ -20,14 +21,14 @@ const ProgressBar = ({ progress }: ProgressBarProps) => {
);
};
const getStyles = (theme: GrafanaTheme2) => ({
const getStyles = (theme: GrafanaTheme2, topBottomSpacing = 2) => ({
container: css({
height: '10px',
width: '400px',
backgroundColor: theme.colors.background.secondary,
borderRadius: theme.shape.radius.pill,
overflow: 'hidden',
margin: theme.spacing(2, 0),
margin: theme.spacing(topBottomSpacing, 0),
}),
filler: css({
height: '100%',
+5 -3
View File
@@ -3516,15 +3516,17 @@
"failed-alert_other": "{{count}} items failed"
},
"bulk-delete-resources-form": {
"api-success_one": "Successfully deleted {{count}} item",
"api-success_other": "Successfully deleted {{count}} items",
"button-cancel": "Cancel",
"button-delete": "Delete",
"button-deleting": "Deleting...",
"button-done": "Done",
"delete-warning": "This will delete selected folders and their descendants. In total, this will affect:",
"error-path-not-found": "Path not found"
"error-path-not-found": "Path not found",
"progress-title": "Success",
"success-message": "All resources have been deleted successfully."
},
"bulk-move-resources-form": {
"deleting": "Deleting:",
"progress": "Progress: {{current}} of {{total}}"
},
"counts": {