Дашборд: переводы обоих направлений, переключатель «Сегодня / Всего»

На 9 при одном входящем переводе (Витвицкий) дашборд показывал «Переводов
ещё нет»: сводка и «Последние переводы» считали только исходящие, и только
последние 200.

- Сводка по направлениям: исходящие и входящие — всего, исполнено, в работе,
  отказ / истёк срок; исход — по состоянию «чего ждёт заявка» (livestatus.go),
  то же, что в журнале: принятый, но не исполненный перевод — «в работе».
- Период «Сегодня / Всего» (?period=, выбор помнится в куке; по умолчанию —
  сегодня, с 00:00 МСК). Плитка «Переводы» ведёт в журнал, счётчики за всё время.
- «Последние переводы» — общая лента журнала (без анкет участника): направление,
  инвестор, контрагент, статус и строка состояния.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
This commit is contained in:
fontvielle
2026-10-23 15:19:13 +03:00
parent a0ffbfa721
commit ffd69b0698
4 changed files with 202 additions and 59 deletions
@@ -38,10 +38,10 @@
<span class="t-sub">{{if devOn}}Симулятор: сценарий робота + кол-во бумаг{{else}}Ручной ввод поручения клиента{{end}}</span>
<span class="t-arrow"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
</a>
<a href="/admin/claims" class="tile">
<a href="/admin/journal" class="tile">
<span class="ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M8 6h12M8 12h12M8 18h12"/><path d="M3.5 6h.01M3.5 12h.01M3.5 18h.01"/></svg></span>
<span class="t-title">Переводы</span>
<span class="t-sub">{{.Counts.Total}} всего · {{.Counts.InProgress}} в работе</span>
<span class="t-sub">{{.All.Out.Total}} исходящих · {{.All.In.Total}} входящих</span>
<span class="t-arrow"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
</a>
<a href="/admin/status" class="tile">
@@ -75,50 +75,61 @@
</div>
{{end}}
{{/* ===== Сводка по переводам ===== */}}
<div class="grid">
<div class="stat accent">
<div class="stat-label">Всего переводов</div>
<div class="stat-value">{{.Counts.Total}}</div>
</div>
<div class="stat ok">
<div class="stat-label">Подтверждено</div>
<div class="stat-value" style="color:var(--ok)">{{.Counts.Confirmed}}</div>
</div>
<div class="stat warn">
<div class="stat-label">В ожидании</div>
<div class="stat-value" style="color:var(--warn)">{{.Counts.InProgress}}</div>
</div>
<div class="stat err">
<div class="stat-label">Отказы / таймауты</div>
<div class="stat-value" style="color:var(--err)">{{.Counts.Failed}}</div>
</div>
{{/* ===== Сводка по переводам: исходящие и входящие, за сегодня или за всё время ===== */}}
<style>
.period { display:inline-flex; border:1px solid var(--border); border-radius:9px; overflow:hidden; font-size:13px; }
.period a { padding:4px 12px; color:var(--muted); text-decoration:none; }
.period a.on { background:var(--accent-weak); color:var(--accent); font-weight:600; }
a.stat { display:block; color:inherit; text-decoration:none; }
.dirsplit { display:flex; flex-wrap:wrap; gap:4px 14px; margin-top:8px; font-size:12.5px; color:var(--muted); }
.dirsplit b { font-family:var(--mono); font-variant-numeric:tabular-nums; font-size:14px; }
</style>
<div class="section-head" style="align-items:center;gap:10px;flex-wrap:wrap">
<h2>Переводы {{if eq .Period "today"}}за сегодня{{else}}за всё время{{end}}</h2>
<span class="period"><a href="/admin/?period=today"{{if eq .Period "today"}} class="on"{{end}}>Сегодня</a><a href="/admin/?period=all"{{if eq .Period "all"}} class="on"{{end}}>Всего</a></span>
</div>
<div class="grid" style="grid-template-columns:repeat(auto-fit,minmax(260px,1fr))">
<a class="stat accent" href="/admin/claims">
<div class="stat-label">↗ Исходящие — от нас в другие депозитарии</div>
<div class="stat-value">{{.Sum.Out.Total}}</div>
{{template "homeDirSplit" .Sum.Out}}
</a>
<a class="stat accent" href="/admin/incoming">
<div class="stat-label">↘ Входящие — к нам</div>
<div class="stat-value">{{.Sum.In.Total}}</div>
{{template "homeDirSplit" .Sum.In}}
</a>
</div>
{{/* ===== Последние переводы ===== */}}
{{/* ===== Последние переводы обоих направлений ===== */}}
<div class="section-head">
<h2>Последние переводы</h2>
<a href="/admin/claims">все →</a>
<a href="/admin/journal">весь журнал →</a>
</div>
<div class="card">
{{if .Recent}}
<div style="overflow-x:auto">
<table>
<thead><tr><th>Время</th><th>ID</th><th>Инвестор</th><th>ЦБ</th><th>Статус</th><th></th></tr></thead>
<thead><tr><th>Время</th><th>Направление</th><th>Инвестор</th><th>Контрагент</th><th>ЦБ</th><th>Статус</th><th></th></tr></thead>
<tbody>
{{range .Recent}}
<tr>
<td class="num muted" style="white-space:nowrap">{{.CreatedAt.Format "02.01.2006 15:04:05"}}</td>
<td><code>{{short 8 .ID}}</code></td>
<td>{{.Investor.LastName}} {{initial .Investor.FirstName}}</td>
<td class="num">{{len .Securities}}</td>
<td><span class="badge {{.Status}}">{{ruState .Status}}</span></td>
<td><a href="/admin/claims/{{.ID}}">открыть →</a></td>
<td class="num muted" style="white-space:nowrap">{{msk .Time}}</td>
<td>{{if eq .DirKind "out"}}<span class="badge" style="background:#e7f0fb;color:#1f4e79">↗ Исходящий</span>{{else}}<span class="badge" style="background:#e6f4ea;color:#1a7f37">↘ Входящий</span>{{end}}</td>
<td style="white-space:nowrap">{{.Investor}}</td>
<td>{{.Party}}</td>
<td class="num">{{.SecN}}</td>
<td><span class="badge {{.Status}}">{{.StatusText}}</span>{{if .Outcome}} <span class="badge {{if eq .Outcome "Принято"}}confirmed{{else if eq .Outcome "Отказано"}}rejected{{else}}awaiting_decision{{end}}">{{.Outcome}}</span>{{end}}{{if .Live.Text}}<div class="muted" style="font-size:12px;margin-top:2px">{{.Live.Text}}</div>{{end}}</td>
<td style="white-space:nowrap"><a href="{{.Href}}">открыть →</a></td>
</tr>
{{end}}
</tbody>
</table>
</div>
{{else if eq .Period "today"}}
<p class="muted" style="margin:0">Сегодня переводов не было. <a href="/admin/?period=all">Показать за всё время</a></p>
{{else}}
<p class="muted" style="margin:0">Переводов ещё нет. {{if devOn}}Нажмите «Новый перевод», чтобы создать первый.{{else}}Они появятся с заявками из личного кабинета, а поручение клиента можно ввести вручную — «Новый перевод».{{end}}</p>
<p class="muted" style="margin:0">Переводов ещё нет. {{if devOn}}Нажмите «Новый перевод», чтобы создать первый.{{else}}Они появятся с заявками из личного кабинета и от других депозитариев, а поручение клиента можно ввести вручную — «Новый перевод».{{end}}</p>
{{end}}
</div>
@@ -170,3 +181,5 @@
</div>
{{end}}
{{end}}
{{/* исход переводов одного направления на дашборде (homeDir) */}}
{{define "homeDirSplit"}}<div class="dirsplit"><span><b style="color:var(--ok)">{{.Done}}</b> исполнено</span><span><b style="color:var(--warn)">{{.InProgress}}</b> в работе</span><span><b style="color:var(--err)">{{.Failed}}</b> отказ / истёк срок</span></div>{{end}}