mirror of
https://github.com/Minidoracat/mcp-feedback-enhanced.git
synced 2025-07-27 10:42:25 +08:00
471 lines
15 KiB
JavaScript
471 lines
15 KiB
JavaScript
/**
|
||
* MCP Feedback Enhanced - UI 管理模組
|
||
* =================================
|
||
*
|
||
* 處理 UI 狀態更新、指示器管理和頁籤切換
|
||
*/
|
||
|
||
(function() {
|
||
'use strict';
|
||
|
||
// 確保命名空間和依賴存在
|
||
window.MCPFeedback = window.MCPFeedback || {};
|
||
const Utils = window.MCPFeedback.Utils;
|
||
|
||
/**
|
||
* UI 管理器建構函數
|
||
*/
|
||
function UIManager(options) {
|
||
options = options || {};
|
||
|
||
// 當前狀態
|
||
this.currentTab = options.currentTab || 'combined';
|
||
this.feedbackState = Utils.CONSTANTS.FEEDBACK_WAITING;
|
||
this.layoutMode = options.layoutMode || 'combined-vertical';
|
||
this.lastSubmissionTime = null;
|
||
|
||
// UI 元素
|
||
this.connectionIndicator = null;
|
||
this.connectionText = null;
|
||
this.tabButtons = null;
|
||
this.tabContents = null;
|
||
this.submitBtn = null;
|
||
this.feedbackText = null;
|
||
|
||
// 回調函數
|
||
this.onTabChange = options.onTabChange || null;
|
||
this.onLayoutModeChange = options.onLayoutModeChange || null;
|
||
|
||
this.initUIElements();
|
||
}
|
||
|
||
/**
|
||
* 初始化 UI 元素
|
||
*/
|
||
UIManager.prototype.initUIElements = function() {
|
||
// 基本 UI 元素
|
||
this.connectionIndicator = Utils.safeQuerySelector('#connectionIndicator');
|
||
this.connectionText = Utils.safeQuerySelector('#connectionText');
|
||
|
||
// 頁籤相關元素
|
||
this.tabButtons = document.querySelectorAll('.tab-button');
|
||
this.tabContents = document.querySelectorAll('.tab-content');
|
||
|
||
// 回饋相關元素
|
||
this.feedbackText = Utils.safeQuerySelector('#feedbackText');
|
||
this.submitBtn = Utils.safeQuerySelector('#submitBtn');
|
||
|
||
console.log('✅ UI 元素初始化完成');
|
||
};
|
||
|
||
/**
|
||
* 初始化頁籤功能
|
||
*/
|
||
UIManager.prototype.initTabs = function() {
|
||
const self = this;
|
||
|
||
// 設置頁籤點擊事件
|
||
this.tabButtons.forEach(function(button) {
|
||
button.addEventListener('click', function() {
|
||
const tabName = button.getAttribute('data-tab');
|
||
self.switchTab(tabName);
|
||
});
|
||
});
|
||
|
||
// 根據佈局模式確定初始頁籤
|
||
let initialTab = this.currentTab;
|
||
if (this.layoutMode.startsWith('combined')) {
|
||
initialTab = 'combined';
|
||
} else if (this.currentTab === 'combined') {
|
||
initialTab = 'feedback';
|
||
}
|
||
|
||
// 設置初始頁籤
|
||
this.setInitialTab(initialTab);
|
||
};
|
||
|
||
/**
|
||
* 設置初始頁籤(不觸發保存)
|
||
*/
|
||
UIManager.prototype.setInitialTab = function(tabName) {
|
||
this.currentTab = tabName;
|
||
this.updateTabDisplay(tabName);
|
||
this.handleSpecialTabs(tabName);
|
||
console.log('初始化頁籤: ' + tabName);
|
||
};
|
||
|
||
/**
|
||
* 切換頁籤
|
||
*/
|
||
UIManager.prototype.switchTab = function(tabName) {
|
||
this.currentTab = tabName;
|
||
this.updateTabDisplay(tabName);
|
||
this.handleSpecialTabs(tabName);
|
||
|
||
// 觸發回調
|
||
if (this.onTabChange) {
|
||
this.onTabChange(tabName);
|
||
}
|
||
|
||
console.log('切換到頁籤: ' + tabName);
|
||
};
|
||
|
||
/**
|
||
* 更新頁籤顯示
|
||
*/
|
||
UIManager.prototype.updateTabDisplay = function(tabName) {
|
||
// 更新按鈕狀態
|
||
this.tabButtons.forEach(function(button) {
|
||
if (button.getAttribute('data-tab') === tabName) {
|
||
button.classList.add('active');
|
||
} else {
|
||
button.classList.remove('active');
|
||
}
|
||
});
|
||
|
||
// 更新內容顯示
|
||
this.tabContents.forEach(function(content) {
|
||
if (content.id === 'tab-' + tabName) {
|
||
content.classList.add('active');
|
||
} else {
|
||
content.classList.remove('active');
|
||
}
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 處理特殊頁籤
|
||
*/
|
||
UIManager.prototype.handleSpecialTabs = function(tabName) {
|
||
if (tabName === 'combined') {
|
||
this.handleCombinedMode();
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 處理合併模式
|
||
*/
|
||
UIManager.prototype.handleCombinedMode = function() {
|
||
console.log('切換到組合模式');
|
||
|
||
// 確保合併模式的佈局樣式正確應用
|
||
const combinedTab = Utils.safeQuerySelector('#tab-combined');
|
||
if (combinedTab) {
|
||
combinedTab.classList.remove('combined-vertical', 'combined-horizontal');
|
||
if (this.layoutMode === 'combined-vertical') {
|
||
combinedTab.classList.add('combined-vertical');
|
||
} else if (this.layoutMode === 'combined-horizontal') {
|
||
combinedTab.classList.add('combined-horizontal');
|
||
}
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 更新頁籤可見性
|
||
*/
|
||
UIManager.prototype.updateTabVisibility = function() {
|
||
const combinedTab = document.querySelector('.tab-button[data-tab="combined"]');
|
||
const feedbackTab = document.querySelector('.tab-button[data-tab="feedback"]');
|
||
const summaryTab = document.querySelector('.tab-button[data-tab="summary"]');
|
||
|
||
// 只使用合併模式:顯示合併模式頁籤,隱藏回饋和AI摘要頁籤
|
||
if (combinedTab) combinedTab.style.display = 'inline-block';
|
||
if (feedbackTab) feedbackTab.style.display = 'none';
|
||
if (summaryTab) summaryTab.style.display = 'none';
|
||
};
|
||
|
||
/**
|
||
* 設置回饋狀態
|
||
*/
|
||
UIManager.prototype.setFeedbackState = function(state, sessionId) {
|
||
const previousState = this.feedbackState;
|
||
this.feedbackState = state;
|
||
|
||
if (sessionId) {
|
||
console.log('🔄 會話 ID: ' + sessionId.substring(0, 8) + '...');
|
||
}
|
||
|
||
console.log('📊 狀態變更: ' + previousState + ' → ' + state);
|
||
this.updateUIState();
|
||
this.updateStatusIndicator();
|
||
};
|
||
|
||
/**
|
||
* 更新 UI 狀態
|
||
*/
|
||
UIManager.prototype.updateUIState = function() {
|
||
this.updateSubmitButton();
|
||
this.updateFeedbackInputs();
|
||
this.updateImageUploadAreas();
|
||
};
|
||
|
||
/**
|
||
* 更新提交按鈕狀態
|
||
*/
|
||
UIManager.prototype.updateSubmitButton = function() {
|
||
const submitButtons = [
|
||
Utils.safeQuerySelector('#submitBtn')
|
||
].filter(function(btn) { return btn !== null; });
|
||
|
||
const self = this;
|
||
submitButtons.forEach(function(button) {
|
||
if (!button) return;
|
||
|
||
switch (self.feedbackState) {
|
||
case Utils.CONSTANTS.FEEDBACK_WAITING:
|
||
button.textContent = window.i18nManager ? window.i18nManager.t('buttons.submit') : '提交回饋';
|
||
button.className = 'btn btn-primary';
|
||
button.disabled = false;
|
||
break;
|
||
case Utils.CONSTANTS.FEEDBACK_PROCESSING:
|
||
button.textContent = window.i18nManager ? window.i18nManager.t('buttons.processing') : '處理中...';
|
||
button.className = 'btn btn-secondary';
|
||
button.disabled = true;
|
||
break;
|
||
case Utils.CONSTANTS.FEEDBACK_SUBMITTED:
|
||
button.textContent = window.i18nManager ? window.i18nManager.t('buttons.submitted') : '已提交';
|
||
button.className = 'btn btn-success';
|
||
button.disabled = true;
|
||
break;
|
||
}
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 更新回饋輸入框狀態
|
||
*/
|
||
UIManager.prototype.updateFeedbackInputs = function() {
|
||
const feedbackInputs = [
|
||
Utils.safeQuerySelector('#feedbackText'),
|
||
Utils.safeQuerySelector('#combinedFeedbackText')
|
||
].filter(function(input) { return input !== null; });
|
||
|
||
const canInput = this.feedbackState === Utils.CONSTANTS.FEEDBACK_WAITING;
|
||
feedbackInputs.forEach(function(input) {
|
||
input.disabled = !canInput;
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 更新圖片上傳區域狀態
|
||
*/
|
||
UIManager.prototype.updateImageUploadAreas = function() {
|
||
const uploadAreas = [
|
||
Utils.safeQuerySelector('#feedbackImageUploadArea'),
|
||
Utils.safeQuerySelector('#combinedImageUploadArea')
|
||
].filter(function(area) { return area !== null; });
|
||
|
||
const canUpload = this.feedbackState === Utils.CONSTANTS.FEEDBACK_WAITING;
|
||
uploadAreas.forEach(function(area) {
|
||
if (canUpload) {
|
||
area.classList.remove('disabled');
|
||
} else {
|
||
area.classList.add('disabled');
|
||
}
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 更新狀態指示器
|
||
*/
|
||
UIManager.prototype.updateStatusIndicator = function() {
|
||
const feedbackStatusIndicator = Utils.safeQuerySelector('#feedbackStatusIndicator');
|
||
const combinedStatusIndicator = Utils.safeQuerySelector('#combinedFeedbackStatusIndicator');
|
||
|
||
const statusInfo = this.getStatusInfo();
|
||
|
||
if (feedbackStatusIndicator) {
|
||
this.updateStatusIndicatorElement(feedbackStatusIndicator, statusInfo);
|
||
}
|
||
|
||
if (combinedStatusIndicator) {
|
||
this.updateStatusIndicatorElement(combinedStatusIndicator, statusInfo);
|
||
}
|
||
|
||
console.log('✅ 狀態指示器已更新: ' + statusInfo.status + ' - ' + statusInfo.title);
|
||
};
|
||
|
||
/**
|
||
* 獲取狀態信息
|
||
*/
|
||
UIManager.prototype.getStatusInfo = function() {
|
||
let icon, title, message, status;
|
||
|
||
switch (this.feedbackState) {
|
||
case Utils.CONSTANTS.FEEDBACK_WAITING:
|
||
icon = '⏳';
|
||
title = window.i18nManager ? window.i18nManager.t('status.waiting.title') : '等待回饋';
|
||
message = window.i18nManager ? window.i18nManager.t('status.waiting.message') : '請提供您的回饋意見';
|
||
status = 'waiting';
|
||
break;
|
||
|
||
case Utils.CONSTANTS.FEEDBACK_PROCESSING:
|
||
icon = '⚙️';
|
||
title = window.i18nManager ? window.i18nManager.t('status.processing.title') : '處理中';
|
||
message = window.i18nManager ? window.i18nManager.t('status.processing.message') : '正在提交您的回饋...';
|
||
status = 'processing';
|
||
break;
|
||
|
||
case Utils.CONSTANTS.FEEDBACK_SUBMITTED:
|
||
const timeStr = this.lastSubmissionTime ?
|
||
new Date(this.lastSubmissionTime).toLocaleTimeString() : '';
|
||
icon = '✅';
|
||
title = window.i18nManager ? window.i18nManager.t('status.submitted.title') : '回饋已提交';
|
||
message = window.i18nManager ? window.i18nManager.t('status.submitted.message') : '等待下次 MCP 調用';
|
||
if (timeStr) {
|
||
message += ' (' + timeStr + ')';
|
||
}
|
||
status = 'submitted';
|
||
break;
|
||
|
||
default:
|
||
icon = '⏳';
|
||
title = window.i18nManager ? window.i18nManager.t('status.waiting.title') : '等待回饋';
|
||
message = window.i18nManager ? window.i18nManager.t('status.waiting.message') : '請提供您的回饋意見';
|
||
status = 'waiting';
|
||
}
|
||
|
||
return { icon: icon, title: title, message: message, status: status };
|
||
};
|
||
|
||
/**
|
||
* 更新單個狀態指示器元素
|
||
*/
|
||
UIManager.prototype.updateStatusIndicatorElement = function(element, statusInfo) {
|
||
if (!element) return;
|
||
|
||
// 更新狀態類別
|
||
element.className = 'feedback-status-indicator status-' + statusInfo.status;
|
||
element.style.display = 'block';
|
||
|
||
// 更新標題
|
||
const titleElement = element.querySelector('.status-title');
|
||
if (titleElement) {
|
||
titleElement.textContent = statusInfo.icon + ' ' + statusInfo.title;
|
||
}
|
||
|
||
// 更新訊息
|
||
const messageElement = element.querySelector('.status-message');
|
||
if (messageElement) {
|
||
messageElement.textContent = statusInfo.message;
|
||
}
|
||
|
||
console.log('🔧 已更新狀態指示器: ' + element.id + ' -> ' + statusInfo.status);
|
||
};
|
||
|
||
/**
|
||
* 更新連接狀態
|
||
*/
|
||
UIManager.prototype.updateConnectionStatus = function(status, text) {
|
||
if (this.connectionIndicator) {
|
||
this.connectionIndicator.className = 'connection-indicator ' + status;
|
||
}
|
||
if (this.connectionText) {
|
||
this.connectionText.textContent = text;
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 更新 AI 摘要內容
|
||
*/
|
||
UIManager.prototype.updateAISummaryContent = function(summary) {
|
||
console.log('📝 更新 AI 摘要內容...');
|
||
|
||
const summaryContent = Utils.safeQuerySelector('#summaryContent');
|
||
if (summaryContent) {
|
||
summaryContent.textContent = summary;
|
||
console.log('✅ 已更新分頁模式摘要內容');
|
||
}
|
||
|
||
const combinedSummaryContent = Utils.safeQuerySelector('#combinedSummaryContent');
|
||
if (combinedSummaryContent) {
|
||
combinedSummaryContent.textContent = summary;
|
||
console.log('✅ 已更新合併模式摘要內容');
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 重置回饋表單
|
||
*/
|
||
UIManager.prototype.resetFeedbackForm = function() {
|
||
console.log('🔄 重置回饋表單...');
|
||
|
||
// 清空回饋輸入
|
||
const feedbackInputs = [
|
||
Utils.safeQuerySelector('#feedbackText'),
|
||
Utils.safeQuerySelector('#combinedFeedbackText')
|
||
].filter(function(input) { return input !== null; });
|
||
|
||
feedbackInputs.forEach(function(input) {
|
||
input.value = '';
|
||
input.disabled = false;
|
||
});
|
||
|
||
// 重新啟用提交按鈕
|
||
const submitButtons = [
|
||
Utils.safeQuerySelector('#submitBtn')
|
||
].filter(function(btn) { return btn !== null; });
|
||
|
||
submitButtons.forEach(function(button) {
|
||
button.disabled = false;
|
||
const defaultText = window.i18nManager ? window.i18nManager.t('buttons.submit') : '提交回饋';
|
||
button.textContent = button.getAttribute('data-original-text') || defaultText;
|
||
});
|
||
|
||
console.log('✅ 回饋表單重置完成');
|
||
};
|
||
|
||
/**
|
||
* 應用佈局模式
|
||
*/
|
||
UIManager.prototype.applyLayoutMode = function(layoutMode) {
|
||
this.layoutMode = layoutMode;
|
||
|
||
const expectedClassName = 'layout-' + layoutMode;
|
||
if (document.body.className !== expectedClassName) {
|
||
console.log('應用佈局模式: ' + layoutMode);
|
||
document.body.className = expectedClassName;
|
||
}
|
||
|
||
this.updateTabVisibility();
|
||
|
||
// 如果當前頁籤不是合併模式,則切換到合併模式頁籤
|
||
if (this.currentTab !== 'combined') {
|
||
this.currentTab = 'combined';
|
||
}
|
||
|
||
// 觸發回調
|
||
if (this.onLayoutModeChange) {
|
||
this.onLayoutModeChange(layoutMode);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 獲取當前頁籤
|
||
*/
|
||
UIManager.prototype.getCurrentTab = function() {
|
||
return this.currentTab;
|
||
};
|
||
|
||
/**
|
||
* 獲取當前回饋狀態
|
||
*/
|
||
UIManager.prototype.getFeedbackState = function() {
|
||
return this.feedbackState;
|
||
};
|
||
|
||
/**
|
||
* 設置最後提交時間
|
||
*/
|
||
UIManager.prototype.setLastSubmissionTime = function(timestamp) {
|
||
this.lastSubmissionTime = timestamp;
|
||
this.updateStatusIndicator();
|
||
};
|
||
|
||
// 將 UIManager 加入命名空間
|
||
window.MCPFeedback.UIManager = UIManager;
|
||
|
||
console.log('✅ UIManager 模組載入完成');
|
||
|
||
})();
|