- SecuritySettings: row bg-white/5 → bg-muted/30 - Toast: close button hover:bg-black/10 → hover:bg-muted/50 - QueueView: autoplay toggle thumb bg-white → bg-background - RolesPage: cards/headers border-white/5, bg-black/40 → border-border, bg-card/80; headings text-white → text-foreground; row hover, inputs, badge → semantic - SettingsPage: wrapper and tabs border/bg → border-border, bg-card/80, bg-muted/20; section cards; System Config title text-foreground Co-authored-by: Cursor <cursoragent@cursor.com>
167 lines
6 KiB
TypeScript
167 lines
6 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { Card } from '../../ui/card';
|
|
import { Button } from '../../ui/button';
|
|
import { Input } from '../../ui/input';
|
|
import { Lock, Key, Plus, AlertCircle, CheckCircle } from 'lucide-react';
|
|
import { useToast } from '../../../components/feedback/ToastProvider';
|
|
import { PasswordStrengthIndicator } from '@/features/auth/components/PasswordStrengthIndicator';
|
|
import { TwoFactorSetup } from './TwoFactorSetup';
|
|
import { PasskeyModal } from './PasskeyModal';
|
|
import { SessionManagement } from './SessionManagement';
|
|
import { LoginHistory } from './LoginHistory';
|
|
|
|
export const SecuritySettings: React.FC = () => {
|
|
const { addToast } = useToast();
|
|
const [view, setView] = useState<'main' | '2fa' | 'sessions' | 'history'>(
|
|
'main',
|
|
);
|
|
|
|
// 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) {
|
|
addToast('Passwords do not match', 'error');
|
|
return;
|
|
}
|
|
addToast('Password successfully updated', 'success');
|
|
setCurrentPassword('');
|
|
setNewPassword('');
|
|
setConfirmPassword('');
|
|
};
|
|
|
|
if (view === '2fa') {
|
|
return (
|
|
<TwoFactorSetup
|
|
onBack={() => setView('main')}
|
|
onComplete={() => {
|
|
setIs2FAEnabled(true);
|
|
setView('main');
|
|
}}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-8 animate-fadeIn pb-10">
|
|
{/* 1. PASSWORD CHANGE */}
|
|
<Card variant="default">
|
|
<h3 className="text-xl font-bold text-foreground mb-6 flex items-center gap-2">
|
|
<Lock className="w-5 h-5 text-muted-foreground" /> Password & Authentication
|
|
</h3>
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
<div className="space-y-4">
|
|
<Input
|
|
type="password"
|
|
label="Current Password"
|
|
value={currentPassword}
|
|
onChange={(e) => setCurrentPassword(e.target.value)}
|
|
/>
|
|
<div className="relative">
|
|
<Input
|
|
type="password"
|
|
label="New Password"
|
|
value={newPassword}
|
|
onChange={(e) => setNewPassword(e.target.value)}
|
|
/>
|
|
{newPassword && (
|
|
<PasswordStrengthIndicator password={newPassword} />
|
|
)}
|
|
</div>
|
|
<Input
|
|
type="password"
|
|
label="Confirm New Password"
|
|
value={confirmPassword}
|
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
/>
|
|
<div className="pt-2 flex justify-end">
|
|
<Button
|
|
variant="primary"
|
|
onClick={handlePasswordUpdate}
|
|
disabled={!currentPassword || !newPassword}
|
|
>
|
|
Update Password
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-6">
|
|
{/* 2FA Summary */}
|
|
<div className="bg-card p-6 rounded-xl border border-border">
|
|
<div className="flex justify-between items-start mb-4">
|
|
<div>
|
|
<h4 className="font-bold text-foreground text-sm">
|
|
Two-Factor Authentication
|
|
</h4>
|
|
<p className="text-xs text-muted-foreground mt-1">
|
|
Add an extra layer of security to your account.
|
|
</p>
|
|
</div>
|
|
{is2FAEnabled ? (
|
|
<span className="text-success flex items-center gap-1 text-xs font-bold border border-success/30 px-2 py-1 rounded bg-success/5">
|
|
<CheckCircle className="w-3 h-3" /> ENABLED
|
|
</span>
|
|
) : (
|
|
<span className="text-destructive flex items-center gap-1 text-xs font-bold border border-destructive/30 px-2 py-1 rounded bg-destructive/5">
|
|
<AlertCircle className="w-3 h-3" /> DISABLED
|
|
</span>
|
|
)}
|
|
</div>
|
|
<Button
|
|
variant="secondary"
|
|
className="w-full"
|
|
onClick={() => setView('2fa')}
|
|
>
|
|
{is2FAEnabled ? 'Manage 2FA Settings' : 'Enable 2FA'}
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Passkeys */}
|
|
<div className="bg-card p-6 rounded-xl border border-border">
|
|
<div className="flex justify-between items-start mb-4">
|
|
<div>
|
|
<h4 className="font-bold text-foreground text-sm flex items-center gap-2">
|
|
<Key className="w-4 h-4 text-warning" /> Passkeys
|
|
</h4>
|
|
<p className="text-xs text-muted-foreground mt-1">
|
|
Sign in with FaceID, TouchID, or device PIN.
|
|
</p>
|
|
</div>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={() => setShowPasskeyModal(true)}
|
|
>
|
|
<Plus className="w-4 h-4" />
|
|
</Button>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<div className="flex justify-between items-center text-sm p-2 rounded bg-muted/30">
|
|
<span className="text-foreground">MacBook Pro (Chrome)</span>
|
|
<span className="text-xs text-muted-foreground">Added 2d ago</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
|
|
{/* 2. SESSIONS & HISTORY */}
|
|
<SessionManagement />
|
|
<LoginHistory />
|
|
|
|
{/* MODALS */}
|
|
{showPasskeyModal && (
|
|
<PasskeyModal
|
|
onClose={() => setShowPasskeyModal(false)}
|
|
onSuccess={() => addToast('Passkey registered', 'success')}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|