CREATING A KANBAN VIEW FOR LEADS

In our present era, no one has time for anything. We all are so busy with our lives that we hardly get time for doing anything. In such an atmosphere who wouldn’t be happy to get things done in a matter of few clicks.

Yesterday when I was digging out the new Salesforce Lightning UI, I was amazed to find the new Kanban view feature on Opportunity object. I noticed that we could change the Stage of an Opportunity by just dragging and dropping into a different column where each columns represent different stages of an opportunity. Isn’t it amazing, just drag and drop an Opportunity into a corresponding stage and the stage of the Opportunity gets changed. Wow!

OPKanbanUI

But then I was disappointed when I came to know that this feature exists only for the Opportunity object. Oops! 😦 What if I want to change the Lead Statuses of the leads in a similar way ? No simple way, the Kanban feature exists only for Opportunity. If you want to change a Lead Status, there is no other way than opening up a lead, editing it and then saving it. 😦

LeadUI

Then I thought to myself, I being a Salesforce developer, why can’t I bring this functionality for my own object? And then I researched and researched and finally I could achieve it 🙂 Yes, it’s simple to bring the Kanban feature into other objects.

Why not implement it for Lead records. The Status field of a Lead record is something which always keeps changing. So let’s implement it.

For this task, all that you need to know is bit of Apex programming for fetching the Leads, Visualforce for the UI and a bit of JavaScript and jQuery to implement the drag and drop feature.

And trust me, though it sounds difficult, it ain’t. I guarantee that 😉

So let’s get it started.

Firstly we will create a controller which fetches all the Leads and groups them into a map according to their Statuses. The map will have the Status as it’s key and it’s value will be a list of Lead records with the respective Status. The controller will also contain a Remote Action method which would update the Status field of the Leads which are altered on the UI.

You can have a look at the Controller code below :

public class LeadKanbanController {
public static String LEAD_MOVED = '{0} was moved successfully to {1}';
public List<Schema.PicklistEntry> leadStatuses { get; set; }
public Map<String, List<Lead>> allLeads { get; set; }
public class UpdateStatus {
public Boolean isSuccess;
public String message;
}
public LeadKanbanController() {
leadStatuses = Lead.Status.getDescribe().getPicklistValues();
fetchLeads();
}
@RemoteAction
public static UpdateStatus updateLeadStatus( Id leadId, String newLeadStatus ) {
Lead leadDetails = [
SELECT Id
,Name
FROM Lead
WHERE Id = :leadId
];
leadDetails.Status = newLeadStatus;
UPDATE leadDetails;
UpdateStatus updatedLeadDetails = new UpdateStatus();
updatedLeadDetails.isSuccess = true;
updatedLeadDetails.message = String.format( LEAD_MOVED, new List<String>{ leadDetails.Name, newLeadStatus } );
return updatedLeadDetails;
}
private void fetchLeads() {
List<Lead> leads = [
SELECT Id
,Name
,Title
,Company
,Email
,Status
FROM Lead
];
allLeads = new Map<String, List<Lead>>();
for( Lead lead : leads ) {
if( !allLeads.containsKey( lead.Status ) ) {
allLeads.put( lead.Status, new List<Lead>{ lead } );
}
else {
allLeads.get( lead.Status ).add( lead );
}
}
}
}

Once the controller part is done, we can move and create the UI for the Kanban.

The UI is built keeping the Salesforce Lightning Design System in mind. The UI contains different columns which represent the Lead Status and the Leads belong to the column of their respective Statuses. This is done with help of two apex repeat tags. One repeat tag to iterate the Lead Statuses horizontally and the other repeat tag to iterate the corresponding Leads vertically.

It will also contain a small piece of JavaScript and jQuery to implement the drag and drop feature. To achieve this feature, we have used the native inbuilt ‘sortable’ method which is included in the jQueryUI Library. It contains mainly three properties :

  • connectWith‘ property which is to connect the childs to the respective columns.
  • handle‘ property which is used to specify the area through which the Lead should be draggable.
  • placeholder‘ property which is to show the placeholder while dropping a Lead.

In the script it then calls the Apex Controller passing the respective Lead Id and Lead Status.

Here is the code for the UI :

