|
232 | 232 | text-align: center; |
233 | 233 | } |
234 | 234 |
|
235 | | - .format-selector { |
| 235 | + .controls-bar { |
| 236 | + display: flex; |
| 237 | + justify-content: space-between; |
| 238 | + align-items: center; |
236 | 239 | margin-bottom: 20px; |
237 | | - text-align: right; |
| 240 | + padding: 10px 15px; |
| 241 | + background-color: var(--bg-secondary); |
| 242 | + border: 1px solid var(--border-color); |
| 243 | + border-radius: 5px; |
| 244 | + } |
| 245 | + |
| 246 | + .feedback-selector, |
| 247 | + .format-selector { |
| 248 | + display: flex; |
| 249 | + align-items: center; |
| 250 | + gap: 10px; |
238 | 251 | } |
239 | 252 |
|
| 253 | + .feedback-selector label, |
240 | 254 | .format-selector label { |
241 | | - margin-right: 10px; |
242 | 255 | font-weight: bold; |
243 | 256 | color: var(--text-secondary); |
244 | 257 | } |
245 | 258 |
|
| 259 | + .feedback-selector select, |
246 | 260 | .format-selector select { |
247 | 261 | padding: 5px 10px; |
248 | 262 | border-radius: 4px; |
|
251 | 265 | color: var(--text-primary); |
252 | 266 | } |
253 | 267 |
|
| 268 | + .feedback-selector select:hover, |
| 269 | + .format-selector select:hover { |
| 270 | + border-color: var(--accent-primary); |
| 271 | + } |
| 272 | + |
254 | 273 | .patch-navigation { |
255 | 274 | display: flex; |
256 | 275 | flex-wrap: wrap; |
@@ -386,13 +405,24 @@ <h2>Review Information <span style="font-family: monospace; font-size: 0.75em; f |
386 | 405 |
|
387 | 406 | <div id="patch-navigation" class="patch-navigation" style="display: none;"></div> |
388 | 407 |
|
389 | | - <div class="format-selector" id="format-selector" style="display: none;"> |
390 | | - <label for="format">Review Format:</label> |
391 | | - <select id="format" onchange="changeFormat()"> |
392 | | - <option value="inline">Review</option> |
393 | | - <option value="markup">Full output</option> |
394 | | - <option value="metadata">Metadata</option> |
395 | | - </select> |
| 408 | + <div class="controls-bar" id="controls-bar" style="display: none;"> |
| 409 | + <div class="feedback-selector"> |
| 410 | + <label for="feedback-select">Feedback:</label> |
| 411 | + <select id="feedback-select" onchange="setFeedback()"> |
| 412 | + <option value="">-- Select --</option> |
| 413 | + <option value="emailed">Emailed</option> |
| 414 | + <option value="false-positive">False Positive</option> |
| 415 | + <option value="false-negative">False Negative</option> |
| 416 | + </select> |
| 417 | + </div> |
| 418 | + <div class="format-selector"> |
| 419 | + <label for="format">Review Format:</label> |
| 420 | + <select id="format" onchange="changeFormat()"> |
| 421 | + <option value="inline">Review</option> |
| 422 | + <option value="markup">Full output</option> |
| 423 | + <option value="metadata">Metadata</option> |
| 424 | + </select> |
| 425 | + </div> |
396 | 426 | </div> |
397 | 427 |
|
398 | 428 | <div id="reviews-container"></div> |
@@ -556,13 +586,15 @@ <h2>Review Information <span style="font-family: monospace; font-size: 0.75em; f |
556 | 586 | document.getElementById('review-duration').textContent = formatDuration(durationSeconds); |
557 | 587 | } |
558 | 588 |
|
559 | | - // Feedback (only shown for superusers) |
| 589 | + // Feedback handling |
| 590 | + const feedbackLabels = { |
| 591 | + 'emailed': '📧 Emailed', |
| 592 | + 'false-positive': '❌ False Positive', |
| 593 | + 'false-negative': '⚠️ False Negative' |
| 594 | + }; |
| 595 | + |
| 596 | + // Always show feedback in info section if it exists |
560 | 597 | if (data.feedback) { |
561 | | - const feedbackLabels = { |
562 | | - 'emailed': '📧 Emailed', |
563 | | - 'false-positive': '❌ False Positive', |
564 | | - 'false-negative': '⚠️ False Negative' |
565 | | - }; |
566 | 598 | document.getElementById('feedback-info').style.display = 'block'; |
567 | 599 | document.getElementById('review-feedback').textContent = feedbackLabels[data.feedback] || data.feedback; |
568 | 600 | } |
@@ -597,16 +629,22 @@ <h2>Review Information <span style="font-family: monospace; font-size: 0.75em; f |
597 | 629 | } |
598 | 630 | } |
599 | 631 |
|
600 | | - // Load reviews if status is done |
601 | | - if (data.status === 'done' || (data.status === 'error' && patchCount > 0)) { |
602 | | - document.getElementById('format-selector').style.display = 'block'; |
603 | | - // Set the dropdown to match currentFormat |
| 632 | + // Load reviews if status is done and user has token |
| 633 | + if ((data.status === 'done' || (data.status === 'error' && patchCount > 0)) && token) { |
| 634 | + document.getElementById('controls-bar').style.display = 'flex'; |
| 635 | + // Set the dropdowns to match current state |
604 | 636 | document.getElementById('format').value = currentFormat; |
| 637 | + document.getElementById('feedback-select').value = data.feedback || ''; |
605 | 638 | // First load inline format to determine which patches have comments |
606 | 639 | loadInlineForNavigation().then(() => { |
607 | 640 | // Then load the current format for display |
608 | 641 | loadReview(currentFormat); |
609 | 642 | }); |
| 643 | + } else if (data.status === 'done' || (data.status === 'error' && patchCount > 0)) { |
| 644 | + // No token - just load reviews without controls bar |
| 645 | + loadInlineForNavigation().then(() => { |
| 646 | + loadReview(currentFormat); |
| 647 | + }); |
610 | 648 | } |
611 | 649 | } |
612 | 650 |
|
@@ -732,6 +770,61 @@ <h2>Review Information <span style="font-family: monospace; font-size: 0.75em; f |
732 | 770 | return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`; |
733 | 771 | } |
734 | 772 | } |
| 773 | + |
| 774 | + function setFeedback() { |
| 775 | + const feedbackSelect = document.getElementById('feedback-select'); |
| 776 | + const feedback = feedbackSelect.value; |
| 777 | + |
| 778 | + if (!feedback || !token || !reviewId) { |
| 779 | + return; |
| 780 | + } |
| 781 | + |
| 782 | + const payload = { |
| 783 | + id: reviewId, |
| 784 | + token: token, |
| 785 | + feedback: feedback |
| 786 | + }; |
| 787 | + |
| 788 | + fetch('/api/review/feedback', { |
| 789 | + method: 'POST', |
| 790 | + headers: { |
| 791 | + 'Content-Type': 'application/json' |
| 792 | + }, |
| 793 | + body: JSON.stringify(payload) |
| 794 | + }) |
| 795 | + .then(response => { |
| 796 | + if (!response.ok) { |
| 797 | + return response.json().then(data => { |
| 798 | + throw new Error(data.error || `HTTP ${response.status}`); |
| 799 | + }); |
| 800 | + } |
| 801 | + return response.json(); |
| 802 | + }) |
| 803 | + .then(data => { |
| 804 | + if (data.success) { |
| 805 | + // Update the feedback display in review info section |
| 806 | + const feedbackLabels = { |
| 807 | + 'emailed': '📧 Emailed', |
| 808 | + 'false-positive': '❌ False Positive', |
| 809 | + 'false-negative': '⚠️ False Negative' |
| 810 | + }; |
| 811 | + document.getElementById('feedback-info').style.display = 'block'; |
| 812 | + document.getElementById('review-feedback').textContent = feedbackLabels[feedback] || feedback; |
| 813 | + |
| 814 | + // Brief visual feedback on dropdown |
| 815 | + feedbackSelect.style.borderColor = 'var(--status-done-bg)'; |
| 816 | + setTimeout(() => { |
| 817 | + feedbackSelect.style.borderColor = ''; |
| 818 | + }, 1000); |
| 819 | + } |
| 820 | + }) |
| 821 | + .catch(error => { |
| 822 | + console.error('Error setting feedback:', error); |
| 823 | + alert(`Failed to set feedback: ${error.message}`); |
| 824 | + // Reload to reset the dropdown to the actual server state |
| 825 | + loadReview(); |
| 826 | + }); |
| 827 | + } |
735 | 828 | </script> |
736 | 829 | </body> |
737 | 830 | </html> |
0 commit comments