393 lines
14 KiB
TypeScript
393 lines
14 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect, useMemo } from "react";
|
|
import ImageSelector from "@/components/ImageSelector";
|
|
|
|
interface SitePreferences {
|
|
id: string;
|
|
homeBackground: string | null;
|
|
eventsBackground: string | null;
|
|
leaderboardBackground: string | null;
|
|
}
|
|
|
|
interface BackgroundPreferencesProps {
|
|
initialPreferences: SitePreferences;
|
|
}
|
|
|
|
const DEFAULT_IMAGES = {
|
|
home: "/got-2.jpg",
|
|
events: "/got-2.jpg",
|
|
leaderboard: "/leaderboard-bg.jpg",
|
|
};
|
|
|
|
export default function BackgroundPreferences({
|
|
initialPreferences,
|
|
}: BackgroundPreferencesProps) {
|
|
// Helper pour obtenir la valeur à afficher dans le formulaire
|
|
const getFormValue = (
|
|
dbValue: string | null | undefined,
|
|
defaultImage: string
|
|
) => {
|
|
return dbValue && dbValue.trim() !== "" ? dbValue : defaultImage;
|
|
};
|
|
|
|
// Helper pour obtenir la valeur à envoyer à l'API
|
|
const getApiValue = (formValue: string, defaultImage: string) => {
|
|
return formValue === defaultImage ? "" : formValue;
|
|
};
|
|
|
|
const [preferences, setPreferences] = useState<SitePreferences | null>(
|
|
initialPreferences
|
|
);
|
|
const [isEditing, setIsEditing] = useState(false);
|
|
|
|
const computedFormData = useMemo(
|
|
() => ({
|
|
homeBackground: getFormValue(
|
|
initialPreferences.homeBackground,
|
|
DEFAULT_IMAGES.home
|
|
),
|
|
eventsBackground: getFormValue(
|
|
initialPreferences.eventsBackground,
|
|
DEFAULT_IMAGES.events
|
|
),
|
|
leaderboardBackground: getFormValue(
|
|
initialPreferences.leaderboardBackground,
|
|
DEFAULT_IMAGES.leaderboard
|
|
),
|
|
}),
|
|
[initialPreferences]
|
|
);
|
|
|
|
const [formData, setFormData] = useState(computedFormData);
|
|
|
|
// Synchroniser les préférences quand initialPreferences change
|
|
useEffect(() => {
|
|
setPreferences(initialPreferences);
|
|
setFormData(computedFormData);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [initialPreferences]);
|
|
|
|
const handleEdit = () => {
|
|
setIsEditing(true);
|
|
};
|
|
|
|
const handleSave = async () => {
|
|
try {
|
|
// Convertir les valeurs du formulaire en valeurs API ("" si c'est l'image par défaut)
|
|
const apiData = {
|
|
homeBackground: getApiValue(
|
|
formData.homeBackground,
|
|
DEFAULT_IMAGES.home
|
|
),
|
|
eventsBackground: getApiValue(
|
|
formData.eventsBackground,
|
|
DEFAULT_IMAGES.events
|
|
),
|
|
leaderboardBackground: getApiValue(
|
|
formData.leaderboardBackground,
|
|
DEFAULT_IMAGES.leaderboard
|
|
),
|
|
};
|
|
|
|
const response = await fetch("/api/admin/preferences", {
|
|
method: "PUT",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(apiData),
|
|
});
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
setPreferences(data);
|
|
// Réinitialiser formData avec les nouvelles valeurs (ou images par défaut)
|
|
setFormData({
|
|
homeBackground: getFormValue(
|
|
data.homeBackground,
|
|
DEFAULT_IMAGES.home
|
|
),
|
|
eventsBackground: getFormValue(
|
|
data.eventsBackground,
|
|
DEFAULT_IMAGES.events
|
|
),
|
|
leaderboardBackground: getFormValue(
|
|
data.leaderboardBackground,
|
|
DEFAULT_IMAGES.leaderboard
|
|
),
|
|
});
|
|
setIsEditing(false);
|
|
} else {
|
|
const errorData = await response.json();
|
|
console.error("Error updating preferences:", errorData);
|
|
alert(errorData.error || "Erreur lors de la mise à jour");
|
|
}
|
|
} catch (error) {
|
|
console.error("Error updating preferences:", error);
|
|
}
|
|
};
|
|
|
|
const handleCancel = () => {
|
|
setIsEditing(false);
|
|
if (preferences) {
|
|
setFormData({
|
|
homeBackground: getFormValue(
|
|
preferences.homeBackground,
|
|
DEFAULT_IMAGES.home
|
|
),
|
|
eventsBackground: getFormValue(
|
|
preferences.eventsBackground,
|
|
DEFAULT_IMAGES.events
|
|
),
|
|
leaderboardBackground: getFormValue(
|
|
preferences.leaderboardBackground,
|
|
DEFAULT_IMAGES.leaderboard
|
|
),
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="bg-black/60 border border-pixel-gold/20 rounded p-3 sm:p-4">
|
|
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-start gap-3 mb-4">
|
|
<div className="min-w-0 flex-1">
|
|
<h3 className="text-pixel-gold font-bold text-base sm:text-lg break-words">
|
|
Images de fond du site
|
|
</h3>
|
|
<p className="text-gray-400 text-xs sm:text-sm">
|
|
Ces préférences s'appliquent à tous les utilisateurs
|
|
</p>
|
|
</div>
|
|
{!isEditing && (
|
|
<button
|
|
onClick={handleEdit}
|
|
className="px-3 sm:px-4 py-2 border border-pixel-gold/50 bg-black/60 text-white uppercase text-[10px] sm:text-xs tracking-widest rounded hover:bg-pixel-gold/10 transition whitespace-nowrap flex-shrink-0"
|
|
>
|
|
Modifier
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{isEditing ? (
|
|
<div className="space-y-6">
|
|
<ImageSelector
|
|
value={formData.homeBackground}
|
|
onChange={(url) =>
|
|
setFormData({
|
|
...formData,
|
|
homeBackground: url,
|
|
})
|
|
}
|
|
label="Background Home"
|
|
/>
|
|
<ImageSelector
|
|
value={formData.eventsBackground}
|
|
onChange={(url) =>
|
|
setFormData({
|
|
...formData,
|
|
eventsBackground: url,
|
|
})
|
|
}
|
|
label="Background Events"
|
|
/>
|
|
<ImageSelector
|
|
value={formData.leaderboardBackground}
|
|
onChange={(url) =>
|
|
setFormData({
|
|
...formData,
|
|
leaderboardBackground: url,
|
|
})
|
|
}
|
|
label="Background Leaderboard"
|
|
/>
|
|
<div className="flex flex-col sm:flex-row gap-2 pt-4">
|
|
<button
|
|
onClick={handleSave}
|
|
className="px-4 py-2 border border-green-500/50 bg-green-900/20 text-green-400 uppercase text-xs tracking-widest rounded hover:bg-green-900/30 transition"
|
|
>
|
|
Enregistrer
|
|
</button>
|
|
<button
|
|
onClick={handleCancel}
|
|
className="px-4 py-2 border border-gray-600/50 bg-gray-900/20 text-gray-400 uppercase text-xs tracking-widest rounded hover:bg-gray-900/30 transition"
|
|
>
|
|
Annuler
|
|
</button>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="space-y-4">
|
|
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
|
|
<span className="text-pixel-gold font-bold text-sm sm:text-base min-w-0 sm:min-w-[120px] flex-shrink-0">
|
|
Home:
|
|
</span>
|
|
{(() => {
|
|
const currentImage =
|
|
preferences?.homeBackground &&
|
|
preferences.homeBackground.trim() !== ""
|
|
? preferences.homeBackground
|
|
: DEFAULT_IMAGES.home;
|
|
const isDefault =
|
|
!preferences?.homeBackground ||
|
|
preferences.homeBackground.trim() === "";
|
|
|
|
return (
|
|
<div className="flex items-center gap-2 sm:gap-3 min-w-0 flex-1">
|
|
<div className="relative w-16 h-10 sm:w-20 sm:h-12 rounded border border-pixel-gold/30 overflow-hidden bg-black/60 flex-shrink-0">
|
|
<img
|
|
src={currentImage}
|
|
alt="Home background"
|
|
className="w-full h-full object-cover"
|
|
onError={(e) => {
|
|
const target = e.currentTarget;
|
|
const currentSrc = target.src;
|
|
const fallbackSrc = "/got-2.jpg";
|
|
if (!currentSrc.includes(fallbackSrc)) {
|
|
target.src = fallbackSrc;
|
|
} else {
|
|
target.style.display = "none";
|
|
const fallbackDiv =
|
|
target.nextElementSibling as HTMLElement;
|
|
if (fallbackDiv) {
|
|
fallbackDiv.classList.remove("hidden");
|
|
}
|
|
}
|
|
}}
|
|
/>
|
|
<div className="absolute inset-0 flex items-center justify-center bg-black/60 text-gray-500 text-xs hidden">
|
|
No image
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col min-w-0 flex-1">
|
|
<span className="text-xs text-gray-400 truncate min-w-0">
|
|
{isDefault ? "Par défaut: " : ""}
|
|
{currentImage}
|
|
</span>
|
|
{isDefault && (
|
|
<span className="text-[10px] text-gray-500 italic">
|
|
(Image par défaut)
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
})()}
|
|
</div>
|
|
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
|
|
<span className="text-pixel-gold font-bold text-sm sm:text-base min-w-0 sm:min-w-[120px] flex-shrink-0">
|
|
Events:
|
|
</span>
|
|
{(() => {
|
|
const currentImage =
|
|
preferences?.eventsBackground &&
|
|
preferences.eventsBackground.trim() !== ""
|
|
? preferences.eventsBackground
|
|
: DEFAULT_IMAGES.events;
|
|
const isDefault =
|
|
!preferences?.eventsBackground ||
|
|
preferences.eventsBackground.trim() === "";
|
|
|
|
return (
|
|
<div className="flex items-center gap-2 sm:gap-3 min-w-0 flex-1">
|
|
<div className="relative w-16 h-10 sm:w-20 sm:h-12 rounded border border-pixel-gold/30 overflow-hidden bg-black/60 flex-shrink-0">
|
|
<img
|
|
src={currentImage}
|
|
alt="Events background"
|
|
className="w-full h-full object-cover"
|
|
onError={(e) => {
|
|
const target = e.currentTarget;
|
|
const currentSrc = target.src;
|
|
const fallbackSrc = "/got-2.jpg";
|
|
if (!currentSrc.includes(fallbackSrc)) {
|
|
target.src = fallbackSrc;
|
|
} else {
|
|
target.style.display = "none";
|
|
const fallbackDiv =
|
|
target.nextElementSibling as HTMLElement;
|
|
if (fallbackDiv) {
|
|
fallbackDiv.classList.remove("hidden");
|
|
}
|
|
}
|
|
}}
|
|
/>
|
|
<div className="absolute inset-0 flex items-center justify-center bg-black/60 text-gray-500 text-xs hidden">
|
|
No image
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col min-w-0 flex-1">
|
|
<span className="text-xs text-gray-400 truncate min-w-0">
|
|
{isDefault ? "Par défaut: " : ""}
|
|
{currentImage}
|
|
</span>
|
|
{isDefault && (
|
|
<span className="text-[10px] text-gray-500 italic">
|
|
(Image par défaut)
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
})()}
|
|
</div>
|
|
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
|
|
<span className="text-pixel-gold font-bold text-sm sm:text-base min-w-0 sm:min-w-[120px] flex-shrink-0">
|
|
Leaderboard:
|
|
</span>
|
|
{(() => {
|
|
const currentImage =
|
|
preferences?.leaderboardBackground &&
|
|
preferences.leaderboardBackground.trim() !== ""
|
|
? preferences.leaderboardBackground
|
|
: DEFAULT_IMAGES.leaderboard;
|
|
const isDefault =
|
|
!preferences?.leaderboardBackground ||
|
|
preferences.leaderboardBackground.trim() === "";
|
|
|
|
return (
|
|
<div className="flex items-center gap-2 sm:gap-3 min-w-0 flex-1">
|
|
<div className="relative w-16 h-10 sm:w-20 sm:h-12 rounded border border-pixel-gold/30 overflow-hidden bg-black/60 flex-shrink-0">
|
|
<img
|
|
src={currentImage}
|
|
alt="Leaderboard background"
|
|
className="w-full h-full object-cover"
|
|
onError={(e) => {
|
|
const target = e.currentTarget;
|
|
const currentSrc = target.src;
|
|
const fallbackSrc = "/got-2.jpg";
|
|
if (!currentSrc.includes(fallbackSrc)) {
|
|
target.src = fallbackSrc;
|
|
} else {
|
|
target.style.display = "none";
|
|
const fallbackDiv =
|
|
target.nextElementSibling as HTMLElement;
|
|
if (fallbackDiv) {
|
|
fallbackDiv.classList.remove("hidden");
|
|
}
|
|
}
|
|
}}
|
|
/>
|
|
<div className="absolute inset-0 flex items-center justify-center bg-black/60 text-gray-500 text-xs hidden">
|
|
No image
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col min-w-0 flex-1">
|
|
<span className="text-xs text-gray-400 truncate min-w-0">
|
|
{isDefault ? "Par défaut: " : ""}
|
|
{currentImage}
|
|
</span>
|
|
{isDefault && (
|
|
<span className="text-[10px] text-gray-500 italic">
|
|
(Image par défaut)
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
})()}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|