From 185389ba3616dd1b8bad8bbaff556b57eacde051 Mon Sep 17 00:00:00 2001 From: github-automation-metabase <166700802+github-automation-metabase@users.noreply.github.com> Date: Thu, 14 Nov 2024 08:16:37 -0500 Subject: [PATCH] fix(sdk): Split `useSummarizeQuery` into specialized hooks (#49841) (#50029) Co-authored-by: Oisin Coveney <oisin@metabase.com> --- .../components/Summarize.tsx | 38 +++---- .../StructuredQueryRightSidebar.jsx | 1 + .../AggregationItem/AggregationItem.tsx | 31 +++--- .../BreakoutColumnList/BreakoutColumnList.tsx | 28 +++--- .../SummarizeAggregationItemList.tsx | 73 ++++++++------ .../SummarizeBreakoutColumnList.tsx | 24 +---- .../SummarizeContent/index.ts | 1 - .../SummarizeContent/use-summarize-query.ts | 98 ------------------- .../SummarizeSidebar/SummarizeSidebar.tsx | 31 +++--- .../SummarizeSidebar.unit.spec.tsx | 1 + .../src/metabase/query_builder/hooks/types.ts | 7 ++ .../hooks/use-breakout-query-handlers.ts | 50 ++++++++++ .../hooks/use-default-query-aggregation.ts | 63 ++++++++++++ .../utils/get-aggregation-items.ts | 26 +++++ 14 files changed, 244 insertions(+), 228 deletions(-) delete mode 100644 frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/use-summarize-query.ts create mode 100644 frontend/src/metabase/query_builder/hooks/types.ts create mode 100644 frontend/src/metabase/query_builder/hooks/use-breakout-query-handlers.ts create mode 100644 frontend/src/metabase/query_builder/hooks/use-default-query-aggregation.ts create mode 100644 frontend/src/metabase/query_builder/utils/get-aggregation-items.ts diff --git a/enterprise/frontend/src/embedding-sdk/components/private/InteractiveQuestion/components/Summarize.tsx b/enterprise/frontend/src/embedding-sdk/components/private/InteractiveQuestion/components/Summarize.tsx index edb75bb0387..593b79ee494 100644 --- a/enterprise/frontend/src/embedding-sdk/components/private/InteractiveQuestion/components/Summarize.tsx +++ b/enterprise/frontend/src/embedding-sdk/components/private/InteractiveQuestion/components/Summarize.tsx @@ -1,17 +1,17 @@ import { useRef, useState } from "react"; import { t } from "ttag"; -import { useInteractiveQuestionContext } from "embedding-sdk/components/private/InteractiveQuestion/context"; import CS from "metabase/css/core/index.css"; import { SummarizeAggregationItemList, SummarizeBreakoutColumnList, - useSummarizeQuery, } from "metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent"; import { Button, Divider, Group, Stack } from "metabase/ui"; -import type * as Lib from "metabase-lib"; +import * as Lib from "metabase-lib"; import type Question from "metabase-lib/v1/Question"; +import { useInteractiveQuestionContext } from "../context"; + type SummarizeProps = { onClose: () => void; }; @@ -38,8 +38,11 @@ const SummarizeInner = ({ const [currentQuery, setCurrentQuery] = useState<Lib.Query>(question.query()); + // yeah we need to change this + const stageIndex = Lib.stageCount(currentQuery); + const onApplyFilter = () => { - if (query) { + if (currentQuery) { onQueryChange(currentQuery); onClose(); } @@ -52,39 +55,22 @@ const SummarizeInner = ({ onClose(); }; - const { - query, - stageIndex, - aggregations, - handleAddBreakout, - handleQueryChange, - handleRemoveBreakout, - handleReplaceBreakouts, - handleUpdateBreakout, - hasAggregations, - } = useSummarizeQuery({ - query: currentQuery, - onQueryChange: setCurrentQuery, - }); + const hasAggregations = Lib.aggregations(currentQuery, stageIndex).length > 0; return ( <Stack className={CS.overflowHidden} h="100%" w="100%"> <Stack className={CS.overflowYScroll}> <SummarizeAggregationItemList - query={query} + query={currentQuery} + onQueryChange={setCurrentQuery} stageIndex={stageIndex} - aggregations={aggregations} - onQueryChange={handleQueryChange} /> <Divider my="lg" /> {hasAggregations && ( <SummarizeBreakoutColumnList - query={query} + query={currentQuery} + onQueryChange={setCurrentQuery} stageIndex={stageIndex} - onAddBreakout={handleAddBreakout} - onUpdateBreakout={handleUpdateBreakout} - onRemoveBreakout={handleRemoveBreakout} - onReplaceBreakouts={handleReplaceBreakouts} /> )} </Stack> diff --git a/frontend/src/metabase/query_builder/components/view/View/StructuredQueryRightSidebar/StructuredQueryRightSidebar.jsx b/frontend/src/metabase/query_builder/components/view/View/StructuredQueryRightSidebar/StructuredQueryRightSidebar.jsx index 4059af6a1d7..e9fb2ad685a 100644 --- a/frontend/src/metabase/query_builder/components/view/View/StructuredQueryRightSidebar/StructuredQueryRightSidebar.jsx +++ b/frontend/src/metabase/query_builder/components/view/View/StructuredQueryRightSidebar/StructuredQueryRightSidebar.jsx @@ -49,6 +49,7 @@ export const StructuredQueryRightSidebar = ({ }); }} onClose={onCloseSummary} + stageIndex={-1} /> ), ) diff --git a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/AggregationItem/AggregationItem.tsx b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/AggregationItem/AggregationItem.tsx index 894976c0733..708140472c9 100644 --- a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/AggregationItem/AggregationItem.tsx +++ b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/AggregationItem/AggregationItem.tsx @@ -1,17 +1,20 @@ -import { useCallback, useState } from "react"; +import { useDisclosure } from "@mantine/hooks"; import { AggregationPicker } from "metabase/common/components/AggregationPicker"; import { Popover } from "metabase/ui"; -import * as Lib from "metabase-lib"; +import type * as Lib from "metabase-lib"; import { AggregationName, RemoveIcon, Root } from "./AggregationItem.styled"; interface AggregationItemProps { query: Lib.Query; + onQueryChange: (query: Lib.Query) => void; stageIndex: number; aggregation: Lib.AggregationClause; aggregationIndex: number; - onQueryChange: (query: Lib.Query) => void; + displayName: string; + onAggregationRemove: () => void; + operators: Lib.AggregationOperator[]; } export function AggregationItem({ @@ -20,30 +23,22 @@ export function AggregationItem({ aggregation, aggregationIndex, onQueryChange, + displayName, + onAggregationRemove, + operators, }: AggregationItemProps) { - const [isOpened, setIsOpened] = useState(false); - const { displayName } = Lib.displayInfo(query, stageIndex, aggregation); - - const operators = Lib.selectedAggregationOperators( - Lib.availableAggregationOperators(query, stageIndex), - aggregation, - ); - - const handleRemove = useCallback(() => { - const nextQuery = Lib.removeClause(query, stageIndex, aggregation); - onQueryChange(nextQuery); - }, [query, stageIndex, aggregation, onQueryChange]); + const [isOpened, { toggle }] = useDisclosure(false); return ( - <Popover opened={isOpened} onChange={setIsOpened}> + <Popover opened={isOpened} onChange={toggle}> <Popover.Target> <Root aria-label={displayName} data-testid="aggregation-item" - onClick={() => setIsOpened(!isOpened)} + onClick={toggle} > <AggregationName>{displayName}</AggregationName> - <RemoveIcon name="close" onClick={handleRemove} /> + <RemoveIcon name="close" onClick={onAggregationRemove} /> </Root> </Popover.Target> <Popover.Dropdown> diff --git a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/BreakoutColumnList/BreakoutColumnList.tsx b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/BreakoutColumnList/BreakoutColumnList.tsx index 94f88b3c01a..7ed97223e80 100644 --- a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/BreakoutColumnList/BreakoutColumnList.tsx +++ b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/BreakoutColumnList/BreakoutColumnList.tsx @@ -4,32 +4,28 @@ import { t } from "ttag"; import Input from "metabase/core/components/Input"; import { useDebouncedValue } from "metabase/hooks/use-debounced-value"; import { SEARCH_DEBOUNCE_DURATION } from "metabase/lib/constants"; +import type { UpdateQueryHookProps } from "metabase/query_builder/hooks/types"; +import { useBreakoutQueryHandlers } from "metabase/query_builder/hooks/use-breakout-query-handlers"; import { DelayGroup } from "metabase/ui"; import * as Lib from "metabase-lib"; import { ColumnGroupName, SearchContainer } from "./BreakoutColumnList.styled"; import { BreakoutColumnListItem } from "./BreakoutColumnListItem"; -export interface BreakoutColumnListProps { - query: Lib.Query; - stageIndex: number; - onAddBreakout: (column: Lib.ColumnMetadata) => void; - onUpdateBreakout: ( - breakout: Lib.BreakoutClause, - column: Lib.ColumnMetadata, - ) => void; - onRemoveBreakout: (breakout: Lib.BreakoutClause) => void; - onReplaceBreakouts: (column: Lib.ColumnMetadata) => void; -} +export type BreakoutColumnListProps = UpdateQueryHookProps; export function BreakoutColumnList({ query, - stageIndex, - onAddBreakout, - onUpdateBreakout, - onRemoveBreakout, - onReplaceBreakouts, + onQueryChange, + stageIndex = -1, }: BreakoutColumnListProps) { + const { + onAddBreakout, + onUpdateBreakout, + onRemoveBreakout, + onReplaceBreakouts, + } = useBreakoutQueryHandlers({ query, onQueryChange, stageIndex }); + const [searchQuery, setSearchQuery] = useState(""); const debouncedSearchQuery = useDebouncedValue( searchQuery, diff --git a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/SummarizeAggregationItemList.tsx b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/SummarizeAggregationItemList.tsx index a7c1436857d..dec000c33a6 100644 --- a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/SummarizeAggregationItemList.tsx +++ b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/SummarizeAggregationItemList.tsx @@ -1,43 +1,58 @@ +import { useMemo } from "react"; + +import type { UpdateQueryHookProps } from "metabase/query_builder/hooks/types"; +import { getAggregationItems } from "metabase/query_builder/utils/get-aggregation-items"; import { Group, type GroupProps } from "metabase/ui"; -import type * as Lib from "metabase-lib"; +import * as Lib from "metabase-lib"; import { AddAggregationButton } from "../AddAggregationButton"; import { AggregationItem } from "../AggregationItem"; -type SummarizeAggregationItemListProps = { - query: Lib.Query; - stageIndex: number; - aggregations: Lib.AggregationClause[]; - onQueryChange: (query: Lib.Query) => void; -} & GroupProps; +type SummarizeAggregationItemListProps = UpdateQueryHookProps & GroupProps; export const SummarizeAggregationItemList = ({ query, - stageIndex, - aggregations, onQueryChange, + stageIndex, ...containerProps -}: SummarizeAggregationItemListProps) => ( - <Group - data-testid="summarize-aggregation-item-list" - spacing="sm" - align="flex-start" - {...containerProps} - > - {aggregations.map((aggregation, aggregationIndex) => ( - <AggregationItem - key={aggregationIndex} +}: SummarizeAggregationItemListProps) => { + const aggregationItems = useMemo( + () => getAggregationItems({ query, stageIndex }), + [query, stageIndex], + ); + + const handleRemove = (aggregation: Lib.AggregationClause) => { + const nextQuery = Lib.removeClause(query, stageIndex, aggregation); + onQueryChange(nextQuery); + }; + + return ( + <Group + data-testid="summarize-aggregation-item-list" + spacing="sm" + align="flex-start" + {...containerProps} + > + {aggregationItems.map( + ({ aggregation, displayName, aggregationIndex, operators }) => ( + <AggregationItem + key={aggregationIndex} + query={query} + stageIndex={stageIndex} + aggregation={aggregation} + aggregationIndex={aggregationIndex} + onQueryChange={onQueryChange} + displayName={displayName} + onAggregationRemove={() => handleRemove(aggregation)} + operators={operators} + /> + ), + )} + <AddAggregationButton query={query} stageIndex={stageIndex} - aggregation={aggregation} - aggregationIndex={aggregationIndex} onQueryChange={onQueryChange} /> - ))} - <AddAggregationButton - query={query} - stageIndex={stageIndex} - onQueryChange={onQueryChange} - /> - </Group> -); + </Group> + ); +}; diff --git a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/SummarizeBreakoutColumnList.tsx b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/SummarizeBreakoutColumnList.tsx index 338a33a4de1..2296d0bb22e 100644 --- a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/SummarizeBreakoutColumnList.tsx +++ b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/SummarizeBreakoutColumnList.tsx @@ -1,29 +1,16 @@ import { t } from "ttag"; +import type { UpdateQueryHookProps } from "metabase/query_builder/hooks/types"; import { Space, Stack, type StackProps, Title } from "metabase/ui"; -import type * as Lib from "metabase-lib"; import { BreakoutColumnList } from "../BreakoutColumnList"; -type SummarizeBreakoutColumnListProps = { - query: Lib.Query; - stageIndex: number; - onAddBreakout: (column: Lib.ColumnMetadata) => void; - onUpdateBreakout: ( - clause: Lib.BreakoutClause, - column: Lib.ColumnMetadata, - ) => void; - onRemoveBreakout: (clause: Lib.BreakoutClause) => void; - onReplaceBreakouts: (column: Lib.ColumnMetadata) => void; -} & StackProps; +type SummarizeBreakoutColumnListProps = UpdateQueryHookProps & StackProps; export const SummarizeBreakoutColumnList = ({ query, + onQueryChange, stageIndex, - onAddBreakout, - onUpdateBreakout, - onRemoveBreakout, - onReplaceBreakouts, ...containerProps }: SummarizeBreakoutColumnListProps) => ( <Stack @@ -36,11 +23,8 @@ export const SummarizeBreakoutColumnList = ({ <Space my="sm" /> <BreakoutColumnList query={query} + onQueryChange={onQueryChange} stageIndex={stageIndex} - onAddBreakout={onAddBreakout} - onUpdateBreakout={onUpdateBreakout} - onRemoveBreakout={onRemoveBreakout} - onReplaceBreakouts={onReplaceBreakouts} /> </Stack> ); diff --git a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/index.ts b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/index.ts index 355c47b8717..f1368d5cf39 100644 --- a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/index.ts +++ b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/index.ts @@ -1,3 +1,2 @@ -export * from "./use-summarize-query"; export * from "./SummarizeAggregationItemList"; export * from "./SummarizeBreakoutColumnList"; diff --git a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/use-summarize-query.ts b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/use-summarize-query.ts deleted file mode 100644 index 5555eba06b5..00000000000 --- a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/use-summarize-query.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { useCallback, useMemo, useState } from "react"; - -import * as Lib from "metabase-lib"; - -const STAGE_INDEX = -1; - -interface UseSummarizeQueryProps { - query: Lib.Query; - onQueryChange: (nextQuery: Lib.Query) => void; -} - -export const useSummarizeQuery = ({ - query: initialQuery, - onQueryChange, -}: UseSummarizeQueryProps) => { - const [hasDefaultAggregation, setHasDefaultAggregation] = useState(() => - shouldAddDefaultAggregation(initialQuery), - ); - - const query = useMemo( - () => - hasDefaultAggregation - ? Lib.aggregateByCount(initialQuery, STAGE_INDEX) - : initialQuery, - [initialQuery, hasDefaultAggregation], - ); - - const aggregations = Lib.aggregations(query, STAGE_INDEX); - const hasAggregations = aggregations.length > 0; - - const handleChange = useCallback( - (nextQuery: Lib.Query) => { - setHasDefaultAggregation(false); - onQueryChange(nextQuery); - }, - [onQueryChange], - ); - - const handleQueryChange = useCallback( - (nextQuery: Lib.Query) => { - const newAggregations = Lib.aggregations(nextQuery, STAGE_INDEX); - if (hasDefaultAggregation && newAggregations.length === 0) { - setHasDefaultAggregation(false); - } else { - handleChange(nextQuery); - } - }, - [handleChange, hasDefaultAggregation], - ); - - const handleAddBreakout = useCallback( - (column: Lib.ColumnMetadata) => { - const nextQuery = Lib.breakout(query, STAGE_INDEX, column); - handleChange(nextQuery); - }, - [query, handleChange], - ); - - const handleUpdateBreakout = useCallback( - (clause: Lib.BreakoutClause, column: Lib.ColumnMetadata) => { - const nextQuery = Lib.replaceClause(query, STAGE_INDEX, clause, column); - handleChange(nextQuery); - }, - [query, handleChange], - ); - - const handleRemoveBreakout = useCallback( - (clause: Lib.BreakoutClause) => { - const nextQuery = Lib.removeClause(query, STAGE_INDEX, clause); - handleChange(nextQuery); - }, - [query, handleChange], - ); - - const handleReplaceBreakouts = useCallback( - (column: Lib.ColumnMetadata) => { - const nextQuery = Lib.replaceBreakouts(query, STAGE_INDEX, column); - handleChange(nextQuery); - }, - [query, handleChange], - ); - return { - query, - stageIndex: STAGE_INDEX, - aggregations, - hasAggregations, - handleQueryChange, - handleAddBreakout, - handleUpdateBreakout, - handleRemoveBreakout, - handleReplaceBreakouts, - }; -}; - -function shouldAddDefaultAggregation(query: Lib.Query): boolean { - const aggregations = Lib.aggregations(query, STAGE_INDEX); - return aggregations.length === 0; -} diff --git a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeSidebar.tsx b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeSidebar.tsx index ddfe547b5e4..c0d1aaf009c 100644 --- a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeSidebar.tsx +++ b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeSidebar.tsx @@ -2,42 +2,37 @@ import { useCallback } from "react"; import { t } from "ttag"; import { color } from "metabase/lib/colors"; +import type { UpdateQueryHookProps } from "metabase/query_builder/hooks/types"; +import { useDefaultQueryAggregation } from "metabase/query_builder/hooks/use-default-query-aggregation"; import { Divider } from "metabase/ui"; -import type * as Lib from "metabase-lib"; import { SummarizeAggregationItemList, SummarizeBreakoutColumnList, - useSummarizeQuery, } from "./SummarizeContent"; import { SidebarView } from "./SummarizeSidebar.styled"; -interface SummarizeSidebarProps { +type SummarizeSidebarProps = { className?: string; - query: Lib.Query; - onQueryChange: (query: Lib.Query) => void; onClose: () => void; -} +} & UpdateQueryHookProps; export function SummarizeSidebar({ className, query: initialQuery, onQueryChange, onClose, + stageIndex, }: SummarizeSidebarProps) { const { query, - stageIndex, - aggregations, + onUpdateQuery: onDefaultQueryChange, + onAggregationChange, hasAggregations, - handleQueryChange, - handleAddBreakout, - handleUpdateBreakout, - handleRemoveBreakout, - handleReplaceBreakouts, - } = useSummarizeQuery({ + } = useDefaultQueryAggregation({ query: initialQuery, onQueryChange, + stageIndex, }); const handleDoneClick = useCallback(() => { @@ -55,20 +50,16 @@ export function SummarizeSidebar({ <SummarizeAggregationItemList px="lg" query={query} + onQueryChange={onAggregationChange} stageIndex={stageIndex} - aggregations={aggregations} - onQueryChange={handleQueryChange} /> <Divider my="lg" /> {hasAggregations && ( <SummarizeBreakoutColumnList px="lg" query={query} + onQueryChange={onDefaultQueryChange} stageIndex={stageIndex} - onAddBreakout={handleAddBreakout} - onUpdateBreakout={handleUpdateBreakout} - onRemoveBreakout={handleRemoveBreakout} - onReplaceBreakouts={handleReplaceBreakouts} /> )} </SidebarView> diff --git a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeSidebar.unit.spec.tsx b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeSidebar.unit.spec.tsx index 45f1c2ee9cb..476e41e889d 100644 --- a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeSidebar.unit.spec.tsx +++ b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeSidebar.unit.spec.tsx @@ -72,6 +72,7 @@ async function setup({ onQueryChange(nextQuery); }} onClose={onClose} + stageIndex={-1} /> ); } diff --git a/frontend/src/metabase/query_builder/hooks/types.ts b/frontend/src/metabase/query_builder/hooks/types.ts new file mode 100644 index 00000000000..682460d5b90 --- /dev/null +++ b/frontend/src/metabase/query_builder/hooks/types.ts @@ -0,0 +1,7 @@ +import type * as Lib from "metabase-lib"; + +export type UpdateQueryHookProps = { + query: Lib.Query; + onQueryChange: (nextQuery: Lib.Query) => void; + stageIndex: number; +}; diff --git a/frontend/src/metabase/query_builder/hooks/use-breakout-query-handlers.ts b/frontend/src/metabase/query_builder/hooks/use-breakout-query-handlers.ts new file mode 100644 index 00000000000..a515970cbf8 --- /dev/null +++ b/frontend/src/metabase/query_builder/hooks/use-breakout-query-handlers.ts @@ -0,0 +1,50 @@ +import { useCallback } from "react"; + +import * as Lib from "metabase-lib"; + +import type { UpdateQueryHookProps } from "./types"; + +export const useBreakoutQueryHandlers = ({ + query, + onQueryChange, + stageIndex, +}: UpdateQueryHookProps) => { + const onAddBreakout = useCallback( + (column: Lib.ColumnMetadata) => { + const nextQuery = Lib.breakout(query, stageIndex, column); + onQueryChange(nextQuery); + }, + [query, stageIndex, onQueryChange], + ); + + const onUpdateBreakout = useCallback( + (clause: Lib.BreakoutClause, column: Lib.ColumnMetadata) => { + const nextQuery = Lib.replaceClause(query, stageIndex, clause, column); + onQueryChange(nextQuery); + }, + [query, stageIndex, onQueryChange], + ); + + const onRemoveBreakout = useCallback( + (clause: Lib.BreakoutClause) => { + const nextQuery = Lib.removeClause(query, stageIndex, clause); + onQueryChange(nextQuery); + }, + [query, stageIndex, onQueryChange], + ); + + const onReplaceBreakouts = useCallback( + (column: Lib.ColumnMetadata) => { + const nextQuery = Lib.replaceBreakouts(query, stageIndex, column); + onQueryChange(nextQuery); + }, + [query, stageIndex, onQueryChange], + ); + + return { + onAddBreakout, + onUpdateBreakout, + onRemoveBreakout, + onReplaceBreakouts, + }; +}; diff --git a/frontend/src/metabase/query_builder/hooks/use-default-query-aggregation.ts b/frontend/src/metabase/query_builder/hooks/use-default-query-aggregation.ts new file mode 100644 index 00000000000..e459275c305 --- /dev/null +++ b/frontend/src/metabase/query_builder/hooks/use-default-query-aggregation.ts @@ -0,0 +1,63 @@ +import { useCallback, useMemo, useState } from "react"; + +import * as Lib from "metabase-lib"; + +import type { UpdateQueryHookProps } from "./types"; + +export const useDefaultQueryAggregation = ({ + query: initialQuery, + onQueryChange, + stageIndex, +}: UpdateQueryHookProps) => { + const [hasDefaultAggregation, setHasDefaultAggregation] = useState(() => + shouldAddDefaultAggregation(initialQuery, stageIndex), + ); + + const query = useMemo( + () => + hasDefaultAggregation + ? Lib.aggregateByCount(initialQuery, stageIndex) + : initialQuery, + [hasDefaultAggregation, initialQuery, stageIndex], + ); + + const hasAggregations = useMemo( + () => Lib.aggregations(query, stageIndex).length > 0, + [query, stageIndex], + ); + + const onUpdateQuery = useCallback( + (nextQuery: Lib.Query) => { + setHasDefaultAggregation(false); + onQueryChange(nextQuery); + }, + [onQueryChange], + ); + + const onAggregationChange = useCallback( + (nextQuery: Lib.Query) => { + const newAggregations = Lib.aggregations(nextQuery, stageIndex); + setHasDefaultAggregation(false); + + if (!hasDefaultAggregation || newAggregations.length !== 0) { + onQueryChange(nextQuery); + } + }, + [hasDefaultAggregation, onQueryChange, stageIndex], + ); + + return { + query, + hasAggregations, + onUpdateQuery, + onAggregationChange, + }; +}; + +function shouldAddDefaultAggregation( + query: Lib.Query, + stageIndex = -1, +): boolean { + const aggregations = Lib.aggregations(query, stageIndex); + return aggregations.length === 0; +} diff --git a/frontend/src/metabase/query_builder/utils/get-aggregation-items.ts b/frontend/src/metabase/query_builder/utils/get-aggregation-items.ts new file mode 100644 index 00000000000..96d59ac6e21 --- /dev/null +++ b/frontend/src/metabase/query_builder/utils/get-aggregation-items.ts @@ -0,0 +1,26 @@ +import * as Lib from "metabase-lib"; + +import type { UpdateQueryHookProps } from "../hooks/types"; + +export const getAggregationItems = ({ + query, + stageIndex, +}: Pick<UpdateQueryHookProps, "query" | "stageIndex">) => { + const aggregations = Lib.aggregations(query, stageIndex); + + return aggregations.map((aggregation, aggregationIndex) => { + const { displayName } = Lib.displayInfo(query, stageIndex, aggregation); + + const operators = Lib.selectedAggregationOperators( + Lib.availableAggregationOperators(query, stageIndex), + aggregation, + ); + + return { + aggregation, + aggregationIndex, + operators, + displayName, + }; + }); +}; -- GitLab