const KCC = function() {
const LOGIN_BOX_CONTAINER = 'kcc_login_box_container';
const LOGIN_BOX = 'login_box';
const LOGIN_SCREEN_1 = 'login_screen_1';
const LOGIN_SCREEN_2 = 'login_screen_2';
const REGISTER_FIELDS = 'register_fields';
const LOGIN_FIELDS = 'login_fields';
const LOGIN_SYSTEM_TEMPLATE = 'login_system_template';
const LOGIN_BUTTON_TEMPLATE = 'login_button_template';
const LOGIN_BOX_WRAPPER = 'login_box_wrapper';
const STATUS_LINE_TEMPLATE = 'status_line_template';
const PROFILE_TEMPLATE = 'profile_template';
const PERSONAL_DATA_SCREEN = 'personal_data_screen';
const CLUB_LEVEL_SCREEN = 'club_level_screen';
const MY_RESERVATIONS_SCREEN = 'my_reservations_screen';
const LOGIN_COMMON_FIELDS = 'login_common_fields';
const ACTIVE_RESERVATIONS_LIST = 'active_reservations_list';
const PAST_RESERVATIONS_LIST = 'past_reservations_list';
const FOUND_RESERVATIONS_LIST = 'found_reservations_list';
const RESERVATION_TEMPLATE = 'reservation_template';
const MODIFY_RESERVATION_BTN = 'modify_reservation_btn';
const CANCEL_RESERVATION_BTN = 'cancel_reservation_btn';
const CANCELLED_STATUS = 'Cancelado';
const EMBEDDED_LOGIN_FORM_ID = 'embedded_login_form';
const DEFAULT_LOGIN_FORM_ID = 'login_form';
const RECOVER_PASSWORD_MODAL = 'recover_password_modal';
const WELCOME_MODAL = 'welcome_modal';
const RESERVATIONS_URLS = {
'Kora Kiliki': 'https://koraliving.com/kiliki/mis-reservas.html',
'Kora Lluna': 'https://koraliving.com/lluna/mis-reservas.html',
'Kora Tigot': 'https://koraliving.com/tigot/mis-reservas.html',
'Kora Olea': 'https://koraliving.com/olea/mis-reservas.html',
'Kora Nivaria Beach': 'https://koraliving.com/nivariabeach/mis-reservas.html',
'Kora Green City': 'https://koraliving.com/greencity/mis-reservas.html',
};
return {
config: {
// All configs here
STATUS_WIDGET: 'club_status_widget',
PROFILE_WIDGET: 'club_profile_widget',
LOGIN_WIDGET: 'club_login_widget',
EMBEDDED_LOGIN_WIDGET: 'inline_club_form',
LOGIN_SCREEN_NUMBER: 1,
LOGIN_SCREEN_REGISTER_MODE: true,
EMBEDDED_LOGIN_SCREEN_NUMBER: 1,
EMBEDDED_LOGIN_SCREEN_REGISTER_MODE: true,
COOKIE_DOMAIN: '',
PROFILE_SCREEN: PERSONAL_DATA_SCREEN,
SCRIPT_DOMAIN: '',
USER_SESSION_ID: null,
},
user_data: null,
escapeHtml: (str) => {
if (str === null || str === undefined) return '';
const text = String(str);
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
let escaped = text.replace(/[&<>"']/g, (m) => map[m]);
escaped = escaped
.replace(/<small>/gi, '')
.replace(/<\/small>/gi, '');
return escaped;
},
init: async () => {
KCC.setScriptDomain();
await KCC.loadTranslations();
KCC.loadStyles();
KCC.loadFB();
KCC.loadGoogle();
KCC.createLoginElement();
KCC.attachEvents();
KCC.config.USER_SESSION_ID = KCC.loadUserSessionId();
if (KCC.config.USER_SESSION_ID) {
await KCC.loadUserData(KCC.config.USER_SESSION_ID);
}
KCC.renderLoginSystem();
KCC.renderLoginBox();
KCC.renderEmbeddedLoginBox();
KCC.renderProfileWidget();
},
attachEvents: () => {
const status_container = document.getElementById(KCC.config.STATUS_WIDGET);
const login_container = document.getElementById(KCC.config.LOGIN_WIDGET);
const profile_container = document.getElementById(KCC.config.PROFILE_WIDGET);
const embedded_login_container = KCC.config.EMBEDDED_LOGIN_WIDGET
? document.getElementById(KCC.config.EMBEDDED_LOGIN_WIDGET)
: null;
status_container.addEventListener('click', (e) => {
if(e.target.matches(".login_trigger_btn") || e.target.closest('.login_trigger_btn')){
const signSection = document.getElementById(LOGIN_BOX_CONTAINER);
if (signSection) {
signSection.style.display = signSection.style.display === 'none' ? '' : 'none';
}
}
if(e.target.matches(".logout_btn")){
KCC.logout();
}
if(e.target.matches(".profile_btn")){
KCC.profileRedirect();
}
if(e.target.closest('.status_line') || e.target.matches(".close_status")){
const triggerElm = document.querySelector('.status_line');
const targetElm = document.querySelector('.status_menu');
triggerElm.classList.toggle('on');
targetElm.classList.toggle('on');
}
});
login_container.addEventListener('click', (e) => {
if(e.target.matches(".sign_with_mail") || e.target.closest('.sign_with_mail')){
KCC.config.LOGIN_SCREEN_NUMBER = 2;
KCC.renderLoginBox();
}
if(e.target.matches(".main_login_back")){
KCC.config.LOGIN_SCREEN_NUMBER = 1;
KCC.renderLoginBox();
}
if(e.target.matches(".change_login_mode_btn")){
KCC.config.LOGIN_SCREEN_REGISTER_MODE = !KCC.config.LOGIN_SCREEN_REGISTER_MODE;
KCC.renderLoginBox();
}
if(e.target.matches(".sign_with_google") || e.target.closest('.sign_with_google')){
e.preventDefault();
const googleButton = KCC.hiddenButtonContainer.querySelector('div[role="button"]');
if (googleButton) {
googleButton.click();
} else {
console.log('Fallback: using prompt method');
google.accounts.id.prompt();
}
}
if(e.target.matches(".sign_with_fb") || e.target.closest('.sign_with_fb')){
FB.login(function(response) {
if (response.authResponse) {
KCC.socialLoginSend('facebook', response.authResponse.accessToken);
} else {
console.log('User cancelled login or did not fully authorize.');
}
}, {scope: 'public_profile,email'});
}
if(e.target.matches(".close_login_btn") || e.target.closest('.close_login_btn')){
const signSection = document.getElementById(LOGIN_BOX_CONTAINER);
if (signSection) {
signSection.style.display = 'none';
}
}
if(e.target.matches(".recover_password_btn") || e.target.closest('.recover_password_btn')){
KCC.openRecoverPasswordModal();
}
});
login_container.addEventListener('submit', (e) => {
e.preventDefault();
const submitBtn = e.target.querySelector('.login_form_btn');
if (!submitBtn) return;
KCC.processEmailLogin(submitBtn.getAttribute('in_register') === 'true');
});
embedded_login_container?.addEventListener('click', (e) => {
if(e.target.matches(".sign_with_mail_2") || e.target.closest('.sign_with_mail_2')){
KCC.config.EMBEDDED_LOGIN_SCREEN_NUMBER = 2;
KCC.renderEmbeddedLoginBox();
}
if(e.target.matches(".main_login_back_2")){
KCC.config.EMBEDDED_LOGIN_SCREEN_NUMBER = 1;
KCC.renderEmbeddedLoginBox();
}
if(e.target.matches(".change_login_mode_btn_2")){
KCC.config.EMBEDDED_LOGIN_SCREEN_REGISTER_MODE = !KCC.config.EMBEDDED_LOGIN_SCREEN_REGISTER_MODE;
KCC.renderEmbeddedLoginBox();
}
if(e.target.matches(".recover_password_btn_2") || e.target.closest('.recover_password_btn_2')){
KCC.openRecoverPasswordModal();
}
if(e.target.matches(".sign_with_google_2") || e.target.closest('.sign_with_google_2')){
e.preventDefault();
const googleButton = KCC.hiddenButtonContainer.querySelector('div[role="button"]');
if (googleButton) {
googleButton.click();
} else {
console.log('Fallback: using prompt method');
google.accounts.id.prompt();
}
}
if(e.target.matches(".sign_with_fb_2") || e.target.closest('.sign_with_fb_2')){
FB.login(function(response) {
if (response.authResponse) {
KCC.socialLoginSend('facebook', response.authResponse.accessToken, EMBEDDED_LOGIN_FORM_ID);
} else {
console.log('User cancelled login or did not fully authorize.');
}
}, {scope: 'public_profile,email'});
}
});
embedded_login_container?.addEventListener('submit', (e) => {
e.preventDefault();
const submitBtn = e.target.querySelector('.login_form_btn_2');
if (!submitBtn) return;
KCC.processEmailLogin(submitBtn.getAttribute('in_register') === 'true', EMBEDDED_LOGIN_FORM_ID);
});
profile_container?.addEventListener('click', (e) => {
if(e.target.matches(".personal_data_btn")){
KCC.config.PROFILE_SCREEN = PERSONAL_DATA_SCREEN;
KCC.renderProfileWidget();
}
if(e.target.matches(".level_data_btn")){
KCC.config.PROFILE_SCREEN = CLUB_LEVEL_SCREEN;
KCC.renderProfileWidget();
}
if(e.target.matches(".reservation_data_btn")){
KCC.foundReservations = [];
KCC.config.PROFILE_SCREEN = MY_RESERVATIONS_SCREEN;
if (!('reservations' in KCC.user_data)){
KCC.loadReservations().then(() => {
KCC.renderProfileWidget();
});
return;
}
KCC.renderProfileWidget();
}
if(e.target.matches(".modify_user_btn")){
KCC.modifyUser();
}
if(e.target.matches(".delete_account")){
if (confirm(KCC.t('T_question_delete_account'))) {
KCC.deleteAccount();
}
}
if(e.target.matches(".change_password")){
KCC.changePassword();
}
if(e.target.matches(".rate_trigger")){
const targetConditionsId = e.target.dataset.reservation_id;
const targetElm = document.querySelector('.rate_conditions[data-reservation_id="' + targetConditionsId + '"]');
targetElm.classList.toggle('on');
}
if(e.target.matches(".search_btn") || e.target.closest('.search_btn')){
document.querySelector('.seach_box').classList.add('active');
document.querySelector('#reservation_search').focus();
}
if(e.target.matches(".modify_reservation") || e.target.matches(".cancel_reservation")){
const url = RESERVATIONS_URLS[e.target.dataset.hotel];
if (url) {
document.location.href = url;
}
}
});
profile_container?.addEventListener("input", (e) => {
if(e.target.matches("#reservation_search")){
const searchStr = e.target.value;
if (searchStr.length >= 3){
KCC.foundReservations = KCC.searchReservations(searchStr);
} else {
KCC.foundReservations = [];
}
document.querySelector(".profile_screen .content_wrapper").innerHTML = KCC.renderTemplate(MY_RESERVATIONS_SCREEN);
}
});
},
renderLoginSystem: () => {
let container = document.getElementById(KCC.config.STATUS_WIDGET);
container.innerHTML = KCC.renderTemplate(LOGIN_SYSTEM_TEMPLATE);
container = document.getElementById(KCC.config.LOGIN_WIDGET);
if (container){
container.innerHTML = KCC.renderTemplate(LOGIN_BOX_WRAPPER);
}
},
buildLoginTexts: (inRegister, suffix = '') => ({
title: inRegister
? KCC.t('T_register_widget_title').replace('@@club_name@@', 'Kora Rewards')
: KCC.t('T_login_widget_title').replace('@@club_name@@', 'Kora Rewards'),
back: inRegister
? KCC.t('T_use_other_register_method')
: KCC.t('T_use_other_access_method'),
form_btn: inRegister
? KCC.t('T_create_account')
: KCC.t('T_acceder'),
privacy: inRegister
? `${KCC.t('T_on_register_accept')} ${KCC.t('T_he_leido_2')}`
: '',
promo_accept: inRegister
? `
${KCC.t('T_accept_commercial_communications')}
`
: '',
changeMode: inRegister
? `${KCC.t('T_tienes_cuenta')} `
: `${KCC.t('T_dont_have_account')} `,
}),
renderLoginBox: () => {
const container = document.getElementById(LOGIN_BOX_CONTAINER);
if (!container){
return;
}
if (KCC.config.USER_SESSION_ID) {
container.innerHTML = '';
return;
}
const inRegister = KCC.config.LOGIN_SCREEN_REGISTER_MODE;
const context = {
...KCC.buildLoginTexts(inRegister),
suffix: '',
screen_number: KCC.config.LOGIN_SCREEN_NUMBER,
register_mode: KCC.config.LOGIN_SCREEN_REGISTER_MODE,
show_close: true,
form_id: DEFAULT_LOGIN_FORM_ID,
form_btn_id: 'login_form_btn',
};
container.innerHTML = KCC.renderTemplate(LOGIN_BOX, context);
},
renderEmbeddedLoginBox: () => {
if (!KCC.config.EMBEDDED_LOGIN_WIDGET) {
return;
}
const container = document.getElementById(KCC.config.EMBEDDED_LOGIN_WIDGET);
if (!container) {
return;
}
if (KCC.config.USER_SESSION_ID) {
container.innerHTML = '';
return;
}
const inRegister = KCC.config.EMBEDDED_LOGIN_SCREEN_REGISTER_MODE;
const context = {
...KCC.buildLoginTexts(inRegister, '_2'),
suffix: '_2',
screen_number: KCC.config.EMBEDDED_LOGIN_SCREEN_NUMBER,
register_mode: KCC.config.EMBEDDED_LOGIN_SCREEN_REGISTER_MODE,
show_close: false,
form_id: EMBEDDED_LOGIN_FORM_ID,
form_btn_id: 'embedded_login_form_btn',
};
container.innerHTML = KCC.renderTemplate(LOGIN_BOX, context);
},
renderProfileWidget: () => {
const container = document.getElementById(KCC.config.PROFILE_WIDGET);
if (!container) return;
if (!KCC.config.USER_SESSION_ID) {
container.innerHTML = '';
return;
}
container.innerHTML = KCC.renderTemplate(PROFILE_TEMPLATE);
},
renderTemplate: (templateName, context = {}) => {
const templates = {
[LOGIN_SYSTEM_TEMPLATE]: () => `
${KCC.config.USER_SESSION_ID ? KCC.renderTemplate(STATUS_LINE_TEMPLATE) : KCC.renderTemplate(LOGIN_BUTTON_TEMPLATE)}
`,
[STATUS_LINE_TEMPLATE]: () => `
${KCC.getUserData('name').charAt(0)}${KCC.getUserData('name')}
`,
[LOGIN_BUTTON_TEMPLATE]: () => `
`,
[LOGIN_BOX_WRAPPER]: () => `
`,
[LOGIN_BOX]: () => `
`,
[LOGIN_SCREEN_1]: () => `
${context.title}
`,
[LOGIN_SCREEN_2]: () => `
${context.title}
${KCC.renderTemplate(context.register_mode ? REGISTER_FIELDS : LOGIN_FIELDS, context)}
`,
[REGISTER_FIELDS]: () => `
`,
[LOGIN_FIELDS]: () => `
`,
[LOGIN_COMMON_FIELDS]: () => `
${context.promo_accept}
${context.privacy}
${context.changeMode}
`,
[RECOVER_PASSWORD_MODAL]: () => `
`,
[WELCOME_MODAL]: () => `
`,
[PROFILE_TEMPLATE]: () => `
`,
[PERSONAL_DATA_SCREEN]: () => `
${KCC.t('T_datos_personales')}
${KCC.t('T_fill_information_save_time')}
`,
[CLUB_LEVEL_SCREEN]: () => `
${KCC.getLevelData(0).description}
${KCC.renderFeatures(KCC.getLevelData(0).features)}
${KCC.getLevelData(1).description}
${KCC.renderFeatures(KCC.getLevelData(1).features)}
${KCC.getLevelData(2).description}
${KCC.renderFeatures(KCC.getLevelData(2).features)}
1/3
`,
[MY_RESERVATIONS_SCREEN]: () => `
${KCC.filterReservations().length && !KCC.foundReservations.length ? KCC.renderTemplate(ACTIVE_RESERVATIONS_LIST, context) : ''}
${KCC.filterReservations(true).length && !KCC.foundReservations.length ? KCC.renderTemplate(PAST_RESERVATIONS_LIST, context) : ''}
${KCC.foundReservations.length? KCC.renderTemplate(FOUND_RESERVATIONS_LIST, context) : ''}
`,
[ACTIVE_RESERVATIONS_LIST]: () => `
${KCC.t('T_next_reservations')}
${KCC.filterReservations().map(item => KCC.renderTemplate(RESERVATION_TEMPLATE, item)).join('')}
`,
[PAST_RESERVATIONS_LIST]: () => `
${KCC.t('T_past_reservations')}
${KCC.filterReservations(true).map(item => KCC.renderTemplate(RESERVATION_TEMPLATE, item)).join('')}
`,
[FOUND_RESERVATIONS_LIST]: () => `
${KCC.t('T_found_reservations')}
${KCC.foundReservations.map(item => KCC.renderTemplate(RESERVATION_TEMPLATE, item)).join('')}
`,
[RESERVATION_TEMPLATE]: () => `
${KCC.escapeHtml(context.room_name ?? '---')}
${KCC.t('T_entrada')}${KCC.escapeHtml(KCC.formatDate(context.check_in)) ?? '---'}
${KCC.t('T_salida')}${KCC.escapeHtml(KCC.formatDate(context.check_out)) ?? '---'}
${KCC.t('T_ocupacion')}${KCC.escapeHtml(KCC.getOccupancy(context)) ?? '---'}
${KCC.t('T_regimen')}${KCC.escapeHtml(context.board_name ?? '---')}
`,
[MODIFY_RESERVATION_BTN]: () => ``,
[CANCEL_RESERVATION_BTN]: () => ``,
}
let rendered = templates[templateName]();
const subtemplateRegex = /\[\[subtemplate:([^\]]+)\]\]/g;
rendered = rendered.replace(subtemplateRegex, (match, subtemplateName) => {
return KCC.renderTemplate(subtemplateName, context);
});
return rendered;
},
profileRedirect: () => {
location.href = '/kora_profile.html';
},
renderCountryList: () => {
let tmpl = ``;
KCC.countriesList.forEach((country) => {
const [cod, country_name] = country;
const selected = (cod.toLowerCase() === KCC.getUserData('pais', true).toLowerCase()) ? 'selected="selected"' : '';
tmpl += ``;
});
return tmpl;
},
processEmailLogin: async (inRegister, formId = DEFAULT_LOGIN_FORM_ID) => {
const form = document.getElementById(formId);
const formData = new FormData(form);
const payload = Object.fromEntries(formData.entries());
payload.action = inRegister ? 'put' : 'user_login';
if (form.querySelector('input[type="checkbox"][name="promotions"]')) {
payload.promotions = form.querySelector('input[type="checkbox"][name="promotions"]').checked;
}
const endpoint = 'https://loyalty-seeker-381529296218.us-central1.run.app/users/';
const response = await KCC.doFetch(endpoint, payload);
if (!response) {
alert(KCC.t('T_user_not_registered'));
return;
}
// The account was registered through a social network, so a
// password login can't succeed: guide the user to Google/Facebook.
if (response.status === 'registered_with_social_network') {
KCC.alert_registered_with_social_network();
return;
}
if (response.results_data === 'repeated') {
KCC.alert_user_already_created_email();
return;
}
KCC.user_data = response;
KCC.saveUserSessionId(response.session_id);
KCC.renderLoginSystem();
KCC.renderLoginBox();
KCC.renderEmbeddedLoginBox();
KCC.renderProfileWidget();
KCC.showWelcomeModal(inRegister);
},
modifyUser: async () => {
const form = document.getElementById('modify_user');
const formData = new FormData(form);
const payload = Object.fromEntries(formData.entries());
payload.action = 'modify';
if (form.querySelector('input[type="checkbox"][name="promotions"]')) {
payload.promotions = form.querySelector('input[type="checkbox"][name="promotions"]').checked;
}
const endpoint = 'https://loyalty-seeker-381529296218.us-central1.run.app/users/';
const response = await KCC.doFetch(endpoint, payload);
if (response) {
KCC.user_data = response;
KCC.renderProfileWidget();
KCC.showNotice(KCC.t('T_user_data_saved'));
} else {
KCC.showNotice(KCC.t('T_user_data_not_saved'), true);
}
},
deleteAccount: async() => {
const form = document.getElementById('modify_user');
const formData = new FormData(form);
const payload = Object.fromEntries(formData.entries());
payload.action = 'remove_user';
payload.user_id = KCC.user_data.idmember;
const endpoint = 'https://loyalty-seeker-381529296218.us-central1.run.app/users/';
const response = await KCC.doFetch(endpoint, payload);
KCC.config.PROFILE_SCREEN = PERSONAL_DATA_SCREEN;
KCC.logout();
},
doFetch: async (endpoint, payload) => {
try {
const res = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
let data;
try{
data = await res.json();
} catch (err){
console.log('data error');
}
if (!res.ok) {
throw new Error(typeof data === 'string' ? data : JSON.stringify(data));
}
return data;
} catch (err) {
console.error('Fetch request failed:', err);
return null;
}
},
saveUserSessionId: (sessionId) => {
const domain = KCC.config.COOKIE_DOMAIN;
let cookieString = `logged_user=${sessionId}; path=/; max-age=31536000`;
if (domain){
cookieString += `; SameSite=None; Secure; domain=${domain}`;
}
document.cookie = cookieString;
KCC.config.USER_SESSION_ID = sessionId;
},
loadUserSessionId: () => {
try {
const sessionId = document.cookie.split('; ').find(row => row.startsWith('logged_user=')).split('=')[1];
if (sessionId) {
return sessionId;
}
} catch (error) {
return null;
}
},
logout: () => {
const domain = KCC.config.COOKIE_DOMAIN;
let cookieString = "logged_user=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/";
if(domain){
cookieString += `; SameSite=None; Secure; domain=${domain}`;
}
document.cookie = cookieString;
KCC.config.USER_SESSION_ID = null;
KCC.config.LOGIN_SCREEN_NUMBER = 1;
KCC.config.EMBEDDED_LOGIN_SCREEN_NUMBER = 1;
KCC.renderLoginSystem();
KCC.renderLoginBox();
KCC.renderEmbeddedLoginBox();
KCC.renderProfileWidget();
},
loadStyles: () =>{
[
`${KCC.config.SCRIPT_DOMAIN}/static_1/css/styles_club_widget.css?v=202606250822`,
'https://fonts.googleapis.com/css2?family=Roboto:wght@500&display=swap',
'https://www3.paratytech.com/wysiwyg/fonts/eloquia/stylesheet.css?v=1.01',
'https://www3.paratytech.com/wysiwyg/fonts/Moneta/stylesheet.css?v=1.01',
'https://webseeker.paraty.es/fontawesome-kit/css/fa-default-kit.css'
].forEach(href => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = href;
document.head.appendChild(link);
});
},
loadFB: () => {
window.fbAsyncInit = function() {
FB.init({
appId : '4230337130524460',
cookie : true,
xfbml : true,
version : 'v20.0'
});
FB.AppEvents.logPageView();
};
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = `https://connect.facebook.net/${KCC.get_locale()}/sdk.js`;
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
},
loadGoogle: () => {
const script = document.createElement('script');
script.src = 'https://accounts.google.com/gsi/client';
script.async = true;
script.onload = KCC.initGoogle;
document.head.appendChild(script);
},
initGoogle: () => {
if (typeof google === "undefined" || !google.accounts || !google.accounts.id) {
console.error("Google API not defined. Probably the script failed due to connection failures.");
return;
}
google.accounts.id.initialize({
client_id: '233230451238-lqirulcmq4dsh9v78c6ebqp2g4bb4i77.apps.googleusercontent.com',
callback: KCC.handleGoogleLoginResponse
});
KCC.hiddenButtonContainer = document.createElement('div');
KCC.hiddenButtonContainer.style.display = 'none';
KCC.hiddenButtonContainer.id = 'hidden-google-signin';
document.body.appendChild(KCC.hiddenButtonContainer);
google.accounts.id.renderButton(KCC.hiddenButtonContainer, {
theme: "outline",
size: "large",
text: "signin_with"
});
google.accounts.id.prompt();
},
handleGoogleLoginResponse: (response) => {
KCC.socialLoginSend('google', response.credential);
},
socialLoginSend: async (loginType, token, formId = null) => {
const formIdsToTry = [formId, DEFAULT_LOGIN_FORM_ID, EMBEDDED_LOGIN_FORM_ID].filter(Boolean);
let language = '';
for (const id of formIdsToTry) {
const form = document.getElementById(id);
const value = form?.querySelector('input[name="language"]')?.value;
if (value) {
language = value;
break;
}
}
if (!language) language = KCC.getLanguage();
const socialLoginData = {
type: loginType,
token: token,
namespace: 'kora-corpo',
language: language
}
const endpoint = 'https://loyalty-seeker-381529296218.us-central1.run.app/social_login/login';
let response;
try {
const res = await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(socialLoginData)
});
response = await res.json();
} catch (err) {
console.error('Error: No response from social login', err);
return;
}
if(response.status === 'success'){
await KCC.loadUserData(response.sid);
if (KCC.user_data.session_id) {
KCC.saveUserSessionId(KCC.user_data.session_id);
KCC.renderLoginBox();
KCC.renderEmbeddedLoginBox();
KCC.renderLoginSystem();
KCC.renderProfileWidget();
KCC.showWelcomeModal(false);
}
} else if(response.message === 'Login failed'){
KCC.alert_missing_user();
} else {
console.log(`Error: ${response.message}`);
}
},
loadUserData: async (userSessionId) => {
const endpoint = 'https://loyalty-seeker-381529296218.us-central1.run.app/users/get_user_info';
const payload = {
namespace: 'kora-corpo',
sid: userSessionId
}
const userData = await KCC.doFetch(endpoint, payload);
KCC.user_data = userData;
},
getUserData: (key, raw = false) => {
const val = KCC.user_data[key];
if (!val){
if(key == 'points') return 0;
return '';
}
return raw ? val : KCC.escapeHtml(val);
},
alert_missing_user: () => {
alert(KCC.t('T_user_not_found'));
},
alert_user_already_created_email: () => {
alert(KCC.t('T_user_already_created_with_email'));
},
alert_registered_with_social_network: () => {
alert(KCC.t('T_registered_with_social_network'));
},
getLevelPoints: () => {
const levelsPoints = [];
KCC.clubLevels.forEach(level => {
levelsPoints.push(parseInt(level.points.replace(/\./g, "")));
});
levelsPoints.sort((a, b) => a - b);
return levelsPoints;
},
calculatePointsToNextLevel: () => {
const levelPoints = KCC.getLevelPoints();
const currentPoints = parseInt(KCC.getUserData('points'));
const nextLevelPoints = levelPoints.find(x => x > currentPoints);
return nextLevelPoints - currentPoints;
},
calculateProgressBar: () => {
const maxGraphicValue = 600;
const levelPoints = KCC.getLevelPoints();
const maxPoints = Math.max(...levelPoints);
const currentPoints = parseInt(KCC.getUserData('points'));
return parseInt(maxGraphicValue * (currentPoints/maxPoints));
},
getLevelData: (level) => {
return KCC.clubLevels[level];
},
getUserLevelName: () => {
return KCC.getUserData('level') || KCC.getLevelData(0).name;
},
renderFeatures: (features) => {
let template = '';
features.forEach(feature => {
template += `
${KCC.escapeHtml(feature.description)} `;
});
return template;
},
_carouselIndex: 0,
carouselGoTo: (index) => {
const container = document.querySelector('.club_level_descriptions');
const items = container ? container.querySelectorAll('.club_level_description') : [];
if (!items.length) return;
const total = items.length;
KCC._carouselIndex = Math.max(0, Math.min(index, total - 1));
items[KCC._carouselIndex].scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
const counter = document.querySelector('.carousel_counter');
if (counter) counter.textContent = (KCC._carouselIndex + 1) + '/' + total;
},
carouselNext: () => {
KCC.carouselGoTo(KCC._carouselIndex + 1);
},
carouselPrev: () => {
KCC.carouselGoTo(KCC._carouselIndex - 1);
},
loadReservations: async () => {
try{
const userSessionId = KCC.user_data.session_id;
const endpoint = 'https://loyalty-seeker-381529296218.us-central1.run.app/transactions/';
const payload = {
action: 'retreive_user_transactions',
sid: userSessionId,
namespace: 'kora-corpo'
}
const reservations = await KCC.doFetch(endpoint, payload);
KCC.user_data.reservations = reservations;
}catch(error){
console.error("Error loading reservations:", error);
}
},
filterReservations: (past=false) => {
const reservations = [];
KCC.user_data.reservations.forEach(reservation => {
if (past && !KCC.reservationIsActive(reservation) || !past && KCC.reservationIsActive(reservation)){
reservations.push(reservation);
}
});
return reservations;
},
reservationIsActive: (reservation) => {
const currentDate = new Date().toISOString().slice(0, 10);
return reservation.check_in >= currentDate
},
t: (label) => {
if (KCC.translations && KCC.translations[label]) {
return KCC.translations[label];
}
return label;
},
searchReservations: (identifier) => {
const reservations = [];
KCC.user_data.reservations.forEach(reservation => {
if (reservation.identifier.toLowerCase().includes(identifier.toLowerCase())){
reservations.push(reservation);
}
});
return reservations;
},
get_locale: () => {
const locales = {
SPANISH: 'es_ES',
ENGLISH: 'en_US',
FRENCH: 'fr_FR',
ITALIANO: 'it_IT',
GERMAN: 'de_DE',
POLISH: 'pl_PL'
}
return locales[KCC.getLanguage()];
},
getLanguage: () => {
const shortLang = document.documentElement.lang.toLowerCase();
const languages = {
es: 'SPANISH',
en: 'ENGLISH',
fr: 'FRENCH',
it: 'ITALIANO',
de: 'GERMAN',
pl: 'POLISH'
}
return languages[shortLang] || 'SPANISH';
},
loadTranslations: async () => {
const language = KCC.getLanguage();
try{
const endpoint = `${KCC.config.SCRIPT_DOMAIN}/koraclub?action=get_translated_context&language=${language}`;
const translated_context = await KCC.doFetch(endpoint, {});
KCC.translations = translated_context.translations;
KCC.clubLevels = translated_context.club_levels;
KCC.countriesList = translated_context.countries_list;
}catch(error){
console.error("Error loading reservations:", error);
}
},
changePassword: async () => {
const pass1 = prompt(KCC.t('T_input_new_password'));
if (pass1 === null || pass1 === '') return;
const pass2 = prompt(KCC.t('T_repita_cotrasena'));
if (pass2 === null) return;
if (pass1 !== pass2){
alert(KCC.t('T_passwords_dont_match'));
} else {
const payload = {
action: 'change_password',
namespace: 'kora-corpo',
sid: KCC.user_data.session_id,
password: pass1
};
const endpoint = 'https://loyalty-seeker-381529296218.us-central1.run.app/users/';
const response = await KCC.doFetch(endpoint, payload);
if(response){
alert(KCC.t('T_password_saved'));
} else {
alert(KCC.t('T_password_not_saved'));
}
}
},
openRecoverPasswordModal: () => {
document.getElementById('kcc_recover_password_overlay')?.remove();
const wrapper = document.createElement('div');
wrapper.innerHTML = KCC.renderTemplate(RECOVER_PASSWORD_MODAL);
const overlay = wrapper.firstElementChild;
document.body.appendChild(overlay);
overlay.addEventListener('click', (e) => {
if (e.target === overlay || e.target.matches('.close_recover_btn') || e.target.closest('.close_recover_btn')) {
overlay.remove();
}
});
overlay.addEventListener('submit', (e) => {
e.preventDefault();
KCC.sendPasswordRecovery();
});
overlay.querySelector('#recover_email').focus();
},
sendPasswordRecovery: async () => {
const overlay = document.getElementById('kcc_recover_password_overlay');
if (!overlay) return;
const email = overlay.querySelector('#recover_email').value.trim();
const messageElm = overlay.querySelector('.recover_password_message');
const submitBtn = overlay.querySelector('.recover_password_send_btn');
messageElm.className = 'recover_password_message';
if (!email) {
messageElm.textContent = KCC.t('T_recover_password_email_required');
messageElm.classList.add('error');
return;
}
if (submitBtn.disabled) return;
submitBtn.disabled = true;
const endpoint = 'https://loyalty-seeker-381529296218.us-central1.run.app/recover-password/';
const payload = {
email: email,
namespace: 'kora-corpo'
};
try {
let res;
try {
res = await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
} catch (err) {
console.error('Recover password request failed:', err);
messageElm.textContent = KCC.t('T_recover_password_error');
messageElm.classList.add('error');
return;
}
let data;
try {
data = await res.json();
} catch (err) {
data = null;
}
const hasError = data && typeof data === 'object' && 'error' in data;
if (res.ok && !hasError) {
messageElm.textContent = KCC.t('T_recover_password_success');
messageElm.classList.add('success');
setTimeout(() => overlay.remove(), 2500);
} else {
messageElm.textContent = KCC.t('T_recover_password_error');
messageElm.classList.add('error');
}
} finally {
submitBtn.disabled = false;
}
},
showWelcomeModal: (inRegister) => {
const message = inRegister ? KCC.t('T_thanks_for_register') : KCC.t('T_thanks_for_login');
KCC.showNotice(message);
},
showNotice: (message, isError = false) => {
document.getElementById('kcc_welcome_overlay')?.remove();
const icon = isError ? 'fa-circle-xmark' : 'fa-circle-check';
const wrapper = document.createElement('div');
wrapper.innerHTML = KCC.renderTemplate(WELCOME_MODAL, { message: message, icon: icon });
const overlay = wrapper.firstElementChild;
document.body.appendChild(overlay);
setTimeout(() => overlay.remove(), 2500);
},
setScriptDomain(){
const scriptSrc = document.currentScript?.src;
if (scriptSrc) {
KCC.config.SCRIPT_DOMAIN = new URL(scriptSrc).origin;
}
},
createLoginElement(){
if (document.getElementById(KCC.config.LOGIN_WIDGET)) {
return;
}
const div = document.createElement('div');
div.id = KCC.config.LOGIN_WIDGET;
document.body.appendChild(div);
},
getOccupancy(context){
return `${context.adults}-${context.children}-${context.babies}`;
},
formatDate(dateString){
const [year, month, day] = dateString.split('-');
return `${day}/${month}/${year}`;
}
};
}();
KCC.init();