import React, { useState, useEffect } from 'react'; import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'; import { Head, usePage, useForm, router } from '@inertiajs/react'; import { PageProps } from '@/types'; import Swal from 'sweetalert2'; import { swal } from '@/lib/swal'; // FilePond import { FilePond, registerPlugin } from 'react-filepond'; import 'filepond/dist/filepond.min.css'; import FilePondPluginImagePreview from 'filepond-plugin-image-preview'; import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css'; import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type'; registerPlugin(FilePondPluginImagePreview, FilePondPluginFileValidateType); interface SettingsProps extends PageProps { mustVerifyEmail: boolean; status?: string; twoFactorSettings: { totp_allowed: boolean; email_allowed: boolean; }; twoFactor: { enabled: boolean; qr_code: string | null; secret: string | null; email_enabled: boolean; smtp_configured: boolean; recovery_codes: string[]; }; } /* ─── Reusable Components from System Settings ─────────────────── */ function SectionCard({ title, description, children, delay = '0s', variant = 'default' }: { title: string; description: string; children: React.ReactNode; delay?: string; variant?: 'default' | 'danger' }) { const isDanger = variant === 'danger'; return (

{title}

{description}

{children}
); } function InputField({ label, id, type = 'text', value, onChange, error, placeholder, required = false }: any) { return (
{error &&

{error}

}
); } const SETTINGS_TABS = ['profile', 'security', '2fa', 'danger'] as const; type SettingsTab = typeof SETTINGS_TABS[number]; function getSettingsTabFromHash(): SettingsTab { const hash = window.location.hash.replace('#', '') as SettingsTab; return SETTINGS_TABS.includes(hash) ? hash : 'profile'; } export default function SettingsIndex({ twoFactor, twoFactorSettings }: SettingsProps) { const { user } = usePage().props.auth; const [activeTab, setActiveTab] = useState(() => { const hash = getSettingsTabFromHash(); if (hash === '2fa' && !(twoFactorSettings.totp_allowed || twoFactorSettings.email_allowed)) { return 'profile'; } return hash; }); useEffect(() => { const onHashChange = () => { const hash = getSettingsTabFromHash(); if (hash === '2fa' && !(twoFactorSettings.totp_allowed || twoFactorSettings.email_allowed)) { setActiveTab('profile'); } else { setActiveTab(hash); } }; window.addEventListener('hashchange', onHashChange); return () => window.removeEventListener('hashchange', onHashChange); }, [twoFactorSettings]); const switchTab = (tab: SettingsTab) => { window.location.hash = tab; setActiveTab(tab); }; const { data: profileData, setData: setProfileData, post: postProfile, processing: profileProcessing, errors: profileErrors } = useForm({ first_name: user.first_name || '', last_name: user.last_name || '', email: user.email || '', phone: (user as any).phone || '', bio: (user as any).bio || '', avatar_file: null as File | null, _method: 'PATCH', }); const [avatarFiles, setAvatarFiles] = useState([]); const handleProfileSubmit = (e: React.SyntheticEvent) => { e.preventDefault(); postProfile(route('profile.update'), { preserveScroll: true, onSuccess: () => swal.success('Saved', 'Profile updated successfully.'), }); }; const { data: passwordData, setData: setPasswordData, put: putPassword, processing: passwordProcessing, errors: passwordErrors, reset: resetPassword } = useForm({ current_password: '', password: '', password_confirmation: '', }); const handlePasswordSubmit = (e: React.SyntheticEvent) => { e.preventDefault(); putPassword(route('password.update'), { preserveScroll: true, onSuccess: () => { resetPassword(); swal.success('Saved', 'Password updated successfully.'); }, }); }; const handleDeleteAccount = async () => { const result = await swal.confirm('Delete account?', 'This action is irreversible.', 'Delete Permanently'); if (result.isConfirmed) { const { value: password } = await Swal.fire({ title: 'Confirm Password', input: 'password', inputPlaceholder: 'Enter your current password', showCancelButton: true, confirmButtonText: 'Delete', confirmButtonColor: '#dc2626', }); if (password) { router.delete(route('profile.destroy'), { data: { password }, preserveScroll: true, onSuccess: () => swal.success('Deleted', 'Account removed.'), onError: (errs) => swal.error('Error', errs.password || 'Incorrect password.'), }); } } }; const initials = `${user.first_name?.charAt(0) || ''}${user.last_name?.charAt(0) || ''}`.toUpperCase(); // 2FA const [copiedSecret, setCopiedSecret] = useState(false); const [showCodes, setShowCodes] = useState(false); const twoFactorForm = useForm({ code: '' }); const copySecret = () => { navigator.clipboard.writeText(twoFactor.secret || ''); setCopiedSecret(true); setTimeout(() => setCopiedSecret(false), 2000); }; const handleEnable2FA = async (e: React.SyntheticEvent) => { e.preventDefault(); if (twoFactor.email_enabled) { const warning = await swal.confirm( 'Deactivate Email 2FA?', 'Enabling Google Authenticator will automatically deactivate Email Two-Factor Authentication. Do you want to proceed?', 'Proceed' ); if (!warning.isConfirmed) return; } twoFactorForm.post(route('two-factor.enable'), { preserveScroll: true, onSuccess: () => { twoFactorForm.reset(); swal.success('Enabled', '2FA is now active on your account.'); }, }); }; const handleDisable2FA = async () => { const { value: password } = await Swal.fire({ title: 'Disable 2FA', text: 'Enter your password to confirm.', input: 'password', inputPlaceholder: 'Your current password', showCancelButton: true, confirmButtonText: 'Disable', confirmButtonColor: '#dc2626', }); if (password) { router.post(route('two-factor.disable'), { password }, { preserveScroll: true, onSuccess: () => swal.success('Disabled', '2FA has been disabled.'), }); } }; const handleRegenerate = async () => { const result = await swal.confirm('Regenerate Codes?', 'Old recovery codes will be invalidated.', 'Regenerate'); if (result.isConfirmed) { router.post(route('two-factor.recovery-codes'), {}, { preserveScroll: true, onSuccess: () => { setShowCodes(true); swal.success('Regenerated', 'New recovery codes generated.'); }, }); } }; const handleToggleEmail2FA = async (enable: boolean) => { if (enable && !twoFactor.smtp_configured) { Swal.fire({ icon: 'warning', title: 'SMTP Mail Server Not Set Up', text: 'Two-Factor Authentication via Email cannot be enabled because the system SMTP configurations are not set up yet. Please configure the SMTP settings under System Settings (or contact your Administrator) first.', confirmButtonColor: '#3D4E4B', }); return; } if (enable && twoFactor.enabled) { const warning = await swal.confirm( 'Switch to Email 2FA?', 'Enabling Email 2FA will automatically deactivate your Google Authenticator setup. Do you want to proceed?', 'Proceed' ); if (!warning.isConfirmed) return; } const { value: password } = await Swal.fire({ title: enable ? 'Enable Email 2FA' : 'Disable Email 2FA', text: 'Enter your password to confirm.', input: 'password', inputPlaceholder: 'Your current password', showCancelButton: true, confirmButtonText: 'Confirm', confirmButtonColor: '#3D4E4B', }); if (password) { router.post(route('two-factor.email.toggle'), { password, enabled: enable }, { preserveScroll: true, onSuccess: () => swal.success('Success', `Email Two-Factor Authentication has been ${enable ? 'enabled' : 'disabled'} successfully.`), onError: (errs) => swal.error('Error', errs.password || 'Incorrect password.'), }); } }; return ( {/* Header Section */}

Account Settings

Manage your personal credentials and security preferences

{/* Tabs Row */}
{(twoFactorSettings.totp_allowed || twoFactorSettings.email_allowed) && ( )}
{/* Content Area */}
{activeTab === 'profile' && (
setProfileData('first_name', e.target.value)} error={profileErrors.first_name} required placeholder="e.g. John" /> setProfileData('last_name', e.target.value)} error={profileErrors.last_name} required placeholder="e.g. Doe" />
setProfileData('email', e.target.value)} error={profileErrors.email} required placeholder="e.g. john.doe@example.com" /> setProfileData('phone', e.target.value)} error={(profileErrors as any).phone} placeholder="e.g. +62 812 3456 7890" />