Extends the bar-label overflow handling: an oversized label defaults to the
right of the bar, but if it would collide with the next bar on the same row it
is placed to the left instead (whichever side has room; larger gap wins when
neither fits). Also make the trigger a debounce (was a throttle) so the layout
runs after a zoom/redraw burst settles, once bar widths are final.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- appearance menu opens 20px below the header/chips and 58px from the right edge
- map "no coordinates" badge is manager-only (hidden for anonymous visitors)
- timeline bar label that is wider than its bar now renders just past the bar's
right edge as dark text on the track, instead of being clipped; labels that
fit still sit inside the bar in white. Applied post-render (measure overflow),
re-run on changed/rangechanged with a double rAF so bar widths are settled.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Counter snippet at the top of <body> (webvisor, clickmap, trackLinks).
Lives in index.html only — no change to the JS/CSS bundle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- appearance menu drops below the active-filter chips row when it is shown,
and sits 10px further from the right edge
- "Скопировать ссылку" moved to the left of the chips, separated by a divider
- enlarge "Green Media" title so it is flush with the "Адресная программа"
line below it
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Appearance ("Оформление") menu:
- add missing status "РК. Предварительное бронирование" (8 statuses total)
- strip the systemic "РК. " prefix from status names in the UI (menu,
tooltip, status filter dropdown, active-filter chips); raw values stay
intact for filtering/URLs
- fixed-position menu: opens 25px below the header, ends >=30px above the
window bottom, scrolls inside — never overflows the viewport
- drop the "Прочие статусы" row
- per-status grey reset glyph (circular arrows) restoring its default colour
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Timeline can now list the entire inventory (occupied + free + never
booked), not just booked surfaces. New manager-only "Все поверхности"
checkbox (on by default; forced on and hidden for anonymous visitors so
they can spot what is currently free with no future bookings).
/api/boards gains all_surfaces: sources rows from board_info and unions
pf_board so a just-booked surface not yet in the inventory snapshot still
gets a row. Surface facets (city/dimension/search) narrow rows; booking
facets (brand/manager/status) narrow to booked surfaces; dates are a
window that narrows bars, not the surface set.
UI polish:
- timeline tooltip flips up/left near viewport edges
- anonymous "Войти" button filled accent, inverts to white-on-accent hover
- appearance button uses a gear icon instead of "+"
- header counter shows only "Поверхности: N"
- map "всего" counter includes surfaces without coordinates
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Anonymous visitors briefly saw the manager-only filters (Brand/Manager/Status),
checkboxes and appearance menu on refresh, because applyRoleUi() hides them only
after the async Keycloak check-sso resolves.
Fix: mark those elements .manager-only and hide them pre-paint via a .role-pending
class set on <html> by the inline head script, gated by a render-blocking CSS rule
(html.role-pending .manager-only { display:none }). applyRoleUi() drops
.role-pending once the role is known and then applies the final per-element state.
Default is now fail-closed (hidden until confirmed manager), so anonymous never
flashes them; managers see the controls appear a moment after load.
- timeline: RenderFlags.managerView drives the tooltip. Anonymous tier shows
a minimal tooltip (date range + duration only) — no title, task №, status,
manager or Planfix hint. Manager tier keeps the rich tooltip.
- header: login/logout button pinned to the top-right corner (.auth-wrap,
absolute) instead of sitting next to the view-mode switch.
Keycloak: register bare origin in post.logout.redirect.uris so keycloak-js
logout (redirectUri without trailing path) is accepted.
Backend (main.py + auth.py):
- Validate Bearer JWT against the intense-sale realm JWKS (RS256, issuer
checked, aud skipped). Missing/invalid token = anonymous, never an error,
so the public map keeps working when Keycloak is down.
- Anonymous truncation: /api/meta hides brand/manager/status facets;
/api/bookings blanks brand/company_name/manager/task_id/task_status
(bars stay, one neutral colour); /api/map keeps occupied_now but empties
booking details. Hidden facet filters are ignored for anonymous callers.
- boards stay visible to all (physical inventory: address/city/dimension).
Frontend (keycloak-js):
- Silent SSO (check-sso) — anonymous by default, Войти/Выйти button.
- Bearer token attached + refreshed on every API call.
- Anonymous UI hides Brand/Manager/Status filters, Brand/Company toggles,
appearance menu, collisions and labels; board detail panel (Planfix
deep-links) gated to managers.
Test manager: user / 1234567890 (realm role `manager`).
- Shareable filter links: current filters sync to the URL query
(?city=&brand=&dimension=&manager=&status=&q=&from=&to=, repeated params for
multi-selects) and are restored on load; a "Скопировать ссылку" action in the
chips row copies the current URL.
- Board detail panel: clicking a timeline row label opens a right-side panel with
the surface's code, city · size, address and the full list of its bookings
(brand/company, dates, status dot, manager, Planfix link). Close via ✕ /
backdrop / Esc. Bars still open the Planfix task. Dropdown gains a public
setOnBoardClick on the timeline view.
- Fix: the panel showed empty on load because .board-panel{display:flex}
overrode the UA [hidden] rule — added an explicit [hidden]{display:none}.
- Spacing: tokenised the secondary chrome (header groups, fields, dropdown panel,
appearance menu, legend, map tooltip) onto the Open Props --size-* scale;
structural density of the header/fields/inputs deliberately kept.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Active-filter chips row under the header: one chip per selected city / brand /
dimension / manager / status plus search and date bounds, each removable; a
"Сбросить всё" clears everything. Dropdown gains setSelected()/clear().
- Empty states for the timeline and map: icon + title + subtitle + "Сбросить
фильтры" button (wired to the same reset). (empty toggled to display:flex.)
- Delayed indeterminate loading bar (250ms threshold so fast loads don't flash),
covering both the data and the map fetches via an in-flight counter.
- Polish: pop-in animation for dropdown/appearance menus, :focus-visible accent
rings, :active press on buttons, thin themed scrollbars in panels, tabular
figures on the timeline date axis and tooltips. New components use the Open
Props --size-* spacing scale.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add Open Props as the token foundation (@import "open-props/style"). Our
semantic tokens (--bg/--surface/--border/--text/--shadow-*/--transition) are
now backed by its gray ramp, shadows and easing; the rest of the CSS is
untouched since it already references var(--…).
- Accent → indigo #4f46e5 (--accent + derived rings / month-hover), and the
timeline bar default colours (appearance.ts) move to the same indigo so the
content and UI share one palette.
- Font → IBM Plex Sans, self-hosted via @fontsource (latin + cyrillic, weights
400/500/600/700) so there is no runtime Google Fonts dependency. Backed into
--font-sans (overrides Open Props' :where() default). Tabular figures on the
counters and map legend.
- Collision default colour → warm amber-orange #e8590c: distinct from the indigo
bars and from the green/red map status, reads as a conflict warning.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Map (map.ts, styles.css):
- Single markers now use the same donut colours (islands#circleDotIcon +
iconColor #37b24d / #e24b4a) instead of Yandex's brighter presets, so single
and cluster markers match.
- Legend is a vertical stack with relabelled live counts: "свободно / занято /
всего", and the "без координат" badge hides when the count is zero.
- Cluster tooltip shows the brand for occupied surfaces (coral); legend raised
above the Yandex controls.
- Hover tooltip: smart positioning — flips above/left of the cursor when it would
overflow the viewport (bottom clusters no longer clip), max-height safety.
- Bug fix: hide the hover tooltip on marker/cluster click — it no longer sticks
and follows the cursor after a balloon opens.
- Bug fix: clusterDisableClickZoom=true so a cluster click opens its balloon at
any zoom instead of only at max zoom.
Header (index.html, styles.css):
- Scale + appearance + checkboxes recomposed into one compact block; the
"Оформление" control is a round "+" button pinned to the header's bottom-right
corner with a hover tooltip; counters relabelled "Поверхности / Брони"; the
counters plate and view-mode switch share one borderless grey fill.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Map (map.ts, styles.css, index.html):
- Cluster icons are now donut charts split green (free) / red (busy) by the
occupancy of the surfaces they contain, count in the centre. Custom
clusterIconLayout with a hit-area shape; hover/click preserved.
- gridSize 64 -> 112 so nearby clusters stop overlapping at the overview zoom.
- Live legend: "свободна: X · занята: Y · поверхностей: N", reacts to filters.
- "Без координат" is now a clickable badge opening a compact codes-only panel
(header "Без координат") so the missing-coordinate surfaces can be fixed.
- Search-to-locate: when a filter narrows to one surface the map flies in and
opens its balloon.
- Balloon restyled to match the timeline tooltip; muted map tiles (ground-pane
filter) so the markers stand out.
- Cluster hover tooltip shows the brand for occupied surfaces; legend plate
raised above the Yandex controls.
Header (index.html, styles.css, main.ts):
- Scale + appearance + checkboxes recomposed into one compact block; shorter
slider; the "Масштаб" label uses the shared field-label style again.
- "Оформление" is now a round "+" button pinned to the header's bottom-right
corner with a hover tooltip; opens the same appearance menu.
- Counters relabelled "Поверхности" / "Брони"; the counters plate and the
view-mode switch now share one borderless grey fill; mode buttons sit on a
more visible grey frame.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Frontend (styles.css, index.html, main.ts):
- Design tokens: single accent/border/radius/shadow/spacing system; flat header
(dropped the boxed groups) with a subtle bottom divider.
- Unified controls: 36px height, consistent focus ring; search field gets a
magnifier icon; view-mode switch is now a compact vertical segmented control.
- Fixed the dropdown option font: `.field label` styles were leaking into the
option <label>s (they're inside .field), forcing uppercase + bold; options are
now normal weight / normal case.
- Header alignment: mode switch fills the row so its bottom lines up with the
date fields; the counters block is pinned to the same bottom line.
- Dropdown lists no longer show the column-resizer line bleeding through: the
header now establishes a stacking context (position:relative + z-index) so its
panels sit above the timeline.
- Dark theme is implemented (tokens + no-flash) but hidden for now (toggle
display:none, forced light) until it's polished — easy to re-enable.
Timeline tooltip (timeline.ts, types.ts):
- Replaced the plain multi-line text tooltip with a structured card: bold brand
title + task №, company subtitle, localized dates + duration, status as a
coloured dot, manager, a distinct collision section, and a "click to open in
Planfix" hint. Left accent stripe in the bar colour; matches the map tooltip.
- Values are escaped (escapeHtml) before going into innerHTML.
Backend (main.py):
- /api/bookings now returns `manager` (Array(String)), canonicalized to the
"Фамилия Имя" form (see canon_manager) so the tooltip can show it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>