Browse Source

Merge pull request 'Panel UI/UX fixes' (#12) from panel-ui-ux-fixes into master

Reviewed-on: #12
pull/14/head
rozetko 2 years ago
parent
commit
341c905513
  1. 75
      src/panels/corpglory-dataexporter-panel/components/Panel.tsx

75
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>(timeRange); const [selectedTimeRange, setTimeRange] = useState<TimeRange>(timeRange);
const [panelStatus, setPanelStatus] = useState<PanelStatus>(PanelStatus.LOADING); const [panelStatus, setPanelStatus] = useState<PanelStatus>(PanelStatus.LOADING);
function setPanelStatusWithValidate(status: PanelStatus): void {
if (panelStatus === PanelStatus.PERMISSION_ERROR) {
return;
}
return setPanelStatus(status);
}
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const timeZoneName = convertTimeZoneTypeToName(timeZone); const timeZoneName = convertTimeZoneTypeToName(timeZone);
if (contextSrv.user.orgRole !== OrgRole.Admin) { if (contextSrv.user.orgRole !== OrgRole.Admin) {
// TODO: it shouldn't be overriten setPanelStatusWithValidate(PanelStatus.PERMISSION_ERROR);
setPanelStatus(PanelStatus.PERMISSION_ERROR);
} }
useEffect(() => { useEffect(() => {
@ -121,7 +128,7 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) {
setTasksDataFrame(dataFrame); setTasksDataFrame(dataFrame);
}, [tasks]); // eslint-disable-line react-hooks/exhaustive-deps }, [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(() => { useEffect(() => {
if (queries === null) { if (queries === null) {
@ -130,25 +137,26 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) {
setQueriesDataFrame(getDataFrameForQueriesTable(queries)); setQueriesDataFrame(getDataFrameForQueriesTable(queries));
}, [queries]); // eslint-disable-line react-hooks/exhaustive-deps }, [queries]); // eslint-disable-line react-hooks/exhaustive-deps
function refresh(): void { function fetchTasks(): void {
getTasks() getTasks()
.then((tasks) => { .then((tasks) => {
setTasks(tasks); setTasks(tasks);
setPanelStatus(PanelStatus.OK); setPanelStatusWithValidate(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(fetchTasks, 1000);
return; return;
} }
} }
}) })
.catch((err) => { .catch((err) => {
setPanelStatus(PanelStatus.DATASOURCE_ERROR); setPanelStatusWithValidate(PanelStatus.DATASOURCE_ERROR);
console.error('some error', err); console.error('some error', err);
setErrorMessage(`${err.name}: ${err.message}`);
}); });
} }
eventBus.subscribe(RefreshEvent, refresh); eventBus.subscribe(RefreshEvent, fetchTasks);
function getDatasourceByUid(uid?: string): DataSourceSettings | undefined { function getDatasourceByUid(uid?: string): DataSourceSettings | undefined {
if (_.isNil(uid)) { if (_.isNil(uid)) {
@ -191,7 +199,7 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) {
}, },
}); });
refresh(); fetchTasks();
onCloseModal(); onCloseModal();
unselectAllQueries(); unselectAllQueries();
@ -271,48 +279,59 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) {
} }
function getDataFrameForTaskTable(tasks: ExportTask[]): DataFrame { function getDataFrameForTaskTable(tasks: ExportTask[]): DataFrame {
const sortedTasks = _.orderBy(tasks, (task) => task.progress?.time, 'desc');
const dataFrame = toDataFrame({ const dataFrame = toDataFrame({
name: 'A', name: 'A',
fields: [ fields: [
{ {
name: 'Time', name: 'Time',
type: FieldType.number, 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', name: 'User',
type: FieldType.string, type: FieldType.string,
values: _.map(tasks, (task) => task.username), values: _.map(sortedTasks, (task) => task.username),
}, },
{ {
name: 'Datasource', name: 'Datasource',
type: FieldType.string, 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', name: 'Exported Rows',
type: FieldType.number, type: FieldType.number,
values: _.map(tasks, (task) => task.progress?.exportedRowsCount), values: _.map(sortedTasks, (task) => task.progress?.exportedRowsCount),
}, },
{ {
name: 'Progress', name: 'Progress',
type: FieldType.string, 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', name: 'Status',
type: FieldType.string, type: FieldType.string,
values: _.map(tasks, (task) => task.progress?.status), values: _.map(sortedTasks, (task) => task.progress?.status),
}, },
{ {
name: 'Error', name: 'Error',
type: FieldType.string, type: FieldType.string,
values: _.map(tasks, (task) => task.progress?.errorMessage || '-'), values: _.map(sortedTasks, (task) => task.progress?.errorMessage || '-'),
}, },
{ {
name: 'Download CSV', name: 'Download CSV',
type: FieldType.string, 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: { config: {
custom: { custom: {
filterable: false, filterable: false,
@ -396,15 +415,19 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) {
<div> <div>
<p>Datasource is unavailable.</p> <p>Datasource is unavailable.</p>
<div> <div>
Click <a href={`/plugins/${APP_ID}`}>here</a> to configure DataExporter. If you have not setup the plugin click
<a className={styles.customLink} href={`/plugins/${APP_ID}`}>
here
</a>
to configure DataExporter.
</div> </div>
{/* TODO: display error message? */} <p style={{ marginTop: '16px' }}>{errorMessage}</p>
</div> </div>
); );
const permissionErrorDiv = ( const permissionErrorDiv = (
<div> <div>
<p> Permission Error. </p> <p> Permission Error. </p>
<div> DataExporter panel availabel only for Admins </div> <div> DataExporter panel available only for Admins. </div>
</div> </div>
); );
const mainDiv = ( const mainDiv = (
@ -422,8 +445,9 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) {
</Button> </Button>
<Modal title="Select Queries" isOpen={isModalOpen} onDismiss={onCloseModal} className={styles.calendarModal}> <Modal title="Select Queries" isOpen={isModalOpen} onDismiss={onCloseModal} className={styles.calendarModal}>
{queriesDataFrame === null ? ( {queriesDataFrame === null ? (
// TODO: if datasource responds with error, display the error <div>
<LoadingPlaceholder text="Loading..."></LoadingPlaceholder> <p>There are no queries to export.</p>
</div>
) : ( ) : (
<div> <div>
<VerticalGroup spacing="xs"> <VerticalGroup spacing="xs">
@ -480,4 +504,11 @@ const getStyles = () => ({
z-index: 1061; z-index: 1061;
} }
`, `,
customLink: css`
color: #6e9fff;
margin: 0 4px;
&:hover {
text-decoration: underline;
}
`,
}); });

Loading…
Cancel
Save