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>