Provisioning: Centralise app notifications (#103290)
* Provisioning: Centralise app event notifications * Remove test started notification * Remove unused import
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
import { notifyApp } from 'app/core/actions';
|
||||
import { createSuccessNotification, createErrorNotification } from 'app/core/copy/appNotification';
|
||||
import { t } from 'app/core/internationalization';
|
||||
|
||||
import {
|
||||
generatedAPI,
|
||||
JobSpec,
|
||||
@@ -13,26 +17,158 @@ import { createOnCacheEntryAdded } from './utils/createOnCacheEntryAdded';
|
||||
|
||||
export const provisioningAPI = generatedAPI.enhanceEndpoints({
|
||||
endpoints: {
|
||||
listJob(endpoint) {
|
||||
listJob: {
|
||||
// Do not include 'watch' in the first query, so we can get the initial list of jobs
|
||||
// and then start watching for changes
|
||||
endpoint.query = ({ watch, ...queryArg }) => ({
|
||||
query: ({ watch, ...queryArg }) => ({
|
||||
url: `/jobs`,
|
||||
params: queryArg,
|
||||
});
|
||||
endpoint.onCacheEntryAdded = createOnCacheEntryAdded<JobSpec, JobStatus, Job, JobList>('jobs');
|
||||
}),
|
||||
onCacheEntryAdded: createOnCacheEntryAdded<JobSpec, JobStatus, Job, JobList>('jobs'),
|
||||
},
|
||||
listRepository(endpoint) {
|
||||
endpoint.query = ({ watch, ...queryArg }) => ({
|
||||
listRepository: {
|
||||
query: ({ watch, ...queryArg }) => ({
|
||||
url: `/repositories`,
|
||||
params: queryArg,
|
||||
});
|
||||
endpoint.onCacheEntryAdded = createOnCacheEntryAdded<
|
||||
RepositorySpec,
|
||||
RepositoryStatus,
|
||||
Repository,
|
||||
RepositoryList
|
||||
>('repositories');
|
||||
}),
|
||||
onCacheEntryAdded: createOnCacheEntryAdded<RepositorySpec, RepositoryStatus, Repository, RepositoryList>(
|
||||
'repositories'
|
||||
),
|
||||
},
|
||||
deleteRepository: {
|
||||
onQueryStarted: async (_, { queryFulfilled, dispatch }) => {
|
||||
try {
|
||||
await queryFulfilled;
|
||||
dispatch(
|
||||
notifyApp(
|
||||
createSuccessNotification(
|
||||
t(
|
||||
'provisioning.delete-repository-button.success-repository-deleted',
|
||||
'Repository settings queued for deletion'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
dispatch(
|
||||
notifyApp(
|
||||
createErrorNotification(
|
||||
t('provisioning.delete-repository-button.error-repository-delete', 'Failed to delete repository'),
|
||||
e
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
deletecollectionRepository: {
|
||||
onQueryStarted: async (_, { queryFulfilled, dispatch }) => {
|
||||
try {
|
||||
await queryFulfilled;
|
||||
dispatch(
|
||||
notifyApp(
|
||||
createSuccessNotification(
|
||||
t('provisioning.home-page.success-all-repositories-deleted', 'All configured repositories deleted')
|
||||
)
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
dispatch(
|
||||
notifyApp(
|
||||
createErrorNotification(
|
||||
t('provisioning.home-page.error-delete-all-repositories', 'Failed to delete all repositories'),
|
||||
e
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
createRepositoryTest: {
|
||||
onQueryStarted: async (_, { queryFulfilled, dispatch }) => {
|
||||
try {
|
||||
await queryFulfilled;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
dispatch(notifyApp(createErrorNotification('Error testing repository', e)));
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
createRepositoryJobs: {
|
||||
onQueryStarted: async (_, { queryFulfilled, dispatch }) => {
|
||||
try {
|
||||
await queryFulfilled;
|
||||
dispatch(
|
||||
notifyApp(createSuccessNotification(t('provisioning.sync-repository.success-pull-started', 'Pull started')))
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
dispatch(
|
||||
notifyApp(
|
||||
createErrorNotification(
|
||||
t('provisioning.sync-repository.error-pulling-resources', 'Error pulling resources'),
|
||||
e
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
createRepository: {
|
||||
onQueryStarted: async (_, { queryFulfilled, dispatch }) => {
|
||||
try {
|
||||
await queryFulfilled;
|
||||
dispatch(
|
||||
notifyApp(
|
||||
createSuccessNotification(
|
||||
t('provisioning.config-form.alert-repository-settings-saved', 'Repository settings saved')
|
||||
)
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
dispatch(
|
||||
notifyApp(
|
||||
createErrorNotification(
|
||||
t('provisioning.config-form.error-save-repository', 'Failed to save repository settings'),
|
||||
e
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
replaceRepository: {
|
||||
onQueryStarted: async (_, { queryFulfilled, dispatch }) => {
|
||||
try {
|
||||
await queryFulfilled;
|
||||
dispatch(
|
||||
notifyApp(
|
||||
createSuccessNotification(
|
||||
t('provisioning.config-form.alert-repository-settings-updated', 'Repository settings updated')
|
||||
)
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
dispatch(
|
||||
notifyApp(
|
||||
createErrorNotification(
|
||||
t('provisioning.config-form.error-save-repository', 'Failed to save repository settings'),
|
||||
e
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,8 +2,6 @@ import { useEffect, useMemo, useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useNavigate } from 'react-router-dom-v5-compat';
|
||||
|
||||
import { AppEvents } from '@grafana/data';
|
||||
import { getAppEvents } from '@grafana/runtime';
|
||||
import {
|
||||
Button,
|
||||
Combobox,
|
||||
@@ -47,8 +45,6 @@ export function getWorkflowOptions(type?: 'github' | 'local'): Array<ComboboxOpt
|
||||
return opts.filter((opt) => opt.value === 'write'); // only write
|
||||
}
|
||||
|
||||
const appEvents = getAppEvents();
|
||||
|
||||
export function getDefaultValues(repository?: RepositorySpec): RepositoryFormData {
|
||||
if (!repository) {
|
||||
return {
|
||||
@@ -109,11 +105,6 @@ export function ConfigForm({ data }: ConfigFormProps) {
|
||||
useEffect(() => {
|
||||
if (request.isSuccess) {
|
||||
const formData = getValues();
|
||||
|
||||
appEvents.publish({
|
||||
type: AppEvents.alertSuccess.name,
|
||||
payload: [t('provisioning.config-form.alert-repository-settings-saved', 'Repository settings saved')],
|
||||
});
|
||||
reset(formData);
|
||||
setTimeout(() => {
|
||||
navigate('/admin/provisioning');
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
import { AppEvents } from '@grafana/data';
|
||||
import { getAppEvents } from '@grafana/runtime';
|
||||
import { Alert, ConfirmModal, Stack, Tab, TabContent, TabsBar } from '@grafana/ui';
|
||||
import { useDeletecollectionRepositoryMutation, useGetFrontendSettingsQuery } from 'app/api/clients/provisioning';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
@@ -12,8 +10,6 @@ import GettingStartedPage from './GettingStarted/GettingStartedPage';
|
||||
import { FolderRepositoryList } from './Shared/FolderRepositoryList';
|
||||
import { useRepositoryList } from './hooks';
|
||||
|
||||
const appEvents = getAppEvents();
|
||||
|
||||
enum TabSelection {
|
||||
Repositories = 'repositories',
|
||||
GettingStarted = 'getting-started',
|
||||
@@ -22,7 +18,7 @@ enum TabSelection {
|
||||
export default function HomePage() {
|
||||
const [items, isLoading] = useRepositoryList({ watch: true });
|
||||
const settings = useGetFrontendSettingsQuery();
|
||||
const [deleteAll, deleteAllResult] = useDeletecollectionRepositoryMutation();
|
||||
const [deleteAll] = useDeletecollectionRepositoryMutation();
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState<TabSelection>(TabSelection.Repositories);
|
||||
|
||||
@@ -42,15 +38,6 @@ export default function HomePage() {
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (deleteAllResult.isSuccess) {
|
||||
appEvents.publish({
|
||||
type: AppEvents.alertSuccess.name,
|
||||
payload: [t('provisioning.home-page.success-all-repositories-deleted', 'All configured repositories deleted')],
|
||||
});
|
||||
}
|
||||
}, [deleteAllResult.isSuccess]);
|
||||
|
||||
// Early return for onboarding
|
||||
if (!items?.length && !isLoading) {
|
||||
return <GettingStartedPage items={items ?? []} />;
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { AppEvents } from '@grafana/data';
|
||||
import { getAppEvents } from '@grafana/runtime';
|
||||
import { Button, Spinner } from '@grafana/ui';
|
||||
import { Repository, useCreateRepositoryTestMutation } from 'app/api/clients/provisioning';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
@@ -14,21 +10,6 @@ export function CheckRepository({ repository }: Props) {
|
||||
const [testRepo, testQuery] = useCreateRepositoryTestMutation();
|
||||
const name = repository.metadata?.name;
|
||||
|
||||
useEffect(() => {
|
||||
const appEvents = getAppEvents();
|
||||
if (testQuery.isSuccess) {
|
||||
appEvents.publish({
|
||||
type: AppEvents.alertSuccess.name,
|
||||
payload: ['Test started'],
|
||||
});
|
||||
} else if (testQuery.isError) {
|
||||
appEvents.publish({
|
||||
type: AppEvents.alertError.name,
|
||||
payload: ['Error testing repository', testQuery.error],
|
||||
});
|
||||
}
|
||||
}, [testQuery.error, testQuery.isError, testQuery.isSuccess]);
|
||||
|
||||
const onClick = () => {
|
||||
if (!name) {
|
||||
return;
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom-v5-compat';
|
||||
|
||||
import { AppEvents } from '@grafana/data';
|
||||
import { getAppEvents } from '@grafana/runtime';
|
||||
import { ConfirmModal, IconButton } from '@grafana/ui';
|
||||
import { useDeleteRepositoryMutation } from 'app/api/clients/provisioning';
|
||||
import { t } from 'app/core/internationalization';
|
||||
|
||||
const appEvents = getAppEvents();
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
redirectTo?: string;
|
||||
@@ -21,15 +17,6 @@ export function DeleteRepositoryButton({ name, redirectTo }: Props) {
|
||||
|
||||
useEffect(() => {
|
||||
if (request.isSuccess) {
|
||||
appEvents.publish({
|
||||
type: AppEvents.alertSuccess.name,
|
||||
payload: [
|
||||
t(
|
||||
'provisioning.delete-repository-button.success-repository-deleted',
|
||||
'Repository settings queued for deletion'
|
||||
),
|
||||
],
|
||||
});
|
||||
setShowModal(false);
|
||||
if (redirectTo) {
|
||||
navigate(redirectTo);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom-v5-compat';
|
||||
|
||||
import { AppEvents } from '@grafana/data';
|
||||
import { getAppEvents } from '@grafana/runtime';
|
||||
import { Button, ConfirmModal } from '@grafana/ui';
|
||||
import { Repository, useCreateRepositoryJobsMutation } from 'app/api/clients/provisioning';
|
||||
import { Trans, t } from 'app/core/internationalization';
|
||||
@@ -19,21 +17,6 @@ export function SyncRepository({ repository }: Props) {
|
||||
const navigate = useNavigate();
|
||||
const name = repository.metadata?.name;
|
||||
|
||||
useEffect(() => {
|
||||
const appEvents = getAppEvents();
|
||||
if (jobQuery.isSuccess) {
|
||||
appEvents.publish({
|
||||
type: AppEvents.alertSuccess.name,
|
||||
payload: [t('provisioning.sync-repository.success-pull-started', 'Pull started')],
|
||||
});
|
||||
} else if (jobQuery.isError) {
|
||||
appEvents.publish({
|
||||
type: AppEvents.alertError.name,
|
||||
payload: [t('provisioning.sync-repository.error-pulling-resources', 'Error pulling resources'), jobQuery.error],
|
||||
});
|
||||
}
|
||||
}, [jobQuery.error, jobQuery.isError, jobQuery.isSuccess]);
|
||||
|
||||
const onClick = () => {
|
||||
if (!name) {
|
||||
return;
|
||||
|
||||
@@ -94,19 +94,9 @@ export function WizardContent({
|
||||
const handleRepositoryDeletion = async (name: string) => {
|
||||
try {
|
||||
await deleteRepository({ name });
|
||||
appEvents.publish({
|
||||
type: AppEvents.alertSuccess.name,
|
||||
payload: [t('provisioning.wizard-content.success-repository-deleted', 'Repository deleted')],
|
||||
});
|
||||
// Wait before redirecting to ensure deletion is indexed
|
||||
setTimeout(() => navigate(PROVISIONING_URL), 1500);
|
||||
} catch (error) {
|
||||
appEvents.publish({
|
||||
type: AppEvents.alertError.name,
|
||||
payload: [
|
||||
t('provisioning.wizard-content.error-failed-to-delete', 'Failed to delete repository. Please try again.'),
|
||||
],
|
||||
});
|
||||
setIsCancelling(false);
|
||||
}
|
||||
};
|
||||
@@ -168,10 +158,6 @@ export function WizardContent({
|
||||
const newName = saveRequest.data?.metadata?.name;
|
||||
if (newName) {
|
||||
setValue('repositoryName', newName);
|
||||
appEvents.publish({
|
||||
type: AppEvents.alertSuccess.name,
|
||||
payload: [t('provisioning.wizard-content.success-repository-saved', 'Repository saved')],
|
||||
});
|
||||
handleStatusChange(true);
|
||||
}
|
||||
} else if (saveRequest.isError) {
|
||||
|
||||
@@ -5054,6 +5054,7 @@
|
||||
},
|
||||
"config-form": {
|
||||
"alert-repository-settings-saved": "Repository settings saved",
|
||||
"alert-repository-settings-updated": "Repository settings updated",
|
||||
"button-save": "Save",
|
||||
"button-saving": "Saving...",
|
||||
"description-branch": "Create a branch (and pull request) for changes",
|
||||
@@ -5064,6 +5065,7 @@
|
||||
"description-workflows-makes-repository": "No workflows makes the repository read only",
|
||||
"description-write": "Allow writing updates to the remote repository",
|
||||
"error-required": "This field is required.",
|
||||
"error-save-repository": "Failed to save repository settings",
|
||||
"error-valid-github-url": "Please enter a valid GitHub repository URL",
|
||||
"label-automatic-pulling": "Automatic pulling",
|
||||
"label-branch": "Branch",
|
||||
@@ -5137,6 +5139,7 @@
|
||||
"delete-repository-button": {
|
||||
"button-delete": "Delete",
|
||||
"confirm-delete-repository": "Are you sure you want to delete the repository config?",
|
||||
"error-repository-delete": "Failed to delete repository",
|
||||
"success-repository-deleted": "Repository settings queued for deletion",
|
||||
"title-delete-repository": "Delete repository config",
|
||||
"tooltip-delete-this-repository": "Delete this repository"
|
||||
@@ -5235,6 +5238,7 @@
|
||||
"button-delete-repositories": "Delete repositories",
|
||||
"configured-repositories-while-running-legacy-storage": "Configured repositories will not work while running legacy storage.",
|
||||
"confirm-delete-repositories": "Are you sure you want to delete all configured repositories? This action cannot be undone.",
|
||||
"error-delete-all-repositories": "Failed to delete all repositories",
|
||||
"remove-all-configured-repositories": "Remove all configured repositories",
|
||||
"subtitle": "View and manage your configured repositories",
|
||||
"success-all-repositories-deleted": "All configured repositories deleted",
|
||||
@@ -5403,10 +5407,7 @@
|
||||
"button-cancel": "Cancel",
|
||||
"button-cancelling": "Cancelling...",
|
||||
"button-submitting": "Submitting...",
|
||||
"error-failed-to-delete": "Failed to delete repository. Please try again.",
|
||||
"error-instance-repository-exists": "Instance repository already exists",
|
||||
"success-repository-deleted": "Repository deleted",
|
||||
"success-repository-saved": "Repository saved",
|
||||
"title-repository-verification-failed": "Repository verification failed"
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user