mirror of
https://github.com/dyzulk/trustlab.git
synced 2026-01-26 05:25:36 +07:00
feat(home): simplify homepage and link to docs
This commit is contained in:
@@ -128,168 +128,10 @@ function CliSnippet({ label, command, t }: { label: string, command: string, t:
|
||||
);
|
||||
}
|
||||
|
||||
function OsGuideContent({ title, steps, selectedOs, certificates, t, linuxDistro, setLinuxDistro }: { title: string, steps: string[], selectedOs: string, certificates: CaCertificate[], t: any, linuxDistro?: string, setLinuxDistro?: (distro: any) => void }) {
|
||||
return (
|
||||
<div className="p-8 bg-white dark:bg-gray-800 rounded-[2rem] border border-gray-100 dark:border-gray-700 shadow-sm space-y-10 animate-in fade-in slide-in-from-bottom-2 duration-500">
|
||||
<div>
|
||||
<h4 className="text-lg font-bold text-gray-900 dark:text-white mb-6 flex items-center gap-2">
|
||||
<div className="w-1.5 h-6 bg-brand-500 rounded-full"></div>
|
||||
{title}
|
||||
</h4>
|
||||
<ul className="space-y-4">
|
||||
{steps.map((step, idx) => (
|
||||
<li key={idx} className="flex gap-4">
|
||||
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-brand-50 dark:bg-brand-500/10 text-brand-600 dark:text-brand-400 flex items-center justify-center text-xs font-bold border border-brand-100 dark:border-brand-500/20">
|
||||
{idx + 1}
|
||||
</span>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 leading-relaxed font-medium">
|
||||
{step}
|
||||
</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
// OsGuideContent removed in favor of centralized documentation
|
||||
|
||||
{/* Global Bundle Section (Recommendations) */}
|
||||
<div className="pt-8 border-t border-dashed border-gray-100 dark:border-gray-700">
|
||||
<div className="relative bg-brand-500/5 dark:bg-brand-400/5 rounded-2xl p-6 border border-brand-500/10 dark:border-brand-400/10">
|
||||
<div className="absolute top-4 right-4">
|
||||
<Badge variant="brand">{t('recommended')}</Badge>
|
||||
</div>
|
||||
<div className="flex flex-col sm:flex-row items-start gap-4">
|
||||
<div className="w-12 h-12 rounded-2xl bg-gradient-to-br from-brand-500 to-brand-600 text-white flex items-center justify-center flex-shrink-0 shadow-lg shadow-brand-500/20">
|
||||
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex-1 pt-1">
|
||||
<h5 className="font-bold text-lg text-gray-900 dark:text-white mb-2 pr-20">
|
||||
{t('bundle_guide_title')}
|
||||
</h5>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 leading-relaxed mb-6 max-w-xl">
|
||||
{t('bundle_guide_desc')}
|
||||
</p>
|
||||
|
||||
{(selectedOs === 'linux' || selectedOs === 'windows' || selectedOs === 'macos') && (
|
||||
<div className="space-y-4">
|
||||
{(selectedOs === 'linux') && (
|
||||
<div className="space-y-4">
|
||||
<div className="flex flex-wrap gap-2 mb-4">
|
||||
<button onClick={() => setLinuxDistro?.('debian')} className={`px-3 py-1.5 rounded-lg text-xs font-bold border transition-colors ${linuxDistro === 'debian' ? 'bg-brand-50 border-brand-200 text-brand-700 dark:bg-brand-500/10 dark:border-brand-500/20 dark:text-brand-400' : 'bg-transparent border-transparent text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'}`}>Debian/Ubuntu</button>
|
||||
<button onClick={() => setLinuxDistro?.('rhel')} className={`px-3 py-1.5 rounded-lg text-xs font-bold border transition-colors ${linuxDistro === 'rhel' ? 'bg-brand-50 border-brand-200 text-brand-700 dark:bg-brand-500/10 dark:border-brand-500/20 dark:text-brand-400' : 'bg-transparent border-transparent text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'}`}>CentOS/RHEL</button>
|
||||
<button onClick={() => setLinuxDistro?.('arch')} className={`px-3 py-1.5 rounded-lg text-xs font-bold border transition-colors ${linuxDistro === 'arch' ? 'bg-brand-50 border-brand-200 text-brand-700 dark:bg-brand-500/10 dark:border-brand-500/20 dark:text-brand-400' : 'bg-transparent border-transparent text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'}`}>Arch Linux</button>
|
||||
<button onClick={() => setLinuxDistro?.('other')} className={`px-3 py-1.5 rounded-lg text-xs font-bold border transition-colors ${linuxDistro === 'other' ? 'bg-brand-50 border-brand-200 text-brand-700 dark:bg-brand-500/10 dark:border-brand-500/20 dark:text-brand-400' : 'bg-transparent border-transparent text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'}`}>Other</button>
|
||||
</div>
|
||||
|
||||
{linuxDistro === 'debian' && (
|
||||
<CliSnippet
|
||||
label={`${t('bundle_cli_label')} (Debian/Ubuntu)`}
|
||||
command={`sudo apt update && sudo apt install -y curl && curl -sL https://cdn.trustlab.dyzulk.com/ca/bundles/trustlab-all.sh | sudo bash`}
|
||||
t={t}
|
||||
/>
|
||||
)}
|
||||
{linuxDistro === 'rhel' && (
|
||||
<CliSnippet
|
||||
label={`${t('bundle_cli_label')} (RHEL/CentOS)`}
|
||||
command={`(sudo yum install -y curl || sudo dnf install -y curl) && curl -sL https://cdn.trustlab.dyzulk.com/ca/bundles/trustlab-all.sh | sudo bash`}
|
||||
t={t}
|
||||
/>
|
||||
)}
|
||||
{linuxDistro === 'arch' && (
|
||||
<CliSnippet
|
||||
label={`${t('bundle_cli_label')} (Arch Linux)`}
|
||||
command={`sudo pacman -Sy --noconfirm curl && curl -sL https://cdn.trustlab.dyzulk.com/ca/bundles/trustlab-all.sh | sudo bash`}
|
||||
t={t}
|
||||
/>
|
||||
)}
|
||||
{linuxDistro === 'other' && (
|
||||
<CliSnippet
|
||||
label={`${t('bundle_cli_label')} (Universal)`}
|
||||
command={`curl -sL https://cdn.trustlab.dyzulk.com/ca/bundles/trustlab-all.sh | sudo bash`}
|
||||
t={t}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{(selectedOs === 'windows' || selectedOs === 'macos') && (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<DownloadBtn
|
||||
href={`https://cdn.trustlab.dyzulk.com/ca/bundles/trustlab-all.${selectedOs === 'windows' ? 'bat' : 'mobileconfig'}`}
|
||||
label={t('download_all_bundle')}
|
||||
icon={selectedOs === 'windows' ? <WindowsIcon className="w-4 h-4" /> : <AppleIcon className="w-4 h-4" />}
|
||||
onClick={() => {}}
|
||||
variant="blue"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{selectedOs === 'linux' && (
|
||||
<div className="pt-10 border-t border-gray-100 dark:border-gray-700 space-y-6">
|
||||
<div>
|
||||
<h5 className="font-bold text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<span className="w-2 h-2 rounded-full bg-green-500 animate-pulse"></span>
|
||||
{t('guide_linux_shortcut_title')}
|
||||
</h5>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">{t('guide_linux_shortcut_desc')}</p>
|
||||
</div>
|
||||
|
||||
{/* Distro Selection for Individual Certs */}
|
||||
<div className="flex flex-wrap gap-2 mb-2">
|
||||
<button onClick={() => setLinuxDistro?.('debian')} className={`px-2.5 py-1 rounded-md text-[10px] uppercase tracking-wider font-bold border transition-colors ${linuxDistro === 'debian' ? 'bg-brand-50 border-brand-200 text-brand-700 dark:bg-brand-500/10 dark:border-brand-500/20 dark:text-brand-400' : 'bg-transparent border-transparent text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800'}`}>Debian</button>
|
||||
<button onClick={() => setLinuxDistro?.('rhel')} className={`px-2.5 py-1 rounded-md text-[10px] uppercase tracking-wider font-bold border transition-colors ${linuxDistro === 'rhel' ? 'bg-brand-50 border-brand-200 text-brand-700 dark:bg-brand-500/10 dark:border-brand-500/20 dark:text-brand-400' : 'bg-transparent border-transparent text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800'}`}>RHEL</button>
|
||||
<button onClick={() => setLinuxDistro?.('arch')} className={`px-2.5 py-1 rounded-md text-[10px] uppercase tracking-wider font-bold border transition-colors ${linuxDistro === 'arch' ? 'bg-brand-50 border-brand-200 text-brand-700 dark:bg-brand-500/10 dark:border-brand-500/20 dark:text-brand-400' : 'bg-transparent border-transparent text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800'}`}>Arch</button>
|
||||
<button onClick={() => setLinuxDistro?.('other')} className={`px-2.5 py-1 rounded-md text-[10px] uppercase tracking-wider font-bold border transition-colors ${linuxDistro === 'other' ? 'bg-brand-50 border-brand-200 text-brand-700 dark:bg-brand-500/10 dark:border-brand-500/20 dark:text-brand-400' : 'bg-transparent border-transparent text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800'}`}>Universal</button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6">
|
||||
{/* Function to generate smart command */}
|
||||
{(() => {
|
||||
const getSmartCommand = (url: string | null) => {
|
||||
if (!url) return '';
|
||||
if (linuxDistro === 'debian') return `sudo apt install -y curl && curl -sL ${url} | sudo bash`;
|
||||
if (linuxDistro === 'rhel') return `(sudo yum install -y curl || sudo dnf install -y curl) && curl -sL ${url} | sudo bash`;
|
||||
if (linuxDistro === 'arch') return `sudo pacman -Sy curl && curl -sL ${url} | sudo bash`;
|
||||
return `curl -sL ${url} | sudo bash`;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Root CAs */}
|
||||
{certificates.filter(c => c.type === 'root').map(c => (
|
||||
<CliSnippet
|
||||
key={c.serial}
|
||||
label={`${t('root_ca')}: ${c.name} [${linuxDistro?.toUpperCase()}]`}
|
||||
command={getSmartCommand(c.linux_cdn_url || null)}
|
||||
t={t}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Intermediate CAs */}
|
||||
{certificates.filter(c => c.type !== 'root').map(c => (
|
||||
<CliSnippet
|
||||
key={c.serial}
|
||||
label={`${t('intermediate_ca')}: ${c.name} [${linuxDistro?.toUpperCase()}]`}
|
||||
command={getSmartCommand(c.linux_cdn_url || null)}
|
||||
t={t}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function CaCard({ cert, isRoot, t, selectedOs, setSelectedOs }: { cert: CaCertificate, isRoot: boolean, t: any, selectedOs: string, setSelectedOs: (os: any) => void }) {
|
||||
function CaCard({ cert, isRoot, t }: { cert: CaCertificate, isRoot: boolean, t: any }) {
|
||||
return (
|
||||
<div className="group relative bg-white/40 dark:bg-gray-900/40 backdrop-blur-xl rounded-[2.5rem] p-8 border border-white/50 dark:border-gray-800/50 shadow-xl hover:shadow-2xl transition-all duration-500 hover:-translate-y-1">
|
||||
{/* Sync Status Badge */}
|
||||
@@ -327,48 +169,19 @@ function CaCard({ cert, isRoot, t, selectedOs, setSelectedOs }: { cert: CaCertif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 sm:gap-4">
|
||||
<DownloadBtn
|
||||
href={cert.cdn_url || `/api/public/ca-certificates/${cert.serial}/download`}
|
||||
label={t('download_standard') || "PEM"}
|
||||
icon={<FileIcon className="w-4 h-4" />}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
<DownloadBtn
|
||||
href={cert.der_cdn_url || `/api/public/ca-certificates/${cert.serial}/download?format=der`}
|
||||
label={t('download_android') || "Android/DER"}
|
||||
icon={<MobileIcon className="w-4 h-4" />}
|
||||
onClick={() => setSelectedOs('mobile')}
|
||||
variant="green"
|
||||
/>
|
||||
<DownloadBtn
|
||||
href={cert.bat_cdn_url || `/api/public/ca-certificates/${cert.serial}/download/windows`}
|
||||
label={t('download_windows') || "Windows"}
|
||||
icon={<WindowsIcon className="w-4 h-4" />}
|
||||
onClick={() => setSelectedOs('windows')}
|
||||
variant="blue"
|
||||
/>
|
||||
<DownloadBtn
|
||||
href={cert.mac_cdn_url || `/api/public/ca-certificates/${cert.serial}/download/mac`}
|
||||
label={t('download_macos') || "macOS"}
|
||||
icon={<AppleIcon className="w-4 h-4" />}
|
||||
onClick={() => setSelectedOs('macos')}
|
||||
variant="gray"
|
||||
/>
|
||||
<div className="flex items-center">
|
||||
<a
|
||||
href="https://trustlab-docs.pages.dev/guide/getting-started/install-root-ca"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-full flex items-center justify-center gap-2 px-6 py-4 rounded-2xl bg-gray-900 dark:bg-white text-white dark:text-gray-900 font-bold transition-all hover:scale-[1.02] shadow-lg shadow-gray-900/10 dark:shadow-white/10"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
||||
</svg>
|
||||
Download & Install Guide (Docs)
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{cert.linux_cdn_url && (
|
||||
<div className="mt-4">
|
||||
<DownloadBtn
|
||||
href={cert.linux_cdn_url || `/api/public/ca-certificates/${cert.serial}/download/linux`}
|
||||
label={t('download_linux') || "Linux (.sh)"}
|
||||
icon={<LinuxIcon className="w-4 h-4" />}
|
||||
onClick={() => setSelectedOs('linux')}
|
||||
variant="gray"
|
||||
isFullWidth
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -448,8 +261,7 @@ export default function HomeClient() {
|
||||
const t = useTranslations("Home");
|
||||
const [certificates, setCertificates] = useState<CaCertificate[]>([]);
|
||||
const [loadingCerts, setLoadingCerts] = useState(true);
|
||||
const [selectedOs, setSelectedOs] = useState<'windows' | 'macos' | 'linux' | 'mobile'>('windows');
|
||||
const [linuxDistro, setLinuxDistro] = useState<'debian' | 'rhel' | 'arch' | 'other'>('debian');
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCertificates = async () => {
|
||||
@@ -635,7 +447,7 @@ export default function HomeClient() {
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 gap-8 w-full max-w-3xl px-4">
|
||||
{familyCerts.filter(c => c.type === 'root').map((cert) => (
|
||||
<CaCard key={cert.serial} cert={cert} isRoot={true} t={t} selectedOs={selectedOs} setSelectedOs={setSelectedOs} />
|
||||
<CaCard key={cert.serial} cert={cert} isRoot={true} t={t} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
@@ -650,34 +462,12 @@ export default function HomeClient() {
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 w-full max-w-6xl px-4">
|
||||
{familyCerts.filter(c => c.type !== 'root').map((cert) => (
|
||||
<CaCard key={cert.serial} cert={cert} isRoot={false} t={t} selectedOs={selectedOs} setSelectedOs={setSelectedOs} />
|
||||
<CaCard key={cert.serial} cert={cert} isRoot={false} t={t} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* OS Selection Tabs for Global Guide */}
|
||||
<div className="max-w-4xl mx-auto mt-20 p-8 rounded-[2rem] bg-gray-50 dark:bg-gray-900/50 border border-gray-100 dark:border-gray-800">
|
||||
<div className="text-center mb-8">
|
||||
<h3 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">{t('install_guide_title')}</h3>
|
||||
<p className="text-gray-600 dark:text-gray-400">{t('install_guide_desc')}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap justify-center gap-2 mb-8 p-1.5 bg-gray-200/50 dark:bg-gray-800/50 rounded-2xl">
|
||||
<TabButton id="windows" label="Windows" active={selectedOs === 'windows'} onClick={() => setSelectedOs('windows')} icon={<WindowsIcon className="w-4 h-4" />} />
|
||||
<TabButton id="macos" label="macOS" active={selectedOs === 'macos'} onClick={() => setSelectedOs('macos')} icon={<AppleIcon className="w-4 h-4" />} />
|
||||
<TabButton id="linux" label="Linux" active={selectedOs === 'linux'} onClick={() => setSelectedOs('linux')} icon={<LinuxIcon className="w-4 h-4" />} />
|
||||
<TabButton id="mobile" label="Android/iOS" active={selectedOs === 'mobile'} onClick={() => setSelectedOs('mobile')} icon={<MobileIcon className="w-4 h-4" />} />
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{selectedOs === 'windows' && <OsGuideContent title="Windows Installation" steps={t.raw('guide_steps_windows')} selectedOs={selectedOs} certificates={certificates} t={t} />}
|
||||
{selectedOs === 'macos' && <OsGuideContent title="macOS Installation" steps={t.raw('guide_steps_macos')} selectedOs={selectedOs} certificates={certificates} t={t} />}
|
||||
{selectedOs === 'linux' && <OsGuideContent title="Linux Installation" steps={t.raw('guide_steps_linux')} selectedOs={selectedOs} certificates={certificates} t={t} linuxDistro={linuxDistro} setLinuxDistro={setLinuxDistro} />}
|
||||
{selectedOs === 'mobile' && <OsGuideContent title="Mobile Installation" steps={t.raw('guide_steps_mobile')} selectedOs={selectedOs} certificates={certificates} t={t} />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user