morozovka-portal/app/templates/dashboard.html

460 lines
14 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.

{% extends "base.html" %}
{% block title %}Дашборд — Morozovka Portal{% endblock %}
{% block main_class %}container{% endblock %}
{% block head %}
<style>
.dashboard-hero {
background: linear-gradient(135deg, #161b22 0%, #1c2128 100%);
border: 1px solid #30363d;
border-radius: 12px;
padding: 24px 32px;
margin-bottom: 24px;
}
.dashboard-hero h1 { margin: 0 0 6px; font-size: 24px; }
.dashboard-hero .greeting { color: #8b949e; font-size: 14px; }
.widgets {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
gap: 16px;
}
.widget {
background: #161b22;
border: 1px solid #30363d;
border-radius: 10px;
padding: 0;
overflow: hidden;
display: flex;
flex-direction: column;
}
.widget-header {
padding: 14px 18px;
border-bottom: 1px solid #30363d;
display: flex;
justify-content: space-between;
align-items: center;
background: #1c2128;
}
.widget-header h3 {
margin: 0;
font-size: 14px;
font-weight: 600;
color: #e6edf3;
display: flex;
align-items: center;
gap: 8px;
}
.widget-badge {
background: #30363d;
color: #e6edf3;
padding: 1px 8px;
border-radius: 10px;
font-size: 11px;
font-weight: 600;
}
.widget-badge.alert { background: #da3633; color: #fff; }
.widget-header a {
color: #58a6ff;
font-size: 12px;
text-decoration: none;
}
.widget-header a:hover { text-decoration: underline; }
.widget-body { padding: 8px 18px; flex: 1; }
.widget-empty {
padding: 24px 18px;
color: #8b949e;
font-size: 13px;
text-align: center;
}
/* Event item */
.dash-event {
display: flex;
gap: 12px;
padding: 8px 0;
border-bottom: 1px solid #21262d;
}
.dash-event:last-child { border-bottom: none; }
.dash-event-time {
color: #58a6ff;
font-weight: 600;
font-size: 13px;
min-width: 90px;
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}
.dash-event-body { flex: 1; min-width: 0; }
.dash-event-title {
color: #e6edf3;
font-weight: 500;
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.dash-event-meta { color: #8b949e; font-size: 11px; margin-top: 2px; }
/* Task item */
.dash-task {
display: flex;
gap: 10px;
align-items: flex-start;
padding: 8px 0;
border-bottom: 1px solid #21262d;
}
.dash-task:last-child { border-bottom: none; }
.dash-task-priority {
width: 4px;
height: 32px;
border-radius: 2px;
flex-shrink: 0;
}
.dash-task-priority.critical { background: #f85149; }
.dash-task-priority.high { background: #d29922; }
.dash-task-priority.medium { background: #58a6ff; }
.dash-task-priority.low { background: #8b949e; }
.dash-task-body { flex: 1; min-width: 0; }
.dash-task-title {
color: #e6edf3;
font-weight: 500;
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-decoration: none;
display: block;
}
.dash-task-title:hover { color: #58a6ff; text-decoration: none; }
.dash-task-meta { color: #8b949e; font-size: 11px; margin-top: 2px; }
/* Project item */
.dash-project {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
padding: 8px 0;
border-bottom: 1px solid #21262d;
text-decoration: none;
}
.dash-project:last-child { border-bottom: none; }
.dash-project:hover { text-decoration: none; }
.dash-project:hover .dash-project-title { color: #58a6ff; }
.dash-project-info { flex: 1; min-width: 0; }
.dash-project-title {
color: #e6edf3;
font-weight: 500;
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.dash-project-meta { color: #8b949e; font-size: 11px; margin-top: 2px; }
.dash-project-role {
font-size: 10px;
padding: 2px 8px;
border-radius: 10px;
font-weight: 600;
text-transform: uppercase;
flex-shrink: 0;
background: #30363d;
color: #c9d1d9;
}
/* Notification item */
.dash-notif {
display: block;
padding: 8px 0;
border-bottom: 1px solid #21262d;
text-decoration: none;
color: inherit;
}
.dash-notif:last-child { border-bottom: none; }
.dash-notif:hover { text-decoration: none; }
.dash-notif:hover .dash-notif-title { color: #58a6ff; }
.dash-notif-title { color: #e6edf3; font-size: 13px; font-weight: 500; }
.dash-notif-body { color: #8b949e; font-size: 12px; margin-top: 2px; }
.dash-notif-time { color: #6e7681; font-size: 11px; margin-top: 2px; }
/* Material item */
.dash-material {
display: flex;
gap: 10px;
align-items: flex-start;
padding: 8px 0;
border-bottom: 1px solid #21262d;
}
.dash-material:last-child { border-bottom: none; }
.dash-material-icon { font-size: 18px; flex-shrink: 0; }
.dash-material-body { flex: 1; min-width: 0; }
.dash-material-title { color: #e6edf3; font-size: 13px; font-weight: 500; }
.dash-material-title a { color: inherit; text-decoration: none; }
.dash-material-title a:hover { color: #58a6ff; text-decoration: none; }
.dash-material-meta { color: #8b949e; font-size: 11px; margin-top: 2px; }
/* Quick links */
.quick-links {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
gap: 10px;
padding: 16px;
}
.quick-link {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
padding: 14px 8px;
background: #0d1117;
border: 1px solid #30363d;
border-radius: 8px;
color: #e6edf3;
text-decoration: none;
transition: all .15s;
font-size: 12px;
text-align: center;
}
.quick-link:hover {
border-color: #58a6ff;
background: #161b22;
text-decoration: none;
color: #58a6ff;
}
.quick-link-icon { font-size: 22px; }
/* Commit item */
.dash-commit-repo {
font-size: 11px;
font-weight: 700;
color: #58a6ff;
padding: 4px 0;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.dash-commit-repo:not(:first-child) {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #21262d;
}
.dash-commit { padding: 4px 0; }
.dash-commit-sha {
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
font-size: 11px;
color: #58a6ff;
background: rgba(88,166,255,0.1);
padding: 1px 6px;
border-radius: 3px;
margin-right: 4px;
}
.dash-commit-msg { color: #c9d1d9; font-size: 12px; }
.dash-commit-meta { color: #8b949e; font-size: 11px; margin-top: 2px; margin-left: 2px; }
@media (max-width: 768px) {
.dashboard-hero { padding: 16px 20px; }
.dashboard-hero h1 { font-size: 20px; }
.widgets { grid-template-columns: 1fr; }
}
</style>
{% endblock %}
{% block content %}
<div class="dashboard-hero">
<h1>Привет, {{ session.get('username') }}!</h1>
<div class="greeting">Твой персональный дашборд</div>
</div>
<div class="widgets">
<!-- 1. Ближайшие события -->
<div class="widget">
<div class="widget-header">
<h3>📅 Ближайшие события</h3>
<a href="/schedule">Все →</a>
</div>
{% if upcoming_events %}
<div class="widget-body">
{% for ev in upcoming_events %}
<div class="dash-event">
<div class="dash-event-time">{{ ev.start_time|msk_strftime('%d.%m %H:%M') }}</div>
<div class="dash-event-body">
<div class="dash-event-title">{{ ev.title }}</div>
{% if ev.location %}
<div class="dash-event-meta">📍 {{ ev.location }}</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="widget-empty">Ближайших событий нет</div>
{% endif %}
</div>
<!-- 2. Мои проекты -->
<div class="widget">
<div class="widget-header">
<h3>🗂 Мои проекты <span class="widget-badge">{{ my_projects|length }}</span></h3>
<a href="/projects">Все →</a>
</div>
{% if my_projects %}
<div class="widget-body">
{% for proj, member in my_projects %}
<a class="dash-project" href="/projects/{{ proj.id }}">
<div class="dash-project-info">
<div class="dash-project-title">{{ proj.title }}</div>
<div class="dash-project-meta">
{% if proj.status == 'idea' %}Идея
{% elif proj.status == 'in_progress' %}В работе
{% elif proj.status == 'completed' %}Завершён
{% else %}Заморожен{% endif %}
</div>
</div>
<span class="dash-project-role">{{ member.role }}</span>
</a>
{% endfor %}
</div>
{% else %}
<div class="widget-empty">Вы пока не в проектах</div>
{% endif %}
</div>
<!-- 3. Мои задачи -->
<div class="widget">
<div class="widget-header">
<h3>✅ Мои задачи <span class="widget-badge">{{ my_tasks|length }}</span></h3>
<a href="/projects">Все →</a>
</div>
{% if my_tasks %}
<div class="widget-body">
{% for task, proj in my_tasks %}
<div class="dash-task">
<div class="dash-task-priority {{ task.priority }}"></div>
<div class="dash-task-body">
<a class="dash-task-title" href="/projects/{{ proj.id }}/board">{{ task.title }}</a>
<div class="dash-task-meta">
{{ proj.title }}
{% if task.due_date %} · до {{ task.due_date|date_ru }}{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="widget-empty">Активных задач нет</div>
{% endif %}
</div>
<!-- 4. Непрочитанные уведомления -->
<div class="widget">
<div class="widget-header">
<h3>🔔 Уведомления {% if unread_count > 0 %}<span class="widget-badge alert">{{ unread_count }}</span>{% endif %}</h3>
<a href="/notifications">Все →</a>
</div>
{% if unread %}
<div class="widget-body">
{% for n in unread %}
<a class="dash-notif" href="{{ n.link_url or '/notifications' }}">
<div class="dash-notif-title">{{ n.title }}</div>
{% if n.body %}<div class="dash-notif-body">{{ n.body }}</div>{% endif %}
<div class="dash-notif-time">{{ n.created_at|msk_strftime('%d.%m.%Y %H:%M') }}</div>
</a>
{% endfor %}
</div>
{% else %}
<div class="widget-empty">Нет непрочитанных уведомлений</div>
{% endif %}
</div>
<!-- 5. Новые материалы МК -->
<div class="widget">
<div class="widget-header">
<h3>📚 Материалы МК <span class="widget-badge">{{ new_materials|length }}</span></h3>
<a href="/workshops">Все →</a>
</div>
{% if new_materials %}
<div class="widget-body">
{% for material, ws in new_materials %}
<div class="dash-material">
<div class="dash-material-icon">
{% if material.external_url %}🔗{% else %}📎{% endif %}
</div>
<div class="dash-material-body">
<div class="dash-material-title">
{% if material.external_url %}
<a href="{{ material.external_url }}" target="_blank">{{ material.title }}</a>
{% elif material.file_id %}
<a href="/files/download/{{ material.file_id }}">{{ material.title }}</a>
{% else %}
{{ material.title }}
{% endif %}
</div>
<div class="dash-material-meta">{{ ws.title }}</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="widget-empty">Пока нет новых материалов</div>
{% endif %}
</div>
<!-- 6. Gitea-коммиты (только если есть данные) -->
{% if gitea_widget %}
<div class="widget">
<div class="widget-header">
<h3>💻 Последние коммиты</h3>
<a href="/projects">Все →</a>
</div>
<div class="widget-body">
{% for repo in gitea_widget %}
<div class="dash-commit-repo">{{ repo.project_title }}</div>
{% for c in repo.commits %}
<div class="dash-commit">
<span class="dash-commit-sha">{{ c.sha_short }}</span>
<span class="dash-commit-msg">{{ c.message }}</span>
<div class="dash-commit-meta">{{ c.author }}{% if c.date %} · {{ c.date[:16].replace('T', ' ') }}{% endif %}</div>
</div>
{% endfor %}
{% endfor %}
</div>
</div>
{% endif %}
<!-- 7. Быстрые ссылки -->
<div class="widget">
<div class="widget-header">
<h3>⚡ Быстрые ссылки</h3>
</div>
<div class="quick-links">
<a href="/schedule" class="quick-link">
<span class="quick-link-icon">📅</span>
Расписание
</a>
<a href="/projects" class="quick-link">
<span class="quick-link-icon">🗂</span>
Проекты
</a>
<a href="/workshops" class="quick-link">
<span class="quick-link-icon">🎓</span>
Мастер-классы
</a>
<a href="/feed" class="quick-link">
<span class="quick-link-icon">📰</span>
Лента
</a>
<a href="/files" class="quick-link">
<span class="quick-link-icon">📁</span>
Файлы
</a>
<a href="/onboarding" class="quick-link">
<span class="quick-link-icon">🎯</span>
Тур
</a>
</div>
</div>
</div>
{% endblock %}