Skip to content

Commit

Permalink
fix(chat): fix search for select folder and change path dialogs, fold…
Browse files Browse the repository at this point in the history
…ers order for change path dialog (Issue #1687) (#1697)
  • Loading branch information
Alexander-Kezik authored Jul 4, 2024
1 parent 2060fe2 commit d1d12a6
Show file tree
Hide file tree
Showing 12 changed files with 270 additions and 122 deletions.
15 changes: 14 additions & 1 deletion apps/chat/src/components/Chat/ChangePathDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
PromptsActions,
PromptsSelectors,
} from '@/src/store/prompts/prompts.reducers';
import { PublicationActions } from '@/src/store/publication/publication.reducers';
import { UIActions } from '@/src/store/ui/ui.reducers';

import {
Expand Down Expand Up @@ -69,10 +70,22 @@ export const ChangePathDialog = ({

const newFolderId = useAppSelector(selectors.selectNewAddedFolderId);
const folders = useAppSelector((state) =>
selectors.selectTemporaryAndFilteredFolders(state, searchQuery),
selectors.selectTemporaryAndPublishedFolders(state, searchQuery),
);
const loadingFolderIds = useAppSelector(selectors.selectLoadingFolderIds);

useEffect(() => {
dispatch(
PublicationActions.uploadAllPublishedWithMeItems({
featureType:
type === SharingType.Conversation ||
type === SharingType.ConversationFolder
? FeatureType.Chat
: FeatureType.Prompt,
}),
);
}, [dispatch, type]);

useEffect(() => {
if (!isOpen) {
setSearchQuery('');
Expand Down
5 changes: 4 additions & 1 deletion apps/chat/src/components/Common/CollapsibleSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface CollapsibleSectionProps {
onToggle?: (isOpen: boolean) => void;
className?: string;
showOnHoverOnly?: boolean;
togglerClassName?: string;
sectionTooltip?: ReactNode;
}

Expand All @@ -32,6 +33,7 @@ export default function CollapsibleSection({
onToggle,
className,
showOnHoverOnly,
togglerClassName,
sectionTooltip,
}: CollapsibleSectionProps) {
const [isOpened, setIsOpened] = useState(openByDefault);
Expand All @@ -50,10 +52,11 @@ export default function CollapsibleSection({
onClick={handleClick}
data-qa={dataQa}
className={classNames(
'flex cursor-pointer items-center gap-1 whitespace-pre text-xs',
'flex cursor-pointer items-center gap-1 whitespace-pre py-1 text-xs',
isHighlighted
? 'text-accent-primary'
: '[&:not(:hover)]:text-secondary',
togglerClassName,
)}
>
<CaretIconComponent
Expand Down
103 changes: 44 additions & 59 deletions apps/chat/src/components/Common/SelectFolder/SelectFolderList.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import { useMemo } from 'react';

import { useTranslation } from 'next-i18next';

import classNames from 'classnames';

import { Conversation } from '@/src/types/chat';
import { DialFile } from '@/src/types/files';
import { Prompt } from '@/src/types/prompt';
import { Translation } from '@/src/types/translation';

import { MAX_CONVERSATION_AND_PROMPT_FOLDERS_DEPTH } from '@/src/constants/folders';

import CaretIconComponent from '@/src/components/Common/CaretIconComponent';
import { NoResultsFound } from '@/src/components/Common/NoResultsFound';
import Folder, { FolderProps } from '@/src/components/Folder/Folder';

import CollapsibleSection from '../CollapsibleSection';
import { NoData } from '../NoData';

interface Props<T, P = unknown> {
Expand All @@ -38,76 +35,64 @@ export const SelectFolderList = <T extends Conversation | Prompt | DialFile>({
rootFolderName,
rootFolderId,
}: Props<T>) => {
const { t } = useTranslation(Translation.Chat);

const highlightedFolders = useMemo(
() => [selectedFolderId].filter(Boolean) as string[],
[selectedFolderId],
);

const filteredFolders = useMemo(
() =>
folderProps.searchTerm
? folderProps.allFolders.filter((folder) =>
folder.name
.toLowerCase()
.includes(folderProps.searchTerm.toLowerCase()),
)
: folderProps.allFolders,
[folderProps.allFolders, folderProps.searchTerm],
);

const noFolders = !filteredFolders.length;
const noFolders = !folderProps.allFolders.length;
const isSearching = !!folderProps.searchTerm;

return (
<div className="flex min-h-[350px] flex-col" data-qa="upload-folders">
<button
className={classNames(
'mb-0.5 flex items-center gap-1 rounded border-l-2 py-1 text-xs text-secondary',
<CollapsibleSection
onToggle={() => handleFolderSelect(rootFolderId)}
name={rootFolderName}
openByDefault
dataQa="root-folder"
isHighlighted={rootFolderId === selectedFolderId}
togglerClassName={classNames(
'mb-0.5 w-full rounded border-l-2 text-secondary',
selectedFolderId === rootFolderId
? 'border-accent-primary bg-accent-primary-alpha'
: 'border-transparent',
)}
onClick={() => handleFolderSelect(rootFolderId)}
data-qa="root-folder"
className="!px-0"
>
<CaretIconComponent isOpen={isAllEntitiesOpened} />
{t(rootFolderName)}
</button>
{isAllEntitiesOpened && (
<div className="flex grow flex-col gap-0.5 overflow-y-auto">
{!noFolders ? (
<div className="flex flex-col gap-1" data-qa="all-folders">
{filteredFolders.map((folder) => {
if (
folder.folderId !== rootFolderId ||
(initiallySelectedFolderId &&
folder.originalId === initiallySelectedFolderId)
) {
return null;
}
{isAllEntitiesOpened && (
<div className="flex grow flex-col gap-0.5 overflow-y-auto">
{!noFolders ? (
<div className="flex flex-col gap-1" data-qa="all-folders">
{folderProps.allFolders.map((folder) => {
if (
folder.folderId !== rootFolderId ||
(initiallySelectedFolderId &&
folder.originalId === initiallySelectedFolderId)
) {
return null;
}

return (
<div className="relative" key={folder.id}>
<Folder
{...folderProps}
maxDepth={MAX_CONVERSATION_AND_PROMPT_FOLDERS_DEPTH}
currentFolder={folder}
highlightedFolders={highlightedFolders}
highlightTemporaryFolders={highlightTemporaryFolders}
/>
</div>
);
})}
</div>
) : (
<div className="my-auto">
{isSearching ? <NoResultsFound /> : <NoData />}
</div>
)}
</div>
)}
return (
<div className="relative" key={folder.id}>
<Folder
{...folderProps}
maxDepth={MAX_CONVERSATION_AND_PROMPT_FOLDERS_DEPTH}
currentFolder={folder}
highlightedFolders={highlightedFolders}
highlightTemporaryFolders={highlightTemporaryFolders}
/>
</div>
);
})}
</div>
) : (
<div className="my-auto">
{isSearching ? <NoResultsFound /> : <NoData />}
</div>
)}
</div>
)}
</CollapsibleSection>
</div>
);
};
3 changes: 1 addition & 2 deletions apps/chat/src/constants/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ export const errorsMessages = {
publicationsUploadFailed: 'Publications uploading failed.',
publicationUploadFailed: 'Publication uploading failed.',
publicationDeletionFailed: 'Publication deletion failed.',
publishedConversationsUploadFailed:
'Published conversations uploading failed.',
publishedItemsUploadFailed: 'Published items uploading failed.',
publicationApproveFailed: 'Publication approving failed.',
publicationRejectFailed: 'Publication rejecting failed.',
publishingByMeItemsUploadingFailed: 'Published by me items uploading failed.',
Expand Down
4 changes: 2 additions & 2 deletions apps/chat/src/store/conversations/conversations.reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
getConversationRootId,
isRootConversationsId,
} from '@/src/utils/app/id';
import { doesPromptOrConversationContainSearchTerm } from '@/src/utils/app/search';
import { doesEntityContainSearchTerm } from '@/src/utils/app/search';
import {
isEntityExternal,
isEntityOrParentsExternal,
Expand Down Expand Up @@ -893,7 +893,7 @@ export const conversationsSlice = createSlice({
.filter(
(conv) =>
!isEntityExternal(conv) &&
doesPromptOrConversationContainSearchTerm(conv, state.searchTerm),
doesEntityContainSearchTerm(conv, state.searchTerm),
)
.map(({ id }) => id);
} else {
Expand Down
28 changes: 14 additions & 14 deletions apps/chat/src/store/conversations/conversations.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,21 @@ import {
import { sortByDateAndName } from '@/src/utils/app/conversation';
import { constructPath } from '@/src/utils/app/file';
import {
getChildAndCurrentFoldersById,
getChildAndCurrentFoldersIdsById,
getConversationAttachmentWithPath,
getFilteredFolders,
getNextDefaultName,
getParentAndChildFolders,
getParentAndCurrentFoldersById,
getParentFolderIdsFromEntityId,
sortByName,
splitEntityId,
} from '@/src/utils/app/folders';
import { getConversationRootId } from '@/src/utils/app/id';
import {
PublishedWithMeFilter,
doesPromptOrConversationContainSearchTerm,
doesEntityContainSearchTerm,
getMyItemsFilters,
} from '@/src/utils/app/search';
import {
Expand Down Expand Up @@ -79,10 +81,7 @@ export const selectFilteredConversations = createSelector(
return conversations.filter(
(conversation) =>
(!searchTerm ||
doesPromptOrConversationContainSearchTerm(
conversation,
searchTerm,
)) &&
doesEntityContainSearchTerm(conversation, searchTerm)) &&
(filters.searchFilter?.(conversation) ?? true) &&
(ignoreSectionFilter ||
(filters.sectionFilter?.(conversation) ?? true)),
Expand Down Expand Up @@ -277,7 +276,7 @@ export const selectSearchedConversations = createSelector(
[selectConversations, selectSearchTerm],
(conversations, searchTerm) =>
conversations.filter((conversation) =>
doesPromptOrConversationContainSearchTerm(conversation, searchTerm),
doesEntityContainSearchTerm(conversation, searchTerm),
),
);

Expand Down Expand Up @@ -557,22 +556,23 @@ export const selectPublishedWithMeFolders = createSelector(
},
);

export const selectTemporaryAndFilteredFolders = createSelector(
export const selectTemporaryAndPublishedFolders = createSelector(
[
selectFolders,
selectPublishedWithMeFolders,
selectTemporaryFolders,
(_state, searchTerm?: string) => searchTerm,
],
(allFolders, filteredFolders, temporaryFolders, searchTerm = '') => {
const filtered = [...filteredFolders, ...temporaryFolders].filter(
(folder) => folder.name.includes(searchTerm.toLowerCase()),
(allFolders, publishedFolders, temporaryFolders, searchTerm = '') => {
const allPublishedFolders = publishedFolders.flatMap((folder) =>
getChildAndCurrentFoldersById(folder.id, allFolders),
);
const filteredFolders = [
...sortByName(allPublishedFolders),
...temporaryFolders,
].filter((folder) => doesEntityContainSearchTerm(folder, searchTerm));

return getParentAndChildFolders(
[...allFolders, ...temporaryFolders],
filtered,
);
return getParentAndChildFolders(sortByName(allFolders), filteredFolders);
},
);

Expand Down
7 changes: 2 additions & 5 deletions apps/chat/src/store/prompts/prompts.reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
getNextDefaultName,
} from '@/src/utils/app/folders';
import { getPromptRootId, isRootPromptId } from '@/src/utils/app/id';
import { doesPromptOrConversationContainSearchTerm } from '@/src/utils/app/search';
import { doesEntityContainSearchTerm } from '@/src/utils/app/search';
import {
isEntityExternal,
isEntityOrParentsExternal,
Expand Down Expand Up @@ -537,10 +537,7 @@ export const promptsSlice = createSlice({
.filter(
(prompt) =>
!isEntityExternal(prompt) &&
doesPromptOrConversationContainSearchTerm(
prompt,
state.searchTerm,
),
doesEntityContainSearchTerm(prompt, state.searchTerm),
)
.map(({ id }) => id);
} else {
Expand Down
Loading

0 comments on commit d1d12a6

Please sign in to comment.