Add challenges background preference support: Extend site preferences and related components to include challengesBackground, update API and UI to handle new background image settings for challenges.
Some checks failed
Deploy with Docker Compose / deploy (push) Failing after 2m23s

This commit is contained in:
Julien Froidefond
2025-12-15 21:26:30 +01:00
parent 83446759fe
commit d3a4fa7cf5
25 changed files with 8373 additions and 12328 deletions

View File

@@ -10,6 +10,7 @@ interface SitePreferences {
homeBackground: string | null;
eventsBackground: string | null;
leaderboardBackground: string | null;
challengesBackground: string | null;
}
interface BackgroundPreferencesProps {
@@ -20,6 +21,7 @@ const DEFAULT_IMAGES = {
home: "/got-2.jpg",
events: "/got-2.jpg",
leaderboard: "/leaderboard-bg.jpg",
challenges: "/got-2.jpg",
};
export default function BackgroundPreferences({
@@ -57,6 +59,10 @@ export default function BackgroundPreferences({
initialPreferences.leaderboardBackground,
DEFAULT_IMAGES.leaderboard
),
challengesBackground: getFormValue(
initialPreferences.challengesBackground,
DEFAULT_IMAGES.challenges
),
}),
[initialPreferences]
);
@@ -90,6 +96,10 @@ export default function BackgroundPreferences({
formData.leaderboardBackground,
DEFAULT_IMAGES.leaderboard
),
challengesBackground: getApiValue(
formData.challengesBackground,
DEFAULT_IMAGES.challenges
),
};
const result = await updateSitePreferences(apiData);
@@ -110,6 +120,10 @@ export default function BackgroundPreferences({
result.data.leaderboardBackground,
DEFAULT_IMAGES.leaderboard
),
challengesBackground: getFormValue(
result.data.challengesBackground,
DEFAULT_IMAGES.challenges
),
});
setIsEditing(false);
} else {
@@ -138,6 +152,10 @@ export default function BackgroundPreferences({
preferences.leaderboardBackground,
DEFAULT_IMAGES.leaderboard
),
challengesBackground: getFormValue(
preferences.challengesBackground,
DEFAULT_IMAGES.challenges
),
});
}
};
@@ -197,6 +215,16 @@ export default function BackgroundPreferences({
}
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
@@ -376,6 +404,62 @@ export default function BackgroundPreferences({
);
})()}
</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>

View File

@@ -204,18 +204,28 @@ export default function ChallengesSection({
};
return (
<section
className="relative w-full min-h-screen flex flex-col items-center overflow-hidden pt-24 pb-16"
style={{
backgroundImage: `url(${backgroundImage})`,
backgroundSize: "cover",
backgroundPosition: "center",
backgroundRepeat: "no-repeat",
}}
>
<div className="absolute inset-0 bg-black/70 backdrop-blur-sm"></div>
<section className="relative w-full min-h-screen flex flex-col items-center overflow-hidden pt-24 pb-16">
{/* Background Image */}
<div
className="absolute inset-0 bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: `url('${backgroundImage}')`,
}}
>
{/* Dark overlay for readability */}
<div
className="absolute inset-0 bg-gradient-to-b"
style={{
background: `linear-gradient(to bottom,
color-mix(in srgb, var(--background) 70%, transparent),
color-mix(in srgb, var(--background) 60%, transparent),
color-mix(in srgb, var(--background) 80%, transparent)
)`,
}}
/>
</div>
<div className="relative z-10 w-full max-w-6xl mx-auto px-8 py-16">
<div className="relative z-10 w-full max-w-6xl mx-auto px-4 sm:px-8 py-16">
<SectionTitle variant="gradient" size="md" className="mb-8 text-center">
DÉFIS ENTRE JOUEURS
</SectionTitle>