Question

I am trying to create a Kanban/task manager that allows users to move 'Cards' from one status to another in order to reflect the current status of a task. Below is the code, but the problem is that the changes made when a card is moved are not persistent -- enter image description hereif the page is refreshed, all of the activity is reset. I'm attaching a screenshot for reference.

<!DOCTYPE html>
    <html>
       <head>
          <meta charset = "utf-8">
          <title>jQuery UI Widget - Default functionality</title>
          <link rel = "stylesheet" href = "//code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
          <script src = "https://code.jquery.com/jquery-1.10.2.js"></script>
          <script src = "https://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
    <!-- css --> 
     <style>
      body {font-family:Roboto;}
      h2 {margin:5px;}
      input[type=text] {margin:10px}
      input[type=button] {margin:10px}  
      .container {width: 20%; float:left;clear: right;margin:10px; border-radius: 5px;}
      .sortable { list-style-type: none; margin:0; padding:2px; min-height:30px; border-radius: 5px;}
      .sortable li { margin: 3px 3px 3px 3px; padding: 0.4em; padding-left: 1.5em; font-size: 1.4em; height: 18px;}
      .sortable li span { position: absolute; margin-left: -1.3em; }
      .card{background-color:white;border-radius:3px;}
      .head-lt {color:#fcfcf4;}
      .head-dk {color:#37474F;}
      </style>
      <script>
      $(function() {
        $( ".sortable" ).sortable({
          connectWith: ".connectedSortable",
          receive: function( event, ui ) {
            $(this).css({"background-color":"#fcfcff4"});
          }
        }).disableSelection();
        $('.add-button').click(function() {
            var txtNewItem = $('#new_text').val();
            $(this).closest('div.container').find('ul').append('<li class="card">'+txtNewItem+'</li>');
        });    
      });
      </script>      
    </head>
    <body>
    <div>
    <div class="container" style="background-color:#EF4931;">
    <h2 class="head-lt">Not started</h2>
    <ul class="sortable connectedSortable">
      <li class="card">Activity A1</li>
      <li class="card">Activity A2</li>
      <li class="card">Activity A3</li>
    </ul>
    <div class="link-div">
        <input type="text" id="new_text" value=""/>
        <input type="button" name="btnAddNew" value="Add" class="add-button"/>
    </div>
    </div>
    <div class="container" style="background-color:#ffd300;">
    <h2 class="head-dk">In-progress</h2>
    <ul class="sortable connectedSortable" >
      <li class="card">Activity B1</li>
      <li class="card">Activity B2</li>
    </ul>
    </div>
    <div class="container" style="background-color:#103c6d;">
    <h2 class="head-lt">Final stage</h2>
    <ul class="sortable connectedSortable" >
      <li class="card">Activity C1</li>
      <li class="card">Activity C2</li>
    </ul>
    </div>
    <div class="container" style="background-color:#B9CEC5;">
    <h2 class="head-dk">Completed</h2>
    <ul class="sortable connectedSortable" >
    </ul>
    </div>
    </div>
    
    <script type="text/javascript">
    function storageAvailable(type) {
        var storage;
        try {
            storage = window[type];
            var x = '__storage_test__';
            storage.setItem(x, x);
            storage.removeItem(x);
            return true;
        }
        catch(e) {
            return e instanceof DOMException && (
                // everything except Firefox
                e.code === 22 ||
                // Firefox
                e.code === 1014 ||
                // test name field too, because code might not be present
                // everything except Firefox
                e.name === 'QuotaExceededError' ||
                // Firefox
                e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
                // acknowledge QuotaExceededError only if there's something already stored
                (storage && storage.length !== 0);
        }
    }
    </script>
    
    
    </body>
    </html>
Was it helpful?

Solution

You need to create a list to store this web part data. When you add a new value, we could create a new item in the list, when you move the option, we could update the list item in list.

My test code for your reference:

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>jQuery UI Widget - Default functionality</title>
  <link rel="stylesheet" href="//code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
  <script src="https://code.jquery.com/jquery-1.10.2.js"></script>
  <script src="https://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
  <!-- css -->
  <style>
    body {
      font-family: Roboto;
    }

    h2 {
      margin: 5px;
    }

    input[type=text] {
      margin: 10px
    }

    input[type=button] {
      margin: 10px
    }

    .container {
      width: 20%;
      float: left;
      clear: right;
      margin: 10px;
      border-radius: 5px;
    }

    .sortable {
      list-style-type: none;
      margin: 0;
      padding: 2px;
      min-height: 30px;
      border-radius: 5px;
    }

    .sortable li {
      margin: 3px 3px 3px 3px;
      padding: 0.4em;
      padding-left: 1.5em;
      font-size: 1.4em;
      height: 18px;
    }

    .sortable li span {
      position: absolute;
      margin-left: -1.3em;
    }

    .card {
      background-color: white;
      border-radius: 3px;
    }

    .head-lt {
      color: #fcfcf4;
    }

    .head-dk {
      color: #37474F;
    }
  </style>
  <script>
    $(function () {
      getListData();
      var id;
      $(".sortable").sortable({
        connectWith: ".connectedSortable",
        receive: function (event, ui) {
          console.log($(ui.item[0]).parent().prev()[0].innerText)
          $(this).css({ "background-color": "#fcfcff4" });
          UpdateItem(id,$(ui.item[0]).parent().prev()[0].innerText);
        },

        update: function (event, ui) {
          //id = $($(this).children()[0]).attr("idvalue")
          id = ui.item.attr('idvalue');
          
          //console.log($($(this).children()[0]).closest("h2").val())
        }
      }).disableSelection();
      $('.add-button').click(function () {
        CreateListItemWithDetails();
        var txtNewItem = $('#new_text').val();
        $(this).closest('div.container').find('ul').append('<li class="card">' + txtNewItem + '</li>');
        location.reload();
      });
    });
    function getListData() {

      var fullUrl = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getByTitle('test5')/items?$selet=Title,Status,ID";
      console.log(fullUrl);
      $.ajax({
        url: fullUrl,
        type: "GET",
        headers: {
          "accept": "application/json;odata=verbose",
          "content-type": "application/json;odata=verbose",
        },
        success: onQuerySucceeded,
        error: onQueryFailed
      });
    }

    function onQuerySucceeded(data) {

      $.each(data.d.results, function (key, value) {
        if (value.Status == "Not started") {
          var listItemInfo = "<li class='card' IDvalue=" + value.ID + ">" + value.Title + "</li>";
          $("#NotStarted").append(listItemInfo)
        }
        if (value.Status == "In-progress") {
          var listItemInfo = "<li class='card' IDvalue=" + value.ID + ">" + value.Title + "</li>";
          $("#InProgress").append(listItemInfo)
        }
        if (value.Status == "Final stage") {
          var listItemInfo = "<li class='card' IDvalue=" + value.ID + ">" + value.Title + "</li>";
          $("#FinalStage").append(listItemInfo)
        }
        if (value.Status == "Completed") {
          var listItemInfo = "<li class='card' IDvalue=" + value.ID + ">" + value.Title + "</li>";
          $("#Completed").append(listItemInfo)
        }


      });

    }
    function onQueryFailed() {
      alert('Error!');
    }
    function CreateListItemWithDetails() {
      var itemType = GetItemTypeForListName("test5");
      var item = {
        "__metadata": { "type": itemType },
        "Title": $('#new_text').val(),
        "Status": "Not started"

      };

      $.ajax({
        url: _spPageContextInfo.siteAbsoluteUrl + "/_api/web/lists/getbytitle('test5')/items",
        type: "POST",
        contentType: "application/json;odata=verbose",
        data: JSON.stringify(item),
        headers: {
          "Accept": "application/json;odata=verbose",
          "X-RequestDigest": $("#__REQUESTDIGEST").val()
        },
        success: function (data) {
          console.log(data);
        },
        error: function (data) {
          console.log(data);
        }
      });
    }

    // Get List Item Type metadata
    function GetItemTypeForListName(name) {
      return "SP.Data." + name.charAt(0).toUpperCase() + name.split(" ").join("").slice(1) + "ListItem";
    }

    function UpdateItem(id,newStatus) {
      var itemType = GetItemTypeForListName("test5");
      var item = {
        "__metadata": { "type": itemType },        
        "Status": newStatus
      };
      $.ajax({
        url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/GetByTitle('test5')/items(" + id + ")",
        type: "POST",
        headers: {
          "accept": "application/json;odata=verbose",
          "X-RequestDigest": $("#__REQUESTDIGEST").val(),
          "content-Type": "application/json;odata=verbose",
          "IF-MATCH": "*",
          "X-HTTP-Method": "MERGE"
        },
        data: JSON.stringify(item),
        /*where Title is column name and add your desired new data*/
        success: function (data) {
          console.log(data);
        },
        error: function (error) {
          alert(JSON.stringify(error));
        }
      });
    }
  </script>
</head>

<body>
  <div>
    <div class="container" style="background-color:#EF4931;">
      <h2 class="head-lt">Not started</h2>
      <ul class="sortable connectedSortable" id="NotStarted">
      </ul>
      <div class="link-div">
        <input type="text" id="new_text" value="" />
        <input type="button" name="btnAddNew" value="Add" class="add-button" />
      </div>
    </div>
    <div class="container" style="background-color:#ffd300;">
      <h2 class="head-dk">In-progress</h2>
      <ul class="sortable connectedSortable" id="InProgress">

      </ul>
    </div>
    <div class="container" style="background-color:#103c6d;">
      <h2 class="head-lt">Final stage</h2>
      <ul class="sortable connectedSortable" id="FinalStage">

      </ul>
    </div>
    <div class="container" style="background-color:#B9CEC5;">
      <h2 class="head-dk">Completed</h2>
      <ul class="sortable connectedSortable" id="Completed">
      </ul>
    </div>
  </div>

  <script type="text/javascript">
    function storageAvailable(type) {
      var storage;
      try {
        storage = window[type];
        var x = '__storage_test__';
        storage.setItem(x, x);
        storage.removeItem(x);
        return true;
      }
      catch (e) {
        return e instanceof DOMException && (
          // everything except Firefox
          e.code === 22 ||
          // Firefox
          e.code === 1014 ||
          // test name field too, because code might not be present
          // everything except Firefox
          e.name === 'QuotaExceededError' ||
          // Firefox
          e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
          // acknowledge QuotaExceededError only if there's something already stored
          (storage && storage.length !== 0);
      }
    }
  </script>


</body>

</html>

Test result: enter image description here Store list:

enter image description here

OTHER TIPS

What you did so far is the implementation of the user interface to display data in kanban based on data in the list. The way all the current kanban products works are that as soon as you move an activity from one card to another or change the order of activity...it will emit an event that will tell you what the activity was performed by the user.

You have to write event handlersr of this movement of activities among cards and then make REST API call to update data back to SharePoint with new values, in your case you will have to change the status of that item/activity to where it was moved... So once user does any activity data is udpated to List and on-page refresh it will make a fresh call and load change

Reference link for Calling SharePoint REST API - https://docs.microsoft.com/en-us/sharepoint/dev/sp-add-ins/working-with-lists-and-list-items-with-rest

Hope this helps..!

Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top