The Lucid Meetings Visual Event Calendar

January 18, 2016 at 3:37 AM by John Keith in meeting technology, release announcement

My ice storm-induced, "bonus" project during the 2015 end-of-year holiday break was focused on improvements to the Lucid Meetings visual event calendar. While we've had our visual calendar display for a couple years now, I always intended the calendar to be more fully interactive for people who prefer calendar-style interactions with our meeting platform. With a little time on my hands, it was finally time to tackle this project!

Calendar Basics

The Lucid Meetings visual event calendar includes a variety of meeting artifacts that support the process around well-run meetings: meetings in the process of being scheduled, meetings already scheduled, meetings created, but not yet setup, open action items from prior meetings, and more.

Week view of the calendar with action items and meetings

Alternate Calendar Views

The Lucid calendar technology is based on the awesome FullCalendar jQuery plugin, which supports user-selectable month, week, and day views of calendar events, along with functional extensibility to add viewer and management interactions.

Month view of calendar

User-Selectable Time Zone Display

All Lucid Meetings time and date displays are shown in the viewer's preferred time zone, and the calendar adheres to this fully. Viewers can change their time zone selection while viewing the calendar.

Timezone selection

Calendar Operations

From a functional perspective, the calendar supports a full range of operations - those little interactions that make it fun and interesting to use the calendar on a regular basis. All operations are available on the calendar itself via click, click + modifier, click + drag, right click, and drag-n-drop.

General Display Functions

  • Display scheduled meetings
  • Display canceled meetings
  • Display dates and times reserved for meetings to-be-scheduled
  • Display open action items with due dates

Meeting Interactions

  • Show quick view of meeting information via popup (event click)
  • Create a new meeting (day click)
  • Create a copy of an existing meeting (alt + click)
  • Delete an existing meeting (right click)
  • Reschedule a meeting to another day or time via drag-n-drop (click + drag)
  • Lengthen or shorten the allocated time for a meeting (click + drag end time)
  • Access to view or edit the full meeting information (shift + click)

Action Item Interactions

  • Show a quick view of action item information via popup (event click)
  • Reschedule an action item for another day (click + drag)
  • Mark an action item as "complete" (right click)
  • Access to view or edit the full action item information (shift + click)

Interaction Examples

The Lucid Meetings interaction model is designed to streamline as many interactions as possible, but to confirm all interactions that require confirmation. The calendar holds to both aspects of this model.

Quick View of a Meeting or Action Item

To view a bit more information about any meeting or action item on the calendar, simply click on the associated calendar event. The software will pop up a quick view of the relevant information, along with options to view the full meeting or action item itself.

Quick view of a meeting on the calendar

And for an action item you'll see a bit more of the action item description, the assignee, and a link with button to view the full action item.

Quick view of an action item on the calendar

Pro tip: to go directly to a meeting or action item without the intermediate dialog, press the shift key while clicking on an event.

Create a New MEeting

To add a new meeting to a room calendar, simply click on the room calendar in any of the month, week, or day views. The software will confirm the request and provide you with an opportunity to immediately configure the full meeting agenda.

Meeting request confirmation

After creating the calendar event managers can elect to configure the meeting or remain on the calendar view.

Meeting created confirmation

Copy an Existing Meeting

To make a copy of an existing meeting we use a key modifier (the "alt" key) when clicking on the calendar event. As with creating a new meeting, we confirm the action before actually adding a new meeting to the calendar. The new meeting is automatically scheduled for the same day of week, and the same time of day as the original meeting.

Copy and existing meeting

As with creating a new meeting from scratch, when a meeting copy is created, the software prompts you to continue the process of updating the agenda and notifiying attendees.

Meeting copy confirmed

Reschedule an Existing Meeting

To reschedule a meeting on the calendar, simply click on the meeting and drag the calendar event to another day and/or time. When looking at the month view, dragging the event to a new day preserves the original meeting time. When looking at the week view, it is possible to drag the event to both a new day and time simultaneously.

Reschedule meeting

If you have previously notified attendees about this meeting, then you will be prompted to re-send calendar invitations with the update date and time for the meeting.

Meeting rescheduled confirmation

Delete an Existing Meeting

To delete a meeting from the calendar, right click (or ctrl + click) on the calendar event for the meeting. You will be prompted to confirm the (non-reversible) delete action.

Confirm meeting delete

REschedule an Action Item

