leotsha / templates /vote_multitask.html
Sediba-AI
Initial Morutabana deployment: XLM-R + EduIntel + sentiment classifier
9d3f668
Raw
History Blame Contribute Delete
9.15 kB
<!DOCTYPE html>
<html lang="nso">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Leotลกa la Sepedi โ€” Build Language Data</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, sans-serif; background: #f5f4f0; color: #1a1a1a; }
.container { max-width: 650px; margin: 0 auto; padding: 15px; }
/* Header */
header { text-align: center; margin-bottom: 20px; }
h1 { font-size: 28px; margin: 10px 0; }
.subtitle { opacity: 0.7; font-size: 14px; }
/* Profile Card */
.profile { background: linear-gradient(135deg, #667eea, #764ba2); color: white; padding: 15px; border-radius: 12px; margin-bottom: 20px; display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.stat { text-align: center; }
.stat-value { font-size: 24px; font-weight: bold; }
.stat-label { font-size: 12px; opacity: 0.9; }
.badges { grid-column: 1/-1; display: flex; gap: 8px; justify-content: center; font-size: 20px; }
.badge { opacity: 0.5; }
.badge.earned { opacity: 1; }
/* Task Card */
.task-card { background: white; padding: 20px; border-radius: 12px; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
.task-type { display: inline-block; background: #667eea; color: white; padding: 6px 12px; border-radius: 20px; font-size: 12px; font-weight: bold; margin-bottom: 10px; }
.task-title { font-size: 18px; font-weight: bold; margin: 10px 0; }
.sentence { background: #f9f9f9; padding: 15px; border-left: 4px solid #667eea; border-radius: 4px; font-size: 16px; margin: 15px 0; }
.task-tip { background: #fff3cd; padding: 10px; border-radius: 6px; font-size: 13px; margin-bottom: 15px; }
.task-tip::before { content: "๐Ÿ’ก "; }
/* Options */
.options { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 15px 0; }
.option-btn { padding: 12px; border: 2px solid #ddd; background: white; border-radius: 8px; cursor: pointer; transition: all 0.3s; font-size: 14px; }
.option-btn:hover { border-color: #667eea; background: #f0f4ff; }
.option-btn.selected { background: #667eea; color: white; border-color: #667eea; }
/* Submit */
.submit-btn { width: 100%; padding: 14px; background: linear-gradient(135deg, #667eea, #764ba2); color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: bold; cursor: pointer; }
.submit-btn:hover { opacity: 0.9; }
/* Stats Dashboard */
.stats-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; margin: 20px 0; }
.stat-box { background: white; padding: 12px; border-radius: 8px; text-align: center; }
.stat-box-value { font-size: 20px; font-weight: bold; color: #667eea; }
.stat-box-label { font-size: 11px; opacity: 0.7; margin-top: 5px; }
/* Leaderboard */
.leaderboard { background: white; padding: 15px; border-radius: 12px; }
.leaderboard h3 { margin-bottom: 10px; }
.lb-item { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #eee; font-size: 14px; }
.lb-rank { font-weight: bold; color: #667eea; width: 30px; }
.lb-pts { font-weight: bold; color: #4CAF50; }
/* Feedback */
.feedback { position: fixed; top: 20px; right: 20px; background: #4CAF50; color: white; padding: 12px 20px; border-radius: 8px; opacity: 0; animation: slideIn 0.3s forwards; }
@keyframes slideIn { to { opacity: 1; } }
/* Responsive */
@media (max-width: 600px) {
.options { grid-template-columns: 1fr; }
.stats-grid { grid-template-columns: 1fr; }
.profile { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>๐ŸŒ Leotลกa la Sepedi</h1>
<p class="subtitle">Help build Sepedi language data</p>
</header>
<!-- Profile -->
<div class="profile">
<div class="stat">
<span class="stat-value">{{ stats.points }}</span>
<span class="stat-label">Points</span>
</div>
<div class="stat">
<span class="stat-value">Lvl {{ stats.level }}</span>
<span class="stat-label">Level</span>
</div>
<div class="badges">
<span class="badge {% if 'bronze' in stats.badges %}earned{% endif %}">๐Ÿฅ‰</span>
<span class="badge {% if 'silver' in stats.badges %}earned{% endif %}">๐Ÿฅˆ</span>
<span class="badge {% if 'gold' in stats.badges %}earned{% endif %}">๐Ÿฅ‡</span>
</div>
</div>
<!-- Data Collection Stats -->
<div>
<h3 style="margin-bottom: 10px;">๐Ÿ“Š Data Collected</h3>
<div class="stats-grid">
{% for type_key, type_data in collection_stats.by_type.items %}
<div class="stat-box">
<span style="font-size: 24px;">{{ type_data.icon }}</span>
<div class="stat-box-value">{{ type_data.count }}</div>
<div class="stat-box-label">{{ type_data.name|truncatewords:2 }}</div>
</div>
{% endfor %}
</div>
</div>
<!-- Task -->
{% if task.sentence %}
<div class="task-card">
<span class="task-type">{{ task.config.name }}</span>
<div class="task-title">{{ task.config.description }}</div>
<div class="sentence">"{{ task.sentence.text }}"</div>
<div class="task-tip">{{ task.config.tip }}</div>
<form id="taskForm" method="POST">
{% csrf_token %}
<input type="hidden" name="task_type" value="{{ task.task_type }}">
<input type="hidden" name="sentence_id" value="{{ task.sentence.id }}">
{% if task.config.type != 'text_input' %}
<div class="options">
{% for opt in task.config.options %}
<button type="button" class="option-btn" data-value="{{ opt.value }}">
{{ opt.emoji|default:'' }} {{ opt.label }}
</button>
{% endfor %}
</div>
<input type="hidden" name="value" id="valueInput" required>
{% else %}
<input type="text" name="value" placeholder="Your answer..." style="width: 100%; padding: 10px; border: 2px solid #ddd; border-radius: 6px; font-size: 14px;" required>
{% endif %}
<button type="submit" class="submit-btn">
โ†’ Next (+{{ task.config.points }} pts)
</button>
</form>
</div>
{% else %}
<div class="task-card">
<h2>๐ŸŽ‰ All Sentences Labeled!</h2>
<p>Thank you! Check back soon for more.</p>
</div>
{% endif %}
<!-- Leaderboard -->
<div class="leaderboard">
<h3>๐Ÿ† Top Contributors</h3>
{% for lb in leaderboard %}
<div class="lb-item">
<span class="lb-rank">#{{ forloop.counter }}</span>
<span>{% if lb.voter_id == voter.voter_id %}๐Ÿ‘ค YOU{% else %}Contributor{% endif %}</span>
<span class="lb-pts">{{ lb.points }} pts</span>
</div>
{% endfor %}
</div>
</div>
<script>
// Handle option selection
document.querySelectorAll('.option-btn').forEach(btn => {
btn.addEventListener('click', function(e) {
e.preventDefault();
document.querySelectorAll('.option-btn').forEach(b => b.classList.remove('selected'));
this.classList.add('selected');
document.getElementById('valueInput').value = this.dataset.value;
});
});
// Handle form submission
document.getElementById('taskForm').addEventListener('submit', async function(e) {
e.preventDefault();
const formData = new FormData(this);
const response = await fetch('', { method: 'POST', body: formData });
const data = await response.json();
if (data.success) {
// Show feedback
const msg = document.createElement('div');
msg.className = 'feedback';
msg.textContent = data.message;
document.body.appendChild(msg);
// Reload after brief delay
setTimeout(() => location.reload(), 500);
}
});
</script>
</body>
</html>