Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Query Source page to React: Visualization actions #4467

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 33 additions & 28 deletions client/app/pages/queries/QuerySource.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ import { QueryControlDropdown } from "@/components/EditVisualizationButton/Query
import { TimeAgo } from "@/components/TimeAgo";
import EmbedQueryDialog from "@/components/queries/EmbedQueryDialog";
import AddToDashboardDialog from "@/components/queries/AddToDashboardDialog";
import EditVisualizationDialog from "@/visualizations/EditVisualizationDialog";
import { routesToAngularRoutes } from "@/lib/utils";
import useQueryResult from "@/lib/hooks/useQueryResult";
import useForceUpdate from "@/lib/hooks/useForceUpdate";
import { durationHumanize, prettySize } from "@/filters";
import { Query } from "@/services/query";
import { DataSource, SCHEMA_NOT_SUPPORTED } from "@/services/data-source";
Expand All @@ -23,7 +21,7 @@ import QueryPageHeader from "./components/QueryPageHeader";
import QueryVisualizationTabs from "./components/QueryVisualizationTabs";
import SchemaBrowser from "./components/SchemaBrowser";
import useVisualizationTabHandler from "./utils/useVisualizationTabHandler";
import { updateQuery } from "./utils";
import { updateQuery, deleteQueryVisualization, addQueryVisualization, editQueryVisualization } from "./utils";

import "./query-source.less";

Expand Down Expand Up @@ -55,8 +53,6 @@ function chooseDataSourceId(dataSourceIds, availableDataSources) {
}

