Visi darbai

Rezervacijų platforma · kalendorius · serverinis sekimas

Vila Valentino

Privati vila Ukmergės rajone, prie Kliepšių ežero. Vietoj nuomos per Booking.com ar paprasto WordPress puslapio – sava rezervacijų sistema su prieinamumo kalendoriumi, geografine lokalizacija ir serveriniu konversijų sekimu.

Metai
2024–2026
Vaidmuo
Sukūrimas, dizainas, architektūra, palaikymas
Trukmė
Iteracinis
Sritis
Privataus poilsio nuoma · tiesioginės rezervacijos

Užvedę pelę ant peržiūros, pamatysite visą puslapį.

Aplankyti vilavalentino.lt

Trumpai

  • Savas prieinamumo kalendorius: mąsto naktimis, ne dienomis; pusiau atviras intervalas leidžia tą pačią dieną vienam svečiui išvykti, kitam atvykti
  • Deterministinė kainodara su įšaldytu pasiūlymo momentu – sezonai, savaitgalio ir darbo dienos naktys, paketai ir priedai apskaičiuojami centų tikslumu
  • iCal sinchronizacija su Airbnb ir Booking.com kas 10 min. + konfliktų aptikimas; persidengimą blokuoja Postgres GiST EXCLUDE apribojimas
  • Geografinis maršrutas: lietuviui rodoma LT, užsieniečiui – EN versija (Cloudflare šalies antraštė + MaxMind GeoLite2), su metiniu slapuku pakartotiniam apsilankymui
  • Serverinis konversijų sekimas (Meta CAPI + Google Ads OCI), atlaikantis Safari ITP, su sutikimo valdymu pagal Consent Mode v2

Iššūkis

Vilai Valentino reikėjo tiesioginio rezervacijų kanalo, nepriklausomo nuo komisinių. Užklausos vyko telefonu ir per OTA platformas, kurios pasiima 15–20 % nuo kiekvieno apsistojimo ir laiko svečio duomenis pas save. Paprastas WordPress puslapis su rezervacijų įskiepiu šios problemos neišsprendžia: prieinamumą tektų vesti rankiniu būdu, o kalendorius nesusikalbėtų su Airbnb ar Booking.com – atsirastų dvigubo užsakymo rizika.

Vila Valentino pagrindinis puslapis telefone

Sprendimas

Pradėjome ne nuo WordPress temos, o nuo duomenų modelio: prieinamumas, kainodara ir rezervacijos užklausa vienoje Postgres bazėje. Next.js App Router su LT/EN next-intl lokalizacija, savas prieinamumo kalendorius, deterministinė kainodara su įšaldytais pasiūlymo momentais, iCal sinchronizacija su išorinėmis platformomis ir serverinis konversijų sekimas, atlaikantis Safari ITP. Užsakymo apmokėjimo lange nėra – tai užklausų modelis, kuriame savininkas patvirtina datas rankiniu būdu.

Vila Valentino kainų puslapis telefone

Rezultatas

Vila priima rezervacijos užklausas tiesiogiai, be komisinių, ir matoma paieškoje pagal mergvakario, bernvakario, šeimos poilsio bei vietos raktažodžius LT ir EN kalbomis. Prieinamumas suvestas į vieną tiesos šaltinį iš rankinių blokų ir Airbnb / Booking.com kalendorių, o reklamos konversijos pasiekia Meta ir Google ir tada, kai naršyklė sekimą apriboja.

Kaip tai sukurta

Pradžios taškas

Vila Valentino – privati vila Ukmergės rajone, Kliepšių kaime prie Kliepšių ežero, maždaug valandos kelio nuo Vilniaus, Kauno ir Panevėžio. Keturi miegamieji, iki dešimties suaugusiųjų, pirtis, kubilas, terasa, privatus tvenkinys. Svečiai užsisakydavo telefonu arba per OTA platformas. Kiekviena rezervacija per Booking.com ar Airbnb reiškė komisinį ir svetimą sąsają tarp savininko ir svečio. Tad nepradėjome nuo WordPress temos – pradėjome nuo duomenų modelio: kaip vienoje vietoje laikyti prieinamumą, kainą ir užklausą taip, kad vienas kitam neprieštarautų.

