Gantt Chart view in SharePoint version 2.0

This is the updated version of the Project Gantt Chart view for SharePoint Online.

Over 2 years ago I published a Gantt Chart view for Tasks in SharePoint Online both here and on github. After publishing I have received numerous questions and suggestions and even an alternative version was created by Federico Sapia.

An updated version

The ideas from Federico’s version and suggestions made by several people got me triggered to create an updated version. But, yeah, time flies.
Apparently summer holidays are good for something because I finally made some time to get back to the initial version.

First of all, it is cleaner. The bar is no longer changing colours depending on the state of the task. It is just blue or lightblue, using the same shades of blue as Project for the web. And for the rest, this new version now supports/includes:

  • Milestones
  • Progressbar
  • Clicks to update the progress
  • Task description
  • Task assignment
  • Labels that mover from right to left based on the amount of space.
A Gantt Chart view applied to a modern SharePoint list

To accomedate these changes I needed three more columns.

ColumnnameColumntype
TitleText
ProjectStartDate and time
ProjectDueDate and time
TaskStartDate and time
TaskDueDate and time
ProgressNumber (min: 0, max:100, percentage)
TaskTypeChoice (2 options: Task, Milestone)New
AssignedToUserPeople (Single select)New
TaskDescriptionMultilines of text (no format)New

Please note that whenever you create a column an internal name is assigned to this column. If you change the name of a column, you do not change the internal name. So make sure you use the correct name straight away.

The code

If you don’t know how to create a view and apply the code below, please do check the post of the orginal SharePoint Project Gantt Chart, just make sure that all of the above columns are used in your view.

