Skip to main content
Petanque Life

Embedding & Widgets

F14.06 6 features Shipped

At a glance

Embedding & Widgets ships drop-in iframes for results, calendars, rankings, and live scores so federation and club websites can surface live tenant-scoped data without writing API code; a WidgetCustomization layer governs an eight-color palette, branding, light/dark/custom themes, locale, fonts, and CSS, and POST /widgets/{id}/embed-code emits hardened iframe, JavaScript, and direct-URL snippets ready to paste into any CMS or rich-text field.

How it works

Each widget is a WidgetConfig document with a typed payload (results, calendar, ranking, live_score) and a stable token used in the public URL. The results widget queries CompetitionResult restricted to published rows with embed-friendly cache headers and configurable optional columns (balls won, club affiliation). The calendar widget pulls CalendarEvent within a date range, filtered by event type and federation/club, with a configurable months_ahead window.

The ranking widget reads RankingEntry by config id, dimension, and category and toggles club and rank-change columns. The live-score widget queries Match for in_progress and scheduled rows, exposes mène-level detail, court assignment, and a client-side auto-refresh interval. WidgetCustomization carries a full eight-color palette, branding (logo URL, powered-by toggle, custom footer), light/dark/custom theme, size preset, BCP 47 locale, border radius, font family, max items, and an escape hatch for custom CSS — all serialised into the iframe URL as compact query parameters so the embedded view can hydrate without an extra round trip.

The embed-code generator (POST /widgets/{id}/embed-code, plus a public GET /public/widgets/{token}/embed-code mirror) returns three artefacts: an iframe snippet hardened with sandbox, lazy-load, and referrerpolicy attributes; a JavaScript snippet that injects the iframe and listens for postMessage resize events; and a raw direct-URL for use in CMS rich-text fields or RSS readers. Widget responses set Content-Security-Policy frame-ancestors so embeds work only on declared domains.

Key capabilities

  • Four widget types — results, calendar, ranking, live_score — backed by tenant-scoped queries
  • Token-based public URL (/public/widgets/{token}) with embed-friendly cache headers
  • WidgetCustomization for colors, branding, theme, locale, size, fonts, custom CSS
  • Embed-code generator producing iframe, JavaScript, and direct-URL snippets
  • Iframe sandbox, lazy-load, and referrerpolicy hardening by default
  • Live-score auto-refresh with mène detail and court assignment
  • Frame-ancestors CSP per widget for domain-locked embedding

In practice

A club webmaster wants live scores from this weekend's tournament on their homepage. They open the admin, create a new live_score WidgetConfig filtered to competition_id=2026-spring-open, set the theme to dark, brand it with their club logo, and choose auto-refresh every 15 seconds. They click Get Embed Code, copy the iframe snippet — already configured with sandbox=allow-scripts allow-same-origin and loading=lazy — and paste it into the WordPress block on their homepage.

The widget renders within the bottle-green theme, shows two live courts with mène-by-mène scores, and refreshes silently. After the tournament they swap the WidgetConfig to a results widget pointing at the same competition and the URL keeps working with no code changes.

Features in this subsystem

6
ID Status Features
F14.06.01 Shipped Embeddable results widget for federation/club websites — WidgetConfig (type: results), GET /public/widgets/{token} returns published CompetitionResult data with embed-friendly headers, tenant-scoped, configurable columns (balls, club) ✅ a95566cc.m-11 — byggd rent i Go/Postgres; se F14.00.19 (raden beskriver det RIVNA PL-F1406-systemet)
F14.06.02 Shipped Embeddable calendar widget — WidgetConfig (type: calendar), queries CalendarEvent with date range, event type filter, federation/club filter, configurable months ahead ✅ a95566cc.m-11 — se F14.00.19/.21; källan är kalenderdomänens EGEN publika söm
F14.06.03 Shipped Embeddable ranking widget — WidgetConfig (type: ranking), queries RankingEntry by config/dimension/category, show/hide club and rank change columns ✅ a95566cc.m-11 — se F14.00.21; bokförd avvikelse: källan är en PUBLIK, aktiv D-RANK-preset (preset_slug), och en icke-publik preset ger ett ärligt tomläge preset_not_public — aldrig en rad
F14.06.04 Shipped Embeddable live score widget — WidgetConfig (type: live_score), queries Match for in_progress/scheduled matches, mène detail, court assignment, auto-refresh config ✅ a95566cc.m-11 — se F14.00.19; auto_refresh_seconds 5—60, pollen armeras EN gång per montering
F14.06.05 Shipped Widget customization (colors, branding) — WidgetCustomization with full color palette (8 colors), branding (logo, powered-by, custom footer), theme (light/dark/custom), size, locale, border radius, font family, max items, custom CSS ✅ a95566cc.m-11 — se F14.00.21; bokförd skärpning: kontrasten DÖMS av servern (WCAG 2.2 AA, 4,5:1) med den uppmätta kvoten i svaret, och custom_css avvisas — aldrig strippas tyst
F14.06.06 Shipped Widget embed code generator — POST /widgets/{id}/embed-code and GET /public/widgets/{token}/embed-code, generates iframe snippet (sandbox, lazy-load, referrerpolicy), JavaScript snippet, and direct URL with configurable dimensions ✅ a95566cc.m-11 — se F14.00.21; bokförd avvikelse: generatorn är GET /v1/widgets/{id}/embed-code (en LÄSNING, inte en POST) och den publika …/embed-code finns INTE — en anonym yta ska inte generera inbäddningskod åt någon annans widget