Kodėl ne Booking.com ir ne WordPress įskiepis

Augančiai nuomai abu keliai turi tą pačią silpną vietą. OTA platforma duoda srautą, bet ima procentą ir laiko klientų duomenis pas save – savininkas nemato, kas, iš kur ir kada domėjosi. WordPress su rezervacijų įskiepiu atrodo pigiau, bet prieinamumą tenka vesti rankiniu būdu, o įskiepio kalendorius nesusikalba su Airbnb – pakanka vieno praleisto bloko ir tas pats savaitgalis parduodamas du kartus. Vila Valentino šį etapą peržengė iš karto: visa logika sutelkta vienoje sistemoje – viena duomenų bazė, vienas tiesos šaltinis apie datą, kainą ir užklausą.

Prieinamumo kalendorius, mąstantis naktimis

Rezervacijos kalendorius sukurtas nuo nulio. Esminis sprendimas – jis skaičiuoja naktimis, ne dienomis: apsistojimas saugomas kaip pusiau atviras intervalas nuo atvykimo iki išvykimo, todėl tą pačią dieną vienas svečias gali išvykti ryte, o kitas atvykti po pietų, ir kalendorius nelaiko tos dienos užimta abiem. Tai industrijos standartas (Airbnb, Booking.com), kurio paprasti įskiepiai dažnai nesilaiko.

Prieinamumas kiekvienai datai suvedamas iš dviejų šaltinių: rankinių blokų, kuriuos savininkas pažymi administravimo pusėje, ir importuotų iCal įvykių iš išorinių platformų. Tą patį laiką naują rezervaciją galima priimti tik tada, kai nė vienas aktyvus blokas su intervalu nepersidengia. Atvykimo dieną galioja 15 val. Vilniaus laiko riba: po jos einamoji diena nebepasirenkama kaip atvykimo data – kad svečias nespėtų užsisakyti laiko, kurio jau nebėra.

Laisvų datų kalendorius telefone

Deterministinė kainodara su įšaldytu pasiūlymu

Kainą skaičiuoja deterministinė logika, ne spėjimas. Iš sezonų taisyklių ir savininko nustatytų išimčių apskaičiuojama kiekvienos nakties kaina: atskiros darbo dienos ir savaitgalio nakties įkainiai, pirmos nakties priemoka, savaitės (7 naktų) ir mėnesio (30 naktų) paketai bei pasirinkti priedai – pirtis, kubilas žiemą. Visos sumos saugomos eurų centais, be slankiojo kablelio paklaidų.

Pateikus užklausą, apskaičiuotas pasiūlymas įšaldomas kaip atskira momentinė kopija: net vėliau pakeitus sezono įkainį, svečiui rodyta kaina nepasikeičia. Tai užklausų modelis – apmokėjimo lange nėra; pasiūlymas suskaidomas į avansą ir likutį, o datas savininkas patvirtina rankiniu būdu.

Pasirinktoms datoms apskaičiuota kaina telefone

iCal sinchronizacija ir konfliktų aptikimas

Vila skelbiama ne vien savo svetainėje – datos užimamos ir per Airbnb bei Booking.com. Kad tas pats savaitgalis nebūtų parduotas du kartus, foninis darbuotojas kas 10 minučių importuoja išorinių platformų iCal kalendorius, sulygina juos su esamais įrašais ir paženklina dingusius. Jei importas grįžta tuščias, o bazėje yra aktyvių įvykių, sistema nesendina visų įrašų iš karto – taip vienas sugedęs atsakymas neištrina viso kalendoriaus.

