import { useState, useEffect } from "react"; import { useParams, useNavigate } from "react-router-dom"; import api from "../api/client"; import { useAuth } from "../auth/AuthContext"; import ConfirmDialog from "../components/ConfirmDialog"; import { getLocalizedValue, getLanguageName, normalizeColor, formatDuration, } from "./melodyUtils"; function Field({ label, children }) { return (
{label}
{children || "-"}
); } export default function MelodyDetail() { const { id } = useParams(); const navigate = useNavigate(); const { hasRole } = useAuth(); const canEdit = hasRole("superadmin", "melody_editor"); const [melody, setMelody] = useState(null); const [files, setFiles] = useState({}); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); const [showDelete, setShowDelete] = useState(false); const [displayLang, setDisplayLang] = useState("en"); const [melodySettings, setMelodySettings] = useState(null); useEffect(() => { api.get("/settings/melody").then((ms) => { setMelodySettings(ms); setDisplayLang(ms.primary_language || "en"); }); }, []); useEffect(() => { loadData(); }, [id]); const loadData = async () => { setLoading(true); try { const [m, f] = await Promise.all([ api.get(`/melodies/${id}`), api.get(`/melodies/${id}/files`), ]); setMelody(m); setFiles(f); } catch (err) { setError(err.message); } finally { setLoading(false); } }; const handleDelete = async () => { try { await api.delete(`/melodies/${id}`); navigate("/melodies"); } catch (err) { setError(err.message); setShowDelete(false); } }; if (loading) { return
Loading...
; } if (error) { return (
{error}
); } if (!melody) return null; const info = melody.information || {}; const settings = melody.default_settings || {}; const languages = melodySettings?.available_languages || ["en"]; const displayName = getLocalizedValue(info.name, displayLang, "Untitled Melody"); const badgeStyle = (active) => ({ backgroundColor: active ? "var(--success-bg)" : "var(--bg-card-hover)", color: active ? "var(--success-text)" : "var(--text-muted)", }); return (

{displayName}

{languages.length > 1 && ( )}
{canEdit && (
)}
{/* Left column */}
{/* Melody Information */}

Melody Information

{melody.type} {info.melodyTone} {info.totalNotes} {info.steps} {info.minSpeed} {info.maxSpeed} {info.color ? ( {info.color} ) : ( "-" )} {info.isTrueRing ? "Yes" : "No"}
{getLocalizedValue(info.description, displayLang)}
{info.customTags?.length > 0 ? (
{info.customTags.map((tag) => ( {tag} ))}
) : ( "-" )}
{/* Identifiers */}

Identifiers

{melody.id} {melody.pid} {melody.uid}
{melody.url}
{/* Right column */}
{/* Default Settings */}

Default Settings

{settings.speed}% {formatDuration(settings.duration)} {settings.totalRunDuration} {settings.pauseDuration} {settings.infiniteLoop ? "Yes" : "No"}
{settings.echoRing?.length > 0 ? settings.echoRing.join(", ") : "-"}
{settings.noteAssignments?.length > 0 ? settings.noteAssignments.join(", ") : "-"}
{/* Files */}

Files

{files.binary_url ? ( Download binary ) : ( Not uploaded )} {files.preview_url ? ( ) : ( Not uploaded )}
setShowDelete(false)} />
); }