import { useState, useRef } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { useAuth } from "../../context/AuthContext"; import { useAlert } from "../../context/AlertContext"; import useModalLock from "../../hooks/useModalLock"; import apiFetch from "../../utils/api"; const API_BASE = "/api/admin"; interface DashProfileProps { totpEnabled: boolean; totpLoading: boolean; totpSubmitting: boolean; onStart2FASetup: () => void; onConfirm2FA: () => void; onDisable2FA: () => void; totpSecret: string | null; totpQrUri: string | null; totpCode: string; setTotpCode: (code: string) => void; backupCodes: string[] | null; setBackupCodes: (codes: string[] | null) => void; show2FASetup: boolean; setShow2FASetup: (show: boolean) => void; show2FADisable: boolean; setShow2FADisable: (show: boolean) => void; disableCode: string; setDisableCode: (code: string) => void; } interface ProfileFormData { username: string; email: string; new_password: string; current_password: string; first_name: string; last_name: string; } export default function DashProfile({ totpEnabled, totpLoading, totpSubmitting, onStart2FASetup, onConfirm2FA, onDisable2FA, totpSecret, totpQrUri, totpCode, setTotpCode, backupCodes, setBackupCodes, show2FASetup, setShow2FASetup, show2FADisable, setShow2FADisable, disableCode, setDisableCode, }: DashProfileProps) { const { user, updateUser } = useAuth(); const alert = useAlert(); const totpSetupRef = useRef(null); const [showModal, setShowModal] = useState(false); const [formData, setFormData] = useState({ username: "", email: "", new_password: "", current_password: "", first_name: "", last_name: "", }); useModalLock(showModal); const openEditModal = () => { const nameParts = (user?.fullName || "").split(" "); setFormData({ username: user?.username || "", email: user?.email || "", new_password: "", current_password: "", first_name: nameParts[0] || "", last_name: nameParts.slice(1).join(" ") || "", }); setShowModal(true); }; const handleSubmit = async (e?: React.FormEvent) => { e?.preventDefault(); const dataToSave = { ...formData }; if (dataToSave.new_password && !dataToSave.current_password) { alert.error("Pro změnu hesla zadejte aktuální heslo"); return; } if (dataToSave.current_password && !dataToSave.new_password) { alert.error("Pro změnu hesla zadejte nové heslo"); return; } // Strip empty password fields so Zod doesn't reject "" if (!dataToSave.current_password) delete (dataToSave as any).current_password; if (!dataToSave.new_password) delete (dataToSave as any).new_password; try { const response = await apiFetch(`${API_BASE}/profile`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(dataToSave), }); const data = await response.json(); if (data.success) { updateUser({ username: dataToSave.username, email: dataToSave.email, fullName: `${dataToSave.first_name} ${dataToSave.last_name}`.trim(), }); setShowModal(false); await new Promise((resolve) => setTimeout(resolve, 300)); alert.success("Profil byl upraven"); } else { alert.error(data.error || "Nepodařilo se uložit profil"); } } catch { alert.error("Chyba připojení"); } }; function getTotpStatusText(): string { if (totpLoading) { return "Načítání..."; } return totpEnabled ? "Aktivní" : "Neaktivní"; } return ( <>

Váš účet

Uživatel {user?.username}
E-mail {user?.email}
Jméno {user?.fullName}
Role {user?.roleDisplay || String(user?.role || "")}
{/* 2FA Section */}
Dvoufaktorové ověření (2FA)
{getTotpStatusText()}
{!totpLoading && (totpEnabled ? ( ) : ( ))}
{/* Edit Profile Modal */} {showModal && (
setShowModal(false)} />

Upravit profil

setFormData({ ...formData, first_name: e.target.value, }) } required className="admin-form-input" />
setFormData({ ...formData, last_name: e.target.value, }) } required className="admin-form-input" />
setFormData({ ...formData, username: e.target.value }) } required className="admin-form-input" />
setFormData({ ...formData, email: e.target.value }) } required className="admin-form-input" />
setFormData({ ...formData, current_password: e.target.value, }) } className="admin-form-input" placeholder="Pro změnu hesla, zadejte aktuální heslo" />
setFormData({ ...formData, new_password: e.target.value, }) } className="admin-form-input" placeholder="Pro změnu hesla, zadejte nové heslo" />
)} {/* 2FA Setup Modal */} {show2FASetup && (
{ if (!backupCodes) { setShow2FASetup(false); } }} />

{backupCodes ? "Záložní kódy" : "Nastavení 2FA"}

{backupCodes ? (
Uložte si tyto kódy na bezpečné místo. Každý kód lze použít pouze jednou. Po zavření tohoto okna je již neuvidíte.
{backupCodes.map((code) => (
{code}
))}
) : (

Naskenujte QR kód v autentizační aplikaci (Google Authenticator, Authy, Microsoft Authenticator apod.)

{totpQrUri && (
TOTP QR Code
)} {totpSecret && (
{totpSecret}
)}
setTotpCode(e.target.value.replace(/\D/g, "")) } placeholder="000000" className="admin-form-input" style={{ textAlign: "center", fontSize: "1.25rem", letterSpacing: "0.4rem", fontFamily: "monospace", }} onKeyDown={(e) => { if (e.key === "Enter" && totpCode.length === 6) { onConfirm2FA(); } }} />
)}
{backupCodes ? ( ) : ( <> )}
)} {/* 2FA Disable Modal */} {show2FADisable && (
setShow2FADisable(false)} />

Deaktivovat 2FA

Pro deaktivaci dvoufaktorového ověření zadejte aktuální kód z autentizační aplikace.

setDisableCode(e.target.value.replace(/\D/g, "")) } placeholder="000000" className="admin-form-input" style={{ textAlign: "center", fontSize: "1.25rem", letterSpacing: "0.4rem", fontFamily: "monospace", }} onKeyDown={(e) => { if (e.key === "Enter" && disableCode.length === 6) { onDisable2FA(); } }} autoFocus />
)} ); }