issue on scheduler timeline view while grouping 2025

2 Answers 18 Views
Scheduler
naseer
Top achievements
Rank 1
Iron
naseer asked on 15 Jul 2025, 02:29 PM | edited on 15 Jul 2025, 02:38 PM

<script>


    // Global variables
    var ArrWorkStationProductTaskMappingList = [];
    var ArrTechncinList = [];
    var scheduler;
    var currentEditingEvent = null;
    var localEvents = [];
    var nextId = 1;

    // Sample data for demonstration
    var sampleWorkStationData = [
        { WorkStationID: "WS001", WorkStationName: "Wall 1", ProdCode: "PROD001", TaskID: "T001", TaskName: "Task 1", Duration: 2.5 },
        { WorkStationID: "WS002", WorkStationName: "Wall 2", ProdCode: "PROD002", TaskID: "T002", TaskName: "Task 2", Duration: 3.0 },
        { WorkStationID: "WS003", WorkStationName: "Floor (B)", ProdCode: "PROD003", TaskID: "T003", TaskName: "Task 3", Duration: 1.5 },
         { WorkStationID: "WS004", WorkStationName: "Black Front", ProdCode: "PROD004", TaskID: "T004", TaskName: "Task 4", Duration: 1.5 },
         { WorkStationID: "WS004", WorkStationName: "Floor (Light 1)", ProdCode: "PROD005", TaskID: "T005", TaskName: "Task 5", Duration: 1.5 }

    ];

    var sampleTechnicianData = [
        { TechnicianName: "John Doe", TechnicianUserID: "USER001", TechnicianMail: "john@example.com" },
        { TechnicianName: "Jane Smith", TechnicianUserID: "USER002", TechnicianMail: "jane@example.com" },
        { TechnicianName: "Bob Johnson", TechnicianUserID: "USER003", TechnicianMail: "bob@example.com" }
    ];

    // Initialize sample data
    ArrWorkStationProductTaskMappingList = sampleWorkStationData;
    ArrTechncinList = sampleTechnicianData;

    // Initialize sample events
    localEvents = [
        {
            ScheduleID: 1,
            Status: "Active",
            ScheduleBatchID: "BATCH001",
            ResourceID: "WS001",
            ResourceName: "Wall 1",
            ResourceMappedForID: "T001",
            ResourceMappedForName: "Task 1",
            JobNo: "JOB001",
            ProdCode: "PROD001",
            JobDetailUniqueID: "JD001",
            Prod_Name: "Product 1",
            ScheduleCode: "SCH001",
            FromDate: "2025/7/15",
            ToDate: "2025/7/15",
            FromTime: "09:00",
            ToTime: "11:00",
            title: "Workstation Task - Product 1",
            IsAllDayEvent: false,
            description: "Sample workstation task for Product 1",
            TypeID: 1,
            start: new Date("2025/7/15 09:00"),
            end: new Date("2025/7/15 11:00"),
            resource: "WS001",
            resourceType: 1
        },
        {
            ScheduleID: 2,
            Status: "Active",
            ScheduleBatchID: "BATCH002",
            ResourceID: "USER001",
            ResourceName: "John Doe",
            ResourceMappedForID: "S001",
            ResourceMappedForName: "Sample 1",
            JobNo: "JOB001",
            ProdCode: "PROD001",
            JobDetailUniqueID: "JD002",
            Prod_Name: "Product 1",
            ScheduleCode: "SCH002",
            FromDate: "2025/7/15",
            ToDate: "2025/7/15",
            FromTime: "10:00",
            ToTime: "12:00",
            title: "Technician Task - John Doe",
            IsAllDayEvent: false,
            description: "Sample technician task",
            TypeID: 2,
            start: new Date("2025/7/15 10:00"),
            end: new Date("2025/7/15 12:00"),
            resource: "USER001",
            resourceType: 2
        },
        {
            ScheduleID: 3,
            Status: "Active",
            ScheduleBatchID: "BATCH003",
            ResourceID: "WS002",
            ResourceName: "Wall 2",
            ResourceMappedForID: "T002",
            ResourceMappedForName: "Task 2",
            JobNo: "JOB002",
            ProdCode: "PROD002",
            JobDetailUniqueID: "JD003",
            Prod_Name: "Product 2",
            ScheduleCode: "SCH003",
            FromDate: "2025/7/15",
            ToDate: "2025/7/15",
            FromTime: "13:00",
            ToTime: "15:00",
            title: "Workstation Task - Product 2",
            IsAllDayEvent: false,
            description: "Another workstation task for Product 2",
            TypeID: 1,
            start: new Date("2025/7/15 13:00"),
            end: new Date("2025/7/15 15:00"),
            resource: "WS002",
            resourceType: 1
        }
    ];

    $(document).ready(function () {
        // Initialize controls
        $("#ddlViewType").val($("#hdViewType").val());
        $("#txtProductCodes").val($("#hdcurrentTestCodeToAllocate").val());

        // Initialize scheduler
        initializeScheduler();

        populateResourceDropdown();


        // Event handlers
        $("#btnRefreshScheduler").click(function () {
            $("#hdViewType").val($("#ddlViewType").val());
            $("#hdcurrentTestCodeToAllocate").val($("#txtProductCodes").val());
            refreshScheduler();
        });

        $("#btnSaveSchedule").click(saveScheduleEvent);


    });



    // Add this function to validate and fix event resourceType values
    function validateAndFixEvents() {
        console.log("Validating events...");

        localEvents.forEach(function (event, index) {
            var correctResourceType = null;

            // Check if it's a workstation resource
            var workstationData = getFilteredWorkstationData();
            var isWorkstation = workstationData.some(function (ws) {
                return ws.WorkStationID === event.ResourceID;
            });

            if (isWorkstation) {
                correctResourceType = 1;
            } else {
                // Check if it's a technician resource
                var isTechnician = ArrTechncinList.some(function (tech) {
                    return tech.TechnicianUserID === event.ResourceID;
                });

                if (isTechnician) {
                    correctResourceType = 2;
                }
            }

            if (correctResourceType && event.resourceType !== correctResourceType) {
                console.log("Fixing event " + index + " resourceType from " + event.resourceType + " to " + correctResourceType);
                event.resourceType = correctResourceType;
                event.resource = event.ResourceID; // Make sure resource field matches ResourceID
            }
        });

        console.log("Events after validation:", localEvents);
    }






    function initializeScheduler() {

        validateAndFixEvents();


        var resources = buildResourcesArray();
        var dataSource = buildDataSource();

        if (scheduler && typeof scheduler.destroy === "function") {
            scheduler.destroy();
            $("#scheduler").empty(); // Optional
        }

        scheduler = $("#scheduler").kendoScheduler({
            date: new Date("2025/7/15"),
            startTime: new Date("2025/7/15 08:00"),
            endTime: new Date("2025/7/15 18:00"),
            height: 800,
            eventHeight: 50,
            majorTick: 60,
            views: [
                "day",
                "week",
                "month",
                {
                    type: "timeline",
                    selected: true,
                    startTime: new Date("2025/7/15 08:00"),
                    endTime: new Date("2025/7/15 18:00"),
                    workDayStart: new Date("2025/7/15 08:00"),
                    workDayEnd: new Date("2025/7/15 18:00"),
                    majorTick: 60,
                    minorTickCount: 2,
                    showWorkHours: true
                },
                {
                    type: "timelineWeek",
                    startTime: new Date("2025/7/15 08:00"),
                    endTime: new Date("2025/7/15 18:00"),
                    workDayStart: new Date("2025/7/15 08:00"),
                    workDayEnd: new Date("2025/7/15 18:00"),
                    majorTick: 60,
                    minorTickCount: 2,
                    showWorkHours: true
                }
            ],
            timezone: "Etc/UTC",
            dataSource: dataSource,
            resources: resources,
            group: {
                resources: ["resourceType", "resource"],
                orientation: "vertical"
            },
            /*eventTemplate: $("#event-template").html(),*/
            add: function (e) {
                e.preventDefault();
                openScheduleModal(null, e.event);
            },
            edit: function (e) {
                e.preventDefault();
                openScheduleModal(e.event, null);
            },
            remove: function (e) {
                if (confirm("Are you sure you want to delete this event?")) {
                    console.log("Deleting event:", e.event);
                    // The scheduler will handle the delete automatically
                } else {
                    e.preventDefault();
                }
            },
            dataBound: function (e) {

                // Add tooltips
                $(".k-event").each(function () {
                    var uid = $(this).attr("data-uid");
                    var event = scheduler.dataSource.getByUid(uid);
                    //if (event) {
                    //    $(this).attr("title", buildTooltipContent(event));
                    //}
                });
            }
        }).data("kendoScheduler");
    }



    function buildResourcesArray() {
        var resources = [];
        var viewType = $("#hdViewType").val();

        console.log("Building resources for view type:", viewType);

        // Build the resource type grouping first
        var resourceTypeData = [];

        if (viewType === "B" || viewType === "W") {
            resourceTypeData.push({
                text: "Workstation",
                value: 1,
                color: "#2572c0"
            });
        }

        if (viewType === "B" || viewType === "T") {
            resourceTypeData.push({
                text: "Technician",
                value: 2,
                color: "#f8a398"
            });
        }

        // Add resourceType as the first resource for grouping
        resources.push({
            field: "resourceType",
            name: "resourceType",
            title: "Resource Type",
            dataSource: resourceTypeData
        });

        // Build individual resources with correct resourceType values
        var allResources = [];

        // Add workstations
        if (viewType === "B" || viewType === "W") {
            var workstationData = getFilteredWorkstationData();
            console.log("Adding workstations:", workstationData);

            workstationData.forEach(function (item) {
                allResources.push({
                    text: item.WorkStationName,
                    value: item.WorkStationID,
                    color: "#2572c0",
                    resourceType: 1  // CRITICAL: This must match the resourceType value above
                });
            });
        }

        // Add technicians
        if (viewType === "B" || viewType === "T") {
            console.log("Adding technicians:", ArrTechncinList);

            ArrTechncinList.forEach(function (item) {
                allResources.push({
                    text: item.TechnicianName,
                    value: item.TechnicianUserID,
                    color: "#f8a398",
                    resourceType: 2  // CRITICAL: This must match the resourceType value above
                });
            });
        }

        // Add the individual resources
        resources.push({
            field: "resource",
            name: "resource",
            title: "Resource",
            valuePrimitive: true,
            dataSource: allResources,
            group: "resourceType"  // This tells Kendo to group by resourceType
        });

        console.log("Final resources array:", resources);
        console.log("All individual resources:", allResources);

        return resources;
    }

    function getFilteredWorkstationData() {
        var productCodes = $("#hdcurrentTestCodeToAllocate").val();
        var filteredData = ArrWorkStationProductTaskMappingList;

        if (productCodes && productCodes.trim() !== "") {
            var codes = productCodes.split(',').map(function (code) {
                return code.trim();
            });
            filteredData = ArrWorkStationProductTaskMappingList.filter(function (item) {
                return codes.indexOf(item.ProdCode) !== -1;
            });
        }

        // Get distinct workstations
        var distinctWorkstations = [];
        var seen = {};

        filteredData.forEach(function (item) {
            var key = item.WorkStationID; // + "_" + item.TaskID;
            if (!seen[key]) {
                seen[key] = true;
                distinctWorkstations.push(item);
            }
        });

        return distinctWorkstations;
    }

    function buildDataSource() {
        return new kendo.data.SchedulerDataSource({
            batch: true,
            transport: {
                read: function (options) {
                    try {
                        options.success(localEvents);
                    } catch (error) {
                        console.error('Error reading events:', error);
                        options.error(error);
                    }
                },
                create: function (options) {
                    try {
                        var newEvents = options.data.models || [options.data];

                        newEvents.forEach(function (event) {
                            event.ScheduleID = nextId++;
                            localEvents.push(event);
                        });

                        options.success(newEvents);

                    } catch (error) {
                        console.error('Error creating events:', error);
                        options.error(error);
                    }
                },
                update: function (options) {
                    try {
                        var updatedEvents = options.data.models || [options.data];

                        updatedEvents.forEach(function (updatedEvent) {
                            var index = localEvents.findIndex(function (e) {
                                return e.ScheduleID === updatedEvent.ScheduleID;
                            });

                            if (index !== -1) {
                                localEvents[index] = updatedEvent;
                            }
                        });

                        options.success(updatedEvents);

                    } catch (error) {
                        console.error('Error updating events:', error);
                        options.error(error);
                    }
                },
                destroy: function (options) {
                    try {
                        var deletedEvents = options.data.models || [options.data];

                        deletedEvents.forEach(function (deletedEvent) {
                            var index = localEvents.findIndex(function (e) {
                                return e.ScheduleID === deletedEvent.ScheduleID;
                            });

                            if (index !== -1) {
                                localEvents.splice(index, 1);
                            }
                        });

                        options.success(deletedEvents);

                    } catch (error) {
                        console.error('Error deleting events:', error);
                        options.error(error);
                    }
                }
            },
            schema: {
                model: {
                    id: "ScheduleID",
                    fields: {
                        ScheduleID: { type: "number" },
                        title: { from: "title", defaultValue: "No title", validation: { required: true } },
                        start: { type: "date", validation: { required: true } },
                        end: { type: "date", validation: { required: true } },
                        resource: { nullable: true },
                        resourceType: { nullable: true },
                        isAllDay: { type: "boolean", from: "IsAllDayEvent", defaultValue: false },
                        description: { type: "string" },
                        Status: { type: "string" },
                        ScheduleBatchID: { type: "string" },
                        ResourceID: { type: "string" },
                        ResourceName: { type: "string" },
                        ResourceMappedForID: { type: "string" },
                        ResourceMappedForName: { type: "string" },
                        JobNo: { type: "string" },
                        ProdCode: { type: "string" },
                        JobDetailUniqueID: { type: "string" },
                        Prod_Name: { type: "string" },
                        ScheduleCode: { type: "string" },
                        FromDate: { type: "string" },
                        ToDate: { type: "string" },
                        FromTime: { type: "string" },
                        ToTime: { type: "string" },
                        TypeID: { type: "number" }
                    }
                }
            }
        });
    }

    function refreshScheduler() {
        initializeScheduler();
        populateResourceDropdown();
    }

    function openScheduleModal(event, newEvent) {
        currentEditingEvent = event;

        // Populate resource dropdown
        populateResourceDropdown();

        if (event) {
            // Edit mode
            $("#modalTitle").text("Edit Schedule Event");
            $("#eventId").val(event.ScheduleID);
            $("#eventTitle").val(event.title);
            $("#eventStart").val(formatDateTimeLocal(event.start));
            $("#eventEnd").val(formatDateTimeLocal(event.end));
            $("#eventResource").val(event.ResourceID);
            $("#eventDescription").val(event.description || "");
            $("#eventTypeId").val(event.TypeID);
            $("#eventResourceId").val(event.ResourceID);
        } else {
            // Add mode
            $("#modalTitle").text("Add Schedule Event");
            $("#eventId").val("");
            $("#eventTitle").val("");
            $("#eventStart").val(formatDateTimeLocal(newEvent.start));
            $("#eventEnd").val(formatDateTimeLocal(newEvent.end));
            $("#eventResource").val("");
            $("#eventDescription").val("");
            $("#eventTypeId").val("");
            $("#eventResourceId").val("");
        }

        $('#scheduleModal').modal('show');
    }



    function populateResourceDropdown() {
        var dropdown = $("#eventResource");
        dropdown.empty();
        dropdown.append('<option value="">Select Resource</option>');

        var resources = buildResourcesArray();
        if (resources.length > 1) {
            var groupedResources = {
                Workstations: [],
                Technicians: []
            };

            resources[1].dataSource.forEach(function (item) {
                if (item.resourceType === 1) {
                    groupedResources.Workstations.push(item);
                } else if (item.resourceType === 2) {
                    groupedResources.Technicians.push(item);
                }
            });

            // Append Workstations
            if (groupedResources.Workstations.length > 0) {
                var wsGroup = $('<optgroup label="Workstations"></optgroup>');
                groupedResources.Workstations.forEach(function (ws) {
                    wsGroup.append('<option value="' + ws.value + '">' + ws.text + '</option>');
                });
                dropdown.append(wsGroup);
            }

            // Append Technicians
            if (groupedResources.Technicians.length > 0) {
                var techGroup = $('<optgroup label="Technicians"></optgroup>');
                groupedResources.Technicians.forEach(function (tech) {
                    techGroup.append('<option value="' + tech.value + '">' + tech.text + '</option>');
                });
                dropdown.append(techGroup);
            }
        }
    }



    function saveScheduleEvent() {
        //if (!$("#scheduleForm")[0].checkValidity()) {
        //    $("#scheduleForm")[0].reportValidity();
        //    return;
        //}

        var startDate = new Date($("#eventStart").val());
        var endDate = new Date($("#eventEnd").val());
        var selectedResource = $("#eventResource").val();

        // Determine TypeID based on selected resource
        var typeId = 1; // default to workstation
        var resourceName = "";
        var resourceMappedForID = "";
        var resourceMappedForName = "";

        // Find the selected resource details
        var resources = buildResourcesArray();
        var resourceData = resources[1].dataSource.find(function (r) { return r.value === selectedResource; });

        if (resourceData) {
            typeId = resourceData.resourceType;
            resourceName = resourceData.text;

            if (typeId === 1) {
                // Workstation
                resourceMappedForID = resourceData.taskId;
                resourceMappedForName = resourceData.taskName;
            } else {
                // Technician
                resourceMappedForID = resourceData.technicianUserId;
                resourceMappedForName = resourceData.text;
            }
        }

        var eventData = {
            ScheduleID: $("#eventId").val() || nextId++,
            Status: "Active",
            ScheduleBatchID: "BATCH" + new Date().getTime(),
            ResourceID: selectedResource,
            ResourceName: resourceName,
            ResourceMappedForID: resourceMappedForID,
            ResourceMappedForName: resourceMappedForName,
            JobNo: $("#hdJobNo").val(),
            ProdCode: $("#hdcurrentTestCodeToAllocate").val().split(',')[0] || "",
            JobDetailUniqueID: $("#hdSelectedJobIDs").val().split(',')[0] || "",
            Prod_Name: "Product Name",
            ScheduleCode: "SCH" + new Date().getTime(),
            FromDate: kendo.toString(startDate, "yyyy/M/d"),
            ToDate: kendo.toString(endDate, "yyyy/M/d"),
            FromTime: kendo.toString(startDate, "HH:mm"),
            ToTime: kendo.toString(endDate, "HH:mm"),
            title: $("#eventTitle").val(),
            IsAllDayEvent: false,
            description: $("#eventDescription").val(),
            TypeID: typeId,
            start: startDate,
            end: endDate,
            resource: selectedResource,
            resourceType: typeId
        };

        if (currentEditingEvent) {
            // Update existing event
            var existingEvent = scheduler.dataSource.get(currentEditingEvent.ScheduleID);
            for (var key in eventData) {
                existingEvent.set(key, eventData[key]);
            }
        } else {
            // Add new event
            scheduler.dataSource.add(eventData);
        }

      
        $('#scheduleModal').modal('hide');
    }

    function formatDateTimeLocal(date) {
        var d = new Date(date);
        var month = ('0' + (d.getMonth() + 1)).slice(-2);
        var day = ('0' + d.getDate()).slice(-2);
        var hours = ('0' + d.getHours()).slice(-2);
        var minutes = ('0' + d.getMinutes()).slice(-2);
        return d.getFullYear() + '-' + month + '-' + day + 'T' + hours + ':' + minutes;
    }



