|
|
|
@ -1,4 +1,4 @@
|
|
|
|
|
import { PanelOptions, TaskTableRowConfig, QueryTableRowConfig, DatasourceType, PanelStatus } from '../types'; |
|
|
|
|
import { PanelOptions, ExportTask, DashboardQuery, DatasourceType, ExportStatus, PanelStatus } 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'; |
|
|
|
@ -48,8 +48,8 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
|
|
|
|
|
const [dashboard, setDashboard] = useState<any | null>(null); |
|
|
|
|
const [datasources, setDatasources] = useState<DataSourceSettings[] | null>(null); |
|
|
|
|
|
|
|
|
|
const [tasks, setTasks] = useState<TaskTableRowConfig[] | null>(null); |
|
|
|
|
const [queries, setQueries] = useState<QueryTableRowConfig[] | null>(null); |
|
|
|
|
const [tasks, setTasks] = useState<ExportTask[] | null>(null); |
|
|
|
|
const [queries, setQueries] = useState<DashboardQuery[] | null>(null); |
|
|
|
|
|
|
|
|
|
const [tasksDataFrame, setTasksDataFrame] = useState<DataFrame | null>(null); |
|
|
|
|
const [queriesDataFrame, setQueriesDataFrame] = useState<DataFrame | null>(null); |
|
|
|
@ -88,7 +88,7 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
dashboard.panels.forEach((panel: PanelModel) => { |
|
|
|
|
const queries: QueryTableRowConfig[] = []; |
|
|
|
|
const queries: DashboardQuery[] = []; |
|
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
if (panel.type === PANEL_ID) { |
|
|
|
@ -135,8 +135,7 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
|
|
|
|
|
setTasks(tasks); |
|
|
|
|
setPanelStatus(PanelStatus.OK); |
|
|
|
|
for (let task of tasks) { |
|
|
|
|
// TODO: ExportStatus enum
|
|
|
|
|
if (task.status === 'exporting') { |
|
|
|
|
if (task.progress?.status === ExportStatus.EXPORTING) { |
|
|
|
|
setTimeout(refresh, 1000); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
@ -169,18 +168,23 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async function onAddTaskClick(): Promise<void> { |
|
|
|
|
const selectedQueries = _.filter(queries, (query: QueryTableRowConfig) => query.selected); |
|
|
|
|
// TODO: timerange picker
|
|
|
|
|
const selectedQueries = _.filter(queries, (query: DashboardQuery) => query.selected); |
|
|
|
|
const timerange: [number, number] = [selectedTimeRange.from.unix(), selectedTimeRange.to.unix()]; |
|
|
|
|
|
|
|
|
|
const task: ExportTask = { |
|
|
|
|
// @ts-ignore
|
|
|
|
|
username: contextSrv.user.name, |
|
|
|
|
timeRange: { |
|
|
|
|
from: timerange[0] * 1000, |
|
|
|
|
to: timerange[1] * 1000, |
|
|
|
|
}, |
|
|
|
|
queries: selectedQueries, |
|
|
|
|
}; |
|
|
|
|
// 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.name, |
|
|
|
|
tasks: selectedQueries, |
|
|
|
|
task, |
|
|
|
|
url: window.location.toString(), |
|
|
|
|
}, |
|
|
|
|
}); |
|
|
|
@ -195,7 +199,7 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
|
|
|
|
|
if (queries === null) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
setQueries(queries.map((query: QueryTableRowConfig) => ({ ...query, selected: false }))); |
|
|
|
|
setQueries(queries.map((query: DashboardQuery) => ({ ...query, selected: false }))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function openDatasourceModal(): void { |
|
|
|
@ -208,7 +212,7 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
|
|
|
|
|
unselectAllQueries(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function getDataFrameForQueriesTable(configs: QueryTableRowConfig[]): DataFrame { |
|
|
|
|
function getDataFrameForQueriesTable(queries: DashboardQuery[]): DataFrame { |
|
|
|
|
const dataFrame = toDataFrame({ |
|
|
|
|
name: 'A', |
|
|
|
|
fields: [ |
|
|
|
@ -216,8 +220,8 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
|
|
|
|
|
name: 'Select', |
|
|
|
|
type: FieldType.string, |
|
|
|
|
values: _.map( |
|
|
|
|
configs, |
|
|
|
|
(config) => `data:image/svg+xml;base64,${config.selected ? SELECT_ICON_BASE_64 : UNSELECT_ICON_BASE_64}` |
|
|
|
|
queries, |
|
|
|
|
(query) => `data:image/svg+xml;base64,${query.selected ? SELECT_ICON_BASE_64 : UNSELECT_ICON_BASE_64}` |
|
|
|
|
), |
|
|
|
|
config: { |
|
|
|
|
custom: { |
|
|
|
@ -237,17 +241,17 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
|
|
|
|
|
{ |
|
|
|
|
name: 'Panel', |
|
|
|
|
type: FieldType.string, |
|
|
|
|
values: _.map(configs, (config) => config.panel.title), |
|
|
|
|
values: _.map(queries, (query) => query.panel.title), |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: 'RefId', |
|
|
|
|
type: FieldType.string, |
|
|
|
|
values: _.map(configs, (config) => config.refId), |
|
|
|
|
values: _.map(queries, (query) => query.refId), |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: 'Datasource', |
|
|
|
|
type: FieldType.string, |
|
|
|
|
values: _.map(configs, (config) => config.datasource.name), |
|
|
|
|
values: _.map(queries, (query) => query.datasource.name), |
|
|
|
|
}, |
|
|
|
|
], |
|
|
|
|
}); |
|
|
|
@ -264,44 +268,49 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
|
|
|
|
|
return dataFrames[0]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function getDataFrameForTaskTable(configs: TaskTableRowConfig[]): DataFrame { |
|
|
|
|
function getDataFrameForTaskTable(tasks: ExportTask[]): DataFrame { |
|
|
|
|
const dataFrame = toDataFrame({ |
|
|
|
|
name: 'A', |
|
|
|
|
fields: [ |
|
|
|
|
{ |
|
|
|
|
name: 'Time', |
|
|
|
|
type: FieldType.number, |
|
|
|
|
values: _.map(configs, (config) => convertTimestampToDate(config.timestamp)), |
|
|
|
|
values: _.map(tasks, (task) => convertTimestampToDate(task.progress?.time)), |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: 'User', |
|
|
|
|
type: FieldType.string, |
|
|
|
|
values: _.map(configs, (config) => config.username), |
|
|
|
|
values: _.map(tasks, (task) => task.username), |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: 'Datasource', |
|
|
|
|
type: FieldType.string, |
|
|
|
|
values: _.map(configs, (config) => getDatasourceByUid(config.datasourceRef?.uid)?.name), |
|
|
|
|
values: _.map(tasks, (task) => task.queries.map((query) => query.datasource?.name).join(',')), |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: 'Exported Rows', |
|
|
|
|
type: FieldType.number, |
|
|
|
|
values: _.map(configs, (config) => config.rowsCount), |
|
|
|
|
values: _.map(tasks, (task) => task.progress?.exportedRowsCount), |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: 'Progress', |
|
|
|
|
type: FieldType.string, |
|
|
|
|
values: _.map(configs, (config) => `${(config.progress * 100).toFixed(0)}%`), |
|
|
|
|
values: _.map(tasks, (task) => `${((task.progress?.progress || 0) * 100).toFixed(0)}%`), |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: 'Status', |
|
|
|
|
type: FieldType.string, |
|
|
|
|
values: _.map(configs, (config) => config.status), |
|
|
|
|
values: _.map(tasks, (task) => task.progress?.status), |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: 'Error', |
|
|
|
|
type: FieldType.string, |
|
|
|
|
values: _.map(tasks, (task) => task.progress?.errorMessage || '-'), |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: 'Download CSV', |
|
|
|
|
type: FieldType.string, |
|
|
|
|
values: _.map(configs, () => `data:image/png;base64,${DOWNLOAD_ICON_BASE_64}`), |
|
|
|
|
values: _.map(tasks, () => `data:image/png;base64,${DOWNLOAD_ICON_BASE_64}`), |
|
|
|
|
config: { |
|
|
|
|
custom: { |
|
|
|
|
filterable: false, |
|
|
|
@ -320,7 +329,7 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
|
|
|
|
|
{ |
|
|
|
|
name: 'Delete task', |
|
|
|
|
type: FieldType.string, |
|
|
|
|
values: _.map(configs, () => `data:image/png;base64,${CLOSE_ICON_BASE_64}`), |
|
|
|
|
values: _.map(tasks, () => `data:image/png;base64,${CLOSE_ICON_BASE_64}`), |
|
|
|
|
config: { |
|
|
|
|
custom: { |
|
|
|
|
filterable: false, |
|
|
|
@ -355,7 +364,7 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
|
|
|
|
|
const rowIndex = e.origin.rowIndex; |
|
|
|
|
|
|
|
|
|
const task = _.find(tasks, (task, idx) => idx === rowIndex); |
|
|
|
|
await deleteTask(task?.filename); |
|
|
|
|
await deleteTask(task?.id); |
|
|
|
|
|
|
|
|
|
const filteredTasks = _.filter(tasks, (task, idx) => idx !== rowIndex); |
|
|
|
|
setTasks(filteredTasks); |
|
|
|
@ -364,7 +373,7 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
|
|
|
|
|
function onDownloadClick(e: DataLinkClickEvent): void { |
|
|
|
|
const rowIndex = e.origin.rowIndex; |
|
|
|
|
const task = _.find(tasks, (task, idx) => idx === rowIndex); |
|
|
|
|
getStaticFile(task?.filename); |
|
|
|
|
getStaticFile(task?.id); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function onDatasourceSelectClick(e: DataLinkClickEvent): void { |
|
|
|
@ -411,6 +420,7 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
|
|
|
|
|
</Button> |
|
|
|
|
<Modal title="Select Queries" isOpen={isModalOpen} onDismiss={onCloseModal} className={styles.calendarModal}> |
|
|
|
|
{queriesDataFrame === null ? ( |
|
|
|
|
// TODO: if datasource responds with error, display the error
|
|
|
|
|
<LoadingPlaceholder text="Loading..."></LoadingPlaceholder> |
|
|
|
|
) : ( |
|
|
|
|
<div> |
|
|
|
@ -430,7 +440,7 @@ export function Panel({ width, height, timeRange, eventBus }: Props) {
|
|
|
|
|
aria-label="Add task button" |
|
|
|
|
onClick={onAddTaskClick} |
|
|
|
|
// TODO: move to function
|
|
|
|
|
disabled={!queries?.filter((query: QueryTableRowConfig) => query.selected)?.length} |
|
|
|
|
disabled={!queries?.filter((query: DashboardQuery) => query.selected)?.length} |
|
|
|
|
> |
|
|
|
|
Add Task |
|
|
|
|
</Button> |
|
|
|
|