Ajout de la possibilité de voir le mot de passe
This commit is contained in:
@@ -0,0 +1,48 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Champ mot de passe avec bouton "afficher/masquer".
|
||||||
|
* Utilisation : remplacer <input type="password" .../> par <PasswordInput .../>
|
||||||
|
* Toutes les props (className, value, onChange, required, autoComplete, style, etc.)
|
||||||
|
* sont transmises telles quelles à l'<input> sous-jacent.
|
||||||
|
*/
|
||||||
|
function EyeIcon({ open }) {
|
||||||
|
return open ? (
|
||||||
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none">
|
||||||
|
<path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z" stroke="var(--text-muted)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
||||||
|
<circle cx="12" cy="12" r="3" stroke="var(--text-muted)" strokeWidth="2"/>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none">
|
||||||
|
<path d="M17.94 17.94A10.94 10.94 0 0112 19c-7 0-11-7-11-7a20.6 20.6 0 015.06-5.94M9.9 4.24A10.9 10.9 0 0112 4c7 0 11 7 11 7a20.6 20.6 0 01-2.66 3.78M14.12 14.12a3 3 0 11-4.24-4.24" stroke="var(--text-muted)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
||||||
|
<line x1="1" y1="1" x2="23" y2="23" stroke="var(--text-muted)" strokeWidth="2" strokeLinecap="round"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PasswordInput({ style, wrapperStyle, ...props }) {
|
||||||
|
const [show, setShow] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ position: 'relative', ...wrapperStyle }}>
|
||||||
|
<input
|
||||||
|
{...props}
|
||||||
|
type={show ? 'text' : 'password'}
|
||||||
|
style={{ ...style, paddingRight: 40 }}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
tabIndex={-1}
|
||||||
|
onClick={() => setShow(v => !v)}
|
||||||
|
aria-label={show ? 'Masquer le mot de passe' : 'Afficher le mot de passe'}
|
||||||
|
style={{
|
||||||
|
position: 'absolute', right: 10, top: '50%', transform: 'translateY(-50%)',
|
||||||
|
background: 'none', border: 'none', padding: 4, display: 'flex',
|
||||||
|
alignItems: 'center', justifyContent: 'center', cursor: 'pointer', lineHeight: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<EyeIcon open={show} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import PasswordStrength from '../components/PasswordStrength.jsx';
|
import PasswordStrength from '../components/PasswordStrength.jsx';
|
||||||
|
import PasswordInput from '../components/PasswordInput.jsx';
|
||||||
import { useParams, useNavigate, Link } from 'react-router-dom';
|
import { useParams, useNavigate, Link } from 'react-router-dom';
|
||||||
|
|
||||||
// Wrap défini en dehors du composant pour éviter le remontage à chaque frappe
|
// Wrap défini en dehors du composant pour éviter le remontage à chaque frappe
|
||||||
@@ -173,13 +174,13 @@ export default function InvitationRegister() {
|
|||||||
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
||||||
<label style={{ fontSize: 14, fontWeight: 500, color: 'var(--text)' }}>Mot de passe <span style={{ color: 'var(--text-muted)', fontWeight: 400 }}>(8 car. min.)</span></label>
|
<label style={{ fontSize: 14, fontWeight: 500, color: 'var(--text)' }}>Mot de passe <span style={{ color: 'var(--text-muted)', fontWeight: 400 }}>(8 car. min.)</span></label>
|
||||||
<input className="form-input" type="password" required minLength={appInfo.minPasswordLength || 8} autoComplete="new-password" placeholder="••••••••" value={form.password} onChange={e => set('password', e.target.value)} style={{ width: '100%' }} />
|
<PasswordInput className="form-input" required minLength={appInfo.minPasswordLength || 8} autoComplete="new-password" placeholder="••••••••" value={form.password} onChange={e => set('password', e.target.value)} style={{ width: '100%' }} />
|
||||||
<PasswordStrength password={form.password} minLength={appInfo.minPasswordLength || 8} />
|
<PasswordStrength password={form.password} minLength={appInfo.minPasswordLength || 8} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
||||||
<label style={{ fontSize: 14, fontWeight: 500, color: 'var(--text)' }}>Confirmer le mot de passe</label>
|
<label style={{ fontSize: 14, fontWeight: 500, color: 'var(--text)' }}>Confirmer le mot de passe</label>
|
||||||
<input className="form-input" type="password" required minLength={appInfo.minPasswordLength || 8} autoComplete="new-password" placeholder="••••••••" value={form.confirm} onChange={e => set('confirm', e.target.value)} style={{ width: '100%' }} />
|
<PasswordInput className="form-input" required minLength={appInfo.minPasswordLength || 8} autoComplete="new-password" placeholder="••••••••" value={form.confirm} onChange={e => set('confirm', e.target.value)} style={{ width: '100%' }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="submit" disabled={busy} style={{ marginTop: 4, width: '100%', padding: '11px 0', background: busy ? 'var(--text-muted)' : 'var(--primary, #1e40af)', color: '#fff', border: 'none', borderRadius: 8, fontSize: 15, fontWeight: 600, cursor: busy ? 'not-allowed' : 'pointer', transition: 'background 0.15s' }}>
|
<button type="submit" disabled={busy} style={{ marginTop: 4, width: '100%', padding: '11px 0', background: busy ? 'var(--text-muted)' : 'var(--primary, #1e40af)', color: '#fff', border: 'none', borderRadius: 8, fontSize: 15, fontWeight: 600, cursor: busy ? 'not-allowed' : 'pointer', transition: 'background 0.15s' }}>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Link, useNavigate } from 'react-router-dom';
|
|||||||
import { useAuth } from '../context/AuthContext.jsx';
|
import { useAuth } from '../context/AuthContext.jsx';
|
||||||
import { api } from '../api.js';
|
import { api } from '../api.js';
|
||||||
import AuthBgCol from '../components/AuthBgCol.jsx';
|
import AuthBgCol from '../components/AuthBgCol.jsx';
|
||||||
|
import PasswordInput from '../components/PasswordInput.jsx';
|
||||||
|
|
||||||
// ── Helpers ────────────────────────────────────────────────────────────────
|
// ── Helpers ────────────────────────────────────────────────────────────────
|
||||||
const DEVICE_KEY = 'cl_device_token';
|
const DEVICE_KEY = 'cl_device_token';
|
||||||
@@ -217,7 +218,7 @@ export default function Login() {
|
|||||||
<label style={{ fontSize: 14, fontWeight: 500, color: 'var(--text)' }}>Mot de passe</label>
|
<label style={{ fontSize: 14, fontWeight: 500, color: 'var(--text)' }}>Mot de passe</label>
|
||||||
<Link to="/forgot-password" style={{ fontSize: 13, color: 'var(--text-muted)', textDecoration: 'underline' }}>Mot de passe oublié ?</Link>
|
<Link to="/forgot-password" style={{ fontSize: 13, color: 'var(--text-muted)', textDecoration: 'underline' }}>Mot de passe oublié ?</Link>
|
||||||
</div>
|
</div>
|
||||||
<input className="form-input" type="password" required autoComplete="current-password" placeholder="••••••••"
|
<PasswordInput className="form-input" required autoComplete="current-password" placeholder="••••••••"
|
||||||
value={password} onChange={e => setPassword(e.target.value)} style={{ width: '100%' }} />
|
value={password} onChange={e => setPassword(e.target.value)} style={{ width: '100%' }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import PasswordStrength from '../components/PasswordStrength.jsx';
|
import PasswordStrength from '../components/PasswordStrength.jsx';
|
||||||
|
import PasswordInput from '../components/PasswordInput.jsx';
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
import { useAuth } from '../context/AuthContext.jsx';
|
import { useAuth } from '../context/AuthContext.jsx';
|
||||||
import { useUi } from '../context/UiContext.jsx';
|
import { useUi } from '../context/UiContext.jsx';
|
||||||
@@ -143,7 +144,7 @@ function EmailChangeForm({ onDone }) {
|
|||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
||||||
<label className="profile-label">Mot de passe actuel (confirmation)</label>
|
<label className="profile-label">Mot de passe actuel (confirmation)</label>
|
||||||
<input className="profile-input" type="password" required value={pwd} onChange={e => setPwd(e.target.value)} placeholder="••••••••" />
|
<PasswordInput className="profile-input" required value={pwd} onChange={e => setPwd(e.target.value)} placeholder="••••••••" />
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', gap: 8 }}>
|
<div style={{ display: 'flex', gap: 8 }}>
|
||||||
<button type="submit" className="btn btn-primary" disabled={busy} style={{ fontSize: 13 }}>
|
<button type="submit" className="btn btn-primary" disabled={busy} style={{ fontSize: 13 }}>
|
||||||
@@ -389,7 +390,7 @@ function TwoFASection({ user }) {
|
|||||||
<div style={{ display: 'flex', gap: 10, alignItems: 'flex-end' }}>
|
<div style={{ display: 'flex', gap: 10, alignItems: 'flex-end' }}>
|
||||||
<div style={{ flex: 1 }}>
|
<div style={{ flex: 1 }}>
|
||||||
<label style={{ display: 'block', marginBottom: 4, fontSize: 13, fontWeight: 500 }}>Mot de passe actuel</label>
|
<label style={{ display: 'block', marginBottom: 4, fontSize: 13, fontWeight: 500 }}>Mot de passe actuel</label>
|
||||||
<input type="password" required value={disablePwd}
|
<PasswordInput required value={disablePwd}
|
||||||
onChange={e => setDisablePwd(e.target.value)}
|
onChange={e => setDisablePwd(e.target.value)}
|
||||||
autoComplete="current-password" placeholder="••••••••" />
|
autoComplete="current-password" placeholder="••••••••" />
|
||||||
</div>
|
</div>
|
||||||
@@ -469,20 +470,20 @@ function SecurityForm() {
|
|||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12, maxWidth: 400 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 12, maxWidth: 400 }}>
|
||||||
<div>
|
<div>
|
||||||
<label>Mot de passe actuel</label>
|
<label>Mot de passe actuel</label>
|
||||||
<input type="password" required autoComplete="current-password"
|
<PasswordInput required autoComplete="current-password"
|
||||||
value={pwdForm.currentPassword}
|
value={pwdForm.currentPassword}
|
||||||
onChange={e => setPwdForm({ ...pwdForm, currentPassword: e.target.value })} />
|
onChange={e => setPwdForm({ ...pwdForm, currentPassword: e.target.value })} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>Nouveau mot de passe</label>
|
<label>Nouveau mot de passe</label>
|
||||||
<input type="password" required autoComplete="new-password"
|
<PasswordInput required autoComplete="new-password"
|
||||||
value={pwdForm.newPassword}
|
value={pwdForm.newPassword}
|
||||||
onChange={e => setPwdForm({ ...pwdForm, newPassword: e.target.value })} />
|
onChange={e => setPwdForm({ ...pwdForm, newPassword: e.target.value })} />
|
||||||
<PasswordStrength password={pwdForm.newPassword} minLength={minPasswordLength} />
|
<PasswordStrength password={pwdForm.newPassword} minLength={minPasswordLength} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>Confirmer le nouveau mot de passe</label>
|
<label>Confirmer le nouveau mot de passe</label>
|
||||||
<input type="password" required autoComplete="new-password"
|
<PasswordInput required autoComplete="new-password"
|
||||||
value={pwdForm.confirm}
|
value={pwdForm.confirm}
|
||||||
onChange={e => setPwdForm({ ...pwdForm, confirm: e.target.value })} />
|
onChange={e => setPwdForm({ ...pwdForm, confirm: e.target.value })} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Link, useNavigate } from 'react-router-dom';
|
|||||||
import { useAuth } from '../context/AuthContext.jsx';
|
import { useAuth } from '../context/AuthContext.jsx';
|
||||||
import PasswordStrength from '../components/PasswordStrength.jsx';
|
import PasswordStrength from '../components/PasswordStrength.jsx';
|
||||||
import AuthBgCol from '../components/AuthBgCol.jsx';
|
import AuthBgCol from '../components/AuthBgCol.jsx';
|
||||||
|
import PasswordInput from '../components/PasswordInput.jsx';
|
||||||
|
|
||||||
export default function Register() {
|
export default function Register() {
|
||||||
const { register } = useAuth();
|
const { register } = useAuth();
|
||||||
@@ -178,9 +179,9 @@ export default function Register() {
|
|||||||
<label style={{ fontSize: 14, fontWeight: 500, color: 'var(--text)' }}>
|
<label style={{ fontSize: 14, fontWeight: 500, color: 'var(--text)' }}>
|
||||||
Mot de passe
|
Mot de passe
|
||||||
</label>
|
</label>
|
||||||
<input
|
<PasswordInput
|
||||||
className="form-input"
|
className="form-input"
|
||||||
type="password" required
|
required
|
||||||
minLength={appInfo.minPasswordLength || 8}
|
minLength={appInfo.minPasswordLength || 8}
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
placeholder="••••••••"
|
placeholder="••••••••"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
|
|||||||
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
|
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
import PasswordStrength from '../components/PasswordStrength.jsx';
|
import PasswordStrength from '../components/PasswordStrength.jsx';
|
||||||
import AuthBgCol from '../components/AuthBgCol.jsx';
|
import AuthBgCol from '../components/AuthBgCol.jsx';
|
||||||
|
import PasswordInput from '../components/PasswordInput.jsx';
|
||||||
|
|
||||||
export default function ResetPassword() {
|
export default function ResetPassword() {
|
||||||
const [params] = useSearchParams();
|
const [params] = useSearchParams();
|
||||||
@@ -154,9 +155,9 @@ export default function ResetPassword() {
|
|||||||
<label style={{ fontSize: 14, fontWeight: 500, color: 'var(--text)' }}>
|
<label style={{ fontSize: 14, fontWeight: 500, color: 'var(--text)' }}>
|
||||||
Nouveau mot de passe
|
Nouveau mot de passe
|
||||||
</label>
|
</label>
|
||||||
<input
|
<PasswordInput
|
||||||
className="form-input"
|
className="form-input"
|
||||||
type="password" required minLength={appInfo.minPasswordLength || 8}
|
required minLength={appInfo.minPasswordLength || 8}
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
placeholder="••••••••"
|
placeholder="••••••••"
|
||||||
value={password}
|
value={password}
|
||||||
@@ -170,9 +171,9 @@ export default function ResetPassword() {
|
|||||||
<label style={{ fontSize: 14, fontWeight: 500, color: 'var(--text)' }}>
|
<label style={{ fontSize: 14, fontWeight: 500, color: 'var(--text)' }}>
|
||||||
Confirmer le mot de passe
|
Confirmer le mot de passe
|
||||||
</label>
|
</label>
|
||||||
<input
|
<PasswordInput
|
||||||
className="form-input"
|
className="form-input"
|
||||||
type="password" required minLength={appInfo.minPasswordLength || 8}
|
required minLength={appInfo.minPasswordLength || 8}
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
placeholder="••••••••"
|
placeholder="••••••••"
|
||||||
value={password2}
|
value={password2}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import ConfirmModal from '../../components/ConfirmModal.jsx';
|
|||||||
import Modal from '../../components/Modal.jsx';
|
import Modal from '../../components/Modal.jsx';
|
||||||
import ResultBanner from '../../components/ResultBanner.jsx';
|
import ResultBanner from '../../components/ResultBanner.jsx';
|
||||||
import PasswordStrength from '../../components/PasswordStrength.jsx';
|
import PasswordStrength from '../../components/PasswordStrength.jsx';
|
||||||
|
import PasswordInput from '../../components/PasswordInput.jsx';
|
||||||
import { fmt, Badge, UserStatusBadge } from './adminHelpers.jsx';
|
import { fmt, Badge, UserStatusBadge } from './adminHelpers.jsx';
|
||||||
|
|
||||||
// ── Helpers ────────────────────────────────────────────────────────────────
|
// ── Helpers ────────────────────────────────────────────────────────────────
|
||||||
@@ -363,7 +364,7 @@ function CreateUserModal({ open, onClose, onCreated }) {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>Mot de passe *</label>
|
<label>Mot de passe *</label>
|
||||||
<input type="password" required minLength={minPasswordLength} autoComplete="new-password" value={form.password} onChange={e => set('password', e.target.value)} placeholder={`${minPasswordLength} caractères minimum`} />
|
<PasswordInput required minLength={minPasswordLength} autoComplete="new-password" value={form.password} onChange={e => set('password', e.target.value)} placeholder={`${minPasswordLength} caractères minimum`} />
|
||||||
<PasswordStrength password={form.password} minLength={minPasswordLength} />
|
<PasswordStrength password={form.password} minLength={minPasswordLength} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user