yksa-web-kit/yksa_web/templates/yksa/base.html
2026-08-21 22:09:17 +08:00

175 lines
10 KiB
HTML

{% 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 the page, in sentence case ("Data sources", not "Data Sources")
title_suffix the service, the same on every page ("YKSA TDAS")
meta_description, meta_keywords the rest of 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>
{% comment %}
`bg-body-tertiary`, not `bg-light`. `bg-light` is a fixed near-white that does
not follow `data-bs-theme`, so it was the one element standing between the
estate and a working dark theme: every card and control below it recoloured
and the page they sat on did not.
{% endcomment %}
<body class="d-flex flex-column min-vh-100 bg-body-tertiary">
<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>
<script src="{% static 'yksa/src/toast.js' %}"></script>
{% block extra_js %}{% endblock extra_js %}
</body>
</html>