Skip to content
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
12 changes: 6 additions & 6 deletions src/components/ExploreDataSection/index.module.less
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,15 @@
align-items: center;
justify-content: center;
white-space: nowrap;
color: #fff;
background: #4a7faa;
border-color: #4a7faa;
color: rgba(0, 0, 0, 0.85);
background: #fff;
border-color: #d9d9d9;

&:hover,
&:focus {
color: #fff !important;
background: #3d6d93 !important;
border-color: #3d6d93 !important;
color: rgba(0, 0, 0, 0.85) !important;
background: #fff !important;
border-color: #4a7faa !important;
}
}

Expand Down
22 changes: 12 additions & 10 deletions src/components/ExploreDataSection/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useEffect, useRef, useState } from "react";

import useAnalyticsQueryMembers from "@/hooks/useAnalyticsQueryMembers";
import useFormatExport from "@/hooks/useFormatExport";
import { hasPlaygroundSelection } from "@/hooks/useAnalyticsQuery";
import Button from "@/components/Button";
import VirtualTable, { cellRenderer } from "@/components/VirtualTable";
import PrismCode from "@/components/PrismCode";
Expand Down Expand Up @@ -312,7 +313,11 @@ const ExploreDataSection: FC<ExploreDataSectionProps> = (props) => {
type="default"
className={s.sqlCopy}
icon={<CopyIcon />}
onClick={() => navigator.clipboard.writeText(rawSql.sql || "")}
onClick={() => {
navigator.clipboard.writeText(rawSql.sql || "").then(() => {
message.success("SQL copied to clipboard");
});
}}
>
Copy
</Button>
Expand All @@ -326,6 +331,8 @@ const ExploreDataSection: FC<ExploreDataSectionProps> = (props) => {
);
}, [empty, queryState, t]);

const hasSelection = hasPlaygroundSelection(playgroundState);

