"use client"; import React, { useState } from "react"; import useSWR, { mutate } from "swr"; import axios from "@/lib/axios"; import { useToast } from "@/context/ToastContext"; import Button from "@/components/ui/button/Button"; import Input from "@/components/form/input/InputField"; import Label from "@/components/form/Label"; import { Modal } from "@/components/ui/modal"; import { useModal } from "@/hooks/useModal"; import Badge from "@/components/ui/badge/Badge"; import Link from "next/link"; import { ShieldCheck, History, Link2, AlertTriangle, Mail, Smartphone, Globe, Monitor, Trash2, Lock, Bell, Palette, Send, Languages, Download, Key, ChevronRight, LogOut, DoorOpen, Sun, Moon, Laptop } from "lucide-react"; import { useTheme } from "@/context/ThemeContext"; import Switch from "@/components/form/switch/Switch"; import { useRouter, useSearchParams } from "next/navigation"; import PageLoader from "@/components/ui/PageLoader"; import { useTranslations } from "next-intl"; import { useI18n } from "@/components/providers/I18nProvider"; import { useAuth } from "@/hooks/useAuth"; const fetcher = (url: string) => axios.get(url).then((res) => res.data); export default function SettingsClient() { const router = useRouter(); const searchParams = useSearchParams(); const { user, mutate: mutateUser } = useAuth(); const { data: loginHistory, isLoading: historyLoading } = useSWR("/api/profile/login-history", fetcher); const { data: sessions, isLoading: sessionsLoading } = useSWR("/api/profile/sessions", fetcher); const { data: apiKeys } = useSWR("/api/api-keys", fetcher); const { addToast } = useToast(); const { theme: currentTheme, setTheme } = useTheme(); const { isOpen, openModal, closeModal } = useModal(); const { setLocale } = useI18n(); const t = useTranslations("Settings"); const [isSavingPassword, setIsSavingPassword] = useState(false); const [isDeletingAccount, setIsDeletingAccount] = useState(false); const [passwordData, setPasswordData] = useState({ current_password: "", password: "", password_confirmation: "", }); // 2FA State const [twoFactorMode, setTwoFactorMode] = useState<'enable' | 'disable' | 'recovery' | null>(null); const [qrCode, setQrCode] = useState(null); const [setupSecret, setSetupSecret] = useState(null); const [verificationCode, setVerificationCode] = useState(""); const [recoveryCodes, setRecoveryCodes] = useState([]); const [confirmPassword, setConfirmPassword] = useState(""); const [isProcessing2FA, setIsProcessing2FA] = useState(false); const enable2FA = async () => { try { setIsProcessing2FA(true); const { data } = await axios.post('/api/auth/2fa/enable'); setQrCode(data.qr_code); setSetupSecret(data.secret); setTwoFactorMode('enable'); } catch (error) { addToast(t("toast_2fa_setup_failed"), "error"); } finally { setIsProcessing2FA(false); } }; const confirmEnable2FA = async () => { try { setIsProcessing2FA(true); const { data } = await axios.post('/api/auth/2fa/confirm', { code: verificationCode }); addToast(t("toast_2fa_enabled"), "success"); setRecoveryCodes(data.recovery_codes); setTwoFactorMode('recovery'); // Show recovery codes immediately mutateUser(); } catch (error: any) { addToast(error.response?.data?.message || t("toast_2fa_setup_failed"), "error"); } finally { setIsProcessing2FA(false); } }; const disable2FA = async () => { try { setIsProcessing2FA(true); await axios.delete('/api/auth/2fa/disable', { data: { password: confirmPassword } }); addToast(t("toast_2fa_disabled"), "success"); setTwoFactorMode(null); setConfirmPassword(""); mutateUser(); } catch (error: any) { addToast(error.response?.data?.message || t("toast_2fa_disable_failed"), "error"); } finally { setIsProcessing2FA(false); } }; const showRecoveryCodes = async () => { try { const { data } = await axios.get('/api/auth/2fa/recovery-codes'); setRecoveryCodes(data.recovery_codes); setTwoFactorMode('recovery'); } catch (error) { addToast(t("toast_recovery_failed"), "error"); } }; const close2FAModal = () => { setTwoFactorMode(null); setQrCode(null); setVerificationCode(""); setRecoveryCodes([]); setConfirmPassword(""); }; // Handle Success/Error Alerts from URL (e.g. from OAuth Callback) React.useEffect(() => { const success = searchParams.get("success"); const error = searchParams.get("error"); if (success) { if (success === 'account_connected') addToast(t("toast_connected"), "success"); // Clear params router.replace("/dashboard/settings"); } if (error) { if (error === 'already_connected') addToast(t("toast_already_connected"), "warning"); if (error === 'connected_to_other_account') addToast(t("toast_connected_other"), "error"); if (error === 'login_required_to_connect') addToast(t("toast_login_required"), "error"); // Clear params router.replace("/dashboard/settings"); } }, [searchParams, router, addToast, t]); const connectAccount = async (provider: string) => { try { addToast(t("toast_connect_init"), "info"); // Get secure link token to identify user during redirect const { data } = await axios.get('/api/auth/link-token'); // Redirect to backend auth endpoint with context and token window.location.href = `${process.env.NEXT_PUBLIC_API_URL}/api/auth/${provider}/redirect?context=connect&link_token=${data.token}`; } catch (error) { addToast(t("toast_connect_failed"), "error"); } }; const disconnectAccount = async (provider: string) => { // confirm disconnect? try { addToast(t("toast_disconnect_init"), "info"); await axios.delete(`/api/auth/social/${provider}`); addToast(t("toast_disconnected"), "success"); mutateUser(); // Refresh user state } catch (error: any) { addToast(error.response?.data?.message || t("toast_disconnect_failed"), "error"); } }; const savePreference = async (key: string, value: any) => { try { // Optimistic update could go here, but for simplicity we rely on SWR revalidation or just wait await axios.patch('/api/profile', { [key]: value }); if (key === 'theme') setTheme(value); if (key === 'language') setLocale(value); mutateUser(); // Refresh user data to confirm sync addToast(t("toast_settings_updated"), "success"); } catch (err) { addToast(t("toast_settings_failed"), "error"); } }; const handlePasswordChange = (field: string, value: string) => { setPasswordData((prev) => ({ ...prev, [field]: value })); }; const updatePassword = async (e: React.FormEvent) => { e.preventDefault(); if (passwordData.password !== passwordData.password_confirmation) { addToast(t("toast_password_mismatch"), "error"); return; } setIsSavingPassword(true); try { await axios.put("/api/profile/password", passwordData); addToast(t("toast_password_updated"), "success"); setPasswordData({ current_password: "", password: "", password_confirmation: "", }); } catch (err: any) { const message = err.response?.data?.message || t("toast_password_failed"); addToast(message, "error"); } finally { setIsSavingPassword(false); } }; const revokeSession = async (id: string) => { try { await axios.delete(`/api/profile/sessions/${id}`); mutate("/api/profile/sessions"); addToast(t("toast_session_revoked"), "success"); } catch (err) { addToast(t("toast_revoke_failed"), "error"); } }; const handleDeleteAccount = async () => { setIsDeletingAccount(true); try { await axios.delete("/api/profile"); addToast(t("toast_account_deleted"), "success"); setTimeout(() => { window.location.href = "/signin"; }, 2000); } catch (err) { addToast(t("toast_delete_failed"), "error"); } finally { setIsDeletingAccount(false); closeModal(); } }; const getDeviceIcon = (deviceType: string) => { switch (deviceType?.toLowerCase()) { case 'ios': case 'android': return ; case 'mac': case 'windows': case 'linux': return ; default: return ; } }; return (
{/* Account Verification & Summary */}

{t("email_verification")}

{user?.email_verified_at ? ( }> {t("verified")} ) : ( {t("not_verified")} )} {user?.email}

