Programming

1. dashboard

{% extends "portfolio/base.html" %}

{% block loanbook_content %}
<div class="lb-kpis-strip">
  <div class="lb-wrap">
    <div class="lb-kpis">
      {% for k in kpis %}
        <div class="lb-kpi">
          <div class="k">{{ k.label }}</div>
          <div class="v">{{ k.value }}</div>
          <div class="d {{ k.cls }}">{{ k.delta|default:"&nbsp;" }}</div>
        </div>
      {% endfor %}
    </div>
  </div>
</div>

<nav class="lb-tabs"><div class="lb-wrap">
  {% for s in sections %}
    <button type="button" class="lb-tab {% if s.slug == active %}is-active{% endif %}"
            data-section="{{ s.slug }}">{{ s.title }}</button>
  {% endfor %}
</div></nav>

{% if quality %}
<div class="lb-wrap">
  <div class="lb-alerts">
    <b>{{ t.methodology }}</b>
    <ul>
      {% for q in quality %}
        <li><span class="status-{{ q.status }}">{{ q.status }}</span>
            {{ q.check_name }} — {{ q.detail }}</li>
      {% endfor %}
    </ul>
  </div>
</div>
{% endif %}

{# First section is server-rendered; the rest arrive from section_partial. #}
<div id="lb-section-host">
  {% include "portfolio/_section.html" %}
</div>

<section class="lb-wrap lb-table-block">
  <h3>{{ t.filial }} — {{ period_label }}</h3>
  <table class="lb-table">
    <thead><tr>
      <th>{{ t.filial }}</th><th>{{ t.exposure }}</th><th>{{ t.npl_ratio }}</th>
      <th>Δ 12м, п.п.</th><th>{{ t.watch_ratio }}</th>
      <th>{{ t.coverage_ratio }}</th><th>{{ t.n_loans }}</th>
    </tr></thead>
    <tbody>
      {% for b in branches %}
        <tr>
          <td>{{ b.name }}</td><td>{{ b.exposure }}</td><td>{{ b.npl }}</td>
          <td class="{{ b.delta_cls }}">{{ b.delta }}</td>
          <td>{{ b.watch }}</td><td>{{ b.coverage }}</td><td>{{ b.n_loans }}</td>
        </tr>
      {% empty %}
        <tr><td colspan="7" class="muted">Нет данных по филиалам.</td></tr>
      {% endfor %}
    </tbody>
  </table>
  <p class="lb-dl">
    <a href="{% url 'portfolio:mart_csv' 'portfolio_monthly' %}">CSV</a> ·
    <a href="{% url 'portfolio:mart_api' 'portfolio_monthly' %}?dim_name=filial">JSON</a>
  </p>
</section>
{% endblock %}

{% block loanbook_scripts %}
<script>
// Tabs fetch their section on first click and cache the markup afterwards.
// Plotly needs the returned <script> tags executed, which innerHTML will not do,
// so they are re-created explicitly.
(function () {
  const host = document.getElementById('lb-section-host');
  if (!host) return;
  const cache = {};
  const qs = new URLSearchParams(window.location.search);
  const base = "{% url 'portfolio:dashboard' %}";

  function inject(html) {
    host.innerHTML = html;
    host.querySelectorAll('script').forEach(old => {
      const s = document.createElement('script');
      if (old.src) s.src = old.src; else s.textContent = old.textContent;
      old.replaceWith(s);
    });
    window.loanbookRelayout && window.loanbookRelayout();
  }

  document.querySelectorAll('.lb-tab').forEach(btn => {
    btn.addEventListener('click', async () => {
      const slug = btn.dataset.section;
      document.querySelectorAll('.lb-tab')
        .forEach(b => b.classList.toggle('is-active', b === btn));
      if (cache[slug]) return inject(cache[slug]);

      host.innerHTML = '<div class="lb-wrap lb-loading">Загрузка…</div>';
      try {
        const r = await fetch(`${base}section/${slug}/?${qs.toString()}`,
                              {headers: {'X-Requested-With': 'fetch'}});
        if (!r.ok) throw new Error(r.status);
        cache[slug] = await r.text();
        inject(cache[slug]);
      } catch (e) {
        host.innerHTML = `
Не удалось загрузить раздел (${e.message}). Обновите страницу.</div>`; } }); }); })(); </script> {% endblock %}
Helpful? Dislike 0 Log in to react