Release 1.69.0

Release date: July 9, 2026

Important Release Documents

Release Notes - Seatmap Platform

Version 1.69.0 - 2026-07-09

Release Focus: Organization Usage dashboard and custom billing plans – organizations can now track seat-credit consumption against their plan (or a custom contract limit) in real time, drill into per-event sold seats, and export to CSV. This release also lands the booking-renderer reliability and state-API hardening from the SEAT-1046 audit (request timeouts, robust event loads and teardown, outline-state and minimap fixes), new editor iframe integration hooks (host-controlled language and price-list change events), and a batch of editor usability and cross-tenant security fixes.

MetaTitle: Release 1.69.0: Usage & Custom Plans - Seatmap.pro

MetaDescription: Seatmap 1.69.0 adds the Organization Usage dashboard and custom billing plans, plus booking-renderer reliability fixes and new editor iframe integration hooks.


What’s New

Organization Usage dashboard (SEAT-1068)

A new self-service Usage area in the editor shows how many seats your organization has sold against its seat-credit allowance for the current billing period:

  • A donut gauge with used / capacity / remaining / percent, plus a burn-rate projection of when credits will run out.
  • A monthly sold-seats chart and an event table grouped by quarter.
  • Drill down from any event into a paginated, load-more list of individual sold seats (section, row, seat, price, and sold date/time).
  • Export either the whole organization or a single event to CSV, with section/row/seat detail.

The gauge degrades gracefully: an organization with a signed agreement but no plan sees raw SOLD counts with the percentage donut hidden. Seatmap administrators can open the same dashboard scoped to any organization from the organizations list, with a “Viewing usage for {org}” banner. The dashboard is localized across all eight supported languages and uses accessible table and disclosure semantics.

