From 7efc5c5d9420de8b1673d073288a43466482b835 Mon Sep 17 00:00:00 2001 From: laxmanhalaki Date: Mon, 10 Nov 2025 20:10:41 +0530 Subject: [PATCH] mention feature addd --- .../workNote/WorkNoteChat/WorkNoteChat.tsx | 135 ++++++++++++++++-- 1 file changed, 126 insertions(+), 9 deletions(-) diff --git a/src/components/workNote/WorkNoteChat/WorkNoteChat.tsx b/src/components/workNote/WorkNoteChat/WorkNoteChat.tsx index 2cd30c3..dc42934 100644 --- a/src/components/workNote/WorkNoteChat/WorkNoteChat.tsx +++ b/src/components/workNote/WorkNoteChat/WorkNoteChat.tsx @@ -105,9 +105,10 @@ const getStatusText = (status: string) => { }; const formatMessage = (content: string) => { - // Enhanced mention highlighting with better regex + // Enhanced mention highlighting - Blue color with extra bold font for high visibility + // Matches: @test user11 or @Test User11 (any case, stops before next sentence/punctuation) return content - .replace(/@([\w\s]+)(?=\s|$|[.,!?])/g, '@$1') + .replace(/@([A-Za-z0-9]+(?:\s+[A-Za-z0-9]+)*?)(?=\s+(?:[a-z][a-z\s]*)?(?:[.,!?;:]|$))/g, '@$1') .replace(/\n/g, '
'); }; @@ -689,6 +690,26 @@ export function WorkNoteChat({ requestId, onBack, messages: externalMessages, on const handleSendMessage = async () => { if (message.trim() || selectedFiles.length > 0) { + // Extract mentions from message + const mentions = extractMentions(message); + + // Find mentioned user IDs from participants + const mentionedUserIds = mentions + .map(mentionedName => { + const participant = participants.find(p => + p.name.toLowerCase().includes(mentionedName.toLowerCase()) + ); + console.log('[Mention Match] Looking for:', mentionedName, 'Found participant:', participant ? `${participant.name} (${(participant as any)?.userId})` : 'NOT FOUND'); + return (participant as any)?.userId; + }) + .filter(Boolean); + + console.log('[WorkNoteChat] 📝 MESSAGE:', message); + console.log('[WorkNoteChat] 👥 ALL PARTICIPANTS:', participants.map(p => ({ name: p.name, userId: (p as any)?.userId }))); + console.log('[WorkNoteChat] 🎯 MENTIONS EXTRACTED:', mentions); + console.log('[WorkNoteChat] 🆔 USER IDS FOUND:', mentionedUserIds); + console.log('[WorkNoteChat] 📤 SENDING TO BACKEND:', { message, mentions: mentionedUserIds }); + const attachments = selectedFiles.map(file => ({ name: file.name, url: URL.createObjectURL(file), @@ -707,19 +728,26 @@ export function WorkNoteChat({ requestId, onBack, messages: externalMessages, on minute: 'numeric', hour12: true }), - mentions: extractMentions(message), + mentions: mentions, isHighPriority: message.includes('!important') || message.includes('urgent'), attachments: attachments.length > 0 ? attachments : undefined, isCurrentUser: true }; - // console.log('new message ->', newMessage, onSend); + // If external onSend provided, delegate to caller (RequestDetail will POST and refresh) if (onSend) { try { await onSend(message, selectedFiles); } catch { /* ignore */ } } else { - // Fallback: call backend directly + // Fallback: call backend directly with mentions try { - await createWorkNoteMultipart(effectiveRequestId, { message }, selectedFiles); + await createWorkNoteMultipart( + effectiveRequestId, + { + message, + mentions: mentionedUserIds // Send mentioned user IDs to backend + }, + selectedFiles + ); const rows = await getWorkNotes(effectiveRequestId); const mapped = Array.isArray(rows) ? rows.map((m: any) => { const noteUserId = m.userId || m.user_id; @@ -1025,7 +1053,8 @@ export function WorkNoteChat({ requestId, onBack, messages: externalMessages, on ]; const extractMentions = (text: string): string[] => { - const mentionRegex = /@([\w\s]+)(?=\s|$|[.,!?])/g; + // Use the SAME regex pattern as formatMessage to ensure consistency + const mentionRegex = /@([A-Za-z0-9]+(?:\s+[A-Za-z0-9]+)*?)(?=\s+(?:[a-z][a-z\s]*)?(?:[.,!?;:]|$))/g; const mentions: string[] = []; let match; while ((match = mentionRegex.exec(text)) !== null) { @@ -1033,6 +1062,7 @@ export function WorkNoteChat({ requestId, onBack, messages: externalMessages, on mentions.push(match[1].trim()); } } + console.log('[Extract Mentions] Found:', mentions, 'from text:', text); return mentions; }; @@ -1361,8 +1391,95 @@ export function WorkNoteChat({ requestId, onBack, messages: externalMessages, on )} - {/* Textarea with Emoji Picker */} + {/* Textarea with Mention Dropdown and Emoji Picker */}
+ {/* Mention Suggestions Dropdown - Shows above textarea */} + {(() => { + const lastAtIndex = message.lastIndexOf('@'); + const hasAt = lastAtIndex >= 0; + const textAfterAt = hasAt ? message.slice(lastAtIndex + 1) : ''; + + // Don't show if: + // 1. No @ found + // 2. Text after @ is too long (>20 chars) + // 3. Text after @ ends with a space (completed mention) + // 4. Text after @ contains a space (already selected a user) + const endsWithSpace = textAfterAt.endsWith(' '); + const containsSpace = textAfterAt.trim().includes(' '); + const shouldShowDropdown = hasAt && + textAfterAt.length <= 20 && + !endsWithSpace && + !containsSpace; + + console.log('[Mention Debug]', { + hasAt, + textAfterAt: `"${textAfterAt}"`, + endsWithSpace, + containsSpace, + shouldShowDropdown, + participantsCount: participants.length + }); + + if (!shouldShowDropdown) return null; + + const searchTerm = textAfterAt.toLowerCase(); + const filteredParticipants = participants.filter(p => { + // Exclude current user from mention suggestions + const isCurrentUserInList = (p as any).userId === currentUserId; + if (isCurrentUserInList) return false; + + // Filter by search term (empty search term shows all) + if (searchTerm) { + return p.name.toLowerCase().includes(searchTerm); + } + return true; // Show all if no search term + }); + + console.log('[Mention Debug] Filtered participants:', filteredParticipants.length); + + return ( +
+

💬 Mention someone

+
+ {filteredParticipants.length > 0 ? ( + filteredParticipants.map((participant, idx) => ( + + )) + ) : ( +

+ {searchTerm ? `No participants found matching "${searchTerm}"` : 'No other participants available'} +

+ )} +
+
+ ); + })()} +