diff --git a/apps/playground-web/src/app/insight/[blueprint_slug]/aggregate-parameter-input.client.tsx b/apps/playground-web/src/app/insight/[blueprint_slug]/aggregate-parameter-input.client.tsx index b60637b8819..536e585cef6 100644 --- a/apps/playground-web/src/app/insight/[blueprint_slug]/aggregate-parameter-input.client.tsx +++ b/apps/playground-web/src/app/insight/[blueprint_slug]/aggregate-parameter-input.client.tsx @@ -1,14 +1,9 @@ "use client"; +import { MultiSelect } from "@/components/blocks/multi-select"; import { Input } from "@/components/ui/input"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; import { cn } from "@/lib/utils"; +import { useCallback, useEffect, useMemo, useState } from "react"; import type { ControllerRenderProps } from "react-hook-form"; interface Preset { @@ -145,53 +140,137 @@ interface AggregateParameterInputProps { }, string >; - showTip: boolean; - hasError: boolean; - placeholder: string; - endpointPath: string; // New prop + showTip?: boolean; + hasError?: boolean; + placeholder?: string; + endpointPath: string; } export function AggregateParameterInput(props: AggregateParameterInputProps) { - const { field, showTip, hasError, placeholder, endpointPath } = props; + const { field, placeholder, endpointPath, showTip } = props; const { value, onChange } = field; - const presets = getAggregatePresets(endpointPath); + const presets = useMemo( + () => getAggregatePresets(endpointPath), + [endpointPath], + ); + + const selectedValues = useMemo(() => { + if (!value) return []; + return Array.from( + new Set( + String(value) + .split(",") + .map((v) => v.trim()) // remove leading / trailing spaces + .filter(Boolean), + ), + ); + }, [value]); + + const handlePresetChange = useCallback( + (values: string[]) => { + onChange({ target: { value: values.join(",") } }); + }, + [onChange], + ); + + // Custom search function for the MultiSelect + const searchFunction = useCallback( + (option: { value: string; label: string }, searchTerm: string) => { + if (!searchTerm) return true; + const query = searchTerm.toLowerCase(); + return ( + option.label.toLowerCase().includes(query) || + option.value.toLowerCase().includes(query) + ); + }, + [], + ); + + // Get display values for the selected items + useCallback( + (value: string) => { + const preset = presets.find((p) => p.value === value); + return preset ? preset.label : value; + }, + [presets], + ); + + // Format selected values for display in the MultiSelect + useMemo(() => { + return selectedValues.map((value) => { + const preset = presets.find((p) => p.value === value); + return { + label: preset?.label || value, + value, + }; + }); + }, [selectedValues, presets]); + + // State for the manual input text + const [manualInput, setManualInput] = useState(""); + + // Update manual input when selected values change + useEffect(() => { + if (selectedValues.length === 0) { + setManualInput(""); + } else { + setManualInput(selectedValues.join(", ")); + } + }, [selectedValues]); + + // Handle manual input changes + const handleManualInputChange = (e: React.ChangeEvent) => { + const value = e.target.value; + setManualInput(value); + + // Update selected values by splitting on commas and trimming whitespace + const newValues = value + .split(",") + .map((v) => v.trim()) + .filter(Boolean); + + onChange({ target: { value: newValues.join(",") } }); + }; return ( -
- + {/* Editable formula text field */} +
+ +
+ + {/* MultiSelect for choosing aggregations */} + ( +
+ {option.label} + + {option.value} + +
)} - placeholder={placeholder} /> -
); } diff --git a/apps/playground-web/src/app/insight/[blueprint_slug]/blueprint-playground.client.tsx b/apps/playground-web/src/app/insight/[blueprint_slug]/blueprint-playground.client.tsx index 46c60c6759e..ec239e54d86 100644 --- a/apps/playground-web/src/app/insight/[blueprint_slug]/blueprint-playground.client.tsx +++ b/apps/playground-web/src/app/insight/[blueprint_slug]/blueprint-playground.client.tsx @@ -527,7 +527,9 @@ function ParameterSection(props: {
{description && (

- {description} + {param.name === "aggregate" + ? "Aggregation(s). You can type in multiple, separated by a comma, or select from the presets" + : description}

)} @@ -536,7 +538,9 @@ function ParameterSection(props: {

Example:{" "} - {exampleToShow} + {param.name === "aggregate" + ? "count() AS count_all, countDistinct(address) AS unique_addresses" + : exampleToShow}

@@ -547,7 +551,10 @@ function ParameterSection(props: {