FullCalendar.js

Powerful Calendar Management for Modern Web Applications

FullCalendar is a full-sized, drag & drop JavaScript event calendar for modern browsers. It's designed to be a highly customizable solution for displaying and managing events, appointments, and schedules in web applications.

In SmartAdmin, FullCalendar is integrated with a custom Bootstrap theme (fullcalendar.bootstrap.theme.js) that ensures visual consistency with the rest of the application.

Pro Tip: SmartAdmin's FullCalendar integration uses the Smart Admin icon system and Bootstrap styling for seamless visual integration with your dashboard.

Installation

FullCalendar needs to be added to your project. Here's how to set it up:

1. Including Required Files

// Add the scripts as modules in your HTML
<link href="./pathTo/fullcalendar.min.css" rel="stylesheet">
<script type="module" src="./pathTo/fullcalendar.bundle.js"></script>
<script type="module" src="./pathTo/fullcalendar.bootstrap.theme.js"></script>

// Or import them in your JavaScript module
import { FullCalendar } from './pathTo/fullcalendar.bundle.js';
import './pathTo/fullcalendar.bootstrap.theme.js';
// Make sure to also include the CSS in your HTML or via a CSS loader

2. Initialize a Calendar

// After importing the modules as shown above
document.addEventListener('DOMContentLoaded', function() {
    const calendarEl = document.getElementById('calendar');
    
    const calendar = new FullCalendar.Calendar(calendarEl, {
        themeSystem: 'bootstrap', // Use the SmartAdmin Bootstrap theme
        initialView: 'dayGridMonth',
        headerToolbar: {
            left: 'prev,next today',
            center: 'title',
            right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek'
        },
        events: [
            {
                title: 'Meeting',
                start: '2023-01-10T14:30:00',
                end: '2023-01-10T16:30:00'
            },
            {
                title: 'Conference',
                start: '2023-01-11',
                end: '2023-01-13'
            }
        ]
    });
    
    calendar.render();
});

HTML Structure

<!-- Basic Calendar Container -->
<div id="calendar"></div>

<!-- Panel-Based Calendar Container (Recommended) -->
<div id="panel-1" class="panel">
    <div class="panel-hdr">
        <h2>Calendar <span class="fw-300"><i>View</i></span></h2>
        <div class="panel-toolbar">
            <button class="btn btn-panel" data-action="panel-collapse">
                <!-- Panel controls -->
            </button>
        </div>
    </div>
    <div class="panel-container show">
        <div class="panel-content">
            <div id="calendar"></div>
        </div>
    </div>
</div>
Important:
  • You need to include the FullCalendar files in your project - they are not automatically loaded.
  • FullCalendar requires a container with a defined height. Without it, the calendar won't be visible.
  • Make sure to include both the JavaScript modules and the CSS file.
  • For more advanced documentation, refer to the official FullCalendar documentation.

SmartAdmin Bootstrap Theme

SmartAdmin includes a custom Bootstrap theme for FullCalendar that ensures visual consistency with the rest of the application. The theme is defined in fullcalendar.bootstrap.theme.js.

// Excerpt from the bootstrap theme definition
class BootstrapTheme extends Theme {
    // CSS classes for various elements
    classes = {
        root: 'fc-theme-bootstrap',
        table: 'table table-calendar', // Uses Bootstrap table styling
        tableCellShaded: 'table-active',
        buttonGroup: 'btn-group',
        button: 'btn btn-outline-default btn-sm',
        buttonActive: 'active',
        popover: 'popover',
        popoverHeader: 'popover-header',
        popoverContent: 'popover-body',
    };
    
    // Icon definitions using SmartAdmin icon system
    baseIconClass = 'sa';
    iconClasses = {
        close: 'sa-close',
        prev: 'sa-chevron-left',
        next: 'sa-chevron-right',
        prevYear: 'sa-angle-double-left',
        nextYear: 'sa-angle-double-right',
    };
}
Pro Tip: The SmartAdmin theme uses our custom icon system with the sa- prefix. This ensures all calendar controls match the rest of your application's design.

Configuration Options

FullCalendar offers extensive configuration options. Here are some of the most common ones:

Option Type Description
initialView String The initial view when the calendar loads. Options: 'dayGridMonth', 'timeGridWeek', 'timeGridDay', 'listWeek'
headerToolbar Object Configuration for the calendar's header, with sections for left, center, and right
events Array/Function/String The events to display on the calendar. Can be an array of event objects, a function, or a URL
editable Boolean Whether the events can be dragged and resized
selectable Boolean Whether dates can be selected by clicking and dragging
businessHours Object/Boolean Emphasizes certain time slots on the calendar
nowIndicator Boolean Whether to show an indicator for the current time
weekNumbers Boolean Whether to show week numbers
height Number/String/Function Sets the height of the calendar. Can be 'auto', a pixel value, or 'parent'
themeSystem String The theme system to use. In SmartAdmin, set this to 'bootstrap'
Pro Tip: When using FullCalendar with SmartAdmin, always set themeSystem: 'bootstrap' to use the custom Bootstrap theme integration.

