vault backup: 2025-12-26 02:09:22
All checks were successful
Deploy Quartz site to GitHub Pages / build (push) Successful in 2m29s
All checks were successful
Deploy Quartz site to GitHub Pages / build (push) Successful in 2m29s
This commit is contained in:
368
stroma/templates/index.html
Normal file
368
stroma/templates/index.html
Normal file
@@ -0,0 +1,368 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 2rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.stat-card.green {
|
||||
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
||||
}
|
||||
|
||||
.stat-card.orange {
|
||||
background: linear-gradient(135deg, #ee0979 0%, #ff6a00 100%);
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.875rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
.quick-start-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.quick-start-card {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(12px);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 1rem;
|
||||
padding: 1.5rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.quick-start-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.quick-start-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, var(--primary), #764ba2);
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
.card-description {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.card-count {
|
||||
display: inline-block;
|
||||
background: #f0f4ff;
|
||||
color: var(--primary);
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 1rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.active-session-card {
|
||||
background: white;
|
||||
border: 2px solid var(--primary);
|
||||
border-radius: 1rem;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.active-session-card:hover {
|
||||
box-shadow: 0 8px 24px rgba(99, 102, 241, 0.2);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.session-badge {
|
||||
display: inline-block;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 2rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.custom-quiz-section {
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||
border-radius: 1rem;
|
||||
padding: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.filter-chips {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.filter-chip {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
padding: 0.375rem 0.875rem;
|
||||
border-radius: 1rem;
|
||||
font-size: 0.875rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.btn-gradient {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 0.875rem 2rem;
|
||||
border-radius: 0.5rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.btn-gradient:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Stats Overview -->
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">{{ total_questions }}</div>
|
||||
<div class="stat-label">Totalt frågor</div>
|
||||
</div>
|
||||
<div class="stat-card green">
|
||||
<div class="stat-value">{{ answered_count }}</div>
|
||||
<div class="stat-label">Besvarade</div>
|
||||
</div>
|
||||
<div class="stat-card orange">
|
||||
<div class="stat-value">{{ total_questions|add:answered_count|floatformat:0|add:"-" }}{{ answered_count }}</div>
|
||||
<div class="stat-label">Återstående</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Active Sessions -->
|
||||
{% if active_sessions %}
|
||||
<div class="section-header">
|
||||
<h2 class="section-title">🎯 Aktiva Quiz</h2>
|
||||
</div>
|
||||
|
||||
{% for session in active_sessions %}
|
||||
<div class="active-session-card" id="session-{{ session.id }}">
|
||||
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 1rem;">
|
||||
<div>
|
||||
<span class="session-badge">
|
||||
{% if session.course %}{{ session.course.name }}{% else %}Blandat{% endif %}
|
||||
</span>
|
||||
<div style="font-size: 0.875rem; color: var(--text-muted); margin-top: 0.25rem;">
|
||||
Startat {{ session.created_at|date:"Y-m-d H:i" }}
|
||||
</div>
|
||||
</div>
|
||||
<form action="{% url 'quiz:close_quiz' session.id %}" method="post" hx-post="{% url 'quiz:close_quiz' session.id %}"
|
||||
hx-target="#session-{{ session.id }}" hx-swap="outerHTML">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-secondary" style="padding: 0.5rem 1rem; font-size: 0.875rem;">
|
||||
✕ Stäng
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% if session.tags.exists %}
|
||||
<div class="filter-chips" style="margin-bottom: 1rem;">
|
||||
{% for tag in session.tags.all %}
|
||||
<span class="filter-chip">🏷️ {{ tag.name }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<a href="{% url 'quiz:quiz_mode' session.id %}" class="btn btn-primary" style="width: 100%; text-align: center; padding: 0.875rem;">
|
||||
▶️ Fortsätt Quiz
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<!-- Quick Start Options -->
|
||||
<div class="section-header" style="margin-top: 3rem;">
|
||||
<h2 class="section-title">🚀 Snabbstart</h2>
|
||||
</div>
|
||||
|
||||
<div class="quick-start-grid">
|
||||
<!-- Tag-based quick starts -->
|
||||
<div class="quick-start-card" onclick="startTagQuiz('anatomi')">
|
||||
<div class="card-icon">🦴</div>
|
||||
<div class="card-title">Anatomi</div>
|
||||
<div class="card-description">Fokusera på anatomifrågor</div>
|
||||
<span class="card-count" id="tag-anatomi-count">...</span>
|
||||
</div>
|
||||
|
||||
<div class="quick-start-card" onclick="startTagQuiz('histologi')">
|
||||
<div class="card-icon">🔬</div>
|
||||
<div class="card-title">Histologi</div>
|
||||
<div class="card-description">Fokusera på histologifrågor</div>
|
||||
<span class="card-count" id="tag-histologi-count">...</span>
|
||||
</div>
|
||||
|
||||
<div class="quick-start-card" onclick="startTagQuiz('cerebrum')">
|
||||
<div class="card-icon">🧠</div>
|
||||
<div class="card-title">Cerebrum</div>
|
||||
<div class="card-description">Hjärnans frågor</div>
|
||||
<span class="card-count" id="tag-cerebrum-count">...</span>
|
||||
</div>
|
||||
|
||||
<div class="quick-start-card" onclick="startTagQuiz('oga')">
|
||||
<div class="card-icon">👁️</div>
|
||||
<div class="card-title">Öga</div>
|
||||
<div class="card-description">Ögats anatomi & histologi</div>
|
||||
<span class="card-count" id="tag-oga-count">...</span>
|
||||
</div>
|
||||
|
||||
<div class="quick-start-card" onclick="startTagQuiz('ora')">
|
||||
<div class="card-icon">👂</div>
|
||||
<div class="card-title">Öra</div>
|
||||
<div class="card-description">Örats anatomi & histologi</div>
|
||||
<span class="card-count" id="tag-ora-count">...</span>
|
||||
</div>
|
||||
|
||||
<div class="quick-start-card" onclick="startAllQuiz()">
|
||||
<div class="card-icon">🎲</div>
|
||||
<div class="card-title">Alla Frågor</div>
|
||||
<div class="card-description">Blandat från hela kursen</div>
|
||||
<span class="card-count">{{ total_questions }} frågor</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom Quiz Builder -->
|
||||
<div class="custom-quiz-section">
|
||||
<h2 style="margin-bottom: 1rem; color: var(--text-main);">🎨 Anpassat Quiz</h2>
|
||||
<p style="color: var(--text-muted); margin-bottom: 1.5rem;">Skapa ett quiz med dina egna filter</p>
|
||||
|
||||
<form method="post" action="{% url 'quiz:create_quiz' %}" id="custom-quiz-form">
|
||||
{% csrf_token %}
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-bottom: 1.5rem;">
|
||||
<div class="form-group">
|
||||
<label for="{{ form.course.id_for_label }}" style="font-weight: 600; margin-bottom: 0.5rem;">Kurs</label>
|
||||
{{ form.course }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="{{ form.tags.id_for_label }}" style="font-weight: 600; margin-bottom: 0.5rem;">Taggar</label>
|
||||
{{ form.tags }}
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn-gradient">
|
||||
✨ Skapa Anpassat Quiz
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Fetch tag counts on load
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const tags = ['anatomi', 'histologi', 'cerebrum', 'oga', 'ora'];
|
||||
|
||||
for (const tag of tags) {
|
||||
try {
|
||||
const response = await fetch(`/api/tag-count/${tag}/`);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const el = document.getElementById(`tag-${tag}-count`);
|
||||
if (el) el.textContent = `${data.count} frågor`;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch count for', tag);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function startTagQuiz(tagSlug) {
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = '{% url "quiz:create_quiz" %}';
|
||||
|
||||
const csrf = document.createElement('input');
|
||||
csrf.type = 'hidden';
|
||||
csrf.name = 'csrfmiddlewaretoken';
|
||||
csrf.value = '{{ csrf_token }}';
|
||||
form.appendChild(csrf);
|
||||
|
||||
const tagInput = document.createElement('input');
|
||||
tagInput.type = 'hidden';
|
||||
tagInput.name = 'tag_slug';
|
||||
tagInput.value = tagSlug;
|
||||
form.appendChild(tagInput);
|
||||
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
|
||||
function startAllQuiz() {
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = '{% url "quiz:create_quiz" %}';
|
||||
|
||||
const csrf = document.createElement('input');
|
||||
csrf.type = 'hidden';
|
||||
csrf.name = 'csrfmiddlewaretoken';
|
||||
csrf.value = '{{ csrf_token }}';
|
||||
form.appendChild(csrf);
|
||||
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user