Po kiekvienos sinchronizacijos tikrinami persidengimai: jei rankinis blokas ir importuotas įvykis kertasi, fiksuojamas konflikto stebėjimas su audito pėdsaku. Galutinę dvigubo užsakymo apsaugą užtikrina pati duomenų bazė – Postgres GiST EXCLUDE apribojimas fiziškai neleidžia įrašyti dviejų persidengiančių rankinių blokų.

Geografinis kalbos maršrutas

Lietuvis turi pamatyti lietuvišką versiją, užsienietis – anglišką, ir nė vienam nereikia jos ieškoti. Pagrindinis adresas veikia kaip maršruto sprendimas: pagal Cloudflare šalies antraštę ir MaxMind GeoLite2 nustatoma lankytojo šalis ir parenkama LT arba EN versija. Pasirinkimas įrašomas į metus galiojantį slapuką, todėl pakartotinis apsilankymas geolokacijos nebekartoja. Paieškos robotai (Googlebot ir kt.) visada nukreipiami į LT versiją, atitinkančią hreflang numatytąją kalbą. Lokalizaciją tvarko next-intl „as-needed“ režimu – lietuviškas turinys be kalbos prefikso, angliškas po /en.

Angliška Vila Valentino versija telefone

Indeksavimas ir ketinimo puslapiai

Vietoj vieno bendro puslapio vila turi atskirus puslapius pagal svečio ketinimą: sodyba mergvakariui, bernvakariui, šeimos poilsiui, kompanijai, prie ežero, su kubilu, Kalėdoms. Kiekvienas paruošiamas serveryje (SSR) su lietuviškomis meta žymomis ir JSON-LD LodgingBusiness struktūra, todėl Google iškart mato turinį, ne tuščią karkasą. Search Console duomenimis (90 d.) vila indeksuota daugiau nei 25 puslapiais LT ir EN kalbomis ir rikiuojasi pagal ne prekės ženklo raktažodžius – „sodyba mergvakariui“ ties 5–6 pozicija, „sodyba bernvakariui“ ties 7, vietos užklausos pagal Ukmergės rajoną. Tarptautiniai parodymai ateina ne tik iš Lietuvos, bet ir iš Jungtinės Karalystės, Latvijos bei kitų šalių – EN versija pasiekia užsienio svečią.

Puslapis „Sodyba mergvakariui“ telefone

Infrastruktūra ir foniniai darbai

Vila sukasi mūsų prižiūrimuose serveriuose: atskiri konteineriai svetainei (web) ir foniniam darbuotojui (worker), Postgres 16 antrojo lygio duomenų bazė, Redis eilėms ir diegimas per Coolify ant Hostinger VPS. Foninis darbuotojas valdo tris eiles: el. paštą (užklausos pranešimas savininkui), kalendorių sinchronizaciją (iCal importas, konfliktų rekonciliacija) ir priežiūrą (pasenusių įrašų bei limitų valymas). Formą nuo šiukšlinimo saugo Cloudflare Turnstile ir užklausų ribojimas, o klaidas fiksuoja Sentry. Administravimo pusė (better-auth) leidžia savininkui peržiūrėti užklausas, blokuoti datas, valdyti sezonų kainodarą ir iCal šaltinius.

Sekimas – trys kanalai, atlaikantys naršyklės apribojimus

Per pastaruosius metus naršyklės apkarpė didelę dalį duomenų, kuriais remiasi reklamos. Iš pirmo žvilgsnio atrodo, kad jos konvertuoja prasčiau, nors realybėje dalis konversijų tiesiog nebefiksuojama. Vilos Valentino užklausos signalas siunčiamas ne vien iš naršyklės: pagrindiniai įvykiai keliauja iš serverio, su sutikimo valdymu ir geografiniu kontekstu, kad Meta ir Google tą patį įvykį suskaičiuotų vieną kartą.

Naršyklės analitika

GA4 · Consent Mode v2 · PostHog

  • GA4 puslapių ir įvykių sekimas; pagrindinė konversija – pateikta rezervacijos užklausa
  • Consent Mode v2: analitikos ir rinkodaros kanalai įsijungia tik gavus atitinkamą sutikimą
  • PostHog seansų įrašai ir piltuvėliai – įjungiami tik su analitikos sutikimu

