Update UI and migrations, fix dockerfile

This commit is contained in:
Your Name
2026-07-18 21:29:05 +05:00
parent 580c75cbf6
commit 26ef145ccf
95 changed files with 9541 additions and 111 deletions
+4
View File
@@ -0,0 +1,4 @@
{% extends "base.html" %}
{% block content %}
<section class="not-found"><p>404</p><h1>Страница не найдена</h1><a href="/">Вернуться в ленту</a></section>
{% endblock %}
@@ -0,0 +1,29 @@
{% macro media(article, eager=false) %}
{% set item = article.media[0] if article.media else none %}
{% if item and item.type == 'image' %}
<img src="{{ item.url }}" alt="{{ item.alt or article.title }}" {% if item.width %}width="{{ item.width }}"{% endif %} {% if item.height %}height="{{ item.height }}"{% endif %} loading="{{ 'eager' if eager else 'lazy' }}" decoding="async">
{% elif item and item.type == 'video' %}
<video src="{{ item.url }}" muted playsinline preload="metadata" aria-label="{{ item.alt or article.title }}"></video>
{% else %}
<span class="media-placeholder"><img src="/static/favi.png" alt="" width="96" height="96"></span>
{% endif %}
{% endmacro %}
{% macro meta(article) %}
<div class="article-meta">
<time datetime="{{ article.published_at.isoformat() }}">{{ article.published_at|ekb_date }} · {{ article.published_at|ekb_time }}</time>
<a class="category-link" href="/category/{{ article.category_tag }}">{{ article.category_name }}</a>
<a class="producer-link" href="/brand/{{ article.producer_tag }}">{{ article.producer_name }}</a>
</div>
{% endmacro %}
{% macro row(article) %}
<article class="feed-row">
<a class="feed-row-media" href="{{ article_path(article) }}" tabindex="-1">{{ media(article) }}</a>
<div class="feed-row-copy">
{{ meta(article) }}
<h2><a href="{{ article_path(article) }}">{{ article.title }}</a></h2>
<p>{{ article.lead or article.body|excerpt(210) }}</p>
</div>
</article>
{% endmacro %}
+7
View File
@@ -0,0 +1,7 @@
{% if pages > 1 %}
<nav class="pagination" aria-label="Страницы ленты">
{% if page > 1 %}<a rel="prev" href="?page={{ page - 1 }}{% if q %}&q={{ q|urlencode }}{% endif %}">← Новее</a>{% else %}<span></span>{% endif %}
<span>{{ page }} / {{ pages }}</span>
{% if page < pages %}<a rel="next" href="?page={{ page + 1 }}{% if q %}&q={{ q|urlencode }}{% endif %}">Старее →</a>{% endif %}
</nav>
{% endif %}
+38
View File
@@ -0,0 +1,38 @@
{% extends "base.html" %}
{% from "_article_rows.html" import media, row %}
{% block og_type %}article{% endblock %}
{% block content %}
<article class="article-page">
<header class="article-header">
<div class="article-breadcrumbs"><a href="/">Лента</a><span>/</span><a href="/category/{{ article.category_tag }}">{{ article.category_name }}</a></div>
<h1>{{ article.title }}</h1>
{% if article.lead %}<p class="article-lead">{{ article.lead }}</p>{% endif %}
<div class="article-byline">
<time datetime="{{ article.published_at.isoformat() }}">{{ article.published_at|ekb_date }} · {{ article.published_at|ekb_time }} ЕКБ</time>
<a href="/brand/{{ article.producer_tag }}">{{ article.producer_name }}</a>
</div>
</header>
{% if article.media %}
<div class="article-media">
{% for item in article.media %}
{% if item.type == 'image' %}<figure><img src="{{ item.url }}" alt="{{ item.alt or article.title }}" loading="{{ 'eager' if loop.first else 'lazy' }}" decoding="async"></figure>{% endif %}
{% if item.type == 'video' %}<figure><video src="{{ item.url }}" controls playsinline preload="metadata"></video></figure>{% endif %}
{% endfor %}
</div>
{% endif %}
<div class="article-layout">
<div class="article-body">{{ article.body|paragraphs }}</div>
<aside class="article-facts">
<div><span>Категория</span><a href="/category/{{ article.category_tag }}">{{ article.category_name }}</a></div>
<div><span>Производитель</span><a href="/brand/{{ article.producer_tag }}">{{ article.producer_name }}</a></div>
{% if article.source_url %}<a class="source-link" href="{{ article.source_url }}" rel="nofollow noopener" target="_blank">Первоисточник ↗</a>{% endif %}
</aside>
</div>
</article>
{% if related %}
<section class="related-section">
<h2>По теме</h2>
<div class="feed-list">{% for item in related %}{{ row(item) }}{% endfor %}</div>
</section>
{% endif %}
{% endblock %}
+67
View File
@@ -0,0 +1,67 @@
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ meta_title }}</title>
<meta name="description" content="{{ meta_description }}">
{% if noindex %}<meta name="robots" content="noindex,follow">{% else %}<meta name="robots" content="index,follow,max-image-preview:large">{% endif %}
<link rel="canonical" href="{{ canonical_url }}">
<link rel="alternate" type="application/rss+xml" title="Forma N8 RSS" href="{{ site_url('/rss.xml') }}">
<link rel="icon" type="image/png" href="/static/favi.png">
<meta property="og:site_name" content="Forma N8">
<meta property="og:locale" content="ru_RU">
<meta property="og:type" content="{% block og_type %}website{% endblock %}">
<meta property="og:title" content="{{ meta_title }}">
<meta property="og:description" content="{{ meta_description }}">
<meta property="og:url" content="{{ canonical_url }}">
{% if og_image %}<meta property="og:image" content="{{ og_image }}">{% endif %}
<link rel="stylesheet" href="/static/site.css?v=1">
{% if json_ld %}<script type="application/ld+json">{{ json_ld|safe }}</script>{% endif %}
</head>
<body>
<a class="skip-link" href="#content">К содержанию</a>
<header class="site-header">
<div class="header-inner">
<a class="brand" href="/" aria-label="Forma N8, главная">
<img src="/static/favi.png" width="44" height="44" alt="">
<span>FORMA<br>N8</span>
</a>
<nav class="main-nav" id="main-nav" aria-label="Основная навигация">
<a href="/" {% if current_path == '/' %}aria-current="page"{% endif %}>Лента</a>
<a href="/category/snaryazhenie">Снаряжение</a>
<a href="/category/odezhda">Одежда</a>
<a href="/category/bronya">Броня</a>
<a href="/category/aksessuary">Аксессуары</a>
</nav>
<div class="header-actions">
<button class="icon-button" type="button" data-search-toggle aria-label="Открыть поиск" title="Поиск">
<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="6.5"></circle><path d="m16 16 4 4"></path></svg>
</button>
<button class="icon-button menu-button" type="button" data-menu-toggle aria-expanded="false" aria-controls="main-nav" aria-label="Открыть меню" title="Меню">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"></path></svg>
</button>
</div>
</div>
<form class="search-form" action="/" method="get" data-search-form {% if not q %}hidden{% endif %}>
<label for="site-search">Поиск по новостям</label>
<div>
<input id="site-search" name="q" value="{{ q or '' }}" type="search" maxlength="100" placeholder="Название, производитель, снаряжение">
<button type="submit">Найти</button>
</div>
</form>
</header>
<main id="content">{% block content %}{% endblock %}</main>
<footer class="site-footer">
<div>
<a class="footer-brand" href="/">FORMA N8</a>
<p>Новости снаряжения и экипировки.</p>
</div>
<nav aria-label="Служебная навигация">
<a href="/rss.xml">RSS</a>
<a href="/sitemap.xml">Sitemap</a>
</nav>
</footer>
<script src="/static/site.js?v=1" defer></script>
</body>
</html>
+34
View File
@@ -0,0 +1,34 @@
{% extends "base.html" %}
{% from "_article_rows.html" import media, meta, row %}
{% block content %}
<section class="feed-shell">
<header class="page-heading">
<h1>Новости снаряжения</h1>
{% if q %}<p>Результаты поиска: «{{ q }}»</p>{% endif %}
</header>
{% if articles %}
{% if page == 1 and not q %}
{% set lead = articles[0] %}
<article class="lead-story">
<a class="lead-media" href="{{ article_path(lead) }}">{{ media(lead, true) }}</a>
<div class="lead-copy">
{{ meta(lead) }}
<h2><a href="{{ article_path(lead) }}">{{ lead.title }}</a></h2>
<p>{{ lead.lead or lead.body|excerpt(340) }}</p>
<a class="read-link" href="{{ article_path(lead) }}">Читать новость <span aria-hidden="true"></span></a>
</div>
</article>
<div class="feed-list">
{% for article in articles[1:] %}{{ row(article) }}{% endfor %}
</div>
{% else %}
<div class="feed-list feed-list-top">
{% for article in articles %}{{ row(article) }}{% endfor %}
</div>
{% endif %}
{% include "_pagination.html" %}
{% else %}
<div class="empty-state"><h2>Ничего не найдено</h2><p>Попробуйте изменить запрос или вернуться к ленте.</p><a href="/">Вернуться в ленту</a></div>
{% endif %}
</section>
{% endblock %}
+18
View File
@@ -0,0 +1,18 @@
{% extends "base.html" %}
{% from "_article_rows.html" import row %}
{% block content %}
<section class="feed-shell taxonomy-shell">
<header class="page-heading taxonomy-heading">
<p>{{ taxonomy_label }}</p>
<h1>{{ heading }}</h1>
</header>
{% if articles %}
<div class="feed-list feed-list-top">
{% for article in articles %}{{ row(article) }}{% endfor %}
</div>
{% include "_pagination.html" %}
{% else %}
<div class="empty-state"><h2>Публикаций пока нет</h2><p>Новые материалы появятся здесь после публикации.</p><a href="/">Вернуться в ленту</a></div>
{% endif %}
</section>
{% endblock %}