To reschedule an action item, simply click on the action item and drag it to a new day. This is an unconfirmed, but acknowledged action. Lucid Meetings does not send email notifications for action item updates, so no further action is required when an action item is rescheduled.

Reschedule action item

Mark an Action Item as Complete

Besides deferring an action item for later (yes, we all do that!), the primary operation for an action item task is to mark it as complete. Occasionally we may want to assign a task to another team member, but usually we are simply saying "it's done!" The Lucid Meetings visual calendar supports this common operation when you right click on an action item.

Mark action item as complete

You Should Try It


 

If you're not already a Lucid Meetings user, you should give it a try. Seriously - the 100% free, personal version includes the entire working calendar, and we'd love to hear your feedback. If you've read this far then I figure you have some interest in seeing it all in action, so go for it :)

Try a sample meeting Start a free trial, no credit card required

If you're interested in building your own calendar, fire up your favorite browser and javascript console to watch the API interactions and data. And look through the technical resources in the final section below for samples and tips.

Technical Resources

We've been avid users of the Full Calendar jQuery plugin for several years now and it's been great to watch the progress and in-place refactoring as the software has evolved. We're happy to give something back by sharing some of our technical work to create a great calendar application.

Here's a rundown of the software we're using in our calendar module, along with some sample code to help you get started if you'd like to roll your own.

Software Components

  • FullCalendar by Adam Shaw is a drag-n-drop jQuery plugin for displaying events on a full-sized calendar. It is customizable, open source, and very well documented. We are using the most recent release, v2.6.0, as of the time of this blog post.
  • For rightClick handling, we're using a small javascript addition, fullcalendar-rightclick, by Michael Hermann. We have a small, outstanding pull request to help update the fullcalendar-rightclick to work with the latest versions of FullCalendar. If you run into issues with the rightClick handling, drop me a line in the comments.
  • For the user interactions we're using Sweet Alert, a beautiful replacement for Javascript's "alert()" function. This keeps the user interactions fast, light, and simple.
  • Here's a link to some sample code for the room calendar. The code contains some artifacts related to our implementation framework, but should be fairly discernable if you're looking for examples or get stuck.

Sample Code for a Meeting Event

If you're looking through our sample Javascript code, you'll see references to event properties that not defined by the FullCalendar documentation. That's okay though, because FullCalendar expects you to augment the basic event object with your custom data. Notably, you'll see references to the editable, copyable, and removable properties. Editable is actually a FullCalendar-defined property that we turn off globally and only selectively enable; the others are specific to the permissions and data flow within Lucid Meetings.

        /* List of events */
        $list = array();

        /* Create the meeting event */
        $data = array();
        $data['meeting_id'] = $meeting_id;
        $data['room_id']    = $meeting->get('room_id');
        $data['title']      = $meeting->get('display_name');
        $data['goal']       = $meeting->get('description');
        $data['start']      = date('c', $meeting->get('start_time'));
        $data['end']        = date('c', $meeting->get('end_time'));
        $data['url']        = '/meeting/' . $meeting_id;

        /* Allow dnd rescheduling if the user can manage the meeting */
        if (lucid_admin_can_manage_meeting($meeting_id)) {
            $data['copyable'] = 'true';
            $data['removable'] = 'true';
            if ($meeting->isPending()) {
                $data['editable'] = 'true';
            }
        }
        
        $list[] = $data;
        return json_encode($list);

Sample Code for an Action Item Event

We represent action items on the calendar as All Day events, but we also add a specific action item class (fc-action-item) to the event so we can distinguish between actual All Day events and action items. Lucid Meetings is geared toward open collaboration, so there are no fine grained permissions associated with action items. If you can see it at all, you can "edit" it (i.e., reschedule or mark complete).

        /* List of events */
        $list = array();
        
        /* Create the action item event */
        $data = array();
        $data['note_id']   = $todo->get('note_id');
        $data['title']     = t('Due: !text', array('!text' => $title));
        $data['text']      = t('Assigned to: @name', array('@name' => $name));
        $data['start']     = date('c', $todo->get('due_ts'));
        $data['end']       = date('c', $todo->get('due_ts'));
        $data['url']       = '/todo/' . $todo->get('note_id');
        $data['allDay']    = 'true';
        $data['className'] = 'fc-action-item';
        $data['editable']  = 'true';
        
        $list[] = $data;
        return json_encode($list);

Callbacks for Model Access and Updates

If you're doing your own implementation of a fully interactive calendar, you'll need a variety of callbacks to handle events triggered by user interactions.

