Try FreshBooks Free

Building the FreshBooks account overview

by Mark on July 24/2012

We recently released a totally re-designed and re-engineered account overview. It replaces one of the slowest pages in FreshBooks with a faster, richer and more interactive client side application. It was built by a team of 4 over the course of a few weeks, and I’d like to share how we built it and some of the lessons we learned along the way.

When reviewing just how much data we had planned to pack on each page it became very apparent that generating a huge HTML page wasn’t going to provide the experience we wanted. The existing homepage is consistently one of the slowest pages in our application. Basically doubling the amount of data displayed on the existing page was not going to be performant. We quickly decided that building the overview as a mini client-side application was going to provide the most performant end result.

Server-side architecture

The server-side architecture is organized around the concept of discrete panels with a single responsibility/function. Visually and structurally the overview is divided into a number of independent panels. Each panel is self-contained and provides information on a different aspect or dimension of your business. Not every FreshBooks account uses every feature, so we only display the panels for the features you have enabled.

After developing the initial framework, we were able to build the panels simultaneously with no fears of bleeding into each other’s work. The panel system allowed us to easily write automated tests, add, remove and replace panels during development. The panel system is comprised of two major parts: the PanelControl and the panels themselves.

The PanelControl is responsible for a few tasks:

It controls which panels are available to users

Provides factory features for creating instances of any panel

Knows how to filter panels based on them being eager or lazy loading

Each panel implements an interface that requires the following functionality:

Whether or not the panel should eager load data on the initial page load, or lazy load via Javascript

Whether or not the current user can access a given panel

Run the panel and fetch the results to be displayed

While simple, the panel system was incredibly flexible and able to accomodate all the requirements we had.

Client-side mini application

I refer the the overview as a mini-app, as unlike many single page applications, the overview does not handle any page/layout changes. To create the mini-app, we leveraged Backbone.js, Hogan.js, Highcharts and canvas. I’m extremely happy with every choice we made. Highcharts was a clear MVP of the project. It provides gorgeous graphs, with a dizzying number of configuration options, customization hooks, and fantastic browser support. Without Highcharts I don’t think we would have been able to deliver the account overview in the timeframe we had.

The Backbone application forms the heart of the account overview. After a minimal HTML page with static content is served, the Backbone application starts up.

The Backbone application is initialized with account settings and list of panels to load. The list of panels is decided by the serverside panel code described earlier

Each panel is managed by a Backbone View, Model, and one or more mustache templates. The Backbone models communicate with a matching endpoint & the server-side panel

As each panel is constructed it loads data from the server, and renders into the DOM. Highcharts is used for more complex graphs while the simple sparklines are simply small canvas elements

This system provides a fast initial page load with progressively loading data + visualizations. For larger accounts this is many times faster than attempting to load all the data as part of the initial page.

Using an asynchronous approach to the panels also made reloading panel data when interactive ranges were changed simple and require very little additional code.

Problems and lessons learned

Not everything was smooth sailing while we built the account overview. The highly customized tooltips and chart aesthetic we wanted required a considerable amount of time to accomplish. The default rounded bars in high charts are round on both ends making more of a sausage than a bar. Getting the effect we wanted took some custom drawing code, and kudos to our designer Thomas for cracking this tricky problem.

Tooltips were another pain point. While highcharts supports HTML tooltips out of the box we still needed to make a few modifications. When creating tooltips shared amongst all series in a chart, Highcharts uses invisible elements to track where the mouse is. This works great until you create really wide tooltips that contain clickable elements. The mousemove regions overlapped our oversize tooltips making it impossible to reach the links. We ended up fixing this using the following inside the tooltip.positioner option:

// eventBound is declared in the containing scope, so only one event is bound.
if (!eventBound) {
eventBound = true;
// SVG comes with a div, VML only has element.
$(this.label.div || this.label.element).on('mousemove', false);
}

By trapping all the mousemove events we were able allow mouse movement inside our oversize toolips without any issue. We were burned a few times by hogan.js treating ” as a truthy value. This was counter to our expectations, and is something you should watch out for when working with mustache templates.

Overall, we’re quite happy with how the overview turned out both from a technical prespective and customer feedback point of view. We’re planning on iterating a bit more on the feature set and doing incremental improvements based on customer feedback and work we weren’t able to fit into the initial release, so stay tuned for more improvements to the overview.