Obsah
1Rýchly štart
Rezervačný widget ReserveIT vložíte na akýkoľvek web dvoma riadkami HTML. Všetko ostatné — načítanie služieb, voľné termíny, overenie a uloženie rezervácie — zabezpečí widget sám priamo voči nášmu API. Nepotrebujete žiadny backend, knižnicu ani framework.
VAS-KLIC
Svoj kľúč nájdete v administrácii v sekcii Widgety (pri každom widgete je aj hotový kód na vloženie). <div data-reserveit="VAS-KLIC"></div>
<script src="http://sk.localhost:8103/widget/reserveit.js" async></script>
Skript stačí na stránke raz, aj keď vkladáte viac widgetov. Widget sa vykreslí do Shadow DOM, takže štýly vášho webu ho nerozbijú a jeho štýly neovplyvnia váš web. Prispôsobí sa šírke kontajnera (mobil aj desktop).
2Spôsoby vloženia
A) Kontajner + skript (odporúčané)
Kontajner <div data-reserveit="…"> umiestnite tam, kde sa má formulár zobraziť. Skript vložte kamkoľvek na stránku (ideálne pred </body>) — nájde všetky kontajnery a každý widget pripojí samostatne.
<section id="rezervace">
<h2>Rezervace</h2>
<div data-reserveit="VAS-KLIC"></div>
</section>
<!-- jednou na stránce, i pro více widgetů -->
<script src="http://sk.localhost:8103/widget/reserveit.js" async></script>
B) Jedným tagom
Hodí sa do redakčných systémov, kam možno vložiť len jeden kus kódu. Widget sa vloží hneď za skript do nového prvku s atribútom data-reserveit-host (podľa neho ho môžete štýlovať). Ak ho chcete inde, pridajte data-reserveit-target s CSS selektorom cieľového prvku. Ostatné atribúty (data-reserveit-service, -locale, -prefill) zapíšte na tag skriptu.
<script src="http://sk.localhost:8103/widget/reserveit.js" data-reserveit="VAS-KLIC" async></script>
<!-- vykreslit do jiného prvku -->
<script src="http://sk.localhost:8103/widget/reserveit.js" data-reserveit="VAS-KLIC"
data-reserveit-target="#rezervace" async></script>
C) Iframe
Pre systémy, ktoré nedovolia vložiť skript. Hostovaná stránka /b/KĽÚČ?embed=1 má priehľadné pozadie a rodičovskej stránke posiela svoju výšku správou postMessage v tvare {type: 'reserveit:height', key, height}. Pri iframe nefungujú CSS premenné, ::part() ani udalosti — tie sú len pri variante so skriptom.
<iframe id="reserveit-frame" src="http://sk.localhost:8103/b/VAS-KLIC?embed=1" title="Rezervace"
style="width:100%;border:0;height:900px" loading="lazy"></iframe>
<script>
window.addEventListener('message', function (e) {
if (e.origin !== 'http://sk.localhost:8103' || !e.data || e.data.type !== 'reserveit:height') return;
document.getElementById('reserveit-frame').style.height = e.data.height + 'px';
});
</script>
D) Rezervačná stránka
Nemáte web alebo chcete odkaz do Instagramu, Google profilu či e-mailu? V administrácii (Widgety → Nová rezervačná stránka) vytvoríte samostatnú stránku na našej doméne s adresou /p/nazov-xxxxxxxxxx. Nastavíte rozloženie, pozadie, logo, texty nad a pod formulárom a SEO. Stránka používa rovnaký widget, takže platí všetko z tejto dokumentácie okrem vlastného CSS/JS webu.
3Atribúty
Atribúty sa zapisujú na kontajner (pri variante B na tag <script>). Všetky začínajú data-reserveit, takže nekolidujú s ničím na vašom webe.
| Atribút | Hodnota | Význam |
|---|---|---|
data-reserveit | kľúč widgetu | Povinný. Určuje, ktorý widget (nastavenia z administrácie) sa zobrazí. |
data-reserveit-service | ID služby | Predvyberie službu a preskočí krok výberu služby. ID nájdete v administrácii v detaile služby. |
data-reserveit-locale | cs | sk | en | de | Jazyk widgetu (prebije jazyk nastavený v administrácii). |
data-reserveit-prefill | JSON | Predvyplní údaje zákazníka: name, email, phone, note. Hodí sa pre prihlásených používateľov vášho webu. |
data-reserveit-target | CSS selektor | Len pri variante jedným tagom — kam widget vykresliť. |
<div data-reserveit="VAS-KLIC"
data-reserveit-service="12"
data-reserveit-locale="en"
data-reserveit-prefill='{"name":"Jana Nováková","email":"jana@example.com","phone":"+420 777 123 456"}'>
</div>
data-reserveit-prefill píšte do jednoduchých apostrofov (atribút) a vnútri používajte dvojité úvodzovky. Ak údaje skladáte na serveri, escapujte ich ako HTML atribút (napr. htmlspecialchars(json_encode($data)) v PHP).4JavaScript API
Skript vytvára jediný globálny objekt window.ReserveIT. Nič ďalšie do stránky nepridáva. Keďže sa načítava s async, volajte API až po jeho načítaní (pozri onload nižšie) alebo použite deklaratívne kontajnery, ktoré sa pripoja samy.
| Volanie | Popis |
|---|---|
ReserveIT.mount(el, config) | Pripojí widget do prvku (element alebo CSS selektor) a vráti inštanciu. |
ReserveIT.get(el) | Vráti inštanciu widgetu v danom prvku (aj pri deklaratívne vložených), inak null. |
ReserveIT.scan() | Pripojí kontajnery pridané do stránky neskôr. Deje sa aj automaticky (MutationObserver), ručné volanie je len poistka. |
ReserveIT.version | Verzia skriptu (reťazec, napr. „2.0.0“). |
ReserveIT.mount()
<div id="booking"></div>
<script src="http://sk.localhost:8103/widget/reserveit.js" async onload="initBooking()"></script>
<script>
function initBooking() {
const widget = ReserveIT.mount('#booking', {
key: 'VAS-KLIC',
locale: 'cs', // cs | sk | en | de
service: 12, // předvybraná služba (ID)
prefill: { name: 'Jana Nováková', email: 'jana@example.com', phone: '+420777123456', note: '' },
theme: { primary: '#0f766e', primary2: '#14b8a6', radius: 6, buttonStyle: 'solid' },
options: { title: 'Objednejte se', showPrices: false, redirectUrl: 'https://example.com/dekujeme' }
});
}
</script>
theme a options odovzdané v JS prebijú nastavenia z administrácie len pre tento jeden widget na tejto stránke — nič sa neukladá. Kľúče sú rovnaké ako v administrácii (tabuľka nižšie). Neuvedené kľúče zostanú podľa administrácie.
Metódy inštancie
| Metóda | Popis |
|---|---|
update({theme, options, mode, template}) | Zmení vzhľad či správanie za behu (napr. prepnutie svetlý/tmavý režim webu). mode: form | calendar | schedule. |
reset() | Vráti widget na začiatok (výber služby) a vymaže rozpracovaný formulár. |
reload() | Znova načíta konfiguráciu a voľné termíny zo servera. |
destroy() | Odpojí widget, zruší poslucháče a vyprázdni kontajner. |
const widget = ReserveIT.get(document.querySelector('[data-reserveit]'));
// tmavý režim webu
document.querySelector('#dark-toggle').addEventListener('change', function (e) {
widget.update({ theme: e.target.checked
? { background: '#0f172a', surface: '#1e293b', text: '#e2e8f0', muted: '#94a3b8', border: '#334155' }
: { background: '#ffffff', surface: '#f5f6fb', text: '#1f2333', muted: '#6b7088', border: '#e3e5f0' } });
});
Kľúče theme a options
theme
| Kľúč | Hodnoty | CSS premenná | Význam |
|---|---|---|---|
primary | #rrggbb | --rit-primary | Hlavná farba (tlačidlá, výber) |
primary2 | #rrggbb | --rit-primary-2 | Druhá farba prechodu |
primaryText | #rrggbb | --rit-primary-text | Text na hlavnej farbe |
background | #rrggbb | transparent | --rit-bg | Pozadie widgetu |
surface | #rrggbb | transparent | --rit-surface | Pozadie kariet a polí |
text | #rrggbb | --rit-text | Text |
muted | #rrggbb | --rit-muted | Vedľajší text |
border | #rrggbb | --rit-border | Rámčeky a linky |
radius | 0–32 (px) | --rit-radius | Zaoblenie rohov |
font | inherit | inter | montserrat | system | sans | rounded | serif | mono | --rit-font | Písmo (inherit = prevziať z webu) |
fontSize | 12–20 (px) | --rit-size | Základná veľkosť písma |
shadow | none | soft | strong | --rit-shadow | Tieň kariet |
maxWidth | 300–1400 (px) | --rit-max | Maximálna šírka widgetu |
density | compact | normal | comfortable | --rit-gap | Hustota rozloženia (medzery) |
buttonStyle | solid | gradient | outline | — | Štýl hlavných tlačidiel |
options
| Kľúč | Hodnoty | Význam |
|---|---|---|
title | text | Nadpis widgetu |
intro | text | Úvodný text pod nadpisom |
showTitle | true | false | Zobraziť nadpis |
showPrices | true | false | Zobraziť ceny služieb |
showDuration | true | false | Zobraziť dĺžku služieb |
showDescription | true | false | Zobraziť popisy služieb |
serviceColors | true | false | Farebná bodka služby a pracovníka (farba z administrácie) |
resourceChoice | true | false | Zákazník si vyberá pracovníka / miesto |
phone | required | optional | hidden | Pole telefón |
note | required | optional | hidden | Pole poznámka |
consent | true | false | Vyžadovať súhlas so spracovaním údajov |
consentText | text | Text súhlasu |
consentUrl | URL | Odkaz na zásady ochrany osobných údajov |
submitLabel | text | Text odosielacieho tlačidla |
successTitle | text | Nadpis po úspešnej rezervácii |
successText | text | Text po úspešnej rezervácii |
redirectUrl | URL | Po rezervácii presmerovať na túto adresu (ďakovacia stránka) |
locale | cs | sk | en | de | Jazyk widgetu |
weekStart | 0 | 1 | Prvý deň týždňa (0 = nedeľa, 1 = pondelok) |
scheduleDays | 5 | 7 | Počet dní v týždennom rozvrhu |
suggestAlternatives | true | false | Ponúkať náhradné termíny, keď je vybraný obsadený |
poweredBy | true | false | Zobrazit odznak „Rezervace přes ReserveIT“ (v tarifu Start vždy zapnuto) |
customCss | CSS | Vlastné CSS vložené do Shadow DOM (pozri Vzhľad) |
SPA a dynamický obsah
Kontajnery pridané do stránky neskôr (AJAX, modálne okno, React/Vue/Angular) skript nájde sám vďaka MutationObserveru. V komponentoch je však čistejšie widget pripojiť a odpojiť ručne:
// React
import { useEffect, useRef } from 'react';
export function Booking({ serviceId, user }) {
const ref = useRef(null);
useEffect(() => {
const widget = window.ReserveIT.mount(ref.current, {
key: 'VAS-KLIC',
service: serviceId,
prefill: user ? { name: user.name, email: user.email } : undefined,
});
return () => widget.destroy();
}, [serviceId, user]);
return <div ref={ref} />;
}
<!-- Vue 3 -->
<template><div ref="box"></div></template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
const box = ref(null);
let widget;
onMounted(() => { widget = window.ReserveIT.mount(box.value, { key: 'VAS-KLIC' }); });
onBeforeUnmount(() => widget && widget.destroy());
</script>
5Udalosti
Widget vyvoláva DOM udalosti (CustomEvent) na svojom kontajneri. Udalosti prebublávajú (bubbles) a prejdú aj zo Shadow DOM (composed), takže ich môžete zachytiť na kontajneri aj na document. Dáta sú v event.detail.
| Udalosť | event.detail | Kedy |
|---|---|---|
reserveit:ready | {key, mode} | Widget načítal konfiguráciu a je zobrazený. |
reserveit:step | {key, step} | Zákazník prešiel na krok: service | term | details | done. |
reserveit:booked | {key, code, status, start, end, service, resource, persons, price, manageUrl} | Rezervácia bola vytvorená. status = confirmed alebo pending (čaká na potvrdenie prevádzkou). |
reserveit:error | {key, code, message} | Chyba (nenačítaná konfigurácia, obsadený termín, výpadok siete…). |
Meranie konverzií (GA4, Meta Pixel, Sklik)
document.addEventListener('reserveit:booked', function (e) {
const b = e.detail;
// Google Analytics 4 / Google Tag Manager
if (window.gtag) {
gtag('event', 'purchase', {
transaction_id: b.code,
value: b.price || 0,
currency: 'CZK',
items: [{ item_name: b.service, item_category: 'rezervace', quantity: b.persons }]
});
}
window.dataLayer && window.dataLayer.push({ event: 'reserveit_booked', booking: b });
// Meta (Facebook) Pixel
if (window.fbq) fbq('track', 'Schedule', { content_name: b.service, value: b.price || 0, currency: 'CZK' });
});
// průchod kroky jako trychtýř
document.addEventListener('reserveit:step', function (e) {
window.gtag && gtag('event', 'reserveit_step', { step: e.detail.step });
});
Presmerovanie a modálne okno
// po rezervaci přesměrovat na děkovací stránku s kódem rezervace
document.addEventListener('reserveit:booked', function (e) {
setTimeout(function () {
location.href = '/dekujeme?rezervace=' + encodeURIComponent(e.detail.code);
}, 1500);
});
<button type="button" id="open-booking">Objednat se</button>
<dialog id="booking-dialog" style="width:min(760px,95vw);border:0;border-radius:16px;padding:0">
<div id="booking-box"></div>
</dialog>
<script src="http://sk.localhost:8103/widget/reserveit.js" async></script>
<script>
const dialog = document.getElementById('booking-dialog');
let widget = null;
document.getElementById('open-booking').addEventListener('click', function () {
widget = widget || ReserveIT.mount('#booking-box', { key: 'VAS-KLIC' });
widget.reset();
dialog.showModal();
});
dialog.addEventListener('click', function (e) { if (e.target === dialog) dialog.close(); });
document.getElementById('booking-box').addEventListener('reserveit:booked', function () {
setTimeout(function () { dialog.close(); }, 4000);
});
</script>
- Zatiaľ žiadne udalosti…
6Vzhľad z vášho webu
Widget žije v Shadow DOM — bežné CSS vášho webu (napr. button { … }) doň neprenikne. To je zámer: formulár sa nerozbije ani na webe s agresívnymi štýlmi. Vzhľad však môžete riadiť štyrmi bezpečnými spôsobmi (od najviac odporúčaného):
a) CSS premenné
CSS custom properties nastavené na kontajneri (alebo ktoromkoľvek predkovi) prebijú farby a rozmery z administrácie. Hodí sa na zladenie s dizajnovým systémom webu — stačí odkázať na vaše vlastné premenné.
| Premenná | Význam | Príklad |
|---|---|---|
--rit-primary | Hlavná farba (tlačidlá, výber) | #e11d48 |
--rit-primary-2 | Druhá farba prechodu | #f97316 |
--rit-primary-text | Text na hlavnej farbe | #ffffff |
--rit-bg | Pozadie widgetu | transparent |
--rit-surface | Pozadie kariet a polí | #ffffff |
--rit-text | Text | #111827 |
--rit-muted | Vedľajší text | #6b7280 |
--rit-border | Rámčeky a linky | #e5e7eb |
--rit-radius | Zaoblenie rohov | 0px |
--rit-font | Písmo (font-family) | var(--font-body) |
--rit-size | Základná veľkosť písma | 16px |
--rit-shadow | Tieň kariet (box-shadow) | none |
--rit-max | Maximálna šírka | 100% |
--rit-gap | Základná medzera | 12px |
#rezervace {
--rit-primary: #e11d48;
--rit-primary-2: #f97316;
--rit-radius: 0px;
--rit-font: var(--font-body, Georgia, serif);
}
/* tmavý režim webu */
@media (prefers-color-scheme: dark) {
#rezervace { --rit-bg: #0b1120; --rit-surface: #111827; --rit-text: #e5e7eb; --rit-muted: #9ca3af; --rit-border: #1f2937; }
}
b) Selektory ::part()
Dôležité prvky widgetu sú sprístupnené cez ::part(). Môžete im nastaviť ľubovoľné CSS vlastnosti (písmo, veľkosť, ohraničenie, tieň, text-transform…). Je to stabilné verejné rozhranie — názvy partov nemeníme.
containercardheadertitleintrostepsstepstep-activeserviceservice-selectedsummarychipchip-selectedcalendarnavdayday-openday-selectedslotslot-selectedslot-takenformlabelinputcheckboxbuttonbutton-primaryalertalert-errorsuccessfooter
[data-reserveit]::part(button-primary) {
text-transform: uppercase;
letter-spacing: .05em;
}
[data-reserveit]::part(title) {
font-family: "Playfair Display", Georgia, serif;
font-size: 2rem;
}
[data-reserveit]::part(slot-selected) {
outline: 2px solid currentColor;
outline-offset: 2px;
}
[data-reserveit]::part(card) {
border: 0;
box-shadow: 0 20px 50px -20px rgba(0, 0, 0, .35);
}
::part() nepodporuje potomkov ani pseudotriedy vnútri (::part(card) .niečo nefunguje), stavy typu :hover a :focus-visible však áno: [data-reserveit]::part(slot):hover.c) Stavové atribúty kontajnera
Kontajner nesie atribúty data-reserveit-state (loading | ready | error) a data-reserveit-step (service | term | details | done). Podľa nich môžete upravovať okolie widgetu — skryť nadpis sekcie po rezervácii, zobraziť placeholder pri načítavaní a pod.
/* místo pro widget, dokud se nenačte (bez poskakování stránky) */
[data-reserveit][data-reserveit-state="loading"] { min-height: 480px; }
/* po dokončení rezervace skrýt úvodní text sekce */
#rezervace:has([data-reserveit-step="done"]) .intro { display: none; }
/* chyba → nabídnout telefon */
.fallback-phone { display: none; }
[data-reserveit-state="error"] + .fallback-phone { display: block; }
d) Vlastné CSS v administrácii
V nastaveniach widgetu (Vzhľad → Vlastné CSS) možno zapísať CSS, ktoré sa vloží dovnútra Shadow DOM. Vnútorné triedy začínajú rit- (napr. .rit-card, .rit-slot). Výhoda: platí všade, kde je widget vložený (aj v iframe a na rezervačnej stránke). Nevýhoda: vnútorná štruktúra sa môže s novými verziami meniť — na trvalé úpravy preto odporúčame CSS premenné a ::part().
.rit-card { border-width: 2px; }
.rit-slot { font-variant-numeric: tabular-nums; }
.docs-styled [data-reserveit] {
--rit-primary: #e11d48; --rit-primary-2: #f97316; --rit-radius: 0px; --rit-font: Georgia, serif;
}
.docs-styled [data-reserveit]::part(button-primary) { text-transform: uppercase; letter-spacing: .06em; }
.docs-styled [data-reserveit]::part(slot-selected) { outline: 2px solid #111; outline-offset: 2px; }
.docs-styled [data-reserveit]::part(title) { font-style: italic; }
7Kompletné príklady
Stránka služby s brandingom webu a meraním
<!doctype html>
<html lang="cs">
<head>
<meta charset="utf-8">
<title>Masáže — objednávka</title>
<style>
:root { --brand: #0f766e; --brand-2: #14b8a6; --font-body: "Nunito", system-ui, sans-serif; }
#rezervace {
--rit-primary: var(--brand);
--rit-primary-2: var(--brand-2);
--rit-font: var(--font-body);
--rit-radius: 10px;
--rit-bg: transparent;
max-width: 820px; margin: 0 auto;
}
#rezervace::part(button-primary) { text-transform: uppercase; letter-spacing: .05em; }
#rezervace[data-reserveit-state="loading"] { min-height: 480px; }
</style>
</head>
<body>
<h1>Relaxační masáž</h1>
<div id="rezervace"
data-reserveit="VAS-KLIC"
data-reserveit-service="12"
data-reserveit-locale="cs"></div>
<script src="http://sk.localhost:8103/widget/reserveit.js" async></script>
<script>
document.getElementById('rezervace').addEventListener('reserveit:booked', function (e) {
window.gtag && gtag('event', 'purchase', { transaction_id: e.detail.code, value: e.detail.price || 0, currency: 'CZK' });
});
</script>
</body>
</html>
Prihlásený zákazník (PHP)
<?php $prefill = ['name' => $user->name, 'email' => $user->email, 'phone' => $user->phone]; ?>
<div data-reserveit="VAS-KLIC"
data-reserveit-prefill="<?= htmlspecialchars(json_encode($prefill), ENT_QUOTES) ?>"></div>
<script src="http://sk.localhost:8103/widget/reserveit.js" async></script>
Viac widgetov na jednej stránke
Každý kontajner je samostatný widget s vlastným stavom, nastaveniami aj udalosťami (rozlíšite ich podľa event.detail.key alebo podľa prvku, na ktorom udalosť zachytíte).
<div data-reserveit="VAS-KLIC" data-reserveit-service="12"></div>
<div data-reserveit="VAS-KLIC" data-reserveit-service="15" style="--rit-primary:#7c3aed"></div>
<script src="http://sk.localhost:8103/widget/reserveit.js" async></script>
<script>
document.addEventListener('reserveit:booked', function (e) {
console.log('Widget', e.detail.key, 'v prvku', e.target, '→ rezervace', e.detail.code);
});
</script>
8Bezpečnosť a kompatibilita
- Žiadne cookies ani localStorage. Widget nič neukladá do prehliadača, takže nepotrebuje súhlas v cookie lište a neprekáža mu blokovanie cookies tretích strán.
- CORS bez preflightu. Požiadavky na API idú bez prihlasovacích údajov (
credentials: omit), server odpovedá CORS hlavičkami aj pri chybách. - Striktné CSP, Trusted Types, COEP. Žiadne inline skripty ani
eval, štýly cez Constructable Stylesheets, HTML cez Trusted Types politikureserveit, skript a API posielajúCross-Origin-Resource-Policy: cross-origin. - Izolácia a menné priestory. Jediný globál
ReserveIT, atribútydata-reserveit*, udalostireserveit:*, triedyrit-len vnútri Shadow DOM, písma pod názvami „ReserveIT …“ — nič nekoliduje s vaším webom ani inými knižnicami. - Viac widgetov na jednej stránke funguje nezávisle (aj s rovnakým kľúčom): každá inštancia má vlastný stav a požiadavky, ID vnútri majú predponu inštancie. Opakované načítanie skriptu nič neprepíše a „znečistený“
Object.prototypewebu neprekáža. - Povolené domény. V administrácii možno widget obmedziť na domény vášho webu — inde rezerváciu server odmietne.
- Prehliadače: aktuálny Chrome, Edge, Firefox, Safari (aj iOS) a Samsung Internet. Kódovanie stránky nehrá rolu (funguje aj na weboch vo windows-1250).
Čo povoliť v Content-Security-Policy webu
Len ak váš web posiela hlavičku CSP. font-src je potrebný kvôli písmam Inter/Montserrat (widget ich načítava z našej domény ako „ReserveIT Inter“ a „ReserveIT Montserrat“) — bez neho widget použije systémové písmo. style-src ani img-src povoľovať nie je potrebné.
script-src 'self' http://sk.localhost:8103;
connect-src 'self' http://sk.localhost:8103;
font-src 'self' http://sk.localhost:8103;
trusted-types reserveit; # jen pokud web používá direktivu trusted-types
frame-src http://sk.localhost:8103; # jen varianta s iframe
Pre české účty platí to isté s doménou https://reserveit.cz.