Demo

Event Manager

This demo showcases an integrated event management system combining the Calendar and Timeline plugins. Schedule events, add milestones, and see your timeline come to life with real-time updates.


live
<html>
<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@lemonadejs/calendar/dist/index.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@lemonadejs/timeline/dist/index.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/calendar/dist/style.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lemonadejs/timeline/dist/style.min.css" />

<div id="root"></div>

<script>
function EventManager() {
    const self = this;

    // Sample events
    const initialEvents = [
        { date: new Date(2025, 0, 15), title: 'Project Kickoff', type: 'meeting', description: 'Initial planning and team assembly' },
        { date: new Date(2025, 1, 1), title: 'Design Review', type: 'review', description: 'UI/UX design presentation' },
        { date: new Date(2025, 1, 15), title: 'Sprint 1 Complete', type: 'milestone', description: 'Core features implemented' },
        { date: new Date(2025, 2, 1), title: 'QA Testing', type: 'testing', description: 'Comprehensive testing phase' },
        { date: new Date(2025, 2, 15), title: 'Launch Date', type: 'milestone', description: 'Product goes live!' }
    ];

    self.events = initialEvents;
    self.selectedDate = new Date().toISOString().split('T')[0];
    self.newEventTitle = '';
    self.newEventType = 'event';
    self.newEventDescription = '';

    // Stats as reactive properties
    self.totalEvents = initialEvents.length;
    self.milestonesCount = initialEvents.filter(e => e.type === 'milestone').length;
    self.meetingsCount = initialEvents.filter(e => e.type === 'meeting').length;

    // Reactive properties for calendar and timeline
    self.calendarEvents = initialEvents.map(e => ({
        date: e.date.toISOString().split('T')[0],
        title: e.title,
        color: e.type === 'milestone' ? '#e74c3c' : e.type === 'meeting' ? '#3498db' : e.type === 'review' ? '#f39c12' : '#2ecc71'
    }));

    // Timeline with date, title, subtitle (type) and description
    self.timelineData = [...initialEvents].sort((a, b) => a.date - b.date).map(e => ({
        date: e.date,
        title: e.title,
        subtitle: e.type.charAt(0).toUpperCase() + e.type.slice(1),
        description: e.description
    }));

    self.onDateChange = function(el, date) {
        if (date && date.length === 10 && self.selectedDate !== date) {
            self.selectedDate = date;
        }
    };

    self.updateViews = function() {
        // Update stats
        self.totalEvents = self.events.length;
        self.milestonesCount = self.events.filter(e => e.type === 'milestone').length;
        self.meetingsCount = self.events.filter(e => e.type === 'meeting').length;

        // Update calendar events
        self.calendarEvents = self.events.map(e => ({
            date: e.date.toISOString().split('T')[0],
            title: e.title,
            color: e.type === 'milestone' ? '#e74c3c' : e.type === 'meeting' ? '#3498db' : e.type === 'review' ? '#f39c12' : '#2ecc71'
        }));

        // Update timeline data
        self.timelineData = [...self.events].sort((a, b) => a.date - b.date).map(e => ({
            date: e.date,
            title: e.title,
            subtitle: e.type.charAt(0).toUpperCase() + e.type.slice(1),
            description: e.description
        }));
    };

    self.addEvent = function() {
        if (self.newEventTitle && self.selectedDate) {
            // Parse date correctly to avoid timezone issues
            const [year, month, day] = self.selectedDate.split('-').map(Number);
            self.events = [...self.events, {
                date: new Date(year, month - 1, day),
                title: self.newEventTitle,
                type: self.newEventType,
                description: self.newEventDescription || 'No description'
            }];
            self.newEventTitle = '';
            self.newEventType = 'event';
            self.newEventDescription = '';
            self.updateViews();
        }
    };

    return `<div style="padding: 20px;">
        <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
            <h2 style="margin: 0;">Event Manager</h2>
            <div style="display: flex; gap: 30px;">
                <div style="text-align: center;">
                    <div style="font-size: 24px; font-weight: 700; color: #3498db;">{{self.totalEvents}}</div>
                    <div style="font-size: 12px; color: #64748b;">Total Events</div>
                </div>
                <div style="text-align: center;">
                    <div style="font-size: 24px; font-weight: 700; color: #e74c3c;">{{self.milestonesCount}}</div>
                    <div style="font-size: 12px; color: #64748b;">Milestones</div>
                </div>
                <div style="text-align: center;">
                    <div style="font-size: 24px; font-weight: 700; color: #2ecc71;">{{self.meetingsCount}}</div>
                    <div style="font-size: 12px; color: #64748b;">Meetings</div>
                </div>
            </div>
        </div>

        <div style="display: flex; gap: 20px;">
            <div style="flex: 1; display: flex; flex-direction: column;">
                <h3 style="margin: 0 0 16px 0; font-size: 18px;">Calendar View</h3>
                <div style="background: white; padding: 20px; border-radius: 10px; border: 1px solid #e2e8f0;">
                    <Calendar :events="self.calendarEvents" :onchange="self.onDateChange" type="inline" footer="false" />
                </div>
            </div>

            <div style="flex: 1; display: flex; flex-direction: column;">
                <h3 style="margin: 0 0 16px 0; font-size: 18px;">Add New Event</h3>
                <div style="background: white; padding: 20px; border-radius: 10px; border: 1px solid #e2e8f0;">
                    <div style="margin-bottom: 12px;">
                        <label style="display: block; margin-bottom: 6px; font-size: 14px; font-weight: 500;">Selected Date:</label>
                        <input type="date" :value="self.selectedDate" oninput="self.selectedDate = this.value" style="width: 100%; padding: 8px; border: 1px solid #cbd5e1; border-radius: 6px;" />
                    </div>
                    <div style="margin-bottom: 12px;">
                        <label style="display: block; margin-bottom: 6px; font-size: 14px; font-weight: 500;">Event Title:</label>
                        <input type="text" :value="self.newEventTitle" oninput="self.newEventTitle = this.value" placeholder="Enter event name..." style="width: 100%; padding: 8px; border: 1px solid #cbd5e1; border-radius: 6px;" />
                    </div>
                    <div style="margin-bottom: 12px;">
                        <label style="display: block; margin-bottom: 6px; font-size: 14px; font-weight: 500;">Event Type:</label>
                        <select :value="self.newEventType" onchange="self.newEventType = this.value" style="width: 100%; padding: 8px; border: 1px solid #cbd5e1; border-radius: 6px;">
                            <option value="event">Event</option>
                            <option value="meeting">Meeting</option>
                            <option value="review">Review</option>
                            <option value="milestone">Milestone</option>
                            <option value="testing">Testing</option>
                        </select>
                    </div>
                    <div style="margin-bottom: 12px;">
                        <label style="display: block; margin-bottom: 6px; font-size: 14px; font-weight: 500;">Description:</label>
                        <textarea :value="self.newEventDescription" oninput="self.newEventDescription = this.value" placeholder="Enter description..." style="width: 100%; padding: 8px; border: 1px solid #cbd5e1; border-radius: 6px; resize: none; height: 80px;"></textarea>
                    </div>
                    <button onclick="self.addEvent()" class="button primary" style="width: 100%;">Add Event</button>
                </div>
            </div>

            <div style="flex: 1; display: flex; flex-direction: column;">
                <h3 style="margin: 0 0 16px 0; font-size: 18px;">Timeline View</h3>
                <div style="background: white; padding: 20px; border-radius: 10px; border: 1px solid #e2e8f0; max-height: 450px; overflow-y: auto;">
                    <Timeline :data="self.timelineData" align="left" order="desc" />
                </div>
            </div>
        </div>
    </div>`;
}

lemonade.render(EventManager, document.getElementById('root'));
</script>
</html>