mirror of
https://github.com/dyzulk/trustlab.git
synced 2026-01-26 05:25:36 +07:00
feat: complete Home page UI overhaul with glassmorphism and installation guides
This commit is contained in:
@@ -11,8 +11,12 @@ interface CaCertificate {
|
||||
type: string;
|
||||
serial: string;
|
||||
expires_at: string;
|
||||
last_synced_at?: string | null;
|
||||
cdn_url?: string | null;
|
||||
der_cdn_url?: string | null;
|
||||
bat_cdn_url?: string | null;
|
||||
mac_cdn_url?: string | null;
|
||||
linux_cdn_url?: string | null;
|
||||
}
|
||||
|
||||
// Simple internal ScrollToTop component
|
||||
@@ -55,10 +59,203 @@ function ScrollToTop() {
|
||||
);
|
||||
}
|
||||
|
||||
// UI Sub-components
|
||||
function Badge({ children, variant = 'brand' }: { children: React.ReactNode, variant?: 'brand' | 'blue' | 'purple' | 'green' | 'gray' }) {
|
||||
const variants = {
|
||||
brand: 'bg-brand-50 text-brand-700 dark:bg-brand-500/10 dark:text-brand-400 border-brand-100 dark:border-brand-500/20',
|
||||
blue: 'bg-blue-50 text-blue-700 dark:bg-blue-500/10 dark:text-blue-400 border-blue-100 dark:border-blue-500/20',
|
||||
purple: 'bg-purple-50 text-purple-700 dark:bg-purple-500/10 dark:text-purple-400 border-purple-100 dark:border-purple-500/20',
|
||||
green: 'bg-green-50 text-green-700 dark:bg-green-500/10 dark:text-green-400 border-green-100 dark:border-green-500/20',
|
||||
gray: 'bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-400 border-gray-200 dark:border-gray-700',
|
||||
};
|
||||
return (
|
||||
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-bold uppercase tracking-wider border ${variants[variant]}`}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function TabButton({ id, label, active, onClick, icon }: { id: string, label: string, active: boolean, onClick: () => void, icon?: React.ReactNode }) {
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`flex items-center gap-2 px-5 py-2.5 rounded-xl text-sm font-bold transition-all duration-300 ${
|
||||
active
|
||||
? 'bg-white dark:bg-gray-700 text-brand-600 dark:text-brand-400 shadow-sm'
|
||||
: 'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200'
|
||||
}`}
|
||||
>
|
||||
{icon}
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function OsGuideContent({ title, steps }: { title: string, steps: string[] }) {
|
||||
return (
|
||||
<div className="p-6 bg-white dark:bg-gray-800 rounded-2xl border border-gray-100 dark:border-gray-700 shadow-sm">
|
||||
<h4 className="text-lg font-bold text-gray-900 dark:text-white mb-4 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>
|
||||
);
|
||||
}
|
||||
|
||||
function CaCard({ cert, isRoot, t, selectedOs, setSelectedOs }: { cert: CaCertificate, isRoot: boolean, t: any, selectedOs: string, setSelectedOs: (os: any) => void }) {
|
||||
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 */}
|
||||
{cert.last_synced_at && (
|
||||
<div className="absolute top-6 right-8 flex items-center gap-1.5 px-2 py-1 rounded-lg bg-green-500/10 border border-green-500/20 text-[10px] font-bold text-green-600 dark:text-green-400 uppercase tracking-tighter">
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-green-500"></span>
|
||||
</span>
|
||||
{t('synced_to_cdn')}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-6 mb-8">
|
||||
<div className="flex items-center gap-6">
|
||||
<div className={`w-16 h-16 rounded-3xl flex items-center justify-center shadow-lg transition-transform group-hover:scale-110 duration-500 ${
|
||||
isRoot ? 'bg-brand-500 text-white shadow-brand-500/20' : 'bg-blue-500 text-white shadow-blue-500/20'
|
||||
}`}>
|
||||
<svg className="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<Badge variant={isRoot ? 'purple' : 'blue'}>
|
||||
{isRoot ? t('root_ca') : t('intermediate_ca')}
|
||||
</Badge>
|
||||
<h4 className="text-2xl font-black text-gray-900 dark:text-white mt-1 group-hover:text-brand-500 transition-colors">{cert.name}</h4>
|
||||
<div className="flex items-center gap-4 mt-2 font-mono text-[10px] text-gray-400">
|
||||
<span className="flex items-center gap-1.5">
|
||||
<span className="w-1 h-1 rounded-full bg-gray-400"></span>
|
||||
ID: {cert.serial}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
|
||||
<DownloadBtn
|
||||
href={cert.cdn_url || `/api/public/ca-certificates/${cert.serial}/download`}
|
||||
label={t('download_standard')}
|
||||
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')}
|
||||
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')}
|
||||
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')}
|
||||
icon={<AppleIcon className="w-4 h-4" />}
|
||||
onClick={() => setSelectedOs('macos')}
|
||||
variant="gray"
|
||||
/>
|
||||
</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')}
|
||||
icon={<LinuxIcon className="w-4 h-4" />}
|
||||
onClick={() => setSelectedOs('linux')}
|
||||
variant="gray"
|
||||
isFullWidth
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DownloadBtn({ href, label, icon, onClick, variant = 'white', isFullWidth = false }: { href: string, label: string, icon: React.ReactNode, onClick: () => void, variant?: 'white' | 'green' | 'blue' | 'gray', isFullWidth?: boolean }) {
|
||||
const variants = {
|
||||
white: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-200 border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700',
|
||||
green: 'bg-green-50 dark:bg-green-500/10 text-green-700 dark:text-green-400 border-green-200 dark:border-green-500/20 hover:bg-green-100 dark:hover:bg-green-500/20',
|
||||
blue: 'bg-blue-50 dark:bg-blue-500/10 text-blue-700 dark:text-blue-400 border-blue-200 dark:border-blue-500/20 hover:bg-blue-100 dark:hover:bg-blue-500/20',
|
||||
gray: 'bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-100 border-gray-200 dark:border-gray-600 hover:bg-gray-200 dark:hover:bg-gray-600',
|
||||
};
|
||||
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
onClick={onClick}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={`flex items-center justify-center gap-2 px-4 py-3 rounded-2xl border font-bold text-xs transition-all duration-300 hover:shadow-lg active:scale-95 ${variants[variant]} ${isFullWidth ? 'w-full' : ''}`}
|
||||
>
|
||||
{icon}
|
||||
{label}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
// Icons
|
||||
const WindowsIcon = ({ className }: { className?: string }) => (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M0 3.449L9.75 2.134V11.3L0 11.3V3.449ZM9.75 12.7L0 12.7V20.551L9.75 19.166V12.7ZM10.5 1.998L24 0.166V11.3L10.5 11.3V1.998ZM10.5 12.7L24 12.7V23.834L10.5 21.966V12.7Z"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const AppleIcon = ({ className }: { className?: string }) => (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.21-1.96 1.07-3.11-1.05.05-2.31.74-3.03 1.59-.65.77-1.2 2.02-1.07 3.12 1.17.09 2.36-.73 3.03-1.6"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const LinuxIcon = ({ className }: { className?: string }) => (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M11.97 2c-3.14 0-5.69 2.51-5.69 5.61v.24c-.11-.03-.23-.05-.36-.05-1.16 0-2.11.93-2.11 2.07s.95 2.06 2.11 2.06c.13 0 .25-.01.36-.04v.24c0 3.1 2.55 5.61 5.69 5.61 3.14 0 5.69-2.51 5.69-5.61v-.24c.11.03.22.04.35.04 1.16 0 2.11-.93 2.11-2.06s-.95-2.07-2.11-2.07c-.13 0-.24.02-.35.05v-.24C17.66 4.51 15.11 2 11.97 2zm0 1.87c2.1 0 3.8 1.67 3.8 3.74v.17c-.38-.08-.78-.13-1.2-.13-2.13 0-3.86 1.69-3.86 3.78s1.73 3.78 3.86 3.78c.42 0 .82-.05 1.2-.13v.17c0 2.07-1.7 3.74-3.8 3.74-2.1 0-3.8-1.67-3.8-3.74v-.17c.38.08.78.13 1.2.13 2.13 0 3.86-1.69 3.86-3.78s-1.73-3.78-3.86-3.78c-.42 0-.82.05-1.2.13v-.17c0-2.07 1.7-3.74 3.8-3.74zM8.33 9.4c.58 0 1.05.47 1.05 1.04s-.47 1.04-1.05 1.04-1.05-.47-1.05-1.04.47-1.04 1.05-1.04zm7.28 0c.58 0 1.05.47 1.05 1.04s-.47 1.04-1.05 1.04-1.05-.47-1.05-1.04.47-1.04 1.05-1.04zM12 21c-1.1 0-2 .9-2 2h4c0-1.1-.9-2-2-2z"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const MobileIcon = ({ className }: { className?: string }) => (
|
||||
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M17 2H7c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 18H7V4h10v16z"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const FileIcon = ({ className }: { className?: string }) => (
|
||||
<svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
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');
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCertificates = async () => {
|
||||
@@ -227,161 +424,54 @@ export default function HomeClient() {
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-brand-500"></div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* Root CA - Centered */}
|
||||
<div className="flex flex-col items-center gap-8 mb-8">
|
||||
{certificates.filter(c => c.type === 'root').map((cert) => (
|
||||
<div key={cert.serial} className="w-full max-w-lg bg-gray-50 dark:bg-gray-900/50 rounded-3xl p-8 border border-gray-100 dark:border-gray-700 hover:shadow-xl transition-all duration-300">
|
||||
<div className="flex items-start justify-between mb-6">
|
||||
<div>
|
||||
<span className={`inline-block px-3 py-1 rounded-full text-xs font-bold uppercase tracking-wider mb-3 bg-purple-100 text-purple-700 dark:bg-purple-500/10 dark:text-purple-400`}>
|
||||
{t('root_ca')}
|
||||
</span>
|
||||
<h3 className="text-2xl font-bold text-gray-900 dark:text-white">{cert.name}</h3>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1 font-mono">{t('serial')}: {cert.serial}</p>
|
||||
</div>
|
||||
<div className="w-12 h-12 bg-white dark:bg-gray-800 rounded-2xl flex items-center justify-center text-gray-400 shadow-sm">
|
||||
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<a
|
||||
href={cert.cdn_url || `/download/ca-certificate?serial=${cert.serial}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 px-4 py-3 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-200 rounded-xl border border-gray-200 dark:border-gray-700 font-medium text-sm hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
|
||||
title={t('download_standard_title')}
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
|
||||
</svg>
|
||||
{t('download_standard')}
|
||||
</a>
|
||||
<a
|
||||
href={cert.der_cdn_url || `/download/ca-certificate?serial=${cert.serial}&format=der`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 px-4 py-3 bg-green-50 dark:bg-green-500/10 text-green-700 dark:text-green-400 rounded-xl border border-green-200 dark:border-green-500/20 font-medium text-sm hover:bg-green-100 dark:hover:bg-green-500/20 transition-colors"
|
||||
title={t('download_android_title')}
|
||||
>
|
||||
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M17.523 15.3414C17.523 16.7113 16.4805 17.7719 15.1743 17.7719C13.8681 17.7719 12.8257 16.7113 12.8257 15.3414C12.8257 13.9715 13.8681 12.9109 15.1743 12.9109C16.4805 12.9109 17.523 13.9715 17.523 15.3414ZM11.1714 15.3414C11.1714 16.7113 10.1289 17.7719 8.82276 17.7719C7.51659 17.7719 6.47412 16.7113 6.47412 15.3414C6.47412 13.9715 7.51659 12.9109 8.82276 12.9109C10.1289 12.9109 11.1714 13.9715 11.1714 15.3414ZM16.3262 5.86762L17.7119 3.44754C17.7981 3.29806 17.7513 3.10499 17.5932 3.01894C17.4391 2.92983 17.2505 2.97372 17.152 3.1232L15.7251 5.61793C14.0754 4.86961 12.1956 4.86961 10.5982 5.5645L9.1713 3.06977C9.0768 2.92028 8.88412 2.87234 8.73004 2.9654C8.5719 3.05144 8.52504 3.24451 8.6112 3.394L9.99708 5.8143C6.31383 7.82084 3.86221 11.6968 3.86221 16.0359H20.1378C20.1378 11.6968 17.6862 7.82084 16.3262 5.86762Z"/>
|
||||
</svg>
|
||||
{t('download_android')}
|
||||
</a>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<a
|
||||
href={`/download/ca-certificate?serial=${cert.serial}&target=windows`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 px-4 py-3 bg-blue-50 dark:bg-blue-500/10 text-blue-700 dark:text-blue-400 rounded-xl border border-blue-200 dark:border-blue-500/20 font-medium text-sm hover:bg-blue-100 dark:hover:bg-blue-500/20 transition-colors"
|
||||
title={t('download_windows_title')}
|
||||
>
|
||||
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M0 3.449L9.75 2.134V11.3L0 11.3V3.449ZM9.75 12.7L0 12.7V20.551L9.75 19.166V12.7ZM10.5 1.998L24 0.166V11.3L10.5 11.3V1.998ZM10.5 12.7L24 12.7V23.834L10.5 21.966V12.7Z"/>
|
||||
</svg>
|
||||
{t('download_windows')}
|
||||
</a>
|
||||
<a
|
||||
href={`/download/ca-certificate?serial=${cert.serial}&target=mac`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 px-4 py-3 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-100 rounded-xl border border-gray-200 dark:border-gray-600 font-medium text-sm hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
|
||||
title={t('download_macos_title')}
|
||||
>
|
||||
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.21-1.96 1.07-3.11-1.05.05-2.31.74-3.03 1.59-.65.77-1.2 2.02-1.07 3.12 1.17.09 2.36-.73 3.03-1.6"/>
|
||||
</svg>
|
||||
{t('download_macos')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div className="space-y-16">
|
||||
{/* Root CA Section */}
|
||||
<div className="flex flex-col items-center">
|
||||
<h3 className="text-sm font-bold text-brand-500 uppercase tracking-widest mb-8 px-4 py-1 bg-brand-50 dark:bg-brand-500/10 rounded-full border border-brand-100 dark:border-brand-500/20">
|
||||
{t('root_ca_hierarchy')}
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 gap-8 w-full max-w-2xl px-4">
|
||||
{certificates.filter(c => c.type === 'root').map((cert) => (
|
||||
<CaCard key={cert.serial} cert={cert} isRoot={true} t={t} selectedOs={selectedOs} setSelectedOs={setSelectedOs} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Intermediate CAs - Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 max-w-5xl mx-auto">
|
||||
{certificates.filter(c => c.type !== 'root').map((cert) => (
|
||||
<div key={cert.serial} className="bg-gray-50 dark:bg-gray-900/50 rounded-3xl p-8 border border-gray-100 dark:border-gray-700 hover:shadow-xl transition-all duration-300">
|
||||
<div className="flex items-start justify-between mb-6">
|
||||
<div>
|
||||
<span className={`inline-block px-3 py-1 rounded-full text-xs font-bold uppercase tracking-wider mb-3 bg-blue-100 text-blue-700 dark:bg-blue-500/10 dark:text-blue-400`}>
|
||||
{t('intermediate_ca')}
|
||||
</span>
|
||||
<h3 className="text-2xl font-bold text-gray-900 dark:text-white">{cert.name}</h3>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1 font-mono">{t('serial')}: {cert.serial}</p>
|
||||
</div>
|
||||
<div className="w-12 h-12 bg-white dark:bg-gray-800 rounded-2xl flex items-center justify-center text-gray-400 shadow-sm">
|
||||
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<a
|
||||
href={cert.cdn_url || `/download/ca-certificate?serial=${cert.serial}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 px-4 py-3 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-200 rounded-xl border border-gray-200 dark:border-gray-700 font-medium text-sm hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
|
||||
title={t('download_standard_title')}
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
|
||||
</svg>
|
||||
{t('download_standard')}
|
||||
</a>
|
||||
<a
|
||||
href={cert.der_cdn_url || `/download/ca-certificate?serial=${cert.serial}&format=der`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 px-4 py-3 bg-green-50 dark:bg-green-500/10 text-green-700 dark:text-green-400 rounded-xl border border-green-200 dark:border-green-500/20 font-medium text-sm hover:bg-green-100 dark:hover:bg-green-500/20 transition-colors"
|
||||
title={t('download_android_title')}
|
||||
>
|
||||
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M17.523 15.3414C17.523 16.7113 16.4805 17.7719 15.1743 17.7719C13.8681 17.7719 12.8257 16.7113 12.8257 15.3414C12.8257 13.9715 13.8681 12.9109 15.1743 12.9109C16.4805 12.9109 17.523 13.9715 17.523 15.3414ZM11.1714 15.3414C11.1714 16.7113 10.1289 17.7719 8.82276 17.7719C7.51659 17.7719 6.47412 16.7113 6.47412 15.3414C6.47412 13.9715 7.51659 12.9109 8.82276 12.9109C10.1289 12.9109 11.1714 13.9715 11.1714 15.3414ZM16.3262 5.86762L17.7119 3.44754C17.7981 3.29806 17.7513 3.10499 17.5932 3.01894C17.4391 2.92983 17.2505 2.97372 17.152 3.1232L15.7251 5.61793C14.0754 4.86961 12.1956 4.86961 10.5982 5.5645L9.1713 3.06977C9.0768 2.92028 8.88412 2.87234 8.73004 2.9654C8.5719 3.05144 8.52504 3.24451 8.6112 3.394L9.99708 5.8143C6.31383 7.82084 3.86221 11.6968 3.86221 16.0359H20.1378C20.1378 11.6968 17.6862 7.82084 16.3262 5.86762Z"/>
|
||||
</svg>
|
||||
{t('download_android')}
|
||||
</a>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<a
|
||||
href={`/download/ca-certificate?serial=${cert.serial}&target=windows`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 px-4 py-3 bg-blue-50 dark:bg-blue-500/10 text-blue-700 dark:text-blue-400 rounded-xl border border-blue-200 dark:border-blue-500/20 font-medium text-sm hover:bg-blue-100 dark:hover:bg-blue-500/20 transition-colors"
|
||||
title={t('download_windows_title')}
|
||||
>
|
||||
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M0 3.449L9.75 2.134V11.3L0 11.3V3.449ZM9.75 12.7L0 12.7V20.551L9.75 19.166V12.7ZM10.5 1.998L24 0.166V11.3L10.5 11.3V1.998ZM10.5 12.7L24 12.7V23.834L10.5 21.966V12.7Z"/>
|
||||
</svg>
|
||||
{t('download_windows')}
|
||||
</a>
|
||||
<a
|
||||
href={`/download/ca-certificate?serial=${cert.serial}&target=mac`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 px-4 py-3 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-100 rounded-xl border border-gray-200 dark:border-gray-600 font-medium text-sm hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
|
||||
title={t('download_macos_title')}
|
||||
>
|
||||
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.21-1.96 1.07-3.11-1.05.05-2.31.74-3.03 1.59-.65.77-1.2 2.02-1.07 3.12 1.17.09 2.36-.73 3.03-1.6"/>
|
||||
</svg>
|
||||
{t('download_macos')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{/* Intermediate CAs Section */}
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="w-px h-12 bg-gradient-to-b from-brand-500/50 to-transparent mb-8"></div>
|
||||
<h3 className="text-sm font-bold text-blue-500 uppercase tracking-widest mb-8 px-4 py-1 bg-blue-50 dark:bg-blue-500/10 rounded-full border border-blue-100 dark:border-blue-500/20">
|
||||
{t('intermediate_ca_hierarchy')}
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 w-full max-w-6xl px-4">
|
||||
{certificates.filter(c => c.type !== 'root').map((cert) => (
|
||||
<CaCard key={cert.serial} cert={cert} isRoot={false} t={t} selectedOs={selectedOs} setSelectedOs={setSelectedOs} />
|
||||
))}
|
||||
</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 animate-in fade-in slide-in-from-bottom-2 duration-500">
|
||||
{selectedOs === 'windows' && <OsGuideContent title="Windows Installation" steps={t.raw('guide_steps_windows')} />}
|
||||
{selectedOs === 'macos' && <OsGuideContent title="macOS Installation" steps={t.raw('guide_steps_macos')} />}
|
||||
{selectedOs === 'linux' && <OsGuideContent title="Linux Installation" steps={t.raw('guide_steps_linux')} />}
|
||||
{selectedOs === 'mobile' && <OsGuideContent title="Mobile Installation" steps={t.raw('guide_steps_mobile')} />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -182,13 +182,42 @@
|
||||
"download_android": "Android",
|
||||
"download_windows": "Windows",
|
||||
"download_macos": "macOS",
|
||||
"download_linux": "Linux",
|
||||
"download_mobile": "Android/iOS",
|
||||
"download_standard_title": "Download Standard CRT (PEM)",
|
||||
"download_android_title": "Download for Android (DER)",
|
||||
"download_windows_title": "Download Windows Installer (.bat)",
|
||||
"download_macos_title": "Download macOS Profile (.mobileconfig)",
|
||||
"download_linux_title": "Download Linux Installer (.sh)",
|
||||
"root_ca": "Root CA",
|
||||
"intermediate_ca": "Intermediate CA",
|
||||
"root_ca_hierarchy": "Root Trust Foundation",
|
||||
"intermediate_ca_hierarchy": "Secure Issuance Layer",
|
||||
"serial": "Serial",
|
||||
"last_synced": "Last synced",
|
||||
"synced_to_cdn": "Ready on CDN",
|
||||
"install_guide_title": "How to Install",
|
||||
"install_guide_desc": "One-click installers or manual steps for your device.",
|
||||
"guide_steps_windows": [
|
||||
"Download the .bat installer.",
|
||||
"Right-click and Choose 'Run as Administrator'.",
|
||||
"Confirm the security prompt to add to 'Root' store."
|
||||
],
|
||||
"guide_steps_macos": [
|
||||
"Download the .mobileconfig profile.",
|
||||
"Open System Settings > Profiles (or Privacy & Security).",
|
||||
"Double-click the TrustLab profile and click Install.",
|
||||
"Go to 'About' > 'Certificate Trust Settings' and enable full trust."
|
||||
],
|
||||
"guide_steps_linux": [
|
||||
"Download the .sh installer script.",
|
||||
"Open terminal and run: sudo bash install-*.sh",
|
||||
"The script will automatically detect and update your CA store."
|
||||
],
|
||||
"guide_steps_mobile": [
|
||||
"Android: Settings > Security > Install from storage > CA Certificate.",
|
||||
"iOS: Install the profile, then Settings > General > About > Certificate Trust Settings."
|
||||
],
|
||||
"cta_ready_title": "Ready to secure your application?",
|
||||
"cta_ready_desc": "Manage your internal security infrastructure independently and securely.",
|
||||
"cta_free_account": "Register Now",
|
||||
|
||||
@@ -182,13 +182,42 @@
|
||||
"download_android": "Android",
|
||||
"download_windows": "Windows",
|
||||
"download_macos": "macOS",
|
||||
"download_linux": "Linux",
|
||||
"download_mobile": "Android/iOS",
|
||||
"download_standard_title": "Unduh CRT Standar (PEM)",
|
||||
"download_android_title": "Unduh untuk Android (DER)",
|
||||
"download_windows_title": "Unduh Penginstal Windows (.bat)",
|
||||
"download_macos_title": "Unduh Profil macOS (.mobileconfig)",
|
||||
"root_ca": "CA Akar",
|
||||
"download_linux_title": "Unduh Penginstal Linux (.sh)",
|
||||
"root_ca": "Root CA",
|
||||
"intermediate_ca": "Intermediate CA",
|
||||
"root_ca_hierarchy": "Fondasi Kepercayaan Akar",
|
||||
"intermediate_ca_hierarchy": "Lapisan Penerbitan Aman",
|
||||
"serial": "Serial",
|
||||
"last_synced": "Terakhir disinkronkan",
|
||||
"synced_to_cdn": "Siap di CDN",
|
||||
"install_guide_title": "Cara Instalasi",
|
||||
"install_guide_desc": "Penginstal satu-klik atau langkah manual untuk perangkat Anda.",
|
||||
"guide_steps_windows": [
|
||||
"Unduh penginstal .bat.",
|
||||
"Klik kanan dan pilih 'Run as Administrator'.",
|
||||
"Konfirmasi jendela keamanan untuk menambahkan ke penyimpanan 'Root'. Kesalahan umum: Memilih toko 'Personal' akan membuat cert tidak dikenal sistem."
|
||||
],
|
||||
"guide_steps_macos": [
|
||||
"Unduh profil .mobileconfig.",
|
||||
"Buka System Settings > Profiles (atau Privacy & Security).",
|
||||
"Klik dua kali profil TrustLab dan klik Install.",
|
||||
"Buka 'About' > 'Certificate Trust Settings' dan aktifkan kepercayaan penuh."
|
||||
],
|
||||
"guide_steps_linux": [
|
||||
"Unduh skrip penginstal .sh.",
|
||||
"Buka terminal dan jalankan: sudo bash install-*.sh",
|
||||
"Skrip akan secara otomatis mendeteksi distro dan memperbarui penyimpanan CA Anda."
|
||||
],
|
||||
"guide_steps_mobile": [
|
||||
"Android: Pengaturan > Keamanan > Instal dari penyimpanan > Sertifikat CA.",
|
||||
"iOS: Instal profil, lalu Pengaturan > Umum > Mengenai > Pengaturan Kepercayaan Sertifikat."
|
||||
],
|
||||
"cta_ready_title": "Siap untuk mengamankan aplikasi Anda?",
|
||||
"cta_ready_desc": "Kelola infrastruktur keamanan internal Anda secara mandiri, aman, dan terorganisir.",
|
||||
"cta_free_account": "Daftar Sekarang",
|
||||
|
||||
Reference in New Issue
Block a user