1) Первая колонка графика: в углу — кнопки «Показывать» Город/Адрес/Код (стиль пилюль, активная — фиолетовая). Строка собирается из включённых сущностей, разделённых кружком 7px; код без скобок; адрес тёмный, город/код приглушённые. «Адрес» (или последний активный) нельзя снять — строка не пустеет. Доступно всем ролям, выбор в localStorage. Лёгкое обновление подписей (view.refreshLabels — без пересборки баров). 2) Временная шкала: единые 3-буквенные месяцы (format.minorLabels/majorLabels), год индиго и жирнее (.vis-text.vis-major), подсветка текущего месяца (фон-элемент cur-month-bg). Красная линия «сегодня» без изменений. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
225 lines
13 KiB
HTML
225 lines
13 KiB
HTML
<!doctype html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Ccircle cx='16' cy='16' r='15' fill='%2326c04c'/%3E%3C/svg%3E" />
|
||
<title>Адресная программа</title>
|
||
<script>
|
||
// Theme is set before first paint (no flash). Dark theme is temporarily
|
||
// hidden — force light regardless of stored/OS preference. To re-enable,
|
||
// restore the localStorage/prefers-color-scheme logic and unhide the toggle.
|
||
document.documentElement.setAttribute('data-theme', 'light');
|
||
// Role is unknown until Keycloak check-sso resolves (async). Start in the
|
||
// "pending" state that hides manager-only controls, so anonymous visitors
|
||
// never see a flash of them before applyRoleUi() runs.
|
||
document.documentElement.classList.add('role-pending');
|
||
</script>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- Yandex.Metrika counter -->
|
||
<script type="text/javascript">
|
||
(function(m,e,t,r,i,k,a){
|
||
m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
|
||
m[i].l=1*new Date();
|
||
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
|
||
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)
|
||
})(window, document,'script','https://mc.yandex.ru/metrika/tag.js?id=111613184', 'ym');
|
||
|
||
ym(111613184, 'init', {ssr:true, webvisor:true, clickmap:true, ecommerce:"dataLayer", referrer: document.referrer, url: location.href, accurateTrackBounce:true, trackLinks:true});
|
||
</script>
|
||
<noscript><div><img src="https://mc.yandex.ru/watch/111613184" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
|
||
<!-- /Yandex.Metrika counter -->
|
||
|
||
<div id="load-bar" aria-hidden="true"></div>
|
||
|
||
<header>
|
||
<!-- Group 1: brand + counters -->
|
||
<div class="hgroup hgroup-brand">
|
||
<div class="brand">
|
||
<div class="brand-name">Green Media</div>
|
||
<div class="brand-sub">Адресная программа</div>
|
||
<div id="status" class="stat-tile stat-accent"><span class="st-l">Поверхности</span><span class="st-v">…</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Group 1b: view mode switch -->
|
||
<div class="hgroup hgroup-modes">
|
||
<div class="mode-switch">
|
||
<div class="mode-ind" aria-hidden="true"></div>
|
||
<button type="button" class="mode-btn active" id="mode-timeline"><svg class="mode-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><line x1="4" y1="7" x2="16" y2="7"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="17" x2="11" y2="17"/></svg>График</button>
|
||
<button type="button" class="mode-btn" id="mode-map"><svg class="mode-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 21s-7-6.3-7-11a7 7 0 0 1 14 0c0 4.7-7 11-7 11z"/><circle cx="12" cy="10" r="2.5"/></svg>Карта</button>
|
||
<button type="button" class="mode-btn" id="mode-split"><svg class="mode-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="7" height="16" rx="1"/><rect x="14" y="4" width="7" height="16" rx="1"/></svg>Карта и график</button>
|
||
</div>
|
||
<button type="button" class="theme-toggle" id="theme-toggle" title="Переключить тему" aria-label="Переключить тему"></button>
|
||
</div>
|
||
|
||
<!-- Group 2: fields (4-column grid; dates + search on the 2nd row) -->
|
||
<div class="hgroup hgroup-fields">
|
||
<div class="field">
|
||
<label>Город</label>
|
||
<div class="dropdown" id="city-dropdown">
|
||
<button type="button" class="dropdown-btn" id="city-btn">
|
||
<span id="city-btn-text">Все города</span>
|
||
<span class="caret">▾</span>
|
||
</button>
|
||
<div class="dropdown-panel" id="city-panel"></div>
|
||
</div>
|
||
</div>
|
||
<div class="field manager-only">
|
||
<label>Бренд</label>
|
||
<div class="dropdown" id="brand-dropdown">
|
||
<button type="button" class="dropdown-btn" id="brand-btn">
|
||
<span id="brand-btn-text">Все бренды</span>
|
||
<span class="caret">▾</span>
|
||
</button>
|
||
<div class="dropdown-panel" id="brand-panel"></div>
|
||
</div>
|
||
</div>
|
||
<div class="field">
|
||
<label>Размер</label>
|
||
<div class="dropdown" id="dimension-dropdown">
|
||
<button type="button" class="dropdown-btn" id="dimension-btn">
|
||
<span id="dimension-btn-text">Все размеры</span>
|
||
<span class="caret">▾</span>
|
||
</button>
|
||
<div class="dropdown-panel" id="dimension-panel"></div>
|
||
</div>
|
||
</div>
|
||
<div class="field manager-only">
|
||
<label>Менеджер</label>
|
||
<div class="dropdown" id="manager-dropdown">
|
||
<button type="button" class="dropdown-btn" id="manager-btn">
|
||
<span id="manager-btn-text">Все менеджеры</span>
|
||
<span class="caret">▾</span>
|
||
</button>
|
||
<div class="dropdown-panel" id="manager-panel"></div>
|
||
</div>
|
||
</div>
|
||
<div class="field field-dates">
|
||
<label>Период</label>
|
||
<div class="daterange" id="daterange">
|
||
<div class="dr-field" id="dr-field" tabindex="0" role="button" aria-label="Выбрать период">
|
||
<svg class="dr-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="17" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="16" y1="2" x2="16" y2="6"/></svg>
|
||
<span class="dr-text" id="dr-text">Период</span>
|
||
<button type="button" class="dr-clear" id="dr-clear" aria-label="Очистить" style="display:none">×</button>
|
||
</div>
|
||
<div class="dr-cal" id="dr-cal" hidden></div>
|
||
<input type="date" id="date-start" class="dr-native" />
|
||
<input type="date" id="date-end" class="dr-native" />
|
||
</div>
|
||
</div>
|
||
<div class="field manager-only">
|
||
<label>Статус</label>
|
||
<div class="dropdown" id="status-dropdown">
|
||
<button type="button" class="dropdown-btn" id="status-btn">
|
||
<span id="status-btn-text">Все статусы</span>
|
||
<span class="caret">▾</span>
|
||
</button>
|
||
<div class="dropdown-panel" id="status-panel"></div>
|
||
</div>
|
||
</div>
|
||
<div class="field">
|
||
<label for="search">Поиск (адрес / код)</label>
|
||
<input type="text" id="search" placeholder="например, Азина" />
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Group 3: display toggles (the scale control now floats over the timeline) -->
|
||
<div class="hgroup hgroup-controls">
|
||
<div class="checks manager-only">
|
||
<div class="checks-row">
|
||
<div class="field checkbox-field">
|
||
<label for="show-brand"><input type="checkbox" id="show-brand" checked /> Бренд</label>
|
||
</div>
|
||
<div class="field checkbox-field">
|
||
<label for="show-company"><input type="checkbox" id="show-company" /> Компания</label>
|
||
</div>
|
||
</div>
|
||
<div class="field checkbox-field">
|
||
<label for="show-collisions"><input type="checkbox" id="show-collisions" checked /> Коллизии размещения</label>
|
||
</div>
|
||
<div class="field checkbox-field">
|
||
<label for="show-all-surfaces"><input type="checkbox" id="show-all-surfaces" checked /> Все поверхности</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Login / logout button, pinned to the header's top-right corner -->
|
||
<div class="auth-wrap">
|
||
<button type="button" class="auth-btn" id="auth-btn" style="display:none">Войти</button>
|
||
</div>
|
||
|
||
<!-- Appearance ("+") button, pinned to the header's bottom-right corner -->
|
||
<div class="decor-wrap manager-only">
|
||
<button type="button" class="decor-btn" id="decor-btn" aria-label="Оформление" data-tip="Оформление"><svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></button>
|
||
<div class="decor-menu" id="decor-menu"></div>
|
||
</div>
|
||
</header>
|
||
|
||
<div id="filter-chips" class="chips" style="display:none"></div>
|
||
|
||
<div id="view" class="mode-timeline">
|
||
<div id="pane-timeline">
|
||
<div id="chart-wrap">
|
||
<div id="timeline"></div>
|
||
<div id="col-info">
|
||
<span class="ci-label">Показывать</span>
|
||
<button type="button" class="ci-toggle" id="ci-city" data-part="city">Город</button>
|
||
<button type="button" class="ci-toggle is-on" id="ci-address" data-part="address">Адрес</button>
|
||
<button type="button" class="ci-toggle is-on" id="ci-code" data-part="code">Код</button>
|
||
</div>
|
||
<div class="field zoom-field" id="zoom-field">
|
||
<span id="zoom-collapsed" class="zoom-collapsed">12М</span>
|
||
<div class="zoom-full">
|
||
<label for="zoom-slider">Масштаб графика</label>
|
||
<div class="zoom-row">
|
||
<span id="zoom-label" class="zoom-bubble">12М</span>
|
||
<input type="range" id="zoom-slider" min="0" max="4" step="1" value="1" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="col-resizer" title="Потяните, чтобы изменить ширину колонки"></div>
|
||
<div id="empty" class="empty-state" style="display:none">
|
||
<svg class="empty-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/><line x1="8" y1="11" x2="14" y2="11"/></svg>
|
||
<div class="empty-title">Ничего не найдено</div>
|
||
<div class="empty-sub">Под текущие фильтры нет броней</div>
|
||
<button type="button" class="empty-reset" id="empty-reset-timeline">Сбросить фильтры</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="split-resizer" title="Потяните, чтобы изменить пропорции"></div>
|
||
<div id="pane-map">
|
||
<div id="map"></div>
|
||
<div id="map-legend">
|
||
<div class="stat-tile"><span class="st-l"><span class="lg-dot lg-free"></span>свободно</span><span class="st-v st-free" id="lg-free">0</span></div>
|
||
<div class="stat-tile"><span class="st-l"><span class="lg-dot lg-busy"></span>занято</span><span class="st-v st-busy" id="lg-busy">0</span></div>
|
||
<div class="stat-tile"><span class="st-l"><span class="lg-dot lg-total"></span>всего</span><span class="st-v" id="map-counter">0</span></div>
|
||
<button type="button" class="stat-tile st-nc" id="map-nocoords-btn" style="display:none"><span class="st-l"><span class="lg-dot"></span>без координат</span><span class="st-v">0</span></button>
|
||
</div>
|
||
<div id="map-nocoords-panel" style="display:none"></div>
|
||
<div id="map-empty" class="empty-state" style="display:none">
|
||
<svg class="empty-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 21s-7-6.3-7-11a7 7 0 0 1 14 0c0 4.7-7 11-7 11z"/><circle cx="12" cy="10" r="2.5"/></svg>
|
||
<div class="empty-title">Нет поверхностей на карте</div>
|
||
<div class="empty-sub">У выбранных поверхностей нет координат</div>
|
||
<button type="button" class="empty-reset" id="empty-reset-map">Сбросить фильтры</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="tooltip"></div>
|
||
|
||
<div id="board-backdrop" class="bp-backdrop" hidden></div>
|
||
<aside id="board-panel" class="board-panel" hidden aria-label="Карточка поверхности">
|
||
<div class="bp-head">
|
||
<div id="board-panel-title" class="bp-title"></div>
|
||
<button type="button" class="bp-close" id="board-panel-close" aria-label="Закрыть">✕</button>
|
||
</div>
|
||
<div id="board-panel-body" class="bp-body"></div>
|
||
</aside>
|
||
|
||
<script type="module" src="/src/main.ts"></script>
|
||
</body>
|
||
</html>
|