feat(tg): premium Mini App cockpit — spend hero, charts, avatars (#582)

* feat(tg): premium Today — spend hero, trend, quick actions, fleet avatars

The cockpit home stops being flat cards and becomes a real app surface:

- Spend HERO: the day's cost at 40px with a signed delta-vs-yesterday
  chip and a live 7-day amber area sparkline (hand-rolled inline SVG, no
  charting lib in the Mini App bundle).
- Quick-action ring: circular Approve (amber + needs-you badge) / Board /
  Inbox / Chat, the wallet-style primary-verb row.
- Needs-you as a rich amber gradient banner (top items + draft chips)
  instead of a plain section.
- Fleet as live avatar tokens (stable per-name hue, pulse dot) over the
  working list.
- "Shipped this week" day-bars (today emphasized) + week total.

Backend: /telegram/today gains spend.series (7-day cost) + delta_pct and
a velocity series (per-day completed tasks) — two cheap grouped-by-day
queries, same DB-only ethos, degrading to zeros on error.

* feat(tg): color-code approval rows by kind

TgRowIcon gains a tone prop; the approvals list tints each tile per kind
(amber Release / sky X post / violet Video / emerald Roadmap) so a mixed
queue reads as color-coded instead of a monochrome column.

* feat(tg): sender/peer avatars on Inbox + Chat

Inbox notifications and Chat conversation rows adopt the fleet-avatar
language: a per-name-hued initials token leads each card, unread inbox
items carry a subtle primary tint, and both cards move to the rounded-2xl
surface — so every tab now shares one visual system. Board keeps the
shared MobileTaskBoard (already grouped/pill-styled, and reused outside
the cockpit).

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
Renzo F
2026-07-19 11:51:06 +02:00
committed by GitHub
co-authored by Renn F
parent 5f32d8760a
commit c7605b0d77
11 changed files with 606 additions and 172 deletions
+3 -1
View File
@@ -120,7 +120,9 @@ async def test_today_brief_shape(db_session: AsyncSession) -> None:
"""Structure + invariants that hold regardless of shared-DB residue."""
brief = await get_tg_cockpit_service(db_session).today()
assert set(brief) == {"needs_you", "fleet", "spend", "ship"}
assert set(brief) == {"needs_you", "fleet", "spend", "velocity", "ship"}
assert len(brief["spend"]["series"]) == 7 # noqa: PLR2004
assert len(brief["velocity"]["series"]) == 7 # noqa: PLR2004
needs = brief["needs_you"]
assert needs["total"] == (
needs["awaiting_ceo_count"]