mirror of
https://github.com/datahub-project/datahub.git
synced 2025-12-26 09:26:22 +00:00
refactor(react): All entity search UI + misc improvements (#2233)
Co-authored-by: John Joyce <john@acryl.io>
This commit is contained in:
parent
f8a7f1ef96
commit
595d3672d2
@ -2,6 +2,7 @@ import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { EntityType } from '../../types.generated';
|
||||
import { BrowsableEntityPage } from '../browse/BrowsableEntityPage';
|
||||
import { SearchablePage } from '../search/SearchablePage';
|
||||
import { useEntityRegistry } from '../useEntityRegistry';
|
||||
|
||||
interface RouteParams {
|
||||
@ -18,10 +19,11 @@ interface Props {
|
||||
export const EntityPage = ({ entityType }: Props) => {
|
||||
const { urn } = useParams<RouteParams>();
|
||||
const entityRegistry = useEntityRegistry();
|
||||
|
||||
const isBrowsable = entityRegistry.getEntity(entityType).isBrowseEnabled();
|
||||
const ContainerPage = isBrowsable ? BrowsableEntityPage : SearchablePage;
|
||||
return (
|
||||
<BrowsableEntityPage urn={urn} type={entityType}>
|
||||
<ContainerPage urn={urn} type={entityType}>
|
||||
{entityRegistry.renderProfile(entityType, urn)}
|
||||
</BrowsableEntityPage>
|
||||
</ContainerPage>
|
||||
);
|
||||
};
|
||||
|
||||
@ -27,6 +27,10 @@ export default class EntityRegistry {
|
||||
this.pathNameToEntityType.set(entity.getPathName(), entity.type);
|
||||
}
|
||||
|
||||
getEntity(type: EntityType): Entity<any> {
|
||||
return validatedGet(type, this.entityTypeToEntity);
|
||||
}
|
||||
|
||||
getEntities(): Array<Entity<any>> {
|
||||
return this.entities;
|
||||
}
|
||||
|
||||
@ -27,6 +27,8 @@ const styles = {
|
||||
typeName: { color: '#585858' },
|
||||
platformName: { color: '#585858' },
|
||||
ownedBy: { color: '#585858' },
|
||||
description: { paddingLeft: 8, margin: 0 },
|
||||
noDescription: { color: '#d8d8d8' },
|
||||
};
|
||||
|
||||
export default function DefaultPreviewCard({
|
||||
@ -45,7 +47,7 @@ export default function DefaultPreviewCard({
|
||||
<Row style={styles.row} justify="space-between">
|
||||
<Space direction="vertical" align="start" size={28} style={styles.leftColumn}>
|
||||
<Link to={url}>
|
||||
<Space direction="horizontal" size={28} align="center">
|
||||
<Space direction="horizontal" size={20} align="center">
|
||||
{logoUrl && <Image style={styles.logoImage} src={logoUrl} preview />}
|
||||
<Space direction="vertical" size={8}>
|
||||
<Typography.Text strong style={styles.name}>
|
||||
@ -59,12 +61,20 @@ export default function DefaultPreviewCard({
|
||||
</Space>
|
||||
</Space>
|
||||
</Link>
|
||||
<Typography.Paragraph>{description}</Typography.Paragraph>
|
||||
{description.length === 0 ? (
|
||||
<Typography.Paragraph style={{ ...styles.description, ...styles.noDescription }}>
|
||||
No description
|
||||
</Typography.Paragraph>
|
||||
) : (
|
||||
<Typography.Paragraph style={styles.description}>{description}</Typography.Paragraph>
|
||||
)}
|
||||
</Space>
|
||||
<Space direction="vertical" align="end" size={36} style={styles.rightColumn}>
|
||||
<Space>
|
||||
<TagGroup globalTags={tags} />
|
||||
</Space>
|
||||
{tags && tags.tags?.length && (
|
||||
<Space>
|
||||
<TagGroup globalTags={tags} />
|
||||
</Space>
|
||||
)}
|
||||
<Space direction="vertical" size={12}>
|
||||
<Typography.Text strong>Owned By</Typography.Text>
|
||||
<Avatar.Group maxCount={4}>
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
import * as React from 'react';
|
||||
import { List } from 'antd';
|
||||
import { useGetAllEntitySearchResults } from '../../utils/customGraphQL/useGetAllEntitySearchResults';
|
||||
import { Message } from '../shared/Message';
|
||||
import { EntityGroupSearchResults } from './EntityGroupSearchResults';
|
||||
|
||||
interface Props {
|
||||
query: string;
|
||||
}
|
||||
|
||||
const RESULTS_PER_GROUP = 3;
|
||||
|
||||
export const AllEntitiesSearchResults = ({ query }: Props) => {
|
||||
const allSearchResultsByType = useGetAllEntitySearchResults({
|
||||
query,
|
||||
start: 0,
|
||||
count: RESULTS_PER_GROUP,
|
||||
filters: null,
|
||||
});
|
||||
|
||||
const loading = Object.keys(allSearchResultsByType).some((type) => {
|
||||
return allSearchResultsByType[type].loading;
|
||||
});
|
||||
|
||||
const noResults = Object.keys(allSearchResultsByType).every((type) => {
|
||||
return (
|
||||
!allSearchResultsByType[type].loading && allSearchResultsByType[type].data?.search?.entities.length === 0
|
||||
);
|
||||
});
|
||||
|
||||
const noResultsView = <List style={{ margin: '28px 160px' }} bordered dataSource={[]} />;
|
||||
|
||||
return (
|
||||
<>
|
||||
{loading && <Message type="loading" content="Loading..." style={{ marginTop: '10%' }} />}
|
||||
{noResults && noResultsView}
|
||||
{Object.keys(allSearchResultsByType).map((type: any) => {
|
||||
const entities = allSearchResultsByType[type].data?.search?.entities;
|
||||
if (entities && entities.length > 0) {
|
||||
return <EntityGroupSearchResults type={type} query={query} entities={entities} />;
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -2,7 +2,6 @@ import { ArrowRightOutlined } from '@ant-design/icons';
|
||||
import { Button, Card, Divider, List, Space, Typography } from 'antd';
|
||||
import * as React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useGetSearchResultsQuery } from '../../graphql/search.generated';
|
||||
import { EntityType } from '../../types.generated';
|
||||
import { IconStyleType } from '../entity/Entity';
|
||||
import { useEntityRegistry } from '../useEntityRegistry';
|
||||
@ -12,7 +11,7 @@ const styles = {
|
||||
header: { marginBottom: 20 },
|
||||
resultHeaderCardBody: { padding: '16px 24px' },
|
||||
resultHeaderCard: { right: '52px', top: '-40px', position: 'absolute' },
|
||||
resultList: { width: '100%', borderColor: '#f0f0f0', marginTop: '12px', padding: '16px 32px' },
|
||||
resultList: { width: '100%', borderColor: '#f0f0f0', marginTop: '8px', padding: '16px 48px' },
|
||||
seeAllButton: { fontSize: 18 },
|
||||
resultsContainer: { width: '100%', padding: '40px 132px' },
|
||||
};
|
||||
@ -20,45 +19,26 @@ const styles = {
|
||||
interface Props {
|
||||
type: EntityType;
|
||||
query: string;
|
||||
entities: Array<any>;
|
||||
}
|
||||
|
||||
const RESULTS_PER_GROUP = 3;
|
||||
|
||||
export const EntityGroupSearchResults = ({ type, query }: Props) => {
|
||||
export const EntityGroupSearchResults = ({ type, query, entities }: Props) => {
|
||||
const history = useHistory();
|
||||
const entityRegistry = useEntityRegistry();
|
||||
const { data } = useGetSearchResultsQuery({
|
||||
variables: {
|
||||
input: {
|
||||
type,
|
||||
query,
|
||||
start: 0,
|
||||
count: RESULTS_PER_GROUP,
|
||||
filters: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!data?.search?.entities.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const results = data?.search?.entities || [];
|
||||
|
||||
return (
|
||||
<Space direction="vertical" style={styles.resultsContainer}>
|
||||
<List
|
||||
header={
|
||||
<span style={styles.header}>
|
||||
<Typography.Title level={3}>{entityRegistry.getCollectionName(type)}</Typography.Title>
|
||||
<Typography.Title level={2}>{entityRegistry.getCollectionName(type)}</Typography.Title>
|
||||
<Card bodyStyle={styles.resultHeaderCardBody} style={styles.resultHeaderCard as any}>
|
||||
{entityRegistry.getIcon(type, 36, IconStyleType.ACCENT)}
|
||||
</Card>
|
||||
</span>
|
||||
}
|
||||
footer={
|
||||
data?.search &&
|
||||
data?.search?.total > 0 && (
|
||||
entities.length > 0 && (
|
||||
<Button
|
||||
type="text"
|
||||
style={styles.seeAllButton}
|
||||
@ -80,12 +60,12 @@ export const EntityGroupSearchResults = ({ type, query }: Props) => {
|
||||
)
|
||||
}
|
||||
style={styles.resultList}
|
||||
dataSource={results}
|
||||
dataSource={entities}
|
||||
split={false}
|
||||
renderItem={(item, index) => (
|
||||
<>
|
||||
<List.Item>{entityRegistry.renderSearchResult(type, item)}</List.Item>
|
||||
{index < results.length - 1 && <Divider />}
|
||||
{index < entities.length - 1 && <Divider />}
|
||||
</>
|
||||
)}
|
||||
bordered
|
||||
|
||||
@ -11,7 +11,7 @@ import useFilters from './utils/useFilters';
|
||||
import { navigateToSearchUrl } from './utils/navigateToSearchUrl';
|
||||
import { EntitySearchResults } from './EntitySearchResults';
|
||||
import { IconStyleType } from '../entity/Entity';
|
||||
import { EntityGroupSearchResults } from './EntityGroupSearchResults';
|
||||
import { AllEntitiesSearchResults } from './AllEntitiesSearchResults';
|
||||
|
||||
const ALL_ENTITIES_TAB_NAME = 'All';
|
||||
|
||||
@ -106,9 +106,7 @@ export const SearchPage = () => {
|
||||
onChangePage={onChangePage}
|
||||
/>
|
||||
) : (
|
||||
entityRegistry
|
||||
.getSearchEntityTypes()
|
||||
.map((entityType) => <EntityGroupSearchResults type={entityType} query={query} />)
|
||||
<AllEntitiesSearchResults query={query} />
|
||||
)}
|
||||
</SearchablePage>
|
||||
);
|
||||
|
||||
@ -1,46 +1,28 @@
|
||||
import { EntityType, SearchInput } from '../../types.generated';
|
||||
import { useGetSearchResultsQuery } from '../../graphql/search.generated';
|
||||
import { useEntityRegistry } from '../../app/useEntityRegistry';
|
||||
|
||||
type AllEntityInput<T, K> = Pick<T, Exclude<keyof T, keyof K>> & K;
|
||||
|
||||
export function useGetAllEntitySearchResults(input: AllEntityInput<SearchInput, { type?: EntityType }>) {
|
||||
const result: any = {};
|
||||
|
||||
result[EntityType.Chart] = useGetSearchResultsQuery({
|
||||
variables: {
|
||||
input: {
|
||||
type: EntityType.Chart,
|
||||
...input,
|
||||
},
|
||||
},
|
||||
});
|
||||
const entityRegistry = useEntityRegistry();
|
||||
|
||||
result[EntityType.Dashboard] = useGetSearchResultsQuery({
|
||||
variables: {
|
||||
input: {
|
||||
type: EntityType.Dashboard,
|
||||
...input,
|
||||
},
|
||||
},
|
||||
});
|
||||
const searchTypes = entityRegistry.getSearchEntityTypes();
|
||||
|
||||
result[EntityType.DataPlatform] = useGetSearchResultsQuery({
|
||||
variables: {
|
||||
input: {
|
||||
type: EntityType.DataPlatform,
|
||||
...input,
|
||||
for (let i = 0; i < searchTypes.length; i++) {
|
||||
const type = searchTypes[i];
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
result[type] = useGetSearchResultsQuery({
|
||||
variables: {
|
||||
input: {
|
||||
type,
|
||||
...input,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
result[EntityType.Dataset] = useGetSearchResultsQuery({
|
||||
variables: {
|
||||
input: {
|
||||
type: EntityType.Dataset,
|
||||
...input,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user