import { PanelOptions, TaskTableRowConfig, QueryTableRowConfig, DatasourceType } from '../types'; 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 { queryApi } from '../../../services/api_service'; import { getDashboardByUid, getDatasources } from '../../../services/grafana_backend_service'; import { contextSrv } from 'grafana/app/core/core'; import { Table, Button, HorizontalGroup, Modal, LoadingPlaceholder } from '@grafana/ui'; import { PanelProps, toDataFrame, FieldType, applyFieldOverrides, createTheme, DataFrame, DataLinkClickEvent, PanelModel, DataQuery, DataSourceSettings, } from '@grafana/data'; import React, { useState, useEffect } from 'react'; import * as _ from 'lodash'; interface Props extends PanelProps {} export function Panel({ width, height, timeRange }: Props) { // TODO: Dashboard type const [dashboard, setDashboard] = useState(null); const [datasources, setDatasources] = useState(null); const [tasks, setTasks] = useState(null); const [queries, setQueries] = useState(null); const [tasksDataFrame, setTasksDataFrame] = useState(null); const [queriesDataFrame, setQueriesDataFrame] = useState(null); const [isModalOpen, setModalVisibility] = useState(false); useEffect(() => { async function getCurrentDashboard(): Promise { const currentDashboardUid = getDashboardUid(window.location.toString()); console.log(currentDashboardUid) return getDashboardByUid(currentDashboardUid); } getCurrentDashboard() .then((dash) => setDashboard(dash.dashboard)) .catch((err) => console.error(err)); }, []); useEffect(() => { getDatasources() .then((datasources) => setDatasources(datasources)) .catch((err) => console.error(err)); }, []); useEffect(() => { if (!dashboard || !datasources) { return; } dashboard.panels.forEach((panel: PanelModel) => { const queries: QueryTableRowConfig[] = []; // @ts-ignore // TODO: move plugin id to const if (panel.type === 'corpglory-dataexporter-panel') { return; } if (!_.includes(_.values(DatasourceType), panel.datasource?.type)) { return; } console.log(panel) panel.targets?.forEach( (target: DataQuery) => { console.log('uid',target.datasource?.uid) const datasource = getDatasourceByUid(target.datasource?.uid); if (!datasource) { return; } queries.push({ ...target, selected: false, panel, datasource }); } ); console.log('q', queries) setQueries(queries); }) }, [dashboard, datasources]); useEffect(() => { if (tasks === null) { return; } const dataFrame = getDataFrameForTaskTable(tasks); setTasksDataFrame(dataFrame); }, [tasks]); useEffect(() => { // TODO: move this function to API Service async function getTasks(): Promise { return queryApi('/task', {}); } getTasks() .then((tasks) => setTasks(tasks)) .catch((err) => console.error(err)); }, []); useEffect(() => { if(queries === null) { return; } setQueriesDataFrame(getDataFrameForQueriesTable(queries)); }, [queries]); function getDatasourceByUid(uid?: string): DataSourceSettings | undefined { if (_.isNil(uid)) { console.warn(`uid is required to get datasource`); return undefined; } if (datasources === null) { console.warn(`there is no datasources yet`); return undefined; } const datasource = _.find(datasources, (datasource: DataSourceSettings) => datasource.uid === uid); if (!datasource) { console.warn(`can't find datasource "${uid}"`); } return datasource; } async function onAddTaskClick(): Promise { const selectedQueries = _.filter(queries, (query: QueryTableRowConfig) => query.selected); // TODO: timerange picker const timerange: [number, number] = [timeRange.from.unix(), timeRange.to.unix()]; // TODO: move this function to API Service await queryApi('/task', { method: 'POST', data: { from: timerange[0] * 1000, to: timerange[1] * 1000, // @ts-ignore username: contextSrv.user.username, tasks: selectedQueries, url: window.location.toString(), }, }); // TODO: refresh panel (or get websocket events) onCloseModal(); unselectAllQueries(); } function unselectAllQueries(): void { if (queries === null) { return; } setQueries(queries.map( (query: QueryTableRowConfig) => ({ ...query, selected: false }) )); } function openDatasourceModal(): void { setModalVisibility(true); } function onCloseModal(): void { setModalVisibility(false); unselectAllQueries(); } function getDataFrameForQueriesTable(configs: QueryTableRowConfig[]): DataFrame { const dataFrame = toDataFrame({ name: 'A', fields: [ { name: 'Select', type: FieldType.string, values: _.map( configs, (config) => `data:image/svg+xml;base64,${config.selected ? SELECT_ICON_BASE_64 : UNSELECT_ICON_BASE_64}` ), config: { custom: { filterable: false, displayMode: 'image', }, links: [ { targetBlank: false, title: 'Select', url: '#', onClick: (event: DataLinkClickEvent) => onDatasourceSelectClick(event, configs), }, ], }, }, { name: 'Panel', type: FieldType.string, values: _.map(configs, (config) => config.panel.title), }, { name: 'RefId', type: FieldType.string, values: _.map(configs, (config) => config.refId), }, { name: 'Datasource', type: FieldType.string, values: _.map(configs, (config) => config.datasource.name), }, ], }); const dataFrames = applyFieldOverrides({ data: [dataFrame], fieldConfig: { overrides: [], defaults: {}, }, theme: createTheme(), replaceVariables: (value: string) => value, }); return dataFrames[0]; } function getDataFrameForTaskTable(configs: TaskTableRowConfig[]): DataFrame { const dataFrame = toDataFrame({ name: 'A', fields: [ { name: 'Time', type: FieldType.number, values: _.map(configs, (config) => convertTimestampToDate(config.timestamp)), }, { name: 'User', type: FieldType.string, values: _.map(configs, (config) => config.user), }, { name: 'Datasource', type: FieldType.string, values: _.map(configs, (config) => getDatasourceByUid(config.datasourceRef?.uid)?.name), }, { name: 'Exported Rows', type: FieldType.number, values: _.map(configs, (config) => config.rowsCount), }, { name: 'Progress', type: FieldType.string, values: _.map(configs, (config) => `${config.progress}%`), }, { name: 'Status', type: FieldType.string, values: _.map(configs, (config) => config.status), }, { name: 'Download CSV', type: FieldType.string, values: _.map(configs, () => `data:image/png;base64,${DOWNLOAD_ICON_BASE_64}`), config: { custom: { filterable: false, displayMode: 'image', }, links: [ { targetBlank: false, title: 'CSV link', // TODO: config.downloadLink url: 'https://chartwerk.io/', }, ], }, }, { name: 'Delete task', type: FieldType.string, values: _.map(configs, () => `data:image/png;base64,${CLOSE_ICON_BASE_64}`), config: { custom: { filterable: false, displayMode: 'image', }, links: [ { targetBlank: false, title: 'Delete', url: '#', onClick: (event: DataLinkClickEvent) => onDeleteClick(event, configs), }, ], }, }, ], }); const dataFrames = applyFieldOverrides({ data: [dataFrame], fieldConfig: { overrides: [], defaults: {}, }, theme: createTheme(), replaceVariables: (value: string) => value, }); return dataFrames[0]; } function onDeleteClick(e: DataLinkClickEvent, tasks: TaskTableRowConfig[]): void { // TODO: make DELETE request to the api const rowIndex = e.origin.rowIndex; const filteredTasks = _.filter(tasks, (task, idx) => idx !== rowIndex); setTasks(filteredTasks); } function onDatasourceSelectClick(e: DataLinkClickEvent, queries: QueryTableRowConfig[]): void { const rowIndex = e.origin.rowIndex; const updatedQueries = _.clone(queries); updatedQueries[rowIndex].selected = !updatedQueries[rowIndex].selected; setQueries(updatedQueries); } return (
{tasksDataFrame === null ? ( // TODO: if datasource responds with error, display the error ) : (
{queriesDataFrame === null ? ( // TODO: if datasource responds with error, display the error ) : (
)} )} ); }