test
This commit is contained in:
parent
71978c7877
commit
667a0035c6
@ -5,4 +5,6 @@ urlpatterns = [
|
||||
path('', views.EventListView.as_view(), name='event-list'),
|
||||
path('<int:id>/', views.EventDetailView.as_view(), name='event-detail'),
|
||||
path('create/', views.EventCreateView.as_view(), name='event-create'),
|
||||
path('change-password/', views.ChangePasswordView.as_view(), name='change-password',
|
||||
),
|
||||
]
|
||||
@ -7,6 +7,7 @@ from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
from .models import Invitation, User
|
||||
from .serializers import UserSerializer, RegisterSerializer
|
||||
from django.contrib.auth import password_validation
|
||||
|
||||
class RegisterView(generics.CreateAPIView):
|
||||
queryset = User.objects.all()
|
||||
@ -73,6 +74,41 @@ class UserPromoteView(APIView):
|
||||
except User.DoesNotExist:
|
||||
return Response({'error': 'Utilisateur non trouvé'}, status=404)
|
||||
|
||||
class ChangePasswordView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
current_password = request.data.get("current_password", "")
|
||||
new_password = request.data.get("new_password", "")
|
||||
confirm_password = request.data.get("confirm_password", "")
|
||||
|
||||
if not request.user.check_password(current_password):
|
||||
return Response(
|
||||
{"current_password": ["Mot de passe actuel incorrect."]},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
if new_password != confirm_password:
|
||||
return Response(
|
||||
{"confirm_password": ["Les mots de passe ne correspondent pas."]},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
try:
|
||||
password_validation.validate_password(
|
||||
new_password,
|
||||
user=request.user,
|
||||
)
|
||||
except DjangoValidationError as error:
|
||||
return Response(
|
||||
{"new_password": error.messages},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
request.user.set_password(new_password)
|
||||
request.user.save(update_fields=["password"])
|
||||
|
||||
return Response({"detail": "Mot de passe modifié."})
|
||||
|
||||
class UserDeleteView(UserDetailView):
|
||||
http_method_names = ['delete', 'options']
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,16 +1,89 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import LoadingCard from '@/components/LoadingCard';
|
||||
import MainCard from '@/components/MainCard';
|
||||
import { useAuth } from '../context/useAuth';
|
||||
import {
|
||||
CardContent,
|
||||
} from '@/components/ui/card';
|
||||
import { CardContent } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { User } from 'lucide-react';
|
||||
import api from '../api/axios'; // Adapte ce chemin si ton instance Axios est ailleurs.
|
||||
|
||||
const MyspacePage = () => {
|
||||
const { user } = useAuth();
|
||||
const [passwordDialogOpen, setPasswordDialogOpen] = useState(false);
|
||||
const [currentPassword, setCurrentPassword] = useState('');
|
||||
const [newPassword, setNewPassword] = useState('');
|
||||
const [confirmPassword, setConfirmPassword] = useState('');
|
||||
const [passwordError, setPasswordError] = useState('');
|
||||
const [passwordSuccess, setPasswordSuccess] = useState('');
|
||||
const [savingPassword, setSavingPassword] = useState(false);
|
||||
|
||||
const resetPasswordForm = () => {
|
||||
setCurrentPassword('');
|
||||
setNewPassword('');
|
||||
setConfirmPassword('');
|
||||
setPasswordError('');
|
||||
};
|
||||
|
||||
const handlePasswordDialogChange = (open) => {
|
||||
if (savingPassword) return;
|
||||
|
||||
setPasswordDialogOpen(open);
|
||||
if (!open) resetPasswordForm();
|
||||
};
|
||||
|
||||
const handleChangePassword = async (event) => {
|
||||
event.preventDefault();
|
||||
setPasswordError('');
|
||||
setPasswordSuccess('');
|
||||
|
||||
if (!currentPassword || !newPassword || !confirmPassword) {
|
||||
setPasswordError('Remplis tous les champs.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword !== confirmPassword) {
|
||||
setPasswordError('Les nouveaux mots de passe ne correspondent pas.');
|
||||
return;
|
||||
}
|
||||
|
||||
setSavingPassword(true);
|
||||
|
||||
try {
|
||||
await api.post('/users/change-password/', {
|
||||
current_password: currentPassword,
|
||||
new_password: newPassword,
|
||||
confirm_password: confirmPassword,
|
||||
});
|
||||
|
||||
resetPasswordForm();
|
||||
setPasswordDialogOpen(false);
|
||||
setPasswordSuccess('Mot de passe modifié.');
|
||||
} catch (error) {
|
||||
const data = error.response?.data;
|
||||
const message =
|
||||
data?.current_password?.[0] ||
|
||||
data?.new_password?.[0] ||
|
||||
data?.confirm_password?.[0] ||
|
||||
data?.detail ||
|
||||
'Impossible de modifier le mot de passe.';
|
||||
|
||||
setPasswordError(message);
|
||||
} finally {
|
||||
setSavingPassword(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
@ -24,24 +97,23 @@ const MyspacePage = () => {
|
||||
|
||||
return (
|
||||
<MainCard title={user.first_name} icon={User}>
|
||||
|
||||
<CardContent className="overflow-y-auto">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<span className="font-medium">Prénom :</span>
|
||||
<span>{user.first_name}</span>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<span className="font-medium">Nom :</span>
|
||||
<span>{user.last_name}</span>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<span className="font-medium">Admin :</span>
|
||||
<Badge variant={user.is_admin ? 'default' : 'secondary'}>
|
||||
{user.is_admin ? 'Oui' : 'Non'}
|
||||
@ -50,14 +122,14 @@ const MyspacePage = () => {
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<span className="font-medium">Nom d'utilisateur :</span>
|
||||
<span>{user.username}</span>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<span className="font-medium">Date d'inscription :</span>
|
||||
<span>
|
||||
{user.date_joined
|
||||
@ -65,8 +137,107 @@ const MyspacePage = () => {
|
||||
: 'Non spécifié'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<span className="font-medium">Mot de passe :</span>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setPasswordSuccess('');
|
||||
setPasswordDialogOpen(true);
|
||||
}}
|
||||
>
|
||||
Modifier le mot de passe
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{passwordSuccess && (
|
||||
<p role="status" className="text-sm text-green-600">
|
||||
{passwordSuccess}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
<Dialog
|
||||
open={passwordDialogOpen}
|
||||
onOpenChange={handlePasswordDialogChange}
|
||||
>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Modifier le mot de passe</DialogTitle>
|
||||
<DialogDescription>
|
||||
Saisis ton mot de passe actuel, puis le nouveau deux fois.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<form onSubmit={handleChangePassword} className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="current-password">Mot de passe actuel</Label>
|
||||
<Input
|
||||
id="current-password"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
value={currentPassword}
|
||||
onChange={(event) => setCurrentPassword(event.target.value)}
|
||||
disabled={savingPassword}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="new-password">Nouveau mot de passe</Label>
|
||||
<Input
|
||||
id="new-password"
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
value={newPassword}
|
||||
onChange={(event) => setNewPassword(event.target.value)}
|
||||
disabled={savingPassword}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="confirm-password">
|
||||
Confirmer le nouveau mot de passe
|
||||
</Label>
|
||||
<Input
|
||||
id="confirm-password"
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
value={confirmPassword}
|
||||
onChange={(event) => setConfirmPassword(event.target.value)}
|
||||
disabled={savingPassword}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
{passwordError && (
|
||||
<p role="alert" className="text-sm text-destructive">
|
||||
{passwordError}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() => handlePasswordDialogChange(false)}
|
||||
disabled={savingPassword}
|
||||
>
|
||||
Annuler
|
||||
</Button>
|
||||
<Button type="submit" disabled={savingPassword}>
|
||||
{savingPassword ? 'Modification...' : 'Enregistrer'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</MainCard>
|
||||
);
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user