admin / Synapse-Cortex
publicSelf Hosted ITSM Tool with RBAC/Tenanting and MFA
Synapse-Cortex / Synapse-Cortexv2 / frontend / src / pages / SettingsProfile.tsx
5568 B · main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | import { useState, type FormEvent } from 'react' import { Link } from 'react-router-dom' import { settingsApi } from '../api/settings' import { ApiError } from '../api/client' import { useAuth } from '../auth/AuthContext' import { usePageHeader } from '../layout/PageHeaderContext' export function SettingsProfile() { usePageHeader('Settings // Profile') const { user } = useAuth() const [currentPassword, setCurrentPassword] = useState('') const [newPassword, setNewPassword] = useState('') const [error, setError] = useState<string | null>(null) const [success, setSuccess] = useState<string | null>(null) const [submitting, setSubmitting] = useState(false) const onSubmit = async (e: FormEvent) => { e.preventDefault() setError(null) setSuccess(null) setSubmitting(true) try { await settingsApi.changePassword(currentPassword, newPassword) setSuccess('Password updated.') setCurrentPassword('') setNewPassword('') } catch (err) { setError(err instanceof ApiError ? err.message : 'Failed to change password.') } finally { setSubmitting(false) } } if (!user) return null return ( <div className="max-w-3xl grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="border border-cortex-border bg-cortex-panel rounded-md p-5 space-y-2 text-sm"> <div className="text-[10px] text-cortex-muted uppercase tracking-wider font-display mb-2">Profile</div> <div className="flex justify-between"> <span className="text-cortex-muted">Name</span> <span className="text-chrome">{user.full_name}</span> </div> <div className="flex justify-between"> <span className="text-cortex-muted">Email</span> <span className="text-chrome">{user.email}</span> </div> <div className="flex justify-between"> <span className="text-cortex-muted">Role</span> <span className="text-chrome uppercase">{user.role.replace('_', ' ')}</span> </div> <div className="flex justify-between"> <span className="text-cortex-muted">Tenant</span> <span className="text-chrome">{user.tenant_name}</span> </div> </div> <div className="border border-cortex-border bg-cortex-panel rounded-md p-5 text-sm"> <div className="text-[10px] text-cortex-muted uppercase tracking-wider font-display mb-2">Multi-Factor Auth</div> {user.mfa_enabled ? ( <> <div className="flex items-center gap-2 text-accent-green text-xs mb-2"> <span className="h-1.5 w-1.5 rounded-full bg-accent-green" /> Enrolled </div> <Link to="/settings/mfa/disable" className="text-xs text-accent-red hover:underline"> Disable MFA </Link> </> ) : ( <> <div className={`flex items-center gap-2 text-xs mb-2 ${user.mfa_required ? 'text-accent-orange' : 'text-cortex-muted'}`}> <span className={`h-1.5 w-1.5 rounded-full ${user.mfa_required ? 'bg-accent-orange' : 'bg-cortex-muted'}`} /> {user.mfa_required ? 'Required, not yet enrolled' : 'Not enrolled'} </div> <Link to="/settings/mfa/enroll" className="inline-block bg-accent-blue text-cortex-bg font-display font-bold uppercase tracking-wider text-xs px-3 py-1.5 rounded hover:bg-accent-blue-bright transition" > Enable MFA </Link> </> )} </div> <div className="border border-cortex-border bg-cortex-panel rounded-md p-5 md:col-span-2"> <div className="text-[10px] text-cortex-muted uppercase tracking-wider font-display mb-3">Change Password</div> {error && ( <div className="mb-3 text-xs border border-accent-red/40 bg-accent-red/10 text-accent-red px-3 py-2 rounded">{error}</div> )} {success && ( <div className="mb-3 text-xs border border-accent-green/40 bg-accent-green/10 text-accent-green px-3 py-2 rounded"> {success} </div> )} <form onSubmit={onSubmit} className="space-y-3 text-sm max-w-md"> <div> <label className="block text-xs text-cortex-muted mb-1 uppercase tracking-wider">Current Password</label> <input required type="password" value={currentPassword} onChange={(e) => setCurrentPassword(e.target.value)} className="w-full bg-cortex-panel2 border border-cortex-border rounded px-3 py-2 focus:outline-none focus:border-accent-blue" /> </div> <div> <label className="block text-xs text-cortex-muted mb-1 uppercase tracking-wider">New Password</label> <input required type="password" minLength={8} value={newPassword} onChange={(e) => setNewPassword(e.target.value)} className="w-full bg-cortex-panel2 border border-cortex-border rounded px-3 py-2 focus:outline-none focus:border-accent-blue" /> </div> <button type="submit" disabled={submitting} className="bg-accent-blue text-cortex-bg font-display font-bold uppercase tracking-wider text-xs px-4 py-2 rounded hover:bg-accent-blue-bright transition disabled:opacity-50" > Update Password </button> </form> </div> </div> ) } |