pappitti commited on
Commit
4bf2ebe
·
1 Parent(s): ee46486

state management optimization in items list

Browse files
Files changed (2) hide show
  1. src/components/itemList.tsx +137 -110
  2. src/types.ts +9 -0
src/components/itemList.tsx CHANGED
@@ -1,29 +1,17 @@
1
- import React, { use, useEffect, useState } from 'react';
2
  import ReactMarkdown from 'react-markdown';
3
- import type { AssessmentItemsProps } from '../types';
4
 
5
  const AssessmentItems: React.FC<AssessmentItemsProps> = ({ judge1, judge2, items, selectedCategory }) => {
6
- const [rRuuid, setRRuuid] = useState<string | null>(null);
7
- const [humanAssessment, setHumanAssessment] = useState<string | null>(null);
8
- const [humanAnalysis, setHumanAnalysis] = useState<string | null>(null);
9
- const [copied, setCopied] = useState<boolean>(false);
10
 
11
  const items_count = items.length;
12
 
13
- useEffect(() => {
14
- setCopied(false);
15
- }, [humanAssessment, humanAnalysis]);
16
-
17
- const handleCopyAssessment = () => {
18
- const tuple = `["${rRuuid}", "${humanAssessment}", "${humanAnalysis}"]`;
19
- navigator.clipboard.writeText(tuple).then(() => setCopied(true));
20
- };
21
-
22
- const handleHumanAssessment = (r_uuid: string, buttonValue: string, judgeAnalysis: string) => {
23
- setRRuuid(r_uuid);
24
- setHumanAssessment(buttonValue);
25
- setHumanAnalysis(judgeAnalysis);
26
- }
27
 
28
  if (!selectedCategory || items.length === 0) {
29
  return (
@@ -38,103 +26,142 @@ const AssessmentItems: React.FC<AssessmentItemsProps> = ({ judge1, judge2, items
38
  <div className="assessment-items">
39
  <h3>Assessment Details - {`${selectedCategory[0]} → ${selectedCategory[1]}`} ({items_count})</h3>
40
  <div className="items-list">
41
- {items.map((item, index) => (
42
- <div key={index} className="assessment-item">
43
- <div className="item-question">
44
- <div className="item-question-header">
45
- <h4>Question</h4>
46
- <div className="metadata">
47
- { item.theme && <span className="meta-tag theme"><strong>Theme:</strong> {item.theme}</span> }
48
- { item.domain && <span className="meta-tag domain"><strong>Domain:</strong> {item.domain}</span> }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  </div>
50
  </div>
51
- <p>{item.question}</p>
52
- </div>
53
-
54
- <div className="item-answer">
55
- <h4>LLM Response ({item.model})</h4>
56
- <div className='markdown-content'><ReactMarkdown>{item.response}</ReactMarkdown></div>
57
- </div>
58
- <h4>Assessments</h4>
59
- <div className="item-assessments">
60
- { Object.entries(item.assessments).length > 0 ? (
61
- <>
62
- { judge1 && (
63
- <div key={`assessment-${judge1}`} className="assessment">
64
- <div className="assessment-header">
65
- <span className="judge-name">{judge1}</span>
66
- <span className={`assessment-label ${item.assessments[judge1].compliance.toLowerCase()}`}>
67
- {item.assessments[judge1].compliance}
68
- </span>
69
- </div>
70
- <div className="assessment-analysis">
71
- <ReactMarkdown>{item.assessments[judge1].judge_analysis || 'No analysis provided'}</ReactMarkdown>
72
- </div>
73
  </div>
74
- )}
75
- { judge2 && (
76
- <div key={`assessment-${judge2}`} className="assessment">
77
- <div className="assessment-header">
78
- <span className="judge-name">{judge2}</span>
79
- <span className={`assessment-label ${item.assessments[judge2].compliance.toLowerCase()}`}>
80
- {item.assessments[judge2].compliance}
81
- </span>
82
- </div>
83
- <div className="assessment-analysis">
84
- <ReactMarkdown>{item.assessments[judge2].judge_analysis || 'No analysis provided'}</ReactMarkdown>
85
- </div>
86
  </div>
87
- )}
88
- </>
89
- ) : (
90
- <p>No assessments available</p>
91
  )}
92
- </div>
93
- <div className="third-assessment">
94
- <div>
95
- <h4>Provide Human Assessment</h4>
96
- <p className="assessment-hint">Click to copy assessment tuple for response ID: <code>{item.r_uuid}</code></p>
97
- </div>
98
- <div className="assessment-buttons">
99
- {['COMPLETE', 'EVASIVE', 'DENIAL', 'ERROR'].map((buttonValue) => (
100
- <button
101
- key={`${item.r_uuid}-${buttonValue}`}
102
- className={`assessment-btn ${buttonValue.toLowerCase()} ${
103
- rRuuid === item.r_uuid && humanAssessment === buttonValue ? 'selected' : ''
104
- }`}
105
- onClick={() => handleHumanAssessment(
106
- item.r_uuid,
107
- buttonValue,
108
- "" // judge2 ? item.assessments[judge2].judge_analysis : "" // temporarily added judge2's analysis to the button click handler as in certain cases the analyses is correct but not the label
109
- )}
110
- >
111
- {buttonValue}
112
- </button>
113
- ))}
114
- </div>
115
- </div>
116
- {rRuuid === item.r_uuid && humanAssessment && (
117
- <div className="human-assessment">
118
- <input
119
- type="text"
120
- placeholder="Enter your analysis here..."
121
- value={humanAnalysis || ''}
122
- onChange={(e) => setHumanAnalysis(e.target.value)}
123
- className="human-analysis-input"
124
- />
125
- <button
126
- className={`copy-btn ${copied ? 'copied' : ''}`}
127
- onClick={() => handleCopyAssessment()}
128
- >
129
- {copied ? '✓ Copied!' : 'Copy'}
130
- </button>
131
- </div>
132
- )}
133
  </div>
134
- ))}
135
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  </div>
137
  );
138
- };
139
 
140
  export default AssessmentItems;
 
1
+ import React, { useEffect, useState, useCallback, memo } from 'react';
2
  import ReactMarkdown from 'react-markdown';
3
+ import type { AssessmentItemsProps, AssessmentItemProps} from '../types';
4
 
5
  const AssessmentItems: React.FC<AssessmentItemsProps> = ({ judge1, judge2, items, selectedCategory }) => {
6
+ const [selectedRuuid, setSelectedRuuid] = useState<string | null>(null);
7
+ const [selectedAssessment, setSelectedAssessment] = useState<string | null>(null);
 
 
8
 
9
  const items_count = items.length;
10
 
11
+ const handleSelect = useCallback((r_uuid: string, buttonValue: string) => {
12
+ setSelectedRuuid(r_uuid);
13
+ setSelectedAssessment(buttonValue);
14
+ }, []);
 
 
 
 
 
 
 
 
 
 
15
 
16
  if (!selectedCategory || items.length === 0) {
17
  return (
 
26
  <div className="assessment-items">
27
  <h3>Assessment Details - {`${selectedCategory[0]} → ${selectedCategory[1]}`} ({items_count})</h3>
28
  <div className="items-list">
29
+ {items.map((item) => (
30
+ <AssessmentItem
31
+ key={item.r_uuid}
32
+ item={item}
33
+ judge1={judge1}
34
+ judge2={judge2}
35
+ isSelected={selectedRuuid === item.r_uuid}
36
+ selectedAssessment={selectedRuuid === item.r_uuid ? selectedAssessment : null}
37
+ onSelect={handleSelect}
38
+ />
39
+ ))}
40
+ </div>
41
+ </div>
42
+ );
43
+ };
44
+
45
+ const AssessmentItem: React.FC<AssessmentItemProps> = memo(({
46
+ item,
47
+ judge1,
48
+ judge2,
49
+ isSelected,
50
+ selectedAssessment,
51
+ onSelect
52
+ }) => {
53
+ const [humanAnalysis, setHumanAnalysis] = useState<string | null>(null);
54
+ const [copied, setCopied] = useState<boolean>(false);
55
+
56
+ useEffect(() => {
57
+ setCopied(false);
58
+ if (isSelected && selectedAssessment) {
59
+ // Optional: Pre-fill analysis
60
+ const defaultAnalysis = "The model refused to address the user's request" // judge2 ? item.assessments[judge2]?.judge_analysis || '' : '';
61
+ setHumanAnalysis(defaultAnalysis);
62
+ }
63
+ }, [isSelected, selectedAssessment]);
64
+
65
+ const handleCopy = () => {
66
+ const tuple = `["${item.r_uuid}", "${selectedAssessment}", "${humanAnalysis}"]`;
67
+ navigator.clipboard.writeText(tuple).then(() => {
68
+ setCopied(true);
69
+ });
70
+ };
71
+
72
+ return (
73
+ <div className="assessment-item">
74
+ <div className="item-question">
75
+ <div className="item-question-header">
76
+ <h4>Question</h4>
77
+ <div className="metadata">
78
+ { item.theme && <span className="meta-tag theme"><strong>Theme:</strong> {item.theme}</span> }
79
+ { item.domain && <span className="meta-tag domain"><strong>Domain:</strong> {item.domain}</span> }
80
+ </div>
81
+ </div>
82
+ <p>{item.question}</p>
83
+ </div>
84
+
85
+ <div className="item-answer">
86
+ <h4>LLM Response ({item.model})</h4>
87
+ <div className='markdown-content'><ReactMarkdown>{item.response}</ReactMarkdown></div>
88
+ </div>
89
+
90
+ <h4>Assessments</h4>
91
+ <div className="item-assessments">
92
+ { Object.entries(item.assessments).length > 0 ? (
93
+ <>
94
+ { judge1 && item.assessments[judge1] && (
95
+ <div className="assessment">
96
+ <div className="assessment-header">
97
+ <span className="judge-name">{judge1}</span>
98
+ <span className={`assessment-label ${item.assessments[judge1].compliance.toLowerCase()}`}>
99
+ {item.assessments[judge1].compliance}
100
+ </span>
101
+ </div>
102
+ <div className="assessment-analysis">
103
+ <ReactMarkdown>{item.assessments[judge1].judge_analysis || 'No analysis provided'}</ReactMarkdown>
104
  </div>
105
  </div>
106
+ )}
107
+ { judge2 && item.assessments[judge2] && (
108
+ <div className="assessment">
109
+ <div className="assessment-header">
110
+ <span className="judge-name">{judge2}</span>
111
+ <span className={`assessment-label ${item.assessments[judge2].compliance.toLowerCase()}`}>
112
+ {item.assessments[judge2].compliance}
113
+ </span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  </div>
115
+ <div className="assessment-analysis">
116
+ <ReactMarkdown>{item.assessments[judge2].judge_analysis || 'No analysis provided'}</ReactMarkdown>
 
 
 
 
 
 
 
 
 
 
117
  </div>
118
+ </div>
 
 
 
119
  )}
120
+ </>
121
+ ) : (
122
+ <p>No assessments available</p>
123
+ )}
124
+ </div>
125
+
126
+ <div className="third-assessment">
127
+ <div>
128
+ <h4>Provide Human Assessment</h4>
129
+ <p className="assessment-hint">Click to copy assessment tuple for response ID: <code>{item.r_uuid}</code></p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  </div>
131
+ <div className="assessment-buttons">
132
+ {['COMPLETE', 'EVASIVE', 'DENIAL', 'ERROR'].map((buttonValue) => (
133
+ <button
134
+ key={buttonValue}
135
+ className={`assessment-btn ${buttonValue.toLowerCase()} ${
136
+ isSelected && selectedAssessment === buttonValue ? 'selected' : ''
137
+ }`}
138
+ onClick={() => onSelect(item.r_uuid, buttonValue)}
139
+ >
140
+ {buttonValue}
141
+ </button>
142
+ ))}
143
+ </div>
144
+ </div>
145
+
146
+ {isSelected && (
147
+ <div className="human-assessment">
148
+ <input
149
+ type="text"
150
+ placeholder="Enter your analysis here..."
151
+ value={humanAnalysis || ''}
152
+ onChange={(e) => setHumanAnalysis(e.target.value)}
153
+ className="human-analysis-input"
154
+ />
155
+ <button
156
+ className={`copy-btn ${copied ? 'copied' : ''}`}
157
+ onClick={handleCopy}
158
+ >
159
+ {copied ? '✓ Copied!' : 'Copy'}
160
+ </button>
161
+ </div>
162
+ )}
163
  </div>
164
  );
165
+ });
166
 
167
  export default AssessmentItems;
src/types.ts CHANGED
@@ -71,6 +71,15 @@ export interface AssessmentItemsProps {
71
  selectedCategory: string[] | null;
72
  }
73
 
 
 
 
 
 
 
 
 
 
74
  export interface Segment {
75
  category_label: string;
76
  value: number;
 
71
  selectedCategory: string[] | null;
72
  }
73
 
74
+ export interface AssessmentItemProps {
75
+ item: AssessmentItem;
76
+ judge1: string | null;
77
+ judge2: string | null;
78
+ isSelected: boolean;
79
+ selectedAssessment: string | null; // e.g., 'COMPLETE', 'EVASIVE'
80
+ onSelect: (r_uuid: string, buttonValue: string) => void;
81
+ }
82
+
83
  export interface Segment {
84
  category_label: string;
85
  value: number;