I am developing an app to see the events on my Outlook calendar using fullcalendar, the problem is that I log in to Microsoft, in principle everything is correct, I return to my website but the calendar is not generated, what’s more, if I click again The login button displays a message that the login is still in progress (errorCode === "interaction_in_progress")
and i get the console log ‘im inside the else (accounts.length)’ .
This is my code, I can’t find what is causing the calendar and events to not load.
<div id="calendar"></div>
<script>
let msalInstance;
const loginRequest = {
scopes: ["User.Read", "Calendars.Read", "Calendars.Read.Shared"]
};
async function mostrarCalendarioOutlook() {
console.log('I am inside of mostrarCalendarioOutlook');
try {
if (!msalInstance) {
throw new Error("msalInstance is not initialized");
}
const accounts = msalInstance.getAllAccounts();
if (accounts.length > 0) {
console.log('Dentro del if (accounts.length > 0)');
const account = accounts[0];
const tokenResponse = await msalInstance.acquireTokenSilent({
account,
scopes: ["User.Read", "Calendars.Read", "Calendars.Read.Shared"]
});
const events = await getCalendarEvents(tokenResponse.accessToken);
console.log('initializing render calendar function');
renderCalendar(events);
} else {
console.log('im inside the else (accounts.length)');
await msalInstance.loginRedirect(loginRequest);
}
} catch (error) {
handleError(error);
}
}
function handleError(error) {
if (error.errorCode === "interaction_in_progress") {
Swal.fire({
icon: 'info',
title: 'Proceso en progreso',
text: 'El proceso de inicio de sesión está en progreso. Por favor, espera a que se complete.',
allowOutsideClick: false
});
} else if (error.errorCode === "consent_required" || error.errorCode === "interaction_required") {
Swal.fire({
icon: 'error',
title: 'Permisos necesarios',
text: 'El administrador debe conceder los permisos necesarios para acceder a los calendarios. Contacta al administrador.'
});
} else if (error.errorCode === "access_denied") {
Swal.fire({
icon: 'error',
title: 'Acceso denegado',
text: 'No se pudo acceder a los datos solicitados. Verifica los permisos y vuelve a intentarlo.'
});
} else {
Swal.fire({
icon: 'error',
title: 'Error de autenticación',
text: 'Ocurrió un error durante la autenticación. Por favor, inténtalo de nuevo más tarde.'
});
console.error('Error durante la autenticación:', error);
}
}
document.addEventListener('DOMContentLoaded', async function() {
try {
const msalConfig = {
auth: {
clientId: "Client_ID",
authority: "https://login.microsoftonline.com/common",
redirectUri: "redirect_Uri",
popUp: true
}
};
msalInstance = new msal.PublicClientApplication(msalConfig);
//EVENTS
async function getCalendarEvents(accessToken) {
console.log('im inside of getCalendarEvents');
const response = await fetch('https://graph.microsoft.com/v1.0/me/events', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
if (!response.ok) {
const errorText = await response.text();
console.error(`Error al obtener los eventos del calendario: ${response.status} ${response.statusText}`, errorText);
throw new Error('Error al obtener los eventos del calendario');
}
const data = await response.json();
const events = [];
function adjustToLocalTime(dateTime) {
const date = new Date(dateTime);
const timeZoneOffset = date.getTimezoneOffset() * 60000;
return new Date(date.getTime() - timeZoneOffset).toISOString();
}
const recurringEvents = data.value.filter(event => event.recurrence && event.recurrence.pattern);
recurringEvents.forEach(event => {
const startDate = new Date(event.start.dateTime);
const endDate = new Date(event.end.dateTime);
const recurrence = event.recurrence.pattern;
let instanceDate = new Date(startDate);
const endRecurrenceDate = event.recurrence.range && event.recurrence.range.endDate ? new Date(event.recurrence.range.endDate) : null;
while (!endRecurrenceDate || instanceDate <= endRecurrenceDate) {
if (!recurrence.daysOfWeek) {
const adjustedStartDate = new Date(instanceDate);
adjustedStartDate.setHours(startDate.getHours());
adjustedStartDate.setMinutes(startDate.getMinutes());
const adjustedEndDate = new Date(instanceDate);
adjustedEndDate.setHours(endDate.getHours());
adjustedEndDate.setMinutes(endDate.getMinutes());
if (adjustedStartDate <= endRecurrenceDate || !endRecurrenceDate) {
events.push({
title: event.subject,
start: adjustToLocalTime(adjustedStartDate.toISOString()),
end: adjustToLocalTime(adjustedEndDate.toISOString()),
allDay: event.isAllDay
});
}
switch (recurrence.type) {
case "daily":
instanceDate.setDate(instanceDate.getDate() + recurrence.interval);
break;
case "absoluteMonthly":
instanceDate.setMonth(instanceDate.getMonth() + recurrence.interval);
break;
case "absoluteYearly":
instanceDate.setFullYear(instanceDate.getFullYear() + recurrence.interval);
break;
}
continue;
}
const daysOfWeekIndices = (recurrence.daysOfWeek || []).map(day => {
switch (day.toLowerCase()) {
case "monday":
return 1;
case "tuesday":
return 2;
case "wednesday":
return 3;
case "thursday":
return 4;
case "friday":
return 5;
case "saturday":
return 6;
case "sunday":
return 0;
}
});
daysOfWeekIndices.forEach(dayIndex => {
let tempDate = new Date(instanceDate);
while (tempDate.getDay() !== dayIndex) {
tempDate.setDate(tempDate.getDate() + 1);
}
if (tempDate >= startDate && (!endRecurrenceDate || tempDate <= endRecurrenceDate)) {
const adjustedStartDate = new Date(tempDate);
adjustedStartDate.setHours(startDate.getHours());
adjustedStartDate.setMinutes(startDate.getMinutes());
const adjustedEndDate = new Date(tempDate);
adjustedEndDate.setHours(endDate.getHours());
adjustedEndDate.setMinutes(endDate.getMinutes());
if (adjustedStartDate <= endRecurrenceDate || !endRecurrenceDate) {
events.push({
title: event.subject,
start: adjustToLocalTime(adjustedStartDate.toISOString()),
end: adjustToLocalTime(adjustedEndDate.toISOString()),
allDay: event.isAllDay
});
}
}
});
instanceDate.setDate(instanceDate.getDate() + 7 * recurrence.interval);
}
if (endRecurrenceDate && recurrence.daysOfWeek) {
const tempDate = new Date(endRecurrenceDate);
const endRecurrenceDay = tempDate.toLocaleString('en-US', {
weekday: 'long'
}).toLowerCase();
if (recurrence.daysOfWeek.includes(endRecurrenceDay)) {
const adjustedStartDate = new Date(tempDate);
adjustedStartDate.setHours(startDate.getHours());
adjustedStartDate.setMinutes(startDate.getMinutes());
const adjustedEndDate = new Date(tempDate);
adjustedEndDate.setHours(endDate.getHours());
adjustedEndDate.setMinutes(endDate.getMinutes());
if (adjustedStartDate.getTime() !== endRecurrenceDate.getTime()) {
events.push({
title: event.subject,
start: adjustToLocalTime(adjustedStartDate.toISOString()),
end: adjustToLocalTime(adjustedEndDate.toISOString()),
allDay: event.isAllDay
});
}
}
}
});
const singleEvents = data.value.filter(event => !event.recurrence);
singleEvents.forEach(event => {
events.push({
title: event.subject,
start: adjustToLocalTime(event.start.dateTime),
end: adjustToLocalTime(event.end.dateTime),
allDay: event.isAllDay
});
});
//console.log("Eventos procesados FINAL:", events);
return events;
}
async function handleLogoutClick() {
console.log('Cerrando sesion Microsoft...');
Swal.fire({
title: 'Cerrando sesión...',
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();
},
});
try {
const accounts = await msalInstance.getAllAccounts();
if (accounts.length === 0) {
Swal.close();
return;
}
const respuesta = await msalInstance.logoutPopup({
account: accounts[0],
});
if (respuesta) {
localStorage.setItem('logoutCompleted', 'true');
console.log('Sesión Microsoft cerrada correctamente');
} else {
localStorage.setItem('logoutCompleted', 'false');
console.log('No se cerró sesión');
}
Swal.close();
} catch (error) {
Swal.fire({
icon: 'error',
title: 'Error al cerrar sesión',
text: 'Ocurrió un error al intentar cerrar sesión. Por favor, inténtalo de nuevo más tarde.'
});
console.log('Error al cerrar sesión Microsoft', error);
} finally {
Swal.close();
}
}
function renderCalendar(events) {
console.log('Entro a la funcion renderCalendar');
const calendarEl = document.getElementById('calendar');
const calendar = new FullCalendar.Calendar(calendarEl, {
customButtons: {
myCustomButton: {
text: 'Refrescar',
click: async function() {
console.log('Refrescando calendario...');
Swal.fire({
title: 'Actualizando Calendario...',
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();
}
});
try {
const accounts = await msalInstance.getAllAccounts();
if (accounts.length === 0) {
await msalInstance.loginRedirect(loginRequest);
return;
}
const response = await msalInstance.acquireTokenSilent({
account: accounts[0],
scopes: ["User.Read", "Calendars.Read", "Calendars.Read.Shared"]
});
if (response !== null) {
const accessToken = response.accessToken;
const events = await getCalendarEvents(accessToken);
renderCalendar(events);
Swal.close();
console.log('Calendario refrescado correctamente');
} else {
console.error('No se pudo obtener el token de acceso.');
console.log('Calendario NO refrescado');
Swal.close();
}
} catch (error) {
console.log('Error al refrescar calendario');
handleError(error);
}
}
},
logout: {
text: 'Cerrar Sesión',
click: function() {
handleLogoutClick();
}
}
},
headerToolbar: {
left: 'prev,next today myCustomButton logout',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
buttonText: {
today: 'Hoy',
month: 'Mes',
week: 'Semana',
day: 'Día',
list: 'Lista'
},
initialView: 'dayGridMonth',
locale: <?php echo '"' . session('language') . '"'; ?>, //He añadido la seleccion automatica de locale
firstDay: 1,
events: events,
dayMaxEvents: true,
eventClick: function(info) {
const event = info.event;
const title = event.title;
const start = event.start;
const end = event.end;
const allDay = event.allDay;
const location = event.extendedProps.location;
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const startFormatted = start.toLocaleString('es-ES', {
timeZone: timeZone
});
const endFormatted = end ? end.toLocaleString('es-ES', {
timeZone: timeZone
}) : 'Evento de todo el día';
let content = `<h2>${title}</h2>`;
if (!allDay) {
content += `<p><strong>Inicio:</strong> ${startFormatted}</p>`;
content += `<p><strong>Fin:</strong> ${endFormatted}</p>`;
} else {
content += `<p><strong>Evento de todo el día</strong></p>`;
}
if (location) {
content += `<p><strong>Ubicación:</strong> ${location}</p>`;
}
Swal.fire({
title: 'Información del Evento',
html: content,
icon: 'info'
});
console.log('Infomacion:' + info.event);
console.log('Titulo:' + title);
console.log('Inicio:' + start);
console.log('Fin:' + end);
console.log('Evento Diario:' + allDay);
}
});
calendar.render();
}
} catch (error) {
console.error('Error during initialization:', error);
Swal.fire({
icon: 'error',
title: 'Error de inicialización',
text: 'Ocurrió un error al cargar la configuración. Por favor, inténtalo de nuevo más tarde.'
});
}
});
</script>
Thank you very much!!