"use client" import background from "@rahoot/web/assets/background.webp" import Button from "@rahoot/web/components/Button" import { useThemeStore } from "@rahoot/web/stores/theme" import clsx from "clsx" import Image from "next/image" import { useEffect, useMemo, useState } from "react" type MediaItem = { fileName: string url: string size: number mime: string type: string } type Props = { onBack: () => void } const ThemeEditor = ({ onBack }: Props) => { const { backgroundUrl, brandName, setBackground, setBrandName, reset } = useThemeStore() const [customUrl, setCustomUrl] = useState("") const [items, setItems] = useState([]) const [loading, setLoading] = useState(false) const [uploading, setUploading] = useState(false) const [uploadError, setUploadError] = useState(null) const [savingTheme, setSavingTheme] = useState(false) const [saveError, setSaveError] = useState(null) const [initializing, setInitializing] = useState(true) const previewUrl = useMemo( () => backgroundUrl || customUrl || background.src, [backgroundUrl, customUrl], ) const load = async () => { setLoading(true) try { const [mediaRes, themeRes] = await Promise.all([ fetch("/api/media", { cache: "no-store" }), fetch("/api/theme", { cache: "no-store" }), ]) const mediaData = await mediaRes.json() const themeData = await themeRes.json() if (!mediaRes.ok) throw new Error(mediaData.error || "Failed to load media") if (!themeRes.ok) throw new Error(themeData.error || "Failed to load theme") const onlyImages = (mediaData.media || []).filter( (item: MediaItem) => item.mime?.startsWith("image/"), ) setItems(onlyImages) if (themeData.theme) { if (typeof themeData.theme.backgroundUrl === "string") { setBackground(themeData.theme.backgroundUrl || null) setCustomUrl(themeData.theme.backgroundUrl || "") } if (typeof themeData.theme.brandName === "string") { setBrandName(themeData.theme.brandName) } } } catch (error) { console.error(error) } finally { setLoading(false) setInitializing(false) } } useEffect(() => { load() }, []) const persistTheme = async (next: { backgroundUrl?: string | null; brandName?: string }) => { setSavingTheme(true) setSaveError(null) try { const res = await fetch("/api/theme", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(next), }) const data = await res.json() if (!res.ok) throw new Error(data.error || "Failed to save theme") } catch (error) { const message = error instanceof Error ? error.message : "Failed to save theme" setSaveError(message) } finally { setSavingTheme(false) } } const handleSet = (url: string) => { if (!url) return setBackground(url) persistTheme({ backgroundUrl: url }) } const handleApplyCustom = () => { if (!customUrl.trim()) return handleSet(customUrl.trim()) } const handleUpload = async (file?: File | null) => { if (!file) return setUploading(true) setUploadError(null) try { const form = new FormData() form.append("file", file) const res = await fetch("/api/media", { method: "POST", body: form, }) const data = await res.json() if (!res.ok) { throw new Error(data.error || "Upload failed") } if (data.media?.url) { handleSet(data.media.url) setCustomUrl(data.media.url) } load() } catch (error) { const message = error instanceof Error ? error.message : "Upload failed" setUploadError(message) } finally { setUploading(false) } } const handleReset = () => { reset() setCustomUrl("") persistTheme({ backgroundUrl: null, brandName: "Rahoot" }) } return (

Theme editor

Set the game background from uploads or a custom URL.

Branding

Preview

Background preview
Current background
setCustomUrl(e.target.value)} placeholder="https://example.com/background.webp or /media/your-file" className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm" />

Paste any reachable image URL (including your uploaded media path). Changes apply immediately to the game background.

Images from media library

Pick any uploaded image as the background.

Upload a background image (webp/png/jpg). It will be available immediately and selected as the current background.

{uploadError && (

{uploadError}

)} {saveError && (

{saveError}

)} {(savingTheme || initializing) && !uploading && (

{initializing ? "Loading theme…" : "Saving theme…"}

)}
{items.map((item) => ( ))} {!loading && items.length === 0 && (
No images uploaded yet. Upload an image in the Media page, then pick it here.
)}
) } export default ThemeEditor