Last active
December 8, 2023 13:29
-
-
Save prince-neres/5311cd5da03a65832f7b3ce02359d414 to your computer and use it in GitHub Desktop.
Calendar form filled as events
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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