I have joined a few codes together and got myself in a right twist, I had this script working but the bootstrap.js was causing the rest of the site to break. so I removed the bootstrap and tried to refactor into alpine and tailwind but it just won’t work no matter what I do, and chat gpt can’t work it out either.
so its a laravel project, using breeze, and also alpine and tailwind.
this is the full code:
<!-- resources/views/calendars/fullcalendar.blade.php -->
@extends('layouts.app')
@push('styles')
<style>
/* Make dropdowns wider */
.modal .form-group select {
width: 100%;
}
/* Consolidate buttons in the modal footer */
.modal-footer {
display: flex;
justify-content: space-between;
}
/* Adjust the width of the calendar container */
#calendar {
max-width: 1200px; /* Increased width */
margin: 0 auto;
}
/* Additional Styles for iCal Buttons and User Dropdown */
.ical-buttons {
margin-bottom: 20px;
text-align: center;
}
.ical-buttons .btn {
margin: 0 5px;
}
.user-selection {
margin-bottom: 20px;
text-align: center;
}
/* Optional: simple icons via emojis for the iCal buttons */
.btn-ics-download::before {
content: "1F4BE "; /* Floppy Disk Emoji as a simple icon */
}
.btn-ics-subscribe::before {
content: "1F517 "; /* Link Emoji */
}
.btn-ics-import::before {
content: "1F4E5 "; /* Inbox Tray Emoji */
}
/* Ensure option texts are visible (for dark themes, etc.) */
.modal .form-group select option {
color: #000;
background-color: #fff;
}
</style>
@endpush
@section('content')
<div class="container mx-auto p-4">
<!-- User Selection Dropdown -->
<div class="user-selection">
<label for="userSelect">Select User:</label>
<select id="userSelect" class="form-control d-inline-block" style="width: 200px;">
<option value="">All Users</option>
@foreach($users as $user)
<option value="{{ $user->id }}">{{ $user->name }}</option>
@endforeach
</select>
</div>
<!-- FullCalendar -->
<div id="calendar"></div>
</div>
<!-- Bootstrap Modal for Event Creation/Edit -->
<div class="modal fade" id="eventModal" tabindex="-1" aria-labelledby="eventModalLabel" aria-hidden="true">
<div class="modal-dialog">
<form id="eventForm">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="eventModalLabel">Create/Edit Event</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<input type="hidden" id="eventId" name="eventId">
<div class="form-group">
<label for="eventTitle">Title <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="eventTitle" name="title" required>
</div>
<div class="form-group">
<label for="eventDescription">Description</label>
<textarea class="form-control" id="eventDescription" name="description"></textarea>
</div>
<div class="form-group">
<label for="eventStart">Start <span class="text-danger">*</span></label>
<input type="datetime-local" class="form-control" id="eventStart" name="start" required>
</div>
<div class="form-group" id="timeFields">
<label for="eventEnd">End</label>
<input type="datetime-local" class="form-control" id="eventEnd" name="end">
</div>
<div class="form-group">
<label for="eventCalendar">Calendar <span class="text-danger">*</span></label>
<select class="form-control" id="eventCalendar" name="calendarId" required>
<option value="">-- Select Calendar --</option>
@foreach($calendars as $calendar)
<option value="{{ $calendar->id }}" data-color="{{ $calendar->color }}">
{{ $calendar->name }} @if($calendar->is_global) (Global) @endif
</option>
@endforeach
</select>
</div>
<div class="form-group">
<label for="eventCategory">Category <span class="text-danger">*</span></label>
<select class="form-control" id="eventCategory" name="category" required>
<option value="">-- Select Category --</option>
<option value="milestone">Milestone</option>
<option value="task">Task</option>
<option value="time">Time</option>
<option value="allday">All Day</option>
<option value="holiday">Holiday</option>
</select>
</div>
<div class="form-group" id="taskField" style="display: none;">
<label for="eventTask">Task</label>
<select class="form-control" id="eventTask" name="taskId">
<option value="">-- Select Task --</option>
@foreach($tasks as $task)
<option value="{{ $task->id }}">{{ $task->name }}</option>
@endforeach
</select>
</div>
<div class="form-group">
<label for="eventColor">Color</label>
<input type="color" class="form-control" id="eventColor" name="color" value="#3788d8">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" id="deleteEventButton" style="display: none;">Delete</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Save Event</button>
</div>
</div>
</form>
</div>
</div>
@endsection
@push('scripts')
<!-- jQuery (Must be loaded before Bootstrap JS) -->
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<!-- FullCalendar -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/index.global.min.js"></script>
<!-- Axios -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<!-- Moment.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
var calendarEl = document.getElementById('calendar');
// Initialize FullCalendar
var calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'dayGridMonth',
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay,listDay'
},
selectable: true,
editable: true,
eventStartEditable: true,
eventDurationEditable: true,
// Use a function to fetch events with the correct parameters
events: function(fetchInfo, successCallback, failureCallback) {
axios.get('{{ route("calendar.events.fetch") }}', {
params: {
userId: $('#userSelect').val(),
start: fetchInfo.startStr,
end: fetchInfo.endStr
}
})
.then(function(response) {
successCallback(response.data);
})
.catch(function(error) {
console.error('Error fetching events:', error);
failureCallback(error);
});
},
eventDidMount: function(info) {
var color = info.event.backgroundColor || '#3788d8'; // Default color if not set
info.el.style.backgroundColor = color;
info.el.style.borderColor = color;
},
select: function (info) {
openEventModal({
start: info.startStr,
end: info.endStr,
allDay: info.allDay
}, false); // create mode
},
eventClick: function (info) {
openEventModal(info.event, true); // edit mode
},
eventDrop: function (info) {
updateEventDate(info.event);
},
eventResize: function (info) {
updateEventDate(info.event);
},
});
calendar.render();
function openEventModal(eventData, isEdit) {
if (isEdit) {
$('#eventModalLabel').text('Edit Event');
$('#eventId').val(eventData.id);
$('#eventTitle').val(eventData.title);
$('#eventDescription').val(eventData.extendedProps.description || '');
$('#eventStart').val(moment(eventData.start).format('YYYY-MM-DDTHH:mm'));
$('#eventEnd').val(eventData.end ? moment(eventData.end).format('YYYY-MM-DDTHH:mm') : '');
$('#eventCalendar').val(eventData.extendedProps.calendar_id || '');
$('#eventCategory').val(eventData.extendedProps.category || '');
$('#eventTask').val(eventData.extendedProps.task_id || '');
toggleTaskField(eventData.extendedProps.category || '');
toggleTimeFields(eventData.extendedProps.category || '');
setColorBasedOnCalendar(eventData.extendedProps.calendar_id || '');
$('#deleteEventButton').show();
} else {
// create
$('#eventModalLabel').text('Create Event');
$('#eventId').val('');
$('#eventTitle').val('');
$('#eventDescription').val('');
$('#eventStart').val(moment(eventData.start).format('YYYY-MM-DDTHH:mm'));
$('#eventEnd').val(moment(eventData.end).format('YYYY-MM-DDTHH:mm'));
$('#eventCalendar').val('');
$('#eventCategory').val('');
$('#eventTask').val('');
toggleTaskField('');
toggleTimeFields('');
$('#eventColor').val('#3788d8');
$('#deleteEventButton').hide();
}
$('#eventModal').modal('show');
}
function toggleTaskField(category) {
if (category === 'task') {
$('#taskField').show();
} else {
$('#taskField').hide();
$('#eventTask').val('');
}
}
function toggleTimeFields(category) {
if (category === 'allday') {
$('#timeFields').hide();
// Remove 'required' attribute from end time
$('#eventEnd').removeAttr('required');
// Optionally, set end time to start time or leave it null
$('#eventEnd').val('');
} else {
$('#timeFields').show();
}
}
function setColorBasedOnCalendar(calendarId) {
if (!calendarId) {
$('#eventColor').val('#3788d8'); // default color
return;
}
var selectedOption = $('#eventCalendar option[value="' + calendarId + '"]');
var color = selectedOption.data('color') || '#3788d8';
$('#eventColor').val(color);
}
// When a calendar is selected, set the color automatically
$('#eventCalendar').on('change', function() {
var calendarId = $(this).val();
setColorBasedOnCalendar(calendarId);
});
// When the category changes, toggle task field and time fields
$('#eventCategory').on('change', function() {
var category = $(this).val();
toggleTaskField(category);
toggleTimeFields(category);
});
// Create/Update
$('#eventForm').on('submit', function (e) {
e.preventDefault();
var eventId = $('#eventId').val();
var title = $('#eventTitle').val();
var description = $('#eventDescription').val();
var start = $('#eventStart').val();
var end = $('#eventEnd').val();
var color = $('#eventColor').val();
var calendarId = $('#eventCalendar').val();
var category = $('#eventCategory').val();
var taskId = $('#eventTask').val();
if (!title || !start || !calendarId || !category) {
alert('Please fill all required fields');
return;
}
var payload = {
title: title,
description: description,
start: start,
end: end,
color: color,
calendarId: calendarId,
category: category,
};
// Only include taskId if category is 'task'
if (category === 'task') {
payload.taskId = taskId;
} else {
payload.taskId = null;
}
if (eventId) {
// update
axios.put('/calendar/events/' + eventId, payload)
.then(response => {
$('#eventModal').modal('hide');
calendar.refetchEvents();
if (response.data.conflict) {
alert('Warning: Overlapping event.');
} else {
alert(response.data.message);
}
})
.catch(error => {
handleError(error, 'Update error');
});
} else {
// store
axios.post('/calendar/events', payload)
.then(response => {
$('#eventModal').modal('hide');
calendar.refetchEvents();
if (response.data.conflict) {
alert('Warning: Overlapping event.');
} else {
alert(response.data.message);
}
})
.catch(error => {
handleError(error, 'Create error');
});
}
});
// Drag/Resize => update start/end
function updateEventDate(fcEvent) {
var category = fcEvent.extendedProps.category;
var calendarId = fcEvent.extendedProps.calendar_id;
var taskId = fcEvent.extendedProps.task_id;
var payload = {
start: fcEvent.start.toISOString(),
end: fcEvent.end ? fcEvent.end.toISOString() : fcEvent.start.toISOString(),
category: category,
calendarId: calendarId,
};
if (category === 'task') {
payload.taskId = taskId;
}
axios.put('/calendar/events/' + fcEvent.id, payload)
.then(response => {
if (response.data.conflict) {
alert('Warning: Overlapping event.');
}
alert(response.data.message);
})
.catch(error => {
fcEvent.revert();
handleError(error, 'Drag/Resize error');
});
}
// Delete
$('#deleteEventButton').on('click', function () {
var eventId = $('#eventId').val();
if (!eventId) return;
if (confirm('Are you sure you want to delete this event?')) {
axios.delete('/calendar/events/' + eventId)
.then(response => {
$('#eventModal').modal('hide');
calendar.refetchEvents();
alert(response.data.message);
})
.catch(error => {
handleError(error, 'Delete error');
});
}
});
function handleError(error, defaultMsg) {
console.error(error);
if (error.response) {
if (error.response.status === 401) {
alert('Unauthorized. Please log in.');
} else if (error.response.status === 403) {
alert('Forbidden. No permission.');
} else if (error.response.data && error.response.data.errors) {
// Laravel validation error messages
alert(Object.values(error.response.data.errors).flat().join('n'));
} else if (error.response.data && error.response.data.message) {
alert(error.response.data.message);
} else {
alert(defaultMsg);
}
} else {
alert(defaultMsg);
}
}
// Re-fetch events when user selection changes
$('#userSelect').on('change', function() {
calendar.refetchEvents();
});
// Ensure CSRF token is included in Axios headers
axios.defaults.headers.common['X-CSRF-TOKEN'] =
document.querySelector('meta[name="csrf-token"]').getAttribute('content');
});
</script>
@endpush
everything is done on that page, its our work calendar that actually works really well, just need to get the popup back working.
I have tried to re-do this soo many times 🙁 and I have lost all mental strength
<!-- Bootstrap Modal for Event Creation/Edit -->
<div class="modal fade" id="eventModal" tabindex="-1" aria-labelledby="eventModalLabel" aria-hidden="true">
<div class="modal-dialog">
<form id="eventForm">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="eventModalLabel">Create/Edit Event</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<input type="hidden" id="eventId" name="eventId">
<div class="form-group">
<label for="eventTitle">Title <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="eventTitle" name="title" required>
</div>
<div class="form-group">
<label for="eventDescription">Description</label>
<textarea class="form-control" id="eventDescription" name="description"></textarea>
</div>
<div class="form-group">
<label for="eventStart">Start <span class="text-danger">*</span></label>
<input type="datetime-local" class="form-control" id="eventStart" name="start" required>
</div>
<div class="form-group" id="timeFields">
<label for="eventEnd">End</label>
<input type="datetime-local" class="form-control" id="eventEnd" name="end">
</div>
<div class="form-group">
<label for="eventCalendar">Calendar <span class="text-danger">*</span></label>
<select class="form-control" id="eventCalendar" name="calendarId" required>
<option value="">-- Select Calendar --</option>
@foreach($calendars as $calendar)
<option value="{{ $calendar->id }}" data-color="{{ $calendar->color }}">
{{ $calendar->name }} @if($calendar->is_global) (Global) @endif
</option>
@endforeach
</select>
</div>
<div class="form-group">
<label for="eventCategory">Category <span class="text-danger">*</span></label>
<select class="form-control" id="eventCategory" name="category" required>
<option value="">-- Select Category --</option>
<option value="milestone">Milestone</option>
<option value="task">Task</option>
<option value="time">Time</option>
<option value="allday">All Day</option>
<option value="holiday">Holiday</option>
</select>
</div>
<div class="form-group" id="taskField" style="display: none;">
<label for="eventTask">Task</label>
<select class="form-control" id="eventTask" name="taskId">
<option value="">-- Select Task --</option>
@foreach($tasks as $task)
<option value="{{ $task->id }}">{{ $task->name }}</option>
@endforeach
</select>
</div>
<div class="form-group">
<label for="eventColor">Color</label>
<input type="color" class="form-control" id="eventColor" name="color" value="#3788d8">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" id="deleteEventButton" style="display: none;">Delete</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Save Event</button>
</div>
</div>
</form>
</div>
</div>
any help would be amazing. or a point in the right direction.
thanks
Mick
I tried to adjust this to work with alpine and tailwind, but I end up breaking everything.