Filament FullCalendar

repository·4.x·Indexed 19 days ago

https://github.com/saade/filament-fullcalendar

A customizable calendar widget for Filament powered by FullCalendar. It integrates with Filament's theme and actions to provide event viewing, creation, editing, and deletion via modals. Features include support for the EventData class, CRUD actions via getFormSchema(), and configuration through the FilamentFullCalendarPlugin.

Tokens
6.9K
Snippets
22
Records
25
Agent score
63%

What's inside filament-fullcalendar

  1. Intercept calendar events

    4.x

    You can intercept calendar events by overriding methods provided by the InteractsWithEvents trait.

    Warning: If you override any of these methods, you must call parent::methodName() to ensure the calendar continues to function correctly.

  2. Install Filament FullCalendar

    4.x

    Install the package via Composer:

    composer require saade/filament-fullcalendar:^3.0

    Important Styling Setup:

    If you are using Filament Panels, ensure you have a custom theme set up following the Filament Docs.

    After setting up your theme, you must add the plugin's views to your theme's CSS file (or your app's CSS file if using standalone packages) using @import and @source directives:

    @import '../../../../vendor/saade/filament-fullcalendar/resources/css/filament-fullcalendar.css';
    
    @source '../../../../vendor/saade/filament-fullcalendar/resources/views/**/*.blade.php';
  3. Create and implement a Calendar Widget

    4.x

    To use the calendar, you must create a Filament Widget and extend the Saade ilament-fullcalendar Widgets\FullCalendarWidget class.

    1. Generate the widget:
    php artisan make:filament-widget CalendarWidget
    1. Update the class to extend FullCalendarWidget and remove the protected static string $view property from the generated class.

    2. Implement the fetchEvents(array $fetchInfo): array method. This method is called by FullCalendar whenever it needs new data (e.g., when navigating between months or changing views). The $fetchInfo array contains start and end timestamps used to filter the data you return.

    <?php
    
    namespace App\Filament\Widgets;
    
    use Saade\FilamentFullCalendar\Widgets\FullCalendarWidget;
    
    class CalendarWidget extends FullCalendarWidget
    {
        public function fetchEvents(array $fetchInfo): array
        {
            // Return an array of event-like objects or EventData objects
            return [];
        }
    }
  4. Register the FilamentFullCalendarPlugin

    4.x

    To enable configuration, you must register the FilamentFullCalendarPlugin within your Panel Provider's plugins array.

    <?php
    
    namespace App\Providers\Filament;
    
    use Filament\Panel;
    use Filament\PanelProvider;
    use Saade\FilamentFullCalendar\FilamentFullCalendarPlugin;
    
    class AdminPanelProvider extends PanelProvider
    {
        public function panel(Panel $panel): Panel
        {
            return $panel
                // ... other configuration
                ->plugin(
                    FilamentFullCalendarPlugin::make()
                        // Call configuration methods here
                        ->selectable()
                        ->editable()
                );
        }
    }
    <?php
    
    namespace App\Providers\Filament;
    
    use Filament\Panel;
    use Filament\PanelProvider;
    use Saade\FilamentFullCalendar\FilamentFullCalendarPlugin;
    
    class AdminPanelProvider extends PanelProvider
    {
        public function panel(Panel $panel): Panel
        {
            return $panel
                ->default()
                ->id('admin')
                ->path('admin')
                ...
                ->plugin(
                    FilamentFullCalendarPlugin::make()
                        ->schedulerLicenseKey()
                        ->selectable()
                        ->editable()
                        ->timezone()
                        ->locale()
                        ->plugins()
                        ->config()
                );
        }
    }
  5. Enable CRUD actions for events

    4.x

    The package uses Filament Actions to allow viewing, creating, editing, and deleting events. To enable this, you must define the $model property in your FullCalendarWidget class and implement the getFormSchema() method to define the form fields used in the action modals.

    Note: The form schema does not need to match the FullCalendar event object fields exactly; it should match your Eloquent model's fields.

    <?php
    
    namespace App\Filament\Widgets;
    
    use Saade\FilamentFullCalendar\Widgets\FullCalendarWidget;
    use App\Models\Event;
    use Filament\Forms;
    
    class CalendarWidget extends FullCalendarWidget
    {
        public Model | string | null $model = Event::class;
    
        public function getFormSchema(): array
        {
            return [
                Forms\Components\TextInput::make('name'),
    
                Forms\Components\Grid::make()
                    ->schema([
                        Forms\Components\DateTimePicker::make('starts_at'),
                        Forms\Components\DateTimePicker::make('ends_at'),
                    ]),
            ];
        }
    }
  6. Initialize the FilamentFullCalendarPlugin

    4.x

    To use the FullCalendar integration in your Filament panel, use the make() method to instantiate the plugin. You can then chain configuration methods to customize its behavior.

    use Saade\FilamentFullCalendar\FilamentFullCalendarPlugin;
    
    // In your Panel Provider
    ->plugins([
        FilamentFullCalendarPlugin::make()
            ->timezone('Europe/London')
            ->locale('en')
    ])
  7. Implement the FullCalendarWidget to display a calendar

    4.x

    To display a FullCalendar instance in your Filament dashboard, create a class that extends Saade\FilamentFullCalendar\Widgets\FullCalendarWidget.

    You must implement the fetchEvents method to provide the calendar with its event data. This method is called by FullCalendar whenever the user interacts with the calendar (e.g., clicking prev/next or switching views).

    You can also define getFormSchema() to provide a Filament form schema used for creating or editing events via modals.

    namespace App\Filament\Widgets;
    
    use Saade\FilamentFullCalendar\Widgets\FullCalendarWidget;
    
    class MyCalendarWidget extends FullCalendarWidget
    {
        /**
         * FullCalendar will call this function whenever it needs new event data.
         * This is triggered when the user clicks prev/next or switches views.
         * 
         * @param array{start: string, end: string, timezone: string} $info
         */
        public function fetchEvents(array $info): array
        {
            // Return an array of event data
            return [
                [
                    'title' => 'Event 1',
                    'start' => $info['start'],
                    'end' => $info['end'],
                ],
            ];
        }
    
        public function getFormSchema(): array
        {
            return [
                // Define your Filament form fields here
            ];
        }
    }
  8. Add additional data during event creation

    4.x

    Use mutateFormDataUsing on the CreateAction to inject extra data into the model during the creation process (e.g., associating the event with a specific calendar_id).

    protected function headerActions(): array
    {
        return [
            Actions\CreateAction::make()
                ->mutateFormDataUsing(function (array $data): array {
                    return [
                        ...$data,
                        'calendar_id' => $this->record->id
                    ];
                })
        ];
    }
  9. Sync drag-and-drop changes to the Edit form

    4.x

    When an event is moved via drag-and-drop, you can use the mountUsing method on the EditAction to capture the new start and end times from the $arguments array and fill the form automatically.

    use Saade\FilamentFullCalendar\Actions;
    
    protected function modalActions(): array
    {
        return [
            Actions\EditAction::make()
                ->mountUsing(
                    function (Event $record, Forms\Form $form, array $arguments) {
                        $form->fill([
                            'name' => $record->name,
                            'starts_at' => $arguments['event']['start'] ?? $record->starts_at,
                            'ends_at' => $arguments['event']['end'] ?? $record->ends_at
                        ]);
                    }
                ),
            Actions\DeleteAction::make(),
        ];
    }
  10. Pre-fill Create action with selected day dates

    4.x

    When creating an event by selecting a day on the calendar, use mountUsing on the CreateAction to extract the start and end dates from the $arguments provided by the calendar interaction.

    use Saade\FilamentFullCalendar\Actions\CreateAction;
    
    protected function headerActions(): array
    {
        return [
            CreateAction::make()
                ->mountUsing(
                    function (Forms\Form $form, array $arguments) {
                        $form->fill([
                            'starts_at' => $arguments['start'] ?? null,
                            'ends_at' => $arguments['end'] ?? null
                        ]);
                    }
                )
        ];
    }
  11. Add event tooltips on hover

    4.x

    You can implement tooltips by using the eventDidMount method to inject JavaScript that sets attributes (like Alpine.js x-tooltip or x-data) onto the event element.

    public function eventDidMount(): string
    {
        return <<<JS
            function({ event, timeText, isStart, isEnd, isMirror, isPast, isFuture, isToday, el, view }){
                el.setAttribute("x-tooltip", "tooltip");
                el.setAttribute("x-data", "{ tooltip: '"+event.title+"' }");
            }
        JS;
    }
  12. Return events using arrays or EventData class

    4.x

    The fetchEvents method in your widget must return an array of event objects. You have two options:

    1. Standard Array (FullCalendar format)

    Return an associative array following the FullCalendar event object specification. Common keys include title, start, end, and url.

    2. Using the EventData class

    For a more fluent API, use the Saade\FilamentFullCalendar\Data\EventData class. This is recommended for cleaner code.

    Supported EventData methods include:

    • id($id)
    • title($title)
    • start($start)
    • end($end)
    • url($url, $shouldOpenUrlInNewTab = false)
    <?php
    
    namespace App\
    Filament\
    Widgets;
    
    use Saade\
    FilamentFullCalendar\
    Widgets\
    FullCalendarWidget;
    use Saade\
    FilamentFullCalendar\
    Data\
    EventData;
    use App\
    Models\
    Event;
    
    class CalendarWidget extends FullCalendarWidget
    {
        public function fetchEvents(array $fetchInfo): array
        {
            return Event::query()
                ->where('starts_at', '>=', $fetchInfo['start'])
                ->where('ends_at', '<=', $fetchInfo['end'])
                ->get()
                ->map(
                    fn (Event $event) => EventData::make()
                        ->id($event->uuid)
                        ->title($event->name)
                        ->start($event->starts_at)
                        ->end($event->ends_at)
                        ->url(
                            url: EventResource::getUrl(name: 'view', parameters: ['record' => $event]),
                            shouldOpenUrlInNewTab: true
                        )
                )
                ->toArray();
        }
    }