champ-chatbot / static /components /feedback-component.js
qyle's picture
feedback component
eebe76e verified
// components/feedback-component.js - Message feedback functionality
import { StateManager } from '../services/state-manager.js';
import { ApiService } from '../services/api-service.js';
import { TranslationService } from '../services/translation-service.js';
import { Utils } from '../utils.js';
export const FeedbackComponent = {
elements: {
feedbackOverlay: null,
closeFeedbackBtn: null,
cancelFeedbackBtn: null,
submitFeedbackBtn: null,
feedbackInput: null,
feedbackRatingDisplay: null,
feedbackMessagePreview: null
},
currentFeedback: {
messageIndex: null,
modelType: null,
rating: null, // 'like', 'dislike', 'mixed'
messageContent: null
},
/**
* Initialize the feedback component
*/
init() {
// Get DOM elements
this.elements.feedbackOverlay = document.getElementById('feedback-overlay');
this.elements.closeFeedbackBtn = document.getElementById('closeFeedbackBtn');
this.elements.cancelFeedbackBtn = document.getElementById('cancelFeedbackBtn');
this.elements.submitFeedbackBtn = document.getElementById('submitFeedbackBtn');
this.elements.feedbackInput = document.getElementById('feedbackInput');
this.elements.feedbackRatingDisplay = document.getElementById('feedbackRatingDisplay');
this.elements.feedbackMessagePreview = document.getElementById('feedbackMessagePreview');
this.attachOutsideClickListener();
this.attachEventListeners();
},
attachOutsideClickListener() {
this.elements.feedbackOverlay.addEventListener('click', (e) => {
// Check if click is on the overlay itself (not its children)
if (e.target === this.elements.feedbackOverlay) {
this.closeModal();
}
});
},
/**
* Attach event listeners
*/
attachEventListeners() {
this.elements.closeFeedbackBtn.addEventListener('click', () => this.closeModal());
this.elements.cancelFeedbackBtn.addEventListener('click', () => this.closeModal());
this.elements.submitFeedbackBtn.addEventListener('click', () => this.submitFeedback());
// Enter to submit (optional comment)
this.elements.feedbackInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
this.submitFeedback();
}
});
},
/**
* Open feedback modal
* @param {number} messageIndex - Index of the message
* @param {string} modelType - Type of model
* @param {string} rating - 'like', 'dislike', or 'mixed'
* @param {string} messageContent - Content of the message being rated
*/
openModal(messageIndex, modelType, rating, messageContent) {
this.currentFeedback = {
messageIndex,
modelType,
rating,
messageContent
};
// Update modal content
this.updateModalContent(rating, messageContent);
// Show modal
this.elements.feedbackOverlay.style.display = '';
// Focus on textarea
setTimeout(() => this.elements.feedbackInput.focus(), 100);
},
/**
* Update modal content based on rating
* @param {string} rating - The rating type
* @param {string} messageContent - The message content
*/
updateModalContent(rating, messageContent) {
// Update rating display
const ratingEmoji = {
'like': '👍',
'dislike': '👎',
'mixed': '~'
};
const ratingText = {
'like': 'feedback_like_title',
'dislike': 'feedback_dislike_title',
'mixed': 'feedback_neutral_title'
};
this.elements.feedbackRatingDisplay.textContent = ratingEmoji[rating] + ' ';
this.elements.feedbackRatingDisplay.dataset.i18n = ratingText[rating];
// Update message preview (truncate if too long)
const preview = messageContent.length > 150
? messageContent.substring(0, 150) + '...'
: messageContent;
this.elements.feedbackMessagePreview.textContent = preview;
// Clear previous comment
this.elements.feedbackInput.value = '';
// Apply translations
TranslationService.applyTranslation();
},
/**
* Close the feedback modal
*/
closeModal() {
this.elements.feedbackOverlay.style.display = 'none';
this.currentFeedback = {
messageIndex: null,
modelType: null,
rating: null,
messageContent: null
};
},
/**
* Submit feedback to the server
*/
async submitFeedback() {
const comment = this.elements.feedbackInput.value.trim();
const feedbackData = {
message_index: this.currentFeedback.messageIndex,
model_type: this.currentFeedback.modelType,
rating: this.currentFeedback.rating,
comment: comment || "", // Optional
reply_content: this.currentFeedback.messageContent,
user_id: Utils.getMachineId(),
session_id: StateManager.sessionId,
conversation_id: StateManager.getConversationId(this.currentFeedback.modelType)
};
try {
const result = await ApiService.submitFeedback(feedbackData);
if (result.success) {
showSnackbar(translations[StateManager.currentLang]["feedback_submitted"], 'success');
// Mark message as rated in state (optional - for UI indication)
this.markMessageAsRated(
this.currentFeedback.modelType,
this.currentFeedback.messageIndex,
this.currentFeedback.rating
);
this.closeModal();
} else {
showSnackbar(translations[StateManager.currentLang]["feedback_failed_server_error"], 'error');
}
} catch (err) {
showSnackbar(translations[StateManager.currentLang]["feedback_failed_network_error"], 'error');
}
},
/**
* Mark a message as rated (for UI purposes)
* @param {string} modelType - Model type
* @param {number} messageIndex - Message index
* @param {string} rating - Rating given
*/
markMessageAsRated(modelType, messageIndex, rating) {
const messages = StateManager.getMessages(modelType);
if (messages[messageIndex]) {
messages[messageIndex].feedback = {
rated: true,
rating: rating
};
window.dispatchEvent(new CustomEvent('feedbackSubmitted', {
detail: { modelType, messageIndex, rating }
}));
}
}
};