diff --git a/src/panels/corpglory-dataexporter-panel/components/Panel.tsx b/src/panels/corpglory-dataexporter-panel/components/Panel.tsx index cb45844..60ef817 100644 --- a/src/panels/corpglory-dataexporter-panel/components/Panel.tsx +++ b/src/panels/corpglory-dataexporter-panel/components/Panel.tsx @@ -59,12 +59,19 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) { const [selectedTimeRange, setTimeRange] = useState(timeRange); const [panelStatus, setPanelStatus] = useState(PanelStatus.LOADING); + function setPanelStatusWithValidate(status: PanelStatus): void { + if (panelStatus === PanelStatus.PERMISSION_ERROR) { + return; + } + return setPanelStatus(status); + } + + const [errorMessage, setErrorMessage] = useState(null); const timeZoneName = convertTimeZoneTypeToName(timeZone); if (contextSrv.user.orgRole !== OrgRole.Admin) { - // TODO: it shouldn't be overriten - setPanelStatus(PanelStatus.PERMISSION_ERROR); + setPanelStatusWithValidate(PanelStatus.PERMISSION_ERROR); } useEffect(() => { @@ -121,7 +128,7 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) { setTasksDataFrame(dataFrame); }, [tasks]); // eslint-disable-line react-hooks/exhaustive-deps - useEffect(refresh, []); // eslint-disable-line react-hooks/exhaustive-deps + useEffect(fetchTasks, []); // eslint-disable-line react-hooks/exhaustive-deps useEffect(() => { if (queries === null) { @@ -130,25 +137,26 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) { setQueriesDataFrame(getDataFrameForQueriesTable(queries)); }, [queries]); // eslint-disable-line react-hooks/exhaustive-deps - function refresh(): void { + function fetchTasks(): void { getTasks() .then((tasks) => { setTasks(tasks); - setPanelStatus(PanelStatus.OK); + setPanelStatusWithValidate(PanelStatus.OK); for (let task of tasks) { if (task.progress?.status === ExportStatus.EXPORTING) { - setTimeout(refresh, 1000); + setTimeout(fetchTasks, 1000); return; } } }) .catch((err) => { - setPanelStatus(PanelStatus.DATASOURCE_ERROR); + setPanelStatusWithValidate(PanelStatus.DATASOURCE_ERROR); console.error('some error', err); + setErrorMessage(`${err.name}: ${err.message}`); }); } - eventBus.subscribe(RefreshEvent, refresh); + eventBus.subscribe(RefreshEvent, fetchTasks); function getDatasourceByUid(uid?: string): DataSourceSettings | undefined { if (_.isNil(uid)) { @@ -191,7 +199,7 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) { }, }); - refresh(); + fetchTasks(); onCloseModal(); unselectAllQueries(); @@ -271,48 +279,59 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) { } function getDataFrameForTaskTable(tasks: ExportTask[]): DataFrame { + const sortedTasks = _.orderBy(tasks, (task) => task.progress?.time, 'desc'); const dataFrame = toDataFrame({ name: 'A', fields: [ { name: 'Time', type: FieldType.number, - values: _.map(tasks, (task) => convertTimestampToDate(task.progress?.time)), + values: _.map(sortedTasks, (task) => convertTimestampToDate(task.progress?.time)), + }, + { + name: 'From', + type: FieldType.number, + values: _.map(sortedTasks, (task) => convertTimestampToDate(task.timeRange.from)), + }, + { + name: 'To', + type: FieldType.number, + values: _.map(sortedTasks, (task) => convertTimestampToDate(task.timeRange.to)), }, { name: 'User', type: FieldType.string, - values: _.map(tasks, (task) => task.username), + values: _.map(sortedTasks, (task) => task.username), }, { name: 'Datasource', type: FieldType.string, - values: _.map(tasks, (task) => task.queries.map((query) => query.datasource?.name).join(',')), + values: _.map(sortedTasks, (task) => task.queries.map((query) => query.datasource?.name).join(',')), }, { name: 'Exported Rows', type: FieldType.number, - values: _.map(tasks, (task) => task.progress?.exportedRowsCount), + values: _.map(sortedTasks, (task) => task.progress?.exportedRowsCount), }, { name: 'Progress', type: FieldType.string, - values: _.map(tasks, (task) => `${((task.progress?.progress || 0) * 100).toFixed(0)}%`), + values: _.map(sortedTasks, (task) => `${((task.progress?.progress || 0) * 100).toFixed(0)}%`), }, { name: 'Status', type: FieldType.string, - values: _.map(tasks, (task) => task.progress?.status), + values: _.map(sortedTasks, (task) => task.progress?.status), }, { name: 'Error', type: FieldType.string, - values: _.map(tasks, (task) => task.progress?.errorMessage || '-'), + values: _.map(sortedTasks, (task) => task.progress?.errorMessage || '-'), }, { name: 'Download CSV', type: FieldType.string, - values: _.map(tasks, () => `data:image/png;base64,${DOWNLOAD_ICON_BASE_64}`), + values: _.map(sortedTasks, () => `data:image/png;base64,${DOWNLOAD_ICON_BASE_64}`), config: { custom: { filterable: false, @@ -396,15 +415,19 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) {

Datasource is unavailable.

- Click here to configure DataExporter. + If you have not setup the plugin click + + here + + to configure DataExporter.
- {/* TODO: display error message? */} +

{errorMessage}

); const permissionErrorDiv = (
-

Permission Error.

-
DataExporter panel availabel only for Admins
+

Permission Error.

+
DataExporter panel available only for Admins.
); const mainDiv = ( @@ -422,8 +445,9 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) { {queriesDataFrame === null ? ( - // TODO: if datasource responds with error, display the error - +
+

There are no queries to export.

+
) : (
@@ -480,4 +504,11 @@ const getStyles = () => ({ z-index: 1061; } `, + customLink: css` + color: #6e9fff; + margin: 0 4px; + &:hover { + text-decoration: underline; + } + `, });