</script>


<input type="hidden" id="hdViewType" value="B" />
<input type="hidden" id="hdcurrentTestCodeToAllocate" value="PROD001,PROD002,PROD003,PROD004,PROD005" />
<input type="hidden" id="hdJobNo" value="JOB001" />
<input type="hidden" id="hdSelectedJobIDs" value="JD001,JD002" />


     <div class="resource-controls">
         <div class="row">
             <div class="col-md-3">
                 <label for="ddlViewType">View Type:</label>
                 <select id="ddlViewType" class="form-control" style="height: 38px;">
                     <option value="B">Both (Workstation & Technician)</option>
                     <option value="W">Workstation Only</option>
                     <option value="T">Technician Only</option>
                 </select>
             </div>
             <div class="col-md-3" style="display:none">
                 <label for="txtProductCodes">Product Codes:</label>
                 <input type="text" id="txtProductCodes" class="form-control" placeholder="PROD001,PROD002,PROD003,PROD004,PROD005" />
             </div>
             <div class="col-md-3">
                 <button id="btnRefreshScheduler" class="btn btn-primary" style="margin-top: 25px;">Refresh Scheduler</button>
             </div>
         </div>
     </div>

     <div class="scheduler-container">
         <div id="scheduler" style="width:100%"></div>
     </div>