New: Profile, Organization, Users, and Usage now live together under a new /app/settings area; existing /app/organization/* links redirect automatically.

Custom billing plans and capacity (SEAT-1036)

Organizations can now be placed on a custom contract instead of a standard plan. A Seatmap administrator selects “Custom” and enters a bespoke seat-sales limit for the billing period; a standard plan and a custom limit are mutually exclusive – choosing one clears the other. The Usage dashboard and analytics reporting use the custom limit as the denominator when set (falling back to the plan capacity otherwise), so custom-contract customers reconcile usage against their real seat credit.

Host-controlled editor language (SEAT-960)

Applications embedding the venue editor in an iframe can now set its UI language directly, instead of relying on the visitor’s browser or stored preference:

  • A ?language=<code> URL parameter opens the editor in the requested language.
  • A SET_LANGUAGE parent-to-editor postMessage switches language at runtime without reloading the iframe.

Both accept the eight supported codes (en, ru, de, es, fr, it, zh, pl) and safely ignore unknown or malformed values, falling back to the previous behavior. The iframe integration guide documents both (English, Russian, Spanish).

Real-time price-list change events (SEAT-998)

The embedded editor now notifies the host page whenever a price is created, renamed, or deleted, via three new postMessage events – PRICE_CREATED, PRICE_UPDATED, and PRICE_DELETED – each carrying the persisted price id (and, for create/update, the name). Because price-list entities are saved immediately rather than deferred to the schema Save, host applications can now keep their own price catalog in sync in real time. Events fire only after the backend call succeeds.

Per-table “show price dot” toggle (SEAT-1030)

Tables now have a “Show price dot” option in the editor’s table panel. When it is switched off, the booking renderer hides the colored price dot and re-centers the table label.


Editor improvements

  • Fixed: Readable labels on flipped and rotated shapes (SEAT-1016) – Section and table name labels now stay upright and un-mirrored when their shape is flipped or rotated, instead of appearing reversed or upside-down. The correction applies to section shape names, object/polyline text, and table labels.
  • Fixed: Reliable Alt-drag selection (SEAT-1049) – Holding Alt to marquee-select individual seats or rows now keeps the section’s shape locked for the whole drag. Releasing another key (such as Shift) while still holding Alt no longer prematurely unlocks the section and makes it jump or transform.
  • Fixed: Whole-table selection with non-numeric prices (SEAT-1032) – Tables whose price label is non-numeric (for example “VIP”) can now be selected as a whole. The selection gate is keyed off the price id rather than a numeric parse of the price label.

Booking renderer – audit fixes (SEAT-1046)

Resilient event loads: request timeouts and visible errors (SEAT-1050)

Hardening so an embedded booking widget fails visibly instead of spinning forever or dying silently:

  • Booking API requests (schema and prices) now time out after 30 seconds by default and surface the loader error state. Integrators can tune this with a new optional requestTimeoutMs setting.
  • loadEvent() now resolves only after prices and rows have fully loaded and applied, and rejects with a visible error if any request fails – giving integrators a reliable completion signal.
  • A fast load failure no longer flashes an error that immediately disappears, and destroying the widget mid-load (React StrictMode, SPA navigation) no longer throws unhandled promise rejections. On browsers without AbortSignal.timeout, the renderer degrades gracefully.

Outline states survive price refreshes (SEAT-1051)

Selected, unavailable, filtered, and highlighted section states are now re-applied automatically after the outline layer is rebuilt (for example when setPricesData runs). Previously every price refresh silently reset these states, because the rebuilt outline SVG was never reconciled with the stored state.

Section state-clearing APIs behave correctly (SEAT-1051)

The no-argument section-state methods on the renderer now do what their names say:

  • unselectSection(), enableSection(), and unfilterSection() with no argument now clear the respective state on all sections (previously they were silently ignored).
  • highlightSection() with no argument highlights all sections; highlightSection(id, true) now clears that section’s highlight (previously both were no-ops).
  • Bulk state updates via setOutlineStates(undefined, { <state>: true }) now apply to every outline (previously only bulk clears were processed, and they left the internal state cache stale).

Outline reconciliation runs silently and skips decorative outlines (SEAT-1067)

The outline-state reconciliation above is applied directly to the rebuilt outline elements without emitting the public sectionStateChanged event, so a price refresh no longer produces a burst of state-change notifications for sections whose state did not change. Consumers subscribed via renderer.getStateManager().on('sectionStateChanged', ...) receive events only for genuine state changes.

Bulk state updates via setOutlineStates(undefined, { <state>: true }) now skip decorative (id-0) outline elements, so shared background and decorative outlines are no longer stamped with selection or other state attributes.

Section-selection and seat-lock APIs honor their arguments (SEAT-1052)

Two renderer methods now behave as their names and documentation describe. Both are corrections that change observable behavior for existing integrations:

  • setSectionSelection(sections) now replaces the current selection with exactly the sections passed. Repeated calls no longer accumulate selected outlines, and setSectionSelection([]) clears the selection. Previously each call added to the selection and an empty array was ignored, so selected outlines built up and could never be cleared through this method.
  • enableSeatsByIds(ids) now unlocks only the seats whose ids are passed. Previously it could re-enable seats outside the provided list, undoing a preceding disableSeatsByIds(...).

Custom seat states render during pan and zoom (SEAT-1052)

Custom seat states applied via setSeatsState(ids, stateKey) now appear immediately while the map is being panned or zoomed, and re-render correctly when a different state is assigned to the same seats. Previously a custom state could stay invisible until roughly 150 ms after a gesture ended, and re-assigning a state to already-stated seats did not always update their appearance.

Outline hit-testing now also resolves a section whose id is 0; previously such a section was treated as “no section” on click and hover.

Minimap navigation lands on the clicked point and pans the venue live (SEAT-1053)

Clicking the minimap now centers the main view on the exact point under the cursor, including on letterboxed minimaps and venues whose coordinate origin is not (0, 0). Previously the click could land offset, because the navigation math did not invert the minimap’s own drawing transform (it ignored the letterbox offset and, on the animated path, double-counted the viewBox origin). Dragging the minimap viewport indicator now pans the main venue continuously as you drag – previously the indicator moved while the venue stayed frozen until an unrelated redraw – and releasing a drag no longer makes the viewport jump.

Repeated event loads and renderer teardown no longer corrupt the host DOM (SEAT-1054)

Loading a second event into an existing renderer (calling loadEvent again) now reuses the host cleanly. The previous section-outline SVG is destroyed before the new one is built, so repeated loads no longer stack overlapping outline layers, and marker pins are re-parented into the new outline SVG instead of disappearing.

When the renderer falls back from WebGL to the Canvas2D pipeline mid-session (for example when a venue has no raster background), the WebGL layer now removes only its own canvas instead of clearing the entire host element, so the loader bar and any surrounding customer DOM are preserved. On teardown, the WebGL layer and its sub-renderers release their GPU programs, buffers, and textures, and the secret-game key listeners are removed.

If a WebGL context cannot be created even though WebGL reports as available, the renderer now falls back to Canvas2D instead of failing. The stage canvas no longer uses a fixed id, so two renderer instances can coexist on one page without producing duplicate element ids.

Native Pointer Events replace Hammer.js (SEAT-1038)

Pan, tap, and pinch-to-zoom gesture handling was rewritten from the third-party Hammer.js library onto the browser-native Pointer Events API, and hammerjs was removed from the renderer’s dependencies, lockfile, and build config. End-user gesture behavior is preserved at parity (pan/tap thresholds, pinch scale and center math, post-pinch pan suppression), with cleaner handling of interrupted gestures. The renderer bundle drops a runtime dependency and one supply-chain surface.


Security

  • Fixed: Cross-tenant isolation hardening (SEAT-1035) – A tenant administrator (SUPER_ADMIN) can no longer read organizations outside its own tenant, and an organization_id override / JWT claim is now validated against the user’s entitlements and ignored (and logged) when it is not authorized.

Additional Documentation

The organization Usage endpoints added in this release are internal editor-service endpoints consumed by the editor UI (guarded by @CanManageOrganization); the public Booking API is functionally unchanged, so no Booking Service API change document is included.


Affected products

  • editor-client – Organization Usage dashboard and /app/settings shell; custom-capacity admin UI; iframe language control and price-list change events; upright labels on flipped/rotated shapes; reliable Alt-drag selection; per-table price-dot toggle.
  • editor-service – organization usage API (GET /api/organization/usage family) with SOLD aggregation, capacity merge, burn-rate projection, and gating; admin cross-org usage view; customCapacity on the org plan endpoint and AdminOrgDTO; cross-tenant access hardening.
  • booking-client – request timeouts and contained event-load failures; loadEvent() full-load contract; loader-error visibility; outline-state rebind and silent reconciliation; corrected section-state and seat-lock APIs; custom seat states during pan/zoom and id-0 resolution; minimap navigation; clean repeated loads and teardown; native Pointer Events (Hammer.js removed); table price-dot visibility; priceId in section callbacks.
  • db-migrationsV108__organization_custom_capacity adds a nullable organization.custom_capacity column; analytics usage views coalesce plan and custom capacity.
  • booking-service – Swagger auth reference clarifies X-API-Key tokens as opaque strings (no functional change).

Upgrade

npm install @seatmap.pro/renderer@1.69.0

No renderer method signatures or configuration options were removed. The booking renderer adds an optional requestTimeoutMs setting and changes the loadEvent() promise contract (it now resolves after a full load and rejects on failure), and section callbacks now include priceId. Review the Renderer API Changes before upgrading – several changes correct previously incorrect behavior and can affect integrations that depended on it. Operators should review the Deployment Guide for the one additive database migration in this release.