2026-01-07 09:31:02 +00:00
|
|
|
import React, { useState } from 'react';
|
|
|
|
|
import { Card } from '../../ui/card';
|
|
|
|
|
import { Button } from '../../ui/button';
|
|
|
|
|
import { Input } from '../../ui/input';
|
2026-01-07 18:39:21 +00:00
|
|
|
import { Lock, Key, Plus, AlertCircle, CheckCircle } from 'lucide-react';
|
2026-01-07 09:31:02 +00:00
|
|
|
import { useToast } from '../../../context/ToastContext';
|
2026-01-07 18:39:21 +00:00
|
|
|
import { PasswordStrengthIndicator } from '@/features/auth/components/PasswordStrengthIndicator';
|
2026-01-07 09:31:02 +00:00
|
|
|
import { TwoFactorSetup } from './TwoFactorSetup';
|
|
|
|
|
import { PasskeyModal } from './PasskeyModal';
|
|
|
|
|
import { SessionManagement } from './SessionManagement';
|
|
|
|
|
import { LoginHistory } from './LoginHistory';
|
|
|
|
|
|
|
|
|
|
export const SecuritySettings: React.FC = () => {
|
|
|
|
|
const { addToast } = useToast();
|
2026-01-13 18:47:57 +00:00
|
|
|
const [view, setView] = useState<'main' | '2fa' | 'sessions' | 'history'>(
|
|
|
|
|
'main',
|
|
|
|
|
);
|
2026-01-07 18:39:21 +00:00
|
|
|
|
2026-01-07 09:31:02 +00:00
|
|
|
// Forms & Modals
|
|
|
|
|
const [currentPassword, setCurrentPassword] = useState('');
|
|
|
|
|
const [newPassword, setNewPassword] = useState('');
|
|
|
|
|
const [confirmPassword, setConfirmPassword] = useState('');
|
|
|
|
|
const [showPasskeyModal, setShowPasskeyModal] = useState(false);
|
|
|
|
|
const [is2FAEnabled, setIs2FAEnabled] = useState(false);
|
|
|
|
|
|
|
|
|
|
const handlePasswordUpdate = () => {
|
|
|
|
|
if (newPassword !== confirmPassword) {
|
2026-01-13 18:47:57 +00:00
|
|
|
addToast('Passwords do not match', 'error');
|
2026-01-07 09:31:02 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2026-01-13 18:47:57 +00:00
|
|
|
addToast('Password successfully updated', 'success');
|
2026-01-07 09:31:02 +00:00
|
|
|
setCurrentPassword('');
|
|
|
|
|
setNewPassword('');
|
|
|
|
|
setConfirmPassword('');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (view === '2fa') {
|
2026-01-13 18:47:57 +00:00
|
|
|
return (
|
|
|
|
|
<TwoFactorSetup
|
|
|
|
|
onBack={() => setView('main')}
|
|
|
|
|
onComplete={() => {
|
|
|
|
|
setIs2FAEnabled(true);
|
|
|
|
|
setView('main');
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
);
|
2026-01-07 09:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="space-y-8 animate-fadeIn pb-10">
|
|
|
|
|
{/* 1. PASSWORD CHANGE */}
|
|
|
|
|
<Card variant="default">
|
|
|
|
|
<h3 className="text-xl font-bold text-white mb-6 flex items-center gap-2">
|
|
|
|
|
<Lock className="w-5 h-5 text-kodo-cyan" /> Password & Authentication
|
|
|
|
|
</h3>
|
2026-01-07 18:39:21 +00:00
|
|
|
|
2026-01-07 09:31:02 +00:00
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
|
|
|
<div className="space-y-4">
|
2026-01-07 18:39:21 +00:00
|
|
|
<Input
|
|
|
|
|
type="password"
|
|
|
|
|
label="Current Password"
|
|
|
|
|
value={currentPassword}
|
|
|
|
|
onChange={(e) => setCurrentPassword(e.target.value)}
|
2026-01-07 09:31:02 +00:00
|
|
|
/>
|
|
|
|
|
<div className="relative">
|
2026-01-07 18:39:21 +00:00
|
|
|
<Input
|
|
|
|
|
type="password"
|
|
|
|
|
label="New Password"
|
|
|
|
|
value={newPassword}
|
|
|
|
|
onChange={(e) => setNewPassword(e.target.value)}
|
2026-01-07 09:31:02 +00:00
|
|
|
/>
|
2026-01-13 18:47:57 +00:00
|
|
|
{newPassword && (
|
|
|
|
|
<PasswordStrengthIndicator password={newPassword} />
|
|
|
|
|
)}
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
2026-01-07 18:39:21 +00:00
|
|
|
<Input
|
|
|
|
|
type="password"
|
|
|
|
|
label="Confirm New Password"
|
|
|
|
|
value={confirmPassword}
|
|
|
|
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
2026-01-07 09:31:02 +00:00
|
|
|
/>
|
|
|
|
|
<div className="pt-2 flex justify-end">
|
2026-01-13 18:47:57 +00:00
|
|
|
<Button
|
|
|
|
|
variant="primary"
|
|
|
|
|
onClick={handlePasswordUpdate}
|
|
|
|
|
disabled={!currentPassword || !newPassword}
|
|
|
|
|
>
|
2026-01-07 09:31:02 +00:00
|
|
|
Update Password
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
{/* 2FA Summary */}
|
|
|
|
|
<div className="bg-kodo-ink p-6 rounded-xl border border-kodo-steel">
|
|
|
|
|
<div className="flex justify-between items-start mb-4">
|
|
|
|
|
<div>
|
2026-01-13 18:47:57 +00:00
|
|
|
<h4 className="font-bold text-white text-sm">
|
|
|
|
|
Two-Factor Authentication
|
|
|
|
|
</h4>
|
|
|
|
|
<p className="text-xs text-gray-400 mt-1">
|
|
|
|
|
Add an extra layer of security to your account.
|
|
|
|
|
</p>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
|
|
|
|
{is2FAEnabled ? (
|
2026-01-13 18:47:57 +00:00
|
|
|
<span className="text-kodo-lime flex items-center gap-1 text-xs font-bold border border-kodo-lime/30 px-2 py-1 rounded bg-kodo-lime/5">
|
|
|
|
|
<CheckCircle className="w-3 h-3" /> ENABLED
|
|
|
|
|
</span>
|
2026-01-07 09:31:02 +00:00
|
|
|
) : (
|
2026-01-13 18:47:57 +00:00
|
|
|
<span className="text-kodo-red flex items-center gap-1 text-xs font-bold border border-kodo-red/30 px-2 py-1 rounded bg-kodo-red/5">
|
|
|
|
|
<AlertCircle className="w-3 h-3" /> DISABLED
|
|
|
|
|
</span>
|
2026-01-07 09:31:02 +00:00
|
|
|
)}
|
|
|
|
|
</div>
|
2026-01-07 18:39:21 +00:00
|
|
|
<Button
|
|
|
|
|
variant="secondary"
|
|
|
|
|
className="w-full"
|
2026-01-07 09:31:02 +00:00
|
|
|
onClick={() => setView('2fa')}
|
|
|
|
|
>
|
|
|
|
|
{is2FAEnabled ? 'Manage 2FA Settings' : 'Enable 2FA'}
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Passkeys */}
|
|
|
|
|
<div className="bg-kodo-ink p-6 rounded-xl border border-kodo-steel">
|
|
|
|
|
<div className="flex justify-between items-start mb-4">
|
|
|
|
|
<div>
|
|
|
|
|
<h4 className="font-bold text-white text-sm flex items-center gap-2">
|
|
|
|
|
<Key className="w-4 h-4 text-kodo-gold" /> Passkeys
|
|
|
|
|
</h4>
|
2026-01-13 18:47:57 +00:00
|
|
|
<p className="text-xs text-gray-400 mt-1">
|
|
|
|
|
Sign in with FaceID, TouchID, or device PIN.
|
|
|
|
|
</p>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
2026-01-13 18:47:57 +00:00
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="icon"
|
|
|
|
|
onClick={() => setShowPasskeyModal(true)}
|
|
|
|
|
>
|
|
|
|
|
<Plus className="w-4 h-4" />
|
|
|
|
|
</Button>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
<div className="flex justify-between items-center text-sm p-2 rounded bg-white/5">
|
|
|
|
|
<span className="text-gray-300">MacBook Pro (Chrome)</span>
|
|
|
|
|
<span className="text-xs text-gray-500">Added 2d ago</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
{/* 2. SESSIONS & HISTORY */}
|
|
|
|
|
<SessionManagement />
|
|
|
|
|
<LoginHistory />
|
|
|
|
|
|
|
|
|
|
{/* MODALS */}
|
|
|
|
|
{showPasskeyModal && (
|
2026-01-07 18:39:21 +00:00
|
|
|
<PasskeyModal
|
|
|
|
|
onClose={() => setShowPasskeyModal(false)}
|
2026-01-13 18:47:57 +00:00
|
|
|
onSuccess={() => addToast('Passkey registered', 'success')}
|
2026-01-07 09:31:02 +00:00
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2026-01-13 18:47:57 +00:00
|
|
|
};
|