diff --git a/public/app/core/reducers/root.ts b/public/app/core/reducers/root.ts
index b4203991570..07e871a703e 100644
--- a/public/app/core/reducers/root.ts
+++ b/public/app/core/reducers/root.ts
@@ -17,6 +17,7 @@ import panelsReducers from 'app/features/panel/state/reducers';
import { reducer as pluginsReducer } from 'app/features/plugins/admin/state/reducer';
import userReducers from 'app/features/profile/state/reducers';
import serviceAccountsReducer from 'app/features/serviceaccounts/state/reducers';
+import supportBundlesReducer from 'app/features/support-bundles/state/reducers';
import teamsReducers from 'app/features/teams/state/reducers';
import usersReducers from 'app/features/users/state/reducers';
import templatingReducers from 'app/features/variables/state/keyedVariablesReducer';
@@ -43,6 +44,7 @@ const rootReducers = {
...panelEditorReducers,
...panelsReducers,
...templatingReducers,
+ ...supportBundlesReducer,
plugins: pluginsReducer,
[alertingApi.reducerPath]: alertingApi.reducer,
[publicDashboardApi.reducerPath]: publicDashboardApi.reducer,
diff --git a/public/app/features/support-bundles/SupportBundles.tsx b/public/app/features/support-bundles/SupportBundles.tsx
index 0e75a1c27a6..a2a6fa98f72 100644
--- a/public/app/features/support-bundles/SupportBundles.tsx
+++ b/public/app/features/support-bundles/SupportBundles.tsx
@@ -1,10 +1,13 @@
import React, { useEffect } from 'react';
-import { useAsyncFn } from 'react-use';
+import { connect, ConnectedProps } from 'react-redux';
import { dateTimeFormat } from '@grafana/data';
-import { config, getBackendSrv } from '@grafana/runtime';
-import { LinkButton } from '@grafana/ui';
+import { config } from '@grafana/runtime';
+import { LinkButton, Spinner, IconButton } from '@grafana/ui';
import { Page } from 'app/core/components/Page/Page';
+import { StoreState } from 'app/types';
+
+import { loadBundles, removeBundle, checkBundles } from './state/actions';
const subTitle = (
@@ -13,39 +16,48 @@ const subTitle = (
);
-const newButton = (
+const NewBundleButton = (
New support bundle
);
-type SupportBundleState = 'complete' | 'error' | 'timeout' | 'pending';
-
-interface SupportBundle {
- uid: string;
- state: SupportBundleState;
- creator: string;
- createdAt: number;
- expiresAt: number;
-}
-
-const getBundles = () => {
- return getBackendSrv().get('/api/support-bundles');
+const mapStateToProps = (state: StoreState) => {
+ return {
+ supportBundles: state.supportBundles.supportBundles,
+ isLoading: state.supportBundles.isLoading,
+ };
};
-function SupportBundles() {
- const [bundlesState, fetchBundles] = useAsyncFn(getBundles, []);
+const mapDispatchToProps = {
+ loadBundles,
+ removeBundle,
+ checkBundles,
+};
+
+const connector = connect(mapStateToProps, mapDispatchToProps);
+
+type Props = ConnectedProps;
+
+const SupportBundlesUnconnected = ({ supportBundles, isLoading, loadBundles, removeBundle, checkBundles }: Props) => {
+ const isPending = supportBundles.some((b) => b.state === 'pending');
useEffect(() => {
- fetchBundles();
- }, [fetchBundles]);
+ loadBundles();
+ }, [loadBundles]);
- const actions = config.featureToggles.topnav ? newButton : undefined;
+ useEffect(() => {
+ if (isPending) {
+ checkBundles();
+ }
+ });
+
+ const actions = config.featureToggles.topnav ? NewBundleButton : undefined;
return (
-
- {!config.featureToggles.topnav && newButton}
+
+ {!config.featureToggles.topnav && NewBundleButton}
@@ -53,25 +65,31 @@ function SupportBundles() {
| Created on |
Requested by |
Expires |
+ |
+ |
|
- {bundlesState?.value?.map((b) => (
-
- | {dateTimeFormat(b.createdAt * 1000)} |
- {b.creator} |
- {dateTimeFormat(b.expiresAt * 1000)} |
+ {supportBundles?.map((bundle) => (
+
+ | {dateTimeFormat(bundle.createdAt * 1000)} |
+ {bundle.creator} |
+ {dateTimeFormat(bundle.expiresAt * 1000)} |
+ {bundle.state === 'pending' && } |
Download
|
+
+ removeBundle(bundle.uid)} name="trash-alt" variant="destructive" />
+ |
))}
@@ -79,6 +97,6 @@ function SupportBundles() {
);
-}
+};
-export default SupportBundles;
+export default connector(SupportBundlesUnconnected);
diff --git a/public/app/features/support-bundles/SupportBundlesCreate.tsx b/public/app/features/support-bundles/SupportBundlesCreate.tsx
index 3ecbc3212f6..d8070c1c096 100644
--- a/public/app/features/support-bundles/SupportBundlesCreate.tsx
+++ b/public/app/features/support-bundles/SupportBundlesCreate.tsx
@@ -1,29 +1,11 @@
-import React, { useCallback, useEffect, useState } from 'react';
-import { useAsyncFn } from 'react-use';
+import React, { useEffect } from 'react';
+import { connect, ConnectedProps } from 'react-redux';
-import { getBackendSrv, locationService } from '@grafana/runtime';
-import { Form, Button, Field, Checkbox } from '@grafana/ui';
+import { Form, Button, Field, Checkbox, LinkButton, HorizontalGroup, Alert } from '@grafana/ui';
import { Page } from 'app/core/components/Page/Page';
+import { StoreState } from 'app/types';
-// move to types
-export interface SupportBundleCreateRequest {
- collectors: string[];
-}
-
-export interface SupportBundleCollector {
- uid: string;
- displayName: string;
- description: string;
- includedByDefault: boolean;
- default: boolean;
-}
-
-export interface Props {}
-
-const createSupportBundle = async (data: SupportBundleCreateRequest) => {
- const result = await getBackendSrv().post('/api/support-bundles', data);
- return result;
-};
+import { loadSupportBundleCollectors, createSupportBundle } from './state/actions';
const subTitle = (
@@ -31,50 +13,60 @@ const subTitle = (
);
-export const SupportBundlesCreate = ({}: Props): JSX.Element => {
- const onSubmit = useCallback(async (data) => {
- try {
- const selectedLabelsArray = Object.keys(data).filter((key) => data[key]);
- const response = await createSupportBundle({ collectors: selectedLabelsArray });
- console.info(response);
- } catch (e) {
- console.error(e);
- }
+const mapStateToProps = (state: StoreState) => {
+ return {
+ collectors: state.supportBundles.supportBundleCollectors,
+ isLoading: state.supportBundles.createBundlePageLoading,
+ loadCollectorsError: state.supportBundles.loadBundlesError,
+ createBundleError: state.supportBundles.createBundleError,
+ };
+};
- locationService.push('/admin/support-bundles');
- }, []);
+const mapDispatchToProps = {
+ loadSupportBundleCollectors,
+ createSupportBundle,
+};
- const [components, setComponents] = useState([]);
- // populate components from the backend
- const populateComponents = async () => {
- return await getBackendSrv().get('/api/support-bundles/collectors');
+const connector = connect(mapStateToProps, mapDispatchToProps);
+
+type Props = ConnectedProps;
+
+export const SupportBundlesCreateUnconnected = ({
+ collectors,
+ isLoading,
+ loadCollectorsError,
+ createBundleError,
+ loadSupportBundleCollectors,
+ createSupportBundle,
+}: Props): JSX.Element => {
+ const onSubmit = (data: Record) => {
+ const selectedLabelsArray = Object.keys(data).filter((key) => data[key]);
+ createSupportBundle({ collectors: selectedLabelsArray });
};
- const [state, fetchComponents] = useAsyncFn(populateComponents);
useEffect(() => {
- fetchComponents().then((res) => {
- setComponents(res);
- });
- }, [fetchComponents]);
+ loadSupportBundleCollectors();
+ }, [loadSupportBundleCollectors]);
// turn components into a uuid -> enabled map
- const values: Record = components.reduce((acc, curr) => {
+ const values: Record = collectors.reduce((acc, curr) => {
return { ...acc, [curr.uid]: curr.default };
}, {});
return (
-
+
Create support bundle
- {state.error && {state.error}
}
- {!!components.length && (
+ {loadCollectorsError && }
+ {createBundleError && }
+ {!!collectors.length && (