{
  "$schema": "https://developer.microsoft.com/json-schemas/sp/v2/row-formatting.schema.json",
  "hideSelection": true,
  "hideColumnHeader": true,
  "rowFormatter": {
    "elmType": "div",
    "style": {
      "height": "=if(@rowIndex == 0, '88px', '52px')",
      "display": "block",
      "width": "100%",
      "position": "sticky"
    },
    "attributes": {
      "class": "ms-bgColor-neutralLighter--hover"
    },
    "children": [
      {
        "elmType": "div",
        "_comment": "Header area",
        "attributes": {
          "class": "ms-bgColor-themePrimary"
        },
        "style": {
          "width": "100%",
          "display": "=if(@rowIndex == 0, 'flex', 'none')",
          "height": "36px",
          "padding": "0",
          "font-weight": "bold",
          "border-radius": "6px 6px 0 0"
        },
        "children": [
          {
            "elmType": "div",
            "txtContent": "Task",
            "style": {
              "width": "220px",
              "text-align": "left",
              "padding-left": "0.4em",
              "box-sizing": "border-box"
            },
            "attributes": {
              "class": "ms-fontSize-16 ms-fontColor-white"
            }
          },
          {
            "elmType": "div",
            "style": {
              "flex-grow": "1",
              "height": "100%",
              "position": "relative"
            },
            "children": [
              {
                "elmType": "div",
                "_comment": "ProjectStart label",
                "txtContent": "=toLocaleDateString([$ProjectStart])",
                "style": {
                  "position": "absolute",
                  "padding": "14px 4px 0 4px",
                  "height": "22px",
                  "border-radius": "6px 6px 0 0"
                },
                "attributes": {
                  "title": "='Project Start: ' + toLocaleDateString([$ProjectStart])",
                  "class": "ms-bgColor-themeDarker ms-fontSize-14 ms-fontColor-white"
                }
              },
              {
                "elmType": "div",
                "_comment": "ProjectDue label",
                "txtContent": "=toLocaleDateString([$ProjectDue])",
                "style": {
                  "position": "absolute",
                  "right": "0",
                  "padding": "14px 4px 0 4px",
                  "height": "22px",
                  "border-radius": "6px 6px 0 0"
                },
                "attributes": {
                  "title": "='Project Finish: ' + toLocaleDateString([$ProjectDue])",
                  "class": "ms-bgColor-themeDarker ms-fontSize-14 ms-fontColor-white"
                }
              },
              {
                "elmType": "span",
                "_comment": "CurrentDate label",
                "txtContent": "=toLocaleDateString( @now)",
                "style": {
                  "position": "relative",
                  "width": "90px",
                  "z-index": "100",
                  "display": "=if([$ProjectDue] < @now , 'none', 'block')",
                  "left": "=floor( (Number(@now)-Number([$ProjectStart])) / (Number([$ProjectDue])-Number([$ProjectStart])) * 100 ) + '%'",
                  "background-color": "#e1dfdd",
                  "text-align": "center",
                  "padding": "0 3px",
                  "margin": "0 0 0 3px"
                }
              },
              {
                "elmType": "span",
                "_comment": "Vertical line for today",
                "attributes": {
                  "class": "ms-fontColor-gray40"
                },
                "style": {
                  "position": "relative",
                  "display": "=if( [$ProjectDue] < @now , 'none', 'block')",
                  "top": "-1.3em",
                  "z-index": "1",
                  "border-left": "5px solid",
                  "height": "800px",
                  "width": "0.1em",
                  "left": "=  (Number(@now)-Number([$ProjectStart])) /  (Number([$ProjectDue])-Number([$ProjectStart]))  * 100  + '%' "
                }
              }
            ]
          }
        ]
      },
      {
        "elmType": "div",
        "_comment": "Rows",
        "style": {
          "width": "100%",
          "display": "flex"
        },
        "children": [
          {
            "elmType": "div",
            "_comment": "Row (task) header",
            "style": {
              "width": "220px",
              "display": "flex",
              "flex-wrap": "wrap"
            },
            "attributes": {
              "class": ""
            },
            "children": [
              {
                "elmType": "span",
                "_comment": "Progress circle icon ",
                "style": {
                  "width": "24px",
                  "padding": "6px 0",
                  "text-align": "center",
                  "cursor": "pointer"
                },
                "attributes": {
                  "iconName": "=if([$Progress] >= 1, 'CompletedSolid', if([$Progress] > 0 , 'CortanaLogoReadyOuter', 'CircleRing'))",
                  "title": "='Progress: ' + Number([$Progress] * 100) + '%'",
                  "class": "= 'ms-fontSize-14 ms-fontColor-' + if([$Progress] >= 1, 'green', 'neutralSecondaryAlt')"
                },
                "customRowAction": {
                  "action": "setValue",
                  "actionInput": {
                    "Progress": "=if([$Progress] == 0, if([$TaskType] == 'Task' || [$TaskType] == '', 25 , 100)  , if([$Progress] == 0.25 , 50, if([$Progress] == 0.50, 75, if([$Progress] == 0.75, 100, 0))))"
                  }
                }
              },
              {
                "elmType": "button",
                "_comment": "tasktitle",
                "txtContent": "[$Title]",
                "customRowAction": {
                  "action": "editProps"
                },
                "style": {
                  "width": "196px",
                  "height": "1.6em",
                  "padding": "0.3em 0 0 0",
                  "border": "none",
                  "background-color": "transparent",
                  "cursor": "pointer",
                  "text-decoration": "none",
                  "text-align": "left",
                  "outline": "none",
                  "display": "inline-block",
                  "overflow": "hidden",
                  "text-overflow": "ellipsis",
                  "white-space": "nowrap"
                },
                "attributes": {
                  "title": "[$Title]",
                  "class": "ms-fontSize-14 ms-fontWeight-semibold ms-fontColor-gray140 ms-fontColor-black--hover"
                }
              },
              {
                "elmType": "span",
                "_comment": "Task status warning",
                "style": {
                  "width": "24px",
                  "padding": "1px 0 0 0",
                  "text-align": "center"
                },
                "attributes": {
                  "iconName": "=if( ( ([$TaskType] == 'Task' || [$TaskType] == '') && [$TaskDue]< @now && [$Progress] < 1 ) || ([$TaskType] == 'Milestone' && [$TaskStart]< @now && [$Progress] < 1 )  , 'EventDateMissed12', '')",
                  "title": "This task is running late!",
                  "class": "='ms-fontSize-14 ms-fontColor-' + if([$TaskDue]< @now && [$Progress] < 1, 'sharedRed20', 'themePrimary')"
                }
              },
              {
                "elmType": "div",
                "txtContent": "=toLocaleDateString([$TaskStart])",
                "style": {
                  "width": "96px",
                  "padding": "0.1em 0",
                  "text-align": " center",
                  "heigth": "1.4em",
                  "border-radius": "12px",
                  "margin": "0 2px 0 0 "
                },
                "attributes": {
                  "class": "ms-fontSize-12 ms-bgColor-neutralLight"
                }
              },
              {
                "elmType": "div",
                "txtContent": "=toLocaleDateString([$TaskDue])",
                "style": {
                  "width": "96px",
                  "padding": "0.1em 0",
                  "text-align": " center",
                  "border-radius": "12px",
                  "margin": "0 2px 0 0 "
                },
                "attributes": {
                  "class": "ms-fontSize-12 ms-bgColor-neutralLight"
                }
              }
            ]
          },
          {
            "elmType": "div",
            "_comment": "Ganttchart area",
            "style": {
              "flex-grow": "1",
              "position": "relative",
              "height": "3.3em",
              "padding": "0.3em 0 0 0"
            },
            "attributes": {
              "class": ""
            },
            "children": [
              {
                "elmType": "div",
                "_comment": "NormalTask Bar",
                "txtContent": "",
                "style": {
                  "position": "absolute",
                  "box-sizing": "border-box",
                  "display": "=if([$TaskType] == 'Task' || [$TaskType] == '' , 'flex', 'none')",
                  "border-radius": "6px",
                  "z-index": "10",
                  "top": "0.6em",
                  "height": "2.4em",
                  "overflow": "hidden",
                  "text-overflow": "ellipsis",
                  "border": "1px solid",
                  "border-color": "#0078db",
                  "background-color": "#cfe6f7",
                  "left": "=  (Number([$TaskStart])-Number([$ProjectStart])) / (Number([$ProjectDue])-Number([$ProjectStart])) * 100  + '%'",
                  "width": "= if( [$TaskDue] > [$ProjectDue],  (Number([$ProjectDue])-Number([$TaskStart])+ 86400000) / (Number([$ProjectDue])-Number([$ProjectStart])+ 86400000) * 100  ,  (Number([$TaskDue])-Number([$TaskStart])+ 86400000) / (Number([$ProjectDue])-Number([$ProjectStart])+ 86400000) * 100 ) + '%'"
                },
                "attributes": {
                  "class": "",
                  "title": ""
                }
              },
              {
                "elmType": "div",
                "_comment": "NormalTask Progress Bar",
                "style": {
                  "position": "absolute",
                  "box-sizing": "border-box",
                  "display": "=if(( [$TaskType] == 'Task' || [$TaskType] == '' ) && [$Progress] > 0, 'flex', 'none')",
                  "border-radius": "=if( [$Progress] < 1, '6px 0 0 6px', '6px')",
                  "z-index": "20",
                  "top": "0.6em",
                  "height": "2.4em",
                  "overflow": "hidden",
                  "text-overflow": "ellipsis",
                  "border": "1px solid",
                  "border-color": "#0078db",
                  "background-color": "#0078db",
                  "left": "=  (Number([$TaskStart])-Number([$ProjectStart])) / (Number([$ProjectDue])-Number([$ProjectStart])) * 100  + '%'",
                  "width": "= if( [$TaskDue] > [$ProjectDue],  ((Number([$ProjectDue])-Number([$TaskStart])+ 86400000) / (Number([$ProjectDue])-Number([$ProjectStart])+ 86400000) * 100) * [$Progress] + '%'  ,  ((Number([$TaskDue])-Number([$TaskStart])+ 86400000) / (Number([$ProjectDue])-Number([$ProjectStart])+ 86400000) * 100 * [$Progress]) + '%'"
                },
                "attributes": {
                  "class": "sp-field-bold",
                  "title": "=[$Title] + ' - ' + toLocaleDateString([$TaskStart]) + ' - ' + toLocaleDateString([$TaskDue])"
                }
              },
              {
                "elmType": "div",
                "_comment": "Bar Placeholder for customcard",
                "txtContent": "",
                "style": {
                  "position": "absolute",
                  "box-sizing": "border-box",
                  "border-radius": "6px",
                  "z-index": "50",
                  "top": "0.6em",
                  "height": "2.4em",
                  "overflow": "hidden",
                  "text-overflow": "ellipsis",
                  "border": "=if([$TaskDue]< @now && [$Progress] < 1 && ([$TaskType] == 'Task' || [$TaskType] == '' ), '2px solid #f8b7bd', 'none')",
                  "background-color": "",
                  "left": "=  (Number([$TaskStart])-Number([$ProjectStart])) / (Number([$ProjectDue])-Number([$ProjectStart])) * 100  + '%'",
                  "width": "=if([$TaskType] == 'Milestone', '28px' ,  if( [$TaskDue] > [$ProjectDue],  (Number([$ProjectDue])-Number([$TaskStart])+ 86400000) / (Number([$ProjectDue])-Number([$ProjectStart])+ 86400000) * 100  ,  (Number([$TaskDue])-Number([$TaskStart])+ 86400000) / (Number([$ProjectDue])-Number([$ProjectStart])+ 86400000) * 100 ) + '%'",
                  "margin-left": "=if([$TaskType] == 'Milestone', '-14px' , 0"
                },
                "attributes": {
                  "class": "",
                  "title": ""
                },
                "customCardProps": {
                  "openOnEvent": "hover",
                  "directionalHint": "bottomCenter",
                  "isBeakVisible": true,
                  "beakStyle": {
                    "backgroundColor": "white"
                  },
                  "formatter": {
                    "elmType": "div",
                    "style": {
                      "max-height": "256px",
                      "width": "282px",
                      "display": "flex",
                      "flex-wrap": "wrap",
                      "align-items": "flex-start"
                    },
                    "attributes": {
                      "class": "ms-ContextualMenu-list is-open list-436"
                    },
                    "children": [
                      {
                        "elmType": "div",
                        "txtContent": "[$Title]",
                        "style": {
                          "width": "250px",
                          "height": "40px",
                          "padding": "4px 4px 0 4px",
                          "overflow": "hidden",
                          "text-overflow": "ellipsis"
                        },
                        "attributes": {
                          "class": "ms-fontSize-14 ms-bgColor-themeLight ms-fontWeight-semibold ms-fontColor-gray160"
                        }
                      },
                      {
                        "elmType": "div",
                        "style": {
                          "width": "12",
                          "flex-grow": "1",
                          "height": "34px",
                          "padding": "10px 0 0 0 ",
                          "text-align": "center",
                          "cursor": "pointer"
                        },
                        "attributes": {
                          "iconName": "Edit",
                          "class": "ms-fontSize-14 ms-bgColor-themeLight  ms-fontColor-gray160"
                        },
                        "customRowAction": {
                          "action": "editProps"
                        }
                      },
                      {
                        "elmType": "div",
                        "_comment": "Progress actionbar",
                        "style": {
                          "width": "100%",
                          "display": "flex",
                          "flex-wrap": "wrap",
                          "align-items": "flex-start",
                          "margin": "2px 0"
                        },
                        "children": [
                          {
                            "elmType": "div",
                            "style": {
                              "width": "24px",
                              "padding": "6px 0",
                              "text-align": " center",
                              "heigth": "1.4em"
                            },
                            "attributes": {
                              "iconName": "Timeline"
                            }
                          },
                          {
                            "elmType": "div",
                            "txtContent": "0%",
                            "style": {
                              "padding": "2px 6px",
                              "text-align": " center",
                              "border-radius": "14px",
                              "margin": "2px 2px 0 0 ",
                              "cursor": "pointer",
                              "heigth": "1.4em"
                            },
                            "attributes": {
                              "class": "='ms-fontSize-14  ms-bgColor-themeLight--hover ms-fontColor-themeDarker--hover ms-bgColor-' + if([$Progress] == 0, 'themeTertiary', 'neutralLight')"
                            },
                            "customRowAction": {
                              "action": "setValue",
                              "actionInput": {
                                "Progress": "0"
                              }
                            }
                          },
                          {
                            "elmType": "div",
                            "txtContent": "25%",
                            "style": {
                              "padding": "2px 6px",
                              "text-align": " center",
                              "border-radius": "14px",
                              "margin": "2px 2px 0 0 ",
                              "cursor": "pointer",
                              "heigth": "1.4em"
                            },
                            "attributes": {
                              "class": "='ms-fontSize-14  ms-bgColor-themeLight--hover ms-fontColor-themeDarker--hover ms-bgColor-' + if([$Progress] == 0.25, 'themeTertiary', 'neutralLight')"
                            },
                            "customRowAction": {
                              "action": "setValue",
                              "actionInput": {
                                "Progress": "25"
                              }
                            }
                          },
                          {
                            "elmType": "div",
                            "txtContent": "50%",
                            "style": {
                              "padding": "2px 6px",
                              "text-align": " center",
                              "border-radius": "14px",
                              "margin": "2px 2px 0 0 ",
                              "cursor": "pointer",
                              "heigth": "1.4em"
                            },
                            "attributes": {
                              "class": "='ms-fontSize-14  ms-bgColor-themeLight--hover ms-fontColor-themeDarker--hover ms-bgColor-' + if([$Progress] == 0.5, 'themeTertiary', 'neutralLight')"
                            },
                            "customRowAction": {
                              "action": "setValue",
                              "actionInput": {
                                "Progress": "50"
                              }
                            }
                          },
                          {
                            "elmType": "div",
                            "txtContent": "75%",
                            "style": {
                              "padding": "2px 6px",
                              "text-align": " center",
                              "border-radius": "14px",
                              "margin": "2px 2px 0 0 ",
                              "cursor": "pointer",
                              "heigth": "1.4em"
                            },
                            "attributes": {
                              "class": "='ms-fontSize-14  ms-bgColor-themeLight--hover ms-fontColor-themeDarker--hover ms-bgColor-' + if([$Progress] == 0.75, 'themeTertiary', 'neutralLight')"
                            },
                            "customRowAction": {
                              "action": "setValue",
                              "actionInput": {
                                "Progress": "75"
                              }
                            }
                          },
                          {
                            "elmType": "div",
                            "txtContent": "100%",
                            "style": {
                              "padding": "2px 6px",
                              "text-align": " center",
                              "border-radius": "14px",
                              "margin": "2px 2px 0 0 ",
                              "cursor": "pointer",
                              "heigth": "1.4em"
                            },
                            "attributes": {
                              "class": "='ms-fontSize-14  ms-bgColor-themeLight--hover ms-fontColor-themeDarker--hover ms-bgColor-' + if([$Progress] == 1, 'themeTertiary', 'neutralLight')"
                            },
                            "customRowAction": {
                              "action": "setValue",
                              "actionInput": {
                                "Progress": "100"
                              }
                            }
                          }
                        ]
                      },
                      {
                        "elmType": "div",
                        "_comment": "Warning ICON for Late tasks",
                        "style": {
                          "width": "24px",
                          "display": "=if( ( ([$TaskType] == 'Task' || [$TaskType] == '') && [$TaskDue]< @now && [$Progress] < 1 ) || ([$TaskType] == 'Milestone' && [$TaskStart]< @now && [$Progress] < 1 ) , 'block', 'none')",
                          "padding": "6px 0 7px 0",
                          "text-align": " center",
                          "heigth": "1.4em",
                          "margin": "2px 0"
                        },
                        "attributes": {
                          "iconName": "EventDateMissed12",
                          "class": "sp-field-severity--blocked30 ms-fontSize-14 ms-fontColor-sharedRed20"
                        }
                      },
                      {
                        "elmType": "div",
                        "_comment": "Warning TEXT for Late tasks",
                        "txtContent": "This task is running late!",
                        "style": {
                          "width": "254px",
                          "display": "=if( ( ([$TaskType] == 'Task' || [$TaskType] == '') && [$TaskDue]< @now && [$Progress] < 1 ) || ([$TaskType] == 'Milestone' && [$TaskStart]< @now && [$Progress] < 1 ) , 'block', 'none')",
                          "padding": "4px 0 4px 4px",
                          "text-align": " Left",
                          "heigth": "1.4em",
                          "margin": "2px 0"
                        },
                        "attributes": {
                          "class": "sp-field-severity--blocked30 ms-fontSize-14 ms-fontColor-sharedRed20"
                        }
                      },
                      {
                        "elmType": "div",
                        "style": {
                          "width": "24px",
                          "padding": "6px 0",
                          "text-align": " center",
                          "heigth": "1.4em"
                        },
                        "attributes": {
                          "iconName": "Calendar"
                        }
                      },
                      {
                        "elmType": "div",
                        "_comment": "Task StartdDate in customCard",
                        "txtContent": "=toLocaleDateString([$TaskStart])",
                        "style": {
                          "width": "127px",
                          "padding": "2px 0",
                          "text-align": " center",
                          "border-radius": "14px",
                          "margin": "2px 2px 0 0 ",
                          "height": "1.4em"
                        },
                        "attributes": {
                          "class": "ms-fontSize-14 ms-bgColor-neutralLight"
                        }
                      },
                      {
                        "elmType": "div",
                        "_comment": "Task DueDate in customCard",
                        "txtContent": "=toLocaleDateString([$TaskDue])",
                        "style": {
                          "width": "127px",
                          "padding": "2px 0",
                          "text-align": " center",
                          "border-radius": "14px",
                          "margin": "2px 2px 0 0 ",
                          "height": "1.4em"
                        },
                        "attributes": {
                          "class": "ms-fontSize-14 ms-bgColor-neutralLight"
                        }
                      },
                      {
                        "elmType": "div",
                        "style": {
                          "width": "24px",
                          "padding": "6px 0",
                          "text-align": " center",
                          "heigth": "1.4em"
                        },
                        "attributes": {
                          "iconName": "Contact",
                          "title": "='Assigned to : ' + [$AssignedToUser.title]"
                        }
                      },
                      {
                        "elmType": "div",
                        "txtContent": "[$AssignedToUser.title]",
                        "style": {
                          "width": "254px",
                          "padding": "4px 0 4px 4px"
                        },
                        "defaultHoverField": "[$AssignedToUser]"
                      },
                      {
                        "elmType": "div",
                        "txtContent": "[$TaskDescription]",
                        "style": {
                          "font-style": "italic",
                          "width": "254px",
                          "padding": "4px 0 4px 4px"
                        }
                      }
                    ]
                  }
                }
              },
              {
                "elmType": "div",
                "_comment": "flag for milestone",
                "txtContent": "",
                "style": {
                  "position": "relative",
                  "box-sizing": "border-box",
                  "display": "=if([$TaskType] == 'Milestone', 'flex', 'none')",
                  "z-index": "10",
                  "top": "0.3em",
                  "height": "3em",
                  "left": "=  (Number([$TaskStart])-Number([$ProjectStart])) / (Number([$ProjectDue])-Number([$ProjectStart])) * 100  + '%'",
                  "margin-left": "-14px",
                  "width": "28px"
                },
                "attributes": {
                  "iconName": "=if([$TaskStart]< @now && [$Progress] < 1, 'EventDateMissed12', 'EventDate'",
                  "title": "=[$Title] + ' - ' + toLocaleDateString([$TaskStart]) ",
                  "class": "='ms-fontSize-24 ' + if([$TaskStart]< @now && [$Progress] < 1, 'ms-fontColor-sharedRed20', 'ms-fontColor-themePrimary') "
                }
              },
              {
                "elmType": "div",
                "_comment": "Milestone Date label",
                "txtContent": "=getDate([$TaskStart]) + '-' +  if(getMonth([$TaskStart])==0,'Jan',if(getMonth([$TaskStart])==1,'Feb',if(getMonth([$TaskStart])==2,'Mar',if(getMonth([$TaskStart])==3,'Apr',if(getMonth([$TaskStart])==4,'May',if(getMonth([$TaskStart])==5,'Jun',if(getMonth([$TaskStart])==6,'Jul',if(getMonth([$TaskStart])==7,'Aug',if(getMonth([$TaskStart])==8,'Sep',if(getMonth([$TaskStart])==9,'Oct',if(getMonth([$TaskStart])==10,'Nov',if(getMonth([$TaskStart])==11,'Dec',''))))))))))))",
                "style": {
                  "position": "absolute",
                  "box-sizing": "border-box",
                  "display": "=if([$TaskType] == 'Milestone'  , 'flex', 'none')",
                  "z-index": "3",
                  "top": "10px",
                  "height": "24px",
                  "margin-left": "-60px",
                  "left": "=  (Number([$TaskStart])-Number([$ProjectStart])) / (Number([$ProjectDue])-Number([$ProjectStart])) * 100  + '%'",
                  "width": "60px",
                  "text-align": "right"
                },
                "attributes": {
                  "class": "sp-field-bold ms-fontSize-14"
                }
              },
              {
                "elmType": "div",
                "_comment": "Progress percentagelabel",
                "txtContent": "=(Number[$Progress]) * 100 + '%'",
                "style": {
                  "position": "absolute",
                  "box-sizing": "border-box",
                  "display": "=if([$TaskType] == 'Task' || [$TaskType] == '' , 'flex', 'none')",
                  "z-index": "3",
                  "top": "10px",
                  "height": "24px",
                  "margin-left": "=if((Number([$TaskDue])-Number([$ProjectStart])) / (Number([$ProjectDue])-Number([$ProjectStart])) <= 0.9  , '10px' , '-40px')",
                  "left": "=if((Number([$TaskDue])-Number([$ProjectStart])) / (Number([$ProjectDue])-Number([$ProjectStart])) <= 0.9 , (Number([$TaskDue])-Number([$ProjectStart])) / (Number([$ProjectDue])-Number([$ProjectStart])) * 100  + '%' , (Number([$TaskStart])-Number([$ProjectStart])) / (Number([$ProjectDue])-Number([$ProjectStart])) * 100  + '%' ) ",
                  "width": "40px"
                },
                "attributes": {
                  "class": "sp-field-bold ms-fontSize-14"
                }
              }
            ]
          }
        ]
      }
    ]
  }
}