A full description of the Lucid Meetings server-side programming framework is way beyond the scope of this blog post, but to help steer the conversation, here's a quick list of callbacks you might want to think about.

At the UI level, the callbacks are all executed with the authenticated user context for finer grained access permissions, so there's a lot of implied control happening behind the callbacks.

  • Retrieve a list of events (meetings, actions items, etc.) within a date range
    /room/[:room_id]/ajax/events (via GET[:start], GET[:end])
  • Retrieve a single event (handy for reverting a change upon cancel)
    /dashboard/ajax/meeting/[:meeting_id]/fetch
  • Create a meeting event on room calendar
    /room/[:room_id]/ajax/meeting_create/[:date_time]
  • Copy a meeting event
    /dashboard/ajax/meeting/[:meeting_id]/copy
  • Move a meeting event to a new date/time
    /dashboard/ajax/meeting/[:meeting_id]/move/[:days]/[:hours]/[:minutes]
  • Resize a meeting event (change meeting duration)
    /dashboard/ajax/meeting/[:meeting_id]/resize/[:days]/[:hours]/[:minutes]
  • Delete a meeting event
    /dashboard/ajax/meeting/[:meeting_id]/delete
  • Move an action item event to a new date
    /dashboard/ajax/todo/[:todo_id]/move/[:days]
  • Close an action item event
    /dashboard/ajax/todo/[:todo_id]/close

Sample JSON Output For Calendar Events

Sometimes it helps to have sample data to look through, so here you go. This is the JSON output that drives the calendar month view, above. You'll see that there are more meeting and action item events in the JSON than appear on the calendar. This is partly due to the way we fetch action items and partly because FullCalendar asks for events from slightly before the start of the month to slightly after the end of the month. The extra events are present in the data, but not shown unless necessary.

GET /room/60/ajax/events&start=2015-12-27&end=2016-02-07

[
  {
    "meeting_id": 1397,
    "room_id": 60,
    "title": "Staff Meeting",
    "goal": "",
    "start": "2016-02-04T16:00:00-08:00",
    "end": "2016-02-04T16:55:00-08:00",
    "url": "\/meeting\/1397",
    "copyable": "true",
    "removable": "true",
    "editable": "true"
  },
  {
    "meeting_id": 1396,
    "room_id": 60,
    "title": "New event",
    "goal": "",
    "start": "2016-02-04T15:00:00-08:00",
    "end": "2016-02-04T15:55:00-08:00",
    "url": "\/meeting\/1396",
    "copyable": "true",
    "removable": "true",
    "editable": "true",
    "className": "fc-new-event"
  },
  {
    "meeting_id": 1395,
    "room_id": 60,
    "title": "Staff Meeting",
    "goal": "",
    "start": "2016-01-27T16:00:00-08:00",
    "end": "2016-01-27T16:55:00-08:00",
    "url": "\/meeting\/1395",
    "copyable": "true",
    "removable": "true",
    "editable": "true",
    "confirmReschedule": "true"
  },
  {
    "meeting_id": 1393,
    "room_id": 60,
    "title": "Staff Meeting",
    "goal": "",
    "start": "2016-01-21T13:00:00-08:00",
    "end": "2016-01-21T14:55:00-08:00",
    "url": "\/meeting\/1393",
    "copyable": "true",
    "removable": "true",
    "editable": "true"
  },
  {
    "meeting_id": 1374,
    "room_id": 60,
    "title": "Staff Meeting",
    "goal": "Sync up on the Spartacus project and review release blockers for the API",
    "start": "2016-01-14T16:00:00-08:00",
    "end": "2016-01-14T16:55:00-08:00",
    "url": "\/meeting\/1374",
    "copyable": "true",
    "removable": "true",
    "className": "fc-adjourned"
  },
  {
    "note_id": 564,
    "title": "Due: Check in with Freddy",
    "text": "Assigned to: Dingo Batsy",
    "start": "2016-01-19T00:00:00-08:00",
    "end": "2016-01-19T00:00:00-08:00",
    "url": "\/todo\/564",
    "allDay": "true",
    "className": "fc-action-item",
    "editable": "true"
  },
  {
    "note_id": 565,
    "title": "Due: Sign the renewal contract",
    "text": "Assigned to: nobody",
    "start": "1969-12-31T16:00:00-08:00",
    "end": "1969-12-31T16:00:00-08:00",
    "url": "\/todo\/565",
    "allDay": "true",
    "className": "fc-action-item",
    "editable": "true"
  }
]

Find this useful?