Refactor component imports and structure: Update import paths for various components to improve organization, moving them into appropriate subdirectories. Remove unused components related to user and event management, enhancing code clarity and maintainability across the application.
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 4m36s

This commit is contained in:
Julien Froidefond
2025-12-12 16:48:41 +01:00
parent 880e96d6e4
commit 97db800c73
27 changed files with 23 additions and 23 deletions

View File

@@ -0,0 +1,383 @@
"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;
}
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 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
),
});
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
),
});
}
};
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"
/>
<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>
)}
</Card>
);
}