feat(ui): Added recommendation on group modal (#5362)

This commit is contained in:
Ankit keshari 2022-07-08 21:49:31 +05:30 committed by GitHub
parent b927c0d219
commit 2543ff1eb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 95 additions and 75 deletions

View File

@ -1,11 +1,12 @@
import React, { useState } from 'react';
import React, { useRef, useState } from 'react';
import { message, Modal, Button, Form, Select, Tag } from 'antd';
import styled from 'styled-components';
import { useAddGroupMembersMutation } from '../../../graphql/group.generated';
import { CorpUser, EntityType, SearchResult } from '../../../types.generated';
import { CustomAvatar } from '../../shared/avatar';
import { CorpUser, Entity, EntityType } from '../../../types.generated';
import { useGetSearchResultsLazyQuery } from '../../../graphql/search.generated';
import { useEntityRegistry } from '../../useEntityRegistry';
import { useGetRecommendations } from '../../shared/recommendation';
import { OwnerLabel } from '../../shared/OwnerLabel';
type Props = {
urn: string;
@ -14,19 +15,6 @@ type Props = {
onSubmit: () => void;
};
const SearchResultContainer = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
padding: 2px;
`;
const SearchResultContent = styled.div`
display: flex;
justify-content: center;
align-items: center;
`;
const SelectInput = styled(Select)`
> .ant-select-selector {
height: 36px;
@ -43,20 +31,13 @@ const StyleTag = styled(Tag)`
export const AddGroupMembersModal = ({ urn, visible, onCloseModal, onSubmit }: Props) => {
const entityRegistry = useEntityRegistry();
const [selectedMembers, setSelectedMembers] = useState<string[]>([]);
const [selectedMembers, setSelectedMembers] = useState<any[]>([]);
const [inputValue, setInputValue] = useState('');
const [addGroupMembersMutation] = useAddGroupMembersMutation();
const [userSearch, { data: userSearchData }] = useGetSearchResultsLazyQuery();
const searchResults = userSearchData?.search?.searchResults || [];
const onSelectMember = (newMemberUrn: string) => {
const newUsers = [...(selectedMembers || []), newMemberUrn];
setSelectedMembers(newUsers);
};
const onDeselectMember = (memberUrn: string) => {
const newUserActors = selectedMembers.filter((user) => user !== memberUrn);
setSelectedMembers(newUserActors);
};
const searchResults = userSearchData?.search?.searchResults?.map((searchResult) => searchResult.entity) || [];
const [recommendedData] = useGetRecommendations([EntityType.CorpUser]);
const inputEl = useRef(null);
const handleUserSearch = (text: string) => {
if (text.length > 2) {
@ -74,19 +55,42 @@ export const AddGroupMembersModal = ({ urn, visible, onCloseModal, onSubmit }: P
};
// Renders a search result in the select dropdown.
const renderSearchResult = (result: SearchResult) => {
const avatarUrl = (result.entity as CorpUser).editableProperties?.pictureLink || undefined;
const displayName = entityRegistry.getDisplayName(result.entity.type, result.entity);
const renderSearchResult = (entity: Entity) => {
const avatarUrl = (entity as CorpUser).editableProperties?.pictureLink || undefined;
const displayName = entityRegistry.getDisplayName(entity.type, entity);
return (
<SearchResultContainer>
<SearchResultContent>
<CustomAvatar size={24} name={displayName} photoUrl={avatarUrl} isGroup={false} />
<div>{displayName}</div>
</SearchResultContent>
</SearchResultContainer>
<Select.Option value={entity.urn} key={entity.urn}>
<OwnerLabel name={displayName} avatarUrl={avatarUrl} type={entity.type} />
</Select.Option>
);
};
const groupResult = !inputValue || inputValue.length === 0 ? recommendedData : searchResults;
const groupSearchOptions = groupResult?.map((result) => {
return renderSearchResult(result);
});
const onModalClose = () => {
setInputValue('');
setSelectedMembers([]);
onCloseModal();
};
const onSelectMember = (newMemberUrn: string) => {
if (inputEl && inputEl.current) {
(inputEl.current as any).blur();
}
const newUsers = [...(selectedMembers || []), newMemberUrn];
setSelectedMembers(newUsers);
};
const onDeselectMember = (memberUrn: string) => {
setInputValue('');
const newUserActors = selectedMembers.filter((user) => user !== memberUrn);
setSelectedMembers(newUserActors);
};
const tagRender = (props) => {
// eslint-disable-next-line react/prop-types
const { label, closable, onClose } = props;
@ -102,38 +106,41 @@ export const AddGroupMembersModal = ({ urn, visible, onCloseModal, onSubmit }: P
};
const onAdd = async () => {
const selectedMemberUrns = selectedMembers.map((selectedMember) => selectedMember.value);
if (selectedMembers.length === 0) {
return;
}
addGroupMembersMutation({
variables: {
groupUrn: urn,
userUrns: selectedMembers,
},
})
.catch((e) => {
message.destroy();
message.error({ content: `Failed to add group members!: \n ${e.message || ''}`, duration: 3 });
})
.finally(() => {
message.success({
content: `Group members added!`,
duration: 3,
});
onSubmit();
setSelectedMembers([]);
try {
await addGroupMembersMutation({
variables: {
groupUrn: urn,
userUrns: selectedMemberUrns,
},
});
onCloseModal();
message.success({ content: 'Group members added!', duration: 3 });
} catch (e: unknown) {
message.destroy();
if (e instanceof Error) {
message.error({ content: `Failed to group members: \n ${e.message || ''}`, duration: 3 });
}
} finally {
onSubmit();
onModalClose();
}
};
function handleBlur() {
setInputValue('');
}
return (
<Modal
title="Add group members"
visible={visible}
onCancel={onCloseModal}
onCancel={onModalClose}
footer={
<>
<Button onClick={onCloseModal} type="text">
<Button onClick={onModalClose} type="text">
Cancel
</Button>
<Button disabled={selectedMembers.length === 0} onClick={onAdd}>
@ -145,20 +152,28 @@ export const AddGroupMembersModal = ({ urn, visible, onCloseModal, onSubmit }: P
<Form component={false}>
<Form.Item>
<SelectInput
showSearch
value={selectedMembers}
labelInValue
autoFocus
defaultOpen
mode="multiple"
filterOption={false}
ref={inputEl}
placeholder="Search for users..."
showSearch
filterOption={false}
defaultActiveFirstOption={false}
onSelect={(actorUrn: any) => onSelectMember(actorUrn)}
onDeselect={(actorUrn: any) => onDeselectMember(actorUrn)}
onSearch={handleUserSearch}
onSearch={(value: string) => {
// eslint-disable-next-line react/prop-types
handleUserSearch(value.trim());
// eslint-disable-next-line react/prop-types
setInputValue(value.trim());
}}
tagRender={tagRender}
onBlur={handleBlur}
value={selectedMembers}
>
{searchResults?.map((result) => (
<Select.Option value={result.entity.urn}>{renderSearchResult(result)}</Select.Option>
))}
{groupSearchOptions}
</SelectInput>
</Form.Item>
</Form>

View File

@ -129,7 +129,7 @@ export default function GroupMembers({ urn, pageSize, onChangeMembers }: Props)
setTimeout(function () {
// Reload the page.
refetch();
}, 2000);
}, 3000);
};
const onRemoveMember = (memberUrn: string) => {
@ -224,12 +224,14 @@ export default function GroupMembers({ urn, pageSize, onChangeMembers }: Props)
showSizeChanger={false}
/>
</Row>
<AddGroupMembersModal
urn={urn}
visible={isEditingMembers}
onSubmit={onAddMembers}
onCloseModal={() => setIsEditingMembers(false)}
/>
{isEditingMembers && (
<AddGroupMembersModal
urn={urn}
visible={isEditingMembers}
onSubmit={onAddMembers}
onCloseModal={() => setIsEditingMembers(false)}
/>
)}
</>
);
}

View File

@ -13,6 +13,7 @@ import { ProfileSidebarResizer } from '../entity/shared/containers/profile/sideb
import EmptyGlossarySection from './EmptyGlossarySection';
import CreateGlossaryEntityModal from '../entity/shared/EntityDropdown/CreateGlossaryEntityModal';
import { EntityType } from '../../types.generated';
import { Message } from '../shared/Message';
export const HeaderWrapper = styled(TabToolbar)`
padding: 15px 45px 10px 24px;
@ -41,8 +42,8 @@ export const MIN_BROWSWER_WIDTH = 200;
function BusinessGlossaryPage() {
const [browserWidth, setBrowserWidth] = useState(window.innerWidth * 0.2);
const { data: termsData, refetch: refetchForTerms } = useGetRootGlossaryTermsQuery();
const { data: nodesData, refetch: refetchForNodes } = useGetRootGlossaryNodesQuery();
const { data: termsData, refetch: refetchForTerms, loading: termsLoading } = useGetRootGlossaryTermsQuery();
const { data: nodesData, refetch: refetchForNodes, loading: nodesLoading } = useGetRootGlossaryNodesQuery();
const terms = termsData?.getRootGlossaryTerms?.terms;
const nodes = nodesData?.getRootGlossaryNodes?.nodes;
@ -55,6 +56,9 @@ function BusinessGlossaryPage() {
return (
<>
<GlossaryWrapper>
{(termsLoading || nodesLoading) && (
<Message type="loading" content="Loading Glossary..." style={{ marginTop: '10%' }} />
)}
<BrowserWrapper width={browserWidth}>
<GlossarySearch />
<GlossaryBrowser rootNodes={nodes} rootTerms={terms} />
@ -80,7 +84,7 @@ function BusinessGlossaryPage() {
</div>
</HeaderWrapper>
{hasTermsOrNodes && <GlossaryEntitiesList nodes={nodes || []} terms={terms || []} />}
{!hasTermsOrNodes && (
{!(termsLoading || nodesLoading) && !hasTermsOrNodes && (
<EmptyGlossarySection refetchForTerms={refetchForTerms} refetchForNodes={refetchForNodes} />
)}
</MainContentWrapper>

View File

@ -68,7 +68,6 @@ export const GroupList = () => {
// Hack to deal with eventual consistency.
const newRemovedUrns = [...removedUrns, urn];
setRemovedUrns(newRemovedUrns);
console.log(newRemovedUrns);
setTimeout(function () {
refetch?.();
}, 3000);