const RestApi = useMemo(() => {
if (dataSource?.id && currentBranch?.id) {
return (
Expand All @@ -350,11 +357,6 @@ const ExploreDataSection: FC<ExploreDataSectionProps> = (props) => {
updateState,
]);

const handleResetQuery = () => {
onResetQuery?.();
message.success("Selection cleared");
};

const onChange = (values: Partial<DataSchemaFormValues>) => {
if (values.limit !== limit) {
onQueryChange("limit", values.limit);
Expand Down Expand Up @@ -386,11 +388,11 @@ const ExploreDataSection: FC<ExploreDataSectionProps> = (props) => {
{onResetQuery && (
<Button
className={s.reset}
type="primary"
onClick={handleResetQuery}
disabled={!queryState?.columns?.length || loading}
type="default"
onClick={onResetQuery}
disabled={!hasSelection || loading}
>
Reset
Clear selection
</Button>
)}
{queryTimeMs != null && !loading && (
Expand Down
10 changes: 9 additions & 1 deletion src/components/ExploreSettingsForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next";
import { useForm } from "react-hook-form";
import cn from "classnames";
import { useResponsive } from "ahooks";
import { useEffect } from "react";

import Input from "@/components/Input";

Expand Down Expand Up @@ -31,10 +32,17 @@ const ExploreSettingsForm: FC<ExploreSettingsFormProps> = ({
const windowSize = useResponsive();
const isMobile = windowSize.lg === false;

const { control, watch } = useForm<DataSchemaFormValues>({
const { control, watch, reset } = useForm<DataSchemaFormValues>({
defaultValues,
});

useEffect(() => {
reset({
limit: defaultValues.limit,
offset: defaultValues.offset,
});
}, [defaultValues.limit, defaultValues.offset, reset]);

useEffect(() => {
const { unsubscribe } = watch((value) => {
onChange(value);
Expand Down
14 changes: 9 additions & 5 deletions src/components/ExploreWorkspace/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { Spin } from "antd";
import { useTranslation } from "react-i18next";
import { Spin, message } from "antd";
import { useCallback, useMemo } from "react";

import SidebarLayout from "@/layouts/SidebarLayout";
import ExploreDataSection from "@/components/ExploreDataSection";
import ErrorFound from "@/components/ErrorFound";
import ExploreCubes from "@/components/ExploreCubes";
import usePlayground, { queryStateKeys } from "@/hooks/usePlayground";
import { initialState } from "@/hooks/useAnalyticsQuery";
import useExploreWorkspace from "@/hooks/useExploreWorkspace";
import useDimensions from "@/hooks/useDimensions";
import useLocation from "@/hooks/useLocation";
Expand Down Expand Up @@ -61,7 +60,6 @@ const ExploreWorkspace: FC<ExploreWorkspaceProps> = (props) => {
icon,
} = props;

const { t } = useTranslation(["common"]);
const [location, setLocation] = useLocation();
const { screenshotMode } = location?.query || {};
const isScreenshotMode = screenshotMode !== undefined;
Expand Down Expand Up @@ -89,6 +87,7 @@ const ExploreWorkspace: FC<ExploreWorkspaceProps> = (props) => {
settings,
dispatchSettings,
selectors,
clearSelection,
} = usePlayground({
explorationData,
meta: meta.data,
Expand All @@ -100,6 +99,11 @@ const ExploreWorkspace: FC<ExploreWorkspaceProps> = (props) => {
selectedQueryMembers,
});

const handleClearSelection = useCallback(() => {
clearSelection();
message.success("Selection cleared");
}, [clearSelection]);

const tableHeight = useMemo(
() =>
DEFAULT_ROW_HEIGHT * explorationState.rows.length + DEFAULT_HEADER_HEIGHT,
Expand Down Expand Up @@ -190,7 +194,7 @@ const ExploreWorkspace: FC<ExploreWorkspaceProps> = (props) => {
onExec={onRunQuery}
onQueryChange={onQueryChange}
onApplyQuery={doReset}
onResetQuery={() => doReset(initialState)}
onResetQuery={handleClearSelection}
onOpenModal={onOpenModal}
disabled={!isQueryChanged}
state={state}
Expand Down
7 changes: 7 additions & 0 deletions src/hooks/useAnalyticsQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,13 @@ export const initialState: PlaygroundState = {
...queryState,
};

export const hasPlaygroundSelection = (state: PlaygroundState): boolean =>
state.measures.length > 0 ||
state.dimensions.length > 0 ||
state.filters.length > 0 ||
state.timeDimensions.length > 0 ||
state.segments.length > 0;

const getName = (member: { name?: string }): any => member.name;

const getOperatorType = (member: CubeMember) =>
Expand Down
42 changes: 37 additions & 5 deletions src/hooks/usePlayground.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { useMemo, useState, useEffect, useReducer, useCallback } from "react";
import {
useMemo,
useState,
useEffect,
useReducer,
useCallback,
useRef,
} from "react";
import { useDeepCompareEffect } from "ahooks";

import useDataSourceMeta from "@/hooks/useDataSourcesMeta";
Expand Down Expand Up @@ -234,6 +241,7 @@ export default ({ meta = [], explorationData, rawSql }: Props) => {
);

const [isQueryChanged, setChangedStatus] = useState(false);
const syncedExplorationIdRef = useRef<string | undefined>();

useEffect(() => {
const playgroundState = exploration?.playground_state || queryState;
Expand All @@ -248,21 +256,44 @@ export default ({ meta = [], explorationData, rawSql }: Props) => {
}
}, [isQueryChanged, currPlaygroundState, exploration, selectorDirty]);

// Hydrate from saved exploration only when opening a (new) exploration — not on
// every refetch, so Apply to Explore / local edits are not overwritten by stale DB state.
useEffect(() => {
const id = exploration?.id;
const newState = exploration?.playground_state;

if (newState) {
doReset(newState as unknown as PlaygroundState);
setSelectorDirty(false);
if (!id || !newState || syncedExplorationIdRef.current === id) {
return;
}
}, [exploration?.playground_state, doReset]);

doReset(newState as unknown as PlaygroundState);
setSelectorDirty(false);
syncedExplorationIdRef.current = id;
}, [exploration?.id, exploration?.playground_state, doReset]);

useEffect(() => {
if (!exploration?.id) {
doReset(initialState);
syncedExplorationIdRef.current = undefined;
}
}, [exploration?.id, doReset]);

const clearSelection = useCallback(() => {
doReset({
measures: [],
dimensions: [],
filters: [],
timeDimensions: [],
segments: [],
order: [],
timezone: "UTC",
limit: 1000,
offset: 0,
});
setSelectorValues({});
setSelectorDirty(false);
}, [doReset]);

return {
state: explorationState,
selectedQueryMembers,
Expand All @@ -286,5 +317,6 @@ export default ({ meta = [], explorationData, rawSql }: Props) => {
setSelectorValue,
selectorFilters,
},
clearSelection,
};
};