All files / src/components/features/support/ChatbotWidget ChatMessage.tsx

17.82% Statements 18/101
100% Branches 0/0
0% Functions 0/1
17.82% Lines 18/101

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 1021x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                                                                                                                                                                        
'use client';
 
/**
 * ChatMessage - Individual message bubble in the chat
 */
 
import React from 'react';
import { ChatbotMessage, ChatbotQuickAction, SupportArticle } from '@/types/support';
 
export interface ChatMessageProps {
  /** The message to display */
  message: ChatbotMessage;
  /** Handler for quick action clicks */
  onQuickAction?: (action: ChatbotQuickAction) => void;
  /** Handler for article clicks */
  onArticleClick?: (article: SupportArticle) => void;
}
 
export default function ChatMessage({
  message,
  onQuickAction,
  onArticleClick}: ChatMessageProps) {
  const isBot = message.type === 'bot';

  return (
    <div className={`flex ${isBot ? 'justify-start' : 'justify-end'}`}>
      <div
        className={`
          max-w-[85%] rounded-lg px-4 py-2
          ${
            isBot
              ? 'bg-white text-gray-800 shadow-sm border border-gray-100'
              : 'bg-blue-600 text-white'
          }
        `}
      >
        {/* Message content */}
        <p className="text-sm whitespace-pre-wrap">{message.content}</p>

        {/* Suggested articles */}
        {isBot && message.suggestedArticles && message.suggestedArticles.length > 0 && (
          <div className="mt-3 space-y-2">
            <p className="text-xs text-gray-500 font-medium">Suggested articles:</p>
            {message.suggestedArticles.map((article) => (
              <button
                key={article.id}
                type="button"
                onClick={() => onArticleClick?.(article)}
                className="
                  block w-full text-left p-2 rounded
                  bg-gray-50 hover:bg-gray-100
                  border border-gray-200
                  transition-colors
                "
              >
                <p className="text-sm font-medium text-blue-600 hover:text-blue-700">
                  {article.title}
                </p>
                <p className="text-xs text-gray-500 line-clamp-2">{article.summary}</p>
              </button>
            ))}
          </div>
        )}

        {/* Quick actions within message */}
        {isBot && message.quickActions && message.quickActions.length > 0 && (
          <div className="mt-3 flex flex-wrap gap-2">
            {message.quickActions.map((action, index) => (
              <button
                key={index}
                type="button"
                onClick={() => onQuickAction?.(action)}
                className="
                  px-3 py-1.5 text-xs font-medium
                  bg-blue-50 text-blue-600
                  rounded-full
                  hover:bg-blue-100
                  transition-colors
                "
              >
                {action.label}
              </button>
            ))}
          </div>
        )}

        {/* Timestamp */}
        <p
          className={`
            text-[10px] mt-1
            ${isBot ? 'text-gray-400' : 'text-blue-200'}
          `}
        >
          {new Date(message.timestamp).toLocaleTimeString([], {
            hour: '2-digit',
            minute: '2-digit'})}
        </p>
      </div>
    </div>
  );
}