---
title: "JQuery Timeline Resource Group Aggregate Summaries Example | Mobiscroll"
component: "Timeline"
framework: "jQuery"
library_version: "6.2.0"
url: "https://demo.mobiscroll.com/jquery/timeline/show-summaries-aggregates-for-resource-groups"
---

# Resource Group Aggregate Summaries - jQuery Timeline demo

Show aggregate totals in parent resource rows on a JQuery Timeline — summary values recalculate as bookings are added, changed, or removed.

- [Live demo](https://demo.mobiscroll.com/jquery/timeline/show-summaries-aggregates-for-resource-groups)
- [View code on GitHub](https://github.com/acidb/mobiscroll-demos-jquery/tree/main/src/demos/eventcalendar/timeline/show-summaries-aggregates-for-resource-groups)
- This demo in other frameworks: [JavaScript](https://demo.mobiscroll.com/javascript/timeline/show-summaries-aggregates-for-resource-groups.md), [Angular](https://demo.mobiscroll.com/angular/timeline/show-summaries-aggregates-for-resource-groups.md), [React](https://demo.mobiscroll.com/react/timeline/show-summaries-aggregates-for-resource-groups.md), [Vue](https://demo.mobiscroll.com/vue/timeline/show-summaries-aggregates-for-resource-groups.md)

## Description

When scheduling across multiple groups of resources it sometimes is helpful to provide summaries and aggregate calculations on a group level. These dynamically calculated values can be presented as custom templated events in resource parent rows. Whenever new bookings are made, updated or deleted, use the [`onEventCreated`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onEventCreated), [`onEventUpdated`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onEventUpdated) and [`onEventDeleted`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onEventDeleted) lifecycle events to update the aggregates for their parent resource. As we are working with variable event heights, make sure to set `eventHeight: 'variable'` which can be configured under the [`view`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-view). This is necessary if the actual booked events have a different height than the summary events (for parent resources).

**Variable event height is an experimental feature.** [Learn more about it in the documentation →](https://mobiscroll.com/docs/jquery/eventcalendar/timeline#variable-event-height)

## What this demo shows

- A desktop timeline that combines a weekly date range with hierarchical resources and group-level summary events.
- **Header navigation** The month and year label opens date navigation, the previous and next arrows move between weeks, and the Today button returns to the current date.
- **Week view** The date strip shows the selected week from Sunday to Saturday, with the current date highlighted.
- **Resource hierarchy** The resource column shows expandable parent groups with nested vehicle resources grouped by fleet or location.
- **Resource details** Child resource rows display vehicle metadata such as model, license plate, and vehicle type.
- **Group summaries** Parent resource rows show daily aggregate metrics for all scheduled trips in that group.
- **Summary metrics** Each group summary displays total estimated distance, total fuel consumption, and estimated operating cost.
- **Event cards** Trip events show booking details such as destination or appointment, assigned driver, estimated distance, estimated fuel consumption, and pick-up and drop-off times.
- **Dynamic aggregates** Summary values are recalculated from the scheduled trips in each resource group when events are created, updated, or deleted.
- **Date positioning** Events are positioned by their assigned resource and exact date range.
- **Event interaction** Events highlight on hover.
- **Event selection** Clicking an event selects and highlights it.
- **Event creation** New events can be created with double click on timeline cells.
- **Scrolling** The timeline supports horizontal and vertical scrolling for navigating through time and resources.

## Best for

- **Fleet management** Scheduling vehicles while tracking daily distance, fuel consumption, and estimated operating cost by fleet or location.
- **Transportation planning** Comparing trip assignments across vehicles and resource groups in a timeline layout.
- **Vehicle reservations** Managing bookings for individual vehicles while keeping parent rows available for group-level utilization summaries.
- **Logistics operations** Reviewing scheduled trips alongside aggregate travel metrics for each resource group.
- **Field service scheduling** Assigning vehicles or crews to appointments where travel estimates and operating costs need to stay visible with the schedule.

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-themeVariant) - option
- [`getEvents`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-getEvents) - method
- [`setEvents`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-setEvents) - method
- [`view`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-view) - option
- [`extendDefaultEvent`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-extendDefaultEvent) - option
- [`clickToCreate`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-clickToCreate) - option
- [`dragToCreate`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-dragToCreate) - option
- [`dragToMove`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-dragToMove) - option
- [`dragToResize`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-dragToResize) - option
- [`eventOverlap`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-eventOverlap) - option
- [`onPageLoading`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onPageLoading) - event
- [`onEventUpdated`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onEventUpdated) - event
- [`onEventCreated`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onEventCreated) - event
- [`onEventDeleted`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onEventDeleted) - event
- [`min`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-min) - option
- [`max`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-max) - option
- [`renderResource`](https://mobiscroll.com/docs/jquery/eventcalendar/api#renderer-renderResource) - renderer
- [`renderTimelineEventContent`](https://mobiscroll.com/docs/jquery/eventcalendar/api#renderer-renderTimelineEventContent) - renderer
- [`resources`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-resources) - option
- [`data`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-data) - option

## Code

### JS

```js
mobiscroll.setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

$(function () {
  function getAggregateEvents(dayEvents) {
    var aggregateEvents = {};
    myResources.forEach(function (resource) {
      for (var date = new Date(firstViewDay); date < lastViewDay; date.setDate(date.getDate() + 1)) {
        aggregateEvents[resource.id + +date] = {
          id: resource.id + +date,
          isSummary: true,
          allDay: true,
          start: new Date(date),
          end: new Date(date),
          consumption: 0,
          distance: 0,
          resource: resource.id,
        };
      }
    });
    dayEvents.forEach(function (event) {
      if (!event.isSummary) {
        var parentResource = event.resource.split('_')[0];
        var dayStart = new Date(event.start).setHours(0, 0, 0, 0);
        if (aggregateEvents[parentResource + dayStart]) {
          aggregateEvents[parentResource + dayStart].consumption += event.consumption;
          aggregateEvents[parentResource + dayStart].distance += event.distance;
        }
      }
    });
    return Object.values(aggregateEvents);
  }

  function updateCalendarEvents(inst) {
    var dailyEvents = inst.getEvents();
    var updatedSummaries = getAggregateEvents(dailyEvents);
    var updatedEvents = myEvents.concat(updatedSummaries);
    setTimeout(function () {
      inst.setEvents(updatedEvents);
    });
  }

  var myResources = [
    {
      id: 'orlando',
      name: 'CNL Tower II',
      address: '420 S Orange Ave, Orlando, FL 32801',
      eventCreation: false,
      eventDragBetweenResources: false,
      eventDragInTime: false,
      eventResize: false,
      color: 'transparent',
      children: [
        {
          id: 'orlando_1',
          car: 'Lincoln Continental',
          plate: '51-5375',
          type: 'Sedan',
        },
        {
          id: 'orlando_2',
          car: 'Porsche Panamera',
          plate: '72-2590',
          type: 'Sports sedan',
        },
        {
          id: 'orlando_3',
          car: 'Range Rover Velar',
          plate: '41-7701',
          type: 'Compact SUV',
        },
      ],
    },
    // Showing partial data. Download full source.
  ];

  var myEvents = [
    {
      id: 1,
      allDay: true,
      start: '2026-10-25T06:00',
      end: '2026-10-25T19:00',
      resource: 'orlando_1',
      title: 'Sales meeting with JLL',
      name: 'John C. Johnson',
      distance: 120,
      consumption: 35,
    },
    {
      id: 2,
      allDay: true,
      start: '2026-10-26T03:00',
      end: '2026-10-26T20:00',
      resource: 'orlando_1',
      title: 'Client consultation',
      name: 'Mary K. Adams',
      distance: 95,
      consumption: 30,
    },
    {
      id: 3,
      allDay: true,
      start: '2026-10-28T10:00',
      end: '2026-10-28T17:00',
      resource: 'orlando_1',
      title: 'Project review at XYZ',
      name: 'Robert B. Brown',
      distance: 150,
      consumption: 40,
    },
    {
      id: 4,
      allDay: true,
      start: '2026-10-30T09:00',
      end: '2026-10-30T16:00',
      resource: 'orlando_1',
      title: 'Team building workshop',
      name: 'Susan E. Smith',
      distance: 60,
      consumption: 20,
    },
    // Showing partial data. Download full source.
  ];

  var firstViewDay = null;
  var lastViewDay = null;

  $('#demo-aggregates')
    .mobiscroll()
    .eventcalendar({
      view: {
        timeline: {
          type: 'week',
          eventDisplay: 'fill',
          eventHeight: 'variable',
        },
      },
      extendDefaultEvent: function (args) {
        var d = args.start;
        var start = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 9);
        var end = new Date(start.getFullYear(), start.getMonth(), start.getDate(), 18);
        return {
          allDay: true,
          start: start,
          end: end,
          title: 'New event',
          name: 'John Doe',
          distance: 42,
          consumption: 6,
        };
      },
      clickToCreate: true,
      dragToCreate: false,
      dragToMove: true,
      dragToResize: false,
      eventOverlap: false,
      onPageLoading: function (args, inst) {
        firstViewDay = new Date(args.firstDay);
        lastViewDay = new Date(args.lastDay);
        updateCalendarEvents(inst);
      },
      onEventUpdated: function (args, inst) {
        updateCalendarEvents(inst);
      },
      onEventCreated: function (args, inst) {
        var newEvent = args.event;
        myEvents.push(newEvent);
        updateCalendarEvents(inst);
      },
      onEventDeleted: function (args, inst) {
        var deletedEvent = args.event;
        var index = myEvents.indexOf(deletedEvent);
        myEvents.splice(index, 1);
        updateCalendarEvents(inst);
      },
      min: '2026-10-25T00:00',
      max: '2026-11-07T00:00',
      renderResource: function (res) {
        if (res.isParent) {
          return '<div>' + res.name + '</div><div class="mds-aggregates-address">' + res.address + '</div>';
        }
        return (
          '<div>' +
          res.car +
          '</div><div class="mds-aggregates-plate">Plate ' +
          res.plate +
          '</div><div class="mds-aggregates-cartype">' +
          res.type +
          '</div>'
        );
      },
      renderTimelineEventContent: function (event) {
        if (event.currentResource.isParent) {
          return (
            '<div class="mds-aggregates-summary-event mbsc-flex">' +
            '<div class="mds-aggregates-summary-details mbsc-flex mbsc-flex-1-1"><div class="mds-aggregates-summary-label">Total dist. <div class="mds-aggregates-summary-value">' +
            event.original.distance +
            ' mi</div></div><div class="mds-aggregates-summary-label">Total cons. <div class="mds-aggregates-summary-value">' +
            event.original.consumption +
            ' gal</div></div><div class="mds-aggregates-summary-label">Est. cost <div class="mds-aggregates-summary-value">$' +
            Math.round(event.original.consumption * 3.706 || 0) +
            '</div></div></div></div>'
          );
        }
        return (
          '<div class="mds-aggregates-event">' +
          '<div class="mds-aggregates-title">' +
          event.title +
          '</div><div class="mds-aggregates-name">' +
          event.original.name +
          '</div><div class="mds-aggregates-details"><div class="mds-aggregates-label">Est. distance: <span class="mds-aggregates-value">' +
          event.original.distance +
          ' mi</span></div><div class="mds-aggregates-label">Est. consumption: <span class="mds-aggregates-value">' +
          event.original.consumption +
          ' gal</span></div></div><div class="mds-aggregates-label mbsc-flex"><div class="mbsc-flex-1-1">Pick up: <span class="mds-aggregates-value"> ' +
          mobiscroll.formatDate('hh:mm A', new Date(event.original.start)) +
          '</span></div><div> Drop off: <span class="mds-aggregates-value">' +
          mobiscroll.formatDate('hh:mm A', new Date(event.original.end)) +
          '</span></div></div></div>'
        );
      },
      resources: myResources,
      data: myEvents,
    });
});
```

### HTML

```html
<div id="demo-aggregates" class="mds-aggregates"></div>
```

### CSS

```css
.mds-aggregates .mbsc-timeline-column,
.mds-aggregates .mbsc-timeline-header-column {
  width: 14.5em;
}

.mds-aggregates .mbsc-timeline-row-gutter {
  height: 6px;
}

.mds-aggregates .mbsc-timeline-resource-title{
  font-size: 14px;
  font-weight: 600;
}

.mds-aggregates .mbsc-ios .mbsc-schedule-event:after {
  display: none;
}

.mds-aggregates .mbsc-schedule-event-inner {
  height: auto;
  width: 100%;
}

.mbsc-windows-dark .mds-aggregates-summary-event,
.mbsc-ios-dark .mds-aggregates-summary-event,
.mbsc-material-dark .mds-aggregates-summary-event {
  color: #fff;
}

.mds-aggregates-address {
  padding: 8px 0;
  font-size: 11px;
  line-height: 16px;
  font-weight: 600;
}

.mds-aggregates-plate {
  padding: 8px 0;
  font-weight: 600;
}

.mds-aggregates-cartype {
  font-size: 11px;
  font-weight: 600;
}

.mds-aggregates-summary-event {
  color: #000;
}

.mds-aggregates-summary-details {
  white-space: normal;
  justify-content: space-between;
  padding: 8px;
}

.mds-aggregates-summary-label {
  font-size: 11px;
  font-weight: 400;
}

.mds-aggregates-summary-value {
  font-weight: 600;
  font-size: 15px;
  line-height: 24px;
}

.mds-aggregates-event {
  padding: 4px;
}

.mds-aggregates-title {
  white-space: normal;
  line-height: 20px;
  font-size: 14px;
  font-weight: 600;
}

.mds-aggregates-name {
  padding: 4px 0;
  font-size: 12px;
  line-height: 16px;
  font-weight: 600;
}

.mds-aggregates-value {
  font-weight: 600;
}

.mds-aggregates-label {
  font-size: 11px;
  font-weight: 400;
}

.mds-aggregates-details {
  white-space: normal;
  justify-content: space-between;
  padding: 8px 0;
}
```

## Implementation notes

- Use `timeline: { type: 'week', eventDisplay: 'fill', eventHeight: 'variable' }`. `eventHeight: 'variable'` is required because summary events on parent rows and regular events on child rows have different heights.
- Restrict the calendar to a 2-week window centered on today: `min: dyndatetime('y,m,d-7')`, `max: dyndatetime('y,m,d+6')`.
- Define 3 parent resource groups (by city), each with 3 child vehicle resources. Use city-prefixed IDs to establish parent-child relationships (e.g. `orlando`, `orlando_1`, `orlando_2`, `orlando_3`). Parent resources carry `name`, `address`, `color: 'transparent'`, and `eventCreation: false`, `eventDragBetweenResources: false`, `eventDragInTime: false`, `eventResize: false` - they display summary data only and cannot be booked directly. Child resources carry `car`, `plate`, and `type` as custom properties.
- Add ~66 regular events (all `allDay: true`), each assigned to a child resource. Every event carries two custom numeric properties: `distance` (miles) and `consumption` (gallons). Use `dyndatetime` offsets relative to today for dates.
- Enable `clickToCreate: true`, `dragToMove: true`. Set `dragToCreate: false`, `dragToResize: false`, `eventOverlap: false`. Provide `extendDefaultEvent` to supply defaults for newly created events (`title: 'New event'`, `name: 'John Doe'`, `distance: 42`, `consumption: 6`, `allDay: true`).
- **Summary (aggregate) events** - generated programmatically, not from user input:
  - For each visible day and each parent resource, create one summary event object: `{ id: parentId + dayTimestamp, isSummary: true, allDay: true, start: day, end: day, resource: parentId, distance: 0, consumption: 0 }`.
  - Iterate over all non-summary events; for each, derive the parent resource ID by splitting the child resource ID on `_` (e.g. `'orlando_1'.split('_')[0]` → `'orlando'`). Add the event's `distance` and `consumption` to the matching (parent, day) summary object.
  - Estimated cost is computed in the render layer: `Math.round(consumption * 3.706)`.
  - Merge the original events and the generated summaries into a single array and pass it to the calendar's `data` prop.
- **Refresh cycle**: Store original events in a mutable ref (not state). On `onPageLoading`, capture `args.firstDay` / `args.lastDay`, then call the aggregate recalculation in a `setTimeout`. On `onEventCreated`, `onEventUpdated`, and `onEventDeleted`, update the events ref and call the same recalculation.
- **Custom resource column** (`renderResource` / `resourceTemplate` / `resource` slot): check `res.isParent` - if true, render `name` + `address`; otherwise render `car`, licence plate, and vehicle type.
- **Custom event content** (`renderTimelineEventContent` / `timelineEventContentTemplate` / `timeline-event-content` slot): check `event.currentResource.isParent` - if true (summary row), render "Total dist.", "Total cons.", "Est. cost" aggregates; otherwise render the booking title, driver name, estimated distance/consumption, and formatted pick-up / drop-off times using `formatDate('hh:mm A', ...)`.

## Run it in your project

1. If not signed up yet, or don't own a license, you can [start a free trial](https://mobiscroll.com/starttrial).

2. Clone the repository:

   ```
   git clone https://github.com/acidb/mobiscroll-demos-jquery.git
   ```

3. If not installed yet, install the Mobiscroll CLI from NPM:

   ```
   npm install -g @mobiscroll/cli
   ```

4. Config Mobiscroll - open a terminal window in the root directory and run:

   ```
   mobiscroll config jquery
   ```

5. Install dependencies - open a terminal window in the root directory and run:

   ```
   npm install
   ```

6. Run the project:

   ```
   npm run dev
   ```

On a trial, add `--trial` to the config command: `mobiscroll config jquery --trial`.

Running a demo locally requires a Mobiscroll trial or a license; setup is per framework at https://mobiscroll.com/docs/jquery/getting-started/installation

## Related demos

- Previous: [Resource Grouping & Hierarchy](https://demo.mobiscroll.com/jquery/timeline/resource-grouping-hierarchy.md) - Build a JQuery resource timeline with collapsible nested groups, pinned resources, and per-group event creation controls.
- Next: [Resource Drag & Drop Reorder](https://demo.mobiscroll.com/jquery/timeline/resource-drag-drop-reorder.md) - Reorder resources on the JQuery Timeline by drag and drop — each row shows a drag handle, and an update callback runs on drop.
