From 202c644acc3d012dbc16e24bd4c0f8248c4e9dd2 Mon Sep 17 00:00:00 2001 From: karanh37 Date: Thu, 25 Sep 2025 12:35:18 +0530 Subject: [PATCH] add explore filter --- .../src/components/AppRouter/DomainRouter.tsx | 6 +- .../DataProduct/DataProductListPage.tsx | 37 +++-- .../DomainDetailPage.test.tsx | 4 +- .../SubDomainsTable.component.tsx | 31 ++-- .../DomainListing/DomainListPage.tsx | 34 ++-- .../atoms/compositions/useListingData.tsx | 12 +- .../atoms/data/useAggregationFetcher.tsx | 51 ------ .../atoms/data/useAggregationProcessor.tsx | 68 -------- .../common/atoms/data/useDataFetching.tsx | 7 + .../atoms/data/useFilterLoadingState.tsx | 52 ------- .../common/atoms/data/useFilterOptions.tsx | 60 ------- .../data/useFilterOptionsComposition.tsx | 147 ------------------ .../atoms/data/useFilterOptionsState.tsx | 62 -------- .../atoms/domain/ui/useDataProductFilters.tsx | 91 +++++++++++ .../atoms/domain/ui/useDomainFilters.tsx | 19 ++- .../atoms/filters/useEntityFiltersGeneric.tsx | 67 -------- .../common/atoms/filters/useFilterConfig.tsx | 55 ------- .../atoms/filters/useFilterDropdowns.tsx | 71 --------- .../common/atoms/filters/useQuickFilters.tsx | 110 +++++++++++++ .../common/atoms/shared/types/index.ts | 2 + .../atoms/shared/utils/commonFilterConfigs.ts | 100 +----------- .../ui/src/locale/languages/de-de.json | 2 +- .../ui/src/locale/languages/es-es.json | 2 +- .../ui/src/locale/languages/fr-fr.json | 2 +- .../ui/src/locale/languages/gl-es.json | 2 +- .../ui/src/locale/languages/he-he.json | 2 +- .../ui/src/locale/languages/ja-jp.json | 2 +- .../ui/src/locale/languages/ko-kr.json | 2 +- .../ui/src/locale/languages/mr-in.json | 2 +- .../ui/src/locale/languages/nl-nl.json | 2 +- .../ui/src/locale/languages/pr-pr.json | 2 +- .../ui/src/locale/languages/pt-br.json | 2 +- .../ui/src/locale/languages/pt-pt.json | 2 +- .../ui/src/locale/languages/ru-ru.json | 2 +- .../ui/src/locale/languages/th-th.json | 2 +- .../ui/src/locale/languages/tr-tr.json | 2 +- .../ui/src/locale/languages/zh-cn.json | 2 +- .../ui/src/locale/languages/zh-tw.json | 2 +- 38 files changed, 304 insertions(+), 816 deletions(-) delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useAggregationFetcher.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useAggregationProcessor.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useFilterLoadingState.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useFilterOptions.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useFilterOptionsComposition.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useFilterOptionsState.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/useDataProductFilters.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useEntityFiltersGeneric.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useFilterConfig.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useFilterDropdowns.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useQuickFilters.tsx diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/DomainRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/DomainRouter.tsx index 2e0c56eb7aa..67c0c327fb0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/DomainRouter.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/DomainRouter.tsx @@ -46,7 +46,7 @@ const DomainRouter = () => { index element={ - + } path="/" @@ -54,7 +54,7 @@ const DomainRouter = () => { - + } path={ROUTES.DOMAIN_DETAILS.replace(ROUTES.DOMAIN, '')} @@ -62,7 +62,7 @@ const DomainRouter = () => { - + } path={ROUTES.DOMAIN_DETAILS_WITH_TAB.replace(ROUTES.DOMAIN, '')} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/DataProductListPage.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/DataProductListPage.tsx index 828307845f2..b95ab93c31b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/DataProductListPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/DataProductListPage.tsx @@ -17,36 +17,51 @@ import { AxiosError } from 'axios'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ERROR_MESSAGE } from '../../constants/constants'; +import { SearchIndex } from '../../enums/search.enum'; import { CreateDataProduct } from '../../generated/api/domains/createDataProduct'; import { withPageLayout } from '../../hoc/withPageLayout'; import { addDataProducts } from '../../rest/dataProductAPI'; import { getIsErrorMatch } from '../../utils/CommonUtils'; import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils'; import { useDelete } from '../common/atoms/actions/useDelete'; +import { useDataProductFilters } from '../common/atoms/domain/ui/useDataProductFilters'; import { useDomainCardTemplates } from '../common/atoms/domain/ui/useDomainCardTemplates'; import { useFormDrawerWithRef } from '../common/atoms/drawer'; -import { useFilterConfig } from '../common/atoms/filters/useFilterConfig'; -import { useFilterDropdowns } from '../common/atoms/filters/useFilterDropdowns'; +import { useQuickFilters } from '../common/atoms/filters/useQuickFilters'; import { useBreadcrumbs } from '../common/atoms/navigation/useBreadcrumbs'; import { usePageHeader } from '../common/atoms/navigation/usePageHeader'; import { useSearch } from '../common/atoms/navigation/useSearch'; import { useTitleAndCount } from '../common/atoms/navigation/useTitleAndCount'; import { useViewToggle } from '../common/atoms/navigation/useViewToggle'; import { usePaginationControls } from '../common/atoms/pagination/usePaginationControls'; -import { DATA_PRODUCT_FILTER_CONFIGS } from '../common/atoms/shared/utils/commonFilterConfigs'; import { useCardView } from '../common/atoms/table/useCardView'; import { useDataTable } from '../common/atoms/table/useDataTable'; import AddDomainForm from '../Domain/AddDomainForm/AddDomainForm.component'; import { DomainFormType } from '../Domain/DomainPage.interface'; import { useDataProductListingData } from './hooks/useDataProductListingData'; -const DataProductListPage = () => { +const DataProductListPage = ({ pageTitle }: { pageTitle: string }) => { const dataProductListing = useDataProductListingData(); const theme = useTheme(); const { t } = useTranslation(); const [form] = useForm(); const [isLoading, setIsLoading] = useState(false); + // Use data product-specific filters configuration + const dataProductFilters = useDataProductFilters({ + enabledFilters: ['owner', 'expert', 'tags', 'glossary'], + }); + + const { quickFilters } = useQuickFilters({ + filterFields: dataProductFilters.filterFields, + filterConfigs: dataProductFilters.filterConfigs, + queryConfig: dataProductFilters.queryConfig, + onFilterChange: dataProductListing.handleFilterChange, + aggregations: dataProductListing.aggregations || {}, + searchIndex: SearchIndex.DATA_PRODUCT, + independent: true, + }); + const { formDrawer, openDrawer, closeDrawer } = useFormDrawerWithRef({ title: t('label.add-entity', { entity: t('label.data-product') }), anchor: 'right', @@ -101,14 +116,6 @@ const DataProductListPage = () => { loading: isLoading, }); - const { dropdownConfigs } = useFilterConfig({ - filterConfigs: DATA_PRODUCT_FILTER_CONFIGS, - filterOptions: dataProductListing.filterOptions || {}, - selectedFilters: dataProductListing.urlState.filters, - onFilterChange: dataProductListing.handleFilterChange, - onFilterSearch: dataProductListing.searchFilterOptions, - }); - // Composable hooks for each UI component const { breadcrumbs } = useBreadcrumbs({ entityLabelKey: 'label.data-product', @@ -135,10 +142,6 @@ const DataProductListPage = () => { initialSearchQuery: dataProductListing.urlState.searchQuery, }); - const { filterDropdowns } = useFilterDropdowns({ - filters: dropdownConfigs, - }); - const { view, viewToggle } = useViewToggle(); const { dataProductCardTemplate } = useDomainCardTemplates(); @@ -190,7 +193,7 @@ const DataProductListPage = () => { }}> {titleAndCount} {search} - {filterDropdowns} + {quickFilters} {viewToggle} {deleteIconButton} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailPage/DomainDetailPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailPage/DomainDetailPage.test.tsx index 8ca20f63a43..1d1dfcf1a79 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailPage/DomainDetailPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailPage/DomainDetailPage.test.tsx @@ -13,7 +13,7 @@ import { render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; -import { MOCK_DOMAINS } from '../../../mocks/Domains.mock'; +import { DOMAINS_LIST } from '../../../mocks/Domains.mock'; import { getDomainByName } from '../../../rest/domainAPI'; import DomainDetailPage from './DomainDetailPage.component'; @@ -38,7 +38,7 @@ const mockGetDomainByName = getDomainByName as jest.MockedFunction< describe('DomainDetailPage', () => { beforeEach(() => { - mockGetDomainByName.mockResolvedValue(MOCK_DOMAINS[0]); + mockGetDomainByName.mockResolvedValue(DOMAINS_LIST[0]); }); it('should render domain detail page', async () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/SubDomainsTable/SubDomainsTable.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Domain/SubDomainsTable/SubDomainsTable.component.tsx index 223db22fd88..5107151ee39 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/SubDomainsTable/SubDomainsTable.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/SubDomainsTable/SubDomainsTable.component.tsx @@ -12,15 +12,15 @@ */ import { Box, Paper, TableContainer, useTheme } from '@mui/material'; +import { SearchIndex } from '../../../enums/search.enum'; import { useDelete } from '../../common/atoms/actions/useDelete'; import { useDomainCardTemplates } from '../../common/atoms/domain/ui/useDomainCardTemplates'; -import { useFilterConfig } from '../../common/atoms/filters/useFilterConfig'; -import { useFilterDropdowns } from '../../common/atoms/filters/useFilterDropdowns'; +import { useDomainFilters } from '../../common/atoms/domain/ui/useDomainFilters'; +import { useQuickFilters } from '../../common/atoms/filters/useQuickFilters'; import { useSearch } from '../../common/atoms/navigation/useSearch'; import { useTitleAndCount } from '../../common/atoms/navigation/useTitleAndCount'; import { useViewToggle } from '../../common/atoms/navigation/useViewToggle'; import { usePaginationControls } from '../../common/atoms/pagination/usePaginationControls'; -import { SUBDOMAIN_FILTER_CONFIGS } from '../../common/atoms/shared/utils/commonFilterConfigs'; import { useCardView } from '../../common/atoms/table/useCardView'; import { useDataTable } from '../../common/atoms/table/useDataTable'; import { useSubdomainListingData } from './hooks/useSubdomainListingData'; @@ -36,12 +36,19 @@ const SubDomainsTable = ({ parentDomainFqn: domainFqn, }); - const { dropdownConfigs } = useFilterConfig({ - filterConfigs: SUBDOMAIN_FILTER_CONFIGS, - filterOptions: subdomainListing.filterOptions || {}, - selectedFilters: subdomainListing.urlState.filters, + // Use the same domain filters configuration + const domainFilters = useDomainFilters({ + enabledFilters: ['owner', 'tags', 'glossary', 'domainType'], + }); + + const { quickFilters } = useQuickFilters({ + filterFields: domainFilters.filterFields, + filterConfigs: domainFilters.filterConfigs, + queryConfig: domainFilters.queryConfig, onFilterChange: subdomainListing.handleFilterChange, - onFilterSearch: subdomainListing.searchFilterOptions, + aggregations: subdomainListing.aggregations || {}, + searchIndex: SearchIndex.DOMAIN, + independent: true, }); const { titleAndCount } = useTitleAndCount({ @@ -51,15 +58,11 @@ const SubDomainsTable = ({ }); const { search } = useSearch({ - searchPlaceholder: 'Search subdomains', + searchPlaceholderKey: 'label.search-subdomain', onSearchChange: subdomainListing.handleSearchChange, initialSearchQuery: subdomainListing.urlState.searchQuery, }); - const { filterDropdowns } = useFilterDropdowns({ - filters: dropdownConfigs, - }); - const { view, viewToggle } = useViewToggle(); const { domainCardTemplate } = useDomainCardTemplates(); @@ -108,7 +111,7 @@ const SubDomainsTable = ({ }}> {titleAndCount} {search} - {filterDropdowns} + {quickFilters} {viewToggle} {deleteIconButton} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DomainListing/DomainListPage.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DomainListing/DomainListPage.tsx index 065c49e4668..f60a225504f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DomainListing/DomainListPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DomainListing/DomainListPage.tsx @@ -17,22 +17,22 @@ import { AxiosError } from 'axios'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ERROR_MESSAGE } from '../../constants/constants'; +import { SearchIndex } from '../../enums/search.enum'; import { withPageLayout } from '../../hoc/withPageLayout'; import { addDomains } from '../../rest/domainAPI'; import { getIsErrorMatch } from '../../utils/CommonUtils'; import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils'; import { useDelete } from '../common/atoms/actions/useDelete'; import { useDomainCardTemplates } from '../common/atoms/domain/ui/useDomainCardTemplates'; +import { useDomainFilters } from '../common/atoms/domain/ui/useDomainFilters'; import { useFormDrawerWithRef } from '../common/atoms/drawer'; -import { useFilterConfig } from '../common/atoms/filters/useFilterConfig'; -import { useFilterDropdowns } from '../common/atoms/filters/useFilterDropdowns'; +import { useQuickFilters } from '../common/atoms/filters/useQuickFilters'; import { useBreadcrumbs } from '../common/atoms/navigation/useBreadcrumbs'; import { usePageHeader } from '../common/atoms/navigation/usePageHeader'; import { useSearch } from '../common/atoms/navigation/useSearch'; import { useTitleAndCount } from '../common/atoms/navigation/useTitleAndCount'; import { useViewToggle } from '../common/atoms/navigation/useViewToggle'; import { usePaginationControls } from '../common/atoms/pagination/usePaginationControls'; -import { DOMAIN_FILTER_CONFIGS } from '../common/atoms/shared/utils/commonFilterConfigs'; import { useCardView } from '../common/atoms/table/useCardView'; import { useDataTable } from '../common/atoms/table/useDataTable'; import AddDomainForm from '../Domain/AddDomainForm/AddDomainForm.component'; @@ -46,6 +46,20 @@ const DomainListPage = () => { const [form] = useForm(); const [isLoading, setIsLoading] = useState(false); + // Use the existing domain filters configuration + const domainFilters = useDomainFilters({ + enabledFilters: ['owner', 'tags', 'glossary', 'domainType'], + }); + + const { quickFilters } = useQuickFilters({ + filterFields: domainFilters.filterFields, + filterConfigs: domainFilters.filterConfigs, + queryConfig: domainFilters.queryConfig, + onFilterChange: domainListing.handleFilterChange, + aggregations: domainListing.aggregations || {}, + searchIndex: SearchIndex.DOMAIN, + }); + const { formDrawer, openDrawer, closeDrawer } = useFormDrawerWithRef({ title: t('label.add-entity', { entity: t('label.domain') }), anchor: 'right', @@ -99,14 +113,6 @@ const DomainListPage = () => { loading: isLoading, }); - const { dropdownConfigs } = useFilterConfig({ - filterConfigs: DOMAIN_FILTER_CONFIGS, - filterOptions: domainListing.filterOptions || {}, - selectedFilters: domainListing.urlState.filters, - onFilterChange: domainListing.handleFilterChange, - onFilterSearch: domainListing.searchFilterOptions, - }); - // Composable hooks for each UI component const { breadcrumbs } = useBreadcrumbs({ entityLabelKey: 'label.domain', @@ -133,10 +139,6 @@ const DomainListPage = () => { initialSearchQuery: domainListing.urlState.searchQuery, }); - const { filterDropdowns } = useFilterDropdowns({ - filters: dropdownConfigs, - }); - const { view, viewToggle } = useViewToggle(); const { domainCardTemplate } = useDomainCardTemplates(); @@ -188,7 +190,7 @@ const DomainListPage = () => { }}> {titleAndCount} {search} - {filterDropdowns} + {quickFilters} {viewToggle} {deleteIconButton} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/compositions/useListingData.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/compositions/useListingData.tsx index 33c027a7baf..aad86dc509c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/compositions/useListingData.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/compositions/useListingData.tsx @@ -13,8 +13,8 @@ import { useCallback, useEffect, useMemo } from 'react'; import { SearchIndex } from '../../../../enums/search.enum'; +import { getAggregations } from '../../../../utils/ExploreUtils'; import { useDataFetching } from '../data/useDataFetching'; -import { useFilterOptions } from '../data/useFilterOptions'; import { useSelectionState } from '../data/useSelectionState'; import { useUrlState } from '../data/useUrlState'; import { usePaginationState } from '../pagination/usePaginationState'; @@ -80,11 +80,6 @@ export const useListingData = ( const selectionState = useSelectionState(dataFetching.entities); - const filterOptionsHook = useFilterOptions({ - searchIndex, - filterFields, - }); - const actionHandlers = useActionHandlers({ basePath, getEntityPath, @@ -176,11 +171,12 @@ export const useListingData = ( clearSelection: selectionState.clearSelection, urlState, actionHandlers, - filterOptions: filterOptionsHook.filterOptions, + filterOptions: {}, + aggregations: getAggregations(dataFetching.aggregations || {}), handleSearchChange, handleFilterChange, handlePageChange, - searchFilterOptions: filterOptionsHook.searchFilterOptions, + searchFilterOptions: () => Promise.resolve([]), refetch, }; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useAggregationFetcher.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useAggregationFetcher.tsx deleted file mode 100644 index ce3a749437d..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useAggregationFetcher.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2024 Collate. - * 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 { useCallback } from 'react'; -import { SearchIndex } from '../../../../enums/search.enum'; -import { getAggregationOptions } from '../../../../utils/ExploreUtils'; -import { FilterField } from '../types'; - -/** - * Fetches aggregation data from API for filter dropdowns - * - * @description - * Pure API fetching hook that calls the aggregation endpoint to get - * filter option data. Handles both initial fetch and search-within-filter. - * Does not manage state - only makes API calls. - * - * @example - * ```typescript - * const fetchAggregation = useAggregationFetcher(); - * const result = await fetchAggregation(SearchIndex.DOMAIN, field, searchTerm); - * ``` - * - * @stability Stable - No external dependencies, pure function - * @complexity Low - Single API call responsibility - */ -export const useAggregationFetcher = () => { - const fetchAggregation = useCallback( - async (searchIndex: SearchIndex, field: FilterField, searchTerm = '') => { - return await getAggregationOptions( - searchIndex, - field.aggregationField, - searchTerm, - JSON.stringify({}), - false - ); - }, - [] // No dependencies - pure API call - ); - - return fetchAggregation; -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useAggregationProcessor.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useAggregationProcessor.tsx deleted file mode 100644 index 6ac8b3b60a6..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useAggregationProcessor.tsx +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2024 Collate. - * 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 { useCallback } from 'react'; - -/** - * Processes raw aggregation API response into SearchDropdownOption format - * - * @description - * This hook converts the raw response from Elasticsearch aggregation APIs - * into a standardized format that can be used by SearchDropdown components. - * Handles both custom processors and default bucket processing. - * - * @example - * ```typescript - * const processResult = useAggregationProcessor(); - * const options = processResult(apiResponse, customProcessor); - * ``` - * - * @stability Stable - Uses empty dependency array for maximum stability - * @complexity Low - Single transformation responsibility - */ -export const useAggregationProcessor = () => { - const processAggregationResult = useCallback( - ( - result: unknown, - processor?: (result: unknown) => unknown[] - ): unknown[] => { - if ((result as any)?.status !== 'fulfilled') { - return []; - } - - if (processor) { - return processor(result); - } - - // Default processing for simple aggregations - const buckets = Object.values( - (result as any).value?.data?.aggregations || {} - )[0] as unknown; - - if (!(buckets as any)?.buckets) { - return []; - } - - return ( - (buckets as any)?.buckets?.map((bucket: any) => ({ - key: bucket.key, - label: bucket.key, - count: bucket.doc_count, - })) || [] - ); - }, - [] // Empty dependency array ensures maximum stability - ); - - return processAggregationResult; -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useDataFetching.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useDataFetching.tsx index a122955099d..15d6fdfbecc 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useDataFetching.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useDataFetching.tsx @@ -14,6 +14,7 @@ import { AxiosError } from 'axios'; import { useCallback, useState } from 'react'; import { SearchIndex } from '../../../../enums/search.enum'; +import { Aggregations } from '../../../../interface/search.interface'; import { searchData } from '../../../../rest/miscAPI'; import { showErrorToast } from '../../../../utils/ToastUtils'; @@ -30,6 +31,7 @@ export interface DataFetchingResult { loading: boolean; error: Error | null; totalEntities: number; + aggregations: Aggregations | null; refetch: () => void; searchEntities: ( page: number, @@ -45,6 +47,7 @@ export const useDataFetching = ( const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [totalEntities, setTotalEntities] = useState(0); + const [aggregations, setAggregations] = useState(null); const { searchIndex, @@ -112,15 +115,18 @@ export const useDataFetching = ( // Process response const transformedEntities = transformData(response.data); const total = response.data?.hits?.total?.value || 0; + const responseAggregations = response.data?.aggregations || null; // Update state setEntities(transformedEntities); setTotalEntities(total); + setAggregations(responseAggregations); setError(null); } catch (err) { setError(err instanceof Error ? err : new Error('Search failed')); setEntities([]); setTotalEntities(0); + setAggregations(null); showErrorToast(err as AxiosError); } finally { setLoading(false); @@ -139,6 +145,7 @@ export const useDataFetching = ( loading, error, totalEntities, + aggregations, refetch, searchEntities, }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useFilterLoadingState.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useFilterLoadingState.tsx deleted file mode 100644 index bf4ecae3b68..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useFilterLoadingState.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2024 Collate. - * 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 { useCallback, useState } from 'react'; - -/** - * Manages loading state for filter operations - * - * @description - * Simple loading state manager specifically for filter-related operations. - * Provides start/stop loading functions and current loading state. - * - * @example - * ```typescript - * const { loading, startLoading, stopLoading } = useFilterLoadingState(); - * - * // In async operation: - * startLoading(); - * await fetchData(); - * stopLoading(); - * ``` - * - * @stability Stable - No external dependencies - * @complexity Very Low - Simple state management only - */ -export const useFilterLoadingState = () => { - const [loading, setLoading] = useState(false); - - const startLoading = useCallback(() => { - setLoading(true); - }, []); - - const stopLoading = useCallback(() => { - setLoading(false); - }, []); - - return { - loading, - startLoading, - stopLoading, - }; -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useFilterOptions.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useFilterOptions.tsx deleted file mode 100644 index 6aa9bb5a587..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useFilterOptions.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2024 Collate. - * 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 { SearchIndex } from '../../../../enums/search.enum'; -import { FilterField } from '../types'; -import { useFilterOptionsComposition } from './useFilterOptionsComposition'; - -interface UseFilterOptionsProps { - searchIndex: SearchIndex; - filterFields: FilterField[]; -} - -/** - * Complete filter options system for dropdown filters - * - * @description - * Provides complete filter options functionality by composing multiple - * micro hooks. This is the main interface for filter options that - * components should use. - * - * Internally composed of: - * - useAggregationProcessor: Data transformation - * - useAggregationFetcher: API calls - * - useFilterLoadingState: Loading management - * - useFilterOptionsState: State management - * - useFilterOptionsComposition: Orchestration - * - * @param config.searchIndex - Elasticsearch index to fetch aggregations from - * @param config.filterFields - Array of fields to create filter options for - * - * @example - * ```typescript - * const { filterOptions, loading, searchFilterOptions } = useFilterOptions({ - * searchIndex: SearchIndex.DOMAIN, - * filterFields: [COMMON_FILTER_FIELDS.owners] - * }); - * ``` - * - * @stability Stable - Composes stable micro hooks - * @complexity Low - Simple composition interface - */ -export const useFilterOptions = ({ - searchIndex, - filterFields, -}: UseFilterOptionsProps) => { - return useFilterOptionsComposition({ - searchIndex, - filterFields, - }); -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useFilterOptionsComposition.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useFilterOptionsComposition.tsx deleted file mode 100644 index 688a640abd6..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useFilterOptionsComposition.tsx +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2024 Collate. - * 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 { useCallback, useEffect } from 'react'; -import { SearchIndex } from '../../../../enums/search.enum'; -import { FilterField } from '../types'; -import { useAggregationFetcher } from './useAggregationFetcher'; -import { useAggregationProcessor } from './useAggregationProcessor'; -import { useFilterLoadingState } from './useFilterLoadingState'; -import { useFilterOptionsState } from './useFilterOptionsState'; - -interface UseFilterOptionsCompositionProps { - searchIndex: SearchIndex; - filterFields: FilterField[]; -} - -/** - * Composes filter option micro hooks into complete filter options system - * - * @description - * Combines multiple micro hooks to provide complete filter options functionality: - * - State management (useFilterOptionsState) - * - API fetching (useAggregationFetcher) - * - Data processing (useAggregationProcessor) - * - Loading management (useFilterLoadingState) - * - * Each micro hook has a single responsibility, making this composition - * easy to understand, test, and debug. - * - * @param config.searchIndex - Elasticsearch index to fetch aggregations from - * @param config.filterFields - Array of fields to create filter options for - * - * @example - * ```typescript - * const { filterOptions, loading, searchFilterOptions } = useFilterOptionsComposition({ - * searchIndex: SearchIndex.DOMAIN, - * filterFields: [COMMON_FILTER_FIELDS.owners, COMMON_FILTER_FIELDS.tags] - * }); - * ``` - * - * @stability Stable - Composes stable micro hooks - * @complexity Low - Simple composition pattern - */ -export const useFilterOptionsComposition = ({ - searchIndex, - filterFields, -}: UseFilterOptionsCompositionProps) => { - // Micro hooks - each with single responsibility - const processResult = useAggregationProcessor(); - const fetchAggregation = useAggregationFetcher(); - const { loading, startLoading, stopLoading } = useFilterLoadingState(); - const { filterOptions, setFilterOptions, updateFilterField } = - useFilterOptionsState(); - - // Fetch all filter options (initial load) - const fetchAllFilterOptions = useCallback(async () => { - startLoading(); - try { - const results = await Promise.allSettled( - filterFields.map((field) => fetchAggregation(searchIndex, field)) - ); - - const newFilterOptions = filterFields.reduce((acc, field, index) => { - acc[field.key] = processResult(results[index], field.processor); - - return acc; - }, {} as Record); - - setFilterOptions(newFilterOptions); - } catch (error) { - // Reset to empty options on error - setFilterOptions({}); - } finally { - stopLoading(); - } - }, [ - searchIndex, - filterFields, - fetchAggregation, - processResult, - startLoading, - stopLoading, - setFilterOptions, - ]); - - // Search within specific filter field - const searchFilterOptions = useCallback( - async (fieldKey: string, searchTerm: string) => { - const field = filterFields.find((f) => f.key === fieldKey); - if (!field) { - return; - } - - if (!searchTerm.trim()) { - fetchAllFilterOptions(); - - return; - } - - startLoading(); - try { - const result = await fetchAggregation(searchIndex, field, searchTerm); - const processedOptions = processResult( - { status: 'fulfilled', value: result }, - field.processor - ); - updateFilterField(fieldKey, processedOptions); - } catch (error) { - // Keep existing options on search error - } finally { - stopLoading(); - } - }, - [ - filterFields, - searchIndex, - fetchAggregation, - processResult, - updateFilterField, - startLoading, - stopLoading, - fetchAllFilterOptions, - ] - ); - - // Load filter options on mount - useEffect(() => { - fetchAllFilterOptions(); - }, [fetchAllFilterOptions]); - - return { - filterOptions, - loading, - refetch: fetchAllFilterOptions, - searchFilterOptions, - }; -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useFilterOptionsState.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useFilterOptionsState.tsx deleted file mode 100644 index 21772cc5363..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useFilterOptionsState.tsx +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2024 Collate. - * 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 { useCallback, useState } from 'react'; -import { FilterOptions } from '../types'; - -/** - * Manages filter options state (data storage only) - * - * @description - * Simple state manager for filter dropdown options. Stores the options - * object and provides functions to update specific filter fields. - * Does not fetch data - only manages state. - * - * @example - * ```typescript - * const { filterOptions, setFilterOptions, updateFilterField } = useFilterOptionsState(); - * - * // Set all options - * setFilterOptions({ owners: [...], tags: [...] }); - * - * // Update specific field - * updateFilterField('owners', newOwnerOptions); - * ``` - * - * @stability Stable - No external dependencies - * @complexity Very Low - Basic state management only - */ -export const useFilterOptionsState = () => { - const [filterOptions, setFilterOptions] = useState({}); - - const updateFilterField = useCallback( - (fieldKey: string, options: unknown[]) => { - setFilterOptions((prev) => ({ - ...prev, - [fieldKey]: options, - })); - }, - [] - ); - - const clearFilterOptions = useCallback(() => { - setFilterOptions({}); - }, []); - - return { - filterOptions, - setFilterOptions, - updateFilterField, - clearFilterOptions, - }; -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/useDataProductFilters.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/useDataProductFilters.tsx new file mode 100644 index 00000000000..2cbe68554ef --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/useDataProductFilters.tsx @@ -0,0 +1,91 @@ +/* + * Copyright 2024 Collate. + * 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 { useMemo } from 'react'; +import { FilterConfig, FilterField } from '../../shared/types'; +import { COMMON_FILTER_FIELDS } from '../../shared/utils/commonFilterConfigs'; + +interface UseDataProductFiltersConfig { + enabledFilters?: string[]; + customFilterFields?: FilterField[]; + customFilterConfigs?: FilterConfig[]; +} + +export const useDataProductFilters = ( + config: UseDataProductFiltersConfig = {} +) => { + const { enabledFilters = ['owner', 'expert', 'tags', 'glossary'] } = config; + + const filterKeys = useMemo(() => enabledFilters, []); + + const queryConfig = useMemo( + () => ({ + owner: 'owners.displayName.keyword', + expert: 'experts.displayName.keyword', + tags: 'classificationTags', + glossary: 'glossaryTags', + }), + [] + ); + + const filterFields = useMemo( + () => [ + COMMON_FILTER_FIELDS.owners, + COMMON_FILTER_FIELDS.experts, + COMMON_FILTER_FIELDS.tags, + COMMON_FILTER_FIELDS.glossary, + ], + [] + ); + + const filterConfigs = useMemo( + () => [ + { + key: 'owners', + labelKey: 'label.owner', + searchKey: 'owner.displayName', + optionsKey: 'owners', + selectedKey: 'owner', + }, + { + key: 'experts', + labelKey: 'label.expert-plural', + searchKey: 'expert.displayName', + optionsKey: 'experts', + selectedKey: 'expert', + }, + { + key: 'tags', + labelKey: 'label.tag-plural', + searchKey: 'tags.tagFQN', + optionsKey: 'tags', + selectedKey: 'tags', + }, + { + key: 'glossary', + labelKey: 'label.glossary-term-plural', + searchKey: 'glossaryTerms', + optionsKey: 'glossary', + selectedKey: 'glossary', + }, + ], + [] + ); + + return { + filterKeys, + queryConfig, + filterFields, + filterConfigs, + }; +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/useDomainFilters.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/useDomainFilters.tsx index 25e6b9bc5fd..da59cc0af7c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/useDomainFilters.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/ui/useDomainFilters.tsx @@ -22,11 +22,8 @@ interface UseDomainFiltersConfig { } export const useDomainFilters = (config: UseDomainFiltersConfig = {}) => { - const { - enabledFilters = ['owner', 'tags', 'glossary'], // Temporarily disabled domainType due to API errors - customFilterFields = [], - customFilterConfigs = [], - } = config; + const { enabledFilters = ['owner', 'tags', 'glossary', 'domainType'] } = + config; const filterKeys = useMemo(() => enabledFilters, []); @@ -35,7 +32,7 @@ export const useDomainFilters = (config: UseDomainFiltersConfig = {}) => { owner: 'owners.displayName.keyword', tags: 'classificationTags', glossary: 'glossaryTags', - domainType: 'domainType', + domainType: 'domainType.keyword', }), [] ); @@ -45,7 +42,7 @@ export const useDomainFilters = (config: UseDomainFiltersConfig = {}) => { COMMON_FILTER_FIELDS.owners, COMMON_FILTER_FIELDS.tags, COMMON_FILTER_FIELDS.glossary, - // COMMON_FILTER_FIELDS.domainTypes, // Temporarily disabled + COMMON_FILTER_FIELDS.domainTypes, // Temporarily disabled ], [] ); @@ -73,7 +70,13 @@ export const useDomainFilters = (config: UseDomainFiltersConfig = {}) => { optionsKey: 'glossary', selectedKey: 'glossary', }, - // domainType temporarily disabled + { + key: 'domainTypes', + labelKey: 'label.domain-type', + searchKey: 'domainType', + optionsKey: 'domainTypes', + selectedKey: 'domainType', + }, ], [] ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useEntityFiltersGeneric.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useEntityFiltersGeneric.tsx deleted file mode 100644 index e16ad98761e..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useEntityFiltersGeneric.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2024 Collate. - * 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 { Box } from '@mui/material'; -import { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import SearchDropdown from '../../../SearchDropdown/SearchDropdown'; -import { SearchDropdownOption } from '../../../SearchDropdown/SearchDropdown.interface'; - -interface FilterConfig { - key: string; - labelKey: string; - searchKey: string; - options: SearchDropdownOption[]; - selectedOptions: SearchDropdownOption[]; - onChange: (values: string[]) => void; - onSearch: (searchTerm: string) => void; -} - -interface EntityFiltersGenericConfig { - filters: FilterConfig[]; -} - -export const useEntityFiltersGeneric = (config: EntityFiltersGenericConfig) => { - const { t } = useTranslation(); - - // Inline implementation copying exact EntityFiltersGeneric functionality - const entityFiltersGeneric = useMemo( - () => ( - - {config.filters.map((filter) => ( - { - filter.onChange(values.map((v) => v.key)); - }} - onSearch={filter.onSearch} - /> - ))} - - ), - [config, t] - ); - - return { - entityFiltersGeneric, - hasActiveFilters: config.filters.some( - (filter) => filter.selectedOptions.length > 0 - ), - clearAllFilters: () => - config.filters.forEach((filter) => filter.onChange([])), - }; -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useFilterConfig.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useFilterConfig.tsx deleted file mode 100644 index 01be2045627..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useFilterConfig.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2024 Collate. - * 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 { useMemo } from 'react'; -import { DropdownConfig, FilterConfig, FilterOptions } from '../types'; - -interface UseFilterConfigProps { - filterConfigs: FilterConfig[]; - filterOptions: FilterOptions; - selectedFilters: Record; - onFilterChange: (key: string, values: string[]) => void; - onFilterSearch: (key: string, term: string) => void; -} - -export const useFilterConfig = (props: UseFilterConfigProps) => { - const { - filterConfigs, - filterOptions, - selectedFilters, - onFilterChange, - onFilterSearch, - } = props; - - const dropdownConfigs: DropdownConfig[] = useMemo(() => { - return filterConfigs.map((config) => ({ - key: config.key, - labelKey: config.labelKey, - searchKey: config.searchKey, - options: filterOptions[config.optionsKey] || [], - selectedOptions: selectedFilters[config.selectedKey] || [], - onChange: (values: string[]) => onFilterChange(config.key, values), - onSearch: (term: string) => onFilterSearch(config.optionsKey, term), - })); - }, [ - filterConfigs, - filterOptions, - selectedFilters, - onFilterChange, - onFilterSearch, - ]); - - return { - dropdownConfigs, - }; -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useFilterDropdowns.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useFilterDropdowns.tsx deleted file mode 100644 index 6bff97e1c1e..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useFilterDropdowns.tsx +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2024 Collate. - * 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 { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { SearchDropdownOption } from '../../../SearchDropdown/SearchDropdown.interface'; -import { useEntityFiltersGeneric } from './useEntityFiltersGeneric'; - -interface FilterDropdownsConfig { - filters: Array<{ - key: string; - labelKey: string; - searchKey: string; - options: any[]; - selectedOptions: any[]; - onChange: (values: string[]) => void; - onSearch: (term: string) => void; - }>; -} - -export const useFilterDropdowns = (config: FilterDropdownsConfig) => { - const { t } = useTranslation(); - - // Convert our filter format to the format expected by useEntityFiltersGeneric - const convertedFilters = useMemo(() => { - return config.filters.map((filter) => ({ - key: filter.key, - labelKey: filter.labelKey, - searchKey: filter.searchKey, - options: filter.options.map( - (option): SearchDropdownOption => ({ - key: option.key || option.label || option, - label: option.label || option.key || option, - count: option.count || 0, - }) - ), - selectedOptions: filter.selectedOptions.map( - (option): SearchDropdownOption => ({ - key: typeof option === 'string' ? option : option.key || option.label, - label: - typeof option === 'string' ? option : option.label || option.key, - count: typeof option === 'object' ? option.count || 0 : 0, - }) - ), - onChange: filter.onChange, - onSearch: filter.onSearch, - })); - }, [config.filters]); - - const { entityFiltersGeneric, hasActiveFilters, clearAllFilters } = - useEntityFiltersGeneric({ - filters: convertedFilters, - }); - - return { - filterDropdowns: entityFiltersGeneric, - clearAllFilters, - hasActiveFilters, - filterCount: config.filters.length, - }; -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useQuickFilters.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useQuickFilters.tsx new file mode 100644 index 00000000000..0401f8e1c68 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useQuickFilters.tsx @@ -0,0 +1,110 @@ +/* + * Copyright 2024 Collate. + * 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 { useCallback, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { SearchIndex } from '../../../../enums/search.enum'; +import { Aggregations } from '../../../../interface/search.interface'; +import { ExploreQuickFilterField } from '../../../Explore/ExplorePage.interface'; +import ExploreQuickFilters from '../../../Explore/ExploreQuickFilters'; +import { FilterConfig, FilterField } from '../shared/types'; + +interface QuickFiltersConfig { + filterFields: FilterField[]; + filterConfigs: FilterConfig[]; + queryConfig: Record; + onFilterChange: (filterKey: string, values: string[]) => void; + aggregations: Aggregations; + searchIndex: SearchIndex; + independent?: boolean; +} + +export const useQuickFilters = (config: QuickFiltersConfig) => { + const { t } = useTranslation(); + + const initialFilters = useMemo( + () => + config.filterFields.map((field) => { + const filterConfig = config.filterConfigs.find( + (fc) => fc.key === field.key + ); + + return { + key: field.aggregationField, + label: filterConfig ? t(filterConfig.labelKey) : field.key, + value: [], + }; + }), + [config.filterFields, config.filterConfigs, t] + ); + + const [selectedQuickFilters, setSelectedQuickFilters] = + useState(initialFilters); + + const getFilterKeyFromField = useCallback( + (fieldKey: string): string => { + // Reverse lookup from aggregationField to filter key + const entry = Object.entries(config.queryConfig).find( + ([, value]) => value === fieldKey + ); + + return entry ? entry[0] : fieldKey; + }, + [config.queryConfig] + ); + + const handleQuickFiltersValueSelect = useCallback( + (field: ExploreQuickFilterField) => { + setSelectedQuickFilters((prev) => { + const data = prev.map((prevField) => { + if (prevField.key === field.key) { + return field; + } + + return prevField; + }); + + const filterKey = getFilterKeyFromField(field.key); + config.onFilterChange(filterKey, field.value?.map((v) => v.key) || []); + + return data; + }); + }, + [config.onFilterChange, getFilterKeyFromField] + ); + + const quickFilters = useMemo( + () => ( + + ), + [ + config.independent, + config.aggregations, + config.searchIndex, + selectedQuickFilters, + handleQuickFiltersValueSelect, + ] + ); + + return { + quickFilters, + selectedQuickFilters, + handleQuickFiltersValueSelect, + }; +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/shared/types/index.ts b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/shared/types/index.ts index d042581ccc9..df98286ccad 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/shared/types/index.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/shared/types/index.ts @@ -12,6 +12,7 @@ */ import { ReactNode } from 'react'; +import { Aggregations } from '../../../../../interface/search.interface'; export interface UrlStateConfig { searchKey?: string; @@ -123,6 +124,7 @@ export interface ListingData { urlState: UrlState; actionHandlers: ActionHandlers; filterOptions?: FilterOptions; + aggregations?: Aggregations | null; handleSearchChange: (query: string) => void; handleFilterChange: (key: string, values: string[]) => void; handlePageChange: (page: number) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/shared/utils/commonFilterConfigs.ts b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/shared/utils/commonFilterConfigs.ts index 49da1e7318a..d032dd1ac32 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/shared/utils/commonFilterConfigs.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/shared/utils/commonFilterConfigs.ts @@ -11,19 +11,16 @@ * limitations under the License. */ -import { FilterConfig, FilterField } from '../types'; -import { processOwnerOptions } from './entityFilterProcessors'; +import { FilterField } from '../types'; export const COMMON_FILTER_FIELDS: Record = { owners: { key: 'owners', aggregationField: 'owners.displayName.keyword', - processor: processOwnerOptions, }, experts: { key: 'experts', aggregationField: 'experts.displayName.keyword', - processor: processOwnerOptions, }, tags: { key: 'tags', @@ -35,99 +32,6 @@ export const COMMON_FILTER_FIELDS: Record = { }, domainTypes: { key: 'domainTypes', - aggregationField: 'domainType', + aggregationField: 'domainType.keyword', }, }; - -export const DOMAIN_FILTER_CONFIGS: FilterConfig[] = [ - { - key: 'owner', - labelKey: 'label.owner', - searchKey: 'owner.displayName', - optionsKey: 'owners', - selectedKey: 'owner', - }, - { - key: 'tags', - labelKey: 'label.tag-plural', - searchKey: 'tags.tagFQN', - optionsKey: 'tags', - selectedKey: 'tags', - }, - { - key: 'glossary', - labelKey: 'label.glossary-term-plural', - searchKey: 'glossaryTerms', - optionsKey: 'glossary', - selectedKey: 'glossary', - }, - { - key: 'domainType', - labelKey: 'label.domain-type', - searchKey: 'domainType', - optionsKey: 'domainTypes', - selectedKey: 'domainType', - }, -]; - -export const DATA_PRODUCT_FILTER_CONFIGS: FilterConfig[] = [ - { - key: 'owner', - labelKey: 'label.owner', - searchKey: 'owner.displayName', - optionsKey: 'owners', - selectedKey: 'owner', - }, - { - key: 'expert', - labelKey: 'label.expert', - searchKey: 'expert.displayName', - optionsKey: 'experts', - selectedKey: 'expert', - }, - { - key: 'tags', - labelKey: 'label.tag-plural', - searchKey: 'tags.tagFQN', - optionsKey: 'tags', - selectedKey: 'tags', - }, - { - key: 'glossary', - labelKey: 'label.glossary-term-plural', - searchKey: 'glossaryTerms', - optionsKey: 'glossary', - selectedKey: 'glossary', - }, -]; - -export const SUBDOMAIN_FILTER_CONFIGS: FilterConfig[] = [ - { - key: 'owner', - labelKey: 'label.owner', - searchKey: 'owner.displayName', - optionsKey: 'owners', - selectedKey: 'owner', - }, - { - key: 'tags', - labelKey: 'label.tag-plural', - searchKey: 'tags.tagFQN', - optionsKey: 'tags', - selectedKey: 'tags', - }, - { - key: 'glossary', - labelKey: 'label.glossary-term-plural', - searchKey: 'glossaryTerms', - optionsKey: 'glossary', - selectedKey: 'glossary', - }, - { - key: 'domainType', - labelKey: 'label.domain-type', - searchKey: 'domainType', - optionsKey: 'domainTypes', - selectedKey: 'domainType', - }, -]; diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json index 536f5a4bcdf..dbefe649af9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "Senden des Dateninsight-Berichts fehlgeschlagen.", "data-insight-report-send-success-message": "Dateninsight-Bericht erfolgreich gesendet.", "data-insight-subtitle": "Erhalten Sie einen einzigen Überblick über die Gesundheit aller Ihrer Datenvermögenswerte im Laufe der Zeit.", - "data-products-no-data-message": "Es sieht so aus, als hätten Sie noch keine Datenprodukte hinzugefügt, erstellen Sie eines!", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "Es sieht so aus, als hätten Sie noch keine Datenprodukte hinzugefügt, erstellen Sie eines!", "data-quality-test-contract-title": "Datenqualitätstests und -überprüfungen", "database-service-name-message": "Fügen Sie die Namen der Datenbankdienste hinzu, um die Lineage zu erstellen.", "dbt-catalog-file-extract-path": " Pfad zur dbt-Katalogdatei, um dbt-Modelle mit ihren Spaltenschemata zu extrahieren.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json index 6f06c597b1e..b105f57e39b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "Error al enviar el informe de información de datos.", "data-insight-report-send-success-message": "Informe de Información de Datos enviado correctamente.", "data-insight-subtitle": "Obtenga una vista global de la salud de todos sus activos de datos a lo largo del tiempo.", - "data-products-no-data-message": "Parece que no has agregado ningún producto de datos, ¡crea uno!", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "Parece que no has agregado ningún producto de datos, ¡crea uno!", "data-quality-test-contract-title": "Pruebas de calidad de datos y aserciones", "database-service-name-message": "Agregue los nombres de servicio de la base de datos para crear la dependencia del linaje.", "dbt-catalog-file-extract-path": "Ruta del archivo de catálogo de dbt para extraer modelos de dbt con sus esquemas de columnas.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json index b7f6c042056..ecc1cce9bb2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "Rapport de l'Aperçu des Données envoyé avec échec.", "data-insight-report-send-success-message": "Rapport de l'Aperçu des Données envoyé avec succès.", "data-insight-subtitle": "Obtenir une vue à 360 de la santé de vos actifs de données au fil du temps.", - "data-products-no-data-message": "Il semble que vous n'ayez ajouté aucun produit de données, créez-en un !", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "Il semble que vous n'ayez ajouté aucun produit de données, créez-en un !", "data-quality-test-contract-title": "Tests et assertions de qualité des données", "database-service-name-message": "Ajouter les noms de service de base de données pour créer la traçabilité.", "dbt-catalog-file-extract-path": "fichier catalog.yml de dbt pour extraire les modèles dbt avec leurs schémas de colonnes.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json index 145dcb8198b..97045230fe3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "Fallou o envío do informe de insights de datos.", "data-insight-report-send-success-message": "Informe de insights de datos enviado correctamente.", "data-insight-subtitle": "Obtén unha vista única da saúde de todos os teus activos de datos ao longo do tempo.", - "data-products-no-data-message": "Parece que non engadiches ningún produto de datos, crea un!", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "Parece que non engadiches ningún produto de datos, crea un!", "data-quality-test-contract-title": "Probas de calidade de datos e asercións", "database-service-name-message": "Engade os nomes dos servizos da base de datos para crear liñaxe.", "dbt-catalog-file-extract-path": "Ficheiro de catálogo dbt para extraer modelos dbt coas súas esquemas de columnas.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json index 0d96a8f82d5..37e7b30e383 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "נכשל בשליחת דוח תובנות נתונים.", "data-insight-report-send-success-message": "דוח תובנות נתונים נשלח בהצלחה.", "data-insight-subtitle": "קבל תצוגה מרוכזת של הבריאות של כל הנכסים שלך מעל לאורך זמן.", - "data-products-no-data-message": "נראה שלא הוספת מוצרי נתונים, צור אחד!", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "נראה שלא הוספת מוצרי נתונים, צור אחד!", "data-quality-test-contract-title": "בדיקות איכות נתונים ואישורים", "database-service-name-message": "הוסף את שמות שירותי מסדי הנתונים ליצירת הקשריות.", "dbt-catalog-file-extract-path": " נתיב קובץ קטלוג dbt לחשיפת מודלי dbt עם סכמות עמודותיהם.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json index 833f8a7cf58..c1be5242ab5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "データインサイトレポートの送信に失敗しました。", "data-insight-report-send-success-message": "データインサイトレポートが正常に送信されました。", "data-insight-subtitle": "すべてのデータアセットの健全性を一目で把握しましょう。", - "data-products-no-data-message": "データプロダクトを追加していないようです、作成してください!", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "データプロダクトを追加していないようです、作成してください!", "data-quality-test-contract-title": "Data quality tests and assertions", "database-service-name-message": "データベースサービス名を追加してリネージを作成します。", "dbt-catalog-file-extract-path": "dbt カタログファイルのパス(モデルとカラムスキーマを抽出)", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json index 9f9d8773fe1..5f8ef51c6e1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "데이터 인사이트 보고서 전송에 실패했습니다.", "data-insight-report-send-success-message": "데이터 인사이트 보고서가 성공적으로 전송되었습니다.", "data-insight-subtitle": "시간에 따른 모든 데이터 자산의 건강 상태를 단일 창에서 볼 수 있습니다.", - "data-products-no-data-message": "데이터 제품을 추가하지 않은 것 같습니다, 하나 만들어보세요!", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "데이터 제품을 추가하지 않은 것 같습니다, 하나 만들어보세요!", "data-quality-test-contract-title": "데이터 품질 테스트 및 검증", "database-service-name-message": "계보를 생성하려면 데이터베이스 서비스 이름을 추가하세요.", "dbt-catalog-file-extract-path": "열 스키마와 함께 dbt 모델을 추출하기 위한 dbt 카탈로그 파일입니다.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json index 246c43a99fa..68c3cfd047b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "डेटा अंतर्दृष्टी अहवाल पाठविण्यात अयशस्वी.", "data-insight-report-send-success-message": "डेटा अंतर्दृष्टी अहवाल यशस्वीरित्या पाठवला.", "data-insight-subtitle": "काळानुसार सर्व डेटा ॲसेटंच्या आरोग्याचे एकल दृश्य मिळवा.", - "data-products-no-data-message": "असे दिसते की तुम्ही कोणतेही डेटा उत्पादने जोडली नाहीत, एक तयार करा!", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "असे दिसते की तुम्ही कोणतेही डेटा उत्पादने जोडली नाहीत, एक तयार करा!", "data-quality-test-contract-title": "डेटा गुणवत्ता चाचण्या आणि पुष्टीकरणे", "database-service-name-message": "वंश तयार करण्यासाठी डेटाबेस सेवा नावे जोडा.", "dbt-catalog-file-extract-path": "dbt कॅटलॉग फाइल dbt मॉडेल्स त्यांच्या स्तंभ योजना सह काढण्यासाठी.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json index c50a8661501..23bb505c4aa 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "Kan het rapport voor inzicht data-assets niet verzenden.", "data-insight-report-send-success-message": "Rapport voor inzicht data-assets succesvol verzonden.", "data-insight-subtitle": "Bekijk in één overzicht de statushistorie van al uw data-assets.", - "data-products-no-data-message": "Het lijkt erop dat je geen dataproducten hebt toegevoegd, maak er een!", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "Het lijkt erop dat je geen dataproducten hebt toegevoegd, maak er een!", "data-quality-test-contract-title": "Datakwaliteitstests en -bevestigingen", "database-service-name-message": "Voeg de namen van databaseservices toe om herkomst vast te leggen.", "dbt-catalog-file-extract-path": "Pad naar dbt-catalogusbestand om dbt-modellen met hun kolomschema's te extraheren.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json index 2785ffbba06..b53f4ed1c47 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "ارسال گزارش بینش داده شکست خورد.", "data-insight-report-send-success-message": "گزارش بینش داده با موفقیت ارسال شد.", "data-insight-subtitle": "نمایی یکپارچه از سلامت تمام دارایی‌های داده‌ای شما در طول زمان.", - "data-products-no-data-message": "به نظر می‌رسد هیچ محصول داده‌ای اضافه نکرده‌اید، یکی بسازید!", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "به نظر می‌رسد هیچ محصول داده‌ای اضافه نکرده‌اید، یکی بسازید!", "data-quality-test-contract-title": "تست‌های کیفیت داده و تأییدها", "database-service-name-message": "نام‌های سرویس پایگاه داده را اضافه کنید تا خط‌مش را ایجاد کنید.", "dbt-catalog-file-extract-path": "فایل کاتالوگ dbt برای استخراج مدل‌های dbt با اسکیماهای ستون آنها.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json index 4012767bfde..36a7886051b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "Falha ao enviar relatório de insight de dados.", "data-insight-report-send-success-message": "Relatório de Insight de Dados enviado com sucesso.", "data-insight-subtitle": "Obtenha uma visão única da saúde de todos os seus ativos de dados ao longo do tempo.", - "data-products-no-data-message": "Parece que você não adicionou nenhum produto de dados, crie um!", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "Parece que você não adicionou nenhum produto de dados, crie um!", "data-quality-test-contract-title": "Testes de qualidade de dados e afirmações", "database-service-name-message": "Adicione os nomes dos serviços de banco de dados para criar linhagem.", "dbt-catalog-file-extract-path": "Arquivo de catálogo dbt para extrair modelos dbt com seus esquemas de coluna.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json index fc814858d48..3238f713418 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "Falha ao enviar relatório de insight de dados.", "data-insight-report-send-success-message": "Relatório de Insight de Dados enviado com sucesso.", "data-insight-subtitle": "Obtenha uma visão única da saúde de todos os seus ativos de dados ao longo do tempo.", - "data-products-no-data-message": "Parece que não adicionou nenhum produto de dados, crie um!", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "Parece que não adicionou nenhum produto de dados, crie um!", "data-quality-test-contract-title": "Testes de qualidade de dados e afirmações", "database-service-name-message": "Adicione os nomes dos serviços de banco de dados para criar linhagem.", "dbt-catalog-file-extract-path": "Arquivo de catálogo dbt para extrair modelos dbt com seus esquemas de coluna.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json index 11d03113495..320577a4522 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "Не удалось отправить отчет об анализе данных", "data-insight-report-send-success-message": "Отчет о анализе данных успешно отправлен.", "data-insight-subtitle": "Получите единое представление о состоянии всех ваших активов данных с течением времени.", - "data-products-no-data-message": "Похоже, вы не добавили никаких продуктов данных, создайте один!", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "Похоже, вы не добавили никаких продуктов данных, создайте один!", "data-quality-test-contract-title": "Тесты качества данных и утверждения", "database-service-name-message": "Добавьте имена сервиса базы данных, чтобы создать lineage.", "dbt-catalog-file-extract-path": "dbt для извлечения моделей dbt со схемами их столбцов.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json index 211b7e9ac27..5b6556885f6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "ส่งรายงานข้อมูลเชิงลึกล้มเหลว", "data-insight-report-send-success-message": "ส่งรายงานข้อมูลเชิงลึกสำเร็จ", "data-insight-subtitle": "ดูภาพรวมของสุขภาพสินทรัพย์ข้อมูลทั้งหมดของคุณตลอดเวลา", - "data-products-no-data-message": "ดูเหมือนว่าคุณยังไม่ได้เพิ่มผลิตภัณฑ์ข้อมูลใด ๆ สร้างหนึ่งรายการ!", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "ดูเหมือนว่าคุณยังไม่ได้เพิ่มผลิตภัณฑ์ข้อมูลใด ๆ สร้างหนึ่งรายการ!", "data-quality-test-contract-title": "Data quality tests and assertions", "database-service-name-message": "เพิ่มชื่อบริการฐานข้อมูลเพื่อสร้างลำดับชั้น", "dbt-catalog-file-extract-path": "ไฟล์ Catalog ของ dbt สำหรับดึงโมเดล dbt พร้อมกับสคีมาคอลัมน์ของพวกเขา", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json index 6157225084a..8c0a6fba134 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "Veri analizi raporu gönderilemedi.", "data-insight-report-send-success-message": "Veri Analizi Raporu başarıyla gönderildi.", "data-insight-subtitle": "Tüm veri varlıklarınızın zaman içindeki sağlığının tek bir görünümünü elde edin.", - "data-products-no-data-message": "Henüz bir veri ürünü eklememiş gibi görünüyor, bir tane oluşturun!", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "Henüz bir veri ürünü eklememiş gibi görünüyor, bir tane oluşturun!", "data-quality-test-contract-title": "Veri kalitesi testleri ve onaylar", "database-service-name-message": "Veri soyu oluşturmak için veritabanı servis adlarını ekleyin.", "dbt-catalog-file-extract-path": " dbt modellerini sütun şemalarıyla birlikte çıkarmak için dbt katalog dosyası.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json index 81a400dac05..d59cf05b675 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "数据洞察报告发送失败。", "data-insight-report-send-success-message": "数据洞察报告发送成功。", "data-insight-subtitle": "查看所有数据资产的健康状况", - "data-products-no-data-message": "看起来您还没有添加任何数据产品,创建一个!", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "看起来您还没有添加任何数据产品,创建一个!", "data-quality-test-contract-title": "数据质量测试和断言", "database-service-name-message": "添加数据库服务名称以创建血缘关系图", "dbt-catalog-file-extract-path": "dbt 目录文件, 用于提取带有 Column Schema (列模式) 的 dbt 模型", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json index a81127eedcb..312c3a4625f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json @@ -1990,8 +1990,8 @@ "data-insight-report-send-failed-message": "傳送資料洞察報告失敗。", "data-insight-report-send-success-message": "資料洞察報告傳送成功。", "data-insight-subtitle": "在一個單一窗格中檢視您所有資料資產隨時間變化的健康狀況。", - "data-products-no-data-message": "看起來您還沒有新增任何資料產品,建立一個!", "data-product-description": "Manage and organize your data products for better data governance.", + "data-products-no-data-message": "看起來您還沒有新增任何資料產品,建立一個!", "data-quality-test-contract-title": "資料品質測試與斷言", "database-service-name-message": "新增資料庫服務名稱以建立血緣。", "dbt-catalog-file-extract-path": "dbt 目錄檔案,用於擷取 dbt 模型及其欄位結構。",