* ๊ฐ์ธ ํ๋ก์ ํธ
* FullCalendar๋ cdn ์ฌ์ฉ
* DB๋ Firebase ์ฌ์ฉ
* FullCalendar ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฌธ์ ์ฐธ๊ณ
Event Parsing - Docs | FullCalendar
When you give your calendar event data, whether it’s through an array, a json feed, or the addEvent method, you specify the event as a plain JavaScript object with properties. This object then gets “parsed” into a proper Event Object that is then exp
fullcalendar.io
1. ๊ธฐ๋ณธ ์บ๋ฆฐ๋ ์์ฑ
*HTML
<!-- ์บ๋ฆฐ๋ ์์ฑ ์์น -->
<div id='calendar-container'>
<div id='calendar'></div>
</div>
<!-- ๋ชจ๋ฌ -->
<div class="modal fade" id="addEventModal" tabindex="-1" aria-labelledby="addEventModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addEventModalLabel">๊ณผ์ ์ถ๊ฐ</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="eventForm">
<div class="mb-3">
<label for="subject" class="form-label">๊ณผ๋ชฉ</label>
<input type="text" class="form-control" id="subject" required>
</div>
<div class="mb-3">
<label for="title" class="form-label">๊ณผ์ </label>
<input type="text" class="form-control" id="title" required>
</div>
<div class="mb-3">
<label for="start" class="form-label">์์ ์ผ์</label>
<input type="date" class="form-control" id="start" required>
</div>
<div class="mb-3">
<label for="end" class="form-label">์ข
๋ฃ ์ผ์</label>
<input type="date" class="form-control" id="end" required>
</div>
<button type="submit" class="btn btn-primary">์ ์ฅ</button>
</form>
</div>
</div>
</div>
</div>
*JS
// fullcalendar ๋ก๋
document.addEventListener('DOMContentLoaded', function() {
const calendarEl = document.getElementById('calendar');
// FullCalendar ์ต์
์ค์
const calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'dayGridMonth',
selectable: true, // ๋ฌ๋ ฅ ์
์ ํ ํ์ฑํ
select: function(info) { // ๋ฌ๋ ฅ ์
์ ํด๋ฆญํ ๋ ๋ชจ๋ฌ ์ด๊ธฐ
$('#addEventModal').modal('show');
$('#start').val(info.startStr);
$('#end').val(info.endStr);
},
events: loadEvents // ์ผ์ ๋ถ๋ฌ์ค๊ธฐ(loadEvents ํจ์ ์ฐธ์กฐ)
});
calendar.render();
// ๊ณผ์ ์ถ๊ฐ ํผ ์ ์ถ ์
$('#eventForm').submit(function(e) {
e.preventDefault(); // ํผ์ ๊ธฐ๋ณธ ๋์ ๋ฐฉ์ง
// ์
๋ ฅ๋ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
const subject = $('#subject').val();
const title = $('#title').val();
const start = $('#start').val();
const end = $('#end').val();
docTime(); // ๊ณ ์ ๋ฒํธ ๋ฆฌ์
(๊ฐ์ธ์ ์ผ๋ก ์ฌ์ฉํ๋ ๊ณ ์ ๋ฒํธ์)
// Firebase์ ๊ณผ์ ์ถ๊ฐ
db.collection("events").doc(docName).set({
docName : docName,
subject : subject,
title: title,
start: start,
end: end
})
.then(function() {
$('#addEventModal').modal('hide'); // ๋ชจ๋ฌ ๋ซ๊ธฐ
calendar.refetchEvents(); // ๋ฌ๋ ฅ ๊ฐฑ์
resetForm(); // form ์ด๊ธฐํ
})
.catch(function(error) {
console.error("๊ณผ์ ์ถ๊ฐ ์ค๋ฅ:", error);
});
});
// Firebase์์ ์ผ์ ๊ฐ์ ธ์ค๊ธฐ
function loadEvents(fetchInfo, successCallback, failureCallback) {
db.collection("events").get().then((querySnapshot) => {
let events = [];
querySnapshot.forEach((doc) => {
const eventData = doc.data();
events.push({
title: eventData.subject,
start: eventData.start,
end: eventData.end
});
});
successCallback(events); // ๊ฐ์ ธ์จ ์ด๋ฒคํธ ๋ฐฐ์ด ์ ๋ฌ
})
.catch(function(error) {
console.error("์ผ์ ๋ถ๋ฌ์ค๊ธฐ ์ค๋ฅ:", error);
failureCallback(error); // ์ค๋ฅ ๋ฐ์ ์ ์คํจ ์ฝ๋ฐฑ ํธ์ถ
});
}
});
2. ์ฃผ๋ง ์ ๋ณ๊ฒฝ
๊ทธ๋ฅ ๊ธฐ๋ณธ ์บ๋ฆฐ๋ ๋ก๋ํ๋๋ ์ ์ฒด ํ๋์์ ๋ง๋์๋๋ ๋ฐ์ค.
์ ๋ค๋ฅธ์ฌ๋๋ค์ ์ ์ฒด ๊ฒ์์์ด๋๋ฐ ๋๋ ํ๋์์ด์ง..? ํ๋๋ฐ ๋ถํธ์คํธ๋ฉ์ ์ฌ์ฉํด์ aํ๊ทธ์ ๋ถ๋ชํ๋๋ดใ
*CSS
/* calendar.html */
.fc-col-header-cell-cushion, .fc-daygrid-day-number {
text-decoration: none;
}
.fc-scrollgrid-sync-inner > .fc-col-header-cell-cushion,
.fc-day-mon .fc-daygrid-day-number,
.fc-day-tue .fc-daygrid-day-number,
.fc-day-wed .fc-daygrid-day-number,
.fc-day-thu .fc-daygrid-day-number,
.fc-day-fri .fc-daygrid-day-number {
color: black;
}
.fc-day-sun .fc-col-header-cell-cushion,
.fc-day-sun a{
color : red;
}
.fc-day-sat .fc-col-header-cell-cushion,
.fc-day-sat a {
color : blue;
}
3. ์ผ์ ์ถ๊ฐ
์ผ์ ์ถ๊ฐ์ ๋ฌธ์ ๊ฐ ์๊ฒผ๋ค.
์์ฒ๋ผ 2์ 5์ผ ~ 2์ 6์ผ ์ ์ผ์ ์ ์ถ๊ฐํ๊ณ ์ถ์๋๋ฐ 5์ผ ํ๋ฃจ์๋ง ์ผ์ ์ด ์ถ๊ฐ ๋ ๊ฒ.
๋ฌธ์๋ฅผ ์ฐพ์๋ณด๋
์๊ฐ ์ค์ ์ด ๋์ด ์์ง ์์ผ๋ฉด 2์ 6์ผ์ ์ผ์ ์ด ์ข ๋ฃ๋๋ค๊ณ ์๊ฐํ๊ณ
2์ 5์ผ์๋ง ์ผ์ ์ด ์ถ๊ฐ๋๋ ๊ฒ ๊ฐ์๋ค.
์ค์ ๋ก input type์ 'date'๊ฐ ์๋๋ผ 'datetime-local'๋ก ์ค์ ํด์ ์๊ฐ๊น์ง ๋ฃ์ด์ฃผ๋ฉด
์ ๋๋ก ์ผ์ ์ด ์ถ๊ฐ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
ํ์ง๋ง ๋๋ ์ผ์ ์ถ๊ฐํ ๋ ๊ท์ฐฎ๊ฒ ์๊ฐ์ ๋ฃ๊ณ ์ถ์ง ์์๋ค!
*JS
// Firebase์ ๊ณผ์ ์ถ๊ฐ
db.collection("events").doc(docName).set({
docName : docName,
subject : subject,
title: title,
start: start+"T00:00",
end: end+"T23:59"
})
๊ทธ๋์ ๋ฐ์ดํฐ ์ถ๊ฐ ์ start + "T00:00", end + "T23:00" ์ผ๋ก ๊ฐ์ ์๊ฐ ์ ๋ ฅํด์คฌ๋ค.
์ ์์ ์ผ๋ก ๋์ํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
4. ์๊ฐ ํ์ ์ ๊ฑฐ
์ผ์ ์ถ๊ฐํ๋ ๊ฑด ํด๊ฒฐ ๋์ผ๋ ์ ๋ชป์๊ธด 12a ๋ผ๊ณ ๋์ค๋ ์๊ฐ ํ์ ๋ถ๋ถ์ ์ ๊ฑฐ ํ๊ณ ์ถ์๋ค.
*JS
// FullCalendar ์ต์
์ค์
const calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'dayGridMonth',
selectable: true, // ๋ฌ๋ ฅ ์
์ ํ ํ์ฑํ
displayEventTime: false, // ์๊ฐ ํ์ ์ ๊ฑฐ
eventColor: '#990e17',
select: function(info) { // ๋ฌ๋ ฅ ์
์ ํด๋ฆญํ ๋ ๋ชจ๋ฌ ์ด๊ธฐ
$('#addEventModal').modal('show');
$('#start').val(info.startStr);
$('#end').val(info.endStr);
},
events: loadEvents // ์ผ์ ๋ถ๋ฌ์ค๊ธฐ(loadEvents ํจ์ ์ฐธ์กฐ)
});
๋ฌธ์์ ๋์์๋๋๋ก displayEventTime์ false๋ก ์ง์ ํด์ฃผ๊ณ
๊ฒธ์ฌ ๊ฒธ์ฌ ์ผ์ ์ปฌ๋ฌ๋ฅผ ํ์ฌ ์ฌ์ดํธ์ ๋ง๋ ์์ผ๋ก ๋ณ๊ฒฝํด์ฃผ์๋ค(= eventColor)
์์ฑ!