Salesforce Training Project - A Lightning Sales Trip Planning App

Small training projects are a great way to build skills and learn new technologies. For me, reading documentation or blog posts is no substitute for actually building a working application. In this post, I’ll share the results of my latest Salesforce project: a simple sales trip planning tool built with Lightning Components. Perhaps it will help give you some ideas for training projects for you to pursue.

I had three goals for the exercise:

Continue to develop experience building Lightning components Explore ways to incorporate mapping into Salesforce apps Integrate Twilio to send SMS messages from Salesforce

Application Overview

The application I envisioned to achieve these goals is a simple trip planner that a salesperson could use to plan a trip to visit several customers in the most efficient way possible. The user chooses a primary customer, and is then presented with a map showing all of their other customers within a 10-mile radius of the primary customer. They can then select additional customers to add to the trip. They can then view the optimum travel path as directions in Google Maps, and have the link to the directions sent to their mobile phone as an SMS message.

Here’s a short screencast (no audio) of the application in use:

The application uses the following external resources and libraries:

  1. LeafletJS mapping library
  2. Mapbox for map tiles
  3. Google Maps Directions API for route optimization
  4. Google Maps URL for directions
  5. Twilio to send SMS messages.

Components

This diagram shows how the application interface is broken down into the various components:

This next diagram outlines the major messages passing between components – (it will make more sense after reading the description of the application flow, but including it here for reference):

User Flow

Step 1 – Select Base Customer

To start using the application, the user needs to identify a “base customer”; the customer around which he or she wants to build the trip. They identify this customer using a search box in the BaseCustomerSearch component– as they enter a search string, search results appear as a listbox dynamically underneath the search box. This approach uses the keyup option of the ui:inputText component to call a controller method, runSearch, each time the user enters another character in the search string. The runSearch method waits for the search string to be three characters long, then calls an Apex controller method to query accounts by name using the search string and return the list, storing it in a component attribute.

Search Method from Lightning Controller

Search Method for Apex Controller

To display the search results, an aura:iteration is used to create one BaseCustomerSearchResult component for each customer in the results, which is passed to it as a parameter.

BaseCustomerSearch iteration

Once the user selects one of the search results by clicking on it, a baseCustomerSelected application event is fired. This event is handled by the BaseCustomerSearch component, which clears the search, by the Map component, which re-centers the map on the base customer’s location and drops a blue marker, by the BaseCustomerDisplay component to display the selected customer, and by the NearbyCustomerList component, which retrieves the nearby customers.

Here is a closeup of the search experience:

Step 2 – View Nearby Customers

The nearby customers are displayed in two ways: as cards in the left column on the interface, and also as markers on the map.

To retrieve the nearby customers, the NearbyCustomerList component handles the baseCustomerSelected application event fired by the BaseCustomerSearch component. The handler calls an Apex controller method that retrieves all customer records within 10 miles of the base customer, using the records’ Location__c field and the DISTANCE SOQL function (this solution requires that you have geocoding in place, recording the location of each customer on their record). The resulting list of customers is stored in a component attribute, and then displayed as cards using an aura:iteration to create a NearbyCustomerItem component for each customer.

Lastly, the handler fires an AddMarkers application event, with the customer list as a parameter, to signal to the Map component to add markers for the nearby customers.

The Map component handles the addMarkers event, and iterates through the list of customers, using the LeafletJS library to drop a grey marker on the map for each.

Step 3- Select Customers for your trip

So at this point, the user has executed a search, and selected the customer they want to base their trip around. They are presented with all of the other customers within 10 miles of their selected customer, displayed as grey markers on the map and as cards on the left with some basic information (customer name and address, distance from the base customer).

The next step is for the user to select the customers they want to include in their trip. They have two options for doing this – they can select a checkbox on the customer’s card in the leftmost section of the interface, or they can click the marker representing the customer on the map.

Clicking the checkbox on the customer card fires an application event that is handled by the Map component. This handler checks whether the checkbox was checked/unchecked, and then flips the color of the marker accordingly (orange/grey).

Handler for the checkbox event.

Helper method called by handler

Clicking on the marker itself calls a handler method that flips the color of the marker, and then fires a markerClicked event, which is handled by the NearbyCustomerItem components (the card in the leftmost section of the interface). Each one checks to see if the marker clicked represents its customer, and if so, it changes the state of its checkbox.

Handler for markerClicked event

Step 4 – Show/Send Directions

With the desired customers ( or “stops”) selected, the user is now ready to generate driving directions for his or her trip. This is a two-step process; first, a call to the Google Directions API is used to identify the optimum order of the stops. Next, a Google Maps URL is assembled based on this order.

Perhaps counter-intuitively, simply sorting the stops by distance from closest to farthest and routing in order often does not yield the shortest trip. Identifying the shortest route is known as the “Traveling Salesman Problem”, and is a famous optimization problem. The Google Directions API will solve this problem for up to eight waypoints (I believe there is a premium plan which raises this limit). Google’s implementation is not perfect for our purposes – it does require we specify the origin and destination, and will only optimize the waypoints in-between. I would prefer to pass all customer locations to Google as equals, to allow the service to optimize the route for all ten; routes that do not start at our base customer are still valid. In this case, I am passing the base customer as the origin, and the farthest customer selected as the destination – so we are getting an imperfect optimization.

The Google Directions API will return a waypoint_order element in its response, which is the only piece of the response that I am using. With the optimum waypoint order I can construct the Google Maps URL for the route.

The interface includes a Commands component, positioned below the Map component. This component has a “Show Directions” button, which calls a method that checks for a Google Maps URL, generates it if it does not exist, then opens a new browser window to display the directions.

The Commands component also has a phone number input field and a “Send Directions” button. This button checks for a Google Maps URL, generates it if it does not exist, then calls an Apex controller method which calls the Twilio service to send an SMS message containing the Google Maps URL to the provided telephone number, allowing the recipient to quickly access the directions on their mobile phone.

Future Enhancements

I’ve accomplished my goals for the project, so I am setting it aside for now. It is, of course, far from production-ready – some of the missing “finish” items on my list include the following (likely you have identified many more, and are probably shaking your head):

  1. Add tooltip with the customer’s name and address to the map markers to help the user select customers for their trip.
  2. Add the ability to save and recall trips, so that a user can recreate the directions later or edit a trip they have already created.
  3. Add a URL shortener – the URLs get LONG as you add more stops to the trips. Incorporating a shortener would result in a cleaner look.
  4. Add some validation and formatting to the phone number input field
  5. Resolve, somehow, the fact that the distances displayed in the cards and used in filtering the list of nearby accounts are “as the crow flies” distances rather than driving distances.
  6. Limit the user to add only nine nearby customers (Google Directions API optimization limit).
  7. Test/modify the application to make it mobile-friendly

Conclusion

Wrapping up this short exercise, I am again pleasantly surprised how quickly and easily you can create simple Lightning applications that do interesting things, look polished, and integrate cleanly into the user interface.

Moving on, I am looking to dig into Platform Events next. I’m planning to create a simple Ruby app to create Platform Events, and some Salesforce code that reacts to the events being fired.

If you got this far – thanks for reading!