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/dashboard-scene/sharing/ShareButton/share-snapshot/ShareSnapshot.tsx

194 lines
6.5 KiB

import { useState } from 'react';
import useAsyncFn from 'react-use/lib/useAsyncFn';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
import { SceneComponentProps } from '@grafana/scenes';
import { Alert, Button, ClipboardButton, Spinner, Stack, TextLink } from '@grafana/ui';
import { t, Trans } from 'app/core/internationalization';
import { contextSrv } from 'app/core/services/context_srv';
import { AccessControlAction } from 'app/types';
import { SnapshotSharingOptions } from '../../../../dashboard/services/SnapshotSrv';
import { ShareDrawerConfirmAction } from '../../ShareDrawer/ShareDrawerConfirmAction';
import { ShareSnapshotTab } from '../../ShareSnapshotTab';
import { ShareView } from '../../types';
import { UpsertSnapshot } from './UpsertSnapshot';
const selectors = e2eSelectors.pages.ShareDashboardDrawer.ShareSnapshot;
export class ShareSnapshot extends ShareSnapshotTab implements ShareView {
static Component = ShareSnapshotRenderer;
public getTabLabel() {
return t('share-dashboard.menu.share-snapshot-title', 'Share snapshot');
}
}
function ShareSnapshotRenderer({ model }: SceneComponentProps<ShareSnapshot>) {
const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false);
const [showDeletedAlert, setShowDeletedAlert] = useState(false);
const [step, setStep] = useState(1);
const { snapshotName, snapshotSharingOptions, selectedExpireOption, panelRef, onDismiss, dashboardRef } =
model.useState();
const [snapshotResult, createSnapshot] = useAsyncFn(async (external = false) => {
const response = await model.onSnapshotCreate(external);
setStep(2);
return response;
});
const [deleteSnapshotResult, deleteSnapshot] = useAsyncFn(async (url: string) => {
const response = await model.onSnapshotDelete(url);
setStep(1);
setShowDeleteConfirmation(false);
setShowDeletedAlert(true);
return response;
});
const onCancelClick = () => {
onDismiss?.();
};
const reset = () => {
model.onSnasphotNameChange(dashboardRef.resolve().state.title);
setStep(1);
};
const onDeleteSnapshotClick = async () => {
await deleteSnapshot(snapshotResult.value?.deleteUrl!);
reset();
};
if (showDeleteConfirmation) {
return (
<ShareDrawerConfirmAction
title={t('snapshot.share.delete-title', 'Delete snapshot')}
confirmButtonLabel={t('snapshot.share.delete-button', 'Delete snapshot')}
onConfirm={onDeleteSnapshotClick}
onDismiss={() => setShowDeleteConfirmation(false)}
description={t('snapshot.share.delete-description', 'Are you sure you want to delete this snapshot?')}
isActionLoading={deleteSnapshotResult.loading}
/>
);
}
return (
<div data-testid={selectors.container}>
<>
{step === 1 && showDeletedAlert && (
<Alert severity="info" title={''} onRemove={() => setShowDeletedAlert(false)}>
<Trans i18nKey="snapshot.share.deleted-alert">
Snapshot deleted. It could take an hour to be cleared from CDN caches.
</Trans>
</Alert>
)}
<UpsertSnapshot
name={snapshotName ?? ''}
selectedExpireOption={selectedExpireOption}
onNameChange={model.onSnasphotNameChange}
onExpireChange={model.onExpireChange}
disableInputs={step === 2}
panelRef={panelRef}
>
<Stack justifyContent="space-between" gap={{ xs: 2 }} direction={{ xs: 'column', xl: 'row' }}>
{step === 1 ? (
<CreateSnapshotActions
onCreateClick={createSnapshot}
isLoading={snapshotResult.loading}
onCancelClick={onCancelClick}
sharingOptions={snapshotSharingOptions}
/>
) : (
step === 2 &&
snapshotResult.value && (
<UpsertSnapshotActions
url={snapshotResult.value!.url}
onDeleteClick={() => setShowDeleteConfirmation(true)}
onNewSnapshotClick={reset}
/>
)
)}
<TextLink icon="external-link-alt" href="/dashboard/snapshots" external>
{t('snapshot.share.view-all-button', 'View all snapshots')}
</TextLink>
</Stack>
</UpsertSnapshot>
</>
</div>
);
}
const CreateSnapshotActions = ({
isLoading,
onCreateClick,
onCancelClick,
sharingOptions,
}: {
isLoading: boolean;
sharingOptions?: SnapshotSharingOptions;
onCancelClick: () => void;
onCreateClick: (isExternal?: boolean) => void;
}) => (
<Stack gap={1} flex={1} direction={{ xs: 'column', sm: 'row' }}>
<Button
variant="primary"
disabled={isLoading}
onClick={() => onCreateClick()}
data-testid={selectors.publishSnapshot}
>
<Trans i18nKey="snapshot.share.local-button">Publish snapshot</Trans>
</Button>
{sharingOptions?.externalEnabled && (
<Button variant="secondary" disabled={isLoading} onClick={() => onCreateClick(true)}>
{sharingOptions?.externalSnapshotName}
</Button>
)}
<Button variant="secondary" fill="outline" onClick={onCancelClick}>
<Trans i18nKey="snapshot.share.cancel-button">Cancel</Trans>
</Button>
{isLoading && <Spinner />}
</Stack>
);
const UpsertSnapshotActions = ({
url,
onDeleteClick,
onNewSnapshotClick,
}: {
url: string;
onDeleteClick: () => void;
onNewSnapshotClick: () => void;
}) => {
const hasDeletePermission = contextSrv.hasPermission(AccessControlAction.SnapshotsDelete);
const deleteTooltip = hasDeletePermission
? ''
: t('snapshot.share.delete-permission-tooltip', "You don't have permission to delete snapshots");
return (
<Stack justifyContent="flex-start" gap={1} direction={{ xs: 'column', sm: 'row' }}>
<ClipboardButton
icon="link"
variant="primary"
fill="outline"
getText={() => url}
data-testid={selectors.copyUrlButton}
>
<Trans i18nKey="snapshot.share.copy-link-button">Copy link</Trans>
</ClipboardButton>
<Button
icon="trash-alt"
variant="destructive"
fill="outline"
onClick={onDeleteClick}
disabled={!hasDeletePermission}
tooltip={deleteTooltip}
>
<Trans i18nKey="snapshot.share.delete-button">Delete snapshot</Trans>
</Button>
<Button variant="secondary" fill="solid" onClick={onNewSnapshotClick}>
<Trans i18nKey="snapshot.share.new-snapshot-button">New snapshot</Trans>
</Button>
</Stack>
);
};