From 251ab3e84b26d23373f96740609e638e496a5bf6 Mon Sep 17 00:00:00 2001
From: Alexander <98586297+Alexander-Kezik@users.noreply.github.com>
Date: Wed, 14 Feb 2024 14:41:53 +0100
Subject: [PATCH 1/4] fix(chat): fix new created prompt appears in three
 sections (Issue #696) (#713)

---
 apps/chat/src/store/prompts/prompts.selectors.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/apps/chat/src/store/prompts/prompts.selectors.ts b/apps/chat/src/store/prompts/prompts.selectors.ts
index 6c7bdd6b33..edb611a642 100644
--- a/apps/chat/src/store/prompts/prompts.selectors.ts
+++ b/apps/chat/src/store/prompts/prompts.selectors.ts
@@ -42,7 +42,7 @@ export const selectFilteredPrompts = createSelector(
         (!searchTerm ||
           doesPromptOrConversationContainSearchTerm(prompt, searchTerm)) &&
         filters.searchFilter(prompt) &&
-        (prompt.folderId || filters.sectionFilter(prompt)),
+        filters.sectionFilter(prompt),
     );
   },
 );

From b4293dd51cf7c390d191f17dcf595ef282f5f5bc Mon Sep 17 00:00:00 2001
From: Mikita Butsko <mikita_butsko@epam.com>
Date: Wed, 14 Feb 2024 15:08:08 +0100
Subject: [PATCH 2/4] fix(chat): fix shared with me display (Issue #704) (#714)

---
 apps/chat/src/hooks/useHandleFileFolders.ts   |  2 +-
 .../conversations/conversations.epics.ts      | 27 +++++-
 .../conversations/conversations.reducers.ts   |  3 +
 .../conversations/conversations.selectors.ts  | 12 +++
 apps/chat/src/store/files/files.epics.ts      |  3 +-
 .../src/store/prompts/prompts.selectors.ts    | 12 +++
 apps/chat/src/store/share/share.epics.ts      | 87 ++++++++++++++-----
 apps/chat/src/utils/app/data/share-service.ts | 26 +++---
 .../data/storages/api/api-entity-storage.ts   |  4 +-
 apps/chat/src/utils/app/file.ts               |  5 +-
 apps/chat/src/utils/app/folders.ts            |  2 +-
 apps/chat/src/utils/app/import-export.ts      | 10 ++-
 apps/chat/src/utils/app/share.ts              |  2 +-
 package-lock.json                             | 22 +----
 package.json                                  |  4 +-
 15 files changed, 150 insertions(+), 71 deletions(-)

diff --git a/apps/chat/src/hooks/useHandleFileFolders.ts b/apps/chat/src/hooks/useHandleFileFolders.ts
index 6dc693b7ce..1fbd680a1e 100644
--- a/apps/chat/src/hooks/useHandleFileFolders.ts
+++ b/apps/chat/src/hooks/useHandleFileFolders.ts
@@ -77,7 +77,7 @@ export const useHandleFileFolders = (
    * @param folderId - ID of the folder to toggle.
    */
   const handleToggleFolder = useCallback(
-    (folderId: string | undefined) => {
+    (folderId: string) => {
       if (!folderId) {
         setIsAllFilesOpened((value) => !value);
         setOpenedFoldersIds([]);
diff --git a/apps/chat/src/store/conversations/conversations.epics.ts b/apps/chat/src/store/conversations/conversations.epics.ts
index 4349528929..537247c25c 100644
--- a/apps/chat/src/store/conversations/conversations.epics.ts
+++ b/apps/chat/src/store/conversations/conversations.epics.ts
@@ -76,6 +76,7 @@ import { ApiKeys } from '@/src/utils/server/api';
 import {
   ChatBody,
   Conversation,
+  ConversationInfo,
   Message,
   MessageSettings,
   Playback,
@@ -83,7 +84,7 @@ import {
   Role,
 } from '@/src/types/chat';
 import { EntityType, FeatureType, UploadStatus } from '@/src/types/common';
-import { FolderType } from '@/src/types/folder';
+import { FolderInterface, FolderType } from '@/src/types/folder';
 import { MigrationStorageKeys, StorageType } from '@/src/types/storage';
 import { AppEpic } from '@/src/types/store';
 
@@ -2096,8 +2097,18 @@ const uploadConversationsWithFoldersEpic: AppEpic = (action$) =>
         ),
       ).pipe(
         switchMap((foldersAndEntities) => {
-          const folders = foldersAndEntities.flatMap((f) => f.folders);
-          const conversations = foldersAndEntities.flatMap((f) => f.entities);
+          const folders = foldersAndEntities
+            .flatMap((f) => f.folders)
+            .map((item) => ({
+              ...item,
+              ...(payload.inheritedMetadata as Partial<FolderInterface>),
+            }));
+          const conversations = foldersAndEntities
+            .flatMap((f) => f.entities)
+            .map((item) => ({
+              ...item,
+              ...(payload.inheritedMetadata as Partial<ConversationInfo>),
+            }));
           return concat(
             of(
               ConversationsActions.uploadFoldersSuccess({
@@ -2150,10 +2161,15 @@ const uploadConversationsWithFoldersRecursiveEpic: AppEpic = (action$) =>
             of(
               ConversationsActions.uploadFoldersSuccess({
                 paths: new Set(),
-                folders: getFoldersFromIds(paths, FolderType.Chat),
+                folders: getFoldersFromIds(
+                  paths,
+                  FolderType.Chat,
+                  UploadStatus.LOADED,
+                ),
                 allLoaded: true,
               }),
             ),
+            of(ConversationsActions.initFoldersAndConversationsSuccess()),
           );
         }),
         catchError(() => of(ConversationsActions.uploadConversationsFail())), // TODO: handle error it in https://github.com/epam/ai-dial-chat/issues/663
@@ -2198,6 +2214,9 @@ const openFolderEpic: AppEpic = (action$, state$) =>
         of(
           ConversationsActions.uploadConversationsWithFolders({
             paths: [payload.id],
+            inheritedMetadata: {
+              sharedWithMe: true,
+            },
           }),
         ),
       );
diff --git a/apps/chat/src/store/conversations/conversations.reducers.ts b/apps/chat/src/store/conversations/conversations.reducers.ts
index 2f2883496c..cbf3e01acc 100644
--- a/apps/chat/src/store/conversations/conversations.reducers.ts
+++ b/apps/chat/src/store/conversations/conversations.reducers.ts
@@ -86,6 +86,7 @@ export const conversationsSlice = createSlice({
     ) => state,
     initSelectedConversations: (state) => state,
     initFoldersAndConversations: (state) => state,
+    initFoldersAndConversationsSuccess: (state) => state,
     saveConversation: (state, _action: PayloadAction<Conversation>) => state,
     recreateConversation: (
       state,
@@ -595,6 +596,8 @@ export const conversationsSlice = createSlice({
         payload,
       }: PayloadAction<{
         paths: (string | undefined)[];
+        // Needed for open shared with me folder and keep shared with me flag
+        inheritedMetadata?: unknown;
       }>,
     ) => {
       state.foldersStatus = UploadStatus.LOADING;
diff --git a/apps/chat/src/store/conversations/conversations.selectors.ts b/apps/chat/src/store/conversations/conversations.selectors.ts
index 1e36f6b762..a0f075aba9 100644
--- a/apps/chat/src/store/conversations/conversations.selectors.ts
+++ b/apps/chat/src/store/conversations/conversations.selectors.ts
@@ -168,6 +168,18 @@ export const selectChildAndCurrentFoldersIdsById = createSelector(
     return new Set(getChildAndCurrentFoldersIdsById(folderId, folders));
   },
 );
+export const selectFullTreeChildConversationsByFolderId = createSelector(
+  [selectConversations, selectChildAndCurrentFoldersIdsById],
+  (conversations, foldersIds) => {
+    return conversations.filter((conv) => foldersIds.has(conv.folderId));
+  },
+);
+export const selectFullTreeChildFoldersByFolderId = createSelector(
+  [selectFolders, selectChildAndCurrentFoldersIdsById],
+  (folders, foldersIds) => {
+    return folders.filter((folder) => foldersIds.has(folder.id));
+  },
+);
 export const selectFirstSelectedConversation = createSelector(
   [selectSelectedConversations],
   (conversations): Conversation | undefined => {
diff --git a/apps/chat/src/store/files/files.epics.ts b/apps/chat/src/store/files/files.epics.ts
index 448d5cdc9a..a72fce3199 100644
--- a/apps/chat/src/store/files/files.epics.ts
+++ b/apps/chat/src/store/files/files.epics.ts
@@ -17,6 +17,7 @@ import { combineEpics } from 'redux-observable';
 import { FileService } from '@/src/utils/app/data/file-service';
 import { triggerDownload } from '@/src/utils/app/file';
 import { translate } from '@/src/utils/app/translation';
+import { encodeApiUrl } from '@/src/utils/server/api';
 
 import { UploadStatus } from '@/src/types/common';
 import { AppEpic } from '@/src/types/store';
@@ -229,7 +230,7 @@ const downloadFilesListEpic: AppEpic = (action$, state$) =>
     tap(({ files }) => {
       files.forEach((file) =>
         triggerDownload(
-          `api/${encodeURI(`${file.absolutePath}/${file.name}`)}`,
+          `api/${encodeApiUrl(`${file.absolutePath}/${file.name}`)}`,
           file.name,
         ),
       );
diff --git a/apps/chat/src/store/prompts/prompts.selectors.ts b/apps/chat/src/store/prompts/prompts.selectors.ts
index edb611a642..1e501b33e4 100644
--- a/apps/chat/src/store/prompts/prompts.selectors.ts
+++ b/apps/chat/src/store/prompts/prompts.selectors.ts
@@ -124,6 +124,18 @@ export const selectChildAndCurrentFoldersIdsById = createSelector(
     return new Set(getChildAndCurrentFoldersIdsById(folderId, folders));
   },
 );
+export const selectFullTreeChildPromptsByFolderId = createSelector(
+  [selectPrompts, selectChildAndCurrentFoldersIdsById],
+  (prompts, foldersIds) => {
+    return prompts.filter((conv) => foldersIds.has(conv.folderId));
+  },
+);
+export const selectFullTreeChildFoldersByFolderId = createSelector(
+  [selectFolders, selectChildAndCurrentFoldersIdsById],
+  (folders, foldersIds) => {
+    return folders.filter((folder) => foldersIds.has(folder.id));
+  },
+);
 
 export const selectSearchTerm = createSelector([rootSelector], (state) => {
   return state.searchTerm;
diff --git a/apps/chat/src/store/share/share.epics.ts b/apps/chat/src/store/share/share.epics.ts
index 59f69f3d53..c6f0f2d679 100644
--- a/apps/chat/src/store/share/share.epics.ts
+++ b/apps/chat/src/store/share/share.epics.ts
@@ -17,7 +17,7 @@ import { ShareService } from '@/src/utils/app/data/share-service';
 import { constructPath } from '@/src/utils/app/file';
 import { splitEntityId } from '@/src/utils/app/folders';
 import { translate } from '@/src/utils/app/translation';
-import { parseConversationApiKey } from '@/src/utils/server/api';
+import { encodeApiUrl, parseConversationApiKey } from '@/src/utils/server/api';
 
 import { Conversation, Message } from '@/src/types/chat';
 import {
@@ -111,7 +111,7 @@ const shareConversationEpic: AppEpic = (action$) =>
             invitationType: ShareRequestType.link,
             resources: [
               {
-                url: encodeURI(payload.resourceId),
+                url: encodeApiUrl(payload.resourceId),
               },
               ...internalResources.map((res) => ({ url: res })),
             ],
@@ -159,7 +159,7 @@ const shareConversationFolderEpic: AppEpic = (action$) =>
             invitationType: ShareRequestType.link,
             resources: [
               {
-                url: encodeURI(payload.resourceId + '/'),
+                url: encodeApiUrl(payload.resourceId) + '/',
               },
               ...internalResourcesIds,
             ],
@@ -190,7 +190,7 @@ const sharePromptEpic: AppEpic = (action$) =>
         invitationType: ShareRequestType.link,
         resources: [
           {
-            url: encodeURI(payload.resourceId),
+            url: encodeApiUrl(payload.resourceId),
           },
         ],
       }).pipe(
@@ -215,7 +215,7 @@ const sharePromptFolderEpic: AppEpic = (action$) =>
         invitationType: ShareRequestType.link,
         resources: [
           {
-            url: encodeURI(payload.resourceId + '/'),
+            url: encodeApiUrl(payload.resourceId) + '/',
           },
         ],
       }).pipe(
@@ -289,7 +289,7 @@ const triggerGettingSharedListingsConversationsEpic: AppEpic = (
   action$.pipe(
     filter(
       (action) =>
-        ConversationsActions.uploadConversationsSuccess.match(action) ||
+        ConversationsActions.initFoldersAndConversationsSuccess.match(action) ||
         ShareActions.acceptShareInvitationSuccess.match(action),
     ),
     filter(() =>
@@ -391,20 +391,44 @@ const getSharedListingSuccessEpic: AppEpic = (action$, state$) =>
             state$.value,
           );
           const folders = ConversationsSelectors.selectFolders(state$.value);
+
           actions.push(
             ...(folders
-              .map((item) => {
+              .flatMap((item) => {
                 const isShared = payload.resources.folders.find(
                   (res) => res.id === item.id,
                 );
 
                 if (isShared) {
-                  return ConversationsActions.updateFolder({
-                    folderId: item.id,
-                    values: {
-                      isShared: true,
-                    },
-                  });
+                  const childConversations =
+                    ConversationsSelectors.selectFullTreeChildConversationsByFolderId(
+                      state$.value,
+                      item.id,
+                    );
+                  const childFolders =
+                    ConversationsSelectors.selectFullTreeChildFoldersByFolderId(
+                      state$.value,
+                      item.id,
+                    );
+
+                  return [
+                    ...childFolders.map((folder) =>
+                      ConversationsActions.updateFolder({
+                        folderId: folder.id,
+                        values: {
+                          isShared: true,
+                        },
+                      }),
+                    ),
+                    ...childConversations.map((conv) =>
+                      ConversationsActions.updateConversation({
+                        id: conv.id,
+                        values: {
+                          isShared: true,
+                        },
+                      }),
+                    ),
+                  ];
                 }
                 return undefined;
               })
@@ -473,18 +497,41 @@ const getSharedListingSuccessEpic: AppEpic = (action$, state$) =>
           const folders = PromptsSelectors.selectFolders(state$.value);
           actions.push(
             ...(folders
-              .map((item) => {
+              .flatMap((item) => {
                 const isShared = payload.resources.folders.find(
                   (res) => res.id === item.id,
                 );
 
                 if (isShared) {
-                  return PromptsActions.updateFolder({
-                    folderId: item.id,
-                    values: {
-                      isShared: true,
-                    },
-                  });
+                  const childPrompts =
+                    PromptsSelectors.selectFullTreeChildPromptsByFolderId(
+                      state$.value,
+                      item.id,
+                    );
+                  const childFolders =
+                    PromptsSelectors.selectFullTreeChildFoldersByFolderId(
+                      state$.value,
+                      item.id,
+                    );
+
+                  return [
+                    ...childFolders.map((folder) =>
+                      PromptsActions.updateFolder({
+                        folderId: folder.id,
+                        values: {
+                          isShared: true,
+                        },
+                      }),
+                    ),
+                    ...childPrompts.map((prompt) =>
+                      PromptsActions.updatePrompt({
+                        id: prompt.id,
+                        values: {
+                          isShared: true,
+                        },
+                      }),
+                    ),
+                  ];
                 }
                 return undefined;
               })
diff --git a/apps/chat/src/utils/app/data/share-service.ts b/apps/chat/src/utils/app/data/share-service.ts
index ec4d45adc7..9c44be1bdc 100644
--- a/apps/chat/src/utils/app/data/share-service.ts
+++ b/apps/chat/src/utils/app/data/share-service.ts
@@ -20,6 +20,7 @@ import {
 import {
   ApiKeys,
   ApiUtils,
+  decodeApiUrl,
   getFolderTypeByApiKey,
   parseConversationApiKey,
   parsePromptApiKey,
@@ -66,25 +67,26 @@ export class ShareService {
 
             if (entity.nodeType === BackendDataNodeType.ITEM) {
               const conversation = conversationResource as BackendChatEntity;
-              const id = decodeURI(
-                conversation.url.slice(0, conversation.url.length - 1),
-              );
+              const id = decodeApiUrl(conversation.url);
+
               const { apiKey, bucket, parentPath } = splitEntityId(id);
 
               entities.push({
                 ...parseConversationApiKey(conversation.name),
-                id: decodeURI(conversation.url),
+                id,
                 lastActivityDate: conversation.updatedAt,
                 folderId: constructPath(apiKey, bucket, parentPath),
               });
             }
             if (entity.nodeType === BackendDataNodeType.FOLDER) {
               const folder = conversationResource as BackendChatFolder;
-              const id = decodeURI(folder.url.slice(0, folder.url.length - 1));
+              const id = decodeApiUrl(
+                folder.url.slice(0, folder.url.length - 1),
+              );
               const { apiKey, bucket, parentPath } = splitEntityId(id);
 
               folders.push({
-                id: decodeURI(folder.url.slice(0, folder.url.length - 1)),
+                id,
                 name: folder.name,
                 folderId: constructPath(apiKey, bucket, parentPath),
                 type: getFolderTypeByApiKey(ApiKeys.Conversations),
@@ -99,25 +101,25 @@ export class ShareService {
 
             if (entity.nodeType === BackendDataNodeType.ITEM) {
               const conversation = conversationResource as BackendChatEntity;
-              const id = decodeURI(
-                conversation.url.slice(0, conversation.url.length - 1),
-              );
+              const id = decodeApiUrl(conversation.url);
               const { apiKey, bucket, parentPath } = splitEntityId(id);
 
               entities.push({
                 ...parsePromptApiKey(conversation.name),
-                id: decodeURI(conversation.url),
+                id,
                 lastActivityDate: conversation.updatedAt,
                 folderId: constructPath(apiKey, bucket, parentPath),
               });
             }
             if (entity.nodeType === BackendDataNodeType.FOLDER) {
               const folder = conversationResource as BackendChatFolder;
-              const id = decodeURI(folder.url.slice(0, folder.url.length - 1));
+              const id = decodeApiUrl(
+                folder.url.slice(0, folder.url.length - 1),
+              );
               const { apiKey, bucket, parentPath } = splitEntityId(id);
 
               folders.push({
-                id: decodeURI(folder.url.slice(0, folder.url.length - 1)),
+                id,
                 name: folder.name,
                 folderId: constructPath(apiKey, bucket, parentPath),
                 type: getFolderTypeByApiKey(ApiKeys.Prompts),
diff --git a/apps/chat/src/utils/app/data/storages/api/api-entity-storage.ts b/apps/chat/src/utils/app/data/storages/api/api-entity-storage.ts
index 4a5f0d685e..17b987ff39 100644
--- a/apps/chat/src/utils/app/data/storages/api/api-entity-storage.ts
+++ b/apps/chat/src/utils/app/data/storages/api/api-entity-storage.ts
@@ -36,6 +36,7 @@ export abstract class ApiEntityStorage<
       name: folder.name,
       folderId: constructPath(apiKey, bucket, parentPath),
       type: getFolderTypeByApiKey(this.getStorageKey()),
+      isShared: false,
     };
   }
 
@@ -49,6 +50,7 @@ export abstract class ApiEntityStorage<
       id,
       lastActivityDate: entity.updatedAt,
       folderId: constructPath(apiKey, bucket, parentPath),
+      isShared: false,
     } as unknown as TEntityInfo;
   }
 
@@ -150,7 +152,7 @@ export abstract class ApiEntityStorage<
         'Content-Type': 'application/json',
       },
       body: JSON.stringify(this.cleanUpEntity(entity)),
-    }) // TODO: handle error it in https://github.com/epam/ai-dial-chat/issues/663
+    }); // TODO: handle error it in https://github.com/epam/ai-dial-chat/issues/663
   }
 
   updateEntity(entity: TEntity): Observable<void> {
diff --git a/apps/chat/src/utils/app/file.ts b/apps/chat/src/utils/app/file.ts
index ccfb5fc0b5..9e030cb4ce 100644
--- a/apps/chat/src/utils/app/file.ts
+++ b/apps/chat/src/utils/app/file.ts
@@ -3,6 +3,7 @@ import { UploadStatus } from '@/src/types/common';
 import { DialFile } from '@/src/types/files';
 import { FolderInterface } from '@/src/types/folder';
 
+import { decodeApiUrl, encodeApiUrl } from '../server/api';
 import { getPathToFolderById } from './folders';
 
 import escapeStringRegexp from 'escape-string-regexp';
@@ -53,7 +54,7 @@ export const getUserCustomContent = (
       .map((file) => ({
         type: file.contentType,
         title: file.name,
-        url: encodeURI(`${file.absolutePath}/${file.name}`),
+        url: encodeApiUrl(`${file.absolutePath}/${file.name}`),
       })),
   };
 };
@@ -122,7 +123,7 @@ export const getFilesWithInvalidFileSize = (
 };
 
 const parseAttachmentUrl = (url: string) => {
-  const decodedUrl = decodeURI(url);
+  const decodedUrl = decodeApiUrl(url);
   const lastIndexSlash = decodedUrl.lastIndexOf('/');
 
   return {
diff --git a/apps/chat/src/utils/app/folders.ts b/apps/chat/src/utils/app/folders.ts
index c4e29e7570..83e8e8c8e8 100644
--- a/apps/chat/src/utils/app/folders.ts
+++ b/apps/chat/src/utils/app/folders.ts
@@ -323,7 +323,7 @@ export const findRootFromItems = (
   const parentIds = new Set(items.map((item) => item.id));
 
   return items.find((item) => {
-    if (!item.folderId) return true;
+    if (isRootId(item.folderId)) return true;
     return !parentIds.has(item.folderId);
   });
 };
diff --git a/apps/chat/src/utils/app/import-export.ts b/apps/chat/src/utils/app/import-export.ts
index eca89499cd..001dc03950 100644
--- a/apps/chat/src/utils/app/import-export.ts
+++ b/apps/chat/src/utils/app/import-export.ts
@@ -14,7 +14,7 @@ import {
 } from '@/src/types/import-export';
 import { Prompt } from '@/src/types/prompt';
 
-import { ApiKeys } from '../server/api';
+import { ApiKeys, decodeApiUrl, encodeApiUrl } from '../server/api';
 import { cleanConversationHistory } from './clean';
 import { combineEntities } from './common';
 import { triggerDownload } from './file';
@@ -329,7 +329,7 @@ export const getAttachmentId = ({
 }) => {
   const regExpForAttachmentId = /^files\/\w*\//;
 
-  const attachmentId = decodeURI(url).split(regExpForAttachmentId)[
+  const attachmentId = decodeApiUrl(url).split(regExpForAttachmentId)[
     attachmentIdIndex
   ];
 
@@ -392,13 +392,15 @@ export const updateAttachment = ({
 
   const newAttachmentUrl =
     oldAttachment.url &&
-    encodeURI(`${newAttachmentFile.absolutePath}/${newAttachmentFile.name}`);
+    encodeApiUrl(`${newAttachmentFile.absolutePath}/${newAttachmentFile.name}`);
   const lastSlashIndex = oldAttachmentId.lastIndexOf('/');
   const oldAttachmentNameInPath = oldAttachmentId.slice(lastSlashIndex + 1);
 
   const newReferenceUrl =
     oldAttachment.reference_url &&
-    encodeURI(`${newAttachmentFile.absolutePath}/${oldAttachmentNameInPath}`);
+    encodeApiUrl(
+      `${newAttachmentFile.absolutePath}/${oldAttachmentNameInPath}`,
+    );
 
   const updatedAttachment: Attachment = {
     ...oldAttachment,
diff --git a/apps/chat/src/utils/app/share.ts b/apps/chat/src/utils/app/share.ts
index 84442ba2ad..4c73ef7292 100644
--- a/apps/chat/src/utils/app/share.ts
+++ b/apps/chat/src/utils/app/share.ts
@@ -50,7 +50,7 @@ export const hasExternalParent = (
   folderId: string | undefined,
   featureType: FeatureType,
 ) => {
-  if (!featureType || !folderId) return false;
+  if (!featureType) return false;
 
   return featureType === FeatureType.Chat
     ? ConversationsSelectors.hasExternalParent(state, folderId)
diff --git a/package-lock.json b/package-lock.json
index 4a29947ba4..209f63fd49 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -55,8 +55,7 @@
         "remark-gfm": "^3.0.1",
         "rxjs": "^7.8.1",
         "svgo": "^3.0.2",
-        "tslib": "^2.6.0",
-        "uuid": "^9.0.0"
+        "tslib": "^2.6.0"
       },
       "devDependencies": {
         "@mozilla/readability": "^0.4.4",
@@ -85,7 +84,6 @@
         "@types/react": "18.2.33",
         "@types/react-dom": "18.2.14",
         "@types/react-syntax-highlighter": "^15.5.6",
-        "@types/uuid": "^9.0.1",
         "@typescript-eslint/eslint-plugin": "^6.7.0",
         "@typescript-eslint/parser": "^6.7.0",
         "@vitejs/plugin-react": "^4.2.1",
@@ -7184,12 +7182,6 @@
       "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
       "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
     },
-    "node_modules/@types/uuid": {
-      "version": "9.0.8",
-      "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz",
-      "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==",
-      "dev": true
-    },
     "node_modules/@types/yargs": {
       "version": "17.0.32",
       "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
@@ -20682,18 +20674,6 @@
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
       "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
     },
-    "node_modules/uuid": {
-      "version": "9.0.1",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
-      "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
-      "funding": [
-        "https://github.com/sponsors/broofa",
-        "https://github.com/sponsors/ctavan"
-      ],
-      "bin": {
-        "uuid": "dist/bin/uuid"
-      }
-    },
     "node_modules/uvu": {
       "version": "0.5.6",
       "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz",
diff --git a/package.json b/package.json
index 5974770640..3e667aa59b 100644
--- a/package.json
+++ b/package.json
@@ -76,8 +76,7 @@
     "remark-gfm": "^3.0.1",
     "rxjs": "^7.8.1",
     "svgo": "^3.0.2",
-    "tslib": "^2.6.0",
-    "uuid": "^9.0.0"
+    "tslib": "^2.6.0"
   },
   "devDependencies": {
     "@mozilla/readability": "^0.4.4",
@@ -106,7 +105,6 @@
     "@types/react": "18.2.33",
     "@types/react-dom": "18.2.14",
     "@types/react-syntax-highlighter": "^15.5.6",
-    "@types/uuid": "^9.0.1",
     "@typescript-eslint/eslint-plugin": "^6.7.0",
     "@typescript-eslint/parser": "^6.7.0",
     "@vitejs/plugin-react": "^4.2.1",

From e4862ca8c08d77a23ad425afd966d9d0e204a41d Mon Sep 17 00:00:00 2001
From: Mikita Butsko <mikita_butsko@epam.com>
Date: Wed, 14 Feb 2024 15:47:45 +0100
Subject: [PATCH 3/4] fix(chat): fix failed build (#716)

---
 apps/chat/src/hooks/useHandleFileFolders.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/apps/chat/src/hooks/useHandleFileFolders.ts b/apps/chat/src/hooks/useHandleFileFolders.ts
index 1fbd680a1e..6dc693b7ce 100644
--- a/apps/chat/src/hooks/useHandleFileFolders.ts
+++ b/apps/chat/src/hooks/useHandleFileFolders.ts
@@ -77,7 +77,7 @@ export const useHandleFileFolders = (
    * @param folderId - ID of the folder to toggle.
    */
   const handleToggleFolder = useCallback(
-    (folderId: string) => {
+    (folderId: string | undefined) => {
       if (!folderId) {
         setIsAllFilesOpened((value) => !value);
         setOpenedFoldersIds([]);

From 961972dbdad17f0409fa8db89073c6046f4c51ff Mon Sep 17 00:00:00 2001
From: nepalevov <33350321+nepalevov@users.noreply.github.com>
Date: Wed, 14 Feb 2024 16:47:50 +0100
Subject: [PATCH 4/4] chore: update workflow reference (#719)

---
 .github/workflows/deploy_dev.yml     |  4 ++--
 .github/workflows/e2e_tests.yml      |  2 +-
 .github/workflows/pr-title-check.yml |  2 +-
 .github/workflows/pr.yml             | 10 ++++++++++
 .github/workflows/pr_check_tests.yml | 12 ------------
 .github/workflows/release.yml        |  7 ++-----
 Dockerfile                           |  6 +++---
 apps/chat-e2e/README.md              | 16 ++++++++--------
 8 files changed, 27 insertions(+), 32 deletions(-)
 create mode 100644 .github/workflows/pr.yml
 delete mode 100644 .github/workflows/pr_check_tests.yml

diff --git a/.github/workflows/deploy_dev.yml b/.github/workflows/deploy_dev.yml
index e6e90aef3d..23d40f9948 100644
--- a/.github/workflows/deploy_dev.yml
+++ b/.github/workflows/deploy_dev.yml
@@ -6,7 +6,7 @@ on:
 jobs:
   gitlab-dev-deploy:
     if: ${{ github.event.registry_package.package_version.container_metadata.tag.name == 'development' }}
-    uses: epam/ai-dial-ci/.github/workflows/deploy-development.yml@1.3.1
+    uses: epam/ai-dial-ci/.github/workflows/deploy-development.yml@1.4.0
     with:
       gitlab-project-id: '1827'
     secrets:
@@ -16,7 +16,7 @@ jobs:
 
   gitlab-dev-deploy-overlay:
     if: ${{ github.event.registry_package.package_version.container_metadata.tag.name == 'development' }}
-    uses: epam/ai-dial-ci/.github/workflows/deploy-development.yml@1.3.1
+    uses: epam/ai-dial-ci/.github/workflows/deploy-development.yml@1.4.0
     with:
       gitlab-project-id: '1856'
     secrets:
diff --git a/.github/workflows/e2e_tests.yml b/.github/workflows/e2e_tests.yml
index b76a9c4dbc..0373b8857e 100644
--- a/.github/workflows/e2e_tests.yml
+++ b/.github/workflows/e2e_tests.yml
@@ -6,7 +6,7 @@ on:
 jobs:
   e2e-tests:
     if: ${{ github.event.registry_package.package_version.container_metadata.tag.name == 'development' }}
-    uses: epam/ai-dial-ci/.github/workflows/e2e-test.yml@1.3.1
+    uses: epam/ai-dial-ci/.github/workflows/e2e-test.yml@1.4.0
     with:
       gitlab-project-id: "1843"
     secrets:
diff --git a/.github/workflows/pr-title-check.yml b/.github/workflows/pr-title-check.yml
index 8aef1e8d84..f6d358fa66 100644
--- a/.github/workflows/pr-title-check.yml
+++ b/.github/workflows/pr-title-check.yml
@@ -9,6 +9,6 @@ on:
 
 jobs:
   pr-title-check:
-    uses: epam/ai-dial-ci/.github/workflows/pr-title-check.yml@1.3.1
+    uses: epam/ai-dial-ci/.github/workflows/pr-title-check.yml@1.4.0
     secrets:
       ACTIONS_BOT_TOKEN: ${{ secrets.ACTIONS_BOT_TOKEN }}
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
new file mode 100644
index 0000000000..1e47c58892
--- /dev/null
+++ b/.github/workflows/pr.yml
@@ -0,0 +1,10 @@
+name: PR Workflow
+
+on:
+  pull_request:
+    branches: [development, release-*]
+
+jobs:
+  run_tests:
+    uses: epam/ai-dial-ci/.github/workflows/node_pr.yml@1.4.0
+    secrets: inherit
diff --git a/.github/workflows/pr_check_tests.yml b/.github/workflows/pr_check_tests.yml
deleted file mode 100644
index b3ea178ccd..0000000000
--- a/.github/workflows/pr_check_tests.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: Code checks - tests
-
-on:
-  pull_request:
-    branches: [development, release-*]
-
-jobs:
-  run_tests:
-    uses: epam/ai-dial-ci/.github/workflows/test_yarn_docker.yml@1.3.1
-    secrets: inherit
-    with:
-      bypass_ort: false
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index ab89195d30..73586da443 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,13 +1,10 @@
-name: Release version
+name: Release Workflow
 
 on:
   push:
     branches: [ development, release-* ]
 
-env:
-  IMAGE_NAME: ${{ github.repository }}
-
 jobs:
   release:
-    uses: epam/ai-dial-ci/.github/workflows/publish_yarn_docker.yml@1.3.1
+    uses: epam/ai-dial-ci/.github/workflows/node_release.yml@1.4.0
     secrets: inherit
diff --git a/Dockerfile b/Dockerfile
index f556ba2968..757a3d743f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -15,7 +15,7 @@ COPY . .
 RUN npm run build
 
 # ---- Only required dependencies ----
-FROM build AS run_ependencies
+FROM build AS run_dependencies
 WORKDIR /app/dist/apps/chat
 COPY /tools /app/dist/apps/chat/tools
 RUN npm i
@@ -25,8 +25,8 @@ RUN node tools/patch-nextjs.js
 FROM node:20-alpine AS production
 RUN apk update && apk upgrade --no-cache libcrypto3 libssl3
 WORKDIR /app
-COPY --from=run_ependencies /app/dist/apps/chat ./
-COPY --from=run_ependencies /app/startup.sh ./startup.sh
+COPY --from=run_dependencies /app/dist/apps/chat ./
+COPY --from=run_dependencies /app/startup.sh ./startup.sh
 
 ENV NODE_ENV production
 ENV NEXT_TELEMETRY_DISABLED 1
diff --git a/apps/chat-e2e/README.md b/apps/chat-e2e/README.md
index b6aea9161d..e08840bcf3 100644
--- a/apps/chat-e2e/README.md
+++ b/apps/chat-e2e/README.md
@@ -59,11 +59,11 @@ CI report includes screenshots for failed tests.
 
 The following variables should be placed inside `chat-e2e/.env.local` file in order to run tests locally
 
-| Variable        | Required | Description                                                                                                                                        | Available Values | Default values |
-|-----------------| -------- |----------------------------------------------------------------------------------------------------------------------------------------------------|------------------| -------------- |
-| `E2E_HOST`      | No       | The host URL for end-to-end testing.                                                                                                               | Any string       |                |
-| `E2E_USERNAME`  | No       | Comma separated list of usernames for e2e authentification. The number of users should be more or equal number of workers set in playwright config | Any string       |                |
-| `E2E_WORKERS`   | No       | Number of threads to run e2e tests                                                                                                                 | Any number       |                |
-| `E2E_PASSWORD`  | No       | A password for e2e authentification                                                                                                                | Any string       |                |
-| `TMS_URL`       | No       | TMS URL                                                                                                                                            | Any string       |                |
-| `ISSUE_URL`     | No       | Issue URL                                                                                                                                          | Any string       |                |
+| Variable       | Required | Description                                                                                                                                        | Available Values | Default values |
+| -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | -------------- |
+| `E2E_HOST`     | No       | The host URL for end-to-end testing.                                                                                                               | Any string       |                |
+| `E2E_USERNAME` | No       | Comma separated list of usernames for e2e authentification. The number of users should be more or equal number of workers set in playwright config | Any string       |                |
+| `E2E_WORKERS`  | No       | Number of threads to run e2e tests                                                                                                                 | Any number       |                |
+| `E2E_PASSWORD` | No       | A password for e2e authentification                                                                                                                | Any string       |                |
+| `TMS_URL`      | No       | TMS URL                                                                                                                                            | Any string       |                |
+| `ISSUE_URL`    | No       | Issue URL                                                                                                                                          | Any string       |                |