Meta

Pixel naršyklėje + CAPI iš serverio

  • Naršyklės Pixel ir serverinis CAPI siunčia tą patį įvykį su bendru event ID
  • Dedublikacija pagal event ID – Meta nesuskaičiuoja vienos užklausos dukart
  • Geografinis praturtinimas (šalis, miestas); kontaktiniai duomenys paverčiami maišos kodu (hash) prieš siuntimą
  • Įvykis pasiekia Meta ir tada, kai naršyklę riboja Safari ITP

Google Ads

OCI / Enhanced Conversions

  • Click ID fiksavimas iš reklamos nuorodų: gclid, gbraid, wbraid
  • Serverinis konversijų perdavimas su maišos kodu paverstais kontaktiniais duomenimis
  • Sutikimu valdomas; veikia po naršyklės sekimo apribojimų

Trys kanalai, vienas event ID. Daugelyje svetainių sekimas vyksta tik naršyklėje ir tyli, kai ją apriboja Safari ITP – čia pirkimo signalas keliauja ir iš serverio, todėl optimizacija nelieka aklame lange.

Architektūra

Next.js App Router su serveriniu atvaizdavimu ir next-intl LT/EN lokalizacija. Drizzle ORM virš PostgreSQL 16 (antrojo lygio bazė): prieinamumas, rankiniai blokai, iCal įvykiai, sezonų taisyklės, kainų išimtys, kainodaros momentinės kopijos ir užklausos. Redis + BullMQ trims eilėms (el. paštas, kalendorių sinchronizacija, priežiūra). better-auth administravimo prieigai. Visa SEO/geo/sekimo kompetencija perimta per bendrus @abyss paketus. Diegimas per Docker + Coolify ant Hostinger VPS, atskiri web ir worker konteineriai.

Front-end
Next.js App Router · React · SSR
Back-end
Node.js · Drizzle ORM · TypeScript · Zod
DB
PostgreSQL 16 (Tier 2) · GiST EXCLUDE apsauga
Eilės
Redis · BullMQ (el. paštas · kalendorius · priežiūra)
Lokalizacija
next-intl (LT/EN, as-needed) · MaxMind GeoLite2
Sekimas
GA4 · Consent Mode v2 · Meta CAPI · Google Ads OCI · PostHog
SEO
SSR · hreflang · sitemap · JSON-LD LodgingBusiness
Sauga
Cloudflare Turnstile · užklausų ribojimas · Sentry
Auth
better-auth (administravimas)
Ops
Docker · Coolify · Hostinger VPS · web + worker
  • Next.js App Router
  • next-intl (LT/EN)
  • Drizzle + PostgreSQL 16
  • Redis + BullMQ
  • better-auth
  • MaxMind GeoLite2
  • Meta CAPI
  • Google Ads OCI
  • GA4 + Consent Mode v2
  • Cloudflare Turnstile
  • JSON-LD LodgingBusiness
  • Docker · Coolify · Hostinger VPS

Geolokalizacija ir kalbų maršrutas

Vietoj atskiro AI sluoksnio vila remiasi tiksliomis integracijomis. Geografinis maršrutas pagal Cloudflare šalies antraštę ir MaxMind GeoLite2 parenka LT arba EN versiją dar prieš pirmą piešinį, metinis slapukas įsimena pasirinkimą, o paieškos robotai nukreipiami į hreflang numatytąją LT versiją. Lokalizaciją tvarko next-intl „as-needed“ režimu. Visa ši kompetencija – sekimas, geolokacija, SEO, sauga – perimta per bendrus @abyss paketus, todėl ta pati patikrinta logika veikia ir kituose projektuose.

Ką teko išspręsti

Naktimis, ne dienomis

