The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/public/app/features/library-panels/components/SaveLibraryPanelModal/SaveLibraryPanelModal.tsx

123 lines
3.7 KiB

import React, { useCallback, useState } from 'react';
import { Button, Icon, Input, Modal, useStyles } from '@grafana/ui';
import { useAsync, useDebounce } from 'react-use';
import { getBackendSrv } from 'app/core/services/backend_srv';
import { usePanelSave } from '../../utils/usePanelSave';
import { getLibraryPanelConnectedDashboards } from '../../state/api';
import { PanelModelWithLibraryPanel } from '../../types';
import { getModalStyles } from '../../styles';
interface Props {
panel: PanelModelWithLibraryPanel;
folderId: number;
isOpen: boolean;
onConfirm: () => void;
onDismiss: () => void;
onDiscard: () => void;
}
export const SaveLibraryPanelModal: React.FC<Props> = ({
panel,
folderId,
isOpen,
onDismiss,
onConfirm,
onDiscard,
}) => {
const [searchString, setSearchString] = useState('');
const connectedDashboardsState = useAsync(async () => {
const connectedDashboards = await getLibraryPanelConnectedDashboards(panel.libraryPanel.uid);
return connectedDashboards;
}, []);
const dashState = useAsync(async () => {
const connectedDashboards = connectedDashboardsState.value;
if (connectedDashboards && connectedDashboards.length > 0) {
const dashboardDTOs = await getBackendSrv().search({ dashboardIds: connectedDashboards });
return dashboardDTOs.map((dash) => dash.title);
}
return [];
}, [connectedDashboardsState.value]);
const [filteredDashboards, setFilteredDashboards] = useState<string[]>([]);
useDebounce(
() => {
if (!dashState.value) {
return setFilteredDashboards([]);
}
return setFilteredDashboards(
dashState.value.filter((dashName) => dashName.toLowerCase().includes(searchString.toLowerCase()))
);
},
300,
[dashState.value, searchString]
);
const { saveLibraryPanel } = usePanelSave();
const styles = useStyles(getModalStyles);
const discardAndClose = useCallback(() => {
onDiscard();
onDismiss();
}, [onDiscard, onDismiss]);
return (
<Modal title="Update all panel instances" icon="save" onDismiss={onDismiss} isOpen={isOpen}>
<div>
<p className={styles.textInfo}>
{'This update will affect '}
<strong>
{panel.libraryPanel.meta.connectedDashboards}{' '}
{panel.libraryPanel.meta.connectedDashboards === 1 ? 'dashboard' : 'dashboards'}.
</strong>
The following dashboards using the panel will be affected:
</p>
<Input
className={styles.dashboardSearch}
prefix={<Icon name="search" />}
placeholder="Search affected dashboards"
value={searchString}
onChange={(e) => setSearchString(e.currentTarget.value)}
/>
{dashState.loading ? (
<p>Loading connected dashboards...</p>
) : (
<table className={styles.myTable}>
<thead>
<tr>
<th>Dashboard name</th>
</tr>
</thead>
<tbody>
{filteredDashboards.map((dashName, i) => (
<tr key={`dashrow-${i}`}>
<td>{dashName}</td>
</tr>
))}
</tbody>
</table>
)}
<Modal.ButtonRow>
<Button variant="secondary" onClick={onDismiss} fill="outline">
Cancel
</Button>
<Button variant="destructive" onClick={discardAndClose}>
Discard
</Button>
<Button
onClick={() => {
saveLibraryPanel(panel, folderId).then(() => {
onConfirm();
onDismiss();
});
}}
>
Update all
</Button>
</Modal.ButtonRow>
</div>
</Modal>
);
};