morozovka-portal/app/templates/workshops.html

186 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Мастер-классы — Morozovka Portal</title>
<style>
* { box-sizing: border-box; }
body { background: #0d1117; color: #e6edf3; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; margin: 0; font-size: 14px; line-height: 1.5; }
a { color: #58a6ff; text-decoration: none; }
a:hover { text-decoration: underline; }
.topnav { background: #161b22; border-bottom: 1px solid #30363d; padding: 12px 32px; display: flex; align-items: center; gap: 24px; }
.topnav-logo { display: flex; align-items: center; gap: 8px; color: #e6edf3; font-weight: 600; font-size: 15px; }
.topnav-logo svg { color: #58a6ff; }
.topnav-links { display: flex; gap: 20px; flex: 1; }
.topnav-links a { color: #c9d1d9; font-weight: 500; }
.topnav-links a:hover { color: #58a6ff; text-decoration: none; }
.topnav-actions { display: flex; gap: 12px; align-items: center; padding-right: 80px; }
.btn { display: inline-flex; align-items: center; gap: 6px; padding: 5px 16px; border-radius: 6px; font-size: 14px; font-weight: 500; line-height: 20px; border: 1px solid rgba(240,246,252,0.1); cursor: pointer; text-decoration: none; white-space: nowrap; transition: all .15s; }
.btn:hover { text-decoration: none; }
.btn-primary { background: #238636; color: #fff; }
.btn-primary:hover { background: #2ea043; }
.btn-default { background: #21262d; color: #c9d1d9; }
.btn-default:hover { background: #30363d; }
.container { max-width: 1100px; margin: 0 auto; padding: 24px 32px; }
.page-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 24px; gap: 16px; flex-wrap: wrap; }
.page-title { font-size: 24px; font-weight: 600; margin: 0 0 4px; color: #e6edf3; }
.page-subtitle { color: #8b949e; font-size: 14px; margin: 0; }
.filters { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 24px; padding: 16px; background: #161b22; border: 1px solid #30363d; border-radius: 6px; }
.filter-chip { display: inline-flex; align-items: center; padding: 4px 10px; font-size: 12px; font-weight: 500; border-radius: 2em; border: 1px solid #30363d; background: #21262d; color: #c9d1d9; cursor: pointer; }
.filter-chip:hover { background: #30363d; text-decoration: none; color: #e6edf3; }
.filter-chip.active { background: #1f6feb; border-color: #1f6feb; color: #fff; }
.filter-chip.active:hover { background: #388bfd; color: #fff; }
.filter-chip-clear { border-color: #f85149; color: #f85149; background: transparent; }
.filter-chip-clear:hover { background: rgba(248,81,73,0.1); color: #f85149; }
.filter-label { color: #8b949e; font-size: 12px; font-weight: 600; text-transform: uppercase; align-self: center; margin-right: 6px; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 16px; }
.ws-card { background: #161b22; border: 1px solid #30363d; border-radius: 6px; overflow: hidden; display: flex; flex-direction: column; transition: border-color .15s; }
.ws-card:hover { border-color: #58a6ff; }
.ws-card-cover { height: 120px; background: linear-gradient(135deg, #1f6feb 0%, #a371f7 100%); display: flex; align-items: center; justify-content: center; font-size: 2.5rem; position: relative; }
.ws-card-cover-partner { position: absolute; top: 8px; right: 10px; background: rgba(0,0,0,0.5); color: #fff; padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 600; }
.ws-card-body { padding: 16px; flex: 1; display: flex; flex-direction: column; }
.ws-card h3 { color: #e6edf3; font-size: 16px; font-weight: 600; margin: 0 0 8px; line-height: 1.4; }
.ws-card h3 a { color: inherit; }
.ws-card h3 a:hover { color: #58a6ff; text-decoration: none; }
.ws-card-desc { color: #8b949e; font-size: 13px; line-height: 1.5; margin: 0 0 12px; flex: 1; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.badges { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; }
.badge { display: inline-block; padding: 0 7px; border-radius: 2em; font-size: 12px; font-weight: 500; line-height: 18px; border: 1px solid; white-space: nowrap; }
.badge-cat-soldering { background: rgba(88,166,255,0.15); color: #58a6ff; border-color: rgba(88,166,255,0.4); }
.badge-cat-embroidery { background: rgba(163,113,247,0.15); color: #a371f7; border-color: rgba(163,113,247,0.4); }
.badge-cat-embedded { background: rgba(63,185,80,0.15); color: #3fb950; border-color: rgba(63,185,80,0.4); }
.badge-cat-linux { background: rgba(210,153,34,0.15); color: #d29922; border-color: rgba(210,153,34,0.4); }
.badge-cat-other { background: rgba(139,148,158,0.15); color: #8b949e; border-color: rgba(139,148,158,0.4); }
.badge-status-draft { background: rgba(139,148,158,0.15); color: #8b949e; border-color: rgba(139,148,158,0.4); }
.badge-status-published { background: rgba(63,185,80,0.15); color: #3fb950; border-color: rgba(63,185,80,0.4); }
.badge-status-archived { background: rgba(210,153,34,0.15); color: #d29922; border-color: rgba(210,153,34,0.4); }
.badge-vis-internal { background: rgba(248,81,73,0.15); color: #f85149; border-color: rgba(248,81,73,0.4); }
.badge-difficulty-beginner { background: rgba(63,185,80,0.15); color: #3fb950; border-color: rgba(63,185,80,0.4); }
.badge-difficulty-intermediate { background: rgba(210,153,34,0.15); color: #d29922; border-color: rgba(210,153,34,0.4); }
.badge-difficulty-advanced { background: rgba(248,81,73,0.15); color: #f85149; border-color: rgba(248,81,73,0.4); }
.ws-meta { display: flex; flex-wrap: wrap; gap: 12px; font-size: 12px; color: #8b949e; padding-top: 12px; border-top: 1px solid #30363d; margin-top: auto; }
.ws-meta-item { display: inline-flex; align-items: center; gap: 4px; }
.empty-state { padding: 64px 24px; text-align: center; color: #8b949e; background: #161b22; border: 1px dashed #30363d; border-radius: 6px; }
.empty-state h3 { color: #e6edf3; margin-bottom: 8px; font-size: 16px; }
.empty-state p { margin-bottom: 16px; }
@media (max-width: 768px) {
.topnav { padding: 12px 16px; gap: 12px; }
.topnav-links { display: none; }
.topnav-actions { padding-right: 60px; }
.container { padding: 16px; }
.page-title { font-size: 20px; }
.grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<nav class="topnav">
<a href="/" class="topnav-logo">
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15h-2v-6h2v6zm0-8h-2V7h2v2zm4 8h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>
Morozovka Platform
</a>
<div class="topnav-links">
<a href="/schedule">Расписание</a>
<a href="/projects">Проекты</a>
<a href="/workshops" style="color:#58a6ff;">Мастер-классы</a>
<a href="/files">Файлы</a>
</div>
<div class="topnav-actions">
{% if session.get('username') %}
<a href="/profile" class="btn btn-default">{{ session.get('username') }}</a>
{% else %}
<a href="/login" class="btn btn-default">Войти</a>
{% endif %}
</div>
</nav>
<div class="container">
<div class="page-header">
<div>
<h1 class="page-title">Мастер-классы</h1>
<p class="page-subtitle">Практические занятия: пайка, embedded, Linux, творчество</p>
</div>
{% if can_create %}
<a href="/workshops/new" class="btn btn-primary">
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><path d="M8 2a.75.75 0 0 1 .75.75v4.5h4.5a.75.75 0 0 1 0 1.5h-4.5v4.5a.75.75 0 0 1-1.5 0v-4.5h-4.5a.75.75 0 0 1 0-1.5h4.5v-4.5A.75.75 0 0 1 8 2Z"/></svg>
Создать мастер-класс
</a>
{% endif %}
</div>
<div class="filters">
<span class="filter-label">Категория:</span>
<a href="?{% if current_status %}status={{ current_status }}&{% endif %}category=" class="filter-chip {% if not current_cat %}active{% endif %}">Все</a>
{% for cat in WORKSHOP_CATEGORIES %}
<a href="?{% if current_status %}status={{ current_status }}&{% endif %}category={{ cat }}" class="filter-chip {% if current_cat == cat %}active{% endif %}">{{ WORKSHOP_CATEGORY_LABELS[cat] }}</a>
{% endfor %}
<span style="width:16px;"></span>
<span class="filter-label">Статус:</span>
<a href="?{% if current_cat %}category={{ current_cat }}&{% endif %}status=" class="filter-chip {% if not current_status %}active{% endif %}">Все</a>
<a href="?{% if current_cat %}category={{ current_cat }}&{% endif %}status=published" class="filter-chip {% if current_status == 'published' %}active{% endif %}">Опубликован</a>
<a href="?{% if current_cat %}category={{ current_cat }}&{% endif %}status=archived" class="filter-chip {% if current_status == 'archived' %}active{% endif %}">В архиве</a>
{% if session.get('role') in ['admin', 'professor', 'lead'] %}
<a href="?{% if current_cat %}category={{ current_cat }}&{% endif %}status=draft" class="filter-chip {% if current_status == 'draft' %}active{% endif %}">Черновик</a>
{% endif %}
{% if current_cat or current_status %}
<a href="/workshops" class="filter-chip filter-chip-clear">✕ Сбросить</a>
{% endif %}
</div>
{% if workshops %}
<div class="grid">
{% for ws in workshops %}
<div class="ws-card">
<div class="ws-card-cover">
{% if ws.category == 'soldering' %}🔥
{% elif ws.category == 'embroidery' %}🎨
{% elif ws.category == 'embedded' %}⚙️
{% elif ws.category == 'linux' %}🐧
{% else %}🎓{% endif %}
{% if ws.partner %}<span class="ws-card-cover-partner">{{ ws.partner }}</span>{% endif %}
</div>
<div class="ws-card-body">
<div class="badges">
<span class="badge badge-cat-{{ ws.category }}">{{ WORKSHOP_CATEGORY_LABELS[ws.category] }}</span>
<span class="badge badge-difficulty-{{ ws.difficulty }}">{{ WORKSHOP_DIFFICULTY_LABELS[ws.difficulty] }}</span>
<span class="badge badge-status-{{ ws.status }}">{{ WORKSHOP_STATUS_LABELS[ws.status] }}</span>
{% if ws.visibility == 'internal' %}<span class="badge badge-vis-internal">Внутренний</span>{% endif %}
</div>
<h3><a href="/workshops/{{ ws.id }}">{{ ws.title }}</a></h3>
<p class="ws-card-desc">{{ ws.description or 'Без описания' }}</p>
<div class="ws-meta">
{% set lead = leads_map.get(ws.lead_id) %}
{% if lead %}<span class="ws-meta-item">👤 {{ lead.full_name or lead.username }}</span>{% endif %}
{% set sess = next_sessions.get(ws.id) %}
{% if sess %}
<span class="ws-meta-item">📅 {{ sess.start_time|msk_strftime('%d.%m %H:%M') }}–{{ sess.end_time|msk_strftime('%H:%M') }}</span>
{% endif %}
<span class="ws-meta-item">🎫 {{ reg_counts.get(ws.id, 0) }}{% if ws.max_participants %}/{{ ws.max_participants }}{% endif %}</span>
{% if ws.location %}<span class="ws-meta-item">📍 {{ ws.location }}</span>{% endif %}
{% if ws.has_certificate %}<span class="ws-meta-item">🏅 Сертификат</span>{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state">
<h3>Пока нет мастер-классов</h3>
<p>{% if can_create %}Создайте первый — он появится здесь.{% else %}Загляните позже.{% endif %}</p>
{% if can_create %}<a href="/workshops/new" class="btn btn-primary">Создать мастер-класс</a>{% endif %}
</div>
{% endif %}
</div>
<script src="/static/notif_bell.js"></script>
</body>
</html>