Skip to content

Instantly share code, notes, and snippets.

@prince-neres
Last active December 8, 2023 13:29
Show Gist options
  • Save prince-neres/5311cd5da03a65832f7b3ce02359d414 to your computer and use it in GitHub Desktop.
Save prince-neres/5311cd5da03a65832f7b3ce02359d414 to your computer and use it in GitHub Desktop.
Calendar form filled as events
<!--
Importar CDN no Header
<script src='https://cdn.jsdelivr.net/npm/[email protected]/index.global.min.js'></script>
-->
[#assign currentYear = .now?string('yyyy')?number]
[#assign currentMonth = .now?string('MM')?number]
<div>
<div class="d-flex gap-3">
<div class="mr-3">
<label for="calendarMonthFilter" class="form-label">
[@liferay.language key="month" /]
</label>
<select name="calendarMonthFilter" class="form-control" id="calendarMonthFilter">
[#list 1..12 as month]
<option value="${month}" [#if month == currentMonth] selected [/#if]>
[@liferay.language key="${['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'][month - 1]}" /]
</option>
[/#list]
</select>
</div>
<div class="mr-3">
<label for="calendarYearFilter" class="form-label">
[@liferay.language key="year" /]
</label>
<select
name="calendarYearFilter"
class="form-control"
id="calendarYearFilter"
>
[#list (currentYear - 4)..(currentYear + 1) as year]
<option
[#if year == currentYear]selected[/#if]
value="${year}"
>
${year}
</option>
[/#list]
</select>
</div>
<div>
<label for="calendarCategoryFilter" class="form-label">
[@liferay.language key="category" /]
</label>
<select
name="calendarCategoryFilter"
class="form-control"
id="calendarCategoryFilter"
>
</select>
</div>
</div>
<div id="calendar" class="mt-3 d-none d-xl-block"></div>
<div id="list-events" class="mt-3 d-flex flex-column d-xl-none"></div>
</div>
const EVENT_STRUCTURE_ID = 155528;
const API_URL_EVENTS = `/o/headless-delivery/v1.0/content-structures/${EVENT_STRUCTURE_ID}/structured-contents`;
const calendarWrapper = document.getElementById("calendar");
const listEventsWrapper = document.getElementById("list-events");
const monthFilterSelect = document.getElementById("calendarMonthFilter");
const yearFilterSelect = document.getElementById("calendarYearFilter");
const locale = themeDisplay.getBCP47LanguageId();
const now = new Date();
let events = [];
(function renderCalendar() {
const calendar = new FullCalendar.Calendar(calendarWrapper, {
headerToolbar: { left: "title", center: "", right: "" },
dayHeaderFormat: { weekday: "long" },
locale: locale,
});
calendarWrapper.calendar = calendar;
calendar.render();
})();
const fetchEvents = (startDate, endDate) => {
const filterQuery = `filter=contentFields%2FstartDateTime%20ge%20${startDate}%20and%20contentFields%2FstartDateTime%20le%20${endDate}`;
const sortParam = "sort=contentFields%2FstartDateTime%3Aasc";
const url = `${API_URL_EVENTS}?${filterQuery}&${sortParam}`;
return Liferay.Util.fetch(url, {
headers: { "Accept-Language": locale },
})
.then((response) => response.json())
.then(({ items }) =>
items.map((event) => ({
title: event.title,
url: "w/" + event.friendlyUrlPath,
start: event.contentFields
.find((content) => content.name === "startDateTime")
.contentFieldValue.data.slice(0, 10),
}))
)
.catch((error) => []);
};
const groupEventsByDay = () => {
if (events.length === 0) return {};
const firstEventDate = calendarWrapper.calendar.getDate();
const year = firstEventDate.getFullYear();
const month = firstEventDate.getMonth();
const time = "T12:00:00";
const filteredEvents = events.filter((event) => {
const eventDate = new Date(event.start + time);
return eventDate.getFullYear() === year && eventDate.getMonth() === month;
});
const groupedEvents = {};
filteredEvents.forEach((event) => {
const date = event.start.split("-").pop();
if (!groupedEvents[date]) groupedEvents[date] = [];
groupedEvents[date].push(event);
});
return groupedEvents;
};
const populateCalendarEvents = () => {
calendarWrapper.calendar.removeAllEvents();
events.forEach((event) => calendarWrapper.calendar.addEvent(event));
};
const populateListEvents = () => {
listEventsWrapper.innerHTML = "";
const groupedEvents = groupEventsByDay(events);
const viewTitle = document.createElement("h2");
viewTitle.innerText = calendarWrapper.calendar.view.title;
listEventsWrapper.appendChild(viewTitle);
for (const day in groupedEvents) {
const eventsForDay = groupedEvents[day];
const dayDiv = document.createElement("div");
dayDiv.innerHTML = `<span>${day}</span>`;
const dayUl = document.createElement("ul");
dayDiv.appendChild(dayUl);
eventsForDay.forEach((event) => {
const eventLi = document.createElement("li");
const eventLink = document.createElement("a");
eventLink.innerText = event.title;
eventLink.setAttribute("href", event.url);
eventLi.appendChild(eventLink);
dayUl.appendChild(eventLi);
});
listEventsWrapper.appendChild(dayDiv);
}
};
const getFirstDayOfMonth = (passedDate = now) => {
const year = passedDate.getFullYear();
const month = String(passedDate.getMonth() + 1).padStart(2, "0");
return `${year}-${month}-01`;
};
const getLastDayOfMonth = (passedDate = now) => {
const year = passedDate.getFullYear();
const month = passedDate.getMonth() + 1;
const nextMonth = month === 12 ? 1 : month + 1;
const nextYear = nextMonth === 1 ? year + 1 : year;
const lastDay = new Date(nextYear, nextMonth - 1, 0).getDate();
return `${nextYear}-${String(nextMonth).padStart(2, "0")}-${String(
lastDay
).padStart(2, "0")}`;
};
const handleChangeDate = () => {
const selectedMonth = monthFilterSelect.value;
const selectedYear = yearFilterSelect.value;
const selectedDate = new Date(selectedYear, selectedMonth - 1, 1);
const startDate = getFirstDayOfMonth(selectedDate);
const endDate = getLastDayOfMonth(selectedDate);
fetchEvents(startDate, endDate)
.then((data) => {
events = data;
populateCalendarEvents();
populateListEvents();
})
.catch((error) => console.error(error));
calendarWrapper.calendar.gotoDate(selectedDate);
};
monthFilterSelect.addEventListener("change", handleChangeDate);
yearFilterSelect.addEventListener("change", handleChangeDate);
fetchEvents(getFirstDayOfMonth(now), getLastDayOfMonth(now))
.then((data) => {
events = data;
populateCalendarEvents();
populateListEvents();
})
.catch((error) => console.error(error));
/* Geral */
#calendar {
user-select: none;
}
/* Títulos e Texto */
.fc .fc-toolbar-title:first-letter {
text-transform: capitalize;
}
.fc .fc-col-header-cell-cushion {
text-transform: uppercase;
}
.fc .fc-daygrid-day-top a,
.fc-scrollgrid-sync-inner a {
color: #666e7a;
}
/* Botões */
.fc .fc-button-primary {
background: none !important;
border: none !important;
color: #0063de;
height: 42px;
width: 42px;
border-radius: 100%;
box-shadow: none !important;
}
.fc .fc-button-primary:hover {
background: #c3edfa !important;
color: #0063de !important;
transition: background 0.3s, color 0.3s;
}
/* Bordas e Fundos */
.fc .fc-col-header-cell,
.fc .fc-scrollgrid-sync-table,
.fc .fc-col-header-cell .fc-scrollgrid-sync-inner,
.fc .fc-scrollgrid,
.fc .fc-scrollgrid-section > * {
border: none !important;
}
.fc .fc-day-other,
.fc .fc-day-other:hover {
border-color: #e3e5e7 !important;
background-color: #f6f6f7 !important;
border-right: none;
border-bottom: none;
}
/* Destaque de Dias */
.fc .fc-daygrid-day.fc-day-today,
.fc .fc-daygrid-day:hover {
background-color: rgba(0, 176, 230, 0.08);
}
.fc .fc-daygrid-day {
border-color: #0063de;
padding-right: 0.5rem;
}
/* Eventos */
.fc .fc-event {
background-color: transparent;
border-color: transparent;
padding: 0.3rem;
margin-bottom: 0.5rem;
transition: background-color 0.2s;
}
.fc .fc-event:hover {
background-color: #bceaf7;
}
.fc .fc-event-title {
white-space: normal;
color: #0063de;
}
.fc .fc-day-other .fc-event,
.fc .fc-day-other .fc-event:hover {
background-color: transparent;
}
.fc .fc-day-other .fc-event-title {
color: #666e7a;
}
.fc .fc-daygrid-day-events {
height: 120px !important;
overflow-y: auto;
}
/* Scrollbar */
.fc .fc-daygrid-day-events::-webkit-scrollbar {
width: 6px;
background: url('https://clientes.b3.com.br/lumis-theme/br/com/b3/portalb2b/theme/b3-portal-b2b/img/scroll-bar.svg') no-repeat center;
}
.fc .fc-daygrid-day-events::-webkit-scrollbar-thumb {
background: #0063de;
border-radius: 6px;
}
.fc .fc-daygrid-day-events::-webkit-scrollbar-track {
border-radius: 6px;
}
.fc .fc-daygrid-day-events::-webkit-scrollbar-track-piece {
width: 3px;
}
/* Visualização */
.fc .fc-scroller {
overflow: hidden !important;
}
.fc .fc-view-harness {
height: 1000px !important;
}
/* Mobile */
#list-events h2:first-letter {
text-transform: capitalize;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment