Files
got-gaming/components/admin/BackgroundPreferences.tsx

468 lines
17 KiB
TypeScript

"use client";
import { useState, useEffect, useMemo } from "react";
import ImageSelector from "@/components/layout/ImageSelector";
import { updateSitePreferences } from "@/actions/admin/preferences";
import { Button, Card } from "@/components/ui";
interface SitePreferences {
id: string;
homeBackground: string | null;
eventsBackground: string | null;
leaderboardBackground: string | null;
challengesBackground: string | null;
}
interface BackgroundPreferencesProps {
initialPreferences: SitePreferences;
}
const DEFAULT_IMAGES = {
home: "/got-2.jpg",
events: "/got-2.jpg",
leaderboard: "/leaderboard-bg.jpg",
challenges: "/got-2.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
),
challengesBackground: getFormValue(
initialPreferences.challengesBackground,
DEFAULT_IMAGES.challenges
),
}),
[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
),
challengesBackground: getApiValue(
formData.challengesBackground,
DEFAULT_IMAGES.challenges
),
};
const result = await updateSitePreferences(apiData);
if (result.success && result.data) {
setPreferences(result.data);
// Réinitialiser formData avec les nouvelles valeurs (ou images par défaut)
setFormData({
homeBackground: getFormValue(
result.data.homeBackground,
DEFAULT_IMAGES.home
),
eventsBackground: getFormValue(
result.data.eventsBackground,
DEFAULT_IMAGES.events
),
leaderboardBackground: getFormValue(
result.data.leaderboardBackground,
DEFAULT_IMAGES.leaderboard
),
challengesBackground: getFormValue(
result.data.challengesBackground,
DEFAULT_IMAGES.challenges
),
});
setIsEditing(false);
} else {
console.error("Error updating preferences:", result.error);
alert(result.error || "Erreur lors de la mise à jour");
}
} catch (error) {
console.error("Error updating preferences:", error);
alert("Erreur lors de la mise à jour");
}
};
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
),
challengesBackground: getFormValue(
preferences.challengesBackground,
DEFAULT_IMAGES.challenges
),
});
}
};
return (
<Card variant="default" className="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&apos;appliquent à tous les utilisateurs
</p>
</div>
{!isEditing && (
<Button
onClick={handleEdit}
variant="primary"
size="sm"
className="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"
/>
<ImageSelector
value={formData.challengesBackground}
onChange={(url) =>
setFormData({
...formData,
challengesBackground: url,
})
}
label="Background Challenges"
/>
<div className="flex flex-col sm:flex-row gap-2 pt-4">
<Button onClick={handleSave} variant="success" size="md">
Enregistrer
</Button>
<Button onClick={handleCancel} variant="secondary" size="md">
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 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">
Challenges:
</span>
{(() => {
const currentImage =
preferences?.challengesBackground &&
preferences.challengesBackground.trim() !== ""
? preferences.challengesBackground
: DEFAULT_IMAGES.challenges;
const isDefault =
!preferences?.challengesBackground ||
preferences.challengesBackground.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="Challenges 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>
)}
</Card>
);
}