From 9f911cb5cb9ae9e6d56c2a84506ed6f2ec4b20cd Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Tue, 3 Jun 2025 03:30:06 +0530 Subject: [PATCH 01/15] (feat:stream) store attachment_ids for query --- application/api/answer/routes.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/application/api/answer/routes.py b/application/api/answer/routes.py index 2aa473d4..3c2ba866 100644 --- a/application/api/answer/routes.py +++ b/application/api/answer/routes.py @@ -164,6 +164,7 @@ def save_conversation( agent_id=None, is_shared_usage=False, shared_token=None, + attachment_ids=None, ): current_time = datetime.datetime.now(datetime.timezone.utc) if conversation_id is not None and index is not None: @@ -177,6 +178,7 @@ def save_conversation( f"queries.{index}.sources": source_log_docs, f"queries.{index}.tool_calls": tool_calls, f"queries.{index}.timestamp": current_time, + f"queries.{index}.attachments": attachment_ids, } }, ) @@ -197,6 +199,7 @@ def save_conversation( "sources": source_log_docs, "tool_calls": tool_calls, "timestamp": current_time, + "attachments": attachment_ids, } } }, @@ -233,6 +236,7 @@ def save_conversation( "sources": source_log_docs, "tool_calls": tool_calls, "timestamp": current_time, + "attachments": attachment_ids, } ], } @@ -273,20 +277,13 @@ def complete_stream( isNoneDoc=False, index=None, should_save_conversation=True, - attachments=None, + attachment_ids=None, agent_id=None, is_shared_usage=False, shared_token=None, ): try: response_full, thought, source_log_docs, tool_calls = "", "", [], [] - attachment_ids = [] - - if attachments: - attachment_ids = [attachment["id"] for attachment in attachments] - logger.info( - f"Processing request with {len(attachments)} attachments: {attachment_ids}" - ) answer = agent.gen(query=question, retriever=retriever) @@ -340,6 +337,7 @@ def complete_stream( decoded_token, index, api_key=user_api_key, + attachment_ids=attachment_ids, agent_id=agent_id, is_shared_usage=is_shared_usage, shared_token=shared_token, @@ -539,6 +537,7 @@ class Stream(Resource): isNoneDoc=data.get("isNoneDoc"), index=index, should_save_conversation=save_conv, + attachment_ids=attachment_ids, agent_id=agent_id, is_shared_usage=is_shared_usage, shared_token=shared_token, From 7cab5b3b09d2f278164b5cc0c98d2df143a18e12 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Tue, 3 Jun 2025 04:02:41 +0530 Subject: [PATCH 02/15] (feat:attachments) store filenames in worker --- application/api/user/routes.py | 24 +++++++++++++++++++++++- application/worker.py | 1 + 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/application/api/user/routes.py b/application/api/user/routes.py index ae653c39..fb2caf82 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -44,6 +44,7 @@ shared_conversations_collections = db["shared_conversations"] users_collection = db["users"] user_logs_collection = db["user_logs"] user_tools_collection = db["user_tools"] +attachments_collection = db["attachments"] agents_collection.create_index( [("shared", 1)], @@ -236,13 +237,34 @@ class GetSingleConversation(Resource): ) if not conversation: return make_response(jsonify({"status": "not found"}), 404) + + # Process queries to include attachment names + queries = conversation["queries"] + for query in queries: + if "attachments" in query and query["attachments"]: + attachment_details = [] + for attachment_id in query["attachments"]: + try: + attachment = attachments_collection.find_one( + {"_id": ObjectId(attachment_id)} + ) + if attachment: + attachment_details.append({ + "id": str(attachment["_id"]), + "filename": attachment.get("filename", "Unknown file") + }) + except Exception as e: + current_app.logger.error( + f"Error retrieving attachment {attachment_id}: {e}", exc_info=True + ) + query["attachments_metadata"] = attachment_details except Exception as err: current_app.logger.error( f"Error retrieving conversation: {err}", exc_info=True ) return make_response(jsonify({"success": False}), 400) data = { - "queries": conversation["queries"], + "queries": queries, "agent_id": conversation.get("agent_id"), "is_shared_usage": conversation.get("is_shared_usage", False), "shared_token": conversation.get("shared_token", None), diff --git a/application/worker.py b/application/worker.py index 9829fde9..285b13af 100755 --- a/application/worker.py +++ b/application/worker.py @@ -475,6 +475,7 @@ def attachment_worker(self, file_info, user): "_id": doc_id, "user": user, "path": relative_path, + "filename": filename, "content": content, "token_count": token_count, "mime_type": mime_type, From e90fe117ec79bb445bd5cbac6693ba65fdd5d156 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Tue, 3 Jun 2025 18:05:47 +0530 Subject: [PATCH 03/15] (feat:attachments) render in bubble --- .../src/conversation/ConversationBubble.tsx | 26 +++++++++++++++++++ .../src/conversation/ConversationMessages.tsx | 1 + .../src/conversation/conversationModels.ts | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/frontend/src/conversation/ConversationBubble.tsx b/frontend/src/conversation/ConversationBubble.tsx index 920005e3..296caeda 100644 --- a/frontend/src/conversation/ConversationBubble.tsx +++ b/frontend/src/conversation/ConversationBubble.tsx @@ -59,6 +59,7 @@ const ConversationBubble = forwardRef< updated?: boolean, index?: number, ) => void; + attachmentsMetadata?: { id: string; filename: string }[]; } >(function ConversationBubble( { @@ -74,6 +75,7 @@ const ConversationBubble = forwardRef< questionNumber, isStreaming, handleUpdatedQuestionSubmission, + attachmentsMetadata, }, ref, ) { @@ -225,11 +227,35 @@ const ConversationBubble = forwardRef< return contentSegments; }; + const renderAttachments = () => { + if (!attachmentsMetadata || attachmentsMetadata.length === 0) return null; + + return ( +
+ {attachmentsMetadata.map((attachment, index) => ( +
+ Attachment + + {attachment.filename} + +
+ ))} +
+ ); + }; bubble = (
+ {type === 'ANSWER' && renderAttachments()} {DisableSourceFE || type === 'ERROR' || sources?.length === 0 || diff --git a/frontend/src/conversation/ConversationMessages.tsx b/frontend/src/conversation/ConversationMessages.tsx index 5c2150a6..44f38159 100644 --- a/frontend/src/conversation/ConversationMessages.tsx +++ b/frontend/src/conversation/ConversationMessages.tsx @@ -145,6 +145,7 @@ export default function ConversationMessages({ toolCalls={query.tool_calls} feedback={query.feedback} isStreaming={isCurrentlyStreaming} + attachmentsMetadata={query.attachments} handleFeedback={ handleFeedback ? (feedback) => handleFeedback(query, feedback, index) diff --git a/frontend/src/conversation/conversationModels.ts b/frontend/src/conversation/conversationModels.ts index 8c5a479b..b9abf853 100644 --- a/frontend/src/conversation/conversationModels.ts +++ b/frontend/src/conversation/conversationModels.ts @@ -46,7 +46,7 @@ export interface Query { sources?: { title: string; text: string; link: string }[]; tool_calls?: ToolCallsType[]; error?: string; - attachments?: { fileName: string; id: string }[]; + attachments?: { id: string; filename: string }[]; } export interface RetrievalPayload { From f325b5489512aa0e3061ea9e9ad9255693d44576 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Thu, 5 Jun 2025 02:53:43 +0530 Subject: [PATCH 04/15] (fix:get_single_conversation) return attachments with filename --- application/api/user/routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/api/user/routes.py b/application/api/user/routes.py index fb2caf82..f0d706e0 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -257,7 +257,7 @@ class GetSingleConversation(Resource): current_app.logger.error( f"Error retrieving attachment {attachment_id}: {e}", exc_info=True ) - query["attachments_metadata"] = attachment_details + query["attachments"] = attachment_details except Exception as err: current_app.logger.error( f"Error retrieving conversation: {err}", exc_info=True From 787d9e3bf527a6f41f075514bf4e92f320a83c1e Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Thu, 5 Jun 2025 02:54:36 +0530 Subject: [PATCH 05/15] (feat:attachments) ui details in bubble --- .../src/conversation/ConversationBubble.tsx | 109 +++++++++--------- 1 file changed, 54 insertions(+), 55 deletions(-) diff --git a/frontend/src/conversation/ConversationBubble.tsx b/frontend/src/conversation/ConversationBubble.tsx index 296caeda..93edfc9a 100644 --- a/frontend/src/conversation/ConversationBubble.tsx +++ b/frontend/src/conversation/ConversationBubble.tsx @@ -12,7 +12,7 @@ import { import rehypeKatex from 'rehype-katex'; import remarkGfm from 'remark-gfm'; import remarkMath from 'remark-math'; - +import DocumentationDark from '../assets/documentation-dark.svg'; import ChevronDown from '../assets/chevron-down.svg'; import Cloud from '../assets/cloud.svg'; import DocsGPT3 from '../assets/cute_docsgpt3.svg'; @@ -108,40 +108,63 @@ const ConversationBubble = forwardRef< onMouseEnter={() => setIsQuestionHovered(true)} onMouseLeave={() => setIsQuestionHovered(false)} > -
- - } - /> - {!isEditClicked && ( - <> -
+
+ {attachmentsMetadata && attachmentsMetadata.length > 0 && ( +
+ {attachmentsMetadata.map((attachment, index) => (
- {message} +
+ Attachment +
+ + {attachment.filename} +
-
- - + ))} +
)} +
+ + } + /> + {!isEditClicked && ( + <> +
+
+ {message} +
+
+ + + )} +
{isEditClicked && (
{ - if (!attachmentsMetadata || attachmentsMetadata.length === 0) return null; - - return ( -
- {attachmentsMetadata.map((attachment, index) => ( -
- Attachment - - {attachment.filename} - -
- ))} -
- ); - }; bubble = (
- {type === 'ANSWER' && renderAttachments()} {DisableSourceFE || type === 'ERROR' || sources?.length === 0 || From 3bae30c70ca622d13a69c28346ae65574f49783c Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Thu, 5 Jun 2025 03:09:37 +0530 Subject: [PATCH 06/15] (fix:messages) attachments are for questions --- frontend/src/conversation/ConversationMessages.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/conversation/ConversationMessages.tsx b/frontend/src/conversation/ConversationMessages.tsx index 44f38159..49329445 100644 --- a/frontend/src/conversation/ConversationMessages.tsx +++ b/frontend/src/conversation/ConversationMessages.tsx @@ -145,7 +145,6 @@ export default function ConversationMessages({ toolCalls={query.tool_calls} feedback={query.feedback} isStreaming={isCurrentlyStreaming} - attachmentsMetadata={query.attachments} handleFeedback={ handleFeedback ? (feedback) => handleFeedback(query, feedback, index) @@ -224,6 +223,7 @@ export default function ConversationMessages({ handleUpdatedQuestionSubmission={handleQuestionSubmission} questionNumber={index} sources={query.sources} + attachmentsMetadata={query.attachments} /> {renderResponseView(query, index)} From 430822bae357f2ea644953854fb35b9a9b3a60d5 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Fri, 6 Jun 2025 18:54:50 +0530 Subject: [PATCH 07/15] (feat:attach)state manage, follow camelCase --- application/api/user/routes.py | 2 +- frontend/src/conversation/Conversation.tsx | 16 ++++++++++++++-- frontend/src/conversation/ConversationBubble.tsx | 4 ++-- frontend/src/conversation/conversationModels.ts | 2 +- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/application/api/user/routes.py b/application/api/user/routes.py index 518c1382..161b0765 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -267,7 +267,7 @@ class GetSingleConversation(Resource): if attachment: attachment_details.append({ "id": str(attachment["_id"]), - "filename": attachment.get("filename", "Unknown file") + "fileName": attachment.get("filename", "Unknown file") }) except Exception as e: current_app.logger.error( diff --git a/frontend/src/conversation/Conversation.tsx b/frontend/src/conversation/Conversation.tsx index 4f34c26c..c433514f 100644 --- a/frontend/src/conversation/Conversation.tsx +++ b/frontend/src/conversation/Conversation.tsx @@ -27,6 +27,7 @@ import { setConversation, updateConversationId, updateQuery, + selectAttachments, } from './conversationSlice'; export default function Conversation() { @@ -39,6 +40,7 @@ export default function Conversation() { const status = useSelector(selectStatus); const conversationId = useSelector(selectConversationId); const selectedAgent = useSelector(selectSelectedAgent); + const attachments = useSelector(selectAttachments); const [uploadModalState, setUploadModalState] = useState('INACTIVE'); @@ -107,15 +109,25 @@ export default function Conversation() { const trimmedQuestion = question.trim(); if (trimmedQuestion === '') return; + const attachmentMetadata = attachments + .filter((a) => a.id && a.status === 'completed') + .map((a) => ({ id: a.id as string, fileName: a.fileName })); + if (index !== undefined) { if (!isRetry) dispatch(resendQuery({ index, prompt: trimmedQuestion })); handleFetchAnswer({ question: trimmedQuestion, index }); } else { - if (!isRetry) dispatch(addQuery({ prompt: trimmedQuestion })); + if (!isRetry) + dispatch( + addQuery({ + prompt: trimmedQuestion, + attachments: attachmentMetadata, + }), + ); handleFetchAnswer({ question: trimmedQuestion, index }); } }, - [dispatch, handleFetchAnswer], + [dispatch, handleFetchAnswer, attachments], ); const handleFeedback = (query: Query, feedback: FEEDBACK, index: number) => { diff --git a/frontend/src/conversation/ConversationBubble.tsx b/frontend/src/conversation/ConversationBubble.tsx index 93edfc9a..c850ad01 100644 --- a/frontend/src/conversation/ConversationBubble.tsx +++ b/frontend/src/conversation/ConversationBubble.tsx @@ -59,7 +59,7 @@ const ConversationBubble = forwardRef< updated?: boolean, index?: number, ) => void; - attachmentsMetadata?: { id: string; filename: string }[]; + attachmentsMetadata?: { id: string; fileName: string }[]; } >(function ConversationBubble( { @@ -124,7 +124,7 @@ const ConversationBubble = forwardRef< />
- {attachment.filename} + {attachment.fileName}
))} diff --git a/frontend/src/conversation/conversationModels.ts b/frontend/src/conversation/conversationModels.ts index b9abf853..f0ccca05 100644 --- a/frontend/src/conversation/conversationModels.ts +++ b/frontend/src/conversation/conversationModels.ts @@ -46,7 +46,7 @@ export interface Query { sources?: { title: string; text: string; link: string }[]; tool_calls?: ToolCallsType[]; error?: string; - attachments?: { id: string; filename: string }[]; + attachments?: { id: string; fileName: string }[]; } export interface RetrievalPayload { From 4862548e656885a3b673c1890739fccc4862761c Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Fri, 6 Jun 2025 21:52:10 +0530 Subject: [PATCH 08/15] (feat:attach) renaming, semantic identifier names --- frontend/src/conversation/Conversation.tsx | 4 ++-- frontend/src/conversation/ConversationBubble.tsx | 10 +++++----- frontend/src/conversation/ConversationMessages.tsx | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/src/conversation/Conversation.tsx b/frontend/src/conversation/Conversation.tsx index c433514f..3d0d5ac2 100644 --- a/frontend/src/conversation/Conversation.tsx +++ b/frontend/src/conversation/Conversation.tsx @@ -109,7 +109,7 @@ export default function Conversation() { const trimmedQuestion = question.trim(); if (trimmedQuestion === '') return; - const attachmentMetadata = attachments + const filesAttached = attachments .filter((a) => a.id && a.status === 'completed') .map((a) => ({ id: a.id as string, fileName: a.fileName })); @@ -121,7 +121,7 @@ export default function Conversation() { dispatch( addQuery({ prompt: trimmedQuestion, - attachments: attachmentMetadata, + attachments: filesAttached, }), ); handleFetchAnswer({ question: trimmedQuestion, index }); diff --git a/frontend/src/conversation/ConversationBubble.tsx b/frontend/src/conversation/ConversationBubble.tsx index c850ad01..709a9843 100644 --- a/frontend/src/conversation/ConversationBubble.tsx +++ b/frontend/src/conversation/ConversationBubble.tsx @@ -59,7 +59,7 @@ const ConversationBubble = forwardRef< updated?: boolean, index?: number, ) => void; - attachmentsMetadata?: { id: string; fileName: string }[]; + filesAttached?: { id: string; fileName: string }[]; } >(function ConversationBubble( { @@ -75,7 +75,7 @@ const ConversationBubble = forwardRef< questionNumber, isStreaming, handleUpdatedQuestionSubmission, - attachmentsMetadata, + filesAttached, }, ref, ) { @@ -109,9 +109,9 @@ const ConversationBubble = forwardRef< onMouseLeave={() => setIsQuestionHovered(false)} >
- {attachmentsMetadata && attachmentsMetadata.length > 0 && ( + {filesAttached && filesAttached.length > 0 && (
- {attachmentsMetadata.map((attachment, index) => ( + {filesAttached.map((file, index) => (
- {attachment.fileName} + {file.fileName}
))} diff --git a/frontend/src/conversation/ConversationMessages.tsx b/frontend/src/conversation/ConversationMessages.tsx index 49329445..8b31dc9c 100644 --- a/frontend/src/conversation/ConversationMessages.tsx +++ b/frontend/src/conversation/ConversationMessages.tsx @@ -223,7 +223,7 @@ export default function ConversationMessages({ handleUpdatedQuestionSubmission={handleQuestionSubmission} questionNumber={index} sources={query.sources} - attachmentsMetadata={query.attachments} + filesAttached={query.attachments} /> {renderResponseView(query, index)} From 69a4bd415a6bfbbbcabe175400686d82259782f7 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Fri, 6 Jun 2025 21:52:51 +0530 Subject: [PATCH 09/15] (feat:attachment) message input update --- frontend/src/components/MessageInput.tsx | 126 +++++++++++++---------- 1 file changed, 71 insertions(+), 55 deletions(-) diff --git a/frontend/src/components/MessageInput.tsx b/frontend/src/components/MessageInput.tsx index 79487188..046f46b3 100644 --- a/frontend/src/components/MessageInput.tsx +++ b/frontend/src/components/MessageInput.tsx @@ -9,6 +9,7 @@ import ClipIcon from '../assets/clip.svg'; import ExitIcon from '../assets/exit.svg'; import PaperPlane from '../assets/paper_plane.svg'; import SourceIcon from '../assets/source.svg'; +import DocumentationDark from '../assets/documentation-dark.svg'; import SpinnerDark from '../assets/spinner-dark.svg'; import Spinner from '../assets/spinner.svg'; import ToolIcon from '../assets/tool.svg'; @@ -262,71 +263,86 @@ export default function MessageInput({ {attachments.map((attachment, index) => (
+
+ {attachment.status === 'completed' && ( + Attachment + )} + + {attachment.status === 'failed' && ( + Upload failed + )} + + {(attachment.status === 'uploading' || + attachment.status === 'processing') && ( +
+ + + + +
+ )} +
+ {attachment.fileName} - {attachment.status === 'completed' && ( - - )} - - {attachment.status === 'failed' && ( +
))}
From b2df431fa411da9958ca3527a8634e0323a645c6 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Sat, 7 Jun 2025 20:04:33 +0530 Subject: [PATCH 10/15] (feat:attachments) shared conversations route --- application/api/user/routes.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/application/api/user/routes.py b/application/api/user/routes.py index 161b0765..d98e4092 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -2227,7 +2227,7 @@ class GetPubliclySharedConversations(Resource): return make_response( jsonify( { - "sucess": False, + "success": False, "error": "might have broken url or the conversation does not exist", } ), @@ -2236,11 +2236,30 @@ class GetPubliclySharedConversations(Resource): conversation_queries = conversation["queries"][ : (shared["first_n_queries"]) ] + + for query in conversation_queries: + if "attachments" in query and query["attachments"]: + attachment_details = [] + for attachment_id in query["attachments"]: + try: + attachment = attachments_collection.find_one( + {"_id": ObjectId(attachment_id)} + ) + if attachment: + attachment_details.append({ + "id": str(attachment["_id"]), + "fileName": attachment.get("filename", "Unknown file") + }) + except Exception as e: + current_app.logger.error( + f"Error retrieving attachment {attachment_id}: {e}", exc_info=True + ) + query["attachments"] = attachment_details else: return make_response( jsonify( { - "sucess": False, + "success": False, "error": "might have broken url or the conversation does not exist", } ), From c0d385b98391578a72bc281432dd7e867d905380 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Mon, 9 Jun 2025 17:10:12 +0530 Subject: [PATCH 11/15] (refactor:attachments) moved to new uploadSlice --- frontend/src/components/MessageInput.tsx | 2 +- frontend/src/conversation/Conversation.tsx | 14 ++-- .../src/conversation/SharedConversation.tsx | 16 ++++- .../src/conversation/conversationHandlers.ts | 22 +++--- .../src/conversation/conversationModels.ts | 1 - .../src/conversation/conversationSlice.ts | 55 ++------------- .../conversation/sharedConversationSlice.ts | 8 ++- frontend/src/store.ts | 2 + frontend/src/upload/uploadSlice.ts | 67 +++++++++++++++++++ 9 files changed, 122 insertions(+), 65 deletions(-) create mode 100644 frontend/src/upload/uploadSlice.ts diff --git a/frontend/src/components/MessageInput.tsx b/frontend/src/components/MessageInput.tsx index 046f46b3..1db5c2c8 100644 --- a/frontend/src/components/MessageInput.tsx +++ b/frontend/src/components/MessageInput.tsx @@ -18,7 +18,7 @@ import { removeAttachment, selectAttachments, updateAttachment, -} from '../conversation/conversationSlice'; +} from '../upload/uploadSlice'; import { useDarkTheme } from '../hooks'; import { ActiveState } from '../models/misc'; import { diff --git a/frontend/src/conversation/Conversation.tsx b/frontend/src/conversation/Conversation.tsx index 3d0d5ac2..a8d86049 100644 --- a/frontend/src/conversation/Conversation.tsx +++ b/frontend/src/conversation/Conversation.tsx @@ -27,8 +27,11 @@ import { setConversation, updateConversationId, updateQuery, - selectAttachments, } from './conversationSlice'; +import { + selectCompletedAttachments, + clearAttachments, +} from '../upload/uploadSlice'; export default function Conversation() { const { t } = useTranslation(); @@ -40,7 +43,7 @@ export default function Conversation() { const status = useSelector(selectStatus); const conversationId = useSelector(selectConversationId); const selectedAgent = useSelector(selectSelectedAgent); - const attachments = useSelector(selectAttachments); + const completedAttachments = useSelector(selectCompletedAttachments); const [uploadModalState, setUploadModalState] = useState('INACTIVE'); @@ -109,8 +112,8 @@ export default function Conversation() { const trimmedQuestion = question.trim(); if (trimmedQuestion === '') return; - const filesAttached = attachments - .filter((a) => a.id && a.status === 'completed') + const filesAttached = completedAttachments + .filter((a) => a.id) .map((a) => ({ id: a.id as string, fileName: a.fileName })); if (index !== undefined) { @@ -127,7 +130,7 @@ export default function Conversation() { handleFetchAnswer({ question: trimmedQuestion, index }); } }, - [dispatch, handleFetchAnswer, attachments], + [dispatch, handleFetchAnswer, completedAttachments], ); const handleFeedback = (query: Query, feedback: FEEDBACK, index: number) => { @@ -190,6 +193,7 @@ export default function Conversation() { query: { conversationId: null }, }), ); + dispatch(clearAttachments()); }; useEffect(() => { diff --git a/frontend/src/conversation/SharedConversation.tsx b/frontend/src/conversation/SharedConversation.tsx index 3627c048..22efae4e 100644 --- a/frontend/src/conversation/SharedConversation.tsx +++ b/frontend/src/conversation/SharedConversation.tsx @@ -23,6 +23,7 @@ import { setIdentifier, updateQuery, } from './sharedConversationSlice'; +import { selectCompletedAttachments } from '../upload/uploadSlice'; export const SharedConversation = () => { const navigate = useNavigate(); @@ -34,6 +35,7 @@ export const SharedConversation = () => { const date = useSelector(selectDate); const apiKey = useSelector(selectClientAPIKey); const status = useSelector(selectStatus); + const completedAttachments = useSelector(selectCompletedAttachments); const { t } = useTranslation(); const dispatch = useDispatch(); @@ -106,7 +108,19 @@ export const SharedConversation = () => { }) => { question = question.trim(); if (question === '') return; - !isRetry && dispatch(addQuery({ prompt: question })); //dispatch only new queries + + const filesAttached = completedAttachments + .filter((a) => a.id) + .map((a) => ({ id: a.id as string, fileName: a.fileName })); + + !isRetry && + dispatch( + addQuery({ + prompt: question, + attachments: filesAttached, + }), + ); //dispatch only new queries + dispatch(fetchSharedAnswer({ question })); }; useEffect(() => { diff --git a/frontend/src/conversation/conversationHandlers.ts b/frontend/src/conversation/conversationHandlers.ts index 8aa7e424..981dffc3 100644 --- a/frontend/src/conversation/conversationHandlers.ts +++ b/frontend/src/conversation/conversationHandlers.ts @@ -284,6 +284,7 @@ export function handleFetchSharedAnswerStreaming( //for shared conversations signal: AbortSignal, apiKey: string, history: Array = [], + attachments: string[] = [], // Add attachments parameter with default empty array onEvent: (event: MessageEvent) => void, ): Promise { history = history.map((item) => { @@ -300,6 +301,7 @@ export function handleFetchSharedAnswerStreaming( //for shared conversations history: JSON.stringify(history), api_key: apiKey, save_conversation: false, + attachments: attachments.length > 0 ? attachments : undefined, // Add attachments to payload }; conversationService .answerStream(payload, null, signal) @@ -355,6 +357,7 @@ export function handleFetchSharedAnswer( question: string, signal: AbortSignal, apiKey: string, + attachments?: string[], ): Promise< | { result: any; @@ -370,15 +373,18 @@ export function handleFetchSharedAnswer( title: any; } > { + const payload = { + question: question, + api_key: apiKey, + }; + + // Add attachments to payload if they exist + if (attachments && attachments.length > 0) { + payload.attachments = attachments; + } + return conversationService - .answer( - { - question: question, - api_key: apiKey, - }, - null, - signal, - ) + .answer(payload, null, signal) .then((response) => { if (response.ok) { return response.json(); diff --git a/frontend/src/conversation/conversationModels.ts b/frontend/src/conversation/conversationModels.ts index f0ccca05..b16dd6c1 100644 --- a/frontend/src/conversation/conversationModels.ts +++ b/frontend/src/conversation/conversationModels.ts @@ -22,7 +22,6 @@ export interface ConversationState { queries: Query[]; status: Status; conversationId: string | null; - attachments: Attachment[]; } export interface Answer { diff --git a/frontend/src/conversation/conversationSlice.ts b/frontend/src/conversation/conversationSlice.ts index 961260ea..081d5871 100644 --- a/frontend/src/conversation/conversationSlice.ts +++ b/frontend/src/conversation/conversationSlice.ts @@ -3,23 +3,17 @@ import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; import { getConversations } from '../preferences/preferenceApi'; import { setConversations } from '../preferences/preferenceSlice'; import store from '../store'; +import { selectCompletedAttachments } from '../upload/uploadSlice'; import { handleFetchAnswer, handleFetchAnswerSteaming, } from './conversationHandlers'; -import { - Answer, - Query, - Status, - ConversationState, - Attachment, -} from './conversationModels'; +import { Answer, Query, Status, ConversationState } from './conversationModels'; const initialState: ConversationState = { queries: [], status: 'idle', conversationId: null, - attachments: [], }; const API_STREAMING = import.meta.env.VITE_API_STREAMING === 'true'; @@ -44,8 +38,8 @@ export const fetchAnswer = createAsyncThunk< let isSourceUpdated = false; const state = getState() as RootState; - const attachmentIds = state.conversation.attachments - .filter((a) => a.id && a.status === 'completed') + const attachmentIds = selectCompletedAttachments(state) + .filter((a) => a.id) .map((a) => a.id) as string[]; const currentConversationId = state.conversation.conversationId; const conversationIdToSend = isPreview ? null : currentConversationId; @@ -307,39 +301,11 @@ export const conversationSlice = createSlice({ const { index, message } = action.payload; state.queries[index].error = message; }, - setAttachments: (state, action: PayloadAction) => { - state.attachments = action.payload; - }, - addAttachment: (state, action: PayloadAction) => { - state.attachments.push(action.payload); - }, - updateAttachment: ( - state, - action: PayloadAction<{ - taskId: string; - updates: Partial; - }>, - ) => { - const index = state.attachments.findIndex( - (att) => att.taskId === action.payload.taskId, - ); - if (index !== -1) { - state.attachments[index] = { - ...state.attachments[index], - ...action.payload.updates, - }; - } - }, - removeAttachment: (state, action: PayloadAction) => { - state.attachments = state.attachments.filter( - (att) => att.taskId !== action.payload && att.id !== action.payload, - ); - }, + resetConversation: (state) => { state.queries = initialState.queries; state.status = initialState.status; state.conversationId = initialState.conversationId; - state.attachments = initialState.attachments; handleAbort(); }, }, @@ -365,11 +331,6 @@ export const selectQueries = (state: RootState) => state.conversation.queries; export const selectStatus = (state: RootState) => state.conversation.status; -export const selectAttachments = (state: RootState) => - state.conversation.attachments; -export const selectCompletedAttachments = (state: RootState) => - state.conversation.attachments.filter((att) => att.status === 'completed'); - export const { addQuery, updateQuery, @@ -380,10 +341,8 @@ export const { updateStreamingSource, updateToolCalls, setConversation, - setAttachments, - addAttachment, - updateAttachment, - removeAttachment, + setStatus, + raiseError, resetConversation, } = conversationSlice.actions; export default conversationSlice.reducer; diff --git a/frontend/src/conversation/sharedConversationSlice.ts b/frontend/src/conversation/sharedConversationSlice.ts index f0482fa5..ae295412 100644 --- a/frontend/src/conversation/sharedConversationSlice.ts +++ b/frontend/src/conversation/sharedConversationSlice.ts @@ -7,6 +7,7 @@ import { handleFetchSharedAnswer, handleFetchSharedAnswerStreaming, } from './conversationHandlers'; +import { selectCompletedAttachments } from '../upload/uploadSlice'; const API_STREAMING = import.meta.env.VITE_API_STREAMING === 'true'; interface SharedConversationsType { @@ -29,6 +30,10 @@ export const fetchSharedAnswer = createAsyncThunk( async ({ question }, { dispatch, getState, signal }) => { const state = getState() as RootState; + const attachmentIds = selectCompletedAttachments(state) + .filter((a) => a.id) + .map((a) => a.id) as string[]; + if (state.preference && state.sharedConversation.apiKey) { if (API_STREAMING) { await handleFetchSharedAnswerStreaming( @@ -36,7 +41,7 @@ export const fetchSharedAnswer = createAsyncThunk( signal, state.sharedConversation.apiKey, state.sharedConversation.queries, - + attachmentIds, (event) => { const data = JSON.parse(event.data); // check if the 'end' event has been received @@ -92,6 +97,7 @@ export const fetchSharedAnswer = createAsyncThunk( question, signal, state.sharedConversation.apiKey, + attachmentIds, ); if (answer) { let sourcesPrepped = []; diff --git a/frontend/src/store.ts b/frontend/src/store.ts index f26ef3d2..1f25606d 100644 --- a/frontend/src/store.ts +++ b/frontend/src/store.ts @@ -7,6 +7,7 @@ import { prefListenerMiddleware, prefSlice, } from './preferences/preferenceSlice'; +import uploadReducer from './upload/uploadSlice'; const key = localStorage.getItem('DocsGPTApiKey'); const prompt = localStorage.getItem('DocsGPTPrompt'); @@ -52,6 +53,7 @@ const store = configureStore({ preference: prefSlice.reducer, conversation: conversationSlice.reducer, sharedConversation: sharedConversationSlice.reducer, + upload: uploadReducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(prefListenerMiddleware.middleware), diff --git a/frontend/src/upload/uploadSlice.ts b/frontend/src/upload/uploadSlice.ts new file mode 100644 index 00000000..fff56dbd --- /dev/null +++ b/frontend/src/upload/uploadSlice.ts @@ -0,0 +1,67 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { RootState } from '../store'; + +export interface Attachment { + fileName: string; + progress: number; + status: 'uploading' | 'processing' | 'completed' | 'failed'; + taskId: string; + id?: string; + token_count?: number; +} + +interface UploadState { + attachments: Attachment[]; +} + +const initialState: UploadState = { + attachments: [], +}; + +export const uploadSlice = createSlice({ + name: 'upload', + initialState, + reducers: { + addAttachment: (state, action: PayloadAction) => { + state.attachments.push(action.payload); + }, + updateAttachment: ( + state, + action: PayloadAction<{ + taskId: string; + updates: Partial; + }>, + ) => { + const index = state.attachments.findIndex( + (att) => att.taskId === action.payload.taskId, + ); + if (index !== -1) { + state.attachments[index] = { + ...state.attachments[index], + ...action.payload.updates, + }; + } + }, + removeAttachment: (state, action: PayloadAction) => { + state.attachments = state.attachments.filter( + (att) => att.taskId !== action.payload && att.id !== action.payload, + ); + }, + clearAttachments: (state) => { + state.attachments = []; + }, + }, +}); + +export const { + addAttachment, + updateAttachment, + removeAttachment, + clearAttachments, +} = uploadSlice.actions; + +export const selectAttachments = (state: RootState) => state.upload.attachments; +export const selectCompletedAttachments = (state: RootState) => + state.upload.attachments.filter((att) => att.status === 'completed'); + +export default uploadSlice.reducer; From ef35864e1668bf1f1e4b41be396b4215959db6cb Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Mon, 9 Jun 2025 19:50:07 +0530 Subject: [PATCH 12/15] (fix) type error, ui adjust --- frontend/src/assets/alert.svg | 2 +- frontend/src/components/MessageInput.tsx | 9 ++------- frontend/src/conversation/ConversationBubble.tsx | 6 ++++-- frontend/src/conversation/conversationHandlers.ts | 13 +++++-------- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/frontend/src/assets/alert.svg b/frontend/src/assets/alert.svg index 05c7634b..07721b57 100644 --- a/frontend/src/assets/alert.svg +++ b/frontend/src/assets/alert.svg @@ -1,3 +1,3 @@ - + diff --git a/frontend/src/components/MessageInput.tsx b/frontend/src/components/MessageInput.tsx index 1db5c2c8..fb405316 100644 --- a/frontend/src/components/MessageInput.tsx +++ b/frontend/src/components/MessageInput.tsx @@ -263,7 +263,7 @@ export default function MessageInput({ {attachments.map((attachment, index) => (
)} diff --git a/frontend/src/conversation/ConversationBubble.tsx b/frontend/src/conversation/ConversationBubble.tsx index 709a9843..102a8363 100644 --- a/frontend/src/conversation/ConversationBubble.tsx +++ b/frontend/src/conversation/ConversationBubble.tsx @@ -107,13 +107,15 @@ const ConversationBubble = forwardRef<
setIsQuestionHovered(true)} onMouseLeave={() => setIsQuestionHovered(false)} + className={className} >
{filesAttached && filesAttached.length > 0 && ( -
+
{filesAttached.map((file, index) => (
@@ -132,7 +134,7 @@ const ConversationBubble = forwardRef< )}
= [], - attachments: string[] = [], // Add attachments parameter with default empty array + attachments: string[] = [], onEvent: (event: MessageEvent) => void, ): Promise { history = history.map((item) => { @@ -301,7 +301,7 @@ export function handleFetchSharedAnswerStreaming( //for shared conversations history: JSON.stringify(history), api_key: apiKey, save_conversation: false, - attachments: attachments.length > 0 ? attachments : undefined, // Add attachments to payload + attachments: attachments.length > 0 ? attachments : undefined, }; conversationService .answerStream(payload, null, signal) @@ -376,13 +376,10 @@ export function handleFetchSharedAnswer( const payload = { question: question, api_key: apiKey, + attachments: + attachments && attachments.length > 0 ? attachments : undefined, }; - // Add attachments to payload if they exist - if (attachments && attachments.length > 0) { - payload.attachments = attachments; - } - return conversationService .answer(payload, null, signal) .then((response) => { From 68dc14c5a173fceefa4019fad029271145856b29 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Tue, 10 Jun 2025 02:50:07 +0530 Subject: [PATCH 13/15] (feat:attachments) clear after passing --- frontend/src/conversation/conversationSlice.ts | 10 +++++++++- frontend/src/conversation/sharedConversationSlice.ts | 9 ++++++++- frontend/src/upload/uploadSlice.ts | 4 +++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/frontend/src/conversation/conversationSlice.ts b/frontend/src/conversation/conversationSlice.ts index 081d5871..036962c4 100644 --- a/frontend/src/conversation/conversationSlice.ts +++ b/frontend/src/conversation/conversationSlice.ts @@ -3,7 +3,10 @@ import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; import { getConversations } from '../preferences/preferenceApi'; import { setConversations } from '../preferences/preferenceSlice'; import store from '../store'; -import { selectCompletedAttachments } from '../upload/uploadSlice'; +import { + selectCompletedAttachments, + clearAttachments, +} from '../upload/uploadSlice'; import { handleFetchAnswer, handleFetchAnswerSteaming, @@ -41,6 +44,11 @@ export const fetchAnswer = createAsyncThunk< const attachmentIds = selectCompletedAttachments(state) .filter((a) => a.id) .map((a) => a.id) as string[]; + + if (attachmentIds.length > 0) { + dispatch(clearAttachments()); + } + const currentConversationId = state.conversation.conversationId; const conversationIdToSend = isPreview ? null : currentConversationId; const save_conversation = isPreview ? false : true; diff --git a/frontend/src/conversation/sharedConversationSlice.ts b/frontend/src/conversation/sharedConversationSlice.ts index ae295412..df2650a3 100644 --- a/frontend/src/conversation/sharedConversationSlice.ts +++ b/frontend/src/conversation/sharedConversationSlice.ts @@ -7,7 +7,10 @@ import { handleFetchSharedAnswer, handleFetchSharedAnswerStreaming, } from './conversationHandlers'; -import { selectCompletedAttachments } from '../upload/uploadSlice'; +import { + selectCompletedAttachments, + clearAttachments, +} from '../upload/uploadSlice'; const API_STREAMING = import.meta.env.VITE_API_STREAMING === 'true'; interface SharedConversationsType { @@ -34,6 +37,10 @@ export const fetchSharedAnswer = createAsyncThunk( .filter((a) => a.id) .map((a) => a.id) as string[]; + if (attachmentIds.length > 0) { + dispatch(clearAttachments()); + } + if (state.preference && state.sharedConversation.apiKey) { if (API_STREAMING) { await handleFetchSharedAnswerStreaming( diff --git a/frontend/src/upload/uploadSlice.ts b/frontend/src/upload/uploadSlice.ts index fff56dbd..732c69bc 100644 --- a/frontend/src/upload/uploadSlice.ts +++ b/frontend/src/upload/uploadSlice.ts @@ -48,7 +48,9 @@ export const uploadSlice = createSlice({ ); }, clearAttachments: (state) => { - state.attachments = []; + state.attachments = state.attachments.filter( + (att) => att.status === 'uploading' || att.status === 'processing', + ); }, }, }); From b4e2588a24ed43d6b96c7c859e10f9198493279c Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Tue, 10 Jun 2025 17:02:24 +0530 Subject: [PATCH 14/15] (fix:prompts) save when content changes --- frontend/src/preferences/PromptsModal.tsx | 57 ++++++++++++++++------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/frontend/src/preferences/PromptsModal.tsx b/frontend/src/preferences/PromptsModal.tsx index f61ba4ee..14e05e86 100644 --- a/frontend/src/preferences/PromptsModal.tsx +++ b/frontend/src/preferences/PromptsModal.tsx @@ -184,29 +184,54 @@ export default function PromptsModal({ setEditPromptName: (name: string) => void; editPromptContent: string; setEditPromptContent: (content: string) => void; - currentPromptEdit: { name: string; id: string; type: string }; + currentPromptEdit: { + name: string; + id: string; + type: string; + content?: string; + }; handleAddPrompt?: () => void; handleEditPrompt?: (id: string, type: string) => void; }) { const [disableSave, setDisableSave] = React.useState(true); - const handlePrompNameChange = (edit: boolean, newName: string) => { - const nameExists = existingPrompts.find( - (prompt) => newName === prompt.name, - ); - - if (newName && !nameExists) { - setDisableSave(false); - } else { - setDisableSave(true); - } - + const handlePromptNameChange = (edit: boolean, newName: string) => { if (edit) { + const nameExists = existingPrompts.find( + (prompt) => + newName === prompt.name && prompt.id !== currentPromptEdit.id, + ); + const nameValid = newName && !nameExists; + const contentChanged = editPromptContent !== currentPromptEdit.content; + + setDisableSave(!(nameValid || contentChanged)); setEditPromptName(newName); } else { + const nameExists = existingPrompts.find( + (prompt) => newName === prompt.name, + ); + setDisableSave(!(newName && !nameExists)); setNewPromptName(newName); } }; + const handleContentChange = (edit: boolean, newContent: string) => { + if (edit) { + const contentChanged = newContent !== currentPromptEdit.content; + const nameValid = + editPromptName && + !existingPrompts.find( + (prompt) => + editPromptName === prompt.name && + prompt.id !== currentPromptEdit.id, + ); + + setDisableSave(!(nameValid || contentChanged)); + setEditPromptContent(newContent); + } else { + setNewPromptContent(newContent); + } + }; + let view; if (type === 'ADD') { @@ -215,9 +240,9 @@ export default function PromptsModal({ setModalState={setModalState} handleAddPrompt={handleAddPrompt} newPromptName={newPromptName} - setNewPromptName={handlePrompNameChange.bind(null, false)} + setNewPromptName={handlePromptNameChange.bind(null, false)} newPromptContent={newPromptContent} - setNewPromptContent={setNewPromptContent} + setNewPromptContent={handleContentChange.bind(null, false)} disableSave={disableSave} /> ); @@ -227,9 +252,9 @@ export default function PromptsModal({ setModalState={setModalState} handleEditPrompt={handleEditPrompt} editPromptName={editPromptName} - setEditPromptName={handlePrompNameChange.bind(null, true)} + setEditPromptName={handlePromptNameChange.bind(null, true)} editPromptContent={editPromptContent} - setEditPromptContent={setEditPromptContent} + setEditPromptContent={handleContentChange.bind(null, true)} currentPromptEdit={currentPromptEdit} disableSave={disableSave} /> From 041d600e454bc5d318ff76928684918d5edba325 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Tue, 10 Jun 2025 18:00:11 +0530 Subject: [PATCH 15/15] (feat:prompts) delete after confirmation --- frontend/src/locale/en.json | 3 +- frontend/src/locale/es.json | 3 +- frontend/src/locale/jp.json | 3 +- frontend/src/locale/ru.json | 3 +- frontend/src/locale/zh-TW.json | 3 +- frontend/src/locale/zh.json | 3 +- frontend/src/settings/Prompts.tsx | 64 ++++++++++++++++++++++++------- 7 files changed, 62 insertions(+), 20 deletions(-) diff --git a/frontend/src/locale/en.json b/frontend/src/locale/en.json index 7fda12c8..b538cdde 100644 --- a/frontend/src/locale/en.json +++ b/frontend/src/locale/en.json @@ -245,7 +245,8 @@ "promptName": "Prompt Name", "promptText": "Prompt Text", "save": "Save", - "nameExists": "Name already exists" + "nameExists": "Name already exists", + "deleteConfirmation": "Are you sure you want to delete the prompt '{{name}}'?" }, "chunk": { "add": "Add Chunk", diff --git a/frontend/src/locale/es.json b/frontend/src/locale/es.json index 2f0260b0..bcdebfcb 100644 --- a/frontend/src/locale/es.json +++ b/frontend/src/locale/es.json @@ -245,7 +245,8 @@ "promptName": "Nombre del Prompt", "promptText": "Texto del Prompt", "save": "Guardar", - "nameExists": "El nombre ya existe" + "nameExists": "El nombre ya existe", + "deleteConfirmation": "¿Estás seguro de que deseas eliminar el prompt '{{name}}'?" }, "chunk": { "add": "Agregar Fragmento", diff --git a/frontend/src/locale/jp.json b/frontend/src/locale/jp.json index 3c1cb64b..d004e0dc 100644 --- a/frontend/src/locale/jp.json +++ b/frontend/src/locale/jp.json @@ -245,7 +245,8 @@ "promptName": "プロンプト名", "promptText": "プロンプトテキスト", "save": "保存", - "nameExists": "名前が既に存在します" + "nameExists": "名前が既に存在します", + "deleteConfirmation": "プロンプト「{{name}}」を削除してもよろしいですか?" }, "chunk": { "add": "チャンクを追加", diff --git a/frontend/src/locale/ru.json b/frontend/src/locale/ru.json index 8d62a577..95c7a228 100644 --- a/frontend/src/locale/ru.json +++ b/frontend/src/locale/ru.json @@ -245,7 +245,8 @@ "promptName": "Название подсказки", "promptText": "Текст подсказки", "save": "Сохранить", - "nameExists": "Название уже существует" + "nameExists": "Название уже существует", + "deleteConfirmation": "Вы уверены, что хотите удалить подсказку «{{name}}»?" }, "chunk": { "add": "Добавить фрагмент", diff --git a/frontend/src/locale/zh-TW.json b/frontend/src/locale/zh-TW.json index 366d0f56..36baa8b1 100644 --- a/frontend/src/locale/zh-TW.json +++ b/frontend/src/locale/zh-TW.json @@ -245,7 +245,8 @@ "promptName": "提示名稱", "promptText": "提示文字", "save": "儲存", - "nameExists": "名稱已存在" + "nameExists": "名稱已存在", + "deleteConfirmation": "您確定要刪除提示「{{name}}」嗎?" }, "chunk": { "add": "新增區塊", diff --git a/frontend/src/locale/zh.json b/frontend/src/locale/zh.json index 6d4e590c..804f5fb2 100644 --- a/frontend/src/locale/zh.json +++ b/frontend/src/locale/zh.json @@ -245,7 +245,8 @@ "promptName": "提示名称", "promptText": "提示文本", "save": "保存", - "nameExists": "名称已存在" + "nameExists": "名称已存在", + "deleteConfirmation": "您确定要删除提示'{{name}}'吗?" }, "chunk": { "add": "添加块", diff --git a/frontend/src/settings/Prompts.tsx b/frontend/src/settings/Prompts.tsx index 055bf0b1..84b739ae 100644 --- a/frontend/src/settings/Prompts.tsx +++ b/frontend/src/settings/Prompts.tsx @@ -7,6 +7,7 @@ import Dropdown from '../components/Dropdown'; import { ActiveState, PromptProps } from '../models/misc'; import { selectToken } from '../preferences/preferenceSlice'; import PromptsModal from '../preferences/PromptsModal'; +import ConfirmationModal from '../modals/ConfirmationModal'; export default function Prompts({ prompts, @@ -40,6 +41,11 @@ export default function Prompts({ const [modalState, setModalState] = React.useState('INACTIVE'); const { t } = useTranslation(); + const [promptToDelete, setPromptToDelete] = React.useState<{ + id: string; + name: string; + } | null>(null); + const handleAddPrompt = async () => { try { const response = await userService.createPrompt( @@ -69,20 +75,37 @@ export default function Prompts({ }; const handleDeletePrompt = (id: string) => { - setPrompts(prompts.filter((prompt) => prompt.id !== id)); - userService - .deletePrompt({ id }, token) - .then((response) => { - if (!response.ok) { - throw new Error('Failed to delete prompt'); - } - if (prompts.length > 0) { - onSelectPrompt(prompts[0].name, prompts[0].id, prompts[0].type); - } - }) - .catch((error) => { - console.error(error); - }); + const promptToRemove = prompts.find((prompt) => prompt.id === id); + if (promptToRemove) { + setPromptToDelete({ id, name: promptToRemove.name }); + } + }; + + const confirmDeletePrompt = () => { + if (promptToDelete) { + setPrompts(prompts.filter((prompt) => prompt.id !== promptToDelete.id)); + userService + .deletePrompt({ id: promptToDelete.id }, token) + .then((response) => { + if (!response.ok) { + throw new Error('Failed to delete prompt'); + } + if (prompts.length > 0) { + const firstPrompt = prompts.find((p) => p.id !== promptToDelete.id); + if (firstPrompt) { + onSelectPrompt( + firstPrompt.name, + firstPrompt.id, + firstPrompt.type, + ); + } + } + }) + .catch((error) => { + console.error(error); + }); + setPromptToDelete(null); + } }; const handleFetchPromptContent = async (id: string) => { @@ -202,6 +225,19 @@ export default function Prompts({ handleAddPrompt={handleAddPrompt} handleEditPrompt={handleSaveChanges} /> + {promptToDelete && ( + setPromptToDelete(null)} + submitLabel={t('modals.deleteConv.delete')} + handleSubmit={confirmDeletePrompt} + handleCancel={() => setPromptToDelete(null)} + variant="danger" + /> + )} ); }