Rezervacija turi būti saugoma kaip pusiau atviras intervalas [atvykimas, išvykimas), kad išvykimo diena liktų laisva kitam atvykti. Paprasti įskiepiai laiko dieną „užimta arba laisva“ – todėl tos pačios dienos persidengimo netvarko ir praranda apyvartą.

Vienas prieinamumo tiesos šaltinis

Prieinamumą reikėjo suvesti iš rankinių blokų ir iCal įvykių (Airbnb, Booking.com) į vieną atsakymą be dublikatų. Skirtingų platformų kalendoriai atnaujinami nevienodu dažnumu – sulyginimas turi būti atsparus vėlavimui.

Tuščio importo apsauga

Jei išorinis kalendorius grįžta tuščias dėl tinklo klaidos, naivi logika paženklintų visus įrašus kaip dingusius ir atlaisvintų užimtas datas. Reikėjo apsaugos: tuščias atsakymas nesendina aktyvių įrašų iš karto.

Dvigubo užsakymo apsauga DB lygmenyje

Programos patikrinimo neužtenka – lygiagretūs įrašai gali prasprūsti. Persidengimą fiziškai blokuoja Postgres GiST EXCLUDE apribojimas, todėl dvi besikertančios rezervacijos negali atsidurti bazėje net teoriškai.

Įšaldytas kainos pasiūlymas

Sezono įkainis gali pasikeisti po to, kai svečias jau gavo pasiūlymą. Apskaičiuota kaina saugoma kaip momentinė kopija, susieta su užklausa, todėl vėlesni pakeitimai jau pateiktos sumos nebekeičia.

Sutikimo laiko žemėlapis

Naršyklės ir serveriniai įvykiai elgiasi skirtingai pagal Consent Mode v2 būseną. Reikėjo tiksliai apibrėžti, kuris įvykis išleidžiamas ir kokia forma iki sutikimo, kad nebūtų pažeistas reglamentas ir neprarasta konversija.

Atribucija po Safari ITP

Naršyklės Meta Pixel ir serverinis CAPI turi siųsti tą patį įvykį, ne du. Jei event ID arba kontekstas išsiskiria, Meta optimizuoja pagal iškreiptus duomenis. Dedublikacija per bendrą event ID sprendžia tai abiejuose – Meta ir Google – kanaluose.

Geo maršrutas prieš pirmą piešinį

Kalbos parinkimas turi įvykti kaip maršruto sprendimas serveryje, ne kaip vėlesnė naršyklės korekcija – kitaip robotai ir reklamos srautas pamatytų netinkamą versiją. Sprendimas priimamas pagal Cloudflare antraštę dar prieš atvaizduojant turinį.

Dvikryptė kalendoriaus sinchronizacija

Importuoti svetimų platformų datas – tik pusė darbo. Kad Airbnb ar Booking.com nepriimtų rezervacijos dienomis, kurias vila užėmė tiesiogiai, vilos užimtų datų informacija eksportuojama atgal kaip iCal srautas. Nuoroda apsaugota atšaukiamu parašu (HMAC) – ją galima bet kada nutraukti, o vidiniai duomenys lieka neatskleisti.

