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
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 4m36s
This commit is contained in:
383
components/admin/BackgroundPreferences.tsx
Normal file
383
components/admin/BackgroundPreferences.tsx
Normal 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'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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user