hi here i have inserted my code , i have workstation and technician two groups, and i want to show the workstation list under the workstation group and technician list under the techncian group, this works fine when i selecting the viewtype as T ot W but in B i have to give the both list but i mentioned the resourcetype as the list belongs to which group , but still calender is showing both list in both group , i am trying this implementation in 2025 version 

anyone please give me the solution for this 

2 Answers, 1 is accepted

Sort by
0
Accepted
Georgi Denchev
Telerik team
answered on 18 Jul 2025, 10:22 AM

Hi,

Based on your scenario and the provided code, the issue is that when you select "Both" (B) as the view type, the Kendo UI Scheduler is displaying all resources (workstations and technicians) under both groups, instead of showing each resource only under its correct group. This happens because, by default, the Scheduler creates a cartesian product of the grouped resources unless you configure cascading between them.

Solution: Use dataParentValueField for Cascading Grouped Resources

To ensure that workstations only appear under the "Workstation" group and technicians only under the "Technician" group when "Both" is selected, you need to use the dataParentValueField property in your resource definition. This property allows you to "cascade" resources so that each resource is only shown under its matching parent group.

What to Change

In your buildResourcesArray() function, update the definition of the individual resource (the one with field: "resource") to include dataParentValueField: "resourceType":

resources.push({
    field: "resource",
    name: "resource",
    title: "Resource",
    valuePrimitive: true,
    dataSource: allResources,
    group: "resourceType",
    dataParentValueField: "resourceType" // <-- Add this line!
});