39 Comments

  1. Hello! Thank you so much for this. I am working on incorporating this into our document review schedule for a yearly basis. Is there a way to segment the chart by quarters where there will be a “task” heading for each quarter? For example:
    1Q2023
    Document A
    Document B

    2Q2023
    Document C
    Document D

    Or would it be best to just create a separate view for each quarter? Also the line specifying the current date seems to cut off halfway down the page. Thank you again!

    1. Hi Shawn,
      Sorry for my late reply.
      I would create multiple views and filter per quarter, it is rather hard to use this together with a grouping option.

      The CurrentDate is displayed at 800 px, just look for :
      “height”: “800px”,
      and increase the value.

      Geert

  2. HI

    That is what I have been wanting for some time to put all the projects I run for a client in visual form.

    One issue can I on one line have an appointment date, then the bar for construction in two different colours, and as per MS Project have a diamond for the appointment date?

  3. Hi Andrew,
    Having 2 options on the same line is not possible in this example.
    It would be feasable to design it, by creating an extra AppointmentDate Column, because all information needs to be stored in the same item.

    Geert

  4. Hi Geert,

    The Gantt Chart isn’t showing corret in neither Teams or SharePoint.
    It look like some columns are compressed and others are too wide.
    I can send you a screen shot.

    /Martin

    1. Hi Geert,
      I deleted the entries I had made in the chart and did it over again and now everything looks fine.

  5. Hi @Jim Allen,
    Well this can certainly be done … but not in this sample.
    To format this all activities need to be savedin the same row. That means you basically have to hardcode the number of columns, so like in your example you have to decide that there will always be three activities, which is most likely not the desired situation.

    The other option is drop all activities in a single multiline text field (plain text, so no format), allowing you to loop through all the activities using a forEach, whilst storing the ProjectStart and ProjectEnd in two separate columns.

    [Activity1Title];[Activity1Start;Activity1End]

    I will keep the idea in mind and when I have more time try to write a sample. I will keep you informed.
    grt Geert

    1. Hi Geert,

      That’s really helpful – thanks!

      Actually, for me there will only ever be 3 activities which I think works pretty well in terms for many people in terms of (say) feasibility, design and implementation.

      What I have is 8 date columns:
      _Start_ (initial notification date)
      StartA( e.g. feasibility start)
      End A (e.g. feasibiity end)
      StartB( e.g. design start)
      End B (e.g. design end)
      StartC( e.g. implementation start)
      EndC (e.g. implementation end)
      _End_ (estimated end date)
      _End_99 (calculated field based on max value of _end_ and EndC

      Activity C will always follow Activity B which will always follow Activity A.

      If there is no data for the sub-activities, data in _Start_ and _End_99 becomes a ‘base progress bar’.

      If there is any data in any of the sub-activities, it can then be ‘overlaid’ on the base progress bar when then acts as a rollup bar.

      I think it’s far better to do this than to embed data for multiple activities in a single field – that would break other kinds of formatting and reporting.

      It allows views to be created containing just dates for specific activities.

      If it’s possible to do this, it should be possible to code as many fixed sub-activities as required I guess.

      I think it would be such a useful feature for so many people.

    2. Hi Geert,

      Many thanks for this great development, which can be very useful. I have the same need than Jim Allen “how to display several sequential bars within the same row”.
      Which part of the code would need to be duplicated to have this visual with fixed columns ?
      Thanks.

      1. Hi @michael,
        At this moment I don’t have the time to develop an additional code.
        Basically you are gonna need all the code that generates the bars, so in the code
        starting from
        “_comment”: “Ganttchart area”,
        it creates the different chart elements
        So if you copy the block starting from
        “_comment”: “NormalTask Bar”,
        and then adapt the references to $TaskStart and $TaskDue towards your own columns it should render correctly.

        Sorry for not being able to create the code at this point, simply a matter of not having anough spare time.
        Geert

        1. @Geert
          Thanks for the info. I will make a try.
          Don’t worry, what you do and sharing it with others is already great.

        2. Hi Geert,

          I tried and managed to duplicate the first version of your code (not this new version) and it works : there are the sequential activities.

          I still have one issue: the display of the bars are not on the same row, but below each other. I don’t know if this is because the start date of the following activity is the same as the end date of the previous one or if there is another reason ?

          Do you eventually have an idea of what is going wrong and how to have the bars on the same row ?

          Many thanks

          1. Hi Michael,
            The bars are in div’s and therefore the positioning woudld force it to move to the next row. so that would be normal.
            Withou seeing you code… try to set the positioning to absolute and the top to 0 or something close to zero

            position": "absolute",
            "top": "0.6em"

          2. Hi Geert,
            I modified according to your suggestion “relative” => “absolute” with “top: 0.2” and it works. Many thanks for your help.
            Have a nice day.

    1. Hi Jason,
      Most Lively, you didn’t use the correct names when you created the columns.
      This kind of formatting is always referring to the internal names of the columns. You can see these names when you go to the library settings and click on a column, you will see the internal name in the address bar.

      Internal names can not be changed.
      So you have to make them correct from. The beginning.

      In the original article this is explained.

      Does this help?

      Geert

      1. Hi Aldi,
        It is almost always the same issue, you did not use the proper column names, and renaming columns doesn’t help.

        When you create a column in SharePoint an internal name is assigned, this name is not always the same thing as what you typed, plus this name will never change. So if you made a mistake with a specific column… delete it en create it again.
        Column names are also case sensitive.

        Geert

    1. Hi Jason,
      You can have as many columns as you like withou impacting the Gantt Chart?
      The only thing you have to take into account is that the gantt chart columns are active in the view.

      grt Geert

  6. Hi, so I tried using a blank list and it seems to work. I was trying to upload a spreadsheet using columns named as required but looks like there is some residual that makes the code not work when applying to a list created from a spreadsheet

    1. Hi Jason,
      Importing directly from Excel, seems to me as a bad idea because you have no control ove the column names.
      grt Geert

  7. Hello Geert,

    Can the code recognize project start time and end time in hh:mm format?

    Thank you,

    Alvin

    1. Hi Alvin,
      No not by default.
      But if you are willing to use the SharePoint dd/mm/yyyy hh:mm or mm/dd/yyyy hh:mm the code can be adopted to your needs.
      You probably want to change the toLocaleDateString() instances to toLocaleString() or toLocaleTimeString().

      In the code you see a few times the number 8640000 this is the number of milliseconds in a day. If you want to work with minutes you probably need to change it to 6000 (the number milliseconds per minute)

      I hope this helps
      Grt Geert

    1. Hi Jose Manuel,
      You can’t actually change the internal names. The internal names are assigned tje moment you create the column.
      So can you create a new sample and start using the proper columnnames from the start.

      You can actually see the internal columnnames when you go to the list settings and click on the column. The addressbar shows the internalcolumnname at the end of url.

      Let me know if this helpt.
      Geert

      1. Hi Geert, thank you very much, I followed your instructions and it works OK.

        Thanks,
        Jose Manuel

  8. Amazing work… This is exactly what I need to get a jump start on my custom list!

    I made some fairly hefty changes. Mine allows for groups and it is also a static view. The items on the screen will move to the left as time goes by. It’s currently set to -30 days and +60 days from current date. This range is currently set in the JSON, but I plan to make this a bit more user friendly in the future.

    I just started this last week and still plan to add some additional functionality to have items filter out once they are completed and the due date is beyond the past 30 days. I would gladly share if anyone is interested in my version.

    I created a a couple additional columns. One to group tasks to have multiple projects. The other is a calculated field to determine if the progress of the task is on target or not.

    Here is a link to a screenshots I shared on Imgur.
    https://imgur.com/a/yAPHHaF

    1. Hi Jorge,
      That is a very nice result.
      I am always interested in other code samples and the extra columns you have used. And to see how you tackled some of the challenges.

      It would be really awesome if you would share it on github.
      Either as a new example, or as a second code in the existing example that I started there: https://github.com/pnp/List-Formatting/tree/master/view-samples/project-gantt-chart

      If you don’t know how to start I can help you out, or bring you into contact with some of the moderators there.

      Geert

      1. To be honest, I’m not very experienced with GitHub, besides just finding some samples.

        Not sure if it’s user error or permissions, but I’m unable to create a new fork. For now, I created a new issue specific to my changes rather than posting under an older issue.

        If you can offer any help, I would really appreciate it. If not, guess I can create a separate project if needed.

        Link to new Issue with my updates.
        https://github.com/pnp/List-Formatting/issues/772

    2. Hi Jorge, your build on Geert’s awesome work sounds really useful to me.

      I want to be able to use this view to visualise our portfolio of work, instead of individual tasks within a project. So, having the option to group line items together would be a big help.

      I would love to get a copy of your code, if you’re happy to share, either directly or via github.

      1. If you decided to use the updated version I created, please use the JSON from this link instead.

        https://github.com/pnp/List-Formatting/issues/772

        I removed the calculated column due to issues with using Today() in the calculations. That function has been moved into the JSON script to ensure the expected percentage remains current and accurate.

Leave a Reply

Your email address will not be published. Required fields are marked *