Kanban drag and drop Lightning Component - reusable


Usage: 
<c:dragAndDrop objLabel="Opportunity" objName="Opportunity" objFields="['Name', 'AccountId', 'Account.Name', 'CloseDate', 'StageName', 'Amount']" kanbanPicklistField="StageName"/>

dragAndDrop.cmp
 <aura:component controller="kanbanController">
    <aura:attribute name="objName" type="String"/>
    <aura:attribute name="objFields" type="String[]"/>
    <aura:attribute name="kanbanPicklistField" type="String"/>
    
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    <aura:attribute name="kanbanData" type="kanbanController.kanbanWrap"/>
    
    <div class="slds-page-header">
        <div class="slds-media">
            <div class="slds-media__figure">
                <span class="slds-icon_container" title="Description of icon when needed">
                    <lightning:icon iconName="utility:kanban" variant="bare" size="small"/>
                </span>
            </div>
            <div class="slds-media__body">
                <h1 class="slds-page-header__title slds-truncate slds-align-middle" title="Kanban - Drag &amp; Drop">Kanban - Drag &amp; Drop : {!v.objName}</h1>
            </div>
        </div>
    </div>
    <div style="padding:0.5rem;">
    
    
    <aura:iteration var="pickVal" items="{!v.kanbanData.pickVals}">
        <div class="stageContainer" style="{!'width:calc(100vw/'+(v.kanbanData.pickVals.length+0.5)+')'}">
            <div class="slds-grid slds-grid_vertical">
                <div>
                    <div class="slds-media slds-no-space slds-has-divider_bottom-space slds-media_center" style="{!'width:calc(100vw/'+(v.kanbanData.pickVals.length+1)+')'}">
                        <div class="slds-media__body">
                            <h1 class="slds-page-header__title slds-align-middle slds-truncate" title="{!pickVal}">{!pickVal}</h1>
                        </div>
                    </div>
                </div>
            </div>
            <ul ondrop="{!c.drop}" ondragover="{!c.allowDrop}" class="slds-has-dividers_around-space dropZone" data-Pick-Val="{!pickVal}" style="height:70vh;overflow-y:auto;">
                <aura:iteration var="objRecord" items="{!v.kanbanData.records}">
                    <aura:if isTrue="{!pickVal == objRecord.StageName}">
                        <li class="slds-item slds-m-around_small" draggable="true" ondragstart="{!c.drag}" id="{!objRecord.Id}">
                            <article class="slds-tile slds-tile_board">
                                <h3 class="slds-truncate" title="{!objRecord.Name}">
                                    <a href="javascript:void(0);" onclick="{!c.doView}">
                                        <span class="slds-truncate" id="{!objRecord.Id}">{!objRecord.Name}</span>
                                    </a>
                                </h3>
                                <div class="slds-tile__detail slds-text-body_small">
                                    <p class="slds-text-heading_medium">Amount: ${!objRecord.Amount}</p>
                                    <p class="slds-truncate" title="{!objRecord.Account.Name}">
                                        <a href="javascript:void(0);" onclick="{!c.doView}">
                                            <span class="slds-truncate" id="{!objRecord.AccountId}">{!objRecord.Account.Name}</span>
                                        </a>
                                    </p>
                                    <p class="slds-truncate" title="{!'Closing ' +objRecord.CloseDate}">Closing {!'Closing ' +objRecord.CloseDate}</p>
                                </div>
                            </article>
                        </li>
                    </aura:if> 
                </aura:iteration>
            </ul>
        </div>
    </aura:iteration>
   </div>
</aura:component>



