Files
MikrotikManager/страна_раскрывает_сервисы_424c424b.plan.md
T

6.5 KiB
Raw Blame History

name, overview, todos, isProject
name overview todos isProject
Страна раскрывает сервисы Клик по стране на карте оставляет остальные страны на холсте и раскрывает справа столбец сервисов этой страны (доля от трафика страны). Данные — вложенная агрегация brand×ISO в том же hops-проходе.
id content status
contract-nested countryServiceGroups + mapCountryServiceNodeId в контракте/брендах done
id content status
backend-nested Агрегация brand×country в hops, рёбра страна→сервис done
id content status
backend-nested-tests Тесты групп US/NL, id cc:us|svc:*, fromId=страна done
id content status
ui-expand Клик по стране: сдвиг колонки, столбец сервисов, панель, mock done
false

Раскрытие сервисов страны на карте

Эталон холста — app/(main)/network-map/page.tsx (Frame не меняем: SVG, не ops-list). UX: клик по стране → справа узлы сервисов, остальные страны остаются.

flowchart LR
  dest[internetPeer] --> brand[mapInternetBrand]
  dest --> geo[ISO country]
  brand --> nested["group cc:us"]
  geo --> nested
  nested --> col["столбец сервисов"]
  click[клик по стране] --> col

Сейчас hops считает бренд и страну независимо в одном цикле по dstAcc (traffic-flow-map-hops.ts ~567–583). Связки «Google внутри US» в API нет — без неё UI может только гадать.

Поведение на холсте

  • Режим Страны, слой «Назначения» включён: как сейчас (EN → страны).
  • Клик по стране: она selected; справа появляется второй столбец сервисов этой страны; остальные страны остаются, но приглушаются (opacity как у isVis).
  • Рёбра раскрытия: страна → сервис (пунктир, те же подписи скорости). EN → страна для выбранной страны подсвечивается.
  • Повторный клик по той же стране / клик по пустому холсту / смена destMode → свернуть столбец.
  • Клик по другой стране → свернуть предыдущий набор и раскрыть новый.
  • Клик по сервису: selected = сервис; столбец остаётся; панель — «Сервис в {страна}».

Раскладка в lib/network-map-layout.ts: сейчас одна колонка x = W - MARGIN - SERVICE_COL_W/2. При раскрытии:

  • страны сдвигаются влево на SERVICE_COL_W (чтобы не налезать на Канаду/Вьетнам с скрина);
  • сервисы занимают прежний правый x, по вертикали вокруг Y выбранной страны (новый хелпер placeCountryServiceNodes(ids, parentPos)), не общий центр EN.

Доля на узле сервиса = bytes / bytes страны (не доля окна): иначе у DE 2% все сервисы будут «0%». В панели дополнительно можно показать долю окна.

Cap вложенного слоя: тот же pickMapServices, но shareBase = байты страны, cap 8, min узлов 4. «Прочее» внутри страны — svc:other.

Контракт

В packages/contracts/src/traffic-flow.ts optional:

countryServiceGroups: z.array(z.object({
  countryId: z.string(), // cc:us
  services: z.array(flowMapServiceDtoSchema),
  edges: z.array(flowMapServiceEdgeDtoSchema), // fromId = countryId
  paths: z.array(flowMapServicePathDtoSchema),  // serviceId = nested id
})).optional()

id сервиса во вложении: cc:us|svc:google (mapCountryServiceNodeId), чтобы Google в US и NL не смешивались в одном payload. Группы только для стран, прошедших pickMapServices стран.

Бэкенд

В том же цикле dstAcc: после svcId + country.id копить nestedTotals/edges/paths с ключом ${country.id}|${svcId}. Рёбра вложенного слоя: fromId = country.id (не EN). Пути: как сервисные, serviceId = nested id (enId остаётся для подсветки HR→JH→EN).

finalize по каждой стране из countriesOut.nodes.

Фронт

  • Состояние expandedCountryId (не в sessionStorage).
  • visibleNested = groups.find(g => g.countryId === expandedCountryId).
  • ServiceNode для вложенных: destMode="services" (бренд), подпись label, доля от страны.
  • Панель страны: текущие KPI + короткий список топ-сервисов (клик = тот же expand/select).
  • Панель сервиса: бренд + «в {countryName}», доля страны, пути visibleNested.paths с serviceId.
  • Mock: MOCK_COUNTRY_SERVICE_GROUPS для cc:us (Google / Cloudflare / AWS).

Тесты

traffic-flow-map-hops.test.ts: ripe US → группа cc:us с брендом 8.8.8.8; кейс US+NL — сервисы не пересекают countryId; nested edges.fromId = cc:us, не svc:*.

Хелпер id — рядом с mapCountryNodeId в brands-тесте.

Вне скоупа

  • Одновременное раскрытие двух стран.
  • Географическая карта мира.
  • Отдельный порог NetFlow для вложенных сервисов (тот же %, но база = страна).