SaveProvisionedDashboardForm: Show preview banner when pushing to non-configured existing branch (#112947)

* SaveProvisionedDashboardForm: Show preview banner when pushing to non-configured existing branch

* useProvisionedRequestHandler: use ref to prevent handler triggered twice
This commit is contained in:
Yunwen Zheng
2025-10-28 09:34:18 -04:00
committed by GitHub
parent 2a5ce2f031
commit 238244fe5c
2 changed files with 64 additions and 37 deletions
@@ -1,4 +1,4 @@
import { useEffect } from 'react';
import { useCallback, useEffect } from 'react';
import { Controller, useForm, FormProvider } from 'react-hook-form';
import { useNavigate } from 'react-router-dom-v5-compat';
@@ -56,7 +56,7 @@ export function SaveProvisionedDashboardForm({
const methods = useForm<ProvisionedDashboardFormData>({ defaultValues });
const { handleSubmit, watch, control, reset, register } = methods;
const [workflow] = watch(['workflow']);
const [workflow, ref, path] = watch(['workflow', 'ref', 'path']);
// Update the form if default values change
useEffect(() => {
@@ -70,46 +70,35 @@ export function SaveProvisionedDashboardForm({
});
};
const handleNewDashboard = (upsert: Resource<Dashboard>) => {
// Navigation for new dashboards
const url = locationUtil.assureBaseUrl(
getDashboardUrl({
uid: upsert.metadata.name,
slug: kbn.slugifyForUrl(upsert.spec.title ?? ''),
currentQueryParams: window.location.search,
})
);
navigate(url);
};
const handleNewDashboard = useCallback(
(upsert: Resource<Dashboard>) => {
// Navigation for new dashboards
const url = locationUtil.assureBaseUrl(
getDashboardUrl({
uid: upsert.metadata.name,
slug: kbn.slugifyForUrl(upsert.spec.title ?? ''),
currentQueryParams: window.location.search,
})
);
navigate(url);
},
[navigate]
);
const onWriteSuccess = (_: ProvisionedOperationInfo, upsert: Resource<Dashboard>) => {
handleDismiss();
if (isNew && upsert?.metadata.name) {
handleNewDashboard(upsert);
} else {
locationService.partial({
viewPanel: null,
editPanel: null,
});
}
};
const onBranchSuccess = (ref: string, path: string, info: ProvisionedOperationInfo, upsert: Resource<Dashboard>) => {
handleDismiss();
if (isNew && upsert?.metadata?.name) {
handleNewDashboard(upsert);
} else {
const navigateToPreview = useCallback(
(ref: string, path: string, repoType: string) => {
const url = buildResourceBranchRedirectUrl({
baseUrl: `${PROVISIONING_URL}/${defaultValues.repo}/dashboard/preview/${path}`,
paramName: 'ref',
paramValue: ref,
repoType: info.repoType,
repoType,
});
navigate(url);
}
};
},
[navigate, defaultValues.repo]
);
const handleDismiss = () => {
const handleDismiss = useCallback(() => {
panelEditor?.onDiscard();
const model = dashboard.getSaveModel();
@@ -118,7 +107,40 @@ export function SaveProvisionedDashboardForm({
dashboard.saveCompleted(model, saveResponse, defaultValues.folder?.uid);
drawer.onClose();
};
}, [dashboard, defaultValues.folder?.uid, drawer, panelEditor, request?.data?.resource]);
const onWriteSuccess = useCallback(
({ repoType }: ProvisionedOperationInfo, upsert: Resource<Dashboard>) => {
handleDismiss();
if (isNew && upsert?.metadata.name) {
handleNewDashboard(upsert);
}
// if pushed to an existing but non-configured branch, navigate to preview page
if (ref !== repository?.branch && ref) {
navigateToPreview(ref, path, repoType);
return;
}
locationService.partial({
viewPanel: null,
editPanel: null,
});
},
[isNew, path, ref, repository?.branch, handleDismiss, handleNewDashboard, navigateToPreview]
);
const onBranchSuccess = useCallback(
(ref: string, path: string, info: ProvisionedOperationInfo, upsert: Resource<Dashboard>) => {
handleDismiss();
if (isNew && upsert?.metadata?.name) {
handleNewDashboard(upsert);
} else {
navigateToPreview(ref, path, info.repoType);
}
},
[isNew, navigateToPreview, handleNewDashboard, handleDismiss]
);
useProvisionedRequestHandler<Dashboard>({
folderUID: defaultValues.folder?.uid,
@@ -1,4 +1,4 @@
import { useEffect } from 'react';
import { useEffect, useRef } from 'react';
import { AppEvents } from '@grafana/data';
import { t } from '@grafana/i18n';
@@ -74,6 +74,9 @@ export function useProvisionedRequestHandler<T>({
resourceType,
}: Props<T>) {
const dispatch = useDispatch();
// useRef to ensure handlers are only called once per request
const hasHandled = useRef(false);
useEffect(() => {
const repoType = repository?.type || 'git';
const info: ProvisionedOperationInfo = {
@@ -83,11 +86,13 @@ export function useProvisionedRequestHandler<T>({
};
if (request.isError) {
hasHandled.current = true;
handlers.onError?.(request.error, info);
return;
}
if (request.isSuccess && request.data) {
if (request.isSuccess && request.data && !hasHandled.current) {
hasHandled.current = true;
const { ref, path, urls, resource } = request.data;
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const resourceData = resource.upsert as Resource<T>;