`;
const timeZoneElement = document.getElementById("time-zone-container");
if (timeZoneElement) {
timeZoneElement.innerHTML = innerHTMLString;
} else {
console.info("Element with ID time-zone-container not found.");
}
const timeZoneMobileElement = document.getElementById("time-zone-container-mobile");
if (timeZoneMobileElement) {
timeZoneMobileElement.innerHTML = innerHTMLString;
} else {
console.info("Element with ID time-zone-container-mobile not found.");
}
}
function renderFooter() {
const footer = document.getElementById('footer');
if (footer) {
footer.innerHTML = `
`;
} else {
console.error("Element with ID footer not found.");
}
}
function renderTopMenu() {
const topMenu = document.getElementById('menu');
if (topMenu) {
const dashboard = getMeta('dashboard');
const isProfessionalDashboard = dashboard === 'professional-dashboard.html';
const isSeekerDashboard = dashboard === 'seekers-dashboard.html';
let loginIsHidden = getMeta('login-is-hidden');
const logoutIsHidden = getMeta('logout-is-hidden');
let hideAvatarMenu = 'style="display:none"';
if (logoutIsHidden.trim().length === 0) hideAvatarMenu = '';
let settingsLink = '/settings.html';
if (isProfessionalDashboard) settingsLink = '/rate-settings.html';
const settingsHtml = `
Settings
`;
const connectForOpportunities = `
Connect for Opportunities
`;
const connectAndEarn = `
Connect & Earn
`;
let topLinks = '';
if (isProfessionalDashboard) {
topLinks = connectAndEarn;
} else if (isSeekerDashboard) {
topLinks = connectForOpportunities;
} else {
topLinks = connectAndEarn + connectForOpportunities;
}
let pictureHtml = '';
if (getMeta('picture') && getMeta('picture').trim().length > 0 && getMeta('picture') !== 'None') {
pictureHtml = ``;
}
if (loginIsHidden) loginIsHidden = 'style="display:none;"'
topMenu.innerHTML = `
`;
if (document.querySelector('.scroll-container')) {
topMenu.innerHTML += `
`;
safeInitSwipeSections();
}
}
}
function renderEarlyAccess() {
let loginIsHidden = getMeta('login-is-hidden');
if (loginIsHidden) {
return;
}
const earlyAccess = document.getElementById(earlyAccessElementId);
if (!earlyAccess) {
console.error('Element with ID early-access not found.');
return;
}
earlyAccess.innerHTML = `
Get Early Access
Try it first. Shape what’s next.
We’re launching soon-but a few curious minds get early access. Join our beta, test it out, and help us make Nectly even better.
Add it to LinkedIn. Share it in DMs. Link it on your site. Wherever you connect, your Nectly link helps you say yes - on your terms, with zero friction.
Post it on LinkedIn, add it to your email signature, and let others know you’re ready for serious conversations-paid or donated.
`;
} else {
console.error(`Element with ID ${proShareLinkElementId} not found.`);
}
}
function getVisibleProCalendar() {
if (window.innerWidth >= 1024) {
return proCalendar || mobileProCalendar;
} else {
return mobileProCalendar || proCalendar;
}
}
function proSlotsFailed() {
const errorMessage = `
Retrieving slots failed.
Try to refresh later.
`;
const postReserveSlotElement = document.getElementById(postReserveSlotProElementId);
if (postReserveSlotElement) {
postReserveSlotElement.innerHTML = errorMessage;
} else {
console.error(`Element with ID ${postReserveSlotProElementId} not found.`);
}
}
function getVisibleSeekerCalendar() {
if (window.innerWidth >= 1024) {
return seekerCalendar || mobileSeekerCalendar;
} else {
return mobileSeekerCalendar || seekerCalendar;
}
}
let resolveProDateElements = [];
let proLastDayInMonth = null;
let activeProDayElement = null;
function loopProDaysInMonth(self, dateEl) {
const leftArrow = self.context.mainElement.querySelectorAll('[data-vc-arrow="prev"]');
const rightArrow = self.context.mainElement.querySelectorAll('[data-vc-arrow="prev"]');
if (leftArrow.length > 0) leftArrow[0].disabled = true;
if (rightArrow.length > 0) rightArrow[0].disabled = true;
const btnFullDate = getDayMonthYearFromDateEl(dateEl);
const btnEl = dateEl.querySelector('[data-vc-date-btn]');
if (dateEl.getAttribute('data-vc-date-week-day') === '6' || dateEl.getAttribute('data-vc-date-week-day') === '0') {
btnEl.classList.add('disabled');
btnEl.disabled = true;
}
if (btnFullDate.btnMonth !== self.context.selectedMonth) {
return;
}
if (activeProDayElement === null) {
const today = new Date();
today.setHours(0, 0, 0, 0);
if (today.getDay() === 0) today.setDate(today.getDate() + 1);
if (today.getDay() === 6) today.setDate(today.getDate() + 2);
if (today.getDate() === btnFullDate.btnDay &&
today.getMonth() === btnFullDate.btnMonth &&
today.getFullYear() === btnFullDate.btnYear) {
activeProDayElement = btnEl;
self.context.selectedDates = [dateEl.getAttribute('data-vc-date')];
}
}
const day = btnEl.innerText;
if (parseInt(day) === 1) {
resolveProDateElements = [dateEl];
proLastDayInMonth = getLastDayOfMonth(btnFullDate.btnYear, convertIntMonthToString(btnFullDate.btnMonth));
} else {
resolveProDateElements.push(dateEl);
}
if (proLastDayInMonth.getDate() === btnFullDate.btnDay &&
proLastDayInMonth.getMonth() === btnFullDate.btnMonth &&
proLastDayInMonth.getFullYear() === btnFullDate.btnYear) {
const today = new Date();
today.setHours(0, 0, 0, 0);
getProCurrentDate(proLastDayInMonth.toISOString(), proLastDayInMonth.getTimezoneOffset(), function(currentDay) {
const busyDaysInMonth = mergeAppointmentsAndSlots(currentDay);
for (const dateEl of resolveProDateElements) {
const btnEl = dateEl.querySelector('[data-vc-date-btn]');
const btnDate = new Date(dateEl.getAttribute('data-vc-date'));
btnDate.setHours(0, 0, 0, 0);
const day = btnEl.innerText;
if (btnDate < today) continue;
if (busyDaysInMonth.indexOf(parseInt(day)) !== -1) {
setDayElementAsBusy(btnEl);
}
}
resolveProDateElements = [];
if (leftArrow.length > 0) leftArrow[0].disabled = false;
if (rightArrow.length > 0) rightArrow[0].disabled = false;
console.log("Days in month updated")
},
function(error) {
console.log("Error fetching slots:", error);
resolveProDateElements = [];
if (leftArrow.length > 0) leftArrow[0].disabled = false;
if (rightArrow.length > 0) rightArrow[0].disabled = false;
});
}
}
function renderProCalendar(onChange, elementId) {
const { Calendar } = window.VanillaCalendarPro;
const calendar = new Calendar(
`#${elementId}`, {
onCreateDateEls: loopProDaysInMonth,
disableAllDates: true,
enableDates:[getEnabledDates()],
firstWeekday: 0,
type: 'default',
onClickArrow(self) {
console.log("Month changed to", self.context.selectedYear, self.context.selectedMonth);
onChange('month', convertIntMonthToString(self.context.selectedMonth));
self.selectedMonth = self.context.selectedMonth;
self.selectedYear = self.context.selectedYear;
},
onClickDate(self, pointerEvent) {
const btnEl = pointerEvent.srcElement;
console.log("Day clicked", self.context.selectedDates, btnEl);
activeProDayElement = btnEl;
onChange('value', self.context.selectedDates[0]);
},
});
calendar.init();
setDayElementAsSelected(calendar, calendar.context.selectedDates[0]);
calendar.selectedDates = [];
return calendar;
}
function updateProCalendarDays(busyDaysInMonth) {
console.log("Updating calendar days with busy days:", busyDaysInMonth);
if (activeProDayElement) {
const day = activeProDayElement.innerText;
if (busyDaysInMonth.indexOf(parseInt(day)) !== -1) {
setDayElementAsBusy(activeProDayElement);
}
}
}
function onProCalendarChange(calendarParameter, value) {
if (calendarParameter === "value") {
console.log("Calendar value changed to", value);
resetProAppointmentListContainer();
proRefreshCalendarSlots();
}
if (calendarParameter === "month") {
console.log("Calendar month changed to", value);
}
}
function onMobileProCalendarChange(calendarParameter, value) {
if (calendarParameter === "value") {
console.log("Professional Mobile calendar value changed to", value);
resetProAppointmentListContainer();
proRefreshCalendarSlots();
}
if (calendarParameter === "month") {
console.log("Professional Mobile calendar month changed to", value);
}
}
function renderProSetupAvailabilityElement(index) {
const proAddYourAvailabilityElement = document.getElementById(proAddYourAvailabilityElementId);
if (proAddYourAvailabilityElement) {
proAddYourAvailabilityElement.className = "setup-account-container flex flex-column halfgap";
proAddYourAvailabilityElement.innerHTML = `
${index}
Add your availability
Pick the times you’re open for conversations-your link will handle the rest.
`;
} else {
console.error(`Element with ID ${proAddYourAvailabilityElementId} not found.`);
}
}
function renderProSetup(currentDay) {
resetProSetup();
let countOfSetupSteps = 0;
if (currentDay.allCreatedSlots.length === 0) countOfSetupSteps++;
if (!currentDay.paymentAccount && !currentDay.charityPayout) countOfSetupSteps++;
if (countOfSetupSteps === 0) {
renderProLinkElement(0, currentDay.paymentAccountEligibleForPayout);
} else {
const proSetupYourAccountMessageElement = document.getElementById(proSetupYourAccountMessageElementId);
if (proSetupYourAccountMessageElement) {
proSetupYourAccountMessageElement.innerHTML = `Setup your Account`;
} else {
console.error(`Element with ID ${proSetupYourAccountMessageElementId} not found.`);
}
let index = 1;
if (!currentDay.paymentAccount && !currentDay.charityPayout) renderProPaymentMethodElement(index++);
if (currentDay.allCreatedSlots.length === 0) renderProSetupAvailabilityElement(index++);
renderProLinkElement(index++, currentDay.paymentAccountEligibleForPayout);
renderProShareLinkElement(index++);
}
}
function proRenderEarnings(payouts) {
const proEarningsElement = document.getElementById(proEarningsElementId);
if (proEarningsElement) {
const inReview = `
In Review
$${payouts.inReview}
This session wrapped recently-your payout will be released within the hour.
`;
const availableToWithdraw = `
Available to Withdraw
$${payouts.availableToWithdraw}
These funds are all yours.
`;
let charityPayout = ``;
if (payouts.donated && payouts.donated > 0) {
charityPayout = `
';
});
slotsListElement.innerHTML = innerHTML;
}
function onProTimeSlotSelectChange() {
const selectedDate = getCalendarSelectedDate(getVisibleProCalendar());
const selectedDates = getVisibleProCalendar().context.selectedDates;
getVisibleProCalendar().selectedDates = selectedDates;
getVisibleProCalendar().selectedMonth = getVisibleProCalendar().context.selectedMonth;
getVisibleProCalendar().selectedYear = getVisibleProCalendar().context.selectedYear;
proPostCreateSlot();
getProCurrentDate(selectedDate.toISOString(), selectedDate.getTimezoneOffset(), function(currentDay) {
console.log("Current day status:", currentDay);
renderProSetup(currentDay);
updateProCalendarDays(mergeAppointmentsAndSlots(currentDay));
getVisibleProCalendar().update();
getVisibleProCalendar().context.selectedDates = selectedDates;
renderProAppointmentList(currentDay);
renderProSlotsList(currentDay);
if (currentDay.freeSlots.length > 0) {
const timeSelect = document.getElementById(proNewSessionTimeSelect);
if (!timeSelect) {
console.error(`Element with ID ${proNewSessionTimeSelect} not found.`);
return;
}
if (currentDay.freeSlots.length !== timeSelect.options.length) {
renderProAvailableSessions(currentDay);
}
toggleSeekerConfirmationButton(false);
} else {
renderProAvailableSessions(currentDay);
toggleSeekerConfirmationButton(true);
}
}, function(error) {
console.error("Error fetching professional's slots:", error);
if (error.status === 400 && error.error === "400: Selected date cannot be in the past") {
proSelectedDateIsinThePast();
} else {
proSlotsFailed();
}
toggleSeekerConfirmationButton(true);
});
}
function proSaveSession(slotType, repeatCount) {
const subject = '';
const timeSelect = document.getElementById(proNewSessionTimeSelect);
if (!timeSelect) {
console.error(`Element with ID ${proNewSessionTimeSelect} not found.`);
return;
}
const selectedOption = timeSelect.options[timeSelect.selectedIndex];
if (!selectedOption) {
console.error("No slot selected.");
return;
}
toggleSeekerConfirmationButton(true);
const startTime = selectedOption.value.split('@')[0];
const endTime = selectedOption.value.split('@')[1];
console.log("Reserving session from", startTime, "to", endTime);
const reserveSlotBody = {
"startTime": startTime,
"endTime": endTime,
"title": subject,
"slotType": slotType || getProRecurringValue(),
"repeatCount": repeatCount || -1, // TODO: implement in UI
}
const pro_id = getLinkPageUserId();
console.log("Professional ID:", pro_id);
console.log("Reserve slot body:", reserveSlotBody);
callApi(`/api/professional-slots`, 'POST', reserveSlotBody)
.then(data => {
console.log('Current month:', data)
onProTimeSlotSelectChange();
onReserveProSlotSuccess();
})
.catch((error) => {
console.error('Error saving professional session:', error);
if (error.status === 400) {
if (error.error === "400: Selected date cannot be in the past") { proSelectedDateIsinThePast(); }
else if (error.error === "400: Slot is too soon for booking") { proSlotIsTooSoon(); }
else { onReserveProSlotError(); }
} else {
onReserveProSlotError();
}
});
}
function getProRecurringValue() {
const recurringSelect = document.getElementById(proRecurringSelectionElementId);
if (!recurringSelect) {
console.error(`Element with ID ${proRecurringSelectionElementId} not found.`);
return null;
}
let selectedOption = null;
for (const child of recurringSelect.children) {
if (child.children[0].checked === true) {
selectedOption = child.children[0];
break;
}
}
if (!selectedOption) {
console.error("No recurring selected.");
return null;
}
return selectedOption.value;
}
function renderProAppointmentList(currentDay) {
const appointmentListElement = document.getElementById(proAppointmentListElementId);
if (!appointmentListElement) {
console.error(`Element with ID ${proAppointmentListElementId} not found.`);
}
const appointmentsTabElement = getActiveProAppointmentList();
if (!appointmentsTabElement) {
console.error('No active appointments tab found.');
return;
}
let workWithAppointments;
let todayTab = false
if (appointmentsTabElement.id === proAppointmentsDateTabElementId) {
workWithAppointments = currentDay.appointmentsToDate;
} else if (appointmentsTabElement.id === proAppointmentsAllTabElementId) {
workWithAppointments = currentDay.appointments;
} else {
workWithAppointments = currentDay.appointmentsToday;
todayTab = true;
}
if (workWithAppointments.length === 0) {
appointmentListElement.innerHTML = "";
proRenderNoAppointments(todayTab);
return;
}
let innerHTML = "";
workWithAppointments.forEach(
(appointment) => {
let subject = "";
if (appointment.title) {
subject = `
${appointment.title}
`;
}
innerHTML += `
Conversation with ${appointment.attendees.join(', ')}
${subject}
${renderAppointmentDate(appointment)}
`;
});
appointmentListElement.innerHTML = innerHTML;
setupCancelMeetingToggle();
}
function resetProAppointmentListContainer() {
const appointmentListElement = document.getElementById(proAppointmentListElementId);
if (appointmentListElement) {
appointmentListElement.innerHTML = "";
} else {
console.error(`Element with ID ${proAppointmentListElementId} not found.`);
}
}
function getActiveProSlotList() {
const slotsElement = document.getElementById(proSlotsElementId);
if (slotsElement) {
for (const child of slotsElement.children) {
if (child.classList.contains('active')) {
return child;
}
}
} else {
console.error(`Element with ID ${proSlotsElementId} not found.`);
return null;
}
}
function getActiveProAppointmentList() {
const appointmentsElement = document.getElementById(proAppointmentsElementId);
if (appointmentsElement) {
for (const child of appointmentsElement.children) {
if (child.classList.contains('active')) {
return child;
}
}
} else {
console.error(`Element with ID ${proAppointmentsElementId} not found.`);
return null;
}
}
function proPostCreateSlot() {
const postReserveSlotElement = document.getElementById(postReserveSlotProElementId);
if (postReserveSlotElement) {
postReserveSlotElement.innerHTML = "";
} else {
console.error(`Element with ID ${postReserveSlotProElementId} not found.`);
}
const noAppointmentsElement = document.getElementById(proNoAppointmentsElementId);
if (noAppointmentsElement) {
noAppointmentsElement.innerHTML = "";
} else {
console.error(`Element with ID ${proNoAppointmentsElementId} not found.`)
}
const noSlotsElement = document.getElementById(proNoSlotsElementId);
if (noSlotsElement) {
noSlotsElement.innerHTML = "";
} else {
console.error(`Element with ID ${proNoSlotsElementId} not found.`)
}
}
function proRenderNoSlots(todayTab) {
const noSlotsElement = document.getElementById(proNoSlotsElementId);
if (noSlotsElement) {
let h4Text = `
No time slots added yet
`;
if (todayTab) h4Text = `
No time slots added for today. Add a few, and they’ll show up here-ready to book.
Choose the times you’re available, and they’ll appear here ready to book.
`;
} else {
console.error(`Element with ID ${proNoSlotsElementId} not found.`);
}
}
function proRenderNoAppointments(todayTab) {
const noAppointmentsElement = document.getElementById(proNoAppointmentsElementId);
if (noAppointmentsElement) {
let h4Text = `
No conversations booked yet
`;
if (todayTab) h4Text = `
No conversations booked for today. Keep sharing your link to start the right ones.
Once an expert books time with you, it’ll appear here. Until then, keep sharing your link to start the right conversations.
`;
} else {
console.error(`Element with ID ${seekerNoAppointmentsElementId} not found.`);
}
}
function renderSeekerAppointmentList(currentDay) {
const appointmentListElement = document.getElementById(seekerAppointmentListElementId);
if (!appointmentListElement) {
console.error(`Element with ID ${seekerAppointmentListElementId} not found.`);
}
const appointmentsTabElement = getActiveSeekerAppointmentList();
if (!appointmentsTabElement) {
console.error('No active appointments tab found.');
return;
}
let workWithAppointments;
let todayTab = false;
if (appointmentsTabElement.id === seekerAppointmentsDateTabElementId) {
workWithAppointments = currentDay.appointmentsToDate;
} else if (appointmentsTabElement.id === seekerAppointmentsAllTabElementId) {
workWithAppointments = currentDay.appointments;
} else {
workWithAppointments = currentDay.appointmentsToday;
todayTab = true;
}
if (workWithAppointments.length === 0) {
appointmentListElement.innerHTML = "";
renderNoAppointments(todayTab);
return;
}
let innerHTML = "";
workWithAppointments.forEach(
(appointment) => {
let subject = "";
if (appointment.title) {
subject = `
${appointment.title}
`;
}
innerHTML += `
Conversation with ${appointment.attendees.join(', ')}
${subject}
${renderAppointmentDate(appointment)}
`;
});
appointmentListElement.innerHTML = innerHTML;
setupCancelMeetingToggle();
}
function renderNoSlots(todayTab) {
const noSlotsElement = document.getElementById(seekerNoSlotsElementId);
if (noSlotsElement) {
let textBelow = 'Choose the times you’re available, and they’ll appear here-ready for experts to book.';
if (window.location.href.indexOf("professional-dashboard.html") !== -1) {
textBelow = 'Choose the times you’re available, and they’ll appear here-ready to book.';
}
let h4Text = `
No time slots added yet
`;
if (todayTab) h4Text = `
No time slots added for today. Add a few, and they’ll show up here-ready to book.