From fe11aa22d1f223e28355a368c55703bc5884ea13 Mon Sep 17 00:00:00 2001 From: karanh37 Date: Fri, 26 Sep 2025 13:59:03 +0530 Subject: [PATCH] persist filters in url and selection --- .../DataProduct/DataProductListPage.tsx | 2 + .../hooks/useDataProductListingData.tsx | 7 +- .../DomainDetailsPage.component.tsx | 287 ++++++++---------- .../SubDomainsTable.component.tsx | 8 +- .../hooks/useSubdomainListingData.tsx | 1 + .../DomainListing/DomainListPage.tsx | 2 + .../atoms/compositions/useListingData.tsx | 13 +- .../common/atoms/data/useUrlState.tsx | 27 +- .../domain/compositions/useDomainListing.tsx | 22 +- .../atoms/domain/ui/useDataProductFilters.tsx | 2 + .../atoms/domain/ui/useDomainFilters.tsx | 4 +- .../atoms/filters/useFilterSelection.tsx | 37 ++- .../common/atoms/filters/useQuickFilters.tsx | 110 ------- .../filters/useQuickFiltersWithComponent.tsx | 30 +- .../common/atoms/shared/types/index.ts | 2 + .../ui/src/constants/Domain.constants.ts | 1 + 16 files changed, 245 insertions(+), 310 deletions(-) delete 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/DataProduct/DataProductListPage.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/DataProductListPage.tsx index 55f6e957021..d0e749957d7 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 @@ -50,6 +50,7 @@ const DataProductListPage = () => { // Use the simplified data product filters configuration const { quickFilters, defaultFilters } = useDataProductFilters({ aggregations: dataProductListing.aggregations || undefined, + parsedFilters: dataProductListing.parsedFilters, onFilterChange: dataProductListing.handleFilterChange, }); @@ -57,6 +58,7 @@ const DataProductListPage = () => { const { filterSelectionDisplay } = useFilterSelection({ urlState: dataProductListing.urlState, filterConfigs: defaultFilters, + parsedFilters: dataProductListing.parsedFilters, onFilterChange: dataProductListing.handleFilterChange, }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/hooks/useDataProductListingData.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/hooks/useDataProductListingData.tsx index 3c3b9c25e01..2b6c780ea29 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/hooks/useDataProductListingData.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataProduct/hooks/useDataProductListingData.tsx @@ -13,7 +13,10 @@ import { useCallback, useMemo } from 'react'; import { TABLE_CARD_PAGE_SIZE } from '../../../constants/constants'; -import { DATAPRODUCT_DEFAULT_QUICK_FILTERS } from '../../../constants/DataProduct.constants'; +import { + DATAPRODUCT_DEFAULT_QUICK_FILTERS, + DATAPRODUCT_FILTERS, +} from '../../../constants/DataProduct.constants'; import { SearchIndex } from '../../../enums/search.enum'; import { DataProduct } from '../../../generated/entity/domains/dataProduct'; import { TagSource } from '../../../generated/type/tagLabel'; @@ -26,6 +29,7 @@ import { export const useDataProductListingData = (): ListingData => { const filterKeys = useMemo(() => DATAPRODUCT_DEFAULT_QUICK_FILTERS, []); + const filterConfigs = useMemo(() => DATAPRODUCT_FILTERS, []); const getGlossaryTags = useCallback( (dataProduct: DataProduct) => @@ -70,6 +74,7 @@ export const useDataProductListingData = (): ListingData => { baseFilter: '', // No parent filter for data products pageSize: TABLE_CARD_PAGE_SIZE, filterKeys, + filterConfigs, columns, renderers, basePath: '/dataProduct', diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx index 41322ef4187..51c6cea9818 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx @@ -88,7 +88,6 @@ import { getDomainDetailsPath, getDomainPath, getDomainVersionsPath, - getEntityDetailsPath, } from '../../../utils/RouterUtils'; import { escapeESReservedCharacters, @@ -139,69 +138,71 @@ const DomainDetailsPage = ({ const [subDomainForm] = Form.useForm(); const [isSubDomainLoading, setIsSubDomainLoading] = useState(false); - const { - formDrawer: subDomainDrawer, - openDrawer: openSubDomainDrawer, - closeDrawer: closeSubDomainDrawer, - } = useFormDrawerWithRef({ - title: t('label.add-entity', { entity: t('label.sub-domain') }), - anchor: 'right', - width: 670, - closeOnEscape: false, - form: ( - { - // No-op: Drawer close is handled by useFormDrawerWithRef - }} - onSubmit={async (formData: any) => { - setIsSubDomainLoading(true); - try { - formData.parent = domain.fullyQualifiedName; - await addDomains(formData); - showSuccessToast( - t('server.create-entity-success', { - entity: t('label.sub-domain'), - }) - ); - fetchSubDomainsCount(); - // Navigate to the subdomains tab - handleTabChange(EntityTabs.SUBDOMAINS); - closeSubDomainDrawer(); - } catch (error) { - showErrorToast( - getIsErrorMatch(error as AxiosError, ERROR_MESSAGE.alreadyExist) - ? t('server.entity-already-exist', { - entity: t('label.sub-domain'), - entityPlural: 'sub-domains', - name: formData.name, - }) - : (error as AxiosError), - t('server.add-entity-error', { - entity: t('label.sub-domain').toLowerCase(), - }) - ); - } finally { - setIsSubDomainLoading(false); - } - }} - /> - ), - formRef: subDomainForm, - onSubmit: () => { - // This is called by the drawer button, but actual submission - // happens via formRef.submit() which triggers form.onFinish - }, - loading: isSubDomainLoading, - }); - // Data product drawer implementation const [dataProductForm] = Form.useForm(); const [isDataProductLoading, setIsDataProductLoading] = useState(false); + const [showActions, setShowActions] = useState(false); + const [isDelete, setIsDelete] = useState(false); + const [isNameEditing, setIsNameEditing] = useState(false); + const [isStyleEditing, setIsStyleEditing] = useState(false); + const [previewAsset, setPreviewAsset] = + useState(); + const [assetCount, setAssetCount] = useState(0); + const [dataProductsCount, setDataProductsCount] = useState(0); + const [subDomainsCount, setSubDomainsCount] = useState(0); + const encodedFqn = getEncodedFqn( + escapeESReservedCharacters(domain.fullyQualifiedName) + ); + const { customizedPage, isLoading } = useCustomPages(PageType.Domain); + const [isTabExpanded, setIsTabExpanded] = useState(false); + const isSubDomain = useMemo(() => !isEmpty(domain.parent), [domain]); + + const queryFilter = useMemo(() => { + return getQueryFilterForDomain(domainFqn); + }, [domainFqn]); + + const isOwner = useMemo( + () => domain.owners?.some((owner) => isEqual(owner.id, currentUser?.id)), + [domain, currentUser] + ); + + const fetchDomainAssets = async () => { + if (domainFqn && !isVersionsView) { + try { + const res = await searchQuery({ + query: '', + pageNumber: 0, + pageSize: 0, + queryFilter, + searchIndex: SearchIndex.ALL, + filters: '', + }); + + const totalCount = res?.hits?.total.value ?? 0; + setAssetCount(totalCount); + } catch (error) { + setAssetCount(0); + showErrorToast( + error as AxiosError, + t('server.entity-fetch-error', { + entity: t('label.asset-plural-lowercase'), + }) + ); + } + } + }; + + const handleTabChange = (activeKey: string) => { + if (activeKey === 'assets') { + // refresh domain count when assets tab is selected + fetchDomainAssets(); + } + if (activeKey !== activeTab) { + navigate(getDomainDetailsPath(domainFqn, activeKey)); + } + }; + const { formDrawer: dataProductDrawer, openDrawer: openDataProductDrawer, @@ -221,11 +222,13 @@ const DomainDetailsPage = ({ onCancel={() => { // No-op: Drawer close is handled by useFormDrawerWithRef }} - onSubmit={async (formData: any) => { + onSubmit={async (formData: CreateDomain | CreateDataProduct) => { setIsDataProductLoading(true); try { - formData.domains = [domain.fullyQualifiedName]; - await addDataProducts(formData); + (formData as CreateDataProduct).domains = [ + domain.fullyQualifiedName ?? '', + ]; + await addDataProducts(formData as CreateDataProduct); showSuccessToast( t('server.create-entity-success', { entity: t('label.data-product'), @@ -262,26 +265,6 @@ const DomainDetailsPage = ({ }, loading: isDataProductLoading, }); - const [showActions, setShowActions] = useState(false); - const [isDelete, setIsDelete] = useState(false); - const [isNameEditing, setIsNameEditing] = useState(false); - const [isStyleEditing, setIsStyleEditing] = useState(false); - const [previewAsset, setPreviewAsset] = - useState(); - const [assetCount, setAssetCount] = useState(0); - const [dataProductsCount, setDataProductsCount] = useState(0); - const [subDomainsCount, setSubDomainsCount] = useState(0); - const encodedFqn = getEncodedFqn( - escapeESReservedCharacters(domain.fullyQualifiedName) - ); - const { customizedPage, isLoading } = useCustomPages(PageType.Domain); - const [isTabExpanded, setIsTabExpanded] = useState(false); - const isSubDomain = useMemo(() => !isEmpty(domain.parent), [domain]); - - const isOwner = useMemo( - () => domain.owners?.some((owner) => isEqual(owner.id, currentUser?.id)), - [domain, currentUser] - ); const breadcrumbs = useMemo(() => { if (!domainFqn) { @@ -328,6 +311,65 @@ const DomainDetailsPage = ({ } }, [domain, isVersionsView]); + const { + formDrawer: subDomainDrawer, + openDrawer: openSubDomainDrawer, + closeDrawer: closeSubDomainDrawer, + } = useFormDrawerWithRef({ + title: t('label.add-entity', { entity: t('label.sub-domain') }), + anchor: 'right', + width: 670, + closeOnEscape: false, + form: ( + { + // No-op: Drawer close is handled by useFormDrawerWithRef + }} + onSubmit={async (formData: CreateDomain | CreateDataProduct) => { + setIsSubDomainLoading(true); + try { + (formData as CreateDomain).parent = domain.fullyQualifiedName; + await addDomains(formData as CreateDomain); + showSuccessToast( + t('server.create-entity-success', { + entity: t('label.sub-domain'), + }) + ); + fetchSubDomainsCount(); + // Navigate to the subdomains tab + handleTabChange(EntityTabs.SUBDOMAINS); + closeSubDomainDrawer(); + } catch (error) { + showErrorToast( + getIsErrorMatch(error as AxiosError, ERROR_MESSAGE.alreadyExist) + ? t('server.entity-already-exist', { + entity: t('label.sub-domain'), + entityPlural: 'sub-domains', + name: formData.name, + }) + : (error as AxiosError), + t('server.add-entity-error', { + entity: t('label.sub-domain').toLowerCase(), + }) + ); + } finally { + setIsSubDomainLoading(false); + } + }} + /> + ), + formRef: subDomainForm, + onSubmit: () => { + // This is called by the drawer button, but actual submission + // happens via formRef.submit() which triggers form.onFinish + }, + loading: isSubDomainLoading, + }); + const editDisplayNamePermission = useMemo(() => { return getPrioritizedEditPermission( domainPermission, @@ -420,45 +462,6 @@ const DomainDetailsPage = ({ [domain, fetchSubDomainsCount] ); - const addDataProduct = useCallback( - async (formData: CreateDataProduct | CreateDomain) => { - if (!domain.fullyQualifiedName) { - return; - } - - const data = { - ...formData, - domains: [domain.fullyQualifiedName], - }; - - try { - const res = await addDataProducts(data); - navigate( - getEntityDetailsPath( - EntityType.DATA_PRODUCT, - res.fullyQualifiedName ?? '' - ) - ); - } catch (error) { - showErrorToast( - getIsErrorMatch(error as AxiosError, ERROR_MESSAGE.alreadyExist) - ? t('server.entity-already-exist', { - entity: t('label.sub-domain'), - entityPlural: t('label.sub-domain-lowercase-plural'), - name: data.name, - }) - : (error as AxiosError), - t('server.add-entity-error', { - entity: t('label.sub-domain-lowercase'), - }) - ); - } finally { - closeDataProductDrawer(); - } - }, - [domain] - ); - const handleVersionClick = async () => { const path = isVersionsView ? getDomainPath(domainFqn) @@ -493,32 +496,6 @@ const DomainDetailsPage = ({ } }; - const fetchDomainAssets = async () => { - if (domainFqn && !isVersionsView) { - try { - const res = await searchQuery({ - query: '', - pageNumber: 0, - pageSize: 0, - queryFilter, - searchIndex: SearchIndex.ALL, - filters: '', - }); - - const totalCount = res?.hits?.total.value ?? 0; - setAssetCount(totalCount); - } catch (error) { - setAssetCount(0); - showErrorToast( - error as AxiosError, - t('server.entity-fetch-error', { - entity: t('label.asset-plural-lowercase'), - }) - ); - } - } - }; - const fetchDomainPermission = async () => { try { const response = await getEntityPermission( @@ -531,16 +508,6 @@ const DomainDetailsPage = ({ } }; - const handleTabChange = (activeKey: string) => { - if (activeKey === 'assets') { - // refresh domain count when assets tab is selected - fetchDomainAssets(); - } - if (activeKey !== activeTab) { - navigate(getDomainDetailsPath(domainFqn, activeKey)); - } - }; - const onAddDataProduct = useCallback(() => { openDataProductDrawer(); }, [openDataProductDrawer]); @@ -658,10 +625,6 @@ const DomainDetailsPage = ({ : []), ]; - const queryFilter = useMemo(() => { - return getQueryFilterForDomain(domainFqn); - }, [domainFqn]); - const tabs = useMemo(() => { const tabLabelMap = getTabLabelMapFromTabs(customizedPage?.tabs); 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 b26cefd289c..f21a2382233 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 @@ -27,11 +27,7 @@ import { useDataTable } from '../../common/atoms/table/useDataTable'; import { useSubdomainListingData } from './hooks/useSubdomainListingData'; import { SubDomainsTableProps } from './SubDomainsTable.interface'; -const SubDomainsTable = ({ - domainFqn, - permissions, - onAddSubDomain, -}: SubDomainsTableProps) => { +const SubDomainsTable = ({ domainFqn }: SubDomainsTableProps) => { const { t } = useTranslation(); const theme = useTheme(); const subdomainListing = useSubdomainListingData({ @@ -42,6 +38,7 @@ const SubDomainsTable = ({ const { quickFilters, defaultFilters } = useDomainFilters({ isSubDomain: true, aggregations: subdomainListing.aggregations || undefined, + parsedFilters: subdomainListing.parsedFilters, onFilterChange: subdomainListing.handleFilterChange, }); @@ -49,6 +46,7 @@ const SubDomainsTable = ({ const { filterSelectionDisplay } = useFilterSelection({ urlState: subdomainListing.urlState, filterConfigs: defaultFilters, + parsedFilters: subdomainListing.parsedFilters, onFilterChange: subdomainListing.handleFilterChange, }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/SubDomainsTable/hooks/useSubdomainListingData.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Domain/SubDomainsTable/hooks/useSubdomainListingData.tsx index 86a222283e4..de3d3ab3870 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/SubDomainsTable/hooks/useSubdomainListingData.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/SubDomainsTable/hooks/useSubdomainListingData.tsx @@ -39,5 +39,6 @@ export const useSubdomainListingData = ({ return useDomainListing({ baseFilter: JSON.stringify(baseFilter), nameLabelKey: 'label.sub-domain', + isSubDomain: true, }); }; 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 4845a27c0bc..ac3639df952 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 @@ -50,6 +50,7 @@ const DomainListPage = () => { // Use the simplified domain filters configuration const { quickFilters, defaultFilters } = useDomainFilters({ aggregations: domainListing.aggregations || undefined, + parsedFilters: domainListing.parsedFilters, onFilterChange: domainListing.handleFilterChange, }); @@ -57,6 +58,7 @@ const DomainListPage = () => { const { filterSelectionDisplay } = useFilterSelection({ urlState: domainListing.urlState, filterConfigs: defaultFilters, + parsedFilters: domainListing.parsedFilters, onFilterChange: domainListing.handleFilterChange, }); 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 e9fa9615aa9..a23546b262e 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 @@ -27,6 +27,7 @@ interface UseListingDataProps { baseFilter?: string; pageSize?: number; filterKeys: string[]; + filterConfigs?: ExploreQuickFilterField[]; columns: ColumnConfig[]; renderers?: CellRenderer; basePath?: string; @@ -46,6 +47,7 @@ export const useListingData = < baseFilter = '', pageSize = 10, filterKeys, + filterConfigs, columns, renderers = {}, basePath, @@ -55,8 +57,14 @@ export const useListingData = < onCustomAddClick, } = props; - const urlStateHook = useUrlState({ filterKeys }); - const { urlState, setSearchQuery, setFilters, setCurrentPage } = urlStateHook; + const urlStateHook = useUrlState({ filterKeys, filterConfigs }); + const { + urlState, + parsedFilters, + setSearchQuery, + setFilters, + setCurrentPage, + } = urlStateHook; const dataFetching = useDataFetching({ searchIndex, @@ -159,6 +167,7 @@ export const useListingData = < isSelected: selectionState.isSelected, clearSelection: selectionState.clearSelection, urlState, + parsedFilters, actionHandlers, filterOptions: {}, aggregations: getAggregations(dataFetching.aggregations || {}), diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useUrlState.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useUrlState.tsx index 16100209cd7..f036dddacd5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useUrlState.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/data/useUrlState.tsx @@ -16,10 +16,17 @@ import { useSearchParams } from 'react-router-dom'; import { ExploreQuickFilterField } from '../../../Explore/ExplorePage.interface'; import { UrlState, UrlStateConfig, UrlStateHook } from '../shared/types'; -export const useUrlState = (config: UrlStateConfig): UrlStateHook => { +export const useUrlState = ( + config: UrlStateConfig & { filterConfigs?: ExploreQuickFilterField[] } +): UrlStateHook => { const [searchParams, setSearchParams] = useSearchParams(); - const { searchKey = 'q', filterKeys, pageKey = 'page' } = config; + const { + searchKey = 'q', + filterKeys, + pageKey = 'page', + filterConfigs, + } = config; const urlState: UrlState = useMemo(() => { const searchQuery = searchParams.get(searchKey) || ''; @@ -38,6 +45,21 @@ export const useUrlState = (config: UrlStateConfig): UrlStateHook => { }; }, [searchParams, searchKey, pageKey, filterKeys]); + const parsedFilters: ExploreQuickFilterField[] = useMemo(() => { + if (!filterConfigs) { + return []; + } + + return filterConfigs.map((filterConfig) => { + const values = urlState.filters[filterConfig.key] || []; + + return { + ...filterConfig, + value: values.map((v) => ({ key: v, label: v })), + }; + }); + }, [urlState.filters, filterConfigs]); + const updateUrlParams = useCallback( (updates: Record) => { setSearchParams((current) => { @@ -125,6 +147,7 @@ export const useUrlState = (config: UrlStateConfig): UrlStateHook => { return { urlState, + parsedFilters, setSearchQuery, setFilters, setCurrentPage, diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/compositions/useDomainListing.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/compositions/useDomainListing.tsx index 91a0cecc131..e0e507d779e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/compositions/useDomainListing.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/domain/compositions/useDomainListing.tsx @@ -13,7 +13,12 @@ import { useMemo } from 'react'; import { TABLE_CARD_PAGE_SIZE } from '../../../../../constants/constants'; -import { DOMAIN_DEFAULT_QUICK_FILTERS } from '../../../../../constants/Domain.constants'; +import { + DOMAIN_DEFAULT_QUICK_FILTERS, + DOMAIN_FILTERS, + SUBDOMAIN_DEFAULT_QUICK_FILTERS, + SUB_DOMAIN_FILTERS, +} from '../../../../../constants/Domain.constants'; import { SearchIndex } from '../../../../../enums/search.enum'; import { Domain } from '../../../../../generated/entity/domains/domain'; import { useListingData } from '../../compositions/useListingData'; @@ -32,6 +37,7 @@ interface UseDomainListingConfig { includeClassificationTags?: boolean; customColumns?: ColumnConfig[]; customRenderers?: CellRenderer; + isSubDomain?: boolean; } export const useDomainListing = ( @@ -48,6 +54,7 @@ export const useDomainListing = ( includeClassificationTags = true, customColumns = [], customRenderers = {}, + isSubDomain = false, } = config; // Memoize domain atom configurations to ensure stability (like DataProduct pattern) @@ -82,7 +89,17 @@ export const useDomainListing = ( const { columns } = useDomainColumns(domainColumnsConfig); const { renderers } = useDomainRenderers(domainRenderersConfig); // Define filterKeys for domain filters - const filterKeys = useMemo(() => DOMAIN_DEFAULT_QUICK_FILTERS, []); + const filterKeys = useMemo( + () => + isSubDomain + ? SUBDOMAIN_DEFAULT_QUICK_FILTERS + : DOMAIN_DEFAULT_QUICK_FILTERS, + [isSubDomain] + ); + const filterConfigs = useMemo( + () => (isSubDomain ? SUB_DOMAIN_FILTERS : DOMAIN_FILTERS), + [isSubDomain] + ); // Use generic listing composition with domain-specific configuration const listingData = useListingData({ @@ -90,6 +107,7 @@ export const useDomainListing = ( baseFilter, pageSize, filterKeys, + filterConfigs, columns, renderers, basePath, 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 index 7277c890483..eb3541c2474 100644 --- 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 @@ -20,6 +20,7 @@ import { useQuickFiltersWithComponent } from '../../filters/useQuickFiltersWithC interface UseDataProductFiltersConfig { aggregations?: Aggregations; + parsedFilters?: ExploreQuickFilterField[]; onFilterChange: (filters: ExploreQuickFilterField[]) => void; } @@ -27,6 +28,7 @@ export const useDataProductFilters = (config: UseDataProductFiltersConfig) => { const { quickFilters, selectedFilters } = useQuickFiltersWithComponent({ defaultFilters: DATAPRODUCT_FILTERS, aggregations: config.aggregations, + parsedFilters: config.parsedFilters, searchIndex: SearchIndex.DATA_PRODUCT, assetType: AssetsOfEntity.DATA_PRODUCT, onFilterChange: config.onFilterChange, 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 f452d044544..d2600c6a7d7 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 @@ -25,11 +25,12 @@ import { useQuickFiltersWithComponent } from '../../filters/useQuickFiltersWithC interface UseDomainFiltersConfig { isSubDomain?: boolean; aggregations?: Aggregations; + parsedFilters?: ExploreQuickFilterField[]; onFilterChange: (filters: ExploreQuickFilterField[]) => void; } export const useDomainFilters = (config: UseDomainFiltersConfig) => { - const { isSubDomain = false } = config; + const { isSubDomain = false, parsedFilters } = config; const defaultFilters = useMemo( () => (isSubDomain ? SUB_DOMAIN_FILTERS : DOMAIN_FILTERS), @@ -39,6 +40,7 @@ export const useDomainFilters = (config: UseDomainFiltersConfig) => { const { quickFilters, selectedFilters } = useQuickFiltersWithComponent({ defaultFilters, aggregations: config.aggregations, + parsedFilters, searchIndex: SearchIndex.DOMAIN, assetType: AssetsOfEntity.DOMAIN, onFilterChange: config.onFilterChange, diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useFilterSelection.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useFilterSelection.tsx index 2c00f139747..9a6c00586aa 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useFilterSelection.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useFilterSelection.tsx @@ -27,6 +27,7 @@ interface FilterConfig { interface FilterSelectionConfig { urlState: UrlState; filterConfigs: FilterConfig[]; + parsedFilters?: ExploreQuickFilterField[]; onFilterChange: (filters: ExploreQuickFilterField[]) => void; } @@ -38,26 +39,40 @@ interface FilterSelectionItem { export const useFilterSelection = (config: FilterSelectionConfig) => { const { t } = useTranslation(); - const { urlState, filterConfigs, onFilterChange } = config; + const { urlState, filterConfigs, parsedFilters, onFilterChange } = config; const selectedFilters = useMemo(() => { const filters: FilterSelectionItem[] = []; - Object.entries(urlState.filters).forEach(([filterKey, values]) => { - if (values && values.length > 0) { - const filterConfig = filterConfigs.find((fc) => fc.key === filterKey); - if (filterConfig) { + // Use parsedFilters if available for consistent values + if (parsedFilters) { + parsedFilters.forEach((filter) => { + if (filter.value && filter.value.length > 0) { filters.push({ - key: filterKey, - label: filterConfig.label, - values, + key: filter.key, + label: filter.label, + values: filter.value.map((v) => v.label || v.key), }); } - } - }); + }); + } else { + // Fallback to urlState.filters + Object.entries(urlState.filters).forEach(([filterKey, values]) => { + if (values && values.length > 0) { + const filterConfig = filterConfigs.find((fc) => fc.key === filterKey); + if (filterConfig) { + filters.push({ + key: filterKey, + label: filterConfig.label, + values, + }); + } + } + }); + } return filters; - }, [urlState.filters, filterConfigs]); + }, [urlState.filters, filterConfigs, parsedFilters]); const handleRemoveFilter = useCallback( (filterKey: string) => { 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 deleted file mode 100644 index 0401f8e1c68..00000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useQuickFilters.tsx +++ /dev/null @@ -1,110 +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, 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/filters/useQuickFiltersWithComponent.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useQuickFiltersWithComponent.tsx index c3ac127db92..9508e0488e9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useQuickFiltersWithComponent.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/filters/useQuickFiltersWithComponent.tsx @@ -21,6 +21,7 @@ import { AssetsOfEntity } from '../../../Glossary/GlossaryTerms/tabs/AssetsTabs. interface QuickFiltersWithComponentConfig { defaultFilters: ExploreQuickFilterField[]; aggregations?: Aggregations; + parsedFilters?: ExploreQuickFilterField[]; searchIndex: SearchIndex; assetType: AssetsOfEntity; onFilterChange: (filters: ExploreQuickFilterField[]) => void; @@ -34,23 +35,25 @@ export const useQuickFiltersWithComponent = ( >([]); useEffect(() => { - setSelectedQuickFilters(config.defaultFilters); - }, [config.defaultFilters]); + // Use parsedFilters if available (from URL), otherwise use defaultFilters + if (config.parsedFilters && config.parsedFilters.length > 0) { + // Merge parsedFilters with defaultFilters to maintain structure + const mergedFilters = config.defaultFilters.map((defaultFilter) => { + const parsedFilter = config.parsedFilters?.find( + (pf) => pf.key === defaultFilter.key + ); + + return parsedFilter || defaultFilter; + }); + setSelectedQuickFilters(mergedFilters); + } else { + setSelectedQuickFilters(config.defaultFilters); + } + }, [config.defaultFilters, config.parsedFilters]); const handleQuickFiltersChange = useCallback( (data: ExploreQuickFilterField[]) => { config.onFilterChange(data); - - // data.forEach((filter) => { - // if (filter.value && filter.value.length > 0) { - // config.onFilterChange( - // filter.key, - // filter.value as unknown as string[] - // ); - // } else { - // config.onFilterChange(filter.key, []); - // } - // }); }, [config.onFilterChange] ); @@ -77,7 +80,6 @@ export const useQuickFiltersWithComponent = ( const quickFilters = useMemo( () => ( void; setFilters: (filters: ExploreQuickFilterField[]) => void; setCurrentPage: (page: number) => void; @@ -123,6 +124,7 @@ export interface ListingData { isSelected: (id: string) => boolean; clearSelection: () => void; urlState: UrlState; + parsedFilters: ExploreQuickFilterField[]; actionHandlers: ActionHandlers; filterOptions?: FilterOptions; aggregations?: Aggregations | null; diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/Domain.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/Domain.constants.ts index 16bdefa9419..32199d36b24 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/Domain.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/Domain.constants.ts @@ -85,6 +85,7 @@ export const SUBDOMAIN_DEFAULT_QUICK_FILTERS = [ EntityFields.OWNERS, EntityFields.CLASSIFICATION_TAGS, EntityFields.GLOSSARY_TERMS, + EntityFields.DOMAIN_TYPE, ]; export const DOMAIN_FILTERS = [