skip to Main Content

I have a laravel 9 project with livewire, jetstream, fullcalendar 5, alpine, and tailwind (TALL stack) based off of this project:
https://github.com/LaravelDaily/Laravel-Jetstream-CRUD-Roles
and this tutorial: https://laravel.sillo.org/liveware-fullcalendar/

I have the drag and drop functionality as well as the select and eventClick actions working with livewire that submit event data to the database. I’ve also got the CRUD functions for events and timeline resources contributing data to the calendar view. I’m trying to employ a modal for both the select function and the eventClick function that adds an event and edits an event respectively.

My issues are currently threefold:

  1. Curently, thanks to @ADyson, I managed to get the livewire calendar element to launch a livewire modal element and retrieve PART of the data. The event title, start, and end times. I’m trying to also include additional fields in the events CRUD for event acronym, city, venue, etc…I think the issue here is that I have not specifically declared these as EventObjects like ExtendedProps.

a)I’m interested specifically in where the livewire calendar class might be edited and imagine it’s in this stanza of the view file where the events are constructed from JSON like this

    events: JSON.parse(@this.events),

b) and I assume the select and eventClick statements in the same file would need to have the event info function altered to retrieve this information as well.

Below is the current fullcalendar 5 view file calendar.blade.php:

<style>
  #calendar-container {
        display: grid;
        grid-template-columns: 200px 1fr;
        padding: 20px;
    }
    #events {
        grid-column: 1;
    }
    #calendar {
        grid-column: 2;
        height: 700px;
    }
    .dropEvent {
        background-color: DodgerBlue;
        color: white;
        padding: 5px 16px;
        margin-bottom: 10px;
        text-align: center;
        display: inline-block;
        font-size: 16px;
        border-radius: 4px;
        cursor:pointer;
    }
</style>
<div>
  @include('livewire.eventmodal')
<div>
  <!-- sidebar -->
    <div id="calendar-container" wire:ignore>
        <div id="events">
            <div data-event='{"title":"Evénement A"}' class="dropEvent">Event One Drag</div>
            <div data-event='{"title":"Evénement B"}' class="dropEvent">Event Two Draggable</div>
        </div>
        <div id="calendar"></div>
    </div>
</div>

</div>

@push('scripts')
<script src='https://cdn.jsdelivr.net/npm/[email protected]/main.min.js'></script>
<script>
create_UUID = () => {
let dt = new Date().getTime();
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
    let r = (dt + Math.random() * 16) % 16 | 0;
    dt = Math.floor(dt / 16);
    return (c == 'x' ? r :(r&0x3|0x8)).toString(16);
});
return uuid;
}
document.addEventListener('livewire:load', function () {
const Calendar = FullCalendar.Calendar;
const calendarEl = document.getElementById('calendar');
const Draggable = FullCalendar.Draggable;
    new Draggable(document.getElementById('events'), {
        itemSelector: '.dropEvent'
    });
const calendar = new Calendar(calendarEl, {
  headerToolbar: {
        left: 'promptResource prev,next today',
        center: 'title',
        right: 'resourceMonth,dayGridMonth,timeGridWeek,timeGridDay,listMonth'
      },
      views: {
        resourceMonth: {
        type: 'resourceTimelineMonth',
        buttonText: 'personnel'
        }
      },
      customButtons: {
        promptResource: {
          text: "+ personnel",
      click: function() {
        var title = prompt("Name");
        if (title) {
          calendar.addResource({
            title: title
          });
          fetch("add_resources.php", {
            method: "POST",
            headers: {
              Accept: "application/json"
            },
            body: encodeFormData({ title: title })
          })
            .then(response => console.log(response))
            .catch(error => console.log(error));
        }
      }
    }
  },
    initialView: 'resourceTimelineMonth',
    locale: '{{ config('app.locale') }}',
    events: JSON.parse(@this.events),
    resourceAreaHeaderContent: 'Personnel',
    resources: JSON.parse(@this.resources),
    //'https://fullcalendar.io/api/demo-feeds/resources.json?with-nesting&with-colors',
    editable: true,
    eventResize: info => @this.eventChange(info.event),
    eventDrop: info => @this.eventChange(info.event),
    eventReceive: info => {
        const id = create_UUID();
          info.event.setProp('id', id);
          @this.eventAdd(info.event);
    },
    selectable: true,
    select: function(selectionInfo) {
          $('#eventModal').modal('show');
          },
    eventClick: function(info) {
          // Display the modal and set the values to the event values.
          $('#updateEventModal').modal('show');
          $('#updateEventModal').find('#title').val(info.event.title);
          $('#updateEventModal').find('#acronym').val(info.event.acronym);
          $('#updateEventModal').find('#city').val(info.event.city);
          $('#updateEventModal').find('#start').val(info.event.start);
          $('#updateEventModal').find('#end').val(info.event.end);
          },
});
calendar.render();
});
</script>
<link href='https://cdn.jsdelivr.net/npm/[email protected]/main.min.css' rel='stylesheet' />
@endpush

