Initial commit

This commit is contained in:
ThePetrovich 2026-08-17 22:42:04 +08:00
commit 97e799fd52
61 changed files with 2252 additions and 0 deletions

View file

@ -0,0 +1,170 @@
{% load static i18n %}{% comment %}
The estate skeleton. Every service's own base.html extends this and fills the
blocks; nothing here is meant to be copied into a service.
The navbar is written out here rather than {% include %}d because blocks inside
an included template cannot be overridden — and the navbar's layout has to be
identical everywhere while its items differ per service. Same for the footer's
position. Fill `navbar_items` and `navbar_tools`; change nothing else.
Blocks a service normally fills:
title, meta_description, meta_keywords the head
brand_url where the logo links
navbar_items the left-hand <li> list
navbar_tools extra right-hand items (auth, duty)
main_class a density scope, e.g. "ops"
content the page
extra_head, extra_js per-page assets
{% endcomment %}
<!DOCTYPE html>
<html lang="{{ request.LANGUAGE_CODE }}">
<head>
<meta charset="UTF-8">
<link rel="shortcut icon" href="{% static 'yksa/favicon.ico' %}" type="image/x-icon">
<meta name="description"
content="{% block meta_description %}YKSA{% endblock meta_description %}">
<meta name="keywords"
content="{% block meta_keywords %}YKSA, TMTC{% endblock meta_keywords %}">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
{% block title %}{% trans "YKSA" %}{% endblock title %}
&mdash; {% block title_suffix %}YKSA{% endblock title_suffix %}</title>
<link rel="stylesheet" href="{% static 'yksa/css/bootstrap.min.css' %}">
{% comment %}
Icons load here, not per page. Loading them in extra_head is why icons
rendered on some pages and not others in two services.
{% endcomment %}
<link rel="stylesheet" href="{% static 'yksa/css/bootstrap-icons.min.css' %}">
<link rel="stylesheet" href="{% static 'yksa/css/kit.css' %}">
{% block extra_head %}{% endblock extra_head %}
</head>
<body class="d-flex flex-column min-vh-100 bg-light">
<nav class="navbar navbar-expand-lg custom-navbar border-bottom sticky-top">
<div class="{% block navbar_container_class %}container{% endblock navbar_container_class %}">
<a class="navbar-brand nav-full-height" href="{% block brand_url %}/{% endblock brand_url %}">
{% if request.LANGUAGE_CODE == 'en' %}
<img src="{% static 'yksa/img/logo-full-en.svg' %}"
alt="YKSA"
height="34"
class="d-inline-block align-text-top" />
{% else %}
<img src="{% static 'yksa/img/logo-full-ru.svg' %}"
alt="ЯКС"
height="34"
class="d-inline-block align-text-top" />
{% endif %}
</a>
<button class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarContent"
aria-controls="navbarContent"
aria-expanded="false"
aria-label="{% trans 'Toggle navigation' %}">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarContent">
<ul class="navbar-nav me-auto mb-lg-0">
{% block navbar_items %}{% endblock navbar_items %}
</ul>
{% block navbar_tools %}{% endblock navbar_tools %}
<ul class="navbar-nav align-items-lg-center">
<li class="nav-item dropdown">
<a class="nav-link nav-full-height border border-top-0 dropdown-toggle d-flex align-items-center"
href="#"
id="tzMenu"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false"
title="{% trans 'Display time zone' %}">
<i class="bi bi-clock me-1" aria-hidden="true"></i>{{ current_timezone_name }}
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="tzMenu">
{% for tz in common_timezones %}
<li>
<form action="{% url 'timezone-preferences' %}" method="post" class="m-0">
{% csrf_token %}
<input type="hidden" name="next" value="{{ request.get_full_path }}">
<input type="hidden" name="timezone" value="{{ tz }}">
<button type="submit"
class="dropdown-item {% if tz == current_timezone_name %}active{% endif %}">{{ tz }}</button>
</form>
</li>
{% endfor %}
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item"
href="{% url 'timezone-preferences' %}?next={{ request.get_full_path|urlencode }}">{% trans "More time zones…" %}</a>
</li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link nav-full-height border border-top-0 dropdown-toggle d-flex align-items-center"
href="#"
id="localeMenu"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false"
aria-label="{% trans 'Language' %}">
{% if request.LANGUAGE_CODE == 'ru' %}
<img src="{% static 'yksa/img/lang/flag-ru.svg' %}" alt="RU" width="20" />
{% else %}
<img src="{% static 'yksa/img/lang/flag-en.svg' %}" alt="EN" width="20" />
{% endif %}
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="localeMenu">
<li>
<form action="{% url 'set_language' %}" method="post" class="m-0">
{% csrf_token %}
<input type="hidden" name="next" value="{{ request.get_full_path }}" />
<input type="hidden" name="language" value="ru" />
<button type="submit"
class="dropdown-item d-flex align-items-center {% if request.LANGUAGE_CODE == 'ru' %}active{% endif %}">
<img src="{% static 'yksa/img/lang/flag-ru.svg' %}"
alt=""
width="20"
class="me-2" />
Русский
</button>
</form>
</li>
<li>
<form action="{% url 'set_language' %}" method="post" class="m-0">
{% csrf_token %}
<input type="hidden" name="next" value="{{ request.get_full_path }}" />
<input type="hidden" name="language" value="en" />
<button type="submit"
class="dropdown-item d-flex align-items-center {% if request.LANGUAGE_CODE == 'en' %}active{% endif %}">
<img src="{% static 'yksa/img/lang/flag-en.svg' %}"
alt=""
width="20"
class="me-2" />
English
</button>
</form>
</li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
{% include "yksa/includes/_messages.html" %}
{% block pre-content %}{% endblock pre-content %}
{% comment %}
A density scope, if the service needs one, goes on <main> and never on
<body>: the navbar and footer keep the estate's default type scale so
they stay identical across services.
{% endcomment %}
<main class="{% block main_class %}{% endblock main_class %} flex-grow-1">
{% block content %}{% endblock content %}
</main>
{% include "yksa/includes/_cookie_banner.html" %}
{% include "yksa/includes/_footer.html" %}
<script src="{% static 'yksa/src/bootstrap.bundle.min.js' %}"></script>
<script src="{% static 'yksa/src/cookie-banner.js' %}"></script>
{% comment %}Shows the toasts yksa/includes/_messages.html renders.{% endcomment %}
<script src="{% static 'yksa/src/toast.js' %}"></script>
{% block extra_js %}{% endblock extra_js %}
</body>
</html>

