Enhance download center with file size preview and dual options
This commit is contained in:
19
App.tsx
19
App.tsx
@@ -9,6 +9,7 @@ import SiteManagerModal from './components/SiteManagerModal';
|
||||
import HelpModal from './components/HelpModal';
|
||||
import { CreateFolderModal, RenameModal, DeleteModal } from './components/FileActionModals';
|
||||
import ConflictModal from './components/ConflictModal';
|
||||
import DownloadModal from './components/DownloadModal';
|
||||
import { formatBytes } from './utils/formatters';
|
||||
|
||||
const AdSenseBanner: React.FC = () => {
|
||||
@@ -58,6 +59,7 @@ const App: React.FC = () => {
|
||||
const [showConnectionHelp, setShowConnectionHelp] = useState(false);
|
||||
const [showSiteManager, setShowSiteManager] = useState(false);
|
||||
const [showHelp, setShowHelp] = useState(false);
|
||||
const [showDownloadModal, setShowDownloadModal] = useState(false);
|
||||
const [helpInitialTab, setHelpInitialTab] = useState<'sites' | 'connection' | 'files' | 'backend'>('sites');
|
||||
const [savedSites, setSavedSites] = useState<SiteConfig[]>([]);
|
||||
|
||||
@@ -976,14 +978,13 @@ const App: React.FC = () => {
|
||||
</button>
|
||||
|
||||
{!(window as any).__IS_STANDALONE__ && (
|
||||
<a
|
||||
href={`${import.meta.env.BASE_URL}webftp-backend.exe`}
|
||||
download="webftp-backend.exe"
|
||||
<button
|
||||
onClick={() => setShowDownloadModal(true)}
|
||||
className={`h-[30px] flex items-center justify-center px-3 rounded bg-emerald-600 hover:bg-emerald-500 text-white border border-emerald-500 shadow-md shadow-emerald-500/20 transition-all ${!connection.connected ? 'animate-pulse ring-2 ring-emerald-400/50' : ''}`}
|
||||
title="백엔드 다운로드"
|
||||
title="다운로드 센터"
|
||||
>
|
||||
<Download size={14} className="mr-1.5" /> 백엔드
|
||||
</a>
|
||||
<Download size={14} className="mr-1.5" /> 다운로드
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
@@ -1089,6 +1090,12 @@ const App: React.FC = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Download Modal - Standalone Only */}
|
||||
<DownloadModal
|
||||
isOpen={showDownloadModal}
|
||||
onClose={() => setShowDownloadModal(false)}
|
||||
/>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="bg-white border-t border-slate-200 px-3 py-1 text-[10px] text-slate-500 flex justify-between shadow-[0_-2px_10px_rgba(0,0,0,0.02)]">
|
||||
<span>WebZilla v1.3.0 <span className="mx-2 text-slate-300">|</span> © SIMP</span>
|
||||
|
||||
119
components/DownloadModal.tsx
Normal file
119
components/DownloadModal.tsx
Normal file
@@ -0,0 +1,119 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Download, Package, Server, X } from 'lucide-react';
|
||||
import { formatBytes } from '../utils/formatters';
|
||||
|
||||
interface DownloadModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const DownloadModal: React.FC<DownloadModalProps> = ({ isOpen, onClose }) => {
|
||||
const [sizes, setSizes] = useState({ full: 0, backend: 0 });
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
// Fetch sizes
|
||||
const fetchSize = async (url: string, key: 'full' | 'backend') => {
|
||||
try {
|
||||
const res = await fetch(url, { method: 'HEAD' });
|
||||
const len = res.headers.get('content-length');
|
||||
if (len) setSizes(prev => ({ ...prev, [key]: parseInt(len, 10) }));
|
||||
} catch (e) {
|
||||
console.error("Failed to fetch size", e);
|
||||
}
|
||||
};
|
||||
|
||||
fetchSize(`${import.meta.env.BASE_URL}webftp.exe`, 'full');
|
||||
fetchSize(`${import.meta.env.BASE_URL}webftp-backend.exe`, 'backend');
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm">
|
||||
<div className="bg-white rounded-lg shadow-xl w-[500px] max-w-full m-4 overflow-hidden animate-in fade-in zoom-in duration-200">
|
||||
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-4 py-3 border-b border-slate-100 bg-slate-50">
|
||||
<h3 className="font-bold text-slate-800 flex items-center gap-2">
|
||||
<Download size={20} className="text-blue-600" />
|
||||
WebZilla 다운로드 센터
|
||||
</h3>
|
||||
<button onClick={onClose} className="text-slate-400 hover:text-slate-600 transition-colors">
|
||||
<X size={20} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-6 space-y-4">
|
||||
<p className="text-sm text-slate-500 mb-4">
|
||||
사용 환경에 맞는 실행 파일을 선택하여 다운로드하세요.
|
||||
</p>
|
||||
|
||||
<div className="grid gap-4">
|
||||
{/* Option 1: Full Version */}
|
||||
<a
|
||||
href={`${import.meta.env.BASE_URL}webftp.exe`}
|
||||
download="webftp.exe"
|
||||
className="flex items-start gap-4 p-4 rounded-lg border border-slate-200 hover:border-blue-400 hover:bg-blue-50 transition-all group relative"
|
||||
>
|
||||
<div className="p-3 bg-blue-100 text-blue-600 rounded-lg group-hover:bg-blue-200 transition-colors">
|
||||
<Package size={24} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className="font-bold text-slate-800">단일 실행 파일 (All-in-One)</h4>
|
||||
<span className="text-xs font-mono bg-slate-100 px-2 py-0.5 rounded text-slate-500">
|
||||
{sizes.full > 0 ? formatBytes(sizes.full) : 'Loading...'}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 mt-1">
|
||||
설치가 필요 없으며, 프론트엔드와 백엔드가 하나로 통합되어 있습니다.
|
||||
<span className="block text-blue-600 mt-1 font-semibold">가장 추천하는 방식입니다.</span>
|
||||
</p>
|
||||
</div>
|
||||
<Download size={16} className="absolute top-4 right-4 text-slate-300 group-hover:text-blue-500" />
|
||||
</a>
|
||||
|
||||
{/* Option 2: Backend Only */}
|
||||
<a
|
||||
href={`${import.meta.env.BASE_URL}webftp-backend.exe`}
|
||||
download="webftp-backend.exe"
|
||||
className="flex items-start gap-4 p-4 rounded-lg border border-slate-200 hover:border-emerald-400 hover:bg-emerald-50 transition-all group relative"
|
||||
>
|
||||
<div className="p-3 bg-emerald-100 text-emerald-600 rounded-lg group-hover:bg-emerald-200 transition-colors">
|
||||
<Server size={24} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className="font-bold text-slate-800">백엔드 전용 (Backend Only)</h4>
|
||||
<span className="text-xs font-mono bg-slate-100 px-2 py-0.5 rounded text-slate-500">
|
||||
{sizes.backend > 0 ? formatBytes(sizes.backend) : 'Loading...'}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 mt-1">
|
||||
정적 파일 호스팅을 별도로 하거나, 서버 리소스를 최소화해야 할 때 사용하세요.
|
||||
(프론트엔드 파일 미포함)
|
||||
</p>
|
||||
</div>
|
||||
<Download size={16} className="absolute top-4 right-4 text-slate-300 group-hover:text-emerald-500" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="px-4 py-3 bg-slate-50 border-t border-slate-100 text-right">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 bg-white border border-slate-300 text-slate-700 rounded hover:bg-slate-50 text-sm font-medium shadow-sm"
|
||||
>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DownloadModal;
|
||||
Reference in New Issue
Block a user