fix(ui): add warning for view all modal (#14702)

This commit is contained in:
Aseem Bansal 2025-09-08 19:27:41 +05:30 committed by GitHub
parent 9105241bfd
commit 3e1e9fcf34
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 123 additions and 10 deletions

View File

@ -78,6 +78,8 @@ export enum EventType {
IngestionTestConnectionEvent, IngestionTestConnectionEvent,
IngestionExecutionResultViewedEvent, IngestionExecutionResultViewedEvent,
IngestionSourceConfigurationImpressionEvent, IngestionSourceConfigurationImpressionEvent,
IngestionViewAllClickEvent,
IngestionViewAllClickWarningEvent,
CreateIngestionSourceEvent, CreateIngestionSourceEvent,
UpdateIngestionSourceEvent, UpdateIngestionSourceEvent,
DeleteIngestionSourceEvent, DeleteIngestionSourceEvent,
@ -620,6 +622,16 @@ export interface IngestionTestConnectionEvent extends BaseEvent {
outcome?: string; outcome?: string;
} }
export interface IngestionViewAllClickEvent extends BaseEvent {
type: EventType.IngestionViewAllClickEvent;
executionUrn?: string;
}
export interface IngestionViewAllClickWarningEvent extends BaseEvent {
type: EventType.IngestionViewAllClickWarningEvent;
executionUrn?: string;
}
export interface IngestionExecutionResultViewedEvent extends BaseEvent { export interface IngestionExecutionResultViewedEvent extends BaseEvent {
type: EventType.IngestionExecutionResultViewedEvent; type: EventType.IngestionExecutionResultViewedEvent;
executionUrn: string; executionUrn: string;
@ -1249,4 +1261,6 @@ export type Event =
| ProductTourButtonClickEvent | ProductTourButtonClickEvent
| IngestionTestConnectionEvent | IngestionTestConnectionEvent
| IngestionExecutionResultViewedEvent | IngestionExecutionResultViewedEvent
| IngestionSourceConfigurationImpressionEvent; | IngestionSourceConfigurationImpressionEvent
| IngestionViewAllClickEvent
| IngestionViewAllClickWarningEvent;

View File

@ -109,6 +109,8 @@ type Props = {
applyView?: boolean; applyView?: boolean;
onLineageClick?: () => void; onLineageClick?: () => void;
isLineageTab?: boolean; isLineageTab?: boolean;
isViewAllMode?: boolean | false;
handleViewAllClickWarning?: () => void;
}; };
export const EmbeddedListSearch = ({ export const EmbeddedListSearch = ({
@ -139,6 +141,8 @@ export const EmbeddedListSearch = ({
applyView = false, applyView = false,
onLineageClick, onLineageClick,
isLineageTab = false, isLineageTab = false,
isViewAllMode = false,
handleViewAllClickWarning,
}: Props) => { }: Props) => {
const { shouldRefetchEmbeddedListSearch, setShouldRefetchEmbeddedListSearch } = useEntityContext(); const { shouldRefetchEmbeddedListSearch, setShouldRefetchEmbeddedListSearch } = useEntityContext();
// Adjust query based on props // Adjust query based on props
@ -348,6 +352,8 @@ export const EmbeddedListSearch = ({
setSelectedEntities={setSelectedEntities} setSelectedEntities={setSelectedEntities}
entityAction={entityAction} entityAction={entityAction}
applyView={applyView} applyView={applyView}
isViewAllMode={isViewAllMode}
handleViewAllClickWarning={handleViewAllClickWarning}
/> />
</Container> </Container>
); );

View File

@ -33,6 +33,8 @@ type Props = {
searchBarInputStyle?: any; searchBarInputStyle?: any;
entityAction?: React.FC<EntityActionProps>; entityAction?: React.FC<EntityActionProps>;
applyView?: boolean; applyView?: boolean;
isViewAllMode?: boolean | false;
handleViewAllClickWarning?: () => void;
}; };
export const EmbeddedListSearchModal = ({ export const EmbeddedListSearchModal = ({
@ -48,6 +50,8 @@ export const EmbeddedListSearchModal = ({
searchBarInputStyle, searchBarInputStyle,
entityAction, entityAction,
applyView, applyView,
isViewAllMode,
handleViewAllClickWarning,
}: Props) => { }: Props) => {
// Component state // Component state
const [query, setQuery] = useState<string>(''); const [query, setQuery] = useState<string>('');
@ -98,6 +102,8 @@ export const EmbeddedListSearchModal = ({
searchBarInputStyle={searchBarInputStyle} searchBarInputStyle={searchBarInputStyle}
entityAction={entityAction} entityAction={entityAction}
applyView={applyView} applyView={applyView}
isViewAllMode={isViewAllMode}
handleViewAllClickWarning={handleViewAllClickWarning}
/> />
</SearchContainer> </SearchContainer>
</Modal> </Modal>

View File

@ -1,6 +1,8 @@
import { LoadingOutlined } from '@ant-design/icons'; import { ExclamationCircleFilled, LoadingOutlined } from '@ant-design/icons';
import { Text, colors } from '@components';
import { Button, Pagination, Spin, Typography } from 'antd'; import { Button, Pagination, Spin, Typography } from 'antd';
import React from 'react'; import React from 'react';
import { useHistory } from 'react-router';
import styled from 'styled-components'; import styled from 'styled-components';
import { import {
@ -13,10 +15,11 @@ import { EntityAndType } from '@app/entity/shared/types';
import { SearchFiltersSection } from '@app/search/SearchFiltersSection'; import { SearchFiltersSection } from '@app/search/SearchFiltersSection';
import { combineSiblingsInSearchResults } from '@app/search/utils/combineSiblingsInSearchResults'; import { combineSiblingsInSearchResults } from '@app/search/utils/combineSiblingsInSearchResults';
import { UnionType } from '@app/search/utils/constants'; import { UnionType } from '@app/search/utils/constants';
import { navigateToSearchUrl } from '@app/searchV2/utils/navigateToSearchUrl';
import { useIsShowSeparateSiblingsEnabled } from '@app/useAppConfig'; import { useIsShowSeparateSiblingsEnabled } from '@app/useAppConfig';
import { SearchCfg } from '@src/conf'; import { SearchCfg } from '@src/conf';
import { FacetFilterInput, FacetMetadata, SearchResults as SearchResultType } from '@types'; import { Dataset, FacetFilterInput, FacetMetadata, SearchResults as SearchResultType } from '@types';
const SearchBody = styled.div` const SearchBody = styled.div`
height: 100%; height: 100%;
@ -82,6 +85,17 @@ const ErrorMessage = styled.div`
flex: 1; flex: 1;
`; `;
const WarningMessage = styled.div`
gap: 8px;
padding: 8px;
display: flex;
margin: 8px 16px 0 16px
align-items: center;
color: ${colors.yellow[1000]};
background-color: ${colors.yellow[0]};
border-radius: 8px;
`;
const StyledLinkButton = styled(Button)` const StyledLinkButton = styled(Button)`
margin: 0 -14px; margin: 0 -14px;
font-size: 16px; font-size: 16px;
@ -110,8 +124,14 @@ interface Props {
onClickLessHops?: () => void; onClickLessHops?: () => void;
onLineageClick?: () => void; onLineageClick?: () => void;
isLineageTab?: boolean; isLineageTab?: boolean;
isViewAllMode?: boolean | false;
handleViewAllClickWarning?: () => void;
} }
const getPlatformUrnFromSearchResponse = (searchResponse: SearchResultType | null | undefined) => {
return searchResponse?.facets?.find((facet) => facet.field === 'platform')?.aggregations?.[0]?.value;
};
export const EmbeddedListSearchResults = ({ export const EmbeddedListSearchResults = ({
page, page,
searchResponse, searchResponse,
@ -135,7 +155,10 @@ export const EmbeddedListSearchResults = ({
onClickLessHops, onClickLessHops,
onLineageClick, onLineageClick,
isLineageTab = false, isLineageTab = false,
isViewAllMode = false,
handleViewAllClickWarning,
}: Props) => { }: Props) => {
const history = useHistory();
const showSeparateSiblings = useIsShowSeparateSiblingsEnabled(); const showSeparateSiblings = useIsShowSeparateSiblingsEnabled();
const combinedSiblingSearchResults = combineSiblingsInSearchResults( const combinedSiblingSearchResults = combineSiblingsInSearchResults(
showSeparateSiblings, showSeparateSiblings,
@ -147,6 +170,28 @@ export const EmbeddedListSearchResults = ({
const totalResults = searchResponse?.total || 0; const totalResults = searchResponse?.total || 0;
const lastResultIndex = pageStart + pageSize > totalResults ? totalResults : pageStart + pageSize; const lastResultIndex = pageStart + pageSize > totalResults ? totalResults : pageStart + pageSize;
const platformUrn = getPlatformUrnFromSearchResponse(searchResponse);
let platform: string | null = null;
try {
platform = (combinedSiblingSearchResults?.[0]?.entity as Dataset).platform?.name;
} catch (error) {
console.error('Error getting platform from search response', error);
}
const handleSearchAllAssetsClick = () => {
handleViewAllClickWarning?.();
if (platformUrn) {
const platformFilter: FacetFilterInput = {
field: 'platform',
values: [platformUrn],
};
navigateToSearchUrl({
filters: [platformFilter],
history,
});
}
};
return ( return (
<> <>
<SearchBody> <SearchBody>
@ -180,6 +225,30 @@ export const EmbeddedListSearchResults = ({
</StyledLinkButton> </StyledLinkButton>
</ErrorMessage> </ErrorMessage>
)} )}
{isViewAllMode && (
<WarningMessage>
<ExclamationCircleFilled style={{ color: colors.yellow[1000], fontSize: 16 }} />
<Text weight="bold" style={{ lineHeight: 'normal' }}>
Results may be incomplete.{' '}
{platform && (
<span
onClick={handleSearchAllAssetsClick}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleSearchAllAssetsClick();
}
}}
role="button"
tabIndex={0}
style={{ cursor: 'pointer', textDecoration: 'underline' }}
>
Search all ingested {platform} assets
</span>
)}
</Text>
</WarningMessage>
)}
{!loading && !isServerOverloadError && ( {!loading && !isServerOverloadError && (
<EntitySearchResults <EntitySearchResults
searchResults={combinedSiblingSearchResults || []} searchResults={combinedSiblingSearchResults || []}

View File

@ -100,7 +100,7 @@ export const SummaryTab = ({
)} )}
<IngestedAssetsSection> <IngestedAssetsSection>
{data?.executionRequest?.id && ( {data?.executionRequest?.id && (
<IngestedAssets executionResult={result} id={data?.executionRequest?.id} /> <IngestedAssets executionResult={result} id={data?.executionRequest?.id} urn={urn} />
)} )}
</IngestedAssetsSection> </IngestedAssetsSection>
<SectionBase> <SectionBase>

View File

@ -1,6 +1,8 @@
import React, { useMemo, useState } from 'react'; import React, { useMemo, useState } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import analytics from '@app/analytics';
import { EventType } from '@app/analytics/event';
import { EmbeddedListSearchModal } from '@app/entity/shared/components/styled/search/EmbeddedListSearchModal'; import { EmbeddedListSearchModal } from '@app/entity/shared/components/styled/search/EmbeddedListSearchModal';
import { import {
extractEntityTypeCountsFromFacets, extractEntityTypeCountsFromFacets,
@ -107,6 +109,7 @@ const IngestionRowCount = styled(Text)`
type Props = { type Props = {
id: string; id: string;
executionResult?: Maybe<Partial<ExecutionRequestResult>>; executionResult?: Maybe<Partial<ExecutionRequestResult>>;
urn: string;
}; };
const ENTITY_FACET_NAME = 'entity'; const ENTITY_FACET_NAME = 'entity';
@ -153,12 +156,27 @@ const IngestionContents: React.FC<RenderIngestionContentsProps> = ({ items, getK
</IngestionBoxesContainer> </IngestionBoxesContainer>
); );
export default function IngestedAssets({ id, executionResult }: Props) { export default function IngestedAssets({ id, executionResult, urn }: Props) {
const entityRegistry = useEntityRegistry(); const entityRegistry = useEntityRegistry();
// First thing to do is to search for all assets with the id as the run id! // First thing to do is to search for all assets with the id as the run id!
const [showAssetSearch, setShowAssetSearch] = useState(false); const [showAssetSearch, setShowAssetSearch] = useState(false);
const handleViewAllClick = () => {
analytics.event({
type: EventType.IngestionViewAllClickEvent,
executionUrn: urn,
});
setShowAssetSearch(true);
};
const handleViewAllClickWarning = () => {
analytics.event({
type: EventType.IngestionViewAllClickWarningEvent,
executionUrn: urn,
});
};
// Try getting the counts via the ingestion report. // Try getting the counts via the ingestion report.
const totalEntitiesIngested = executionResult && getTotalEntitiesIngested(executionResult, entityRegistry); const totalEntitiesIngested = executionResult && getTotalEntitiesIngested(executionResult, entityRegistry);
const entitiesIngestedByTypeFromReport = const entitiesIngestedByTypeFromReport =
@ -245,11 +263,7 @@ export default function IngestedAssets({ id, executionResult }: Props) {
<Card <Card
title={formatNumber(total)} title={formatNumber(total)}
button={ button={
<Button <Button style={{ width: '110px' }} variant="text" onClick={handleViewAllClick}>
style={{ width: '110px' }}
variant="text"
onClick={() => setShowAssetSearch(true)}
>
View All View All
</Button> </Button>
} }
@ -323,6 +337,8 @@ export default function IngestedAssets({ id, executionResult }: Props) {
unionType: UnionType.AND, unionType: UnionType.AND,
filters: [{ field: 'runId', values: [id] }], filters: [{ field: 'runId', values: [id] }],
}} }}
isViewAllMode
handleViewAllClickWarning={handleViewAllClickWarning}
onClose={() => setShowAssetSearch(false)} onClose={() => setShowAssetSearch(false)}
/> />
)} )}

View File

@ -58,6 +58,8 @@ public enum DataHubUsageEventType {
INGESTION_TEST_CONNECTION_EVENT("IngestionTestConnectionEvent"), INGESTION_TEST_CONNECTION_EVENT("IngestionTestConnectionEvent"),
INGESTION_EXECUTION_RESULT_VIEWED_EVENT("IngestionExecutionResultViewedEvent"), INGESTION_EXECUTION_RESULT_VIEWED_EVENT("IngestionExecutionResultViewedEvent"),
INGESTION_SOURCE_CONFIGURATION_IMPRESSION_EVENT("IngestionSourceConfigurationImpressionEvent"), INGESTION_SOURCE_CONFIGURATION_IMPRESSION_EVENT("IngestionSourceConfigurationImpressionEvent"),
INGESTION_VIEW_ALL_CLICK_EVENT("IngestionViewAllClickEvent"),
INGESTION_VIEW_ALL_CLICK_WARNING_EVENT("IngestionViewAllClickWarningEvent"),
CREATE_INGESTION_SOURCE_EVENT("CreateIngestionSourceEvent"), CREATE_INGESTION_SOURCE_EVENT("CreateIngestionSourceEvent"),
UPDATE_INGESTION_SOURCE_EVENT("UpdateIngestionSourceEvent"), UPDATE_INGESTION_SOURCE_EVENT("UpdateIngestionSourceEvent"),
DELETE_INGESTION_SOURCE_EVENT("DeleteIngestionSourceEvent"), DELETE_INGESTION_SOURCE_EVENT("DeleteIngestionSourceEvent"),