Files
promote/extension/src/App.tsx
2025-03-07 18:04:27 +08:00

239 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;