View file

@ -0,0 +1,18 @@
{% load i18n %}
<div id="cookie-banner" class="cookie-banner d-none" role="dialog" aria-live="polite" aria-label="Cookie consent banner">
<div class="container py-3">
<div class="row g-2 align-items-center">
<div class="col-lg-8">
<div class="fw-bold mb-1">{% trans "Cookie Notice" %}</div>
<div class="small mb-0">
{% trans "This site uses essential cookies for language and time zone preferences. You can review details in our cookie policy." %}
<a href="{% url 'cookie-policy' %}">{% trans "Read cookie policy" %}</a>.
</div>
</div>
<div class="col-lg-4 text-lg-end d-flex gap-2 justify-content-lg-end">
<button type="button" class="btn btn-outline-light btn-sm" data-cookie-consent="rejected">{% trans "Reject" %}</button>
<button type="button" class="btn btn-light btn-sm" data-cookie-consent="accepted">{% trans "Accept" %}</button>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,18 @@
{% load static i18n %}{% comment %}
Layout-identical across every YKSA service. Do not restyle here; change it in
all of them or, better, in yksa-web-kit once that exists.
{% endcomment %}
<footer class="bg-dark text-bg-dark mt-auto">
<div class="container pb-4 pt-4">
<div class="row">
<div class="col-6 small">
<div>{% blocktrans %}Copyright © 2026 Sakha Aerospace Systems, LLC{% endblocktrans %}</div>
</div>
<div class="col-6 text-end small">
<a class="text-decoration-none" href="{% url 'privacy-policy' %}">{% trans "Privacy Policy" %}</a>
&ndash;
<a class="text-decoration-none" href="{% url 'cookie-policy' %}">{% trans "Cookie Policy" %}</a>
</div>
</div>
</div>
</footer>

View file

@ -0,0 +1,26 @@
{% comment %}
Floating toast stack for Django messages. Rendered outside <main> so it never
disturbs page layout (the old top-of-main alert block used to shove the pass
page around). z-index sits above the pass action bar (1030).
{% endcomment %}
{% if messages %}
<div class="toast-container position-fixed top-0 end-0 p-3" style="z-index: 1100;">
{% for message in messages %}
{% if message.tags == 'error' %}{% with level='danger' %}
<div class="toast align-items-center text-bg-{{ level }} border-0" role="alert" aria-live="assertive" aria-atomic="true" data-bs-autohide="false">
<div class="d-flex">
<div class="toast-body">{{ message }}</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</div>
{% endwith %}{% else %}{% with level=message.tags|default:'info' %}
<div class="toast align-items-center text-bg-{{ level }} border-0" role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body">{{ message }}</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</div>
{% endwith %}{% endif %}
{% endfor %}
</div>
{% endif %}

View file

@ -0,0 +1,42 @@
{% comment %}
Reusable chip-style filter group.
Drop this partial into any list/catalog page. Multiple groups can live in the
same enclosing <form>; each one drives its own comma-separated hidden input.
Required context:
* name: string -- the group key. The hidden input MUST be present in the
parent template as
<input type="hidden" name="{{ name }}" value="..."
data-filter-csv="{{ name }}">
(kept in the parent so callers control its initial value).
* label: string -- heading shown above the chips.
* chips: iterable of {value, label, selected} dicts.
Optional context:
* empty_message: text shown when ``chips`` is empty (defaults to a generic
placeholder via {{ empty_message|default:_("No options yet.") }}).
The accompanying JS (static/src/chip-filters.js) toggles ``.filter-chip.active``
on click and re-emits the comma-separated value into the hidden input keyed by
``data-filter-csv``. Submitting the form sends the result; servers split on
comma. No XHR -- keeps URLs bookmarkable.
{% endcomment %}
{% load i18n %}
<div class="card mb-3">
<div class="card-body">
{% if label %}<h2 class="h6 mb-2">{{ label }}</h2>{% endif %}
{% if chips %}
<div class="d-flex flex-wrap gap-1" data-filter-group="{{ name }}">
{% for chip in chips %}
<span class="badge bg-secondary filter-chip {% if chip.selected %}active{% endif %}"
data-value="{{ chip.value }}">{{ chip.label }}</span>
{% endfor %}
</div>
{% else %}
<p class="small text-body-secondary mb-0">
{{ empty_message|default:_("No options yet.") }}
</p>
{% endif %}
</div>
</div>

View file

@ -0,0 +1,84 @@
<div class="container my-4">
<h1 class="h3 mb-3">Cookie Policy</h1>
<p class="text-body-secondary">Last updated: April 20, 2026</p>
<div class="card mb-3">
<div class="card-body text-content">
<p>
This Cookie Policy explains what cookies we use on qsl.yksa.space, why we use them,
and what choices you have.
</p>
<p>
For broader information about personal data processing, see our
<a href="{% url 'privacy-policy' %}">Privacy Policy</a>.
</p>
</div>
</div>
<div class="card mb-3">
<div class="card-body text-content">
<h2 class="h5" id="cookie-types">1. Cookies We Use</h2>
<p>At this time, we use essential cookies only:</p>
<ul>
<li>Language preference cookie (<b>django_language</b>): remembers selected interface language</li>
<li>Session and security cookies (<b>sessionid</b>, <b>csrftoken</b>): maintain authenticated sessions and protect forms, used for timezone settings</li>
<li>Cookie consent cookie (<b>yksa_cookie_consent</b>): stores your accept/reject choice for the banner</li>
</ul>
</div>
</div>
<div class="card mb-3">
<div class="card-body text-content">
<h2 class="h5" id="cookie-purpose">2. Why We Use Cookies</h2>
<ul>
<li>To provide core site functionality</li>
<li>To improve usability (language and time zone persistence)</li>
<li>To maintain security and prevent misuse</li>
<li>To remember your cookie consent choice</li>
</ul>
</div>
</div>
<div class="card mb-3">
<div class="card-body text-content">
<h2 class="h5" id="cookie-third-parties">3. Third-Party Cookies</h2>
<p>
We do not use any advertising or tracking cookies. If third-party integrations are added,
this policy will be updated accordingly.
</p>
</div>
</div>
<div class="card mb-3">
<div class="card-body text-content">
<h2 class="h5" id="cookie-control">4. Your Choices</h2>
<p>
You can accept or reject non-essential cookie categories when available. You can also
manage cookies via your browser settings.
</p>
<p>
Blocking essential cookies may affect service functionality.
</p>
</div>
</div>
<div class="card mb-3">
<div class="card-body text-content">
<h2 class="h5" id="cookie-updates">5. Policy Updates</h2>
<p>
We may update this Cookie Policy from time to time. The "Last updated" date reflects
the current version.
</p>
</div>
</div>
<div class="card mb-3">
<div class="card-body text-content">
<h2 class="h5" id="cookie-contact">6. Contact</h2>
<p>
For cookie-related questions, contact
<a href="mailto:ops@yksa.space">ops@yksa.space</a>.
</p>
</div>
</div>
</div>