Make sure that each resource object in your allResources array has a resourceType property (which you already do).

Why This Works

  • The dataParentValueField tells the Scheduler to only show resources under the parent group that matches their resourceType.
  • Workstations (resourceType: 1) will appear only under "Workstation", and Technicians (resourceType: 2) will appear only under "Technician".
  • This setup prevents the cartesian product behavior.

Example Snippet

Here's the relevant part of the resources array definition:

resources.push({
    field: "resourceType",
    name: "resourceType",
    title: "Resource Type",
    dataSource: resourceTypeData
});

resources.push({
    field: "resource",
    name: "resource",
    title: "Resource",
    valuePrimitive: true,
    dataSource: allResources,
    group: "resourceType",
    dataParentValueField: "resourceType" // Ensures correct grouping
});

Additional Notes

  • Double-check that every resource in allResources has the correct resourceType property.
  • No further changes to your data model are needed, since you already include resourceType in your resource definitions.

Expected Result

With this configuration, when you select "Both" as the view type:

  • The Scheduler should display two groups: "Workstation" and "Technician".
  • The resources should be correctly grouped under their respective headers, and not duplicated under both.

This approach is supported in Kendo UI for jQuery Scheduler 2025 and should resolve the grouping issue you are experiencing.

    Best Regards,
    Georgi Denchev
    Progress Telerik

    Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

    0
    naseer
    Top achievements
    Rank 1
    Iron
    answered on 18 Jul 2025, 10:49 AM
    Thank you so much , its working as expected 
    Tags
    Scheduler
    Asked by
    naseer
    Top achievements
    Rank 1
    Iron
    Answers by
    Georgi Denchev
    Telerik team
    naseer
    Top achievements
    Rank 1
    Iron
    Share this question
    or