Vendored
-1510
File diff suppressed because one or more lines are too long
Vendored
+1510
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Omnex — Plateforme de gestion de commandes & livraison</title>
|
||||
<meta name="description" content="Déployez en un clic une démo complète de la plateforme de gestion de commandes et de livraison." />
|
||||
<script type="module" crossorigin src="/assets/index-CbXT_Xos.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-DVA6g9E7.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -29,11 +29,10 @@ import {
|
||||
useToast,
|
||||
} from '@chakra-ui/react'
|
||||
import { CheckIcon, CloseIcon, EditIcon } from '@chakra-ui/icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { api, ApiError } from '../../lib/api'
|
||||
import { useAuth } from '../../lib/auth'
|
||||
import { PasswordInput } from '../../components/PasswordInput'
|
||||
|
||||
interface MeResponse {
|
||||
user_id: string
|
||||
@@ -106,8 +105,8 @@ export function Profile() {
|
||||
const [loggingOut, setLoggingOut] = useState(false)
|
||||
const [updatingUsername, setUpdatingUsername] = useState(false)
|
||||
const [updatingPassword, setUpdatingPassword] = useState(false)
|
||||
const [passwordVersion, setPasswordVersion] = useState(0)
|
||||
const [passwordVisible, setPasswordVisible] = useState(false)
|
||||
const [editingPassword, setEditingPassword] = useState(false)
|
||||
const [passwordDraft, setPasswordDraft] = useState('')
|
||||
const [telegram, setTelegramState] = useState('')
|
||||
const [updatingTelegram, setUpdatingTelegram] = useState(false)
|
||||
const [addingTelegram, setAddingTelegram] = useState(false)
|
||||
@@ -237,13 +236,10 @@ export function Profile() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleUpdatePassword = async (newPassword: string) => {
|
||||
const trimmed = newPassword.trim()
|
||||
if (!me || trimmed.length < 8) {
|
||||
if (trimmed.length > 0) {
|
||||
const handleUpdatePassword = async () => {
|
||||
const trimmed = passwordDraft.trim()
|
||||
if (trimmed.length < 8) {
|
||||
toast({ status: 'warning', title: 'Le mot de passe doit contenir au moins 8 caractères' })
|
||||
}
|
||||
setPasswordVersion(v => v + 1) // reset le champ même en cas d'annulation
|
||||
return
|
||||
}
|
||||
|
||||
@@ -251,6 +247,8 @@ export function Profile() {
|
||||
try {
|
||||
await api.updatePassword(trimmed)
|
||||
toast({ status: 'success', title: 'Mot de passe mis à jour' })
|
||||
setEditingPassword(false)
|
||||
setPasswordDraft('')
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError && err.status === 401) {
|
||||
navigate('/login')
|
||||
@@ -263,9 +261,13 @@ export function Profile() {
|
||||
})
|
||||
} finally {
|
||||
setUpdatingPassword(false)
|
||||
setPasswordVersion(v => v + 1) // vide le champ après tentative
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancelPassword = () => {
|
||||
setPasswordDraft('')
|
||||
setEditingPassword(false)
|
||||
}
|
||||
const handleUpdateTelegram = async (newTelegram: string) => {
|
||||
const trimmed = newTelegram.trim()
|
||||
if (!trimmed) {
|
||||
@@ -386,32 +388,51 @@ export function Profile() {
|
||||
|
||||
<Stat>
|
||||
<StatLabel>Mot de passe</StatLabel>
|
||||
<Editable
|
||||
key={passwordVersion}
|
||||
defaultValue=""
|
||||
placeholder="••••••••"
|
||||
onSubmit={handleUpdatePassword}
|
||||
isDisabled={updatingPassword}
|
||||
submitOnBlur={false}
|
||||
>
|
||||
{editingPassword ? (
|
||||
<HStack spacing={2}>
|
||||
<EditablePreview as={StatNumber} fontSize="md" fontFamily="mono" />
|
||||
<EditableInput
|
||||
type={passwordVisible ? 'text' : 'password'}
|
||||
fontSize="md"
|
||||
<PasswordInput
|
||||
size="sm"
|
||||
fontFamily="mono"
|
||||
fontSize="md"
|
||||
value={passwordDraft}
|
||||
onChange={(e) => setPasswordDraft(e.target.value)}
|
||||
isDisabled={updatingPassword}
|
||||
autoFocus
|
||||
/>
|
||||
<ButtonGroup size="sm" spacing={1}>
|
||||
<IconButton
|
||||
aria-label="Enregistrer"
|
||||
icon={<CheckIcon />}
|
||||
isLoading={updatingPassword}
|
||||
onClick={handleUpdatePassword}
|
||||
/>
|
||||
<IconButton
|
||||
aria-label={passwordVisible ? 'Masquer le mot de passe' : 'Afficher le mot de passe'}
|
||||
icon={<FontAwesomeIcon icon={passwordVisible ? faEyeSlash : faEye} />}
|
||||
aria-label="Annuler"
|
||||
icon={<CloseIcon />}
|
||||
isDisabled={updatingPassword}
|
||||
onClick={handleCancelPassword}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
</HStack>
|
||||
) : (
|
||||
<HStack spacing={2}>
|
||||
{/* Jamais le vrai mot de passe : ni celui en base (jamais
|
||||
renvoyé par l'API), ni celui qu'on vient de saisir —
|
||||
contrairement à EditablePreview, qui aurait affiché en
|
||||
clair le dernier mot de passe soumis (le "type" masqué
|
||||
d'EditableInput ne s'applique qu'en mode édition). */}
|
||||
<StatNumber fontSize="md" fontFamily="mono">
|
||||
••••••••
|
||||
</StatNumber>
|
||||
<IconButton
|
||||
aria-label="Modifier le mot de passe"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
tabIndex={-1}
|
||||
onClick={() => setPasswordVisible((v) => !v)}
|
||||
icon={<EditIcon />}
|
||||
onClick={() => setEditingPassword(true)}
|
||||
/>
|
||||
<EditableUsernameControls />
|
||||
</HStack>
|
||||
</Editable>
|
||||
)}
|
||||
</Stat>
|
||||
<Stat>
|
||||
<StatLabel>Telegram</StatLabel>
|
||||
|
||||
Reference in New Issue
Block a user