'use client'; import { PageHeader } from '@/components/layout/page-header'; import { useTranslations } from 'next-intl'; import { useState } from 'react'; import { DropZone } from './components/DropZone'; import { PasswordField } from './components/PasswordField'; import { InspectTab } from './components/InspectTab'; import { SplitTab } from './components/SplitTab'; import { MergeTab } from './components/MergeTab'; import { ConvertTab } from './components/ConvertTab'; type TabId = 'inspect' | 'split' | 'merge' | 'convert'; const TABS: TabId[] = ['inspect', 'split', 'merge', 'convert']; /** Returns true if the selected file is a PFX/P12 (requires decryption password) */ function isPfxFile(file: File | null): boolean { if (!file) return false; const name = file.name.toLowerCase(); return name.endsWith('.pfx') || name.endsWith('.p12'); } /** * CertManagerPage — tab-based shell for certificate operations. * Layout per UI-SPEC: max-w-4xl, shared input card, tab nav, tab content card. * T-09-02: password lives in local React state only; never logged or placed in URLs. * T-09-04: all API calls via postForm() which sends credentials:'include'. * * The shared PasswordField is shown when: * 1. A PFX/P12 file is selected in the shared DropZone, OR * 2. The active tab (Merge or Convert) has PFX chosen as the output format. */ export default function CertManagerPage() { const t = useTranslations('certManager'); const [activeTab, setActiveTab] = useState('inspect'); const [file, setFile] = useState(null); const [pemText, setPemText] = useState(''); const [password, setPassword] = useState(''); // Track the output format of the active Merge and Convert tabs // so the shared PasswordField can be shown when PFX output is chosen. const [mergeOutputFormat, setMergeOutputFormat] = useState('pem'); const [convertOutputFormat, setConvertOutputFormat] = useState('pem'); const showPassword = isPfxFile(file) || (activeTab === 'merge' && mergeOutputFormat === 'pfx') || (activeTab === 'convert' && convertOutputFormat === 'pfx'); const handleFile = (selected: File) => { setFile(selected); setPemText(''); // Single active source: file XOR paste setPassword(''); }; const handleClearFile = () => { setFile(null); setPassword(''); }; const handlePemChange = (text: string) => { setPemText(text); if (text) { setFile(null); // Single active source: file XOR paste setPassword(''); } }; const handleTabChange = (tab: TabId) => { setActiveTab(tab); // Tab switch: keep shared input, clear result (result cleared per tab re-render) }; const renderActiveTab = () => { switch (activeTab) { case 'inspect': return ; case 'split': return ; case 'merge': return ( ); case 'convert': return ( ); } }; return (
{/* Shared input card */}
{/* DropZone */} {/* ODER divider */}

{t('or')}
{/* PEM textarea */}