<section class="org-travel-time-calculator-block" data-component="travel-time-calculator" data-location="">
<div class="vebego-container">
<div class="calculator-content">
<svg class="ambient absolute -z-10 h-full w-full" preserveAspectRatio="xMidYMid slice" viewBox="0 0 1401 561" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_732_31)">
<path class="animate-wiggle" d="M-2050.14 -204.345L1538.67 -2276.34L3610.67 1312.46L21.8563 3384.46L-2050.14 -204.345Z" fill="url(#paint0_linear_732_31)" />
<path class="animate-wiggle" opacity="0.35" d="M-235.763 -1021.04C184.007 -844.351 514.379 -368.568 498.964 32.3718C481.557 487.334 63.3508 859.84 -256.582 932.059C-576.515 1004.28 -869.519 656.538 -468.412 471.643C-67.3055 286.749 116.195 264.613 302.527 -1.01921C259.546 -168.006 -47.7195 -422.581 -483.652 -646.742C-845.528 -833.228 -639.19 -1190.84 -235.763 -1021.04ZM743.374 -403.471C551.089 -574.814 489.961 -1069.37 661.244 -1309.44C793.684 -1495.02 1076.36 -1333.68 1004.04 -1160.87C858.856 -813.25 857.106 -584.44 881.882 -502.563C1123.47 -371.494 1565.15 -600.068 1652.51 -628.004C1865.35 -695.764 2057.55 -489.499 1784.38 -336.284C1318.25 -75.0371 933.111 -234.594 743.374 -403.471ZM2441.76 28.8834C2740.56 160.667 2742.79 590.227 2272.6 525.9C1818.24 463.679 1215.21 334.868 855.28 500.489C776.87 717.311 898.58 1141.45 1223.94 1711.81C1463.93 2132.35 883.395 2305.53 644.6 1988.36C335.85 1578.27 206.665 902.528 641.102 364.285C870.363 79.4919 1630.36 -328.355 2441.76 28.8834Z" fill="#562081" />
<path class="animate-wiggle" d="M619.906 283.605C1040.16 304.998 1934.43 736.16 2151.54 1733.93C2231.54 2101.51 1833.08 2394.62 1574.58 1912.78C1324.5 1447.48 1036.08 798.822 638.144 575.386C382.987 648.953 70.9201 1049.81 -239.065 1738.82C-468.239 2247.19 -1022.52 1824.1 -889.306 1386.33C-717.037 821.296 -175.264 243.348 619.906 283.605Z" fill="url(#paint2_linear_732_31)" />
<path class="animate-wiggle-2" d="M831.823 -74.8587C396.739 216.832 -233.363 80.0584 -517.676 -168.618C-801.989 -417.294 -676.358 -925.148 -232.682 -677.367C210.995 -429.586 356.12 -273.778 730.026 -280.429C856.731 -433.447 885.528 -891.581 799.415 -1449.29C728.136 -1912.07 1201.02 -1961.81 1315.63 -1471.74C1435.17 -961.448 1215.23 -332.216 831.823 -74.8587Z" fill="url(#paint3_linear_732_31)" />
</g>
<defs>
<linearGradient id="paint0_linear_732_31" x1="3569.5" y1="1265.2" x2="-2038.5" y2="-164.605" gradientUnits="userSpaceOnUse">
<stop stop-color="#501D79" />
<stop offset="0.1001" stop-color="#562081" />
<stop offset="0.4029" stop-color="#980D7F" />
<stop offset="0.5587" stop-color="#871280" />
<stop offset="0.8653" stop-color="#5C1E81" />
<stop offset="0.9028" stop-color="#562081" />
<stop offset="1" stop-color="#501D79" />
</linearGradient>
<linearGradient id="paint1_linear_732_31" x1="1940.44" y1="-897.473" x2="1576.67" y2="1225.63" gradientUnits="userSpaceOnUse">
<stop stop-color="#B92D85" stop-opacity="0.7" />
<stop offset="0.2185" stop-color="#C44072" stop-opacity="0.7655" />
<stop offset="0.6611" stop-color="#DF703F" stop-opacity="0.8983" />
<stop offset="1" stop-color="#F69915" />
</linearGradient>
<linearGradient id="paint2_linear_732_31" x1="1683.94" y1="1213.25" x2="-473.832" y2="1087.84" gradientUnits="userSpaceOnUse">
<stop stop-color="#D62B87" stop-opacity="0.7" />
<stop offset="0.2013" stop-color="#C02084" stop-opacity="0.7" />
<stop offset="0.505" stop-color="#980D7F" stop-opacity="0.7" />
<stop offset="0.9013" stop-color="#3F2584" stop-opacity="0.7" />
<stop offset="1" stop-color="#282B85" stop-opacity="0.7" />
</linearGradient>
<linearGradient id="paint3_linear_732_31" x1="-150.413" y1="-274.39" x2="1381.97" y2="-1527.36" gradientUnits="userSpaceOnUse">
<stop stop-color="#651B82" />
<stop offset="0.9974" stop-color="#901080" />
</linearGradient>
<clipPath id="clip0_732_31">
<rect width="1401" height="561" fill="white" />
</clipPath>
</defs>
</svg>
<h2 class="atm-heading ">Hoe reis je naar je werk?</h2>
<div class="atm-form-input ">
<input class="form-input" type="text" id="input" name="input">
<label class="atm-form-label form-label " for="input">Label</label>
<i class="atm-icon form-input-error-icon far fa-times "></i>
<i class="atm-icon form-input-valid-icon far fa-check "></i>
</div>
<div class="transport-options">
<div class="atm-form-radiobutton icon">
<input type="radio" id="transport-bus" name="transport" value="bus" class="peer hidden" checked>
<label for="transport-bus" class="radiobutton-icon-wrapper">
<i class="atm-icon far fa-external-link "></i>
</label>
</div>
<div class="atm-form-radiobutton icon">
<input type="radio" id="transport-train" name="transport" value="train" class="peer hidden">
<label for="transport-train" class="radiobutton-icon-wrapper">
<i class="atm-icon far fa-external-link "></i>
</label>
</div>
<div class="atm-form-radiobutton icon">
<input type="radio" id="transport-bicycle" name="transport" value="bicycle" class="peer hidden">
<label for="transport-bicycle" class="radiobutton-icon-wrapper">
<i class="atm-icon far fa-external-link "></i>
</label>
</div>
<div class="atm-form-radiobutton icon">
<input type="radio" id="transport-walking" name="transport" value="walking" class="peer hidden">
<label for="transport-walking" class="radiobutton-icon-wrapper">
<i class="atm-icon far fa-external-link "></i>
</label>
</div>
</div>
<a class="atm-button atm-button-primary button-md no-icon ">
<div class="button-content-wrapper">
<span class="button-content">
<span>Button</span>
<i class="atm-icon fal fa-arrow-right "></i>
</span>
</div>
<span class="button-triangle"></span>
</a>
</div>
</section>
<section class="org-travel-time-calculator-block" data-component="travel-time-calculator" data-location="{{location}}">
<div class="vebego-container">
<div class="calculator-content">
{{render "@ambient"}}
{{#if title}}
{{render '@heading' title merge=true}}
{{/if}}
{{ render '@input' input merge=true }}
<div class="transport-options">
{{#each transportOptions}}
{{render '@radiobutton' this merge=true icon=true}}
{{/each}}
</div>
{{ render '@button-primary' }}
</div>
</section>
{
"title": {
"text": "Hoe reis je naar je werk?",
"tag": "h2"
},
"transportOptions": [
{
"name": "transport",
"value": "bus",
"checked": true,
"modifier": "icon",
"icon": {
"class": "fa-solid fa-bus"
}
},
{
"name": "transport",
"value": "train",
"modifier": "icon",
"icon": {
"class": "fa-solid fa-train"
}
},
{
"name": "transport",
"value": "bicycle",
"modifier": "icon",
"icon": {
"class": "fa-solid fa-bicycle"
}
},
{
"name": "transport",
"value": "walking",
"modifier": "icon",
"icon": {
"class": "fa-solid fa-person-walking"
}
}
]
}
.org-travel-time-calculator-block {
@apply relative overflow-hidden rounded-lg;
.vebego-container {
@apply relative z-10;
}
.ambient {
@apply top-0 left-0;
}
.travel-time-block {
@apply flex gap-6 flex-col transition;
}
.atm-form-input {
@apply w-full;
.form-input {
@apply rounded-md py-8 w-full;
}
.atm-form-label {
@apply font-display text-primary;
}
}
.calculator-content {
@apply flex flex-col gap-4 lg:gap-8 p-8 lg:p-10 rounded-lg overflow-hidden relative transition min-h-[400px] justify-center;
}
.atm-heading {
@apply text-white;
}
.transport-options {
@apply flex justify-between gap-2 md:gap-3;
}
.results {
@apply text-white;
.result-description {
@apply mb-2 text-lg;
}
.result-amount {
@apply text-h1 font-semibold my-8 font-display;
}
}
.disclaimer {
@apply mt-8 text-white;
}
}
document.addEventListener('DOMContentLoaded', function () {
const calculatorBlock = document.querySelector(
'.org-travel-time-calculator-block'
);
const travelTimeBlock = document.querySelector('.travel-time-block');
const calculatorContent = document.querySelector(
'.org-travel-time-calculator-block .calculator-content'
);
const form = calculatorBlock?.querySelector('form') || calculatorBlock;
const postalCodeInput = calculatorBlock?.querySelector(
'input[type="search"]'
);
const transportRadios = calculatorBlock?.querySelectorAll(
'input[type="radio"]'
);
const submitButton = calculatorBlock?.querySelector(
'button[type="submit"]'
);
let resultContainer = calculatorContent?.querySelector(
'.travel-time-result'
);
if (!resultContainer && calculatorContent) {
resultContainer = document.createElement('div');
resultContainer.className = 'travel-time-result hidden font-display';
calculatorContent.appendChild(resultContainer);
}
if (submitButton) {
submitButton.addEventListener('click', handleSubmit);
} else if (form) {
form.addEventListener('submit', handleSubmit);
}
async function handleSubmit(e) {
e?.preventDefault();
const selectedTransport = Array.from(transportRadios || []).find(
(radio) => radio.checked
)?.value;
const postalCode = postalCodeInput?.value.trim();
const postalCodeRegex = /^[1-9][0-9]{3}\s?[A-Z]{2}$/i;
if (!postalCode) {
showError('Vul een postcode in');
postalCodeInput?.focus();
return;
}
if (!postalCodeRegex.test(postalCode)) {
showError(
'Vul een geldige Nederlandse postcode in (bijv. 1234 AB)'
);
postalCodeInput?.focus();
return;
}
if (!selectedTransport) {
showError('Selecteer een vervoersmiddel');
return;
}
try {
const postalCodeClean = postalCode.replace(/\s/g, '');
const vacancyPostcode = calculatorBlock?.dataset.vacancyPostcode;
const vacancyPostcodeClean = vacancyPostcode.replace(/\s/g, '');
const apiUrl = `/umbraco/surface/TravelTimeSurface/GetTravelTime?postalCode=${encodeURIComponent(
postalCodeClean
)}&travelMethod=${encodeURIComponent(
selectedTransport
)}&location=${encodeURIComponent(vacancyPostcodeClean)}`;
const response = await fetch(apiUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
if (!response.ok) {
if (response.status === 404) {
throw new Error('Reistijd calculator niet beschikbaar');
} else if (response.status === 400) {
throw new Error('Ongeldige invoer');
} else {
throw new Error(
`Gebruik een bestaande postcode`
);
}
}
// Only hide travel time block after successful response
travelTimeBlock?.classList.add('!hidden');
const responseText = await response.text();
let data;
try {
const parsedResponse = JSON.parse(responseText);
data =
typeof parsedResponse === 'string'
? JSON.parse(parsedResponse)
: parsedResponse;
} catch (parseError) {
throw new Error('Ongeldige JSON response ontvangen');
}
if (
!data ||
typeof data.distance === 'undefined' ||
typeof data.duration === 'undefined'
) {
throw new Error('Ongeldige data ontvangen');
}
showResult(data, selectedTransport);
} catch (error) {
console.error('Fout bij ophalen reistijd:', error);
showError(error.message || 'Er is een fout opgetreden');
}
}
function showResult(data, selectedTransport) {
if (!resultContainer) return;
const distance = data.distance
? `${data.distance.toFixed(1)} km`
: 'Onbekend';
const duration = data.duration
? `${Math.round(data.duration)} minuten`
: 'Onbekend';
const transportMethods = {
transit: 'met het openbaar vervoer',
train: 'met de trein',
bicycle: 'met de fiets',
walking: 'te voet',
};
const transportText =
transportMethods[selectedTransport] || 'met de auto';
resultContainer.innerHTML = `
<div>
<div class="mb-6">
<h1 class="text-h2 text-white mb-2">Je reisafstand is <span class="font-bold">${distance}</span>, je bent <span class="font-bold">${duration}</span> onderweg</h1>
<p class="text-md text-white">wanneer je <span class="font-bold">${transportText}</span> reist.</p>
</div>
<a class="atm-link !text-white mt-6" href="#">Terug</a>
${
data.route
? `<div class="mt-4 text-sm text-white"><strong>Route:</strong> ${data.route}</div>`
: ''
}
</div>
`;
resultContainer.classList.remove('hidden', 'bg-red-50', 'text-red-700');
const backLink = resultContainer.querySelector('a[href="#"]');
if (backLink) {
backLink.addEventListener('click', (e) => {
e.preventDefault();
travelTimeBlock?.classList.remove('!hidden');
resultContainer.classList.add('hidden');
});
}
}
function showError(message) {
if (!resultContainer) return;
resultContainer.innerHTML = `
<label class="text-white">${message}</label>
`;
resultContainer.classList.remove('hidden');
}
});
No notes defined.