feat: add update username
ci-web / test (push) Has been cancelled

This commit is contained in:
Nuxgrid
2026-07-29 09:28:33 +02:00
parent ef280799c1
commit 2cc532a64d
3 changed files with 102 additions and 23 deletions
+81 -3
View File
@@ -4,11 +4,16 @@ import {
Badge,
Box,
Button,
ButtonGroup,
Container,
Divider,
Editable,
EditableInput,
EditablePreview,
Flex,
Heading,
HStack,
IconButton,
SimpleGrid,
Spinner,
Stack,
@@ -16,8 +21,10 @@ import {
StatLabel,
StatNumber,
Text,
useEditableControls,
useToast,
} from '@chakra-ui/react'
import { CheckIcon, CloseIcon, EditIcon } from '@chakra-ui/icons'
import { useNavigate } from 'react-router-dom'
import { api, ApiError } from '../../lib/api'
import { useAuth } from '../../lib/auth'
@@ -56,6 +63,34 @@ function formatDate(iso: string) {
})
}
function EditableUsernameControls() {
const { isEditing, getSubmitButtonProps, getCancelButtonProps, getEditButtonProps } =
useEditableControls()
return isEditing ? (
<ButtonGroup size="sm" spacing={1}>
<IconButton
aria-label="Enregistrer"
icon={<CheckIcon />}
{...getSubmitButtonProps()}
/>
<IconButton
aria-label="Annuler"
icon={<CloseIcon />}
{...getCancelButtonProps()}
/>
</ButtonGroup>
) : (
<IconButton
aria-label="Modifier le nom d'utilisateur"
size="sm"
variant="ghost"
icon={<EditIcon />}
{...getEditButtonProps()}
/>
)
}
export function Profile() {
const toast = useToast()
const navigate = useNavigate()
@@ -63,6 +98,7 @@ export function Profile() {
const [me, setMe] = useState<MeResponse | null>(null)
const [loading, setLoading] = useState(true)
const [loggingOut, setLoggingOut] = useState(false)
const [updatingUsername, setUpdatingUsername] = useState(false)
useEffect(() => {
let cancelled = false
@@ -85,6 +121,30 @@ export function Profile() {
}
}, [navigate, toast])
const handleUpdateUsername = async (newUsername: string) => {
const trimmed = newUsername.trim()
if (!me || !trimmed || trimmed === me.username) return
setUpdatingUsername(true)
try {
const res = await api.updateUsername(trimmed)
setMe({ ...me, username: res.username })
toast({ status: 'success', title: "Nom d'utilisateur mis à jour" })
} catch (err) {
if (err instanceof ApiError && err.status === 401) {
navigate('/login')
return
}
toast({
status: 'error',
title: "Impossible de mettre à jour le nom d'utilisateur",
description: err instanceof ApiError ? err.message : undefined,
})
} finally {
setUpdatingUsername(false)
}
}
const handleLogout = async () => {
setLoggingOut(true)
try {
@@ -127,9 +187,27 @@ export function Profile() {
<Flex align="center" gap={5} wrap="wrap">
<Avatar name={me.username} size="xl" />
<Box>
<Heading size="md" fontFamily="mono">
{me.username}
</Heading>
<Editable
key={me.username}
defaultValue={me.username}
onSubmit={handleUpdateUsername}
isDisabled={updatingUsername}
submitOnBlur={false}
>
<HStack spacing={2}>
<EditablePreview
as={Heading}
size="md"
fontFamily="mono"
/>
<EditableInput
fontFamily="mono"
fontSize="md"
fontWeight="bold"
/>
<EditableUsernameControls />
</HStack>
</Editable>
<HStack mt={2} spacing={2}>
<Badge colorScheme={roleColor(me.role)}>{roleLabel(me.role)}</Badge>
{me.type_abonnement && (