I have further separated out the actual modal from being included in this file and it’s a separate livewire element now called eventmodal.blade.php:

<!-- Insert Modal -->
<div wire:ignore.self class="modal fade" id="eventModal" tabindex="-1" aria-labelledby="eventModalLabel"
    aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="eventModalLabel">Create Event</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"
                    wire:click="closeModal"></button>
            </div>
            <form wire:submit.prevent="saveEvent">
                <div class="modal-body">
                    <div class="mb-3">
                        <label>Event Name</label>
                        <input type="text" id="title" wire:model="title" class="form-control">
                        @error('title') <span class="text-danger">{{ $message }}</span> @enderror
                    </div>
                    <div class="mb-3">
                        <label>Event Acronym</label>
                        <input type="text" id="acronym" wire:model="acronym" class="form-control">
                        @error('acronym') <span class="text-danger">{{ $message }}</span> @enderror
                    </div>
                    <div class="mb-3">
                        <label>Event City</label>
                        <input type="text" id="city" wire:model="city" class="form-control">
                        @error('city') <span class="text-danger">{{ $message }}</span> @enderror
                    </div>
                    <div class="mb-3">
                        <label>Event Venue</label>
                        <input type="text" id="venue" wire:model="venue" class="form-control">
                        @error('venue') <span class="text-danger">{{ $message }}</span> @enderror
                    </div>
                    <div class="mb-3">
                        <label>Event Value</label>
                        <input type="text" id="value" wire:model="value" class="form-control">
                        @error('value') <span class="text-danger">{{ $message }}</span> @enderror
                    </div>
                    <div class="mb-3">
                        <label>Event Start</label>
                        <input type="text" id="start" wire:model="start" class="form-control">
                        @error('start') <span class="text-danger">{{ $message }}</span> @enderror
                    </div>
                    <div class="mb-3">
                        <label>Event End</label>
                        <input type="text" id="end"  wire:model="end" class="form-control">
                        @error('end') <span class="text-danger">{{ $message }}</span> @enderror
                    </div>

                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" wire:click="closeModal"
                        data-bs-dismiss="modal">Close</button>
                    <button type="submit" class="btn btn-primary">Save</button>
                </div>
            </form>
        </div>
    </div>
</div>


<!-- Update Event Modal -->
<div wire:ignore.self class="modal fade" id="updateEventModal" tabindex="-1" aria-labelledby="updateEventModalLabel"
    aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="updateEventModalLabel">Edit Event</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" wire:click="closeModal"
                    aria-label="Close"></button>
            </div>
            <form wire:submit.prevent="updateEvent">
                <div class="modal-body">
                    <div class="mb-3">
                        <label>Event Name</label>
                        <input type="text" id="title" wire:model="title" class="form-control">
                        @error('title') <span class="text-danger">{{ $message }}</span> @enderror
                    </div>
                    <div class="mb-3">
                        <label>Event Acronym</label>
                        <input type="text" id="acronym" wire:model="acronym" class="form-control">
                        @error('acronym') <span class="text-danger">{{ $message }}</span> @enderror
                    </div>
                    <div class="mb-3">
                        <label>Event City</label>
                        <input type="text" id="city" wire:model="city" class="form-control">
                        @error('city') <span class="text-danger">{{ $message }}</span> @enderror
                    </div>
                    <div class="mb-3">
                        <label>Event Venue</label>
                        <input type="text" id="venue" wire:model="venue" class="form-control">
                        @error('venue') <span class="text-danger">{{ $message }}</span> @enderror
                    </div>
                    <div class="mb-3">
                        <label>Event Value</label>
                        <input type="text" id="value" wire:model="value" class="form-control">
                        @error('value') <span class="text-danger">{{ $message }}</span> @enderror
                    </div>
                    <div class="mb-3">
                        <label>Event Start</label>
                        <input type="text" id="start" wire:model="start" class="form-control">
                        @error('start') <span class="text-danger">{{ $message }}</span> @enderror
                    </div>
                    <div class="mb-3">
                        <label>Event End</label>
                        <input type="text" id="end" wire:model="end" class="form-control">
                        @error('end') <span class="text-danger">{{ $message }}</span> @enderror
                    </div>

                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" wire:click="closeModal"
                        data-bs-dismiss="modal">Close</button>
                    <button type="submit" class="btn btn-primary">Update</button>
                </div>
            </form>
        </div>
    </div>