dragAndDropController.js
({
    doInit: function(component, event, helper) {
        var action = component.get("c.getKanbanWrap");
        action.setParams({
            "objName":component.get("v.objName"),
            "objFields":component.get("v.objFields"),
            "kanbanField":component.get("v.kanbanPicklistField")
        });
        action.setCallback(this, function(response){
            var state = response.getState();
            if (state === "SUCCESS") {
                console.dir(response.getReturnValue());
                component.set("v.kanbanData", response.getReturnValue());
            }
        });
        $A.enqueueAction(action);
    },
    doView: function(component, event, helper) {
        var editRecordEvent = $A.get("e.force:navigateToSObject");
        editRecordEvent.setParams({
            "recordId": event.target.id
        });
        editRecordEvent.fire();
    },
    allowDrop: function(component, event, helper) {
        event.preventDefault();
    },
    
    drag: function (component, event, helper) {
        event.dataTransfer.setData("text", event.target.id);
    },
    
    drop: function (component, event, helper) {
        event.preventDefault();
        var data = event.dataTransfer.getData("text");
        var tar = event.target;
        while(tar.tagName != 'ul' && tar.tagName != 'UL')
            tar = tar.parentElement;
        tar.appendChild(document.getElementById(data));
        console.log('aaaaaaaaaaaaa   :   ' + tar.getAttribute('data-Pick-Val'));
        document.getElementById(data).style.backgroundColor = "#ffb75d";
        helper.updatePickVal(component,data,component.get("v.kanbanPicklistField"),tar.getAttribute('data-Pick-Val'));
    }
})

dragAndDropHelper.js
({
 updatePickVal : function(component, recId, pField, pVal) {
        //Id recId, String kanbanField, String kanbanNewValue
  var action = component.get("c.getUpdateStage");
        action.setParams({
            "recId":recId,
            "kanbanField":pField,
            "kanbanNewValue":pVal
        });
        action.setCallback(this, function(response){
            var state = response.getState();
            if (state === "SUCCESS") {
                console.log(response.getReturnValue());
                document.getElementById(recId).style.backgroundColor = "#04844b";
                setTimeout(function(){ document.getElementById(recId).style.backgroundColor = ""; }, 300);
            }
        });
        $A.enqueueAction(action);
 }
})


dragAndDrop.css
.THIS {
    
} 
.THIS .slds-item {
    border: 1px solid #d8dde6;
    border-radius: 0.25rem;
    background-clip: padding-box;
    padding: 0.45rem;
    margin: 0.25rem;
}
.THIS .stageContainer{
 /*   width:320px; */
    float:left;
    border: 2px solid #d8dde6;
    margin: 0.05rem;
    border-radius: .25rem;
}
.THIS .slds-page-header__title {
    padding: 0rem 0.5rem;
    background: #f7f9fb;
}


.THIS .dropZone{
    min-height:25rem;
}

kanbanController.apxc
public class kanbanController {
    
    @AuraEnabled
    public static kanbanWrap getKanbanWrap(String objName, String[] objFields, String kanbanField) {
        List<String> lstPickvals=new List<String>();
        for (Schema.PicklistEntry a : Schema.getGlobalDescribe().get(objName).getDescribe().fields.getMap().get(kanbanField).getDescribe().getPickListValues())
            lstPickvals.add(a.getValue());
        System.debug(lstPickvals);
        
        String query = 'SELECT Id, ';
        for(String s:objFields){
            query += s+' ,';
        }
        query = query.removeEnd(',');
        query += ' FROM ' + objName;
        System.debug('qq  ' + query);
        
        return new kanbanWrap(Database.query(query), lstPickvals);
    }
    
    @AuraEnabled
    public static String getUpdateStage(Id recId, String kanbanField, String kanbanNewValue) {
        SObject o1 = recId.getSObjectType().newSObject(recId);
        o1.put(kanbanField,kanbanNewValue);
        update o1;
        return 'Success';
    }
    
    
    
    public class kanbanWrap{
        @AuraEnabled
        List<sObject> records {get;set;}
        @AuraEnabled
        List<String> pickVals {get;set;}
        
        public kanbanWrap(List<sObject> recs, List<String> pVals){
            this.records = recs;
            this.pickVals = pVals;
        }
    }
    
}

Comments