diff --git a/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/SummarizeContent.tsx b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/SummarizeContent.tsx new file mode 100644 index 0000000000000000000000000000000000000000..49abef055b007f8d411ace5a79350865f230dbe9 --- /dev/null +++ b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/SummarizeContent.tsx @@ -0,0 +1,84 @@ +import { t } from "ttag"; + +import * as Lib from "metabase-lib"; + +import { AddAggregationButton } from "../AddAggregationButton"; +import { AggregationItem } from "../AggregationItem"; +import { BreakoutColumnList } from "../BreakoutColumnList"; +import { + AggregationsContainer, + ColumnListContainer, + SectionTitle, +} from "../SummarizeSidebar.styled"; + +import { STAGE_INDEX } from "./use-summarize-query"; + +export type SummarizeContentProps = { + query: Lib.Query; + aggregations: Lib.AggregationClause[]; + hasAggregations: boolean; + onAddAggregations: (aggregations: Lib.Aggregable[]) => void; + onUpdateAggregation: ( + aggregation: Lib.AggregationClause, + nextAggregation: Lib.Aggregable, + ) => void; + onRemoveAggregation: (aggregation: Lib.AggregationClause) => void; + onAddBreakout: (column: Lib.ColumnMetadata) => void; + onUpdateBreakout: ( + clause: Lib.BreakoutClause, + column: Lib.ColumnMetadata, + ) => void; + onRemoveBreakout: (column: Lib.ColumnMetadata) => void; + onReplaceBreakouts: (column: Lib.ColumnMetadata) => void; +}; + +export const SummarizeContent = ({ + query, + aggregations, + hasAggregations, + onAddAggregations, + onUpdateAggregation, + onRemoveAggregation, + onAddBreakout, + onUpdateBreakout, + onRemoveBreakout, + onReplaceBreakouts, +}: SummarizeContentProps) => { + return ( + <> + <AggregationsContainer> + {aggregations.map((aggregation, aggregationIndex) => ( + <AggregationItem + key={ + Lib.displayInfo(query, STAGE_INDEX, aggregation).longDisplayName + } + query={query} + aggregation={aggregation} + aggregationIndex={aggregationIndex} + onAdd={onAddAggregations} + onUpdate={nextAggregation => + onUpdateAggregation(aggregation, nextAggregation) + } + onRemove={() => onRemoveAggregation(aggregation)} + /> + ))} + <AddAggregationButton + query={query} + onAddAggregations={onAddAggregations} + /> + </AggregationsContainer> + {hasAggregations && ( + <ColumnListContainer> + <SectionTitle>{t`Group by`}</SectionTitle> + <BreakoutColumnList + query={query} + onAddBreakout={onAddBreakout} + onUpdateBreakout={onUpdateBreakout} + onRemoveBreakout={onRemoveBreakout} + onReplaceBreakout={onReplaceBreakouts} + /> + </ColumnListContainer> + )} + </> + ); +}; 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 new file mode 100644 index 0000000000000000000000000000000000000000..a64cebbdd5597c14e80600d38c39cb352ef49ce5 --- /dev/null +++ b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/index.ts @@ -0,0 +1,2 @@ +export * from "./use-summarize-query"; +export * from "./SummarizeContent"; 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 new file mode 100644 index 0000000000000000000000000000000000000000..09297b7af410c61700d1b9b31d072ce5edc3d2e8 --- /dev/null +++ b/frontend/src/metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/use-summarize-query.ts @@ -0,0 +1,120 @@ +import { useCallback, useMemo, useState } from "react"; + +import * as Lib from "metabase-lib"; + +export const STAGE_INDEX = -1; +export const useSummarizeQuery = ( + initialQuery: Lib.Query, + onQueryChange: (query: Lib.Query) => void, +) => { + const [isDefaultAggregationRemoved, setDefaultAggregationRemoved] = + useState(false); + + const query = useMemo( + () => getQuery(initialQuery, isDefaultAggregationRemoved), + [initialQuery, isDefaultAggregationRemoved], + ); + + const aggregations = Lib.aggregations(query, STAGE_INDEX); + const hasAggregations = aggregations.length > 0; + + const handleAddAggregations = useCallback( + (aggregations: Lib.Aggregable[]) => { + const nextQuery = aggregations.reduce( + (query, aggregation) => Lib.aggregate(query, STAGE_INDEX, aggregation), + query, + ); + onQueryChange(nextQuery); + }, + [query, onQueryChange], + ); + + const handleUpdateAggregation = useCallback( + (aggregation: Lib.AggregationClause, nextAggregation: Lib.Aggregable) => { + const nextQuery = Lib.replaceClause( + query, + STAGE_INDEX, + aggregation, + nextAggregation, + ); + onQueryChange(nextQuery); + }, + [query, onQueryChange], + ); + + const handleRemoveAggregation = useCallback( + (aggregation: Lib.AggregationClause) => { + const nextQuery = Lib.removeClause(query, STAGE_INDEX, aggregation); + const nextAggregations = Lib.aggregations(nextQuery, STAGE_INDEX); + if (nextAggregations.length === 0) { + setDefaultAggregationRemoved(true); + } + onQueryChange(nextQuery); + }, + [query, onQueryChange], + ); + + const handleAddBreakout = useCallback( + (column: Lib.ColumnMetadata) => { + const nextQuery = Lib.breakout(query, STAGE_INDEX, column); + onQueryChange(nextQuery); + }, + [query, onQueryChange], + ); + + const handleUpdateBreakout = useCallback( + (clause: Lib.BreakoutClause, column: Lib.ColumnMetadata) => { + const nextQuery = Lib.replaceClause(query, STAGE_INDEX, clause, column); + onQueryChange(nextQuery); + }, + [query, onQueryChange], + ); + + const handleRemoveBreakout = useCallback( + (column: Lib.ColumnMetadata) => { + const { breakoutPosition } = Lib.displayInfo(query, STAGE_INDEX, column); + if (typeof breakoutPosition === "number") { + const breakouts = Lib.breakouts(query, STAGE_INDEX); + const clause = breakouts[breakoutPosition]; + const nextQuery = Lib.removeClause(query, STAGE_INDEX, clause); + onQueryChange(nextQuery); + } + }, + [query, onQueryChange], + ); + + const handleReplaceBreakouts = useCallback( + (column: Lib.ColumnMetadata) => { + const nextQuery = Lib.replaceBreakouts(query, STAGE_INDEX, column); + onQueryChange(nextQuery); + }, + [query, onQueryChange], + ); + return { + query, + aggregations, + hasAggregations, + handleAddAggregations, + handleUpdateAggregation, + handleRemoveAggregation, + handleAddBreakout, + handleUpdateBreakout, + handleRemoveBreakout, + handleReplaceBreakouts, + }; +}; + +function getQuery(query: Lib.Query, isDefaultAggregationRemoved: boolean) { + const hasAggregations = Lib.aggregations(query, STAGE_INDEX).length > 0; + + const shouldAddDefaultAggregation = + !hasAggregations && + !Lib.isMetricBased(query, STAGE_INDEX) && + !isDefaultAggregationRemoved; + + if (!shouldAddDefaultAggregation) { + return query; + } + + return Lib.aggregateByCount(query); +} 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 400ac4dba79abf9c0701b56d3277455ea9bbff58..e4ce79c7a18a763d90301d79fcbe1472e6f54341 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 @@ -1,20 +1,12 @@ -import { useCallback, useMemo, useState } from "react"; +import { useCallback } from "react"; import { t } from "ttag"; import { color } from "metabase/lib/colors"; -import * as Lib from "metabase-lib"; +import { SummarizeContent } from "metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/SummarizeContent"; +import { useSummarizeQuery } from "metabase/query_builder/components/view/sidebars/SummarizeSidebar/SummarizeContent/use-summarize-query"; +import type * as Lib from "metabase-lib"; -import { AddAggregationButton } from "./AddAggregationButton"; -import { AggregationItem } from "./AggregationItem"; -import { BreakoutColumnList } from "./BreakoutColumnList"; -import { - AggregationsContainer, - ColumnListContainer, - SectionTitle, - SidebarView, -} from "./SummarizeSidebar.styled"; - -const STAGE_INDEX = -1; +import { SidebarView } from "./SummarizeSidebar.styled"; interface SummarizeSidebarProps { className?: string; @@ -29,89 +21,18 @@ export function SummarizeSidebar({ onQueryChange, onClose, }: SummarizeSidebarProps) { - const [isDefaultAggregationRemoved, setDefaultAggregationRemoved] = - useState(false); - - const query = useMemo( - () => getQuery(initialQuery, isDefaultAggregationRemoved), - [initialQuery, isDefaultAggregationRemoved], - ); - - const aggregations = Lib.aggregations(query, STAGE_INDEX); - const hasAggregations = aggregations.length > 0; - - const handleAddAggregations = useCallback( - (aggregations: Lib.Aggregable[]) => { - const nextQuery = aggregations.reduce( - (query, aggregation) => Lib.aggregate(query, STAGE_INDEX, aggregation), - query, - ); - onQueryChange(nextQuery); - }, - [query, onQueryChange], - ); - - const handleUpdateAggregation = useCallback( - (aggregation: Lib.AggregationClause, nextAggregation: Lib.Aggregable) => { - const nextQuery = Lib.replaceClause( - query, - STAGE_INDEX, - aggregation, - nextAggregation, - ); - onQueryChange(nextQuery); - }, - [query, onQueryChange], - ); - - const handleRemoveAggregation = useCallback( - (aggregation: Lib.AggregationClause) => { - const nextQuery = Lib.removeClause(query, STAGE_INDEX, aggregation); - const nextAggregations = Lib.aggregations(nextQuery, STAGE_INDEX); - if (nextAggregations.length === 0) { - setDefaultAggregationRemoved(true); - } - onQueryChange(nextQuery); - }, - [query, onQueryChange], - ); - - const handleAddBreakout = useCallback( - (column: Lib.ColumnMetadata) => { - const nextQuery = Lib.breakout(query, STAGE_INDEX, column); - onQueryChange(nextQuery); - }, - [query, onQueryChange], - ); - - const handleUpdateBreakout = useCallback( - (clause: Lib.BreakoutClause, column: Lib.ColumnMetadata) => { - const nextQuery = Lib.replaceClause(query, STAGE_INDEX, clause, column); - onQueryChange(nextQuery); - }, - [query, onQueryChange], - ); - - const handleRemoveBreakout = useCallback( - (column: Lib.ColumnMetadata) => { - const { breakoutPosition } = Lib.displayInfo(query, STAGE_INDEX, column); - if (typeof breakoutPosition === "number") { - const breakouts = Lib.breakouts(query, STAGE_INDEX); - const clause = breakouts[breakoutPosition]; - const nextQuery = Lib.removeClause(query, STAGE_INDEX, clause); - onQueryChange(nextQuery); - } - }, - [query, onQueryChange], - ); - - const handleReplaceBreakouts = useCallback( - (column: Lib.ColumnMetadata) => { - const nextQuery = Lib.replaceBreakouts(query, STAGE_INDEX, column); - onQueryChange(nextQuery); - }, - [query, onQueryChange], - ); + const { + query, + aggregations, + hasAggregations, + handleAddAggregations, + handleUpdateAggregation, + handleRemoveAggregation, + handleAddBreakout, + handleUpdateBreakout, + handleRemoveBreakout, + handleReplaceBreakouts, + } = useSummarizeQuery(initialQuery, onQueryChange); const handleDoneClick = useCallback(() => { onQueryChange(query); @@ -125,54 +46,18 @@ export function SummarizeSidebar({ color={color("summarize")} onDone={handleDoneClick} > - <AggregationsContainer> - {aggregations.map((aggregation, aggregationIndex) => ( - <AggregationItem - key={ - Lib.displayInfo(query, STAGE_INDEX, aggregation).longDisplayName - } - query={query} - aggregation={aggregation} - aggregationIndex={aggregationIndex} - onAdd={handleAddAggregations} - onUpdate={nextAggregation => - handleUpdateAggregation(aggregation, nextAggregation) - } - onRemove={() => handleRemoveAggregation(aggregation)} - /> - ))} - <AddAggregationButton - query={query} - onAddAggregations={handleAddAggregations} - /> - </AggregationsContainer> - {hasAggregations && ( - <ColumnListContainer> - <SectionTitle>{t`Group by`}</SectionTitle> - <BreakoutColumnList - query={query} - onAddBreakout={handleAddBreakout} - onUpdateBreakout={handleUpdateBreakout} - onRemoveBreakout={handleRemoveBreakout} - onReplaceBreakout={handleReplaceBreakouts} - /> - </ColumnListContainer> - )} + <SummarizeContent + query={query} + aggregations={aggregations} + hasAggregations={hasAggregations} + onAddAggregations={handleAddAggregations} + onUpdateAggregation={handleUpdateAggregation} + onRemoveAggregation={handleRemoveAggregation} + onAddBreakout={handleAddBreakout} + onUpdateBreakout={handleUpdateBreakout} + onRemoveBreakout={handleRemoveBreakout} + onReplaceBreakouts={handleReplaceBreakouts} + /> </SidebarView> ); } - -function getQuery(query: Lib.Query, isDefaultAggregationRemoved: boolean) { - const hasAggregations = Lib.aggregations(query, STAGE_INDEX).length > 0; - - const shouldAddDefaultAggregation = - !hasAggregations && - !Lib.isMetricBased(query, STAGE_INDEX) && - !isDefaultAggregationRemoved; - - if (!shouldAddDefaultAggregation) { - return query; - } - - return Lib.aggregateByCount(query); -}