{t("api_keys_summary")}

{t("api_keys_desc", { count: apiKeys?.data?.filter((k: any) => k.is_active).length || 0 })}

{/* Security: Update Password */}

{t("security_password")}

handlePasswordChange("current_password", e.target.value)} placeholder={t("current_password")} required />
handlePasswordChange("password", e.target.value)} placeholder={t("new_password")} required />
handlePasswordChange("password_confirmation", e.target.value)} placeholder={t("confirm_new_password")} required />
{/* Active Sessions Management */}

{t("active_sessions")}

{sessionsLoading ? ( ) : sessions?.length > 0 ? (
{sessions.map((session: any) => (

{session.name || "Default Session"}

{session.is_current && ( {t("session_current")} )}

Last active: {new Date(session.last_active * 1000).toLocaleString()}

{!session.is_current && ( )}
))}
) : (
{t("no_sessions")}
)}
{/* Login History (Last 30 Days) */}

{t("login_activity")}

{t("last_month")}
{historyLoading ? ( ) : loginHistory?.length > 0 ? (
{loginHistory.map((item: any) => ( ))}
{t("browser_os")} {t("ip_address")} {t("location")} {t("time")}
{getDeviceIcon(item.device_type)}

{item.browser}

{item.os}

{item.ip_address}
{item.country_code && (
{item.country}
)}

{item.city && item.country ? `${item.city}, ${item.country}` : "Unknown"}

{new Date(item.created_at).toLocaleString()}

) : (
{t("no_activity")}
)}
{/* Advanced Security & UI Placeholders */}
{/* 2FA Section */}

