Skip to content

Instantly share code, notes, and snippets.

@OlinB
Last active June 18, 2024 07:54
Show Gist options
  • Save OlinB/002b80d0b685d96297708612b842171a to your computer and use it in GitHub Desktop.
Save OlinB/002b80d0b685d96297708612b842171a to your computer and use it in GitHub Desktop.
Octobercms calendar controller

Calendar controller

I needed an interactable calendar, so I used the new list controller popup mode, removed the list and replaced it with the calendar from fullcalendar.io

requirements

An Event model with title, start_at, end_at, all_day properties

<div class="row">
<div id='list-calendar' class="col-10 offset-1"></div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
var calendarEl = document.getElementById('list-calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'dayGridMonth',
themeSystem: 'bootstrap',
timeZone: 'UTC',
eventSources: [
'<?= $this->actionUrl('json') ?>'
],
locale: 'fr',
eventSourceFailure(error) {
if (error.status === 401) {
window.location.reload();
}
},
headerToolbar: {
left: '',
center: '',
right: ''
},
selectable: true,
eventClick: function (info) {
if (info.event.extendedProps.form_record_id) {
oc.listOnLoadForm(info.event.extendedProps.form_record_id);
}
},
select: function (info) {
$('<a />').popup({
handler: 'onLoadPopupForm',
extraData: {
'start-at': info.startStr,
'end-at': info.endStr
},
success: function() {
calendar.refetchEvents();
}
});
},
height: 'auto',
editable: true,
eventResizableFromStart: true,
eventDrop: function(info) {
oc.ajax('onMoveEvent', {
data: {
id: info.event.extendedProps.form_record_id,
start_at: info.event.start.toISOString(),
end_at: info.event.end.toISOString()
},
error: function () {info.revert()},
success: function () {},
});
},
eventResize: function (info) {
oc.ajax('onMoveEvent', {
data: {
id: info.event.extendedProps.form_record_id,
start_at: info.event.start.toISOString(),
end_at: info.event.end.toISOString()
},
error: function () {info.revert()},
success: function () {},
});
},
});
calendar.render();
document.getElementById('calendar-prev-button').addEventListener('click', function () {
calendar.prev();
})
document.getElementById('calendar-today-button').addEventListener('click', function () {
calendar.today();
});
document.getElementById('calendar-next-button').addEventListener('click', function () {
calendar.next();
})
document.querySelectorAll('a[data-bs-toggle="tab"]').forEach(e => e.addEventListener('shown.bs.tab', function (event) {
calendar.render();
})
)
document.getElementById('calendar-view-week-button').addEventListener('click', function () {
calendar.changeView('timeGridWeek');
})
document.getElementById('calendar-view-month-button').addEventListener('click', function () {
calendar.changeView('dayGridMonth');
})
document.getElementById('calendar-view-year-button').addEventListener('click', function () {
calendar.changeView('multiMonthYear');
})
addEventListener('ajax:update', function() {
calendar.refetchEvents()
});
});
</script>
<div data-control="toolbar loader-container">
<div class="btn-group">
<button id="calendar-prev-button" class="btn btn-default oc-icon-chevron-left"></button>
<button id="calendar-today-button" class="btn btn-default">Today</button>
<button id="calendar-next-button" class="btn btn-default oc-icon-chevron-right"></button>
</div>
<div class="btn-group">
<button id="calendar-view-week-button" class="btn btn-default">Week</button>
<button id="calendar-view-month-button" class="btn btn-default">Month</button>
<button id="calendar-view-year-button" class="btn btn-default">Year</button>
</div>
</div>
.clickable-event:hover {
cursor: pointer;
}
<?php namespace Inetis\Demo\Controllers;
use BackendMenu;
use Backend\Classes\Controller;
use Carbon\Carbon;
use Illuminate\Support\Facades\Response;
use Inetis\Demo\Models\Event;
use Redirect;
class Events extends Controller
{
public $implement = [
\Backend\Behaviors\FormController::class,
\Backend\Behaviors\ListController::class,
];
public $formConfig = 'config_form.yaml';
public $listConfig = 'config_list.yaml';
public function __construct()
{
parent::__construct();
$this->addJs('/plugins/inetis/demo/assets/vendor/fullcalendar-6.1.11/dist/index.global.js');
$this->addCss('/plugins/inetis/demo/assets/css/calendar.css');
BackendMenu::setContext('Inetis.Demo', 'demo', 'events');
}
public function json()
{
$start = Carbon::make(input('start'));
$end = Carbon::make(input('end'));
// Add buffer for smoother navigation
$start = $start->subMonth();
$end = $end->addMonth();
$events = Event::where('start_at', '<=', $end)
->where('end_at', '>=', $start)
->get();
return Response::json(
$events->map(function (Event $event) {
return [
'start' => $event->start_at->toIso8601String(),
'end' => $event->end_at->toIso8601String(),
'title' => $event->title,
'backgroundColor' => '#ff0000',
'borderColor' => '#ff0000',
'form_record_id' => $event->id,
'classNames' => ['clickable-event'],
'allDay' => $event->all_day,
];
})
);
}
public function formExtendFields($form, $fields)
{
if ($form->context == 'create') {
if (input('start-at')) {
$form->addFields([
'start_at' => [
'label' => 'De',
'type' => 'datepicker',
'mode' => 'datetime',
'span' => 'auto',
'default' => input('start-at'),
],
]);
}
if (input('end-at')) {
$form->addFields([
'end_at' => [
'label' => 'À',
'type' => 'datepicker',
'mode' => 'datetime',
'span' => 'auto',
'default' => input('end-at'),
],
]);
}
}
}
public function onMoveEvent()
{
if (!input('id') || !input('start_at') || !input('end_at')) {
return false;
}
$event = Event::find(input('id'));
if (!$event) {
return false;
}
$event->start_at = Carbon::make(input('start_at'));
$event->end_at = Carbon::make(input('end_at'));
$event->save();
return true;
}
public function index_onPopupSave()
{
$this->asExtension('FormController')->index_onPopupSave();
return ['success' => true];
}
public function index_onPopupDelete()
{
$this->asExtension('FormController')->index_onPopupDelete();
return ['success' => true];
}
}
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="control-toolbar">
<?= $this->makePartial('list_toolbar') ?>
</div>
</div>
</div>
<?= $this->makePartial('calendar') ?>
</div>
@damsfx
Copy link

damsfx commented Jun 7, 2024

@OlinB Nice !! 🤩

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment