<aside class="mol-vacancy-reminder " data-vacancy-reminder>
<div class="atm-paragraph reminder-message">
<p>Je bekeek eerder de vacature <strong>Teamcoach Schoonmaak | 30 uur per week | Maandag t/m Vrijdag | Ochtend | Amersfoort</strong>.</p>
</div>
<div class="atm-paragraph reminder-message-mobile">
<p><span class="reminder-lead">Je bekeek eerder de vacature</span><strong class="reminder-title">Teamcoach…</strong></p>
</div>
<div class="reminder-actions">
<a class="atm-button atm-button-primary button-md reminder-apply no-icon " href="#">
<div class="button-content-wrapper">
<span class="button-content">
<span>Solliciteren</span>
</span>
</div>
</a>
<button type="button" class="reminder-dismiss" data-vacancy-reminder-dismiss>
Sluiten
</button>
</div>
</aside>
<aside class="mol-vacancy-reminder {{modifier}}" data-vacancy-reminder>
{{ render '@paragraph' message }}
{{ render '@paragraph' messageMobile }}
<div class="reminder-actions">
{{ render '@button-primary' applyCta }}
<button type="button" class="reminder-dismiss" data-vacancy-reminder-dismiss>
{{ dismissLabel }}
</button>
</div>
</aside>
{
"modifier": "",
"message": {
"modifier": "reminder-message",
"isRichText": true,
"text": "<p>Je bekeek eerder de vacature <strong>Teamcoach Schoonmaak | 30 uur per week | Maandag t/m Vrijdag | Ochtend | Amersfoort</strong>.</p>"
},
"messageMobile": {
"modifier": "reminder-message-mobile",
"isRichText": true,
"text": "<p><span class=\"reminder-lead\">Je bekeek eerder de vacature</span><strong class=\"reminder-title\">Teamcoach…</strong></p>"
},
"applyCta": {
"element": "a",
"href": "#",
"modifier": "button-md reminder-apply",
"text": "Solliciteren",
"hasIcon": false,
"showTriangle": false
},
"dismissLabel": "Sluiten"
}
.mol-vacancy-reminder {
@apply bg-primary text-white font-display;
@apply fixed z-998 rounded-xl;
@apply flex items-center gap-2 px-3 py-3 md:gap-6;
@apply inset-x-4 bottom-4;
@apply md:inset-x-0 md:mx-auto md:w-176 md:max-w-[calc(100%-10rem)] md:gap-6 md:px-6;
@apply animate-fade-on-load;
@media (prefers-reduced-motion: reduce) {
animation: none;
}
.reminder-message {
@apply hidden md:block;
}
/* Needs truncation on mobile, so we use a different element for that. */
.reminder-message-mobile {
@apply block md:hidden;
}
.atm-paragraph {
@apply min-w-0 flex-1;
p {
@apply leading-tight mb-0;
}
}
.reminder-message p {
@apply text-md line-clamp-2;
}
.reminder-message-mobile {
.reminder-title {
@apply text-sm;
}
.reminder-lead,
.reminder-title {
@apply block truncate;
}
.reminder-lead {
@apply text-xs;
}
}
.reminder-actions {
@apply flex flex-shrink-0 items-center gap-1.5 md:gap-4;
}
.reminder-apply {
@apply bg-white text-secondary-col-1;
@apply text-sm px-3 md:text-lg md:px-4;
.button-content-wrapper {
@apply h-10;
}
}
.reminder-dismiss {
@apply cursor-pointer whitespace-nowrap text-sm underline-offset-2 md:text-md;
&:hover {
@apply underline;
}
}
}
// ============================================================================
// Vacancy application reminder
// ============================================================================
// Visitors who open the job apply page and leave without submitting get a small
// reminder bar on their next visit to the vacancy site, offering to take them
// back to the form they left.
//
// The bar itself is rendered server side by VacancyReminderMoleculeMapDefinition,
// which reads the same cookie this file writes. Only three things have to happen
// in the browser:
// - WRITE the cookie on the apply page. The form is a third-party Bullhorn
// Connexys widget injected into #cxsFormHolder, so there is no native submit
// to hook; the data comes from attributes the server already resolved.
// - CLEAR it from the vendor's onAfterSubmit callback, which the GTM
// 'application_completed' event in JobApplyPage.cshtml already relies on.
// - CLEAR it when the visitor dismisses the bar or withdraws consent.
//
// The cookie falls in Cookiebot's "preferences" category, following the convention
// documented in engage-browser-location.js: without that consent nothing is stored
// and anything previously stored is removed. Local environments don't load
// Cookiebot at all, so there it is written without a consent check to stay testable.
//
// Flip REQUIRES_CONSENT if this is ever reclassified as a functional cookie.
(function () {
'use strict';
// Must match VacancyConstants.ReminderCookieName in Vebego.Code, which reads it.
var COOKIE_NAME = 'vebegoVacancyReminder';
var COOKIE_MAX_AGE_SECONDS = 30 * 24 * 60 * 60;
var REQUIRES_CONSENT = true;
function writeReminder(reminder) {
// encodeURIComponent escapes the comma to %2C. A raw comma is outside
// RFC 6265 cookie-octet grammar, and ASP.NET Core Request.Cookies
// silently drops the whole cookie when it sees one.
document.cookie = COOKIE_NAME + '=' + encodeURIComponent(JSON.stringify(reminder)) +
'; max-age=' + COOKIE_MAX_AGE_SECONDS +
'; path=/; SameSite=Lax' +
(window.location.protocol === 'https:' ? '; Secure' : '');
}
function deleteReminder() {
document.cookie = COOKIE_NAME + '=; max-age=0; path=/';
}
function getBanner() {
return document.querySelector('[data-vacancy-reminder]');
}
function removeBanner() {
var banner = getBanner();
if (banner) {
banner.remove();
}
}
// Apply page only: everything else on the page is server rendered.
function record() {
var source = document.querySelector('[data-vacancy-reminder-source]');
if (!source) {
return;
}
// This page is the abandoned form, so its own URL is what "Solliciteren" returns to.
writeReminder({
jobTitle: source.dataset.jobTitle || '',
applyUrl: window.location.pathname + window.location.search,
});
}
function bindDismiss() {
var banner = getBanner();
var dismiss = banner && banner.querySelector('[data-vacancy-reminder-dismiss]');
if (dismiss) {
dismiss.addEventListener('click', function () {
deleteReminder();
banner.remove();
});
}
}
function preferencesConsentGiven() {
return !!(window.Cookiebot && window.Cookiebot.consent && window.Cookiebot.consent.preferences);
}
function isLocalEnvironment() {
var hostname = window.location.hostname;
return hostname === 'localhost'
|| hostname === '127.0.0.1'
|| hostname.endsWith('.127.0.0.1.nip.io');
}
function applyConsentState() {
if (!REQUIRES_CONSENT || preferencesConsentGiven()) {
record();
} else {
// Consent withdrawn or declined: clean up and take the bar off the page.
// The server had no way of knowing, since it only sees the cookie.
deleteReminder();
removeBanner();
}
}
// Always available, so the Connexys onAfterSubmit callback can clear the
// reminder regardless of consent state.
window.vebegoVacancyReminder = { clear: deleteReminder };
bindDismiss();
window.addEventListener('CookiebotOnConsentReady', applyConsentState);
if (window.Cookiebot && window.Cookiebot.consent) {
applyConsentState();
} else if (isLocalEnvironment() || !REQUIRES_CONSENT) {
// Local environments don't load Cookiebot: record anyway so the feature
// stays testable, mirroring engage-browser-location.js.
record();
}
})();
No notes defined.