239 lines
10 KiB
TypeScript
239 lines
10 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
||
import { MessageSquare, BarChart2, Send, RefreshCw, Settings as SettingsIcon, AlertCircle } from 'lucide-react';
|
||
import CommentList from './sidebar/components/CommentList';
|
||
import Analytics from './sidebar/components/Analytics';
|
||
import ReplyGenerator from './sidebar/components/ReplyGenerator';
|
||
import Settings from './sidebar/components/Settings';
|
||
import { Comment } from './types';
|
||
import mockComments from './mockData';
|
||
|
||
function App() {
|
||
const [activeTab, setActiveTab] = useState<'comments' | 'analytics' | 'reply' | 'settings'>('comments');
|
||
const [comments, setComments] = useState<Comment[]>([]);
|
||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||
const [selectedComment, setSelectedComment] = useState<Comment | null>(null);
|
||
const [mockDelay, setMockDelay] = useState<number>(1000);
|
||
const [error, setError] = useState<string | null>(null);
|
||
|
||
useEffect(() => {
|
||
// Simulate loading comments with a delay
|
||
setError(null);
|
||
const timer = setTimeout(() => {
|
||
try {
|
||
setComments(mockComments);
|
||
setIsLoading(false);
|
||
} catch (err) {
|
||
setError('Error loading comments: ' + (err instanceof Error ? err.message : String(err)));
|
||
setIsLoading(false);
|
||
}
|
||
}, mockDelay);
|
||
|
||
return () => clearTimeout(timer);
|
||
}, [mockDelay]);
|
||
|
||
const refreshComments = () => {
|
||
setIsLoading(true);
|
||
setError(null);
|
||
setTimeout(() => {
|
||
try {
|
||
setComments(mockComments);
|
||
setIsLoading(false);
|
||
} catch (err) {
|
||
setError('Error refreshing comments: ' + (err instanceof Error ? err.message : String(err)));
|
||
setIsLoading(false);
|
||
}
|
||
}, mockDelay);
|
||
};
|
||
|
||
const handleSelectComment = (comment: Comment) => {
|
||
setSelectedComment(comment);
|
||
setActiveTab('reply');
|
||
};
|
||
|
||
return (
|
||
<div className="flex flex-col min-h-screen bg-gray-100">
|
||
<header className="bg-blue-600 text-white p-4">
|
||
<div className="container mx-auto flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
|
||
<h1 className="text-2xl font-bold">社群留言助手 - 開發模式</h1>
|
||
<div className="flex items-center space-x-4">
|
||
<div className="flex items-center">
|
||
<span className="text-sm mr-2">模擬延遲:</span>
|
||
<select
|
||
value={mockDelay}
|
||
onChange={(e) => setMockDelay(Number(e.target.value))}
|
||
className="bg-blue-700 text-white rounded px-2 py-1 text-sm"
|
||
>
|
||
<option value="0">無延遲</option>
|
||
<option value="500">0.5 秒</option>
|
||
<option value="1000">1 秒</option>
|
||
<option value="2000">2 秒</option>
|
||
</select>
|
||
</div>
|
||
<button
|
||
onClick={refreshComments}
|
||
className="p-2 bg-blue-700 rounded-full text-white hover:bg-blue-800 transition-colors"
|
||
title="重新載入留言"
|
||
>
|
||
<RefreshCw size={16} />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
{error && (
|
||
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mx-auto mt-4 container" role="alert">
|
||
<div className="flex items-center">
|
||
<AlertCircle className="mr-2" size={20} />
|
||
<span className="block sm:inline">{error}</span>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<main className="flex-1 container mx-auto p-4 flex flex-col md:flex-row gap-4">
|
||
{/* Sidebar Preview */}
|
||
<div className="w-full md:w-80 bg-white rounded-lg shadow-lg overflow-hidden flex flex-col h-[600px] border border-gray-200">
|
||
{/* Header */}
|
||
<div className="bg-blue-600 text-white p-4 shadow-md">
|
||
<h2 className="text-xl font-bold flex items-center">
|
||
<MessageSquare className="mr-2" size={20} />
|
||
社群留言助手
|
||
</h2>
|
||
<p className="text-sm opacity-80">自動捕獲留言並產生回覆建議</p>
|
||
</div>
|
||
|
||
{/* Content */}
|
||
<div className="flex-1 overflow-auto p-4">
|
||
{activeTab === 'comments' && (
|
||
<CommentList
|
||
comments={comments}
|
||
isLoading={isLoading}
|
||
onSelectComment={handleSelectComment}
|
||
/>
|
||
)}
|
||
{activeTab === 'analytics' && (
|
||
<Analytics comments={comments} />
|
||
)}
|
||
{activeTab === 'reply' && (
|
||
<ReplyGenerator
|
||
comment={selectedComment}
|
||
onBack={() => setActiveTab('comments')}
|
||
/>
|
||
)}
|
||
{activeTab === 'settings' && (
|
||
<Settings />
|
||
)}
|
||
</div>
|
||
|
||
{/* Navigation */}
|
||
<nav className="bg-white border-t border-gray-200 p-2">
|
||
<div className="flex justify-around">
|
||
<button
|
||
onClick={() => setActiveTab('comments')}
|
||
className={`flex flex-col items-center p-2 rounded-md ${activeTab === 'comments' ? 'text-blue-600' : 'text-gray-600'}`}
|
||
>
|
||
<MessageSquare size={20} />
|
||
<span className="text-xs mt-1">留言</span>
|
||
</button>
|
||
<button
|
||
onClick={() => setActiveTab('analytics')}
|
||
className={`flex flex-col items-center p-2 rounded-md ${activeTab === 'analytics' ? 'text-blue-600' : 'text-gray-600'}`}
|
||
>
|
||
<BarChart2 size={20} />
|
||
<span className="text-xs mt-1">數據</span>
|
||
</button>
|
||
<button
|
||
onClick={() => setActiveTab('reply')}
|
||
className={`flex flex-col items-center p-2 rounded-md ${activeTab === 'reply' ? 'text-blue-600' : 'text-gray-600'}`}
|
||
>
|
||
<Send size={20} />
|
||
<span className="text-xs mt-1">回覆</span>
|
||
</button>
|
||
<button
|
||
onClick={() => setActiveTab('settings')}
|
||
className={`flex flex-col items-center p-2 rounded-md ${activeTab === 'settings' ? 'text-blue-600' : 'text-gray-600'}`}
|
||
>
|
||
<SettingsIcon size={20} />
|
||
<span className="text-xs mt-1">設置</span>
|
||
</button>
|
||
</div>
|
||
</nav>
|
||
</div>
|
||
|
||
{/* Development Info */}
|
||
<div className="flex-1">
|
||
<div className="bg-white rounded-lg shadow-lg p-6">
|
||
<h2 className="text-xl font-bold mb-4">開發資訊</h2>
|
||
|
||
<div className="mb-6">
|
||
<h3 className="text-lg font-semibold mb-2">當前狀態</h3>
|
||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||
<div className="bg-gray-50 p-3 rounded border border-gray-200">
|
||
<p className="text-sm font-medium text-gray-700">當前頁面</p>
|
||
<p className="text-lg">{activeTab}</p>
|
||
</div>
|
||
<div className="bg-gray-50 p-3 rounded border border-gray-200">
|
||
<p className="text-sm font-medium text-gray-700">留言數量</p>
|
||
<p className="text-lg">{comments.length}</p>
|
||
</div>
|
||
<div className="bg-gray-50 p-3 rounded border border-gray-200">
|
||
<p className="text-sm font-medium text-gray-700">載入狀態</p>
|
||
<p className="text-lg">{isLoading ? '載入中' : '已載入'}</p>
|
||
</div>
|
||
<div className="bg-gray-50 p-3 rounded border border-gray-200">
|
||
<p className="text-sm font-medium text-gray-700">選中的留言</p>
|
||
<p className="text-lg">{selectedComment ? `ID: ${selectedComment.id}` : '無'}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="mb-6">
|
||
<h3 className="text-lg font-semibold mb-2">開發指南</h3>
|
||
<div className="bg-blue-50 p-4 rounded border border-blue-200">
|
||
<p className="mb-2">這是一個開發環境,用於測試 Chrome 擴展的功能。</p>
|
||
<ul className="list-disc pl-5 space-y-1 text-sm">
|
||
<li>左側顯示的是擴展的側邊欄界面預覽</li>
|
||
<li>可以調整模擬延遲來測試不同的載入狀態</li>
|
||
<li>點擊刷新按鈕可以重新載入模擬數據</li>
|
||
<li>所有功能都使用模擬數據,不會實際抓取網頁留言</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<h3 className="text-lg font-semibold mb-2">構建與測試</h3>
|
||
<div className="bg-gray-50 p-4 rounded border border-gray-200 space-y-3">
|
||
<div>
|
||
<p className="font-medium">構建擴展:</p>
|
||
<code className="bg-gray-100 px-2 py-1 rounded text-sm">npm run build</code>
|
||
</div>
|
||
<div>
|
||
<p className="font-medium">載入擴展:</p>
|
||
<ol className="list-decimal pl-5 text-sm space-y-1">
|
||
<li>打開 Chrome 瀏覽器,進入擴展管理頁面 (chrome://extensions/)</li>
|
||
<li>開啟開發者模式</li>
|
||
<li>點擊「載入已解壓的擴展」</li>
|
||
<li>選擇項目的 dist 目錄</li>
|
||
</ol>
|
||
</div>
|
||
<div>
|
||
<p className="font-medium">測試擴展:</p>
|
||
<ol className="list-decimal pl-5 text-sm space-y-1">
|
||
<li>在任意網頁點擊擴展圖標</li>
|
||
<li>側邊欄將會打開,顯示留言助手界面</li>
|
||
<li>如果沒有自動打開,可以右鍵點擊擴展圖標,選擇「打開側邊欄」</li>
|
||
</ol>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<footer className="bg-gray-800 text-white p-4 text-center">
|
||
<p>社群留言助手 - 開發模式 © 2025</p>
|
||
</footer>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default App; |