From b8bfd23a9cb08021323aa16c496d3f2202c0906b Mon Sep 17 00:00:00 2001 From: Nuwan Date: Thu, 5 Feb 2026 19:43:46 +0530 Subject: [PATCH] feat(14-01): render attachment messages with distinct styling and metadata - Import formatFileSize from attachmentValidation service - Add isAttachmentMessage helper to detect attachment messages - Render attachment messages with light blue background (#e3f2fd) - Display paperclip icon with accessible aria-label - Show '[name] attached [filename]' format with file size if available - Update PropTypes to include optional attachment fields - Maintain React.memo for performance --- .../components/client/chat/JKChatMessage.js | 124 +++++++++++++----- 1 file changed, 92 insertions(+), 32 deletions(-) diff --git a/jam-ui/src/components/client/chat/JKChatMessage.js b/jam-ui/src/components/client/chat/JKChatMessage.js index 4f35da668..da7479df2 100644 --- a/jam-ui/src/components/client/chat/JKChatMessage.js +++ b/jam-ui/src/components/client/chat/JKChatMessage.js @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { formatTimestamp } from '../../../utils/formatTimestamp'; +import { formatFileSize } from '../../../services/attachmentValidation'; /** * Get initials for avatar from sender name @@ -8,67 +9,120 @@ import { formatTimestamp } from '../../../utils/formatTimestamp'; * @param {string} name - Sender name * @returns {string} Initials (e.g., "JD" for "John Doe") */ -const getInitials = (name) => { +const getInitials = name => { if (!name) return '?'; const parts = name.trim().split(' '); if (parts.length === 1) return parts[0][0].toUpperCase(); return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase(); }; +/** + * Check if message is an attachment + * @param {Object} message - Message object + * @returns {boolean} True if message has attachment + */ +const isAttachmentMessage = message => { + return message.attachmentId && message.attachmentName; +}; + /** * JKChatMessage - Individual message display component * * Displays a single chat message with avatar, sender name, message text, and timestamp. + * Supports attachment messages with distinct styling and metadata display. * * Features: * - Avatar with initials (first + last name) * - Sender name in bold * - Relative timestamp (via formatTimestamp utility) * - Message text with word wrapping + * - Attachment display with paperclip icon, filename, and size * - React.memo for performance optimization */ const JKChatMessage = ({ message }) => { - const { senderName, message: text, createdAt } = message; + const { senderName, message: text, createdAt, attachmentName, attachmentSize } = message; - return ( -
- {/* Avatar */} -
- {getInitials(senderName)} + backgroundColor: '#e3f2fd', + border: '1px solid #bbdefb' + }; + + const fileIconStyle = { + fontSize: '16px', + marginRight: '4px' + }; + + return ( +
+ {/* Avatar */} +
{getInitials(senderName)}
+ + {/* Attachment content */} +
+
+ {senderName || 'Unknown'} + {formatTimestamp(createdAt)} +
+
+ + 📎 + + + attached {attachmentName} + {attachmentSize && ( + ({formatFileSize(attachmentSize)}) + )} + +
+
+ ); + } + + // Regular text message + return ( +
+ {/* Avatar */} +
{getInitials(senderName)}
{/* Message content */}
- - {senderName || 'Unknown'} - - - {formatTimestamp(createdAt)} - -
-
- {text} + {senderName || 'Unknown'} + {formatTimestamp(createdAt)}
+
{text}
); @@ -79,8 +133,14 @@ JKChatMessage.propTypes = { id: PropTypes.string.isRequired, senderId: PropTypes.string.isRequired, senderName: PropTypes.string, - message: PropTypes.string.isRequired, - createdAt: PropTypes.string.isRequired + message: PropTypes.string, // Optional for attachment-only messages + createdAt: PropTypes.string.isRequired, + // Attachment fields (optional) + attachmentId: PropTypes.string, + attachmentName: PropTypes.string, + attachmentType: PropTypes.oneOf(['notation', 'audio']), + purpose: PropTypes.string, + attachmentSize: PropTypes.number }).isRequired };