UX improvements for bad cases #9

Merged
rozetko merged 4 commits from ui-ux-improvements into master 2 years ago
  1. 162
      src/panels/corpglory-dataexporter-panel/components/Panel.tsx
  2. 7
      src/panels/corpglory-dataexporter-panel/types.ts

162
src/panels/corpglory-dataexporter-panel/components/Panel.tsx

@ -1,4 +1,4 @@
import { PanelOptions, ExportTask, DashboardQuery, DatasourceType, ExportStatus } from '../types'; import { PanelOptions, ExportTask, DashboardQuery, DatasourceType, ExportStatus, PanelStatus } from '../types';
import { convertTimestampToDate, getDashboardUid } from '../../../utils'; import { convertTimestampToDate, getDashboardUid } from '../../../utils';
import { CLOSE_ICON_BASE_64, DOWNLOAD_ICON_BASE_64, SELECT_ICON_BASE_64, UNSELECT_ICON_BASE_64 } from '../../../icons'; import { CLOSE_ICON_BASE_64, DOWNLOAD_ICON_BASE_64, SELECT_ICON_BASE_64, UNSELECT_ICON_BASE_64 } from '../../../icons';
@ -31,12 +31,16 @@ import {
DataQuery, DataQuery,
DataSourceSettings, DataSourceSettings,
TimeRange, TimeRange,
OrgRole,
} from '@grafana/data'; } from '@grafana/data';
import { RefreshEvent } from '@grafana/runtime'; import { RefreshEvent } from '@grafana/runtime';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import * as _ from 'lodash'; import * as _ from 'lodash';
const PANEL_ID = 'corpglory-dataexporter-panel';
const APP_ID = 'corpglory-dataexporter-app';
interface Props extends PanelProps<PanelOptions> {} interface Props extends PanelProps<PanelOptions> {}
export function Panel({ width, height, timeRange, eventBus }: Props) { export function Panel({ width, height, timeRange, eventBus }: Props) {
@ -54,6 +58,13 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
const [selectedTimeRange, setTimeRange] = useState<TimeRange>(timeRange); const [selectedTimeRange, setTimeRange] = useState<TimeRange>(timeRange);
const [panelStatus, setPanelStatus] = useState<PanelStatus>(PanelStatus.LOADING);
if (contextSrv.user.orgRole !== OrgRole.Admin) {
// TODO: it shouldn't be overriten
setPanelStatus(PanelStatus.PERMISSION_ERROR);
}
useEffect(() => { useEffect(() => {
async function getCurrentDashboard(): Promise<any> { async function getCurrentDashboard(): Promise<any> {
const currentDashboardUid = getDashboardUid(window.location.toString()); const currentDashboardUid = getDashboardUid(window.location.toString());
@ -80,8 +91,7 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
const queries: DashboardQuery[] = []; const queries: DashboardQuery[] = [];
// @ts-ignore // @ts-ignore
// TODO: move plugin id to const if (panel.type === PANEL_ID) {
if (panel.type === 'corpglory-dataexporter-panel') {
return; return;
} }
@ -123,6 +133,7 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
getTasks() getTasks()
.then((tasks) => { .then((tasks) => {
setTasks(tasks); setTasks(tasks);
setPanelStatus(PanelStatus.OK);
for (let task of tasks) { for (let task of tasks) {
if (task.progress?.status === ExportStatus.EXPORTING) { if (task.progress?.status === ExportStatus.EXPORTING) {
setTimeout(refresh, 1000); setTimeout(refresh, 1000);
@ -130,7 +141,10 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
} }
} }
}) })
.catch((err) => console.error(err)); .catch((err) => {
setPanelStatus(PanelStatus.DATASOURCE_ERROR);
console.error('some error', err);
});
} }
eventBus.subscribe(RefreshEvent, refresh); eventBus.subscribe(RefreshEvent, refresh);
@ -164,8 +178,8 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
from: timerange[0] * 1000, from: timerange[0] * 1000,
to: timerange[1] * 1000, to: timerange[1] * 1000,
}, },
queries: selectedQueries queries: selectedQueries,
} };
// TODO: move this function to API Service // TODO: move this function to API Service
await queryApi('/task', { await queryApi('/task', {
method: 'POST', method: 'POST',
@ -189,6 +203,7 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
} }
function openDatasourceModal(): void { function openDatasourceModal(): void {
setTimeRange(timeRange);
setModalVisibility(true); setModalVisibility(true);
} }
@ -270,7 +285,7 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
{ {
name: 'Datasource', name: 'Datasource',
type: FieldType.string, type: FieldType.string,
values: _.map(tasks, (task) => task.queries.map(query => query.datasource?.name).join(',')), values: _.map(tasks, (task) => task.queries.map((query) => query.datasource?.name).join(',')),
}, },
{ {
name: 'Exported Rows', name: 'Exported Rows',
@ -373,65 +388,86 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
return ( const loadingDiv = <LoadingPlaceholder text="Loading..."></LoadingPlaceholder>;
// TODO: add styles
const datasourceErrorDiv = (
<div>
<p>Datasource is unavailable.</p>
<div>
Click <a href={`/plugins/${APP_ID}`}>here</a> to configure DataExporter.
</div>
{/* TODO: display error message? */}
</div>
);
const permissionErrorDiv = (
<div>
<p>Permission Error.</p>
<div> DataExporter panel availabel only for Admins </div>
</div>
);
const mainDiv = (
<div> <div>
{tasksDataFrame === null ? ( <Table width={width} height={height - 40} data={tasksDataFrame as DataFrame} />
// TODO: if datasource responds with error, display the error <HorizontalGroup justify="flex-end">
<LoadingPlaceholder text="Loading..."></LoadingPlaceholder> <Button
) : ( variant="primary"
<div> aria-label="Rich history button"
<Table width={width} height={height - 40} data={tasksDataFrame} /> icon="plus"
<HorizontalGroup justify="flex-end"> style={{ marginTop: '8px' }}
<Button onClick={openDatasourceModal}
variant="primary" >
aria-label="Rich history button" Add Task
icon="plus" </Button>
style={{ marginTop: '8px' }} <Modal title="Select Queries" isOpen={isModalOpen} onDismiss={onCloseModal} className={styles.calendarModal}>
onClick={openDatasourceModal} {queriesDataFrame === null ? (
> // TODO: if datasource responds with error, display the error
Add Task <LoadingPlaceholder text="Loading..."></LoadingPlaceholder>
</Button> ) : (
<Modal <div>
title="Select Queries" <VerticalGroup spacing="xs">
isOpen={isModalOpen} <HorizontalGroup justify="flex-start" spacing="md">
onDismiss={onCloseModal} <TimeRangeInput
className={styles.calendarModal} value={selectedTimeRange}
> onChange={(newTimeRange) => {
{queriesDataFrame === null ? ( setTimeRange(newTimeRange);
// TODO: if datasource responds with error, display the error }}
<LoadingPlaceholder text="Loading..."></LoadingPlaceholder> />
) : ( </HorizontalGroup>
<div> <Table width={width / 2 - 20} height={height - 40} data={queriesDataFrame} />
<VerticalGroup spacing="xs"> <HorizontalGroup justify="flex-end" spacing="md">
<HorizontalGroup justify="flex-start" spacing="md"> <Button
<TimeRangeInput variant="primary"
value={selectedTimeRange} aria-label="Add task button"
onChange={(newTimeRange) => { onClick={onAddTaskClick}
setTimeRange(newTimeRange); // TODO: move to function
}} disabled={!queries?.filter((query: DashboardQuery) => query.selected)?.length}
/> >
</HorizontalGroup> Add Task
<Table width={width / 2 - 20} height={height - 40} data={queriesDataFrame} /> </Button>
<HorizontalGroup justify="flex-end" spacing="md"> </HorizontalGroup>
<Button </VerticalGroup>
variant="primary" </div>
aria-label="Add task button" )}
onClick={onAddTaskClick} </Modal>
// TODO: move to function </HorizontalGroup>
disabled={!queries?.filter((query: DashboardQuery) => query.selected)?.length}
>
Add Task
</Button>
</HorizontalGroup>
</VerticalGroup>
</div>
)}
</Modal>
</HorizontalGroup>
</div>
)}
</div> </div>
); );
function renderSwitch(panelStatus: PanelStatus): JSX.Element {
switch (panelStatus) {
case PanelStatus.LOADING:
return loadingDiv;
case PanelStatus.DATASOURCE_ERROR:
return datasourceErrorDiv;
case PanelStatus.PERMISSION_ERROR:
return permissionErrorDiv;
case PanelStatus.OK:
return mainDiv;
default:
return datasourceErrorDiv;
}
}
return <div>{renderSwitch(panelStatus)}</div>;
} }
const getStyles = () => ({ const getStyles = () => ({

7
src/panels/corpglory-dataexporter-panel/types.ts

@ -11,6 +11,13 @@ export enum DatasourceType {
MYSQL = 'mysql', MYSQL = 'mysql',
} }
export enum PanelStatus {
LOADING = 'Loading',
DATASOURCE_ERROR = 'Datasource Error',
PERMISSION_ERROR = 'Permission Error',
OK = 'Ok',
}
export enum ExportStatus { export enum ExportStatus {
EXPORTING = 'exporting', EXPORTING = 'exporting',
FINISHED = 'finished', FINISHED = 'finished',

Loading…
Cancel
Save