function QuerySource(props) {
const forceUpdate = useForceUpdate();

const [query, setQuery] = useState(props.query);
const [allDataSources, setAllDataSources] = useState([]);
const [dataSourcesLoaded, setDataSourcesLoaded] = useState(false);
Expand Down Expand Up @@ -160,28 +156,21 @@ function QuerySource(props) {

const queryExecuting = false; // TODO: Replace with real value

const openAddToDashboardDialog = useCallback(visualizationId => {
const visualization = find(query.visualizations, { id: visualizationId });
AddToDashboardDialog.showModal({ visualization });
}, [query]);

const openEmbedDialog = useCallback((unused, visualizationId) => {
const visualization = find(query.visualizations, { id: visualizationId });
EmbedQueryDialog.showModal({ query, visualization });
}, [query]);
const openAddToDashboardDialog = useCallback(
visualizationId => {
const visualization = find(query.visualizations, { id: visualizationId });
AddToDashboardDialog.showModal({ visualization });
},
[query]
);

const openEditVisualizationDialog = useCallback(visualizationId => {
// TODO: New queries should be saved (and URL updated), and only then this dialog should appear
const visualization = find(query.visualizations, { id: visualizationId });
EditVisualizationDialog.showModal({
query,
visualization,
queryResult,
}).result.then(visualization => {
setSelectedTab(visualization.id);
forceUpdate();
});
}, [query, queryResult, setSelectedTab, forceUpdate]);
const openEmbedDialog = useCallback(
(unused, visualizationId) => {
const visualization = find(query.visualizations, { id: visualizationId });
EmbedQueryDialog.showModal({ query, visualization });
},
[query]
);

return (
<div className="query-page-wrapper">
Expand Down Expand Up @@ -252,8 +241,18 @@ function QuerySource(props) {
queryResult={queryResult}
visualizations={query.visualizations}
showNewVisualizationButton={query.can_edit}
canDeleteVisualizations={query.can_edit}
selectedTab={selectedTab}
onChangeTab={setSelectedTab}
onClickNewVisualization={() =>
addQueryVisualization(query, queryResult).then(({ query, visualization }) => {
setQuery(query);
setSelectedTab(visualization.id);
})
}
onDeleteVisualization={visualization =>
deleteQueryVisualization(query, visualization).then(setQuery)
}
/>
</div>
</div>
Expand All @@ -267,7 +266,13 @@ function QuerySource(props) {
<div className="bottom-controller">
{!query.isNew() && query.can_edit && (
<EditVisualizationButton
openVisualizationEditor={openEditVisualizationDialog}
openVisualizationEditor={visId =>
editQueryVisualization(
query,
queryResult,
find(query.visualizations, { id: visId })
).then(({ query }) => setQuery(query))
}
selectedTab={selectedTab}
/>
)}
Expand All @@ -291,7 +296,7 @@ function QuerySource(props) {
{!queryExecuting && (
<React.Fragment>
<strong>{durationHumanize(queryResultData.runtime)}</strong>
<span className="hidden-xs">{" "}runtime</span>
<span className="hidden-xs"> runtime</span>
</React.Fragment>
)}
{queryExecuting && <span>Running&hellip;</span>}
Expand Down
15 changes: 15 additions & 0 deletions client/app/pages/queries/utils/deleteQueryVisualization.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { clone, extend, filter } from "lodash";
import { Visualization } from "@/services/visualization";
import notification from "@/services/notification";

export default function deleteQueryVisualization(query, visualization) {
return Visualization.delete({ id: visualization.id })
.$promise.then(() => {
const filteredVisualizations = filter(query.visualizations, v => v.id !== visualization.id);
return Promise.resolve(extend(clone(query), { visualizations: filteredVisualizations }));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While working on #4468 I realized that _.clone is not enough to properly clone query instance, so I added a .clone() method to Query service. No need to fix it in this PR, but we should keep this in mind and fix in one of subsequent PRs

})
.catch(() => {
notification.error("Error deleting visualization.", "Maybe it's used in a dashboard?");
return Promise.reject();
});
}
20 changes: 20 additions & 0 deletions client/app/pages/queries/utils/editQueryVisualization.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { clone, extend, filter, get } from "lodash";
import EditVisualizationDialog from "@/visualizations/EditVisualizationDialog";

export function editQueryVisualization(query, queryResult, visualization) {
return EditVisualizationDialog.showModal({
query,
visualization,
queryResult,
}).result.then(updatedVisualization => {
const filteredVisualizations = filter(query.visualizations, v => v.id !== updatedVisualization.id);
return Promise.resolve({
query: extend(clone(query), { visualizations: [...filteredVisualizations, updatedVisualization] }),
visualization: updatedVisualization,
});
});
}

export function addQueryVisualization(query, queryResult) {
return editQueryVisualization(query, queryResult, null);
}
14 changes: 13 additions & 1 deletion client/app/pages/queries/utils/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import updateQuery from "./updateQuery";
import archiveQuery from "./archiveQuery";
import duplicateQuery from "./duplicateQuery";
import deleteQueryVisualization from "./deleteQueryVisualization";
import { addQueryVisualization, editQueryVisualization } from "./editQueryVisualization";
import { clientConfig } from "@/services/auth";
import recordEvent from "@/services/recordEvent";

Expand All @@ -27,4 +29,14 @@ function renameQuery(query, name) {
return updateQuery(query, changes, options);
}

export { updateQuery, archiveQuery, duplicateQuery, publishQuery, unpublishQuery, renameQuery };
export {
updateQuery,
archiveQuery,
duplicateQuery,
publishQuery,
unpublishQuery,
renameQuery,
deleteQueryVisualization,
addQueryVisualization,
editQueryVisualization,
};
9 changes: 8 additions & 1 deletion client/app/pages/queries/utils/useVisualizationTabHandler.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useEffect, useMemo } from "react";
import { first, orderBy } from "lodash";
import { first, orderBy, find } from "lodash";
import { $location, $rootScope } from "@/services/ng";

function updateUrlHash(...args) {
Expand Down Expand Up @@ -28,5 +28,12 @@ export default function useVisualizationTabHandler(visualizations) {
return unwatch;
}, [firstVisualization.id, selectedTab]);

// make sure selectedTab is in visualizations
useEffect(() => {
if (!find(visualizations, { id: selectedTab })) {
setSelectedTab(firstVisualization.id);
}
}, [firstVisualization.id, selectedTab, visualizations]);

return [selectedTab, setSelectedTab];
}