{t("2fa_title")}

{user?.two_factor_confirmed_at ? ( Enabled ) : ( Disabled )}

{t("2fa_desc")}

{user?.two_factor_confirmed_at ? (
) : ( )}
{/* Notifications Placeholder */}

{t("notifications")}

{/* Badge Removed */}
{t("email_alerts")} {t("email_alerts_desc")}
savePreference('settings_email_alerts', checked)} disabled={!user} />
{t("cert_renewal")} {t("cert_renewal_desc")}
savePreference('settings_certificate_renewal', checked)} disabled={!user} />
{(user?.role === 'admin' || user?.role === 'owner') && (
)}
{/* Appearance Settings */}

{t("appearance")}

{[ { id: 'light', label: t('light'), icon: }, { id: 'dark', label: t('dark'), icon: }, { id: 'system', label: t('system'), icon: }, ].map((t) => ( ))}
{/* Export & Data Placeholder */}

{t("privacy_data")}

{t("privacy_desc")}

{/* Landing Page Selection */}

{t("landing_page")}

{[ { id: '/dashboard', label: t('landing_page_dashboard'), icon: }, { id: '/dashboard/support', label: t('landing_page_support'), icon: }, { id: '/dashboard/certificates', label: t('landing_page_certs'), icon: }, { id: '/dashboard/api-keys', label: t('landing_page_keys'), icon: }, ].map((option) => ( ))}
{/* Linked Accounts */}

{t("linked_accounts")}

{t("google_account")}

{user?.social_accounts?.some((a: any) => a.provider === 'google') ? ( {t("connected")} ) : ( {t("not_connected")} )}
{user?.social_accounts?.some((a: any) => a.provider === 'google') ? ( ) : ( )}

{t("github_account")}

{user?.social_accounts?.some((a: any) => a.provider === 'github') ? ( {t("connected")} ) : ( {t("not_connected")} )}
{user?.social_accounts?.some((a: any) => a.provider === 'github') ? ( ) : ( )}
{/* Danger Zone */}

{t("danger_zone")}

{t("delete_account_desc")}

{/* Delete Confirmation Modal */}

{t("delete_modal_title")}

{t("delete_modal_desc")}

{/* 2FA Modals */}
{twoFactorMode === 'enable' && (

{t("setup_2fa_modal_title")}

{t("setup_2fa_modal_desc")}

{!qrCode ? (
) : (

{t("2fa_step_1")}

{t("2fa_step_1_desc")}

{t("2fa_step_2")}

setVerificationCode(e.target.value.replace(/[^0-9]/g, '').slice(0, 6))} maxLength={6} />

{t("2fa_step_2_desc")}

)}
)} {twoFactorMode === 'recovery' && (

{t("recovery_codes_title")}

{t("recovery_codes_desc")}

{recoveryCodes.map((code, idx) => (
{code}
))}
)} {twoFactorMode === 'disable' && (

{t("disable_2fa")}?

{t("disable_2fa_warning")}

setConfirmPassword(e.target.value)} placeholder={t("current_password")} />
)}
); }