Refactor ProfileForm component: Improve code readability by formatting error messages and UI elements. Add default avatar selection feature for user profile customization, enhancing user experience. Update button labels for clarity during avatar upload and password modification.
This commit is contained in:
@@ -147,7 +147,9 @@ export default function ProfileForm({
|
||||
setTimeout(() => setSuccess(null), 3000);
|
||||
} else {
|
||||
const errorData = await response.json();
|
||||
setError(errorData.error || "Erreur lors de la modification du mot de passe");
|
||||
setError(
|
||||
errorData.error || "Erreur lors de la modification du mot de passe"
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error changing password:", err);
|
||||
@@ -238,6 +240,45 @@ export default function ProfileForm({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Avatars par défaut */}
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<label className="block text-pixel-gold text-xs uppercase tracking-widest mb-2">
|
||||
Avatars par défaut
|
||||
</label>
|
||||
<div className="flex gap-3">
|
||||
{[
|
||||
"/avatar-1.jpg",
|
||||
"/avatar-2.jpg",
|
||||
"/avatar-3.jpg",
|
||||
"/avatar-4.jpg",
|
||||
"/avatar-5.jpg",
|
||||
"/avatar-6.jpg",
|
||||
].map((defaultAvatar) => (
|
||||
<button
|
||||
key={defaultAvatar}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setAvatar(defaultAvatar);
|
||||
setSuccess("Avatar sélectionné");
|
||||
setTimeout(() => setSuccess(null), 3000);
|
||||
}}
|
||||
className={`w-16 h-16 rounded-full border-2 overflow-hidden transition ${
|
||||
avatar === defaultAvatar
|
||||
? "border-pixel-gold scale-110"
|
||||
: "border-pixel-gold/30 hover:border-pixel-gold/50"
|
||||
}`}
|
||||
>
|
||||
<img
|
||||
src={defaultAvatar}
|
||||
alt="Avatar par défaut"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
@@ -251,7 +292,9 @@ export default function ProfileForm({
|
||||
htmlFor="avatar-upload"
|
||||
className="px-4 py-2 border border-pixel-gold/50 bg-black/40 text-white uppercase text-xs tracking-widest rounded hover:bg-pixel-gold/10 hover:border-pixel-gold transition cursor-pointer inline-block"
|
||||
>
|
||||
{uploadingAvatar ? "Upload en cours..." : "Changer l'avatar"}
|
||||
{uploadingAvatar
|
||||
? "Upload en cours..."
|
||||
: "Upload un avatar custom"}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -270,9 +313,7 @@ export default function ProfileForm({
|
||||
minLength={3}
|
||||
maxLength={20}
|
||||
/>
|
||||
<p className="text-gray-500 text-xs mt-1">
|
||||
3-20 caractères
|
||||
</p>
|
||||
<p className="text-gray-500 text-xs mt-1">3-20 caractères</p>
|
||||
</div>
|
||||
|
||||
{/* Stats Display */}
|
||||
@@ -283,13 +324,17 @@ export default function ProfileForm({
|
||||
|
||||
<div className="grid grid-cols-2 gap-4 mb-4">
|
||||
<div className="bg-black/40 border border-pixel-gold/20 rounded p-4">
|
||||
<div className="text-gray-400 text-xs uppercase mb-1">Score</div>
|
||||
<div className="text-gray-400 text-xs uppercase mb-1">
|
||||
Score
|
||||
</div>
|
||||
<div className="text-pixel-gold text-xl font-bold">
|
||||
{formatNumber(profile.score)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-black/40 border border-pixel-gold/20 rounded p-4">
|
||||
<div className="text-gray-400 text-xs uppercase mb-1">Niveau</div>
|
||||
<div className="text-gray-400 text-xs uppercase mb-1">
|
||||
Niveau
|
||||
</div>
|
||||
<div className="text-pixel-gold text-xl font-bold">
|
||||
Lv.{profile.level}
|
||||
</div>
|
||||
@@ -300,7 +345,9 @@ export default function ProfileForm({
|
||||
<div className="mb-4">
|
||||
<div className="flex justify-between text-xs text-gray-400 mb-1">
|
||||
<span>HP</span>
|
||||
<span>{profile.hp} / {profile.maxHp}</span>
|
||||
<span>
|
||||
{profile.hp} / {profile.maxHp}
|
||||
</span>
|
||||
</div>
|
||||
<div className="relative h-3 bg-gray-900 border border-gray-700 rounded overflow-hidden">
|
||||
<div
|
||||
@@ -314,7 +361,9 @@ export default function ProfileForm({
|
||||
<div>
|
||||
<div className="flex justify-between text-xs text-gray-400 mb-1">
|
||||
<span>XP</span>
|
||||
<span>{formatNumber(profile.xp)} / {formatNumber(profile.maxXp)}</span>
|
||||
<span>
|
||||
{formatNumber(profile.xp)} / {formatNumber(profile.maxXp)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="relative h-3 bg-gray-900 border border-pixel-gold/30 rounded overflow-hidden">
|
||||
<div
|
||||
@@ -432,7 +481,9 @@ export default function ProfileForm({
|
||||
disabled={changingPassword}
|
||||
className="px-4 py-2 border border-pixel-gold/50 bg-black/40 text-white uppercase text-xs tracking-widest rounded hover:bg-pixel-gold/10 hover:border-pixel-gold transition disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{changingPassword ? "Modification..." : "Modifier le mot de passe"}
|
||||
{changingPassword
|
||||
? "Modification..."
|
||||
: "Modifier le mot de passe"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -443,4 +494,3 @@ export default function ProfileForm({
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
84
components/WebpackErrorHandler.tsx
Normal file
84
components/WebpackErrorHandler.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function WebpackErrorHandler() {
|
||||
useEffect(() => {
|
||||
// Listen for unhandled errors
|
||||
const handleError = (event: ErrorEvent) => {
|
||||
const error = event.error || event.message || "";
|
||||
const errorString = String(error);
|
||||
|
||||
// Check if it's a webpack module loading error
|
||||
if (
|
||||
errorString.includes("Cannot read properties of undefined") &&
|
||||
(errorString.includes("reading 'call'") ||
|
||||
errorString.includes("webpack") ||
|
||||
errorString.includes("react-server-dom-webpack"))
|
||||
) {
|
||||
console.warn(
|
||||
"Webpack module loading error detected, forcing page reload..."
|
||||
);
|
||||
|
||||
// Clear all caches and reload
|
||||
if (typeof window !== "undefined") {
|
||||
// Clear service worker cache if exists
|
||||
if ("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker.getRegistrations().then((registrations) => {
|
||||
registrations.forEach((registration) => {
|
||||
registration.unregister();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Clear all caches
|
||||
if ("caches" in window) {
|
||||
caches.keys().then((names) => {
|
||||
names.forEach((name) => {
|
||||
caches.delete(name);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Force hard reload after a short delay
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Listen for unhandled promise rejections
|
||||
const handleRejection = (event: PromiseRejectionEvent) => {
|
||||
const error = event.reason || "";
|
||||
const errorString = String(error);
|
||||
|
||||
if (
|
||||
errorString.includes("Cannot read properties of undefined") &&
|
||||
(errorString.includes("reading 'call'") ||
|
||||
errorString.includes("webpack") ||
|
||||
errorString.includes("react-server-dom-webpack"))
|
||||
) {
|
||||
console.warn(
|
||||
"Webpack module loading error detected in promise, forcing page reload..."
|
||||
);
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("error", handleError);
|
||||
window.addEventListener("unhandledrejection", handleRejection);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("error", handleError);
|
||||
window.removeEventListener("unhandledrejection", handleRejection);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user