View file

@ -0,0 +1,84 @@
<div class="container my-4">
<h1 class="h3 mb-3">Политика cookie</h1>
<p class="text-body-secondary">Последнее обновление: 20 апреля 2026</p>
<div class="card mb-3">
<div class="card-body text-content">
<p>
Эта Политика cookie описывает, какие cookie используются на qsl.yksa.space,
для чего они нужны и какие у вас есть варианты управления.
</p>
<p>
Общая информация об обработке персональных данных приведена в
<a href="{% url 'privacy-policy' %}">Политике конфиденциальности</a>.
</p>
</div>
</div>
<div class="card mb-3">
<div class="card-body text-content">
<h2 class="h5" id="cookie-types">1. Какие cookie мы используем</h2>
<p>Сейчас используются только необходимые cookie:</p>
<ul>
<li>Cookie выбора языка интерфейса (<b>django_language</b>)</li>
<li>Сессионные и защитные cookie для корректной и безопасной работы форм и сохранения настроек часового пояса (<b>sessionid</b>, <b>csrftoken</b>)</li>
<li>Cookie согласия, сохраняющий ваш выбор в баннере (<b>yksa_cookie_consent</b>)</li>
</ul>
</div>
</div>
<div class="card mb-3">
<div class="card-body text-content">
<h2 class="h5" id="cookie-purpose">2. Зачем мы используем cookie</h2>
<ul>
<li>Для базовой работоспособности сайта</li>
<li>Для удобства пользователя (язык и часовой пояс)</li>
<li>Для безопасности и предотвращения злоупотреблений</li>
<li>Для запоминания выбора по cookie-баннеру</li>
</ul>
</div>
</div>
<div class="card mb-3">
<div class="card-body text-content">
<h2 class="h5" id="cookie-third-parties">3. Сторонние cookie</h2>
<p>
Мы не используем рекламные cookie намеренно. Если появятся сторонние интеграции,
эта политика будет обновлена.
</p>
</div>
</div>
<div class="card mb-3">
<div class="card-body text-content">
<h2 class="h5" id="cookie-control">4. Ваши настройки</h2>
<p>
Для необязательных категорий (если они появятся) вы сможете выбирать согласие.
Также вы можете управлять cookie через настройки браузера.
</p>
<p>
Отключение необходимых cookie может повлиять на работу сервиса.
</p>
</div>
</div>
<div class="card mb-3">
<div class="card-body text-content">
<h2 class="h5" id="cookie-updates">5. Обновления политики</h2>
<p>
Мы можем периодически обновлять эту Политику cookie.
Дата вверху страницы показывает актуальную редакцию.
</p>
</div>
</div>
<div class="card mb-3">
<div class="card-body text-content">
<h2 class="h5" id="cookie-contact">6. Контакты</h2>
<p>
По вопросам cookie напишите на
<a href="mailto:ops@yksa.space">ops@yksa.space</a>.
</p>
</div>
</div>
</div>

View file

