Biweekly View with DHTMLX Gantt Chart

200 Views Asked by At

In my dhtmlx gantt chart, I've configured my scales to show the year and Quarter (3 PIs per quarter)

gantt.config.scales = [
    { name: 'year', format: '%Y' },
    { name: 'quarter', template: date => `PI-${(Math.floor((new Date(date).getMonth() / 3)) + 1 )}` },
]

This gives me the result

|             2022             |
|   PI-1  |   PI-2   |  PI-3   |

I now want to add 2-week increments to my scale to represent sprints per Quarter (6 sprints per quarter, 24 sprints per year)

|             2022             |
|  PI-1   |   PI-2   |  PI-3   |
| S1 | S2 | S3 |  S4 | S5 | S6 |

I can't figure out how to structure my template to achieve this. Any thoughts?

1

There are 1 best solutions below

0
Maksim Lakatkou On

It should be noted that if the increment is 2 weeks, then there will be more than 24 sprints in a year. The same can be said about months, one quarter is equal to 3 months, which contain a non-integer number of weeks, so the boundaries of the quarter and sprint scales cannot coincide. To set sprints in the scale, you need to use Custom time units. And so I can offer the following solution:

If the increment is to be strictly 2 weeks, first determine the start date of each sprint. It is also necessary to set the start for the first sprint, for example, I set it to start on Monday of the first week of the year:

// specify the start date of the first sprint
gantt.config.project_start = new Date(2022, 0, 3);

// find a sprint for a given date
function findSprint(date) {
    const firstSprint = gantt.date.week_start(new Date(gantt.config.project_start));
    let currentDate = firstSprint;
    let direction = 1;

    if (date < firstSprint) {
        direction = -1;
    }
    const increment = 2 * direction;

    let nextDate = gantt.date.add(currentDate, increment, "week");
    let num = 0;
    
    while (!(currentDate.valueOf() <= date.valueOf() && nextDate.valueOf() > date.valueOf())) {
        if (increment > 0) {
            currentDate = nextDate;
            nextDate = gantt.date.add(currentDate, increment, "week");
        } else {
            nextDate = currentDate;
            currentDate = gantt.date.add(currentDate, increment, "week");
        }
        num += 1 * direction;
    }

    return {
        sprintStart: currentDate,
        sprintEnd: nextDate,
        sprintNumber: num
    }
}

// custom scale unit definition
gantt.date.sprint_start = function (date) {
    return findSprint(date).sprintStart;
};

The next step is to specify that the increment will be two weeks:

gantt.date.add_sprint = function (date, inc) {
    return gantt.date.add(gantt.date.sprint_start(date), inc * 2, "week");
};

And finally, add a new unit to the scale:

gantt.config.scales = [
    { unit: "year", step: 1, format: "%Y" },
    {
        unit: 'quarter',
        format: date => {
            return `PI-${(Math.floor((new Date(date).getMonth() / 3)) + 1)}`
        }
    },
    { unit: 'sprint', step: 1, template: function (date) {
            const sprintInfo = findSprint(date);
            return `Sprint ${sprintInfo.sprintNumber + 1}, (${gantt.templates.date_grid(sprintInfo.sprintStart)} - ${gantt.templates.date_grid(new Date(sprintInfo.sprintEnd - 1))})`
        }
    }
];

Please see an example: https://snippet.dhtmlx.com/15u2bd85.

Sometimes for some reason the borders of the quarter and year scales do not match, and this is most likely a bug, if this happens to you, please contact dhtmlx technical support.

If you want to have 24 sprints in a year, you can set specific dates for each sprint, it might look something like this:

const sprints = [
    { name: 'S1',  start_date: new Date(2022,00,01), end_date: new Date(2022,00,15) },
    { name: 'S2',  start_date: new Date(2022,00,15), end_date: new Date(2022,01,01) },
    { name: 'S3',  start_date: new Date(2022,01,01), end_date: new Date(2022,01,15) },
    { name: 'S4',  start_date: new Date(2022,01,15), end_date: new Date(2022,02,01) },
    { name: 'S5',  start_date: new Date(2022,02,01), end_date: new Date(2022,02,15) },
    { name: 'S6',  start_date: new Date(2022,02,15), end_date: new Date(2022,03,01) },
    ...
];

You need to add the given sprints to the unit:

gantt.date.sprints_start = function(date) {
    return date;
};

function getSprint(date,type) {
    const tempDate = new Date(date);

    for (let i = 0; i < sprints.length; i++) {
        if (+tempDate >= +sprints[i].start_date && +tempDate < +sprints[i].end_date) {
            if (type == 'scaleUnit') {
                return sprints[i].end_date;
            }

            if (type == 'template') {
                return "<div class='sprint'>"+sprints[i].name+"</div>";
            }
        }
    }

    if (type == 'scaleUnit') {
        const newDate = gantt.date.add(date, 1,'day');

        return newDate;
    }

    if (type == 'template') {
        return gantt.date.date_to_str("%m-%d")(date);
    }
}

gantt.date.add_sprints = function(date, inc) {
    return getSprint(date, 'scaleUnit');
};

const sprintsTemplate = function(date) {
    return getSprint(date,'template');
}

and also add a new unit to the scale:

gantt.config.scales = [
    { unit: "year", step: 1, format: "%Y" },
    {
        unit: 'quarter',
        format: date => {
            return `PI-${(Math.floor((new Date(date).getMonth() / 3)) + 1 )}`
        }
    },
    { unit: 'month', step: 1, format: "%M" },
    { unit: 'sprints', step: 1, template: sprintsTemplate },
];

Here is an example: https://snippet.dhtmlx.com/0xznw5m9