/** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { observer } from 'mobx-react-lite'; import { type QueryLoaderState, QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT, SORT_BY_OPTIONS, V1_BasicValueSpecificationEditor, } from '@finos/legend-query-builder'; import type { LegendQueryDataCubeSourceBuilderState } from '../../../stores/builder/source/LegendQueryDataCubeSourceBuilderState.js'; import { generateGAVCoordinates } from '@finos/legend-storage'; import { cn, DataCubeIcon, useDropdownMenu } from '@finos/legend-art'; import { debounce, formatDistanceToNow, guaranteeIsString, guaranteeType, quantifyList, } from '@finos/legend-shared'; import { flowResult } from 'mobx'; import { useRef, useState, useMemo, useEffect } from 'react'; import { _defaultPrimitiveTypeValue, _elementPtr, _primitiveValue, _property, FormButton, FormCheckbox, FormCodeEditor, FormDropdownMenu, FormDropdownMenuItem, FormDropdownMenuTrigger, FormTextInput, isPrimitiveType, } from '@finos/legend-data-cube'; import { CODE_EDITOR_LANGUAGE } from '@finos/legend-code-editor'; import { useLegendDataCubeBuilderStore } from '../LegendDataCubeBuilderStoreProvider.js'; import { useApplicationStore } from '@finos/legend-application'; import { type V1_ValueSpecification, PRIMITIVE_TYPE, V1_observe_ValueSpecification, V1_PackageableType, } from '@finos/legend-graph'; const LegendQuerySearcher = observer((props: { state: QueryLoaderState }) => { const { state } = props; const store = useLegendDataCubeBuilderStore(); const searchInputRef = useRef(null); const searchResults = state.queries; useEffect(() => { searchInputRef.current?.focus(); }, [state]); // search text const debouncedLoader = useMemo( () => debounce((input: string) => { flowResult(state.searchQueries(input)).catch((error) => store.alertService.alertUnhandledError(error), ); }, 500), [store, state], ); const onSearchTextChange: React.ChangeEventHandler = ( event, ) => { if (event.target.value !== state.searchText) { state.setSearchText(event.target.value); debouncedLoader.cancel(); debouncedLoader(event.target.value); } }; const clearSearches = () => { state.setSearchText(''); debouncedLoader.cancel(); debouncedLoader(''); }; // filter and sort const [isMineOnly, setIsMineOnly] = useState(false); const toggleShowCurrentUserQueriesOnly = () => { state.setShowCurrentUserQueriesOnly(!state.showCurrentUserQueriesOnly); setIsMineOnly(!isMineOnly); debouncedLoader.cancel(); debouncedLoader(state.searchText); }; const [ openSortDropdown, closeSortDropdown, sortDropdownProps, sortDropdownPropsOpen, ] = useDropdownMenu(); const applySort = (value: SORT_BY_OPTIONS) => { state.setSortBy(value); debouncedLoader.cancel(); debouncedLoader(state.searchText); }; useEffect(() => { flowResult(state.searchQueries('')).catch((error) => store.alertService.alertUnhandledError(error), ); }, [store, state]); return (
{Boolean(state.searchText) && ( <> )}
Filters:
{/* TODO?: support extra filters */}
Sort by: {state.sortBy} {Object.values(SORT_BY_OPTIONS).map((option) => ( { applySort(option); closeSortDropdown(); }} autoFocus={option === state.sortBy} > {option} ))}
{state.searchQueriesState.hasCompleted && ( <>
{state.showingDefaultQueries ? ( (state.generateDefaultQueriesSummaryText?.(searchResults) ?? `Refine your search to get better matches`) ) : searchResults.length >= QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT ? ( <> {`Found ${QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT}+ matches`}{' '} ) : ( `Found ${quantifyList(searchResults, 'match', 'matches')}` )}
{searchResults .slice(0, QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT) .map((query, idx) => (
state.loadQuery(query)} >
{query.name}
{query.lastUpdatedAt ? formatDistanceToNow( new Date(query.lastUpdatedAt), { includeSeconds: true, addSuffix: true, }, ) : '(unknown)'}
{query.owner}
))} )} {!state.searchQueriesState.hasCompleted && (
Searching...
)}
); }); export const LegendQueryDataCubeSourceBuilder = observer( (props: { sourceBuilder: LegendQueryDataCubeSourceBuilderState }) => { const { sourceBuilder } = props; const application = useApplicationStore(); const store = useLegendDataCubeBuilderStore(); const query = sourceBuilder.query; if (!query) { return ; } return (
{query.name}
{`[ ${generateGAVCoordinates( query.groupId, query.artifactId, query.versionId, )} ]`}
{query.lastUpdatedAt ? formatDistanceToNow(new Date(query.lastUpdatedAt), { includeSeconds: true, addSuffix: true, }) : '(unknown)'}
{query.owner}
{sourceBuilder.queryCode !== undefined && (
)} {sourceBuilder.queryParameters && sourceBuilder.queryParameters.length > 0 && (
{sourceBuilder.queryParameterValues && Object.entries(sourceBuilder.queryParameterValues).map( ([name, { variable, valueSpec }]) => { const packageableType = guaranteeType( variable.genericType?.rawType, V1_PackageableType, 'Can only edit parameters with packageable type', ); const enumeration = sourceBuilder.queryEnumerations?.[name]; const resetValue = (): void => { if (isPrimitiveType(packageableType.fullPath)) { sourceBuilder.setQueryParameterValue( name, V1_observe_ValueSpecification( _primitiveValue( packageableType.fullPath, _defaultPrimitiveTypeValue( packageableType.fullPath, ), ), ), ); } else { // If not a primitive, assume it is an enum const typeParam = _elementPtr( guaranteeIsString(packageableType.fullPath), ); const enumValueSpec = _property('', [typeParam]); sourceBuilder.setQueryParameterValue( name, V1_observe_ValueSpecification(enumValueSpec), ); } }; return (
{name} {': '}
{ sourceBuilder.setQueryParameterValue( name, V1_observe_ValueSpecification(val), ); }} resetValue={resetValue} className="ml-2 flex flex-auto" enumeration={enumeration} selectorConfig={{ optionCustomization: { rowHeight: 20 }, }} lightMode={true} />
); }, )}
)} sourceBuilder.unsetQuery()} > Go Back
); }, );