<apex:page controller="LeadKanbanController" standardStylesheets="false" showHeader="false" applyHtmlTag="false" applyBodyTag="false" docType="html-5.0" cache="false" expires="0">
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Lead Kanban</title>
<apex:slds />
<link type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" />
<link type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.css" rel="stylesheet" />
<style type="text/css">
body {
padding: 1em;
}
.slds-item {
box-shadow: 0 0 10px gray;
border: none !important;
background-color: #fff;
border-radius: 0 !important;
border-left: 5px solid #56aadf !important;
}
.slds-title {
border-radius: 3px;
background-color: #e0e5ee;
border: 1px solid #d8dde6;
cursor: move;
}
.slds-col {
height: 520px;
overflow-y: auto;
border-left: 1px solid whitesmoke;
}
.slds-item-placeholder {
border: 2px dotted gray !important;
height: 5rem;
}
.moving-card {
opacity: 0.5;
transform: rotate( -5deg );
}
</style>
</head>
<body>
<div class="slds-scope">
<!–Page Header–>
<div class="slds-page-header slds-m-bottom–small" role="banner">
<div class="slds-media slds-media–center">
<div class="slds-media__body">
<p class="slds-page-header__title slds-truncate slds-align-middle slds-text-heading–large">
<strong><i class="fa fa-th" aria-hidden="true"></i>&nbsp;Lead Kanban</strong>
</p>
<p class="slds-text-body–small page-header__info">Adjusting Lead Stages have never become so easy!</p>
</div>
</div>
</div>
<!–Kanban Column Headers–>
<div class="slds-grid">
<div class="slds-tabs–path" role="application">
<ul class="slds-tabs–path__nav" role="tablist">
<apex:repeat value="{!leadStatuses}" var="status">
<li class="slds-tabs–path__item slds-is-incomplete" role="presentation">
<a class="slds-tabs–path__link" tabindex="-1" role="tab" href="javascript:void(0);">
<span class="slds-tabs–path__title slds-text-heading–medium">{!status.label}</span>
</a>
</li>
</apex:repeat>
</ul>
</div>
</div>
<!–Kanban Columns–>
<div class="slds-grid">
<apex:repeat value="{!leadStatuses}" var="status">
<div class="slds-col slds-size–3-of-12 slds-has-dividers–around-space slds-scrollable–y" name="{!status.value}">
<apex:repeat value="{!allLeads[status.value]}" var="lead">
<div class="slds-item slds-m-around–small" id="{!lead.Id}">
<div class="slds-tile slds-tile–board">
<h3 class="slds-section-title–divider slds-m-bottom–small slds-title">
<a href="javascript:void(0);">
<i class="fa fa-user fa-fw" aria-hidden="true"></i>&nbsp;{!lead.Name}
</a>
</h3>
<div class="slds-tile__detail slds-text-body–small">
<p class="slds-truncate">
<i class="fa fa-briefcase fa-fw" aria-hidden="true"></i>&nbsp;{!lead.Title}
</p>
<p class="slds-truncate">
<i class="fa fa-building fa-fw" aria-hidden="true"></i>&nbsp;{!lead.Company}
</p>
<p class="slds-truncate">
<i class="fa fa-envelope fa-fw" aria-hidden="true"></i>&nbsp;{!lead.Email}
</p>
</div>
</div>
</div>
</apex:repeat>
</div>
</apex:repeat>
</div>
</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script type="text/javascript" src="https://code.jquery.com/ui/1.12.0/jquery-ui.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.js"></script>
<script type="text/javascript">
$( ".slds-col" ).sortable(
{
connectWith: ".slds-col",
handle: ".slds-title",
placeholder: "slds-item slds-m-around–small slds-item-placeholder",
start: function( event, ui ) {
$( ui.item ).addClass( "moving-card" );
},
stop: function( event, ui ) {
$( ui.item ).removeClass( "moving-card" );
var leadId = $( ui.item ).attr( "id" );
var leadStatus = $( ui.item ).parent().attr( "name" );
LeadKanbanController.updateLeadStatus(
leadId,
leadStatus,
function( result, event ) {
if( result.isSuccess ) {
toastr.info( result.message );
}
}
);
}
}
);
</script>
</body>
</html>
</apex:page>

view raw
LeadKanban.html
hosted with ❤ by GitHub

That’s it, you can have a look at your page in which you have implemented the Lead Kanban functionality.

LKUI1

LeadKanbanUI2

LKUI3

I have created a Force.com Site page for my Lead Kanban so that you guys can fiddle around it.

Here is the link for it. Give it a try and let me know your feedbacks. 🙂

http://shrutis22-developer-edition.ap7.force.com/Projects/LeadKanban

13 thoughts on “CREATING A KANBAN VIEW FOR LEADS

  1. Appreciate your thought process ! Just a thought from me though…. I feel that if you build a lightning component rather than a VF page, it would better align with the Lightning experience !

    Like

  2. Hi Shruti,

    I tried to recreate this scenario in my Org. I cannot drag the boxes from one status to another.
    Do i have to include any static resources in my org in order to make this thing work.

    thanks,
    Arvind

    Like

    1. No you don’t need any Static Resources. Are you sure you recreated the Apex Class and the VF Page with the exact same content?

      Can you copy paste the Apex Class and the Controller once again? I have added the tag to reference LDS styles.

      Like

  3. Hi Shruti,
    It is really awesome.
    However i am facing an issue.
    Lets say there is no record in a given status then we will get error in vf :
    Map key Closed – Not Converted not found in map
    Error is in expression ‘{!allLeads[status.value]}’ in component in page kanban

    Kindly help me on this..

    Like

    1. Thanks for trying it out. I am glad you liked it.

      I actually tried to replicate this by moving all the cards from one given lane(say the first) to the others to make it empty and then refreshed the page. The page still loaded with that(first) column being EMPTY which means it should still work even if there are no records with a given status.

      Correct ? Or did I get you wrong ?

      Like

      1. Map key Closed – Converted not found in map
        Error is in expression ‘{!allLeads[status.value]}’ in component in page leadkanban

        Like

  4. Hi Shruti,

    Amazing, thank you for sharing..
    One question for you – is there an option to sort the cards within a column according to a number field? I’m asked to generate a Kanban view to show our internal projects and I should be able to sort the cards according to their priority (number field on Project object).

    Thank you very much for the help. Much appreciated.
    Aviv.

    Like

    1. Thanks for the comments. I have handled the same scenario already. The cards in each lane is ordered by field called Kanban_Sort_Order__c which in our case is on Lead object. You could re-purpose the same logic.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s