leaderboard / frontend /submit.html
Alyafeai's picture
add arabic support
9efd00d
<!DOCTYPE html>
<html lang="en">
<body class="bg-white dark:bg-slate-900">
<style>
html[dir="rtl"] #submitPage {
direction: rtl;
text-align: right;
}
html[dir="rtl"] #submitPage .submit-title-icon {
margin-left: 0.75rem;
margin-right: 0;
}
html[dir="rtl"] #submitPage .submit-select-icon {
left: 0.75rem;
right: auto;
}
html[dir="rtl"] #submitPage #submitBtn {
flex-direction: row-reverse;
}
html[dir="rtl"] #submitPage #sidebar-status-container button,
html[dir="rtl"] #submitPage #sidebar-status-container .text-xs,
html[dir="rtl"] #submitPage #submitAboutBody {
text-align: right;
}
</style>
<div id="submitPage" class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div class="lg:col-span-1 space-y-4">
<div
class="bg-white dark:bg-slate-800 p-8 rounded-2xl shadow-sm border border-slate-200 dark:border-slate-700">
<h3 class="text-xl font-bold mb-6 flex items-center text-slate-800 dark:text-slate-100">
<span
class="submit-title-icon bg-indigo-100 dark:bg-indigo-900 text-indigo-600 dark:text-indigo-300 p-2 rounded-full mr-3">
<i data-lucide="rocket" class="w-5 h-5"></i>
</span>
<span id="submitTitle">Submit Model</span>
</h3>
<form id="submitForm" class="space-y-6">
<div class="grid grid-cols-1 gap-6">
<div class="space-y-4">
<div><label id="submitLabelModelName" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">Model Name</label><input required name="model_name" type="text"
placeholder="myorg/mymodel"
class="w-full px-4 py-2 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 dark:text-white focus:ring-2 focus:ring-indigo-500 outline-none transition">
</div>
<div><label id="submitLabelModelType" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">Model Type</label>
<div class="relative"><select name="model_type"
class="w-full px-4 py-2 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 dark:text-white focus:ring-2 focus:ring-indigo-500 outline-none appearance-none transition cursor-pointer">
<option id="submitOptionBase" value="base">🟢 Base</option>
<option id="submitOptionInstruct" value="instruct" selected>🔶 Instruct</option>
</select><i data-lucide="chevron-down" class="submit-select-icon absolute right-3 top-3 h-4 w-4 text-slate-400 pointer-events-none"></i>
</div>
</div>
<!-- <div><label
class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">Precision</label>
<div class="relative"><select name="precision"
class="w-full px-4 py-2 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 dark:text-white focus:ring-2 focus:ring-indigo-500 outline-none appearance-none transition cursor-pointer">
<option value="float16">float16</option>
<option value="bfloat16" selected>bfloat16</option>
<option value="8bit">8bit</option>
<option value="4bit">4bit</option>
</select><i data-lucide="chevron-down"
class="absolute right-3 top-3 h-4 w-4 text-slate-400 pointer-events-none"></i>
</div>
</div> -->
</div>
<!-- <div class="space-y-4">
<div><label
class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">Revision
Commit</label><input name="revision" type="text" value="main" placeholder="main"
class="w-full px-4 py-2 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 dark:text-white focus:ring-2 focus:ring-indigo-500 outline-none transition font-mono text-sm">
</div>
<div><label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">Weight
Type</label>
<div class="relative"><select name="weight_type"
class="w-full px-4 py-2 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 dark:text-white focus:ring-2 focus:ring-indigo-500 outline-none appearance-none transition cursor-pointer">
<option value="Original" selected>Original</option>
<option value="Adapter">Adapter</option>
<option value="Delta">Delta</option>
</select><i data-lucide="chevron-down"
class="absolute right-3 top-3 h-4 w-4 text-slate-400 pointer-events-none"></i>
</div>
</div>
<div><label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">Base
Model <span class="text-xs text-slate-400 font-normal">(if
adapter)</span></label><input name="base_model" type="text"
placeholder="e.g. myorg/base-model"
class="w-full px-4 py-2 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-900 dark:text-white focus:ring-2 focus:ring-indigo-500 outline-none transition">
</div>
</div> -->
</div>
<div class="pt-4 border-t border-slate-100 dark:border-slate-700">
<button type="submit" id="submitBtn"
class="w-full py-3 px-4 rounded-xl shadow-lg shadow-indigo-500/30 text-sm font-bold text-white bg-indigo-600 hover:bg-indigo-700 transition-all flex justify-center items-center gap-2 group"><span id="submitBtnLabel">Submit Model</span><i data-lucide="arrow-right"
class="w-4 h-4 group-hover:translate-x-1 transition-transform"></i></button>
</div>
<div id="submitMsg" class="text-center text-sm font-medium min-h-[20px]"></div>
</form>
</div>
</div>
<div class="lg:col-span-1 space-y-4">
<h3 id="submitStatusTitle" class="text-lg font-bold text-slate-800 dark:text-slate-100 mb-4 px-2">Evaluation Status</h3>
<div id="sidebar-status-container" class="space-y-3">
<div id="submitQueueLoading" class="p-4 text-center text-sm text-slate-400 animate-pulse">Loading queue...</div>
</div>
<div
class="mt-6 p-4 bg-slate-50 dark:bg-slate-800/50 rounded-xl border border-slate-200 dark:border-slate-700 text-xs text-slate-500 dark:text-slate-400 leading-relaxed">
<h4 id="submitAboutTitle" class="font-bold text-slate-700 dark:text-slate-300 mb-2">About Submission</h4>
<p id="submitAboutBody">Submitted models are added to the queue automatically. Results appear on the leaderboard once finished.</p>
</div>
</div>
</div>
<script>
(function () {
const SUBMIT_TRANSLATIONS = {
ar: {
title: "إرسال نموذج",
model_name: "اسم النموذج",
model_type: "نوع النموذج",
base: "🟢 أساسي",
instruct: "🔶 موجّه",
submit_btn: "إرسال النموذج",
status_title: "حالة التقييم",
loading_queue: "جارٍ تحميل قائمة الانتظار...",
about_title: "عن الإرسال",
about_body: "تُضاف النماذج المرسلة تلقائيًا إلى قائمة الانتظار، وتظهر نتائجها على لوحة الصدارة بمجرد الانتهاء.",
status_offline: "خدمة الحالة غير متاحة الآن",
submitting: "جارٍ الإرسال...",
success_default: "تم الإرسال بنجاح",
pending: "قيد الانتظار",
running: "قيد التنفيذ",
finished: "مكتمل",
failed: "فشل",
no_status: "لا توجد تقييمات في حالة {status}.",
anon: "مجهول",
error_default: "حدث خطأ",
},
en: {
title: "Submit Model",
model_name: "Model Name",
model_type: "Model Type",
base: "🟢 Base",
instruct: "🔶 Instruct",
submit_btn: "Submit Model",
status_title: "Evaluation Status",
loading_queue: "Loading queue...",
about_title: "About Submission",
about_body: "Submitted models are added to the queue automatically. Results appear on the leaderboard once finished.",
status_offline: "Status offline",
submitting: "Submitting...",
success_default: "Submitted successfully",
pending: "Pending",
running: "Running",
finished: "Finished",
failed: "Failed",
no_status: "No {status} evaluations.",
anon: "anon",
error_default: "Error",
}
};
const getText = (key, vars = {}) => {
const lang = window.getCurrentLanguage ? window.getCurrentLanguage() : 'en';
const dict = SUBMIT_TRANSLATIONS[lang] || SUBMIT_TRANSLATIONS.en;
let value = dict[key] ?? key;
Object.entries(vars).forEach(([name, replacement]) => {
value = value.replace(`{${name}}`, replacement);
});
return value;
};
function applySubmitTranslations() {
const setTxt = (id, value) => {
const el = document.getElementById(id);
if (el) el.innerText = value;
};
setTxt('submitTitle', getText('title'));
setTxt('submitLabelModelName', getText('model_name'));
setTxt('submitLabelModelType', getText('model_type'));
setTxt('submitOptionBase', getText('base'));
setTxt('submitOptionInstruct', getText('instruct'));
setTxt('submitBtnLabel', getText('submit_btn'));
setTxt('submitStatusTitle', getText('status_title'));
setTxt('submitQueueLoading', getText('loading_queue'));
setTxt('submitAboutTitle', getText('about_title'));
setTxt('submitAboutBody', getText('about_body'));
}
// 1. Handle Sidebar Loading
async function updateSidebarQueue() {
const container = document.querySelector('#sidebar-status-container');
if (!container) return;
try {
const res = await fetch('/api/queue');
const q = await res.json();
const safeQ = q || { "pending": [], "running": [], "finished": [], "failed": [] };
if (window.updateHeaderStats) {
window.updateHeaderStats(safeQ);
}
const cf = { pending: ["text-amber-500 bg-amber-500", "clock"], running: ["text-blue-500 bg-blue-500", "loader-2"], finished: ["text-emerald-500 bg-emerald-500", "check-circle"], failed: ["text-rose-500 bg-rose-500", "x-circle"] };
container.innerHTML = Object.entries(safeQ).map(([k, items]) => `
<div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 overflow-hidden shadow-sm">
<button onclick="this.nextElementSibling.classList.toggle('hidden')" class="w-full flex items-center justify-between p-4 text-left hover:bg-slate-50 dark:hover:bg-slate-700/50 transition-colors">
<div class="flex items-center gap-3"><div class="p-2 rounded-lg ${cf[k] ? cf[k][0] : 'text-slate-500 bg-slate-500'} bg-opacity-10 text-current"><i data-lucide="${cf[k] ? cf[k][1] : 'help-circle'}" class="w-4 h-4"></i></div><span class="text-sm font-bold capitalize text-slate-700 dark:text-slate-200">${getText(k)}</span></div><span class="text-xs font-bold px-2 py-1 rounded-full bg-slate-100 dark:bg-slate-700 text-slate-500 dark:text-slate-400">${items.length}</span>
</button>
<div class="hidden text-sm">${items.length ? `<div class="bg-slate-50 dark:bg-darkbg p-3 border-t border-slate-100 dark:border-slate-700 space-y-2">${items.map(i => `<div class="flex justify-between text-xs p-2 bg-white dark:bg-slate-800 rounded border border-slate-200 dark:border-slate-600"><span class="font-mono text-slate-600 dark:text-slate-300 truncate" title="${i.name}">${i.name}</span><span class="px-1.5 bg-slate-100 dark:bg-slate-700 text-slate-500 rounded">${i.user || getText('anon')}</span></div>`).join('')}</div>` : `<div class="p-4 text-xs text-center text-slate-400 bg-slate-50 dark:bg-darkbg border-t border-slate-100 dark:border-slate-700 italic">${getText('no_status', { status: getText(k).toLowerCase() })}</div>`}</div>
</div>`).join('');
if (window.lucide) lucide.createIcons();
} catch (err) {
console.error("Sidebar queue error:", err);
container.innerHTML = `<div class="p-4 text-center text-rose-500 text-sm">${getText('status_offline')}</div>`;
}
}
// 2. Handle Form Submission
const form = document.querySelector('#submitForm');
if (form) {
form.onsubmit = async (e) => {
e.preventDefault();
const btn = document.querySelector('#submitBtn');
const msg = document.querySelector('#submitMsg');
btn.disabled = true;
btn.innerHTML = `<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i> ${getText('submitting')}`;
if (window.lucide) lucide.createIcons();
try {
const res = await fetch('/api/submit', { method: 'POST', body: new FormData(e.target) });
const json = await res.json();
if (!res.ok) throw new Error(json.detail ? JSON.stringify(json.detail) : (json.message || "Error"));
msg.className = "text-center text-sm font-medium text-emerald-600 dark:text-emerald-400";
msg.innerText = "✅ " + (json.message || getText('success_default'));
e.target.reset();
// Update Local Sidebar & Header
await updateSidebarQueue();
} catch (err) {
msg.className = "text-center text-sm font-medium text-rose-600 dark:text-rose-400";
msg.innerText = "❌ " + (err.message || getText('error_default'));
}
btn.disabled = false;
btn.innerHTML = `<span>${getText('submit_btn')}</span> <i data-lucide="arrow-right" class="w-4 h-4"></i>`;
if (window.lucide) lucide.createIcons();
};
}
applySubmitTranslations();
if (window.registerLanguageListener) {
window.registerLanguageListener(() => {
applySubmitTranslations();
updateSidebarQueue();
});
}
// Init
updateSidebarQueue();
})();
</script>
</body>
</html>