saade / filament-fullcalendar

The Most Popular JavaScript Calendar as a Filament Widget
MIT License
269 stars 83 forks source link

Typed property Saade\FilamentFullCalendar\Widgets\FullCalendarWidget::$record (on drag and drop) #140

Closed dreamfast closed 7 months ago

dreamfast commented 9 months ago

Hi, thanks for making this plugin!

I've followed the readme and I'm trying to update the dates on drag and drop.

I have a Calendar page which has the CalendarWidget.php

I have at the top public Model | string | null $model = JobTask::class;, although have tried with and without it. I am hoping to target many models, let me know if this is an issue.

Using use Saade\FilamentFullCalendar\Actions; I get the error Typed property Saade\FilamentFullCalendar\Widgets\FullCalendarWidget::$record, with no model updates.

Using use Filament\Actions; it will work, although have App\Filament\Dashboard\Widgets\CalendarWidget::App\Filament\Dashboard\Widgets\{closure}(): Argument #1 ($record) must be of type Illuminate\Database\Eloquent\Model, null given, called in /var/www/laravel/vendor/filament/support/src/Concerns/EvaluatesClosures.php on line 35 after the event has finished.

image

I am using this code, note for the arguments I had appended |null for the use Filament\Actions; to work. Without them there is an error thrown as they do not exist.

I figured it's best to use use Saade\FilamentFullCalendar\Actions; but I must be overlooking something or missed a step with the setup. This is the CalendarWidget.php file. Note I had tried to work around this by using the switch and find model statements, but no luck.

<?php

namespace App\Filament\Dashboard\Widgets;

use App\Filament\Dashboard\Resources\JobResource;
use App\Filament\Dashboard\Resources\JobTaskResource;
use App\Models\Job;
use Carbon\Carbon;
use Filament\Actions;
use Filament\Forms\Form;
use Illuminate\Database\Eloquent\Model;
use Saade\FilamentFullCalendar\Widgets\FullCalendarWidget;
use App\Models\JobTask;

class CalendarWidget 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 on the calendar.
     */
//    public Model | string | null $model = JobTask::class;

    public function fetchEvents(array $info): array
    {
        // You can use $info to filter events by date.
        // This method should return an array of event-like objects. See: https://github.com/saade/filament-fullcalendar/blob/3.x/#returning-events
        // You can also return an array of EventData objects. See: https://github.com/saade/filament-fullcalendar/blob/3.x/#the-eventdata-class

        $jobTasks = JobTask::query()
            ->where('start_date', '>=', $info['start'])
            ->where('end_date', '<=', $info['end'])
            ->get()
            ->map(
                fn (JobTask $event) => [
                    'id' => $event->id,
                    'calendar_type' => 'job-task',
                    'title' => $event->name,
                    'start' => $event->start_date,
//                    'end' => $event->end_date,
                    'backgroundColor' => 'blue',
                    'eventBorderColor' => 'blue',
                    'displayEventTime' => false,
                    'url' => JobTaskResource::getUrl(name: 'view', parameters: ['record' => $event]),
                ]
            )
            ->all();

        $jobs = Job::query()
            ->where('start_date', '>=', $info['start'])
            ->where('deadline_date', '<=', $info['end'])
            ->get()
            ->map(
                fn (Job $event) => [
                    'id' => $event->id,
                    'calendar_type' => 'job',
                    'title' => $event->name,
                    'start' => $event->start_date,
//                    'end' => $event->deadline_date,
                    'backgroundColor' => 'darkgrey',
                    'eventBorderColor' => 'darkgrey',
                    'displayEventTime' => false,
                    'url' => JobResource::getUrl(name: 'view', parameters: ['record' => $event]),
                ]
            )
            ->all();

        return array_merge($jobTasks, $jobs);
    }

    protected function modalActions(): array
    {
        return [
            Actions\EditAction::make()
                ->mountUsing(
                    function (Model|null $record, Form|null $form, array $arguments) {
                        dd($record,$form,$arguments);
                        switch ($arguments['event']['extendedProps']['calendar_type']) {
                            case 'job':
                                $record = Job::find($arguments['event']['id']);
                                $record ->update([
                                    'start_date' => Carbon::parse($arguments['event']['start']),
                                ]);
                                break;

                            case 'job-task':
                                $record = JobTask::find($arguments['event']['id']);
                                $record->update([
                                    'start_date' => Carbon::parse($arguments['event']['start']),
                                ]);
                                break;
                        }
                    }
                ),
        ];
    }

}
dreamfast commented 7 months ago

Actually the solution for this was rather simple and I had had overridden the drag and drop event here - https://github.com/saade/filament-fullcalendar/blob/3.x/src/Widgets/Concerns/InteractsWithEvents.php

Originally the documentation assumes there is just one Model type Event. Having more than one Model type and having some custom code to parse the events and save them works much better.

This is what I had ended up using.

    public function onEventDrop(array $event, array $oldEvent, array $relatedEvents, array $delta): bool
    {
        $model = null;

        switch ($event['extendedProps']['calendar_type']) {
            case 'job':
                $model = Job::findOrFail($event['id']);
                $model->deadline_date = Carbon::parse($event['end']);
            break;

            case 'job-task':
                $model = JobTask::findOrFail($event['id']);
                if ($event['end']) {
                    $model->end_date = Carbon::parse($event['end']);
                }
            break;
        }

        $model->start_date = Carbon::parse($event['start']);
        $model->save();

        return false;
    }