feat: Add Help System, Local File Operations, Site Manager improvements, and UI refinements

This commit is contained in:
backuppc
2026-01-19 13:34:14 +09:00
parent c485f411b3
commit 5fd84a7ff1
7 changed files with 1105 additions and 746 deletions

228
components/HelpModal.tsx Normal file
View File

@@ -0,0 +1,228 @@
import React, { useState, useEffect } from 'react';
import { X, HelpCircle, Server, Folder, FileText, Settings, Wifi, Terminal } from 'lucide-react';
interface HelpModalProps {
isOpen: boolean;
onClose: () => void;
initialTab?: 'sites' | 'connection' | 'files' | 'backend';
}
const HelpModal: React.FC<HelpModalProps> = ({ isOpen, onClose, initialTab }) => {
const [activeTab, setActiveTab] = useState<'sites' | 'connection' | 'files' | 'backend'>('sites');
useEffect(() => {
if (isOpen && initialTab) {
setActiveTab(initialTab);
}
}, [isOpen, initialTab]);
// ESC key handler
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (isOpen && (e.key === 'Escape' || e.key === 'Esc')) {
onClose();
}
};
if (isOpen) {
window.addEventListener('keydown', handleKeyDown);
}
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, [isOpen, onClose]);
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/20 backdrop-blur-sm p-4">
<div className="bg-white border border-slate-200 rounded-lg shadow-2xl w-full max-w-2xl flex flex-col h-[600px] max-h-[90vh]">
{/* Header */}
<div className="flex items-center justify-between p-4 border-b border-slate-200 bg-slate-50 rounded-t-lg">
<h2 className="text-base font-bold text-slate-800 flex items-center gap-2">
<HelpCircle size={20} className="text-blue-600" />
</h2>
<button onClick={onClose} className="text-slate-400 hover:text-slate-600 transition-colors">
<X size={20} />
</button>
</div>
<div className="flex flex-1 min-h-0">
{/* Sidebar */}
<div className="w-48 border-r border-slate-200 bg-slate-50 p-2 flex flex-col gap-1">
<button
onClick={() => setActiveTab('sites')}
className={`flex items-center gap-2 px-3 py-2 text-sm rounded transition-colors ${activeTab === 'sites' ? 'bg-white text-blue-600 shadow-sm font-medium' : 'text-slate-600 hover:bg-slate-200'
}`}
>
<Server size={16} />
</button>
<button
onClick={() => setActiveTab('connection')}
className={`flex items-center gap-2 px-3 py-2 text-sm rounded transition-colors ${activeTab === 'connection' ? 'bg-white text-blue-600 shadow-sm font-medium' : 'text-slate-600 hover:bg-slate-200'
}`}
>
<Wifi size={16} />
</button>
<button
onClick={() => setActiveTab('backend')}
className={`flex items-center gap-2 px-3 py-2 text-sm rounded transition-colors ${activeTab === 'backend' ? 'bg-white text-blue-600 shadow-sm font-medium' : 'text-slate-600 hover:bg-slate-200'
}`}
>
<Terminal size={16} /> /
</button>
<button
onClick={() => setActiveTab('files')}
className={`flex items-center gap-2 px-3 py-2 text-sm rounded transition-colors ${activeTab === 'files' ? 'bg-white text-blue-600 shadow-sm font-medium' : 'text-slate-600 hover:bg-slate-200'
}`}
>
<Folder size={16} />
</button>
</div>
{/* Content */}
<div className="flex-1 overflow-y-auto p-6 bg-white">
{activeTab === 'sites' && (
<div className="space-y-6">
<div>
<h3 className="text-lg font-bold text-slate-800 mb-2 flex items-center gap-2">
<Server size={20} className="text-slate-400" />
</h3>
<p className="text-slate-600 text-sm leading-relaxed mb-4">
FTP .
</p>
<ul className="list-disc list-inside text-sm text-slate-600 space-y-2 bg-slate-50 p-4 rounded border border-slate-100">
<li><strong> :</strong> .</li>
<li><strong> :</strong> . (: /public_html)</li>
<li><strong> :</strong> '연결' . ( )</li>
</ul>
</div>
</div>
)}
{activeTab === 'connection' && (
<div className="space-y-6">
<div>
<h3 className="text-lg font-bold text-slate-800 mb-2 flex items-center gap-2">
<Wifi size={20} className="text-slate-400" />
</h3>
<p className="text-slate-600 text-sm leading-relaxed mb-4">
.
</p>
<ul className="list-disc list-inside text-sm text-slate-600 space-y-2 bg-slate-50 p-4 rounded border border-slate-100">
<li><strong> :</strong> , , .</li>
<li><strong> :</strong> (/) .</li>
<li><strong> :</strong> .</li>
</ul>
</div>
</div>
)}
{activeTab === 'backend' && (
<div className="space-y-6">
<div>
<h3 className="text-lg font-bold text-slate-800 mb-2 flex items-center gap-2">
<Terminal size={20} className="text-slate-400" />
</h3>
<p className="text-slate-600 text-sm leading-relaxed mb-4">
WebZilla는 .
</p>
<div className="space-y-4">
<div className="bg-amber-50 p-4 rounded border border-amber-100">
<h4 className="font-bold text-amber-800 text-sm mb-2 flex items-center gap-2">
<Settings size={16} /> 1.
</h4>
<p className="text-xs text-amber-700 leading-relaxed">
<span className="font-bold bg-emerald-600 text-white px-1.5 py-0.5 rounded text-[10px]"></span>
<code className="mx-1 bg-amber-100 px-1 rounded text-amber-900">backend_proxy.cjs</code> .
</p>
</div>
<div className="bg-slate-50 p-4 rounded border border-slate-200">
<h4 className="font-bold text-slate-800 text-sm mb-2 text- mb-2">2. </h4>
<div className="space-y-3">
<div>
<span className="text-xs font-bold text-slate-600 block mb-1"> A: Node.js가 ()</span>
<div className="bg-slate-800 text-slate-200 p-2 rounded text-xs font-mono">
node backend_proxy.cjs
</div>
</div>
<div>
<span className="text-xs font-bold text-slate-600 block mb-1"> B: 실행 (.exe) </span>
<p className="text-xs text-slate-500"> exe .</p>
</div>
</div>
</div>
<div className="flex items-start gap-2 text-xs text-blue-600 bg-blue-50 p-3 rounded">
<div className="shrink-0 mt-0.5"><Wifi size={14} /></div>
<p> <strong>8090</strong> , 'Server' <span className="font-bold text-green-600">Connected</span> .</p>
</div>
</div>
</div>
</div>
)}
{activeTab === 'files' && (
<div className="space-y-6">
<div>
<h3 className="text-lg font-bold text-slate-800 mb-2 flex items-center gap-2">
<Folder size={20} className="text-slate-400" />
</h3>
<p className="text-slate-600 text-sm leading-relaxed mb-4">
( ) () .
</p>
<div className="space-y-4">
<div className="bg-blue-50 p-3 rounded border border-blue-100">
<h4 className="font-bold text-blue-800 text-sm mb-1"> </h4>
<p className="text-xs text-blue-600">
/ . ( )
</p>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="border border-slate-200 p-3 rounded">
<h4 className="font-bold text-slate-700 text-sm mb-1"> ( )</h4>
<ul className="text-xs text-slate-500 space-y-1">
<li> </li>
<li> </li>
<li> / </li>
</ul>
</div>
<div className="border border-slate-200 p-3 rounded">
<h4 className="font-bold text-slate-700 text-sm mb-1"> ()</h4>
<ul className="text-xs text-slate-500 space-y-1">
<li> (MKD)</li>
<li> (RENAME)</li>
<li> / (DELE)</li>
</ul>
</div>
</div>
</div>
</div>
</div>
)}
</div>
</div>
{/* Footer */}
<div className="p-4 border-t border-slate-200 bg-slate-50 flex justify-end rounded-b-lg">
<button
onClick={onClose}
className="px-4 py-2 bg-slate-800 hover:bg-slate-700 text-white text-sm rounded shadow-sm transition-colors"
>
</button>
</div>
</div>
</div>
);
};
export default HelpModal;