Girish Jeswani commited on
Commit
637ec04
·
1 Parent(s): 69fa630

add action buttons to advisor messages

Browse files
phd-advisor-frontend/src/components/MessageBubble.js CHANGED
@@ -1,10 +1,45 @@
1
- import React from 'react';
2
- import { Reply } from 'lucide-react';
3
  import { advisors, getAdvisorColors } from '../data/advisors';
4
  import { useTheme } from '../contexts/ThemeContext';
5
 
6
- const MessageBubble = ({ message, onClick, showReplyButton = false }) => {
 
 
 
 
 
 
7
  const { isDark } = useTheme();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
  if (message.type === 'user') {
10
  return (
@@ -26,6 +61,7 @@ const MessageBubble = ({ message, onClick, showReplyButton = false }) => {
26
  const advisor = advisors[message.advisorId];
27
  const Icon = advisor.icon;
28
  const colors = getAdvisorColors(message.advisorId, isDark);
 
29
 
30
  return (
31
  <div className="advisor-message-container">
@@ -36,12 +72,11 @@ const MessageBubble = ({ message, onClick, showReplyButton = false }) => {
36
  <Icon style={{ color: colors.color }} />
37
  </div>
38
  <div
39
- className={`advisor-message-bubble ${showReplyButton ? 'clickable' : ''}`}
40
  style={{
41
  backgroundColor: colors.bgColor,
42
  borderColor: colors.color + '40'
43
  }}
44
- onClick={onClick}
45
  >
46
  <div className="advisor-message-header">
47
  <h4
@@ -50,6 +85,7 @@ const MessageBubble = ({ message, onClick, showReplyButton = false }) => {
50
  >
51
  {advisor.name}
52
  {message.isReply && <span className="reply-badge">↳ Reply</span>}
 
53
  </h4>
54
  <span
55
  className="message-time"
@@ -74,20 +110,66 @@ const MessageBubble = ({ message, onClick, showReplyButton = false }) => {
74
  </p>
75
  {showReplyButton && (
76
  <div className="message-actions">
77
- <button
78
- className="reply-button"
79
- onClick={(e) => {
80
- e.stopPropagation();
81
- onClick();
82
- }}
83
- style={{
84
- color: colors.color,
85
- borderColor: colors.color + '40'
86
- }}
87
- >
88
- <Reply size={14} />
89
- <span>Reply</span>
90
- </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  </div>
92
  )}
93
  </div>
 
1
+ import React, { useState } from 'react';
2
+ import { Reply, Copy, Check, Maximize2 } from 'lucide-react';
3
  import { advisors, getAdvisorColors } from '../data/advisors';
4
  import { useTheme } from '../contexts/ThemeContext';
5
 
6
+ const MessageBubble = ({
7
+ message,
8
+ onReply,
9
+ onCopy,
10
+ onExpand,
11
+ showReplyButton = false
12
+ }) => {
13
  const { isDark } = useTheme();
14
+ const [showTooltip, setShowTooltip] = useState(null);
15
+ const [copiedStates, setCopiedStates] = useState({});
16
+
17
+ const handleCopy = async (messageId, content) => {
18
+ try {
19
+ await navigator.clipboard.writeText(content);
20
+ setCopiedStates(prev => ({ ...prev, [messageId]: true }));
21
+ if (onCopy) onCopy(messageId, content);
22
+
23
+ // Reset the copied state after 2 seconds
24
+ setTimeout(() => {
25
+ setCopiedStates(prev => ({ ...prev, [messageId]: false }));
26
+ }, 2000);
27
+ } catch (err) {
28
+ console.error('Failed to copy text: ', err);
29
+ }
30
+ };
31
+
32
+ const handleExpand = (messageId, advisorId) => {
33
+ if (onExpand) onExpand(messageId, advisorId);
34
+ };
35
+
36
+ const showTooltipWithDelay = (tooltipType) => {
37
+ setTimeout(() => setShowTooltip(tooltipType), 500);
38
+ };
39
+
40
+ const hideTooltip = () => {
41
+ setShowTooltip(null);
42
+ };
43
 
44
  if (message.type === 'user') {
45
  return (
 
61
  const advisor = advisors[message.advisorId];
62
  const Icon = advisor.icon;
63
  const colors = getAdvisorColors(message.advisorId, isDark);
64
+ const isCopied = copiedStates[message.id];
65
 
66
  return (
67
  <div className="advisor-message-container">
 
72
  <Icon style={{ color: colors.color }} />
73
  </div>
74
  <div
75
+ className="advisor-message-bubble"
76
  style={{
77
  backgroundColor: colors.bgColor,
78
  borderColor: colors.color + '40'
79
  }}
 
80
  >
81
  <div className="advisor-message-header">
82
  <h4
 
85
  >
86
  {advisor.name}
87
  {message.isReply && <span className="reply-badge">↳ Reply</span>}
88
+ {message.isExpansion && <span className="expansion-badge">⤴ Expanded</span>}
89
  </h4>
90
  <span
91
  className="message-time"
 
110
  </p>
111
  {showReplyButton && (
112
  <div className="message-actions">
113
+ <div className="action-buttons">
114
+ {/* Reply Button */}
115
+ <div className="tooltip-container">
116
+ <button
117
+ className="action-button"
118
+ onClick={() => onReply && onReply(message)}
119
+ onMouseEnter={() => showTooltipWithDelay('reply')}
120
+ onMouseLeave={hideTooltip}
121
+ style={{
122
+ color: colors.color,
123
+ borderColor: colors.color + '40'
124
+ }}
125
+ >
126
+ <Reply size={14} />
127
+ </button>
128
+ {showTooltip === 'reply' && (
129
+ <div className="tooltip">Reply to this message</div>
130
+ )}
131
+ </div>
132
+
133
+ {/* Copy Button */}
134
+ <div className="tooltip-container">
135
+ <button
136
+ className="action-button"
137
+ onClick={() => handleCopy(message.id, message.content)}
138
+ onMouseEnter={() => showTooltipWithDelay('copy')}
139
+ onMouseLeave={hideTooltip}
140
+ style={{
141
+ color: isCopied ? '#10B981' : colors.color,
142
+ borderColor: isCopied ? '#10B98140' : colors.color + '40'
143
+ }}
144
+ >
145
+ {isCopied ? <Check size={14} /> : <Copy size={14} />}
146
+ </button>
147
+ {showTooltip === 'copy' && (
148
+ <div className="tooltip">
149
+ {isCopied ? 'Copied!' : 'Copy response'}
150
+ </div>
151
+ )}
152
+ </div>
153
+
154
+ {/* Expand/Elaborate Button */}
155
+ <div className="tooltip-container">
156
+ <button
157
+ className="action-button"
158
+ onClick={() => handleExpand(message.id, message.advisorId)}
159
+ onMouseEnter={() => showTooltipWithDelay('expand')}
160
+ onMouseLeave={hideTooltip}
161
+ style={{
162
+ color: colors.color,
163
+ borderColor: colors.color + '40'
164
+ }}
165
+ >
166
+ <Maximize2 size={14} />
167
+ </button>
168
+ {showTooltip === 'expand' && (
169
+ <div className="tooltip">Expand on this response</div>
170
+ )}
171
+ </div>
172
+ </div>
173
  </div>
174
  )}
175
  </div>
phd-advisor-frontend/src/styles/components.css CHANGED
@@ -554,12 +554,119 @@
554
  margin: 0;
555
  }
556
 
 
557
  .message-actions {
558
  margin-top: 12px;
559
  padding-top: 12px;
560
  border-top: 1px solid var(--border-tertiary);
561
  }
562
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
563
  .reply-button {
564
  display: flex;
565
  align-items: center;
@@ -789,4 +896,19 @@
789
  .features-grid {
790
  grid-template-columns: 1fr;
791
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
792
  }
 
554
  margin: 0;
555
  }
556
 
557
+ /* Updated Message Actions Styles */
558
  .message-actions {
559
  margin-top: 12px;
560
  padding-top: 12px;
561
  border-top: 1px solid var(--border-tertiary);
562
  }
563
 
564
+ .action-buttons {
565
+ display: flex;
566
+ align-items: center;
567
+ gap: 8px;
568
+ }
569
+
570
+ .action-button {
571
+ display: flex;
572
+ align-items: center;
573
+ justify-content: center;
574
+ width: 32px;
575
+ height: 32px;
576
+ background: none;
577
+ border: 1px solid;
578
+ border-radius: 6px;
579
+ cursor: pointer;
580
+ transition: all 0.2s ease;
581
+ position: relative;
582
+ }
583
+
584
+ .action-button:hover {
585
+ background: var(--bg-secondary);
586
+ transform: translateY(-1px);
587
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
588
+ }
589
+
590
+ .action-button:active {
591
+ transform: translateY(0);
592
+ }
593
+
594
+ /* Tooltip Styles */
595
+ .tooltip-container {
596
+ position: relative;
597
+ display: inline-block;
598
+ }
599
+
600
+ .tooltip {
601
+ position: absolute;
602
+ bottom: 100%;
603
+ left: 50%;
604
+ transform: translateX(-50%);
605
+ margin-bottom: 8px;
606
+ padding: 6px 10px;
607
+ background: var(--bg-primary);
608
+ color: var(--text-primary);
609
+ border: 1px solid var(--border-secondary);
610
+ border-radius: 6px;
611
+ font-size: 12px;
612
+ font-weight: 500;
613
+ white-space: nowrap;
614
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
615
+ z-index: 1000;
616
+ opacity: 0;
617
+ animation: tooltipFadeIn 0.2s ease-out forwards;
618
+ }
619
+
620
+ .tooltip::after {
621
+ content: '';
622
+ position: absolute;
623
+ top: 100%;
624
+ left: 50%;
625
+ transform: translateX(-50%);
626
+ border: 4px solid transparent;
627
+ border-top-color: var(--border-secondary);
628
+ }
629
+
630
+ .tooltip::before {
631
+ content: '';
632
+ position: absolute;
633
+ top: 100%;
634
+ left: 50%;
635
+ transform: translateX(-50%);
636
+ margin-top: -1px;
637
+ border: 4px solid transparent;
638
+ border-top-color: var(--bg-primary);
639
+ }
640
+
641
+ /* Tooltip Animation */
642
+ @keyframes tooltipFadeIn {
643
+ from {
644
+ opacity: 0;
645
+ transform: translateX(-50%) translateY(4px);
646
+ }
647
+ to {
648
+ opacity: 1;
649
+ transform: translateX(-50%) translateY(0);
650
+ }
651
+ }
652
+
653
+ /* Dark theme tooltip adjustments */
654
+ [data-theme="dark"] .tooltip {
655
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
656
+ }
657
+
658
+ /* Focus states for accessibility */
659
+ .action-button:focus-visible {
660
+ outline: 2px solid var(--accent-primary);
661
+ outline-offset: 2px;
662
+ }
663
+
664
+ /* Copy button success state */
665
+ .action-button.copied {
666
+ color: #10B981 !important;
667
+ border-color: #10B98140 !important;
668
+ }
669
+
670
  .reply-button {
671
  display: flex;
672
  align-items: center;
 
896
  .features-grid {
897
  grid-template-columns: 1fr;
898
  }
899
+
900
+ .action-buttons {
901
+ gap: 6px;
902
+ }
903
+
904
+ .action-button {
905
+ width: 28px;
906
+ height: 28px;
907
+ }
908
+
909
+ .tooltip {
910
+ font-size: 11px;
911
+ padding: 4px 8px;
912
+ }
913
+
914
  }