readysite / website / frontend / components / ConversationList.jsx
4.4 KB
ConversationList.jsx
import * as React from 'react';

// Format relative time (e.g., "2 minutes ago", "Yesterday")
function formatRelativeTime(dateStr) {
  const date = new Date(dateStr);
  const now = new Date();
  const diffMs = now - date;
  const diffMins = Math.floor(diffMs / (1000 * 60));
  const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
  const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));

  if (diffMins < 1) return 'Just now';
  if (diffMins < 60) return `${diffMins} min${diffMins > 1 ? 's' : ''} ago`;
  if (diffHours < 24) return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`;
  if (diffDays === 1) return 'Yesterday';
  if (diffDays < 7) return `${diffDays} days ago`;
  return date.toLocaleDateString();
}

export function ConversationList({ conversations, current, onSelect, onNew, onDelete }) {
  return (
    <div className="flex flex-col h-full">
      {/* Header */}
      <div className="flex items-center justify-between px-4 py-3 border-b border-base-300 bg-base-200/50">
        <h3 className="font-semibold text-sm">Conversations</h3>
        <button
          onClick={onNew}
          className="btn btn-ghost btn-xs btn-circle"
          title="New conversation"
        >
          <svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 4v16m8-8H4" />
          </svg>
        </button>
      </div>

      {/* Conversation List */}
      <div className="flex-1 overflow-y-auto p-2">
        {conversations.length > 0 ? (
          <div className="space-y-1">
            {conversations.map(conv => (
              <ConversationItem
                key={conv.id}
                conversation={conv}
                isActive={current?.id === conv.id}
                onSelect={() => onSelect(conv.id)}
                onDelete={() => onDelete(conv.id)}
              />
            ))}
          </div>
        ) : (
          <div className="text-center py-8 text-base-content/50">
            <svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8 mx-auto mb-2 opacity-50" fill="none" viewBox="0 0 24 24" stroke="currentColor">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
            </svg>
            <p className="text-sm">No conversations yet</p>
            <button
              onClick={onNew}
              className="btn btn-primary btn-sm mt-3"
            >
              Start a conversation
            </button>
          </div>
        )}
      </div>
    </div>
  );
}

function ConversationItem({ conversation, isActive, onSelect, onDelete }) {
  const [showDelete, setShowDelete] = React.useState(false);

  const handleDelete = (e) => {
    e.stopPropagation();
    if (confirm('Delete this conversation?')) {
      onDelete();
    }
  };

  return (
    <div
      onClick={onSelect}
      onMouseEnter={() => setShowDelete(true)}
      onMouseLeave={() => setShowDelete(false)}
      className={`relative p-3 rounded-lg cursor-pointer transition-all duration-200 ${
        isActive
          ? 'bg-primary/10 border border-primary/30'
          : 'hover:bg-base-200 border border-transparent'
      }`}
    >
      <div className="flex items-start justify-between gap-2">
        <div className="flex-1 min-w-0">
          <p className={`text-sm font-medium truncate ${isActive ? 'text-primary' : ''}`}>
            {conversation.title || 'New Conversation'}
          </p>
          <p className="text-xs text-base-content/50 mt-0.5">
            {formatRelativeTime(conversation.updated || conversation.created)}
          </p>
        </div>
        {showDelete && !isActive && (
          <button
            onClick={handleDelete}
            className="btn btn-ghost btn-xs btn-circle text-base-content/40 hover:text-error flex-shrink-0"
          >
            <svg xmlns="http://www.w3.org/2000/svg" className="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
            </svg>
          </button>
        )}
      </div>
    </div>
  );
}
← Back