Premium support for our pure JavaScript UI components


Post by rocketreading »

Hi Team,

Is there a way to showcase the holidays specific to resources in the Resource utilisation view, so that planning can be done in a proper way and those days should be marked as blocked for any planning.


Post by ghulam.ghous »

Hey there,

Have you tried using the resourceCalendars? I believe this is what you are looking for. See this: https://bryntum.com/products/gantt/docs/api/Gantt/model/ResourceModel#field-calendar

Read this section here: https://bryntum.com/products/gantt/docs/guide/Gantt/basics/calendars#resource-calendars


Post by rocketreading »

Could you please provide any examples for the same? I want to see the implementation way for the same.


Post by ghulam.ghous »

Hey there,

I have checked deeply now. Though in the resourceUtilization widget no visualization for resourceCalendars is available. We have a FR for that: https://github.com/bryntum/support/issues/4274

But the resource calendars does take part in the scheduling. For example, try this example:

const project = new ProjectModel({
    taskModelClass : Task,

    loadUrl      : 'data/load.json',
    startDate    : '2024-01-16',
    calendar     : 'general',
    daysPerWeek  : 7,
    daysPerMonth : 30,
    hoursPerDay  : 24,
    calendars    : [
        {
            id        : 'general',
            name      : 'General',
            intervals : [
                {
                    recurrentStartDate : 'on Sat',
                    recurrentEndDate   : 'on Mon',
                    isWorking          : false
                }
            ]
        },
        {
            id                       : 'late',
            name                     : 'Day shift',
            unspecifiedTimeIsWorking : false,
            intervals                : [
                {
                    recurrentStartDate : 'at 16:00',
                    recurrentEndDate   : 'at 17:00',
                    isWorking          : true
                }
            ]
        }
    ],
    assignments : [
        {
            id       : 1,
            event    : 11,
            resource : 1,
            units    : 100
        }
    ],
    resources : [
        { id : 1, name : "Celia's team", city : 'Barcelona', calendar : 'late', image : 'celia.jpg', maxUnits : 300 }
    ],
    tasks : [
        {
            id             : 1000,
            startDate      : '2024-01-16',
            name           : 'Project A',
            isProjectTask  : true,
            note           : 'Project A description',
            percentDone    : 48.627450980392155,
            duration       : 20,
            iconCls        : 'b-icon projectIcon',
            expanded       : true,
            constraintType : 'startnoearlierthan',
            constraintDate : '2024-01-16',
            children       : [
                {
                    id          : 1,
                    name        : 'Planning',
                    percentDone : 50,
                    startDate   : '2024-01-16',
                    duration    : 10,
                    expanded    : true,
                    rollup      : true,
                    children    : [
                        {
                            id          : 11,
                            name        : 'Investigate',
                            percentDone : 50,
                            cls         : 'LowPrio',
                            startDate   : '2024-01-16',
                            duration    : 8,
                            segments    : [
                                {
                                    id        : 1,
                                    startDate : '2024-01-16',
                                    duration  : 1
                                },
                                {
                                    id        : 2,
                                    startDate : '2024-01-18',
                                    duration  : 2
                                },
                                {
                                    id        : 3,
                                    startDate : '2024-01-23',
                                    duration  : 5
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ],
    autoSetConstraints : true,

    // This config enables response validation and dumping of found errors to the browser console.
    // It's meant to be used as a development stage helper only so please set it to false for production systems.
    validateResponse : true
});

const gantt = new Gantt({
    project,
    dependencyIdField       : 'sequenceNumber',
    resourceImageFolderPath : '../_shared/images/users/',
    appendTo                : 'container',
    viewPreset              : 'weekAndDayLetter',
    tickSize                : 40,
    columnLines             : true,
    startDate               : '2024-01-16',
    columns                 : [
        { type : 'name', width : 280 },
        { type : 'resourceassignment', showAvatars : true, width : 170 }
    ]
});

new Splitter({
    appendTo : 'container'
});

// prepare array of functions we are going to group the view store by
// (we make a constant since we are going to use it in few places)
const resourceNProjectGroupFns = [
    // group by resource
    ({ origin }) => {
        // If record is a resource means it has no assignments ..since this function is called for leaves only.
        // So further grouping makes no sense for this record - stop grouping.
        if (origin.isResourceModel) {
            return Store.StopBranch;
        }

        return origin.resource;
    },
    // group by the task project
    ({ origin }) => {
        // If record is a resource means it has no assignments since this function is called for leaves only.
        // So further grouping makes no sense for this record - stop grouping.
        if (origin.isResourceModel) {
            return Store.StopBranch;
        }

        return origin.event?.projectTask || 'No Project';
    }
];

const resourceUtilization = new ResourceUtilization({
    appendTo                : 'container',
    project,
    partner                 : gantt,
    rowHeight               : 40,
    showBarTip              : true,
    resourceImageFolderPath : '../_shared/images/users/',
    features                : {
        treeGroup : {
            levels : resourceNProjectGroupFns
        }
    },

    // Display either allocated time (default) or time left in bars
    getBarText({ effort, maxEffort }, index, series, renderData) {
        const view = this.owner;

        // default text
        let result = view.getBarTextDefault(...arguments);

        // If we have "Show time left" checked
        // and both spent and max time are provided
        if (view.widgetMap.showTimeLeft.checked && maxEffort && effort) {

            const unit = view.getBarTextEffortUnit();

            // display available time left
            result = view.getEffortText(Math.max(maxEffort - effort, 0), unit);
        }

        return result;
    },

    listeners : {
        // Let's scroll Gantt to the corresponding task
        // when clicking a row representing an assignment or a project
        cellClick({ grid, record }) {
            record = grid.resolveRecordToOrigin(record);

            const task = record.isAssignmentModel ? record.event : record.key?.isProjectTask ? record.key : null;

            if (task) {
                gantt.scrollTaskIntoView(task, {
                    highlight : true,
                    animate   : {
                        easing   : 'easeFromTo',
                        duration : 2000
                    }
                });
            }
        }
    },

    columns : [
        {
            type  : 'tree',
            field : 'name',
            width : 280,
            text  : 'Resource / Task',
            renderer({ record, grid }) {
                // Unwrap record to its origin - resource or assignment
                record = grid.resolveRecordToOrigin(record);

                if (record.key?.isResourceModel) {
                    record = record.key;
                }

                // If that's a resource row
                if (record.isResourceModel) {
                    if (!this.avatarRendering) {
                        this.avatarRendering = new AvatarRendering({
                            element : grid.element
                        });
                    }

                    return {
                        class    : 'b-resource-info',
                        children : [
                            this.avatarRendering.getResourceAvatar({
                                initials : record.initials,
                                color    : record.eventColor,
                                iconCls  : record.iconCls,
                                imageUrl : record.image ? `${grid.resourceImageFolderPath}${record.image}` : null
                            }),
                            record.name
                        ]
                    };
                }
                // If that's an assignment row
                else if (record.isAssignmentModel) {
                    return StringHelper.encodeHtml(record.event?.name);
                }

                // Otherwise record represents a group
                // so record.key might have: resource, event or city
                return record.key?.name || record.key;
            }
        },
        {
            text    : 'Task date range',
            cellCls : 'taskDateRange',
            renderer({ record, grid }) {
                record = grid.resolveRecordToOrigin(record);

                // Show event start/end for assignment row
                if (record.isAssignmentModel) {
                    const task = record.event;

                    return DateHelper.format(task.startDate, 'MMM Do') + ' - ' + DateHelper.format(task.endDate, 'MMM Do');
                }

                return '';
            }
        }
    ],

    tbar : {
        cls   : 'utilization-toolbar',
        items : [
            {
                type     : 'checkbox',
                ref      : 'showBarTip',
                text     : 'Enable bar tooltip',
                tooltip  : 'Check to show tooltips when moving mouse over bars',
                checked  : true,
                onAction : 'up.onShowBarTipToggle'
            },
            {
                ref      : 'showTimeLeft',
                type     : 'checkbox',
                text     : 'Show time left',
                tooltip  : 'Check to show time left in bars',
                onAction : 'up.onShowTimeLeftToggle'
            },
            '->',
            {
                type : 'label',
                text : 'Group by'
            },
            {
                type        : 'buttongroup',
                toggleGroup : true,
                cls         : 'group-buttons',
                items       : [
                    {
                        text                 : 'Resource, Project',
                        tooltip              : 'Click to toggle Resource-Project to Project-Resource grouping',
                        icon                 : 'b-fa b-fa-arrow-down',
                        pressed              : true,
                        supportsPressedClick : true,
                        onAction() {
                            // toggle group direction for this button
                            this._groupDirection = !this._groupDirection;

                            if (this._groupDirection) {
                                this.icon = 'b-fa b-fa-arrow-up';

                                // group in backward order - first by Project and then by Resource
                                resourceUtilization.group([...resourceNProjectGroupFns].reverse());
                            }
                            else {
                                this.icon = 'b-fa b-fa-arrow-down';

                                resourceUtilization.group(resourceNProjectGroupFns);
                            }
                        }
                    },
                    {
                        text    : 'City, Resource',
                        tooltip : 'Group by City and Resource',
                        onAction() {
                            resourceUtilization.group([
                                // by city
                                ({ origin }) => origin.isResourceModel ? origin.city : origin.resource.city,
                                // Second group by resource ..if that's an unassigned resource just stop grouping
                                ({ origin }) => origin.isResourceModel ? Store.StopBranch : origin.resource
                            ]);
                        }
                    },
                    {
                        text    : 'Default',
                        tooltip : 'Reset grouping to the default state',
                        onAction() {
                            // reset grouping feature - back to default view
                            resourceUtilization.clearGroups();
                        }
                    }
                ]
            }
        ]
    },

    onShowBarTipToggle({ source }) {
        resourceUtilization.showBarTip = source.checked;
    },

    onShowTimeLeftToggle({ source }) {
        resourceUtilization.showLeftTime = source.checked;

        // schedule the view refresh
        resourceUtilization.scheduleRefreshRows();
    }
});

You will see that resourceUtilization does show the resources allocation accoring to the calendars defined for resources.

Does this help?

If not can we take a step back and please help me understand the usecase here


Post by rocketreading »

I want to display resource absences in the ResourceUtilisationView so that any task planning happening for that specific resource should not allow.


Post by alex.l »

Hi,

That's the answer - you need to use Calendars. Did you read guides Ghulam provided? Did it look like that you needed?

All the best,
Alex Lazarev

How to ask for help? Please read our Support Policy

We do not write the code in bounds of forum support. If you need help with development, contact us via bryntum.com/services


Post by rocketreading »

Could you please let me know the calendar values, that we can use in, he mentioned late as one of the value.
Or if you can provide the documentation where I can see the visualisation w.r.t to values?


Post by ghulam.ghous »

Hey,

The calendar values are not something that are provided are by default. Rather it is something that you can define. I have provided one example. You can take inspiration from that and define those values. You can read on how to define calendars here:
https://bryntum.com/products/gantt/docs/guide/SchedulerPro/basics/calendars
https://bryntum.com/products/gantt/docs/api/SchedulerPro/model/CalendarIntervalModel


Post by rocketreading »

Examples or link that you have sent is referring to Scheduler Pro, My use case is for Bryntum Gantt Resource Utilisation View.
I am expecting this functionality is common in both.
Could you please confirm the same?


Post by ghulam.ghous »

Both schedulerPro and Gantt use the same engine for scheduling, so yes the above share links and functionality of calendars is same for both schedulerPro and Gantt.

Please go ahead and define some calendars. If you face any problems please let us know, we will assist you!


Post Reply