import React, { useCallback, useEffect, useRef, useState } from 'react';
import LoadingCard from '@/components/LoadingCard';
import MainCard from '@/components/MainCard';
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 {
AlertCircle,
CheckCircle2,
FileText,
FolderOpen,
KeyRound,
Loader2,
RefreshCw,
User,
} from 'lucide-react';
import { useAuth } from '../context/useAuth';
import api from '../api/axios';
const FILES_ENDPOINT = '/file/files/';
const getErrorText = (value) => {
if (typeof value === 'string') return value;
if (Array.isArray(value)) return value.map(getErrorText).filter(Boolean).join(' ');
return '';
};
const getRequestError = (error, fallback) => {
const data = error.response?.data;
return (
getErrorText(data?.current_password) ||
getErrorText(data?.new_password) ||
getErrorText(data?.confirm_password) ||
getErrorText(data?.non_field_errors) ||
getErrorText(data?.detail) ||
getErrorText(data?.error) ||
getErrorText(Array.isArray(data) ? data : null) ||
fallback
);
};
const parseFilesResponse = (data) => {
const items = Array.isArray(data) ? data : data?.results;
if (!Array.isArray(items)) {
throw new Error('Réponse de fichiers invalide.');
}
// La pagination reste sur le même endpoint Axios, même si Django
// renvoie une URL absolue dans "next".
const next = typeof data?.next === 'string' && data.next
? `${FILES_ENDPOINT}${new URL(data.next, 'https://localhost').search}`
: null;
return {
items,
next,
total: Array.isArray(data)
? data.length
: Number.isInteger(data?.count) && data.count >= 0
? data.count
: null,
};
};
const formatDate = (value) => {
if (!value) return 'Non renseignée';
const date = new Date(value);
if (Number.isNaN(date.getTime())) return 'Non renseignée';
return date.toLocaleDateString('fr-FR', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
};
const ProfileField = ({ label, children }) => (
{label}
{children || 'Non renseigné'}
);
const ProfileContent = ({ user }) => {
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 passwordRequestPending = useRef(false);
const [filesState, setFilesState] = useState({
items: [],
next: null,
total: null,
loading: true,
loadingMore: false,
error: '',
});
const filesRequest = useRef(null);
const loadFiles = useCallback(async (nextPage = null) => {
filesRequest.current?.abort();
const controller = new AbortController();
filesRequest.current = controller;
const append = Boolean(nextPage);
setFilesState((previous) => ({
...previous,
loading: !append,
loadingMore: append,
error: '',
}));
try {
const response = await api.get(nextPage || FILES_ENDPOINT, {
signal: controller.signal,
});
const page = parseFilesResponse(response.data);
if (controller.signal.aborted) return;
setFilesState((previous) => ({
items: append
? Array.from(new Map(
[...previous.items, ...page.items].map((file) => [file.id, file]),
).values())
: page.items,
next: page.next,
total: page.total,
loading: false,
loadingMore: false,
error: '',
}));
} catch (error) {
if (controller.signal.aborted) return;
setFilesState((previous) => ({
...previous,
loading: false,
loadingMore: false,
error: getRequestError(error, 'Impossible de charger tes fichiers. Réessaie dans un instant.'),
}));
}
}, []);
useEffect(() => {
loadFiles();
return () => filesRequest.current?.abort();
}, [loadFiles]);
const resetPasswordForm = () => {
setCurrentPassword('');
setNewPassword('');
setConfirmPassword('');
setPasswordError('');
};
const handlePasswordDialogChange = (open) => {
if (passwordRequestPending.current) return;
resetPasswordForm();
if (open) setPasswordSuccess('');
setPasswordDialogOpen(open);
};
const handleChangePassword = async (event) => {
event.preventDefault();
if (passwordRequestPending.current) return;
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;
}
passwordRequestPending.current = true;
setSavingPassword(true);
try {
await api.post('/users/change-password/', {
current_password: currentPassword,
new_password: newPassword,
confirm_password: confirmPassword,
});
resetPasswordForm();
setPasswordDialogOpen(false);
setPasswordSuccess('Ton mot de passe a bien été modifié.');
} catch (error) {
setPasswordError(getRequestError(error, 'Impossible de modifier le mot de passe.'));
} finally {
passwordRequestPending.current = false;
setSavingPassword(false);
}
};
const fullName = [user.first_name, user.last_name].filter(Boolean).join(' ').trim();
const displayName = fullName || user.username || 'Mon profil';
const initials = [user.first_name?.trim()?.[0], user.last_name?.trim()?.[0]]
.filter(Boolean).join('').toUpperCase() || user.username?.[0]?.toUpperCase() || '?';
const filesBusy = filesState.loading || filesState.loadingMore;
return (
{initials}
{displayName}
{user.is_admin ? 'Administrateur' : 'Utilisateur'}
Informations personnelles
Les informations associées à ton compte.
{user.first_name}
{user.last_name}
{user.username}
{formatDate(user.date_joined)}
Mot de passe
Modifie le mot de passe utilisé pour te connecter.
handlePasswordDialogChange(true)}>
Modifier le mot de passe
{passwordSuccess && (
)}
Mes fichiers
Les documents mis à ta disposition.
loadFiles()}>
{filesState.error && (
loadFiles()} disabled={filesBusy}>Réessayer
)}
{filesState.loading && filesState.items.length === 0 && (
Chargement des fichiers…
)}
{!filesState.loading && !filesState.error && filesState.items.length === 0 && (
Aucun fichier pour le moment
Les documents ajoutés à ton espace apparaîtront ici.
)}
{filesState.items.length > 0 && (
<>
{filesState.items.map((file) => (
{file.name || 'Fichier sans nom'}
{typeof file.type === 'string' && file.type.trim() ? file.type : 'Sans catégorie'}
))}
{filesState.items.length} fichier{filesState.items.length > 1 ? 's' : ''} affiché{filesState.items.length > 1 ? 's' : ''}
{filesState.total !== null && filesState.total > filesState.items.length ? ` sur ${filesState.total}` : ''}
{filesState.next && (
loadFiles(filesState.next)}>
{filesState.loadingMore && }
{filesState.loadingMore ? 'Chargement…' : 'Afficher plus'}
)}
>
)}
Modifier le mot de passe
Saisis ton mot de passe actuel, puis le nouveau deux fois.
);
};
const MyspacePage = () => {
const { user } = useAuth();
if (!user) {
return ;
}
// Réinitialise les formulaires et les fichiers si le compte change.
return ;
};
export default MyspacePage;