Event Handling

FullCalendar provides extensive event handling capabilities. Here's how to define and manage events:

Event Object Properties

// Basic event object structure
{
    id: '123',                      // Unique identifier
    title: 'Meeting with Client',   // Event title
    start: '2023-01-10T10:30:00',   // Start date/time (ISO8601 format)
    end: '2023-01-10T12:30:00',     // End date/time (optional)
    allDay: false,                  // Whether it's an all-day event
    url: 'https://example.com',     // Makes the event clickable as a link (optional)
    className: 'bg-success',        // CSS class names for styling (optional)
    editable: true,                 // Whether this specific event is editable (optional)
    extendedProps: {                // Additional custom data (optional)
        department: 'Marketing',
        location: 'Conference Room A'
    }
}

Event Sources

// Array of event objects
events: [
    { title: 'Event 1', start: '2023-01-01' },
    { title: 'Event 2', start: '2023-01-05', end: '2023-01-07' }
]

// JSON feed (AJAX)
events: '/api/events'

// Function that fetches events
events: function(info, successCallback, failureCallback) {
    console.log('Fetching events for', info.startStr, 'to', info.endStr);
    
    fetch('/api/events?start=' + info.startStr + '&end=' + info.endStr)
        .then(response => {
            if (!response.ok) {
                throw new Error('Network response was not ok');
            }
            return response.json();
        })
        .then(data => {
            console.log('Loaded events:', data);
            successCallback(data);
        })
        .catch(error => {
            console.error('Error loading events:', error);
            failureCallback(error);
        });
}

// Multiple event sources with different colors
eventSources: [
    {
        url: '/api/events/team-a',
        color: 'var(--success-500)',   // All events from this source will be this color
        textColor: 'white'             // Text color for this source
    },
    {
        url: '/api/events/team-b',
        color: 'var(--info-500)', 
        textColor: 'white'
    }
]

Event Callbacks

const calendar = new Calendar(calendarEl, {
    // ...other options
    
    // Triggered when an event is clicked
    eventClick: function(info) {
        alert('Event: ' + info.event.title);
        
        // Change the background color of the event
        info.event.setProp('backgroundColor', 'var(--danger-500)');
        
        // To prevent default action (URL navigation)
        info.jsEvent.preventDefault();
    },
    
    // Triggered when an event is dragged and dropped
    eventDrop: function(info) {
        if (!confirm("Are you sure you want to move this event?")) {
            info.revert(); // Revert the change
        } else {
            // Update the event in your database
            updateEventInDatabase(info.event);
        }
    },
    
    // Triggered when an event is resized
    eventResize: function(info) {
        alert('Event has been resized. New end time: ' + info.event.end);
    },
    
    // Triggered when dates are selected by clicking and dragging
    select: function(info) {
        const title = prompt('Enter a new title for your event:');
        if (title) {
            calendar.addEvent({
                title: title,
                start: info.startStr,
                end: info.endStr,
                allDay: info.allDay
            });
        }
        calendar.unselect(); // Clear selection
    }
});

Advanced Features

API Methods

// Get a reference to the calendar
const calendar = new Calendar(calendarEl, options);
calendar.render();

// Navigate to different dates
calendar.next();     // Move to next month/week/day
calendar.prev();     // Move to previous month/week/day
calendar.today();    // Move to today
calendar.gotoDate('2023-05-01'); // Jump to a specific date

// Change views
calendar.changeView('timeGridWeek');  // Switch to week view
calendar.changeView('dayGridMonth');  // Switch to month view

// Event manipulation
const event = calendar.getEventById('123');
if (event) {
    event.setProp('title', 'Updated Title');   // Update title
    event.setStart('2023-01-12T10:00:00');     // Update start time
    event.setEnd('2023-01-12T12:00:00');       // Update end time
    event.setAllDay(true);                     // Make it an all-day event
    event.setExtendedProp('status', 'confirmed'); // Update custom property
    event.remove();                            // Remove the event
}

// Add a new event
calendar.addEvent({
    title: 'New Event',
    start: '2023-01-15'
});

// Programmatically select dates
calendar.select('2023-01-24', '2023-01-26');

// Refetch events (if using dynamic source)
calendar.refetchEvents();

Localization

// Import the locale
import frLocale from '@fullcalendar/core/locales/fr';

const calendar = new Calendar(calendarEl, {
    // ...other options
    locale: frLocale,   // Use French locale
    
    // Or use multiple locales
    locales: [frLocale, esLocale],
    locale: 'fr'        // Set the active locale
});

Business Hours

// Simple business hours (9am to 5pm, Monday to Friday)
businessHours: true

// Customized business hours
businessHours: {
    daysOfWeek: [1, 2, 3, 4, 5], // Monday to Friday
    startTime: '08:00',           // 8am
    endTime: '18:00'              // 6pm
}

