@memori.ai/memori-react 7.33.3 → 7.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/dist/components/ChatBubble/ChatBubble.js +10 -2
- package/dist/components/ChatBubble/ChatBubble.js.map +1 -1
- package/dist/components/ChatHistoryDrawer/ChatHistory.js +49 -29
- package/dist/components/ChatHistoryDrawer/ChatHistory.js.map +1 -1
- package/dist/components/MemoriWidget/MemoriWidget.js +5 -0
- package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/dist/components/UploadButton/UploadButton.css +32 -12
- package/dist/components/UploadButton/UploadButton.js +96 -20
- package/dist/components/UploadButton/UploadButton.js.map +1 -1
- package/dist/components/UploadButton/UploadDocuments/UploadDocuments.d.ts +12 -0
- package/dist/components/UploadButton/UploadDocuments/UploadDocuments.js +80 -57
- package/dist/components/UploadButton/UploadDocuments/UploadDocuments.js.map +1 -1
- package/dist/components/UploadButton/UploadImages/UploadImages.d.ts +5 -0
- package/dist/components/UploadButton/UploadImages/UploadImages.js +10 -42
- package/dist/components/UploadButton/UploadImages/UploadImages.js.map +1 -1
- package/dist/helpers/constants.d.ts +3 -0
- package/dist/helpers/constants.js +4 -1
- package/dist/helpers/constants.js.map +1 -1
- package/dist/helpers/sanitizer.d.ts +6 -0
- package/dist/helpers/sanitizer.js +41 -0
- package/dist/helpers/sanitizer.js.map +1 -0
- package/dist/helpers/tts/ttsVoiceUtility.d.ts +158 -0
- package/dist/helpers/tts/ttsVoiceUtility.js +192 -0
- package/dist/helpers/tts/ttsVoiceUtility.js.map +1 -0
- package/dist/helpers/tts/useTTS.d.ts +26 -0
- package/dist/helpers/tts/useTTS.js +294 -0
- package/dist/helpers/tts/useTTS.js.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/locales/en.json +3 -1
- package/dist/locales/it.json +2 -0
- package/esm/components/ChatBubble/ChatBubble.js +10 -2
- package/esm/components/ChatBubble/ChatBubble.js.map +1 -1
- package/esm/components/ChatHistoryDrawer/ChatHistory.js +49 -29
- package/esm/components/ChatHistoryDrawer/ChatHistory.js.map +1 -1
- package/esm/components/MemoriWidget/MemoriWidget.js +5 -0
- package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/esm/components/UploadButton/UploadButton.css +32 -12
- package/esm/components/UploadButton/UploadButton.js +96 -20
- package/esm/components/UploadButton/UploadButton.js.map +1 -1
- package/esm/components/UploadButton/UploadDocuments/UploadDocuments.d.ts +12 -0
- package/esm/components/UploadButton/UploadDocuments/UploadDocuments.js +81 -58
- package/esm/components/UploadButton/UploadDocuments/UploadDocuments.js.map +1 -1
- package/esm/components/UploadButton/UploadImages/UploadImages.d.ts +5 -0
- package/esm/components/UploadButton/UploadImages/UploadImages.js +10 -42
- package/esm/components/UploadButton/UploadImages/UploadImages.js.map +1 -1
- package/esm/helpers/constants.d.ts +3 -0
- package/esm/helpers/constants.js +3 -0
- package/esm/helpers/constants.js.map +1 -1
- package/esm/helpers/sanitizer.d.ts +6 -0
- package/esm/helpers/sanitizer.js +32 -0
- package/esm/helpers/sanitizer.js.map +1 -0
- package/esm/helpers/tts/ttsVoiceUtility.d.ts +158 -0
- package/esm/helpers/tts/ttsVoiceUtility.js +182 -0
- package/esm/helpers/tts/ttsVoiceUtility.js.map +1 -0
- package/esm/helpers/tts/useTTS.d.ts +26 -0
- package/esm/helpers/tts/useTTS.js +290 -0
- package/esm/helpers/tts/useTTS.js.map +1 -0
- package/esm/index.js +8 -0
- package/esm/index.js.map +1 -1
- package/esm/locales/en.json +3 -1
- package/esm/locales/it.json +2 -0
- package/package.json +2 -2
- package/src/__snapshots__/index.test.tsx.snap +41 -0
- package/src/components/ChatBubble/ChatBubble.stories.tsx +35 -0
- package/src/components/ChatBubble/ChatBubble.tsx +25 -7
- package/src/components/ChatHistoryDrawer/ChatHistory.stories.tsx +40 -1
- package/src/components/ChatHistoryDrawer/ChatHistory.tsx +106 -58
- package/src/components/MemoriWidget/MemoriWidget.tsx +9 -0
- package/src/components/UploadButton/UploadButton.css +32 -12
- package/src/components/UploadButton/UploadButton.tsx +145 -21
- package/src/components/UploadButton/UploadDocuments/UploadDocuments.tsx +105 -87
- package/src/components/UploadButton/UploadImages/UploadImages.tsx +12 -76
- package/src/components/UploadButton/__snapshots__/UploadButton.test.tsx.snap +0 -6
- package/src/helpers/constants.ts +5 -0
- package/src/index.test.tsx +65 -0
- package/src/index.tsx +16 -0
- package/src/locales/en.json +3 -1
- package/src/locales/it.json +2 -0
|
@@ -377,7 +377,7 @@ const ChatHistoryDrawer = ({
|
|
|
377
377
|
loginToken,
|
|
378
378
|
}: Props) => {
|
|
379
379
|
const { t } = useTranslation();
|
|
380
|
-
const {
|
|
380
|
+
const { getUserChatLogsByTokenPaged } = apiClient.chatLogs;
|
|
381
381
|
|
|
382
382
|
const textCurrentChat = `${t(
|
|
383
383
|
'write_and_speak.conversationStartedLabel'
|
|
@@ -392,6 +392,7 @@ const ChatHistoryDrawer = ({
|
|
|
392
392
|
const [chatLogs, setChatLogs] = useState<ChatLog[]>([]);
|
|
393
393
|
const [selectedChatLog, setSelectedChatLog] = useState<ChatLog | null>(null);
|
|
394
394
|
const [currentPage, setCurrentPage] = useState(1);
|
|
395
|
+
const [indexPage, setIndexPage] = useState(0);
|
|
395
396
|
const [searchText, setSearchText] = useState('');
|
|
396
397
|
const [isLoading, setIsLoading] = useState(false);
|
|
397
398
|
const [error, setError] = useState<string | null>(null);
|
|
@@ -399,6 +400,8 @@ const ChatHistoryDrawer = ({
|
|
|
399
400
|
const [dateRange, setDateRange] = useState<
|
|
400
401
|
'today' | 'yesterday' | 'last_7_days' | 'last_30_days' | 'all'
|
|
401
402
|
>('all');
|
|
403
|
+
const [totalItems, setTotalItems] = useState(0);
|
|
404
|
+
const [totalPages, setTotalPages] = useState(0);
|
|
402
405
|
|
|
403
406
|
const formatDateForAPI = (date: Date) => {
|
|
404
407
|
const year = date.getFullYear();
|
|
@@ -466,75 +469,111 @@ const ChatHistoryDrawer = ({
|
|
|
466
469
|
}
|
|
467
470
|
};
|
|
468
471
|
|
|
469
|
-
const fetchChatLogs = useCallback(
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const { dateFrom: dateFromTemp, dateTo: dateToTemp } = formatDateRangeForAPI(dateRange) ?? {};
|
|
485
|
-
dateFrom = dateFromTemp ?? '';
|
|
486
|
-
dateTo = dateToTemp ?? '';
|
|
472
|
+
const fetchChatLogs = useCallback(
|
|
473
|
+
async (
|
|
474
|
+
dateRangeValue:
|
|
475
|
+
| 'today'
|
|
476
|
+
| 'yesterday'
|
|
477
|
+
| 'last_7_days'
|
|
478
|
+
| 'last_30_days'
|
|
479
|
+
| 'all' = 'all'
|
|
480
|
+
) => {
|
|
481
|
+
if (!loginToken) {
|
|
482
|
+
setError(
|
|
483
|
+
t('errorFetchingSession') ||
|
|
484
|
+
'Login token required to fetch chat history'
|
|
485
|
+
);
|
|
486
|
+
return;
|
|
487
487
|
}
|
|
488
488
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
);
|
|
495
|
-
|
|
496
|
-
|
|
489
|
+
// Calculate the index based on current page
|
|
490
|
+
const calculatedIndex = ITEMS_PER_PAGE * (currentPage - 1);
|
|
491
|
+
setIndexPage(calculatedIndex);
|
|
492
|
+
|
|
493
|
+
setIsLoading(true);
|
|
494
|
+
setError(null);
|
|
495
|
+
let response;
|
|
496
|
+
try {
|
|
497
|
+
if (dateRangeValue === 'all') {
|
|
498
|
+
response = await getUserChatLogsByTokenPaged(
|
|
499
|
+
loginToken,
|
|
500
|
+
memori.engineMemoriID ?? '',
|
|
501
|
+
calculatedIndex,
|
|
502
|
+
ITEMS_PER_PAGE,
|
|
503
|
+
undefined,
|
|
504
|
+
undefined,
|
|
505
|
+
false,
|
|
506
|
+
);
|
|
507
|
+
} else {
|
|
508
|
+
const { dateFrom, dateTo } =
|
|
509
|
+
formatDateRangeForAPI(dateRangeValue) ?? {};
|
|
510
|
+
response = await getUserChatLogsByTokenPaged(
|
|
511
|
+
loginToken,
|
|
512
|
+
memori.engineMemoriID ?? '',
|
|
513
|
+
calculatedIndex,
|
|
514
|
+
ITEMS_PER_PAGE,
|
|
515
|
+
dateFrom ?? undefined,
|
|
516
|
+
dateTo ?? undefined,
|
|
517
|
+
false,
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
const res = response;
|
|
522
|
+
|
|
523
|
+
// Sort the chat logs by date
|
|
524
|
+
const sortedChatLogs = res.chatLogs.sort((a: ChatLog, b: ChatLog) => {
|
|
497
525
|
const dateA = Math.max(
|
|
498
|
-
...a.lines.map(l => new Date(l.timestamp).getTime())
|
|
526
|
+
...a.lines.map((l: any) => new Date(l.timestamp).getTime())
|
|
499
527
|
);
|
|
500
528
|
const dateB = Math.max(
|
|
501
|
-
...b.lines.map(l => new Date(l.timestamp).getTime())
|
|
529
|
+
...b.lines.map((l: any) => new Date(l.timestamp).getTime())
|
|
502
530
|
);
|
|
503
531
|
return sortOrder === 'desc' ? dateB - dateA : dateA - dateB;
|
|
504
|
-
})
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
setChatLogs(sortedChatLogs);
|
|
535
|
+
setTotalItems(res.count || sortedChatLogs.length);
|
|
536
|
+
setTotalPages(
|
|
537
|
+
Math.ceil((res.count || sortedChatLogs.length) / ITEMS_PER_PAGE)
|
|
538
|
+
);
|
|
539
|
+
} catch (err) {
|
|
540
|
+
setError(t('errorFetchingSession') || 'Error loading chat history');
|
|
541
|
+
console.error('Error fetching chat logs:', err);
|
|
542
|
+
} finally {
|
|
543
|
+
setIsLoading(false);
|
|
544
|
+
}
|
|
545
|
+
},
|
|
546
|
+
[loginToken, memori.engineMemoriID, currentPage, sortOrder, apiUrl]
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
useEffect(() => {
|
|
550
|
+
if (open) {
|
|
551
|
+
setCurrentPage(1); // Reset to first page when opening
|
|
552
|
+
setIndexPage(0); // Reset index to 0
|
|
511
553
|
}
|
|
512
|
-
}, [
|
|
554
|
+
}, [open]);
|
|
513
555
|
|
|
556
|
+
// Reset to first page when changing sort order or date range
|
|
514
557
|
useEffect(() => {
|
|
515
|
-
if (open)
|
|
516
|
-
|
|
558
|
+
if (open) {
|
|
559
|
+
setCurrentPage(1);
|
|
560
|
+
setIndexPage(0);
|
|
561
|
+
}
|
|
562
|
+
}, [sortOrder, dateRange, open]);
|
|
563
|
+
|
|
564
|
+
// Fetch chat logs when current page or date range changes
|
|
565
|
+
useEffect(() => {
|
|
566
|
+
if (open && currentPage > 0) {
|
|
567
|
+
fetchChatLogs(dateRange);
|
|
568
|
+
}
|
|
569
|
+
}, [currentPage, dateRange, fetchChatLogs, open]);
|
|
517
570
|
|
|
518
571
|
const debouncedSearch = useMemo(
|
|
519
572
|
() => debounce((value: string) => setSearchText(value), DEBOUNCE_DELAY),
|
|
520
573
|
[]
|
|
521
574
|
);
|
|
522
|
-
|
|
523
|
-
const
|
|
524
|
-
return chatLogs.filter(
|
|
525
|
-
c =>
|
|
526
|
-
c.lines.some(l =>
|
|
527
|
-
l.text.toLowerCase().includes(searchText.toLowerCase())
|
|
528
|
-
) && c.lines.length > 1
|
|
529
|
-
);
|
|
530
|
-
}, [chatLogs, searchText]);
|
|
531
|
-
|
|
532
|
-
const totalPages = Math.ceil(filteredChatLogs.length / ITEMS_PER_PAGE);
|
|
533
|
-
|
|
534
|
-
const paginatedChatLogs = useMemo(() => {
|
|
535
|
-
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
|
|
536
|
-
return filteredChatLogs.slice(startIndex, startIndex + ITEMS_PER_PAGE);
|
|
537
|
-
}, [filteredChatLogs, currentPage]);
|
|
575
|
+
// Remove client-side pagination since we're now using server-side pagination
|
|
576
|
+
const paginatedChatLogs = chatLogs;
|
|
538
577
|
|
|
539
578
|
const handleResumeChat = async () => {
|
|
540
579
|
if (selectedChatLog) {
|
|
@@ -613,7 +652,7 @@ const ChatHistoryDrawer = ({
|
|
|
613
652
|
);
|
|
614
653
|
}
|
|
615
654
|
|
|
616
|
-
if (
|
|
655
|
+
if (chatLogs.length === 0) {
|
|
617
656
|
return (
|
|
618
657
|
<div className="memori-chat-history-drawer--no-results">
|
|
619
658
|
<p>
|
|
@@ -849,7 +888,10 @@ const ChatHistoryDrawer = ({
|
|
|
849
888
|
<div className="memori-chat-history-drawer--pagination">
|
|
850
889
|
<Button
|
|
851
890
|
primary
|
|
852
|
-
onClick={() =>
|
|
891
|
+
onClick={() => {
|
|
892
|
+
setCurrentPage(p => Math.max(1, p - 1));
|
|
893
|
+
// fetchChatLogs will be triggered by the useEffect that depends on currentPage
|
|
894
|
+
}}
|
|
853
895
|
disabled={currentPage === 1}
|
|
854
896
|
className="memori-chat-history-drawer--pagination--button"
|
|
855
897
|
>
|
|
@@ -863,7 +905,10 @@ const ChatHistoryDrawer = ({
|
|
|
863
905
|
</span>
|
|
864
906
|
<Button
|
|
865
907
|
primary
|
|
866
|
-
onClick={() =>
|
|
908
|
+
onClick={() => {
|
|
909
|
+
setCurrentPage(p => Math.min(totalPages, p + 1));
|
|
910
|
+
// fetchChatLogs will be triggered by the useEffect that depends on currentPage
|
|
911
|
+
}}
|
|
867
912
|
disabled={currentPage === totalPages}
|
|
868
913
|
className="memori-chat-history-drawer--pagination--button"
|
|
869
914
|
>
|
|
@@ -992,7 +1037,10 @@ const ChatHistoryDrawer = ({
|
|
|
992
1037
|
| 'yesterday'
|
|
993
1038
|
| 'last_7_days'
|
|
994
1039
|
| 'last_30_days'
|
|
1040
|
+
| 'all'
|
|
995
1041
|
);
|
|
1042
|
+
setCurrentPage(1);
|
|
1043
|
+
// fetchChatLogs will be triggered by the useEffect that depends on dateRange
|
|
996
1044
|
}}
|
|
997
1045
|
/>
|
|
998
1046
|
</div>
|
|
@@ -834,6 +834,15 @@ const MemoriWidget = ({
|
|
|
834
834
|
msg = msg + ' ' + findMediaDocument.content;
|
|
835
835
|
}
|
|
836
836
|
|
|
837
|
+
// Handle multiple documents
|
|
838
|
+
const mediaDocuments = media?.filter(
|
|
839
|
+
m => !m.mediumID && m.properties?.isAttachedFile
|
|
840
|
+
);
|
|
841
|
+
if (mediaDocuments && mediaDocuments.length > 0) {
|
|
842
|
+
const documentContents = mediaDocuments.map(doc => doc.content).join(' ');
|
|
843
|
+
msg = msg + ' ' + documentContents;
|
|
844
|
+
}
|
|
845
|
+
|
|
837
846
|
// Add chat reference link to the message if it exists
|
|
838
847
|
// if (chatLogID) {
|
|
839
848
|
// msg =
|
|
@@ -380,10 +380,20 @@
|
|
|
380
380
|
/* Positioned containers */
|
|
381
381
|
.memori--error-message-container,
|
|
382
382
|
.memori--login-tip {
|
|
383
|
-
position:
|
|
383
|
+
position: fixed;
|
|
384
384
|
z-index: 1001;
|
|
385
|
-
|
|
386
|
-
|
|
385
|
+
top: 120px;
|
|
386
|
+
right: 20px;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/* Responsive adjustments for mobile */
|
|
390
|
+
@media (max-width: 768px) {
|
|
391
|
+
.memori--error-message-container,
|
|
392
|
+
.memori--login-tip {
|
|
393
|
+
top: 100px;
|
|
394
|
+
right: 10px;
|
|
395
|
+
max-width: calc(100vw - 20px);
|
|
396
|
+
}
|
|
387
397
|
}
|
|
388
398
|
|
|
389
399
|
/* File Stats */
|
|
@@ -425,6 +435,25 @@
|
|
|
425
435
|
background-color: #fa5252;
|
|
426
436
|
}
|
|
427
437
|
|
|
438
|
+
/* Document count indicator */
|
|
439
|
+
.memori--document-count {
|
|
440
|
+
position: absolute;
|
|
441
|
+
z-index: 5;
|
|
442
|
+
top: -8px;
|
|
443
|
+
right: -8px;
|
|
444
|
+
padding: 1px 6px;
|
|
445
|
+
border-radius: 10px;
|
|
446
|
+
background-color: #51cf66;
|
|
447
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
448
|
+
color: white;
|
|
449
|
+
font-size: 11px;
|
|
450
|
+
font-weight: bold;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.memori--document-count-full {
|
|
454
|
+
background-color: #fa5252;
|
|
455
|
+
}
|
|
456
|
+
|
|
428
457
|
/* Styled upload slots info */
|
|
429
458
|
.memori--upload-slots-info {
|
|
430
459
|
margin-left: 4px;
|
|
@@ -465,15 +494,6 @@
|
|
|
465
494
|
transform: translateX(4px);
|
|
466
495
|
}
|
|
467
496
|
|
|
468
|
-
/* Error container positioning */
|
|
469
|
-
.memori--error-message-container {
|
|
470
|
-
position: absolute;
|
|
471
|
-
z-index: 1001;
|
|
472
|
-
right: 0;
|
|
473
|
-
bottom: 50px;
|
|
474
|
-
min-width: 250px;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
497
|
input.memori--upload-title-input {
|
|
478
498
|
min-height: 2.5rem;
|
|
479
499
|
border: 1px solid #e0e0e0;
|
|
@@ -9,10 +9,11 @@ import UploadDocuments from './UploadDocuments/UploadDocuments';
|
|
|
9
9
|
import UploadImages from './UploadImages/UploadImages';
|
|
10
10
|
import { useTranslation } from 'react-i18next';
|
|
11
11
|
import memoriApiClient from '@memori.ai/memori-api-client';
|
|
12
|
+
import { MAX_DOCUMENTS_PER_MESSAGE } from '../../helpers/constants';
|
|
12
13
|
|
|
13
14
|
// Constants
|
|
14
15
|
const MAX_IMAGES = 5;
|
|
15
|
-
const MAX_DOCUMENTS =
|
|
16
|
+
const MAX_DOCUMENTS = MAX_DOCUMENTS_PER_MESSAGE;
|
|
16
17
|
|
|
17
18
|
// Props interface
|
|
18
19
|
interface UploadManagerProps {
|
|
@@ -107,15 +108,12 @@ const UploadButton: React.FC<UploadManagerProps> = ({
|
|
|
107
108
|
};
|
|
108
109
|
}, []);
|
|
109
110
|
|
|
110
|
-
// Handler for document files -
|
|
111
|
+
// Handler for document files - now supports multiple documents
|
|
111
112
|
const handleDocumentFiles = (
|
|
112
113
|
files: { name: string; id: string; content: string; mimeType: string }[]
|
|
113
114
|
) => {
|
|
114
115
|
if (files.length === 0) return;
|
|
115
116
|
|
|
116
|
-
// For simplicity, we only take the first file
|
|
117
|
-
const file = files[0];
|
|
118
|
-
|
|
119
117
|
// Funzione helper per fare escape dell'HTML nei valori degli attributi
|
|
120
118
|
const escapeAttributeValue = (text: string) => {
|
|
121
119
|
return text
|
|
@@ -126,34 +124,134 @@ const UploadButton: React.FC<UploadManagerProps> = ({
|
|
|
126
124
|
.replace(/>/g, '>');
|
|
127
125
|
};
|
|
128
126
|
|
|
129
|
-
|
|
130
|
-
const
|
|
127
|
+
// Process each document file
|
|
128
|
+
const processedDocuments = files.map(file => {
|
|
129
|
+
const escapedFileName = escapeAttributeValue(file.name);
|
|
130
|
+
const formattedContent = `<document_attachment filename="${escapedFileName}" type="${file.mimeType}">
|
|
131
131
|
|
|
132
132
|
${file.content}
|
|
133
133
|
|
|
134
134
|
</document_attachment>`;
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
const imageFiles = documentPreviewFiles.filter(
|
|
138
|
-
(file: any) => file.type === 'image'
|
|
139
|
-
);
|
|
140
|
-
// Replace existing file with new one
|
|
141
|
-
setDocumentPreviewFiles([
|
|
142
|
-
{
|
|
136
|
+
return {
|
|
143
137
|
name: file.name,
|
|
144
138
|
id: file.id,
|
|
145
139
|
content: formattedContent,
|
|
146
|
-
type: '
|
|
140
|
+
type: 'document',
|
|
147
141
|
mimeType: file.mimeType,
|
|
148
|
-
}
|
|
142
|
+
};
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Keep existing images and add new documents
|
|
146
|
+
const imageFiles = documentPreviewFiles.filter(
|
|
147
|
+
(file: any) => file.type === 'image'
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
setDocumentPreviewFiles([
|
|
151
|
+
...processedDocuments,
|
|
149
152
|
...imageFiles,
|
|
150
153
|
]);
|
|
151
154
|
|
|
152
155
|
setIsLoading(false);
|
|
153
156
|
};
|
|
154
157
|
|
|
158
|
+
// Document validation and error handling
|
|
159
|
+
const validateDocumentFile = (file: File): boolean => {
|
|
160
|
+
const fileExt = `.${file.name.split('.').pop()?.toLowerCase()}`;
|
|
161
|
+
const ALLOWED_FILE_TYPES = ['.pdf', '.txt', '.json', '.xlsx', '.csv', '.md'];
|
|
162
|
+
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
|
|
163
|
+
|
|
164
|
+
if (!ALLOWED_FILE_TYPES.includes(fileExt)) {
|
|
165
|
+
addError({
|
|
166
|
+
message: `File type "${fileExt}" is not supported. Please use: ${ALLOWED_FILE_TYPES.join(', ')}`,
|
|
167
|
+
severity: 'error',
|
|
168
|
+
});
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (file.size > MAX_FILE_SIZE) {
|
|
173
|
+
addError({
|
|
174
|
+
message: `File "${file.name}" exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit`,
|
|
175
|
+
severity: 'error',
|
|
176
|
+
});
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return true;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// Validate total payload size
|
|
184
|
+
const validatePayloadSize = (newDocuments: { name: string; id: string; content: string; mimeType: string }[]): boolean => {
|
|
185
|
+
const { MAX_TOTAL_MESSAGE_PAYLOAD } = require('../../helpers/constants');
|
|
186
|
+
|
|
187
|
+
const existingDocuments = documentPreviewFiles.filter(
|
|
188
|
+
(file: any) => file.type === 'document'
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const allDocuments = [...existingDocuments, ...newDocuments];
|
|
192
|
+
const totalPayloadSize = allDocuments.reduce((total, doc) => total + doc.content.length, 0);
|
|
193
|
+
|
|
194
|
+
if (totalPayloadSize > MAX_TOTAL_MESSAGE_PAYLOAD) {
|
|
195
|
+
addError({
|
|
196
|
+
message: `Total document content exceeds ${MAX_TOTAL_MESSAGE_PAYLOAD} characters limit. Please remove some documents.`,
|
|
197
|
+
severity: 'error',
|
|
198
|
+
});
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return true;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// Handle document upload errors
|
|
206
|
+
const handleDocumentError = (error: { message: string; severity: 'error' | 'warning' | 'info' }) => {
|
|
207
|
+
addError(error);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// Image validation and error handling
|
|
211
|
+
const validateImageFile = (file: File): boolean => {
|
|
212
|
+
const fileExt = `.${file.name.split('.').pop()?.toLowerCase()}`;
|
|
213
|
+
const ALLOWED_FILE_TYPES = ['.jpg', '.jpeg', '.png'];
|
|
214
|
+
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
|
|
215
|
+
|
|
216
|
+
if (
|
|
217
|
+
!ALLOWED_FILE_TYPES.includes(fileExt) &&
|
|
218
|
+
!file.type.startsWith('image/')
|
|
219
|
+
) {
|
|
220
|
+
addError({
|
|
221
|
+
message: `File type "${fileExt}" is not supported. Please use: ${ALLOWED_FILE_TYPES.join(', ')}`,
|
|
222
|
+
severity: 'error',
|
|
223
|
+
});
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (file.size > MAX_FILE_SIZE) {
|
|
228
|
+
addError({
|
|
229
|
+
message: `File "${file.name}" exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit`,
|
|
230
|
+
severity: 'error',
|
|
231
|
+
});
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return true;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// Handle image upload errors
|
|
239
|
+
const handleImageError = (error: { message: string; severity: 'error' | 'warning' | 'info' }) => {
|
|
240
|
+
addError(error);
|
|
241
|
+
};
|
|
242
|
+
|
|
155
243
|
// When document option is clicked
|
|
156
244
|
const handleDocumentClick = () => {
|
|
245
|
+
// Check if document limit has been reached
|
|
246
|
+
if (hasReachedDocumentLimit) {
|
|
247
|
+
addError({
|
|
248
|
+
message: `Maximum ${MAX_DOCUMENTS} documents allowed.`,
|
|
249
|
+
severity: 'error',
|
|
250
|
+
});
|
|
251
|
+
closeMenu();
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
157
255
|
// Find the actual button in the UploadDocuments component and click it
|
|
158
256
|
const documentButtonElement = documentRef.current?.querySelector('button');
|
|
159
257
|
if (documentButtonElement) {
|
|
@@ -234,6 +332,17 @@ ${file.content}
|
|
|
234
332
|
</div>
|
|
235
333
|
)}
|
|
236
334
|
|
|
335
|
+
{/* Document count indicator */}
|
|
336
|
+
{currentDocumentCount > 0 && (
|
|
337
|
+
<div
|
|
338
|
+
className={cx('memori--document-count', {
|
|
339
|
+
'memori--document-count-full': hasReachedDocumentLimit,
|
|
340
|
+
})}
|
|
341
|
+
>
|
|
342
|
+
{currentDocumentCount}/{MAX_DOCUMENTS}
|
|
343
|
+
</div>
|
|
344
|
+
)}
|
|
345
|
+
|
|
237
346
|
{/* Floating menu */}
|
|
238
347
|
{menuOpen && (
|
|
239
348
|
<div className="memori--upload-menu" ref={menuRef}>
|
|
@@ -242,11 +351,20 @@ ${file.content}
|
|
|
242
351
|
'memori--upload-menu-item--disabled': hasReachedDocumentLimit,
|
|
243
352
|
})}
|
|
244
353
|
onClick={handleDocumentClick}
|
|
354
|
+
title={
|
|
355
|
+
hasReachedDocumentLimit
|
|
356
|
+
? t('upload.maxDocumentsReached', { max: MAX_DOCUMENTS }) ??
|
|
357
|
+
`Maximum ${MAX_DOCUMENTS} documents already uploaded`
|
|
358
|
+
: remainingDocumentSlots === 1
|
|
359
|
+
? t('upload.lastDocumentSlot') ?? 'Upload last document'
|
|
360
|
+
: t('upload.uploadDocument', { remaining: remainingDocumentSlots }) ??
|
|
361
|
+
`Upload document (${remainingDocumentSlots} remaining)`
|
|
362
|
+
}
|
|
245
363
|
>
|
|
246
364
|
<DocumentIcon className="memori--upload-menu-icon" />
|
|
247
365
|
<span>
|
|
248
366
|
{t('upload.uploadDocument') ?? 'Upload document'}
|
|
249
|
-
{currentDocumentCount > 0 && (
|
|
367
|
+
{/* {currentDocumentCount > 0 && (
|
|
250
368
|
<span className="memori--upload-slots-info">
|
|
251
369
|
{hasReachedDocumentLimit
|
|
252
370
|
? ` (${t('upload.maxReached') ?? 'Max reached'})`
|
|
@@ -254,7 +372,7 @@ ${file.content}
|
|
|
254
372
|
t('upload.remaining') ?? 'remaining'
|
|
255
373
|
})`}
|
|
256
374
|
</span>
|
|
257
|
-
)}
|
|
375
|
+
)} */}
|
|
258
376
|
</span>
|
|
259
377
|
</div>
|
|
260
378
|
|
|
@@ -279,7 +397,7 @@ ${file.content}
|
|
|
279
397
|
<ImageIcon className="memori--upload-menu-icon-image" />
|
|
280
398
|
<span>
|
|
281
399
|
{t('upload.uploadImage') ?? 'Upload image'}
|
|
282
|
-
{currentImageCount > 0 && (
|
|
400
|
+
{/* {currentImageCount > 0 && (
|
|
283
401
|
<span className="memori--upload-slots-info">
|
|
284
402
|
{hasReachedImageLimit
|
|
285
403
|
? ` (${t('upload.maxReached') ?? 'Max reached'})`
|
|
@@ -287,7 +405,7 @@ ${file.content}
|
|
|
287
405
|
t('upload.remaining') ?? 'remaining'
|
|
288
406
|
})`}
|
|
289
407
|
</span>
|
|
290
|
-
)}
|
|
408
|
+
)} */}
|
|
291
409
|
</span>
|
|
292
410
|
</div>
|
|
293
411
|
</div>
|
|
@@ -299,7 +417,10 @@ ${file.content}
|
|
|
299
417
|
setDocumentPreviewFiles={handleDocumentFiles}
|
|
300
418
|
maxDocuments={MAX_DOCUMENTS}
|
|
301
419
|
documentPreviewFiles={documentPreviewFiles}
|
|
302
|
-
|
|
420
|
+
onLoadingChange={handleLoadingChange}
|
|
421
|
+
onDocumentError={handleDocumentError}
|
|
422
|
+
onValidateFile={validateDocumentFile}
|
|
423
|
+
onValidatePayloadSize={validatePayloadSize}
|
|
303
424
|
/>
|
|
304
425
|
</div>
|
|
305
426
|
|
|
@@ -314,6 +435,8 @@ ${file.content}
|
|
|
314
435
|
onLoadingChange={handleLoadingChange}
|
|
315
436
|
maxImages={MAX_IMAGES}
|
|
316
437
|
memoriID={memoriID}
|
|
438
|
+
onImageError={handleImageError}
|
|
439
|
+
onValidateImageFile={validateImageFile}
|
|
317
440
|
/>
|
|
318
441
|
</div>
|
|
319
442
|
|
|
@@ -321,6 +444,7 @@ ${file.content}
|
|
|
321
444
|
<div className="memori--error-message-container">
|
|
322
445
|
{errors.map((error, index) => (
|
|
323
446
|
<Alert
|
|
447
|
+
className='memori--error-message-alert'
|
|
324
448
|
key={`${error.message}-${index}`}
|
|
325
449
|
open={true}
|
|
326
450
|
type={error.severity}
|