Phase 2 UI Adjustments/Edits by bonamin
This commit is contained in:
98
frontend/src/melodies/TranslationModal.jsx
Normal file
98
frontend/src/melodies/TranslationModal.jsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { getLanguageName } from "./melodyUtils";
|
||||
|
||||
/**
|
||||
* Modal for editing translations of a field (Name or Description).
|
||||
* Props:
|
||||
* open - boolean
|
||||
* onClose - function
|
||||
* field - string label ("Name" or "Description")
|
||||
* value - dict { lang_code: text }
|
||||
* onChange - function(updatedDict)
|
||||
* languages - array of lang codes ["en", "el", "sr"]
|
||||
* multiline - boolean (use textarea instead of input)
|
||||
*/
|
||||
export default function TranslationModal({
|
||||
open,
|
||||
onClose,
|
||||
field,
|
||||
value,
|
||||
onChange,
|
||||
languages,
|
||||
multiline = false,
|
||||
}) {
|
||||
const [draft, setDraft] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setDraft({ ...value });
|
||||
}
|
||||
}, [open, value]);
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
const updateDraft = (lang, text) => {
|
||||
setDraft((prev) => ({ ...prev, [lang]: text }));
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
onChange(draft);
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div className="fixed inset-0 bg-black/50" onClick={onClose} />
|
||||
<div className="relative bg-white rounded-lg shadow-xl w-full max-w-lg mx-4 max-h-[80vh] overflow-y-auto">
|
||||
<div className="p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
Translations — {field}
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
{languages.map((lang) => (
|
||||
<div key={lang}>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
{getLanguageName(lang)}{" "}
|
||||
<span className="text-gray-400 font-mono text-xs uppercase">
|
||||
({lang})
|
||||
</span>
|
||||
</label>
|
||||
{multiline ? (
|
||||
<textarea
|
||||
value={draft[lang] || ""}
|
||||
onChange={(e) => updateDraft(lang, e.target.value)}
|
||||
rows={2}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
type="text"
|
||||
value={draft[lang] || ""}
|
||||
onChange={(e) => updateDraft(lang, e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-end gap-3 mt-6">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 bg-gray-100 text-gray-700 text-sm rounded-md hover:bg-gray-200 transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSave}
|
||||
className="px-4 py-2 bg-blue-600 text-white text-sm rounded-md hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
Save Translations
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user