@ -0,0 +1,208 @@
<div class="container my-4">
<h1 class="h3 mb-3">Privacy Policy</h1>
<p class="text-body-secondary">Last updated: April 20, 2026</p>
<div class="card mb-3">
<div class="card-body text-content">
<p>
This Privacy Policy explains how YKSA collects, uses, shares, and protects personal information when you use qsl.yksa.space and related
services.
</p>
<p>
Questions about this policy can be sent to
<a href="mailto:ops@yksa.space">ops@yksa.space</a>.
</p>
</div>
</div>
<div class="card mb-3">
<div class="card-body text-content">
<h2 class="h5" id="toc">Table of Contents</h2>
<ol class="mb-0">
<li><a href="#info-collect">What Information We Collect</a></li>
<li><a href="#info-use">How We Use Information</a></li>
<li><a href="#legal-bases">Legal Bases for Processing</a></li>
<li><a href="#sharing">When We Share Information</a></li>
<li><a href="#cookies">Cookies and Similar Technologies</a></li>
<li><a href="#transfers">International Data Transfers</a></li>
<li><a href="#retention">Data Retention</a></li>
<li><a href="#security">Data Security</a></li>
<li><a href="#minors">Children and Minors</a></li>
<li><a href="#rights">Your Privacy Rights</a></li>
<li><a href="#dnt">Do Not Track</a></li>
<li><a href="#changes">Changes to This Policy</a></li>
<li><a href="#contact">Contact</a></li>
</ol>
</div>
</div>
<div class="card mb-3" id="info-collect">
<div class="card-body text-content">
<h2 class="h5">1. What Information We Collect</h2>
<p>We collect information you provide directly, including:</p>
<ul>
<li>Name</li>
<li>Email address</li>
<li>Mailing address (for physical cards, if requested)</li>
<li>Amateur radio callsign, as defined by your local amateur radio administration</li>
<li>Reception report details (UTC date/time, frequency, mode)</li>
<li>Approximate location (for example, Maidenhead grid/QTH)</li>
<li>Information entered in optional notes fields</li>
<li>Optional uploaded attachments submitted with a request</li>
</ul>
<p>
We do not intentionally collect sensitive personal information (such as health data, biometric data, or data revealing racial or ethnic origin).
</p>
</div>
</div>
<div class="card mb-3" id="info-use">
<div class="card-body text-content">
<h2 class="h5">2. How We Use Information</h2>
<p>We use personal information to operate and improve the service, including to:</p>
<ul>
<li>Receive, validate, and process QSL requests</li>
<li>Generate and deliver digital and/or physical QSL cards</li>
<li>Send request status updates and service messages</li>
<li>Provide user support and respond to inquiries</li>
<li>Prevent abuse, fraud, or misuse of our systems</li>
<li>Comply with legal obligations and enforce our terms</li>
</ul>
</div>
</div>
<div class="card mb-3" id="legal-bases">
<div class="card-body text-content">
<h2 class="h5">3. Legal Bases for Processing</h2>
<p>Depending on your location, we may process personal data under one or more of these bases:</p>
<ul>
<li>Consent (for example, when you submit a request form)</li>
<li>Performance of a contract or pre-contractual steps</li>
<li>Legitimate interests in operating, securing, and improving the service</li>
<li>Compliance with legal obligations</li>
<li>Protection of vital interests where legally permitted</li>
</ul>
</div>
</div>
<div class="card mb-3" id="sharing">
<div class="card-body text-content">
<h2 class="h5">4. When We Share Information</h2>
<p>We do not sell personal information. We may share information only when needed:</p>
<ul>
<li>With infrastructure providers (hosting, storage, and email delivery)</li>
<li>With affiliated entities operating this service under common control</li>
<li>During corporate transactions (for example, merger or acquisition)</li>
<li>To comply with law, legal process, or enforceable government requests</li>
<li>To protect rights, safety, and service integrity</li>
</ul>
<p>The following entities, in addition to YKSA, may have access to your information:</p>
<ul>
<li>Beget, LLC (https://beget.com) (Email provider)</li>
<li>
Federal State Budgetary Scientific Institution "Yu.G. Shafer Institute of Cosmophysical Research and Aeronomy" of Siberian Branch of the
Russian Academy of Sciences (Russian: ФГБУН ФИЦ «ЯНЦ СО РАН» «Институт космофизических исследований и аэрономии им. Ю.Г. Шафера» СО РАН)
(https://ikfia.ysn.ru)
</li>
<li>Sakha Science Academy (Russian: ГАНОУ РЦ РС(Я) «Малая академия наук РС(Я)») (https://lensky-kray.ru)</li>
</ul>
</div>
</div>
<div class="card mb-3" id="cookies">
<div class="card-body text-content">
<h2 class="h5">5. Cookies and Similar Technologies</h2>
<p>
We use essential cookies and similar technologies for security, session continuity, language preferences, time zone preferences, and basic site
functionality. Please see our separate <a href="{% url 'cookie-policy' %}">Cookie Policy</a> for detailed information about our cookie use and your choices.
</p>
<p>No analytics or non-essential cookies are used at this time. If such cookies are introduced in the future, we will update this policy and, where required, request your consent.</p>
</div>
</div>
<div class="card mb-3" id="transfers">
<div class="card-body text-content">
<h2 class="h5">6. International Data Transfers</h2>
<p>Your information may be processed in countries outside your own. Where applicable, we use appropriate safeguards to protect transferred data.</p>
<p>Data processing jurisdiction: Russian Federation</p>
</div>
</div>
<div class="card mb-3" id="retention">
<div class="card-body text-content">
<h2 class="h5">7. Data Retention</h2>
<p>
We retain personal information only as long as needed for the purposes described in this policy, including QSL fulfillment, support, operational
integrity, and legal compliance.
</p>
<p>When retention is no longer required, we delete or anonymize data where reasonably possible.</p>
</div>
</div>
<div class="card mb-3" id="security">
<div class="card-body text-content">
<h2 class="h5">8. Data Security</h2>
<p>
We apply reasonable technical and organizational safeguards to protect personal information. However, no internet-based service can be
guaranteed 100% secure.
</p>
</div>
</div>
<div class="card mb-3" id="minors">
<div class="card-body text-content">
<h2 class="h5">9. Children and Minors</h2>
<p>
Our services are not directed to children under 13 (or the minimum age required by local law). We do not knowingly collect personal data from
children in violation of applicable law.
</p>
</div>
</div>
<div class="card mb-3" id="rights">
<div class="card-body text-content">
<h2 class="h5">10. Your Privacy Rights</h2>
<p>Depending on your jurisdiction, you may have rights to:</p>
<ul>
<li>Request access to your personal information</li>
<li>Request correction of inaccurate information</li>
<li>Request deletion of personal information</li>
<li>Object to or restrict certain processing</li>
<li>Withdraw consent where processing is based on consent</li>
</ul>
<p>
To exercise your rights, contact us at
<a href="mailto:ops@yksa.space">ops@yksa.space</a>.
</p>
</div>
</div>
<div class="card mb-3" id="dnt">
<div class="card-body text-content">
<h2 class="h5">11. Do Not Track</h2>
<p>
Some browsers offer a Do Not Track signal. Because there is no universally accepted standard for interpreting this signal, we currently do not
respond to it.
</p>
</div>
</div>
<div class="card mb-3" id="changes">
<div class="card-body text-content">
<h2 class="h5">12. Changes to This Policy</h2>
<p>We may update this Privacy Policy from time to time. The "Last updated" date at the top indicates when this version became effective.</p>
</div>
</div>
<div class="card mb-3" id="contact">
<div class="card-body text-content">
<h2 class="h5">13. Contact</h2>
<p>If you have questions or concerns about this policy, contact:</p>
<p class="mb-0">
Sakha Aerospace Systems, LLC (YKSA)<br />
Email: <a href="mailto:ops@yksa.space">ops@yksa.space</a><br />
Website: <a href="https://www.yksa.space">www.yksa.space</a>
</p>
</div>
</div>
</div>

View file

@ -0,0 +1,315 @@
<div class="container my-4">
<h1 class="h3 mb-3">Политика в отношении обработки персональных данных</h1>
<p class="text-body-secondary">Последнее обновление: 20 апреля 2026</p>
<div class="card mb-3">
<div class="card-body text-content">
<p>
Настоящая политика обработки персональных данных составлена в соответствии с требованиями
Федерального закона от 27.07.2006 N 152-ФЗ «О персональных данных» (далее - Закон о персональных данных)
и определяет порядок обработки персональных данных и меры по обеспечению безопасности персональных данных,
предпринимаемые ООО «Якутские Космические Системы» (далее - Оператор).
</p>
</div>
</div>
<div class="card mb-3" id="section-1">
<div class="card-body text-content">
<h2 class="h5">1. Общие положения</h2>
<p>
1.1. Оператор ставит своей важнейшей целью и условием осуществления своей деятельности соблюдение прав и
свобод человека и гражданина при обработке его персональных данных, в том числе защиты прав на
неприкосновенность частной жизни, личную и семейную тайну.
</p>
<p class="mb-0">
1.2. Настоящая политика Оператора в отношении обработки персональных данных (далее - Политика) применяется
ко всей информации, которую Оператор может получить о посетителях веб-сайта
<a href="https://qsl.yksa.space">https://qsl.yksa.space</a>.
</p>
</div>
</div>
<div class="card mb-3" id="section-2">
<div class="card-body text-content">
<h2 class="h5">2. Основные понятия, используемые в Политике</h2>
<ol>
<li>
<strong>Автоматизированная обработка персональных данных</strong> - обработка персональных данных
с помощью средств вычислительной техники.
</li>
<li>
<strong>Блокирование персональных данных</strong> - временное прекращение обработки персональных данных
(за исключением случаев, если обработка необходима для уточнения персональных данных).
</li>
<li>
<strong>Веб-сайт</strong> - совокупность графических и информационных материалов, а также программ
для ЭВМ и баз данных, обеспечивающих их доступность в сети интернет по адресу
<a href="https://qsl.yksa.space">https://qsl.yksa.space</a>.
</li>
<li>
<strong>Информационная система персональных данных</strong> - совокупность содержащихся в базах данных
персональных данных и обеспечивающих их обработку информационных технологий и технических средств.
</li>
<li>
<strong>Обезличивание персональных данных</strong> - действия, в результате которых невозможно определить
без использования дополнительной информации принадлежность персональных данных конкретному Пользователю
или иному субъекту персональных данных.
</li>
<li>
<strong>Обработка персональных данных</strong> - любое действие (операция) или совокупность действий
(операций), совершаемых с использованием средств автоматизации или без использования таких средств
с персональными данными, включая сбор, запись, систематизацию, накопление, хранение,
уточнение (обновление, изменение), извлечение, использование, передачу (распространение,
предоставление, доступ), обезличивание, блокирование, удаление, уничтожение персональных данных.
</li>
<li>
<strong>Оператор</strong> - государственный орган, муниципальный орган, юридическое или физическое лицо,
самостоятельно или совместно с другими лицами организующие и/или осуществляющие обработку персональных данных,
а также определяющие цели обработки персональных данных, состав персональных данных, подлежащих обработке,
действия (операции), совершаемые с персональными данными.
</li>
<li>
<strong>Персональные данные</strong> - любая информация, относящаяся прямо или косвенно
к определенному или определяемому Пользователю веб-сайта
<a href="https://qsl.yksa.space">https://qsl.yksa.space</a>.
</li>
<li>
<strong>Персональные данные, разрешенные субъектом персональных данных для распространения</strong> -
персональные данные, доступ неограниченного круга лиц к которым предоставлен субъектом персональных данных
путем дачи согласия на обработку персональных данных, разрешенных субъектом персональных данных
для распространения в порядке, предусмотренном Законом о персональных данных
(далее - персональные данные, разрешенные для распространения).
</li>
<li><strong>Пользователь</strong> - любой посетитель веб-сайта <a href="https://qsl.yksa.space">https://qsl.yksa.space</a>.</li>
<li>
<strong>Предоставление персональных данных</strong> - действия, направленные на раскрытие персональных данных
определенному лицу или определенному кругу лиц.
</li>
<li>
<strong>Распространение персональных данных</strong> - любые действия, направленные на раскрытие
персональных данных неопределенному кругу лиц (передача персональных данных) или на ознакомление
с персональными данными неограниченного круга лиц, в том числе обнародование персональных данных
в средствах массовой информации, размещение в информационно-телекоммуникационных сетях
или предоставление доступа к персональным данным каким-либо иным способом.
</li>
<li>
<strong>Трансграничная передача персональных данных</strong> - передача персональных данных
на территорию иностранного государства органу власти иностранного государства, иностранному
физическому или иностранному юридическому лицу.
</li>
<li class="mb-0">
<strong>Уничтожение персональных данных</strong> - любые действия, в результате которых персональные
данные уничтожаются безвозвратно с невозможностью дальнейшего восстановления содержания персональных
данных в информационной системе персональных данных и/или уничтожаются материальные носители
персональных данных.
</li>
</ol>
</div>
</div>
<div class="card mb-3" id="section-3">
<div class="card-body text-content">
<h2 class="h5">3. Основные права и обязанности Оператора</h2>
<h3 class="h6">3.1. Оператор имеет право:</h3>
<ul>
<li>получать от субъекта персональных данных достоверные информацию и/или документы, содержащие персональные данные;</li>
<li>
в случае отзыва субъектом персональных данных согласия на обработку персональных данных,
а также направления обращения с требованием о прекращении обработки персональных данных,
Оператор вправе продолжить обработку персональных данных без согласия субъекта персональных данных
при наличии оснований, указанных в Законе о персональных данных;
</li>
<li>
самостоятельно определять состав и перечень мер, необходимых и достаточных для обеспечения
выполнения обязанностей, предусмотренных Законом о персональных данных и принятыми в соответствии
с ним нормативными правовыми актами, если иное не предусмотрено Законом о персональных данных
или другими федеральными законами.
</li>
</ul>
<h3 class="h6">3.2. Оператор обязан:</h3>
<ul class="mb-0">
<li>предоставлять субъекту персональных данных по его просьбе информацию, касающуюся обработки его персональных данных;</li>
<li>организовывать обработку персональных данных в порядке, установленном действующим законодательством РФ;</li>
<li>отвечать на обращения и запросы субъектов персональных данных и их законных представителей в соответствии с требованиями Закона о персональных данных;</li>
<li>сообщать в уполномоченный орган по защите прав субъектов персональных данных по запросу этого органа необходимую информацию в течение 10 дней с даты получения такого запроса;</li>
<li>публиковать или иным образом обеспечивать неограниченный доступ к настоящей Политике в отношении обработки персональных данных;</li>
<li>принимать правовые, организационные и технические меры для защиты персональных данных от неправомерного или случайного доступа к ним, уничтожения, изменения, блокирования, копирования, предоставления, распространения персональных данных, а также от иных неправомерных действий в отношении персональных данных;</li>
<li>прекратить передачу (распространение, предоставление, доступ) персональных данных, прекратить обработку и уничтожить персональные данные в порядке и случаях, предусмотренных Законом о персональных данных;</li>
<li>исполнять иные обязанности, предусмотренные Законом о персональных данных.</li>
</ul>
</div>
</div>
<div class="card mb-3" id="section-4">
<div class="card-body text-content">
<h2 class="h5">4. Основные права и обязанности субъектов персональных данных</h2>
<h3 class="h6">4.1. Субъекты персональных данных имеют право:</h3>
<ul>
<li>
получать информацию, касающуюся обработки его персональных данных, за исключением случаев,
предусмотренных федеральными законами. Сведения предоставляются субъекту персональных данных
Оператором в доступной форме, и в них не должны содержаться персональные данные,
относящиеся к другим субъектам персональных данных, за исключением случаев,
когда имеются законные основания для раскрытия таких персональных данных.
Перечень информации и порядок ее получения установлен Законом о персональных данных;
</li>
<li>
требовать от оператора уточнения его персональных данных, их блокирования или уничтожения
в случае, если персональные данные являются неполными, устаревшими, неточными,
незаконно полученными или не являются необходимыми для заявленной цели обработки,
а также принимать предусмотренные законом меры по защите своих прав;
</li>
<li>выдвигать условие предварительного согласия при обработке персональных данных в целях продвижения на рынке товаров, работ и услуг;</li>
<li>на отзыв согласия на обработку персональных данных, а также на направление требования о прекращении обработки персональных данных;</li>
<li>обжаловать в уполномоченный орган по защите прав субъектов персональных данных или в судебном порядке неправомерные действия или бездействие Оператора при обработке его персональных данных;</li>
<li>на осуществление иных прав, предусмотренных законодательством РФ.</li>
</ul>
<h3 class="h6">4.2. Субъекты персональных данных обязаны:</h3>
<ul>
<li>предоставлять Оператору достоверные данные о себе;</li>
<li>сообщать Оператору об уточнении (обновлении, изменении) своих персональных данных.</li>
</ul>
<h3 class="h6">4.3.</h3>
<p class="mb-0">
Лица, передавшие Оператору недостоверные сведения о себе либо сведения о другом субъекте
персональных данных без согласия последнего, несут ответственность в соответствии
с законодательством РФ.
</p>
</div>
</div>
<div class="card mb-3" id="section-5">
<div class="card-body text-content">
<h2 class="h5">5. Принципы обработки персональных данных</h2>
<ol class="mb-0">
<li>Обработка персональных данных осуществляется на законной и справедливой основе.</li>
<li>Обработка персональных данных ограничивается достижением конкретных, заранее определенных и законных целей. Не допускается обработка персональных данных, несовместимая с целями сбора персональных данных.</li>
<li>Не допускается объединение баз данных, содержащих персональные данные, обработка которых осуществляется в целях, несовместимых между собой.</li>
<li>Обработке подлежат только персональные данные, которые отвечают целям их обработки.</li>
<li>Содержание и объем обрабатываемых персональных данных соответствуют заявленным целям обработки. Не допускается избыточность обрабатываемых персональных данных по отношению к заявленным целям их обработки.</li>
<li>При обработке персональных данных обеспечивается точность персональных данных, их достаточность, а в необходимых случаях и актуальность по отношению к целям обработки персональных данных. Оператор принимает необходимые меры и/или обеспечивает их принятие по удалению или уточнению неполных или неточных данных.</li>
<li>Хранение персональных данных осуществляется в форме, позволяющей определить субъекта персональных данных, не дольше, чем этого требуют цели обработки персональных данных, если срок хранения персональных данных не установлен федеральным законом, договором, стороной которого, выгодоприобретателем или поручителем по которому является субъект персональных данных. Обрабатываемые персональные данные уничтожаются либо обезличиваются по достижении целей обработки или в случае утраты необходимости в достижении этих целей, если иное не предусмотрено федеральным законом.</li>
</ol>
</div>
</div>
<div class="card mb-3" id="section-6">
<div class="card-body text-content">
<h2 class="h5">6. Цели обработки персональных данных</h2>
<h3 class="h6">6.1. Предоставление доступа Пользователю к сервисам, информации и/или материалам, содержащимся на веб-сайте</h3>
<p><strong>Персональные данные:</strong></p>
<ul>
<li>электронный адрес;</li>
<li>фамилия и имя;</li>
<li>позывной сигнал (согласно Приказу Минкомсвязи России от 12.01.2012 N 4 (ред. от 04.03.2019) «Об утверждении Порядка образования позывных сигналов для опознавания радиоэлектронных средств гражданского назначения»).</li>
</ul>
<p><strong>Правовые основания:</strong></p>
<ul>
<li>Федеральный закон «Об информации, информационных технологиях и о защите информации» от 27.07.2006 N 149-ФЗ.</li>
</ul>
<p><strong>Виды обработки персональных данных:</strong></p>
<ul>
<li>сбор, запись, систематизация, накопление, хранение, уничтожение и обезличивание персональных данных.</li>
</ul>
<h3 class="h6">6.2. Информирование Пользователя посредством отправки электронных писем</h3>
<p><strong>Персональные данные:</strong></p>
<ul>
<li>электронный адрес.</li>
</ul>
<p><strong>Правовые основания:</strong></p>
<ul>
<li>Федеральный закон «Об информации, информационных технологиях и о защите информации» от 27.07.2006 N 149-ФЗ.</li>
</ul>
<p class="mb-0"><strong>Виды обработки персональных данных:</strong></p>
<ul class="mb-0">
<li>отправка информационных писем на адрес электронной почты.</li>
</ul>
</div>
</div>
<div class="card mb-3" id="section-7">
<div class="card-body text-content">
<h2 class="h5">7. Условия обработки персональных данных</h2>
<ol class="mb-0">
<li>Обработка персональных данных осуществляется с согласия субъекта персональных данных на обработку его персональных данных.</li>
<li>Обработка персональных данных необходима для достижения целей, предусмотренных международным договором Российской Федерации или законом, для осуществления возложенных законодательством Российской Федерации на оператора функций, полномочий и обязанностей.</li>
<li>Обработка персональных данных необходима для осуществления правосудия, исполнения судебного акта, акта другого органа или должностного лица, подлежащих исполнению в соответствии с законодательством Российской Федерации об исполнительном производстве.</li>
<li>Обработка персональных данных необходима для исполнения договора, стороной которого либо выгодоприобретателем или поручителем по которому является субъект персональных данных, а также для заключения договора по инициативе субъекта персональных данных или договора, по которому субъект персональных данных будет являться выгодоприобретателем или поручителем.</li>
<li>Обработка персональных данных необходима для осуществления прав и законных интересов оператора или третьих лиц либо для достижения общественно значимых целей при условии, что при этом не нарушаются права и свободы субъекта персональных данных.</li>
<li>Осуществляется обработка персональных данных, доступ неограниченного круга лиц к которым предоставлен субъектом персональных данных либо по его просьбе (далее - общедоступные персональные данные).</li>
<li>Осуществляется обработка персональных данных, подлежащих опубликованию или обязательному раскрытию в соответствии с федеральным законом.</li>
</ol>
</div>
</div>
<div class="card mb-3" id="section-8">
<div class="card-body text-content">
<h2 class="h5">8. Порядок сбора, хранения, передачи и других видов обработки персональных данных</h2>
<p>
Безопасность персональных данных, которые обрабатываются Оператором, обеспечивается путем реализации
правовых, организационных и технических мер, необходимых для выполнения в полном объеме требований
действующего законодательства в области защиты персональных данных.
</p>
<ol>
<li>Оператор обеспечивает сохранность персональных данных и принимает все возможные меры, исключающие доступ к персональным данным неуполномоченных лиц.</li>
<li>Персональные данные Пользователя никогда, ни при каких условиях не будут переданы третьим лицам, за исключением случаев, связанных с исполнением действующего законодательства, либо в случае, если субъектом персональных данных дано согласие Оператору на передачу данных третьему лицу для исполнения обязательств по гражданско-правовому договору.</li>
<li>В случае выявления неточностей в персональных данных Пользователь может актуализировать их самостоятельно, путем направления Оператору уведомления на адрес электронной почты Оператора <a href="mailto:ops@yksa.space">ops@yksa.space</a> с пометкой «Актуализация персональных данных».</li>
<li>Срок обработки персональных данных определяется достижением целей, для которых были собраны персональные данные, если иной срок не предусмотрен договором или действующим законодательством. Пользователь может в любой момент отозвать свое согласие на обработку персональных данных, направив Оператору уведомление посредством электронной почты на адрес <a href="mailto:ops@yksa.space">ops@yksa.space</a> с пометкой «Отзыв согласия на обработку персональных данных».</li>
<li>Вся информация, которая собирается сторонними сервисами, в том числе платежными системами, средствами связи и другими поставщиками услуг, хранится и обрабатывается указанными лицами (Операторами) в соответствии с их пользовательскими соглашениями и политиками конфиденциальности. Субъект персональных данных обязан самостоятельно ознакомиться с указанными документами. Оператор не несет ответственность за действия третьих лиц, в том числе указанных в настоящем пункте поставщиков услуг.</li>
<li>Установленные субъектом персональных данных запреты на передачу (кроме предоставления доступа), а также на обработку или условия обработки (кроме получения доступа) персональных данных, разрешенных для распространения, не действуют в случаях обработки персональных данных в государственных, общественных и иных публичных интересах, определенных законодательством РФ.</li>
<li>Оператор при обработке персональных данных обеспечивает конфиденциальность персональных данных.</li>
<li>Оператор осуществляет хранение персональных данных в форме, позволяющей определить субъекта персональных данных, не дольше, чем этого требуют цели обработки персональных данных, если срок хранения персональных данных не установлен федеральным законом, договором, стороной которого, выгодоприобретателем или поручителем по которому является субъект персональных данных.</li>
<li class="mb-0">Условием прекращения обработки персональных данных может являться достижение целей обработки персональных данных, истечение срока действия согласия субъекта персональных данных, отзыв согласия субъектом персональных данных или требование о прекращении обработки персональных данных, а также выявление неправомерной обработки персональных данных.</li>
</ol>
</div>
</div>
<div class="card mb-3" id="section-9">
<div class="card-body text-content">
<h2 class="h5">9. Перечень действий, производимых Оператором с полученными персональными данными</h2>
<ol class="mb-0">
<li>Оператор осуществляет сбор, запись, систематизацию, накопление, хранение, уточнение (обновление, изменение), извлечение, использование, передачу (распространение, предоставление, доступ), обезличивание, блокирование, удаление и уничтожение персональных данных.</li>
<li>Оператор осуществляет автоматизированную обработку персональных данных с получением и/или передачей полученной информации по информационно-телекоммуникационным сетям или без таковой.</li>
</ol>
</div>
</div>
<div class="card mb-3" id="section-10">
<div class="card-body text-content">
<h2 class="h5">10. Трансграничная передача персональных данных</h2>
<ol class="mb-0">
<li>Оператор до начала осуществления деятельности по трансграничной передаче персональных данных обязан уведомить уполномоченный орган по защите прав субъектов персональных данных о своем намерении осуществлять трансграничную передачу персональных данных (такое уведомление направляется отдельно от уведомления о намерении осуществлять обработку персональных данных).</li>
<li>Оператор до подачи вышеуказанного уведомления обязан получить от органов власти иностранного государства, иностранных физических лиц, иностранных юридических лиц, которым планируется трансграничная передача персональных данных, соответствующие сведения.</li>
</ol>
</div>
</div>
<div class="card mb-3" id="section-11">
<div class="card-body text-content">
<h2 class="h5">11. Конфиденциальность персональных данных</h2>
<p class="mb-0">
Оператор и иные лица, получившие доступ к персональным данным, обязаны не раскрывать третьим лицам
и не распространять персональные данные без согласия субъекта персональных данных,
если иное не предусмотрено федеральным законом.
</p>
</div>
</div>
<div class="card mb-3" id="section-12">
<div class="card-body text-content">
<h2 class="h5">12. Заключительные положения</h2>
<ol class="mb-0">
<li>Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты <a href="mailto:ops@yksa.space">ops@yksa.space</a>.</li>
<li>В данном документе будут отражены любые изменения политики обработки персональных данных Оператором. Политика действует бессрочно до замены ее новой версией.</li>
<li>Актуальная версия Политики в свободном доступе расположена в сети Интернет по адресу <a href="{% url 'privacy-policy' %}">https://qsl.yksa.space/privacy</a>.</li>
</ol>
</div>
</div>
</div>

View file

@ -0,0 +1,16 @@
{% comment %}
Colourless metadata: a version, an identifier, a count, a role. Anything that
carries no status. If you are reaching for a coloured badge to show a *fact*,
you want this instead.
{% include "yksa/ui/_chip.html" with text=element.source.slug mono=1 %}
text the chip content (required)
icon Bootstrap Icons name without the `bi-` prefix
mono render monospace -- machine identifiers only
title tooltip
{% endcomment %}{% spaceless %}
<span class="yksa-chip{% if mono %} font-monospace{% endif %}"{% if title %} title="{{ title }}"{% endif %}>
{% if icon %}<i class="bi bi-{{ icon }}" aria-hidden="true"></i>{% endif %}{{ text }}
</span>
{% endspaceless %}

View file

@ -0,0 +1,11 @@
{% comment %}
Nothing to show, said the same way everywhere.
{% trans "No widgets yet." as empty_text %}
{% include "yksa/ui/_empty.html" with text=empty_text %}
When you can offer the action that resolves the emptiness, build the row inline
with the same `.yksa-empty` class so the action sits beside the sentence rather
than under it.
{% endcomment %}
<div class="yksa-empty justify-content-center">{{ text }}</div>

View file

@ -0,0 +1,15 @@
{% load i18n %}{% comment %}
Inline result of an `expect:` assertion on a captured value.
{% include "yksa/ui/_expect_icon.html" with state=field.expect_state %}
state pass | fail | unknown (anything else renders nothing)
Deliberately not a yksa/ui/_state.html state: this annotates one value inside a
row that already carries a state of its own, and a second pill there would read
as a second object.
{% endcomment %}{% spaceless %}
{% if state == 'pass' %}<i class="bi bi-check-circle-fill text-success" title="{% trans 'Within expected range' %}"></i>
{% elif state == 'fail' %}<i class="bi bi-x-circle-fill text-danger" title="{% trans 'Outside expected range' %}"></i>
{% elif state == 'unknown' %}<i class="bi bi-question-circle text-body-secondary" title="{% trans 'Not yet evaluable' %}"></i>{% endif %}
{% endspaceless %}

View file

@ -0,0 +1,24 @@
{% comment %}
KPI tile. One column of a .row-cols-* grid.
{% include "yksa/ui/_kpi.html" with label=label value=value %}
{% include "yksa/ui/_kpi.html" with label=label value=value hint=hint accent="primary" %}
label the caption (required)
value the figure (required)
hint muted secondary line under the figure
accent Bootstrap semantic name for a left border -- at most one per strip
A tile whose secondary line is itself markup (a timestamp through
yksa/ui/_utc.html, say) builds the same three elements inline with the same
classes, as with .yksa-empty.
{% endcomment %}
<div class="col">
<div class="card h-100 border-0 bg-white shadow-sm">
<div class="card-body py-2 px-3{% if accent %} border-start border-{{ accent }} border-3{% endif %}">
<div class="yksa-label text-truncate">{{ label }}</div>
<div class="yksa-readout fs-4">{{ value }}</div>
{% if hint %}<div class="text-body-secondary small">{{ hint }}</div>{% endif %}
</div>
</div>
</div>

View file

@ -0,0 +1,13 @@
{% comment %}
Always h1.h3, so pages cannot drift apart. Heading rank is structural; size is
fixed. Never pick a heading level for its size.
{% trans "Widgets" as parent %}{% url 'public:widget-list' as parent_url %}
{% include "yksa/ui/_page_title.html" with title=widget.name parent=parent parent_url=parent_url %}
Put it inside the page header's flex row; the row owns the actions on the right.
{% endcomment %}
<h1 class="h3 mb-0">
{% if parent %}<a class="text-decoration-none" href="{{ parent_url }}">{{ parent }}</a> / {% endif %}{{ title }}
</h1>
{% if subtitle %}<span class="text-body-secondary small">{{ subtitle }}</span>{% endif %}

View file

@ -0,0 +1,22 @@
{% load i18n yksa_ui %}{% comment %}
The only sanctioned rendering of state. Two vocabularies -- health (ok, warning,
failed, running, skipped, unknown) and progress (not-started, in-progress,
complete, forced, skipped, failed) -- one visual treatment. Never describe one
object with both.
{% include "yksa/ui/_state.html" with state=run.ui_state %}
{% include "yksa/ui/_state.html" with state=plan.ui_state label=plan.progress_label %}
`label` overrides the default word; `title` sets a tooltip. Colour is never the
only signal, so the icon and the word ship with it.
The mapping from a domain status to a state name lives next to the model, not
here. A conditional chain choosing a colour inside a template is the bug this
partial exists to prevent: it is how the same run status ended up green on one
page and grey on another.
{% endcomment %}{% state state as s %}{% spaceless %}
<span class="yksa-state yksa-state-{{ state|default:'unknown' }}"{% if title %} title="{{ title }}"{% endif %}>
<i class="bi bi-{{ s.icon }}" aria-hidden="true"></i>
<span>{{ label|default:s.label }}</span>
</span>
{% endspaceless %}

View file

@ -0,0 +1,16 @@
{% load tz %}{% comment %}
UTC-primary time: `2026-07-24 11:59:23Z` with the session's zone as a muted
secondary. Never render bare local time on an epoch, an AOS or a reception
timestamp -- a reader who does not know which zone they are looking at cannot
compare two numbers.
{% include "yksa/ui/_utc.html" with dt=element.epoch %}
{% include "yksa/ui/_utc.html" with dt=pass_obj.los date=False %}
dt the datetime (required)
date include the date part (default: yes)
seconds include seconds (default: yes)
local show the muted local secondary (default: yes)
{% endcomment %}{% spaceless %}
<span class="yksa-utc">{% localtime off %}{% if date|default_if_none:True %}{{ dt|utc|date:"Y-m-d " }}{% endif %}{{ dt|utc|date:"H:i" }}{% if seconds|default_if_none:True %}{{ dt|utc|date:":s" }}{% endif %}Z{% endlocaltime %}{% if local|default_if_none:True %} <span class="yksa-local">{% localtime on %}{{ dt|date:"H:i O" }}{% endlocaltime %}</span>{% endif %}</span>
{% endspaceless %}