mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
E2C: Improvements to workflow (#91045)
* E2C: Improvements to workflow * no eslint comment * i18n * i18n: * lint
This commit is contained in:
parent
8e006fedda
commit
d7e85354d1
@ -4744,7 +4744,9 @@ exports[`better eslint`] = {
|
|||||||
],
|
],
|
||||||
"public/app/features/migrate-to-cloud/onprem/NameCell.tsx:5381": [
|
"public/app/features/migrate-to-cloud/onprem/NameCell.tsx:5381": [
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"]
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"],
|
||||||
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "2"],
|
||||||
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "3"]
|
||||||
],
|
],
|
||||||
"public/app/features/notifications/StoredNotifications.tsx:5381": [
|
"public/app/features/notifications/StoredNotifications.tsx:5381": [
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"]
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"]
|
||||||
|
@ -26,7 +26,7 @@ export const CallToAction = () => {
|
|||||||
<ConnectModal
|
<ConnectModal
|
||||||
isOpen={modalOpen}
|
isOpen={modalOpen}
|
||||||
isLoading={createMigrationResponse.isLoading}
|
isLoading={createMigrationResponse.isLoading}
|
||||||
isError={createMigrationResponse.isError}
|
error={createMigrationResponse.error}
|
||||||
onConfirm={createMigration}
|
onConfirm={createMigration}
|
||||||
hideModal={() => setModalOpen(false)}
|
hideModal={() => setModalOpen(false)}
|
||||||
/>
|
/>
|
||||||
|
@ -3,15 +3,16 @@ import { useId } from 'react';
|
|||||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { Modal, Button, Stack, TextLink, Field, Input, Text, useStyles2, Alert } from '@grafana/ui';
|
import { Modal, Button, Stack, TextLink, Field, Input, Text, useStyles2 } from '@grafana/ui';
|
||||||
import { Trans, t } from 'app/core/internationalization';
|
import { Trans, t } from 'app/core/internationalization';
|
||||||
|
import { AlertWithTraceID } from 'app/features/migrate-to-cloud/shared/AlertWithTraceID';
|
||||||
|
|
||||||
import { CreateSessionApiArg } from '../../../api';
|
import { CreateSessionApiArg } from '../../../api';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
isError: boolean;
|
error: unknown;
|
||||||
hideModal: () => void;
|
hideModal: () => void;
|
||||||
onConfirm: (connectStackData: CreateSessionApiArg) => Promise<unknown>;
|
onConfirm: (connectStackData: CreateSessionApiArg) => Promise<unknown>;
|
||||||
}
|
}
|
||||||
@ -20,7 +21,7 @@ interface FormData {
|
|||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ConnectModal = ({ isOpen, isLoading, isError, hideModal, onConfirm }: Props) => {
|
export const ConnectModal = ({ isOpen, isLoading, error, hideModal, onConfirm }: Props) => {
|
||||||
const tokenId = useId();
|
const tokenId = useId();
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
@ -94,16 +95,17 @@ export const ConnectModal = ({ isOpen, isLoading, isError, hideModal, onConfirm
|
|||||||
</Trans>
|
</Trans>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isError && (
|
{error ? (
|
||||||
<Alert
|
<AlertWithTraceID
|
||||||
|
error={error}
|
||||||
severity="error"
|
severity="error"
|
||||||
title={t('migrate-to-cloud.connect-modal.token-error-title', 'Error saving token')}
|
title={t('migrate-to-cloud.connect-modal.token-error-title', 'Error saving token')}
|
||||||
>
|
>
|
||||||
<Trans i18nKey="migrate-to-cloud.connect-modal.token-error-description">
|
<Trans i18nKey="migrate-to-cloud.connect-modal.token-error-description">
|
||||||
There was an error saving the token. See the Grafana server logs for more details.
|
There was an error saving the token. See the Grafana server logs for more details.
|
||||||
</Trans>
|
</Trans>
|
||||||
</Alert>
|
</AlertWithTraceID>
|
||||||
)}
|
) : undefined}
|
||||||
|
|
||||||
<Field
|
<Field
|
||||||
className={styles.field}
|
className={styles.field}
|
||||||
|
@ -7,10 +7,13 @@ import { config } from '@grafana/runtime';
|
|||||||
import { CellProps, Stack, Text, Icon, useStyles2 } from '@grafana/ui';
|
import { CellProps, Stack, Text, Icon, useStyles2 } from '@grafana/ui';
|
||||||
import { getSvgSize } from '@grafana/ui/src/components/Icon/utils';
|
import { getSvgSize } from '@grafana/ui/src/components/Icon/utils';
|
||||||
import { Trans } from 'app/core/internationalization';
|
import { Trans } from 'app/core/internationalization';
|
||||||
|
import { useGetFolderQuery } from 'app/features/browse-dashboards/api/browseDashboardsAPI';
|
||||||
|
|
||||||
import { useGetDashboardByUidQuery, MigrateDataResponseItemDto } from '../api';
|
import { useGetDashboardByUidQuery } from '../api';
|
||||||
|
|
||||||
export function NameCell(props: CellProps<MigrateDataResponseItemDto>) {
|
import { ResourceTableItem } from './types';
|
||||||
|
|
||||||
|
export function NameCell(props: CellProps<ResourceTableItem>) {
|
||||||
const data = props.row.original;
|
const data = props.row.original;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -18,12 +21,23 @@ export function NameCell(props: CellProps<MigrateDataResponseItemDto>) {
|
|||||||
<ResourceIcon resource={data} />
|
<ResourceIcon resource={data} />
|
||||||
|
|
||||||
<Stack direction="column" gap={0}>
|
<Stack direction="column" gap={0}>
|
||||||
{data.type === 'DATASOURCE' ? <DatasourceInfo data={data} /> : <DashboardInfo data={data} />}
|
<ResourceInfo data={data} />
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ResourceInfo({ data }: { data: ResourceTableItem }) {
|
||||||
|
switch (data.type) {
|
||||||
|
case 'DASHBOARD':
|
||||||
|
return <DashboardInfo data={data} />;
|
||||||
|
case 'DATASOURCE':
|
||||||
|
return <DatasourceInfo data={data} />;
|
||||||
|
case 'FOLDER':
|
||||||
|
return <FolderInfo data={data} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getDashboardTitle(dashboardData: object) {
|
function getDashboardTitle(dashboardData: object) {
|
||||||
if ('title' in dashboardData && typeof dashboardData.title === 'string') {
|
if ('title' in dashboardData && typeof dashboardData.title === 'string') {
|
||||||
return dashboardData.title;
|
return dashboardData.title;
|
||||||
@ -32,7 +46,7 @@ function getDashboardTitle(dashboardData: object) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DatasourceInfo({ data }: { data: MigrateDataResponseItemDto }) {
|
function DatasourceInfo({ data }: { data: ResourceTableItem }) {
|
||||||
const datasourceUID = data.refId;
|
const datasourceUID = data.refId;
|
||||||
const datasource = useDatasource(datasourceUID);
|
const datasource = useDatasource(datasourceUID);
|
||||||
|
|
||||||
@ -59,7 +73,7 @@ function DatasourceInfo({ data }: { data: MigrateDataResponseItemDto }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DashboardInfo({ data }: { data: MigrateDataResponseItemDto }) {
|
function DashboardInfo({ data }: { data: ResourceTableItem }) {
|
||||||
const dashboardUID = data.refId;
|
const dashboardUID = data.refId;
|
||||||
// TODO: really, the API should return this directly
|
// TODO: really, the API should return this directly
|
||||||
const { data: dashboardData, isError } = useGetDashboardByUidQuery({
|
const { data: dashboardData, isError } = useGetDashboardByUidQuery({
|
||||||
@ -92,6 +106,32 @@ function DashboardInfo({ data }: { data: MigrateDataResponseItemDto }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function FolderInfo({ data }: { data: ResourceTableItem }) {
|
||||||
|
const { data: folderData, isLoading, isError } = useGetFolderQuery(data.refId);
|
||||||
|
|
||||||
|
if (isLoading || !folderData) {
|
||||||
|
return <InfoSkeleton />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Text italic>Unable to load dashboard</Text>
|
||||||
|
<Text color="secondary">Dashboard {data.refId}</Text>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentFolderName = folderData.parents?.[folderData.parents.length - 1]?.title;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span>{folderData.title}</span>
|
||||||
|
<Text color="secondary">{parentFolderName ?? 'Dashboards'}</Text>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function InfoSkeleton() {
|
function InfoSkeleton() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -101,15 +141,15 @@ function InfoSkeleton() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ResourceIcon({ resource }: { resource: MigrateDataResponseItemDto }) {
|
function ResourceIcon({ resource }: { resource: ResourceTableItem }) {
|
||||||
const styles = useStyles2(getIconStyles);
|
const styles = useStyles2(getIconStyles);
|
||||||
const datasource = useDatasource(resource.type === 'DATASOURCE' ? resource.refId : undefined);
|
const datasource = useDatasource(resource.type === 'DATASOURCE' ? resource.refId : undefined);
|
||||||
|
|
||||||
if (resource.type === 'DASHBOARD') {
|
if (resource.type === 'DASHBOARD') {
|
||||||
return <Icon size="xl" name="dashboard" />;
|
return <Icon size="xl" name="dashboard" />;
|
||||||
}
|
} else if (resource.type === 'FOLDER') {
|
||||||
|
return <Icon size="xl" name="folder" />;
|
||||||
if (resource.type === 'DATASOURCE' && datasource?.meta?.info?.logos?.small) {
|
} else if (resource.type === 'DATASOURCE' && datasource?.meta?.info?.logos?.small) {
|
||||||
return <img className={styles.icon} src={datasource.meta.info.logos.small} alt="" />;
|
return <img className={styles.icon} src={datasource.meta.info.logos.small} alt="" />;
|
||||||
} else if (resource.type === 'DATASOURCE') {
|
} else if (resource.type === 'DATASOURCE') {
|
||||||
return <Icon size="xl" name="database" />;
|
return <Icon size="xl" name="database" />;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { skipToken } from '@reduxjs/toolkit/query/react';
|
import { skipToken } from '@reduxjs/toolkit/query/react';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { isFetchError } from '@grafana/runtime';
|
|
||||||
import { Alert, Box, Stack, Text } from '@grafana/ui';
|
import { Alert, Box, Stack, Text } from '@grafana/ui';
|
||||||
import { Trans, t } from 'app/core/internationalization';
|
import { Trans, t } from 'app/core/internationalization';
|
||||||
|
|
||||||
@ -15,6 +14,7 @@ import {
|
|||||||
useGetSnapshotQuery,
|
useGetSnapshotQuery,
|
||||||
useUploadSnapshotMutation,
|
useUploadSnapshotMutation,
|
||||||
} from '../api';
|
} from '../api';
|
||||||
|
import { AlertWithTraceID } from '../shared/AlertWithTraceID';
|
||||||
|
|
||||||
import { DisconnectModal } from './DisconnectModal';
|
import { DisconnectModal } from './DisconnectModal';
|
||||||
import { EmptyState } from './EmptyState/EmptyState';
|
import { EmptyState } from './EmptyState/EmptyState';
|
||||||
@ -57,7 +57,7 @@ const SHOULD_POLL_STATUSES: Array<SnapshotDto['status']> = [
|
|||||||
'PROCESSING',
|
'PROCESSING',
|
||||||
];
|
];
|
||||||
|
|
||||||
const SNAPSHOT_REBUILD_STATUSES: Array<SnapshotDto['status']> = ['FINISHED', 'ERROR', 'UNKNOWN'];
|
const SNAPSHOT_REBUILD_STATUSES: Array<SnapshotDto['status']> = ['PENDING_PROCESSING', 'FINISHED', 'ERROR', 'UNKNOWN'];
|
||||||
|
|
||||||
const SNAPSHOT_BUILDING_STATUSES: Array<SnapshotDto['status']> = ['INITIALIZING', 'CREATING'];
|
const SNAPSHOT_BUILDING_STATUSES: Array<SnapshotDto['status']> = ['INITIALIZING', 'CREATING'];
|
||||||
|
|
||||||
@ -166,7 +166,8 @@ export const Page = () => {
|
|||||||
{/* TODO: show errors from all mutation's in a... modal? */}
|
{/* TODO: show errors from all mutation's in a... modal? */}
|
||||||
|
|
||||||
{createSnapshotResult.isError && (
|
{createSnapshotResult.isError && (
|
||||||
<Alert
|
<AlertWithTraceID
|
||||||
|
error={createSnapshotResult.error}
|
||||||
severity="error"
|
severity="error"
|
||||||
title={t('migrate-to-cloud.summary.run-migration-error-title', 'Error creating snapshot')}
|
title={t('migrate-to-cloud.summary.run-migration-error-title', 'Error creating snapshot')}
|
||||||
>
|
>
|
||||||
@ -175,13 +176,7 @@ export const Page = () => {
|
|||||||
See the Grafana server logs for more details
|
See the Grafana server logs for more details
|
||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
</AlertWithTraceID>
|
||||||
{maybeGetTraceID(createSnapshotResult.error) && (
|
|
||||||
// Deliberately don't want to translate 'Trace ID'
|
|
||||||
// eslint-disable-next-line @grafana/no-untranslated-strings
|
|
||||||
<Text element="p">Trace ID: {maybeGetTraceID(createSnapshotResult.error)}</Text>
|
|
||||||
)}
|
|
||||||
</Alert>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{disconnectResult.isError && (
|
{disconnectResult.isError && (
|
||||||
@ -247,13 +242,3 @@ export const Page = () => {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function maybeGetTraceID(err: unknown) {
|
|
||||||
const data = isFetchError<unknown>(err) ? err.data : undefined;
|
|
||||||
|
|
||||||
if (typeof data === 'object' && data && 'traceID' in data && typeof data.traceID === 'string') {
|
|
||||||
return data.traceID;
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
import { Button, Modal, Stack, Text } from '@grafana/ui';
|
||||||
|
import { Trans, t } from 'app/core/internationalization';
|
||||||
|
|
||||||
|
import { prettyTypeName } from './TypeCell';
|
||||||
|
import { ResourceTableItem } from './types';
|
||||||
|
|
||||||
|
interface ResourceErrorModalProps {
|
||||||
|
resource: ResourceTableItem | undefined;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ResourceErrorModal(props: ResourceErrorModalProps) {
|
||||||
|
const { resource, onClose } = props;
|
||||||
|
|
||||||
|
const refId = resource?.refId;
|
||||||
|
const typeName = resource && prettyTypeName(resource.type);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={t('migrate-to-cloud.resource-error.title', 'Unable to migrate this resource')}
|
||||||
|
isOpen={Boolean(resource)}
|
||||||
|
onDismiss={onClose}
|
||||||
|
>
|
||||||
|
{resource && (
|
||||||
|
<Stack direction="column" gap={2} alignItems="flex-start">
|
||||||
|
<Text element="p" weight="bold">
|
||||||
|
<Trans i18nKey="migrate-to-cloud.resource-error.resource-summary">
|
||||||
|
{{ refId }} ({{ typeName }})
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{resource.error ? (
|
||||||
|
<>
|
||||||
|
<Text element="p">
|
||||||
|
<Trans i18nKey="migrate-to-cloud.resource-error.specific-error">The specific error was:</Trans>
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text element="p" weight="bold">
|
||||||
|
{resource.error}
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Text element="p">
|
||||||
|
<Trans i18nKey="migrate-to-cloud.resource-error.unknown-error">An unknown error occurred.</Trans>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button onClick={onClose}>
|
||||||
|
<Trans i18nKey="migrate-to-cloud.resource-error.dismiss-button">OK</Trans>
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
@ -1,10 +1,14 @@
|
|||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { InteractiveTable } from '@grafana/ui';
|
import { InteractiveTable } from '@grafana/ui';
|
||||||
|
|
||||||
import { MigrateDataResponseItemDto } from '../api';
|
import { MigrateDataResponseItemDto } from '../api';
|
||||||
|
|
||||||
import { NameCell } from './NameCell';
|
import { NameCell } from './NameCell';
|
||||||
|
import { ResourceErrorModal } from './ResourceErrorModal';
|
||||||
import { StatusCell } from './StatusCell';
|
import { StatusCell } from './StatusCell';
|
||||||
import { TypeCell } from './TypeCell';
|
import { TypeCell } from './TypeCell';
|
||||||
|
import { ResourceTableItem } from './types';
|
||||||
|
|
||||||
interface ResourcesTableProps {
|
interface ResourcesTableProps {
|
||||||
resources: MigrateDataResponseItemDto[];
|
resources: MigrateDataResponseItemDto[];
|
||||||
@ -17,5 +21,21 @@ const columns = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export function ResourcesTable({ resources }: ResourcesTableProps) {
|
export function ResourcesTable({ resources }: ResourcesTableProps) {
|
||||||
return <InteractiveTable columns={columns} data={resources} getRowId={(r) => r.refId} pageSize={15} />;
|
const [erroredResource, setErroredResource] = useState<ResourceTableItem | undefined>();
|
||||||
|
|
||||||
|
const handleShowErrorModal = useCallback((resource: ResourceTableItem) => {
|
||||||
|
setErroredResource(resource);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const data = useMemo(() => {
|
||||||
|
return resources.map((r) => ({ ...r, showError: handleShowErrorModal }));
|
||||||
|
}, [resources, handleShowErrorModal]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<InteractiveTable columns={columns} data={data} getRowId={(r) => r.refId} pageSize={15} />
|
||||||
|
|
||||||
|
<ResourceErrorModal resource={erroredResource} onClose={() => setErroredResource(undefined)} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,35 @@
|
|||||||
import { CellProps, Text, Stack, Button } from '@grafana/ui';
|
import { CellProps, Text, Stack, Button } from '@grafana/ui';
|
||||||
import { t } from 'app/core/internationalization';
|
import { t } from 'app/core/internationalization';
|
||||||
|
|
||||||
import { MigrateDataResponseItemDto } from '../api';
|
import { ResourceTableItem } from './types';
|
||||||
|
|
||||||
export function StatusCell(props: CellProps<MigrateDataResponseItemDto>) {
|
export function StatusCell(props: CellProps<ResourceTableItem>) {
|
||||||
const { status, error } = props.row.original;
|
const item = props.row.original;
|
||||||
|
|
||||||
// Keep these here to preserve the translations
|
// Keep these here to preserve the translations
|
||||||
// t('migrate-to-cloud.resource-status.migrating', 'Uploading...')
|
// t('migrate-to-cloud.resource-status.migrating', 'Uploading...')
|
||||||
|
|
||||||
if (status === 'PENDING') {
|
if (item.status === 'PENDING') {
|
||||||
return <Text color="secondary">{t('migrate-to-cloud.resource-status.not-migrated', 'Not yet uploaded')}</Text>;
|
return <Text color="secondary">{t('migrate-to-cloud.resource-status.not-migrated', 'Not yet uploaded')}</Text>;
|
||||||
} else if (status === 'OK') {
|
} else if (item.status === 'OK') {
|
||||||
return <Text color="success">{t('migrate-to-cloud.resource-status.migrated', 'Uploaded to cloud')}</Text>;
|
return <Text color="success">{t('migrate-to-cloud.resource-status.migrated', 'Uploaded to cloud')}</Text>;
|
||||||
} else if (status === 'ERROR') {
|
} else if (item.status === 'ERROR') {
|
||||||
return (
|
return <ErrorCell item={item} />;
|
||||||
<Stack alignItems="center">
|
|
||||||
<Text color="error">{t('migrate-to-cloud.resource-status.failed', 'Error')}</Text>
|
|
||||||
|
|
||||||
{error && (
|
|
||||||
// TODO: trigger a proper modal, probably from the parent, on click
|
|
||||||
<Button size="sm" variant="secondary" onClick={() => window.alert(error)}>
|
|
||||||
{t('migrate-to-cloud.resource-status.error-details-button', 'Details')}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Text color="secondary">{t('migrate-to-cloud.resource-status.unknown', 'Unknown')}</Text>;
|
return <Text color="secondary">{t('migrate-to-cloud.resource-status.unknown', 'Unknown')}</Text>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ErrorCell({ item }: { item: ResourceTableItem }) {
|
||||||
|
return (
|
||||||
|
<Stack alignItems="center">
|
||||||
|
<Text color="error">{t('migrate-to-cloud.resource-status.failed', 'Error')}</Text>
|
||||||
|
|
||||||
|
{item.error && (
|
||||||
|
<Button size="sm" variant="secondary" onClick={() => item.showError(item)}>
|
||||||
|
{t('migrate-to-cloud.resource-status.error-details-button', 'Details')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import { CellProps } from '@grafana/ui';
|
import { CellProps } from '@grafana/ui';
|
||||||
import { t } from 'app/core/internationalization';
|
import { t } from 'app/core/internationalization';
|
||||||
|
|
||||||
import { MigrateDataResponseItemDto } from '../api';
|
import { ResourceTableItem } from './types';
|
||||||
|
|
||||||
export function TypeCell(props: CellProps<MigrateDataResponseItemDto>) {
|
|
||||||
const { type } = props.row.original;
|
|
||||||
|
|
||||||
|
export function prettyTypeName(type: ResourceTableItem['type']) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'DATASOURCE':
|
case 'DATASOURCE':
|
||||||
return t('migrate-to-cloud.resource-type.datasource', 'Data source');
|
return t('migrate-to-cloud.resource-type.datasource', 'Data source');
|
||||||
@ -17,3 +15,8 @@ export function TypeCell(props: CellProps<MigrateDataResponseItemDto>) {
|
|||||||
return t('migrate-to-cloud.resource-type.unknown', 'Unknown');
|
return t('migrate-to-cloud.resource-type.unknown', 'Unknown');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function TypeCell(props: CellProps<ResourceTableItem>) {
|
||||||
|
const { type } = props.row.original;
|
||||||
|
return <>{prettyTypeName(type)}</>;
|
||||||
|
}
|
||||||
|
5
public/app/features/migrate-to-cloud/onprem/types.ts
Normal file
5
public/app/features/migrate-to-cloud/onprem/types.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { MigrateDataResponseItemDto } from '../api';
|
||||||
|
|
||||||
|
export interface ResourceTableItem extends MigrateDataResponseItemDto {
|
||||||
|
showError: (resource: ResourceTableItem) => void;
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
import { isFetchError } from '@grafana/runtime';
|
||||||
|
import { Alert, Stack, Text } from '@grafana/ui';
|
||||||
|
import { Props as AlertProps } from '@grafana/ui/src/components/Alert/Alert';
|
||||||
|
|
||||||
|
interface AlertWithTraceIDProps extends AlertProps {
|
||||||
|
error?: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AlertWithTraceID(props: AlertWithTraceIDProps) {
|
||||||
|
const { error, children, ...rest } = props;
|
||||||
|
const traceID = maybeGetTraceID(error);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Alert {...rest}>
|
||||||
|
<Stack direction="column" gap={1}>
|
||||||
|
{children}
|
||||||
|
|
||||||
|
{/* Deliberately don't want to translate 'Trace ID' */}
|
||||||
|
{/* eslint-disable-next-line @grafana/no-untranslated-strings */}
|
||||||
|
{traceID && <Text element="p">Trace ID: {traceID}</Text>}
|
||||||
|
</Stack>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybeGetTraceID(err: unknown) {
|
||||||
|
const data = isFetchError<unknown>(err) ? err.data : err;
|
||||||
|
|
||||||
|
if (typeof data === 'object' && data && 'traceID' in data && typeof data.traceID === 'string') {
|
||||||
|
return data.traceID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
@ -1087,6 +1087,13 @@
|
|||||||
"message": "Help us improve this feature by providing feedback and reporting any issues.",
|
"message": "Help us improve this feature by providing feedback and reporting any issues.",
|
||||||
"title": "Migrate to Grafana Cloud is in public preview"
|
"title": "Migrate to Grafana Cloud is in public preview"
|
||||||
},
|
},
|
||||||
|
"resource-error": {
|
||||||
|
"dismiss-button": "OK",
|
||||||
|
"resource-summary": "{{refId}} ({{typeName}})",
|
||||||
|
"specific-error": "The specific error was:",
|
||||||
|
"title": "Unable to migrate this resource",
|
||||||
|
"unknown-error": "An unknown error occurred."
|
||||||
|
},
|
||||||
"resource-status": {
|
"resource-status": {
|
||||||
"error-details-button": "Details",
|
"error-details-button": "Details",
|
||||||
"failed": "Error",
|
"failed": "Error",
|
||||||
|
@ -1087,6 +1087,13 @@
|
|||||||
"message": "Ħęľp ūş įmpřővę ŧĥįş ƒęäŧūřę þy přővįđįʼnģ ƒęęđþäčĸ äʼnđ řępőřŧįʼnģ äʼny įşşūęş.",
|
"message": "Ħęľp ūş įmpřővę ŧĥįş ƒęäŧūřę þy přővįđįʼnģ ƒęęđþäčĸ äʼnđ řępőřŧįʼnģ äʼny įşşūęş.",
|
||||||
"title": "Mįģřäŧę ŧő Ğřäƒäʼnä Cľőūđ įş įʼn pūþľįč přęvįęŵ"
|
"title": "Mįģřäŧę ŧő Ğřäƒäʼnä Cľőūđ įş įʼn pūþľįč přęvįęŵ"
|
||||||
},
|
},
|
||||||
|
"resource-error": {
|
||||||
|
"dismiss-button": "ØĶ",
|
||||||
|
"resource-summary": "{{refId}} ({{typeName}})",
|
||||||
|
"specific-error": "Ŧĥę şpęčįƒįč ęřřőř ŵäş:",
|
||||||
|
"title": "Ůʼnäþľę ŧő mįģřäŧę ŧĥįş řęşőūřčę",
|
||||||
|
"unknown-error": "Åʼn ūʼnĸʼnőŵʼn ęřřőř őččūřřęđ."
|
||||||
|
},
|
||||||
"resource-status": {
|
"resource-status": {
|
||||||
"error-details-button": "Đęŧäįľş",
|
"error-details-button": "Đęŧäįľş",
|
||||||
"failed": "Ēřřőř",
|
"failed": "Ēřřőř",
|
||||||
|
Loading…
Reference in New Issue
Block a user