Kuo lenkia standartą

  • Tiesioginis kanalas be komisinių

    Užklausa ateina tiesiai pas savininką, ne per OTA platformą su 15–20 % mokesčiu. Svečio duomenys lieka vilos pusėje, ne tarpininko.

  • Vienas prieinamumo tiesos šaltinis

    Rankiniai blokai ir Airbnb / Booking.com kalendoriai suvesti į vieną atsakymą su automatine sinchronizacija ir konfliktų aptikimu.

  • Dvigubo užsakymo apsauga pamate

    Persidengimą blokuoja pati duomenų bazė per GiST EXCLUDE apribojimą, ne tik programos patikra. Tas pats laikas fiziškai negali būti parduotas du kartus.

  • Kalendorius pagal industrijos standartą

    Pusiau atviras nakties intervalas ir 15 val. atvykimo riba elgiasi kaip Airbnb ar Booking.com – be klaidų, kurias daro paprasti įskiepiai.

  • Skaidri deterministinė kainodara

    Sezonai, savaitgalio ir darbo dienos naktys, paketai ir priedai apskaičiuojami centų tikslumu, o pasiūlymas įšaldomas – svečiui rodyta kaina nepasikeičia.

  • Atribucija po Safari ITP

    Serverinis sekimas reiškia, kad Meta ir Google užklausos signalą gauna ir tada, kai naršyklė tyli. Reklamos optimizacija nelieka aklame lange.

  • Lietuviškas SEO ir geo maršrutas

    Ketinimo puslapiai (mergvakaris, bernvakaris, šeima) su SSR ir JSON-LD; lietuviui rodoma LT, užsieniečiui – EN, automatiškai pagal šalį.

  • Duomenų nuosavybė ir prognozuojama kaina

    Visi duomenys – savoje Postgres bazėje, be procento nuo apsistojimo. Mėnesinė serverio sąskaita nepriklauso nuo užsakymų skaičiaus.

  • Sutikimu grįstas sekimas

    Analitikos ir rinkodaros žymos įsijungia tik gavus sutikimą (Consent Mode v2), o kontaktiniai duomenys siunčiami tik maišos kodu. Privatumas įrašytas į sekimo pamatą, ne prilipdytas šone.

Vila Valentino ir standartinis sprendimas

Kas lyginamaWordPress + rezervacijų įskiepisVila Valentino
Komisiniai už rezervacijąOTA 15–20 % arba įskiepio licencija0 % – tiesioginė užklausa
Prieinamumo sinchronizacijaRankinė arba atskiri kalendoriaiAutomatinis iCal importas + konfliktų aptikimas
Dvigubo užsakymo apsaugaTik įskiepio logikaPostgres GiST EXCLUDE (DB lygmuo)
KainodaraFiksuota arba rankinėDeterministinė: sezonai, paketai, priedai, įšaldytas pasiūlymas
LokalizacijaVertimo įskiepisnext-intl + geo maršrutas (Cloudflare / MaxMind)
Reklamos sekimas po Safari ITPTik naršyklės pikselisServerinis Meta CAPI + Google Ads OCI
Lietuviškas SEO matomumasĮskiepis + vienas bendras puslapisSSR + JSON-LD + ketinimo puslapiai
Klientų duomenų nuosavybėPriklauso platformai / įskiepiui100 % savoje Postgres bazėje

Pažiūrėkite patys: Vila Valentino svetainė

Kas pasikeitė

Rezervacijų kanalas
Be komisinių
Tik telefonas / OTA → Tiesioginė svetainė
Lokalizacijos aprėptis
2 kalbos
0 kalbų → LT + EN su geo maršrutu
Paieškos matomumas
Search Console, 90 d.
OTA profilis → 25+ indeksuotų puslapių (LT + EN)
Ne prekės ženklo užklausos
Sava ketinimo puslapiuose
— → „mergvakaris“ ~5–6, „bernvakaris“ ~7 poz.
Prieinamumo šaltinis
Vienas tiesos šaltinis
Rankinis kalendorius → Rankiniai blokai + Airbnb/Booking iCal
Reklamos matomumas po Safari ITP
Atribucija nelieka aklame lange
Dalinis (tik naršyklė) → Pilnas (serverinis CAPI + OCI)
Dvigubo užsakymo rizika
Persidengimas negalimas
Galima → Postgres EXCLUDE apsauga
Tarptautinis pasiekiamumas
Search Console, 90 d.
— → LT + GBR, LVA, AUS parodymai

Konkretūs sprendimo receptai – prieinamumo intervalų logika, kainodaros formulė, iCal rekonciliacijos ir konfliktų aptikimo seka, event ID dedublikacijos schema – viešai nepateikiami. Rimtam užsakovui pristatomi atskirai.

Reikia panašios svetainės?

Papasakokite, ką sumanėte. Kiekvieną užklausą perskaitome patys.

Aptarti projektą