mirror of
http://172.20.10.11:3000/gitadmin/INSIGHT-MVP.git
synced 2026-06-24 22:46:39 +02:00
Backend: - POST /auth/2fa/setup - generate TOTP secret + QR code (temp Redis storage) - POST /auth/2fa/enable - verify TOTP code and activate 2FA - POST /auth/2fa/disable - deactivate 2FA (requires password) - PATCH /users/me - update own profile (firstName, lastName) - POST /users/me/change-password - change own password Frontend: - New ProfilePage with 3 sections: personal info, password, 2FA - QR code display for Authenticator app setup - Clickable user info in sidebar navigates to /profile - AuthContext extended with twoFactorEnabled + refreshUser Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
206 lines
3.6 KiB
CSS
206 lines
3.6 KiB
CSS
.section {
|
|
background: var(--color-bg-card);
|
|
border-radius: var(--radius-md);
|
|
padding: 1.5rem;
|
|
box-shadow: var(--shadow-sm);
|
|
border: 1px solid var(--color-border);
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.sectionTitle {
|
|
font-size: 1.125rem;
|
|
font-weight: 600;
|
|
margin-bottom: 1.25rem;
|
|
padding-bottom: 0.75rem;
|
|
border-bottom: 1px solid var(--color-border);
|
|
}
|
|
|
|
.form {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.field {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.375rem;
|
|
}
|
|
|
|
.field label {
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
color: var(--color-text);
|
|
}
|
|
|
|
.field input {
|
|
padding: 0.625rem 0.75rem;
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-sm);
|
|
font-size: 0.9375rem;
|
|
transition: border-color 0.15s;
|
|
}
|
|
|
|
.field input:focus {
|
|
outline: none;
|
|
border-color: var(--color-primary);
|
|
box-shadow: 0 0 0 3px var(--color-primary-light);
|
|
}
|
|
|
|
.field input:disabled {
|
|
background: var(--color-bg);
|
|
color: var(--color-text-muted);
|
|
}
|
|
|
|
.field small {
|
|
color: var(--color-text-muted);
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.fieldRow {
|
|
display: flex;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.fieldRow .field {
|
|
flex: 1;
|
|
}
|
|
|
|
.button {
|
|
padding: 0.625rem 1.25rem;
|
|
background: var(--color-primary);
|
|
color: white;
|
|
border: none;
|
|
border-radius: var(--radius-sm);
|
|
font-size: 0.875rem;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: background 0.15s;
|
|
align-self: flex-start;
|
|
}
|
|
|
|
.button:hover:not(:disabled) {
|
|
background: var(--color-primary-hover);
|
|
}
|
|
|
|
.button:disabled {
|
|
opacity: 0.7;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.buttonSecondary {
|
|
padding: 0.625rem 1.25rem;
|
|
background: transparent;
|
|
color: var(--color-text-secondary);
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-sm);
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: all 0.15s;
|
|
}
|
|
|
|
.buttonSecondary:hover {
|
|
background: var(--color-bg);
|
|
}
|
|
|
|
.buttonDanger {
|
|
padding: 0.625rem 1.25rem;
|
|
background: var(--color-error);
|
|
color: white;
|
|
border: none;
|
|
border-radius: var(--radius-sm);
|
|
font-size: 0.875rem;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: background 0.15s;
|
|
align-self: flex-start;
|
|
}
|
|
|
|
.buttonDanger:hover:not(:disabled) {
|
|
background: #b91c1c;
|
|
}
|
|
|
|
.buttonDanger:disabled {
|
|
opacity: 0.7;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.buttonRow {
|
|
display: flex;
|
|
gap: 0.75rem;
|
|
align-items: center;
|
|
}
|
|
|
|
.success {
|
|
background: #f0fdf4;
|
|
color: var(--color-success);
|
|
padding: 0.75rem;
|
|
border-radius: var(--radius-sm);
|
|
font-size: 0.875rem;
|
|
border: 1px solid #bbf7d0;
|
|
}
|
|
|
|
.error {
|
|
background: #fef2f2;
|
|
color: var(--color-error);
|
|
padding: 0.75rem;
|
|
border-radius: var(--radius-sm);
|
|
font-size: 0.875rem;
|
|
border: 1px solid #fecaca;
|
|
}
|
|
|
|
.tfaStatus {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0.75rem 0;
|
|
font-size: 0.9375rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.tfaSetup {
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.tfaInstructions {
|
|
color: var(--color-text-secondary);
|
|
font-size: 0.875rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.qrContainer {
|
|
display: flex;
|
|
justify-content: center;
|
|
padding: 1.5rem;
|
|
background: white;
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-md);
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.qrContainer img {
|
|
width: 200px;
|
|
height: 200px;
|
|
}
|
|
|
|
.manualSecret {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.375rem;
|
|
margin-bottom: 1.25rem;
|
|
}
|
|
|
|
.manualSecret label {
|
|
font-size: 0.8125rem;
|
|
color: var(--color-text-muted);
|
|
}
|
|
|
|
.manualSecret code {
|
|
background: var(--color-bg);
|
|
padding: 0.5rem 0.75rem;
|
|
border-radius: var(--radius-sm);
|
|
font-size: 0.875rem;
|
|
letter-spacing: 2px;
|
|
word-break: break-all;
|
|
border: 1px solid var(--color-border);
|
|
}
|