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
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> |
@OlinB Nice !! 🤩