</div>

<!-- Delete Event Modal -->
<div wire:ignore.self class="modal fade" id="deleteEventModal" tabindex="-1" aria-labelledby="deleteEventModalLabel"
    aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="deleteEventModalLabel">Delete Event</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" wire:click="closeModal"
                    aria-label="Close"></button>
            </div>
            <form wire:submit.prevent="destroyEvent">
                <div class="modal-body">
                    <h4>Are you sure you want to delete this data ?</h4>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" wire:click="closeModal"
                        data-bs-dismiss="modal">Close</button>
                    <button type="submit" class="btn btn-primary">Yes! Delete</button>
                </div>
            </form>
        </div>
    </div>
</div>
  1. Secondly, the current modals save functions are not working. I’m lost at where to start…do I need an additional route? Some additional code in the form of a controller?

  2. Lastly, my select function…when you click on a date or a date range in the default fullcalendar view is not picking up the date ranges from fullcalendar as it does without the modal using the standard function like this:

    select: arg => {
        const title = prompt('Title :');
        const id = create_UUID();
        if (title) {
            calendar.addEvent({
                id: id,
                title: title,
                start: arg.start,
                end: arg.end,
                allDay: arg.allDay
            });
            @this.eventAdd(calendar.getEventById(id));
        };
        calendar.unselect();
 

2

Answers


  1. Chosen as BEST ANSWER

    Thanks to @ADyson, I've managed to retrieve the start and end date on the eventClick events within my livewire modal with the calendar.blade.php like so:

    //...previously posted calendar view code above
    select: function(selectionInfo) {
                    // Display the modal.
                    // You could fill in the start and end fields based on the parameters
                    $('#AddEventModal').modal('show');
    
                },
                eventClick: function(info) {
                    // Display the modal and set the values to the event values.
                    $('#AddEventModal').find('#title').val(info.event.title);
                    $('#AddEventModal').find('#acronym').val(info.event.acronym);
                    $('#AddEventModal').find('#start').val(info.event.start);
                    $('#AddEventModal').find('#end').val(info.event.end);
                    $('#AddEventModal').modal();
                },
    ...
    

    As you can see, I'm also trying to retrieve data from another field in the events table for 'acronym'. Perhaps the issue lies in either my livewire/calendar.php

    <?php
    
    namespace AppHttpLivewire;
    
    use LivewireComponent;
    use AppModelsEvent;
    use AppModelsResource;
    use IlluminateSupportArr;
    
    class Calendar extends Component
    {
        public $events, $resources, $title, $acronym, $city, $venue, $value ;
    
    
        public function eventChange ( $event )
        {
        $e=Event::find($event['id']) ;
        $e->start=$event['start'] ;
        $e->acronym=$event['acronym'] ;
        if(Arr::exists($event,'end')) {
            $e->end=$event['end'];
        }
        $e->save();
        }
    
        public function render()
        {
            $this->events=json_encode(Event::all());
            $this->resources=json_encode(Resource::all());
            return view('livewire.calendar');
        }
        public function eventAdd ( $event )
        {
          Event::create ( $event );
        }
        public function eventRemove ( $id )
        {
          Event::destroy ( $id );
        }
        public function resourceAdd ( $resources )
        {
          Resource::create ( $resources );
        }
        public function resourceRemove ( $id )
        {
          Resource::destroy ( $id );
        }
    }
    

  2. I managed to get the first part of my issue resolved. FullCalendar 5, when retrieving the events via JSON automatically detects extendedProps that can be retrieved with the eventClick method like so:

    eventClick: function(info) {
              // Display the modal and set the values to the event values.
              $('#updateEventModal').modal('show');
              $('#updateEventModal').find('#title').val(info.event.title);
              $('#updateEventModal').find('#acronym').val(info.event.extendedProps.acronym);
              $('#updateEventModal').find('#city').val(info.event.extendedProps.city);
              $('#updateEventModal').find('#venue').val(info.event.extendedProps.venue);
              $('#updateEventModal').find('#value').val(info.event.extendedProps.value);
              $('#updateEventModal').find('#start').val(info.event.start);
              $('#updateEventModal').find('#end').val(info.event.end);
              },
      
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search