// Multiple business hour ranges
businessHours: [
    {
        daysOfWeek: [1, 2, 3, 4], // Monday to Thursday
        startTime: '08:00',
        endTime: '18:00'
    },
    {
        daysOfWeek: [5],          // Friday
        startTime: '08:00',
        endTime: '14:00'          // 2pm (shorter day)
    }
]

SmartAdmin Integration

For optimal integration with SmartAdmin, consider these tips:

Event Colors

Use SmartAdmin color variables for consistent styling:

// Use SmartAdmin color variables
events: [
    {
        title: 'Critical Task',
        start: '2023-01-05',
        backgroundColor: 'var(--danger-500)',
        borderColor: 'var(--danger-500)'
    },
    {
        title: 'Team Meeting',
        start: '2023-01-07',
        backgroundColor: 'var(--primary-500)',
        borderColor: 'var(--primary-500)'
    },
    {
        title: 'Completed Project',
        start: '2023-01-09',
        backgroundColor: 'var(--success-500)',
        borderColor: 'var(--success-500)'
    }
]

// Or use predefined SmartAdmin classes
events: [
    {
        title: 'Warning Event',
        start: '2023-01-11',
        classNames: ['bg-warning-500', 'text-white']
    },
    {
        title: 'Info Event',
        start: '2023-01-13',
        classNames: ['bg-info-500', 'text-white']
    }
]

Panel Integration

Use SmartAdmin panels for a consistent look:

<div class="row">
    <div class="col-xl-12">
        <div id="panel-1" class="panel">
            <div class="panel-hdr">
                <h2>
                    Company <span class="fw-300"><i>Calendar</i></span>
                </h2>
                <div class="panel-toolbar">
                    <button class="btn btn-panel" data-action="panel-collapse">
                        <svg class="sa-icon">
                            <use class="panel-collapsed-icon" href="img/sprite.svg#minus-circle"></use>
                            <use class="panel-expand-icon" href="img/sprite.svg#plus-circle"></use>
                        </svg>
                    </button>
                    <button class="btn btn-panel" data-action="panel-fullscreen">
                        <svg class="sa-icon">
                            <use href="img/sprite.svg#stop-circle"></use>
                        </svg>
                    </button>
                </div>
            </div>
            <div class="panel-container show">
                <div class="panel-content">
                    <div id="calendar"></div>
                </div>
            </div>
        </div>
    </div>
</div>

Modal Integration for Event Details

// Calendar setup with event click handling for modals
const calendar = new Calendar(calendarEl, {
    // ...other options
    eventClick: function(info) {
        // Prevent default action (URL navigation)
        info.jsEvent.preventDefault();
        
        // Get event details
        const event = info.event;
        const title = event.title;
        const start = event.start ? event.start.toLocaleString() : '';
        const end = event.end ? event.end.toLocaleString() : '';
        const description = event.extendedProps.description || '';
        
        // Populate and show modal with Bootstrap 5
        const modal = new bootstrap.Modal(document.getElementById('event-details-modal'));
        document.getElementById('modal-event-title').textContent = title;
        document.getElementById('modal-event-time').textContent = start + ' - ' + end;
        document.getElementById('modal-event-description').textContent = description;
        
        // Set up edit/delete buttons if needed
        document.getElementById('edit-event-btn').onclick = function() {
            // Open edit form for this event
            openEditForm(event);
            modal.hide();
        };
        
        document.getElementById('delete-event-btn').onclick = function() {
            if (confirm('Are you sure you want to delete this event?')) {
                event.remove();
                // Also delete from backend
                deleteEventFromServer(event.id);
                modal.hide();
            }
        };
        
        modal.show();
    }
});

Troubleshooting

Common Issues:
  • Calendar not displaying? Ensure the container has a defined height.
  • Events not appearing? Check the date range and time format of your events.
  • Styling issues? Make sure themeSystem: 'bootstrap' is set.
  • Icons not showing? Verify the custom theme is properly loaded.

Calendar Height Issues

// Set explicit height
const calendar = new Calendar(calendarEl, {
    // ...other options
    height: 800  // Explicit pixel height
});

// Or use parent container's height
const calendar = new Calendar(calendarEl, {
    // ...other options
    height: 'parent'  // Use parent container's height
});

// Or use CSS
#calendar {
    height: 800px;
}

// For responsive height
#calendar {
    height: calc(100vh - 250px); /* Viewport height minus offset */
}

AJAX Loading Issues

// Debug AJAX event loading
events: function(info, successCallback, failureCallback) {
    console.log('Fetching events for', info.startStr, 'to', info.endStr);
    
    fetch('/api/events?start=' + info.startStr + '&end=' + info.endStr)
        .then(response => {
            if (!response.ok) {
                throw new Error('Network response was not ok');
            }
            return response.json();
        })
        .then(data => {
            console.log('Loaded events:', data);
            successCallback(data);
        })
        .catch(error => {
            console.error('Error loading events:', error);
            failureCallback(error);
        });
}

Further Resources

For more advanced usage and detailed API documentation, refer to these resources: