Discuss anything related to web development but no technical support questions


Post by xjpmauricio »

How can i have two SchedulerPanel on the same page rendering on the same div?

Thanks.

Post by mats »

Why would you want two schedulers rendering to the same div? Use case?

Post by xjpmauricio »

Hi, i finally made it and created a page with two schedulers. It's a customer demand because he needs to have fixed events to drag onto the
main scheduler, etc. Business needs that i will no bore you with the explanations.

Now i have a problem, after both schedulers load all data stores, if i click on the filter (after changing dates, etc) button i get this error:

Line: 11
Error: 'this.config[...].width' is null or not an object

If i just leave the main scheduler everything is ok but this error only started to appear after adding the second schduler. I'm doing something wrong i know!

Any ideias!?

This is how i load the child scheduler:
      var thisContext = this;
        this.scheduler.on('afterrender', function () {

            var schPnlRotations = thisContext.createRotationsScheduler();
            var pnlRotationsScheduler = Ext.getCmp('pnlRotationsScheduler');
            pnlRotationsScheduler.items.add(schPnlRotations);

        });

...both schedulers have diferent id's, stores with diferent names, etc.
....this is where i insert the child scheduler...
{
                width: '88%',
                region: 'center',
                layout: 'border',
                border: true,
                frame: true,
                items: [

                    new Ext.Panel({
                        id: 'pnlRotationsScheduler',
                        border: false,
                        frame: false,
                        title: 'Rotations',                        
                        width: '100%',                 
                        region: 'north',
                        height: 140,
                        collapsible: true,
                        bodyStyle: 'padding:0px;margin:0px;',
                        margins: '0 0 0 0'
                    }),

 
                    //main  scheduler
                    this.scheduler
                    
                ]
            },
what a mess!?!!?!? on top the child scheduler ant on the bottom the main scheduler.
Attachments
Capture.JPG
Capture.JPG (76.32 KiB) Viewed 8610 times

Post by mats »

Remember to post code in CODE tags, otherwise it's impossible to read (I've now modified your last post). Could you post your entire code so I can look at it?

Post by xjpmauricio »

hi...here's the code...i'm sorry about the mess it is; it's because it's still work in progress and there's a lot of ambiguous things.


Ext.ns('App');

Ext.onReady(function () {
    Ext.QuickTips.init();

    <%ViewData["title"] = "Roster Plan";
    ViewData["renderTo"] = "panel";
    Html.RenderPartial("/Views/Shared/Menu.ascx");%>

    App.init();
});

App = {


    // Initialize application
    init: function () {

        var start = new Date();
        start.clearTime();
        start.setHours(24);

        // show this week and the next four.
        var beginDate = new Date();
        var dow = beginDate.getDay();
        beginDate = beginDate.add(Date.DAY, -dow);
        var endDate = beginDate.add(Date.DAY, 4 * 7);

        //this.scheduler.store.data.items[0].data.Group = 'dsdasdasd';

        //Store CrewRanks
        var crewRanksStore = new Ext.data.Store({
            // TODO unhardcode this URL...
            url: "/CrewRank/ListJson",
            reader: new Ext.data.JsonReader({
                root: 'rows',
                totalProperty: 'total',
                id: 'id',
                fields: [
                { name: 'code' },
                { name: 'description' }
            ]
            })
        });

        //Store aircraft Models
        var aircraftModelsStore = new Ext.data.Store({
            // TODO unhardcode this URL...
            url: "/AircraftModels/ListJson",
            reader: new Ext.data.JsonReader({
                root: 'rows',
                totalProperty: 'total',
                id: 'id',
                fields: [
                    {name: 'code'},
                    {name: 'name'},
                    {name: 'description'}
                ]
            })
        }); 
            
        //Store crew Rank Groups
        var crewRankGroupStore = new Ext.data.Store({
            // TODO unhardcode this URL...
            url: "/crewRankGroup/ListJson",
            reader: new Ext.data.JsonReader({
                root: 'rows',
                totalProperty: 'total',
                id: 'id',
                fields: [
                    {name: 'id'},
                    {name: 'code'},
                    {name: 'name'}
                ]
            })
        });

        //Store activities, the right side grid store containing all available activities
        var storeActivities = new Ext.data.Store({
		    url: "/rpmdata/ListSchedulerActivitiesJson",
		    reader: new Ext.data.JsonReader({
			    root: 'rows',
			    totalProperty: 'total',
			    id: 'Id',                    
			    fields: [
				    {name: 'Id', type: 'string'},
                    {name: 'top', type: 'int'},
                    {name: 'resizeHandles', type:'boolean'},
                    {name: 'eventTypeId', type: 'int'},
                    {name: 'Title', type: 'string'},
                    {name: 'Description', type: 'string'},
                    {name: 'Duration', type: 'int'},
                    {name: 'Color', type: 'string'},
                    {name: 'ActivityId', type:'int' },
                    {name: 'RotationId', type:'int' }
			    ]
		    }),
		    sortInfo: {field:'Title', direction:'ASC'},
		    remoteSort: true
	    });

        // Store holding all the resources: CrewMembers rows and Rotations row
        var resourceStore = new Ext.data.GroupingStore({
            proxy: new Ext.data.HttpProxy({method: 'POST', url: '/rpmdata/ListSchedulerResources'}),
            sortInfo: { field: 'Name', direction: "ASC" },
            groupField: 'Group',
            groupDir: 'DESC',
            //idProperty: 'id',
            baseParams: {start: 0, limit: 8},     
            reader: new Ext.data.JsonReader(
                {idProperty: 'Id', root: 'rows', totalProperty: 'total'}, 
                ['Id', 'Group', 'Name', 'StartAvailability', 'EndAvailability']
            )
        });         
        
        //Loads all the events from CrewMembers
        var eventStore = new Ext.data.Store({
		    url: "/rpmdata/ListSchedulerEvents",
		    reader: new Ext.data.JsonReader({
			    root: 'rows',
			    totalProperty: 'total',
			    id: 'Id',
			    fields: [
                    //be very precise about field types here, it can lead to stupid errors if you set them wrong.
				    { name: 'Id', type: 'string' },
                    { name: 'ResourceId', type: 'int' },
                    { name: 'Duration', type: 'int' },
                    { name: 'StartDate', type: 'date', dateFormat: 'Y-m-d G:i:s'},
                    { name: 'EndDate', type: 'date', dateFormat: 'Y-m-d G:i:s'},
                    { name: 'Title', type: 'string' },
                    { name: 'Description', type: 'string' },
                    { name: 'Color', type: 'string' },
                    { name: 'eventTypeId', type: 'int' },
                    { name: 'resizeHandles', type:'boolean' },
                    { name: 'top', type: 'int' },
                    { name: 'ActivityId', type:'int' },
                    { name: 'RotationId', type:'int' },
                    { name: 'RotationStartDate', type: 'date', dateFormat: 'Y-m-d G:i:s'},
                    { name: 'RotationEndDate', type: 'date', dateFormat: 'Y-m-d G:i:s'},
                    { name: 'Rotation'}
			    ]
		    }),
		    sortInfo: {field:'Title', direction:'ASC'},
		    remoteSort: true
	    });

        

        this.scheduler = this.createScheduler(resourceStore, eventStore);        
   
        this.unplannedTaskGrid = this.createTaskGrid(storeActivities);

        this.scheduler.on('eventcontextmenu', this.onEventContextMenu, this);


        var ctPanel = new Ext.Panel({
            layout: 'border',
            border: false,
            height: 700,
            width: '100%',
            items: [

                new Ext.FormPanel({
                    labelWidth: 125,
                    frame: true,
                    //title: 'Filter by date range',
                    bodyStyle: 'padding:5px 5px 0',
                    width: '100%',
                    defaults: { width: 175 },
                    region: 'north',
                    height: 130,
                    collapsible: true,
                    items: [
                    {
                        layout:'column',
                        border:true,
                        width:'100%',
                        items:[ 
                                                       
                            {
                                columnWidth:.3,
						        layout: 'form',
						        border:true,
						        items: [
                                       
                                    {
                                        xtype: 'combo',
                                        store: crewRanksStore,
                                        valueField: 'code',
                                        displayField: 'code',
                                        name: 'rank',
                                        id: 'crewRankField',
                                        fieldLabel: 'Crew Rank',
                                        typeAhead: true,
                                        triggerAction: 'all',
                                        selectOnFocus: true,
                                        forceSelection: true
                                    },

                                    {
                                        xtype: 'datefield',
                                        fieldLabel: 'Begin Date',
                                        name: 'beginDate',
                                        id: 'beginDateField',
                                        vtype: 'daterange',
                                        endDateField: 'endDateField', // id of the end date field
                                        format: 'Y-m-d',
                                        value: beginDate.format('Y-m-d')
                                    }

                                ]
                            },

                            {
                                columnWidth:.3,
						        layout: 'form',
						        border:true,
						        items: [                               
                                    
                                    {
                                        xtype: 'combo',
                                        store: crewRankGroupStore,
                                        valueField: 'id',
                                        displayField: 'name',
                                        name: 'crewRankGroup',
                                        id: 'crewRankGroupField',
                                        fieldLabel: 'Crew Rank Group',
                                        typeAhead: true,
                                        triggerAction: 'all',
                                        selectOnFocus: true,
                                        forceSelection: true
                                    },

                                    {
                                        xtype: 'datefield',
                                        fieldLabel: 'End Date',
                                        name: 'endDate',
                                        id: 'endDateField',
                                        startDateField: 'startDateField', // id of the start date field
                                        format: 'Y-m-d',
                                        value: endDate.format('Y-m-d')
                                    }

                                ]
                            },

                            {
                                columnWidth:.3,
						        layout: 'form',
						        border:true,
						        items: [

                                    {
                                        xtype: 'combo',
                                        store: aircraftModelsStore,
                                        valueField: 'code',
                                        displayField: 'name',
                                        name: 'aircraftModel',
                                        id: 'aircraftField',
                                        fieldLabel: 'Aircraft Model',
                                        typeAhead: true,
                                        triggerAction: 'all',
                                        selectOnFocus: true,
                                        forceSelection: true
                                    }

                                ]
                            }

                        ]
                    }
                ],

                buttonAlign: 'left',
                buttons: [

                    {
                        text: 'Filter',
                        handler: function () {

                            var schPnlRosterPlan = Ext.getCmp('schPnlRosterPlan');
                            var schPnlRotations = Ext.getCmp('schPnlRotations');

                            var crewRankCode = Ext.getCmp('crewRankField').getValue();
                            var RankGroupId = Ext.getCmp('crewRankGroupField').getValue();
                            var beginDateField = Ext.getCmp('beginDateField').getValue();
                            var endDateField = Ext.getCmp('endDateField').getValue();
                            endDateField = endDateField.add(Date.DAY, 1);                      
                                                         
                            //load resources (crewmembers rows & rotation row)    
                            resourceStore.load({
                                params: {rankCode: crewRankCode, RankGroupId: RankGroupId},
                                callback:function(records, options, success) { 
                                  
                                    var arrResources = getResources(records);

                                    //load all events; crewmember events & rotations events
                                    eventStore.load({
                                        params: {arrResourcedId: arrResources, rankCode: crewRankCode, rankGroupCode: null, beginAtTime: beginDateField, endAtTime: endDateField},
                                        callback:function(records, options, success) {
                      
                                            schPnlRosterPlan.setView(beginDateField, endDateField, 'day', Sch.ViewBehaviour.DayView);  
                                            schPnlRotations.setView(beginDateField, endDateField, 'day', Sch.ViewBehaviour.DayView);             
                                        }            
                                    });
                                                                                   
                                }            
                            });                                                      

                        }
                    }

                ]

            }),



            //the main scheduler            
            {
                width: '88%',
                region: 'center',
                layout: 'border',
                border: true,
                frame: true,
                items: [

                    new Ext.Panel({
                        id: 'pnlRotationsScheduler',
                        border: false,
                        frame: false,
                        title: 'Rotations',                        
                        width: '100%',                 
                        region: 'north',
                        height: 140,
                        collapsible: true,
                        bodyStyle: 'padding:0px;margin:0px;',
                        margins: '0 0 0 0'
                    }),

 
                    //add tasks grid
                    this.scheduler
                    
                ]
            },

            {
                width: '12%',
                region: 'east',
                layout: 'border',
                border: true,
                frame: true,
                collapsible: true,
                resizable: true,
                items: [                    
                    //add tasks grid
                    this.unplannedTaskGrid
                ]
            }

            ]
        });

        storeActivities.load({
		    callback:function(records, options, success) {
            
                //For some odd reason we get a js error if we load the Scheduler before the events grid.
                ctPanel.render(Ext.getBody());  
  
		    }	
	    });

        var thisContext = this;
        this.scheduler.on('afterrender', function () {

            var schPnlRotations = thisContext.createRotationsScheduler();
            var pnlRotationsScheduler = Ext.getCmp('pnlRotationsScheduler');
            pnlRotationsScheduler.items.add(schPnlRotations);


        });
      
        ctPanel.on('afterrender', function () {
            
         
            initializeTaskDragZone(this.unplannedTaskGrid, this.scheduler.id);

            Ext.getCmp('crewRankField').setValue("CPT");//default filter   
            
            //load resources (crewmembers rows & rotation row)         
            resourceStore.load({
                params: {rankCode: Ext.getCmp('crewRankField').getValue("CPT"), RankGroupId: null},
                callback:function(records, options, success) { 
                                 
                    var arrResources = getResources(records);
                                    
                    //load all events; crewmember events & rotations events
                    eventStore.load({
                        params: {arrResourcedId: arrResources, rankCode: 'CPT', rankGroupCode: null, beginAtTime: beginDate, endAtTime: endDate},
                        callback:function(records, options, success) {
 
                             
                        }            
                    });             
                             
                }            
            });

        }, this);

       
         
    },
   

    //Create the Activities Grid
    createTaskGrid: function (storeActivities) {

        var rendererEvent = function (value, row, record)
	    { 
		    return '<div style="margin-right:4px;width:14px;height:14px;float:left;background-color:' + 
                record.data.Color + ';">&nbsp;</div><span>' + value + '</span>';
 	    }

        return new Ext.grid.GridPanel({
		    id: 'gridTasks',				  
            store: storeActivities,
		    stripeRows: true,
		    border: false,
		    region:'center',
            //cls: 'taskgrid',
            loadMask: true,
            columns: [
                { header: 'Tasks', sortable: false, width: '40%', dataIndex: 'Title', renderer: rendererEvent }
            ],
		    selModel: new Ext.grid.RowSelectionModel({singleSelect:true})
        });
    },

    //Context Menu
    onEventContextMenu: function (g, rec, e) {
        e.stopEvent();
  
        //prevents from showing context menu if event is of type Rotation
        if (rec.data.ResourceId == -1) {//Rotations row
            return false;
        }

        if (!g.gCtx) {
            g.gCtx = new Ext.menu.Menu({
                items: [
                    {
                        id: 'context-delete',
                        text: 'Delete event',
                        iconCls: 'icon-delete'
                    },
                    {
                        id: 'context-lock',
                        text: 'Lock event',
                        iconCls: 'icon-lock'
                    }
                ]
            });

            g.gCtx.on('itemclick', function (item, e) {
                switch (item.id) {
                    case 'context-delete':
                        g.eventStore.remove(g.gCtx.rec);
                        break;

                    default:
                        alert(item.id + ' is not a valid menu action');
                        break;
                }
            }, this);
        };

        g.gCtx.rec = rec;
        g.gCtx.showAt(e.getXY());
        },

        //Event style afer being dropped onto the scheduler
        eventRenderer: function (event, r, row, col, ds) {

            var eventTypeId = event.data ? event.data.eventTypeId : event.json.eventTypeId;
            var resizeHandles = event.data ? event.data.resizeHandles : event.json.resizeHandles;

            //alert('renderer: ' + event.get('Id'));
            return {
                Id: event.get('Id'),
                ResourceId: event.get('ResourceId'),
                //Duration
                //StartDate
                //EndDate
                //Title
                Description: event.get('Description'),
                //Color
                eventTypeId: eventTypeId,
                resizeHandles: resizeHandles,
                top: event.get('top'),
                ActivityId: event.get('ActivityId'),

                RotationId: event.get('RotationId'),
                RotationStartDate: event.get('RotationStartDate'),
                RotationEndDate: event.get('RotationEndDate'),
                //TODO: create object of type Rotation
                Rotation: event.get('Rotation'),

                cls: event.get('Color'),
                text: event.get('Title')                
            };
        },
 
        //TODO: remove this? unused renderer?
        rendererRow: function (event, r, row, col, ds) {
            
        },

        // This function adds some background styling to the scheduling cells
        timeCellRenderer : function (events, m, resourceRecord, row, col, ds, cellStartTime, cellEndTime, grid) {
        
            /*var cellStartHours = cellStartTime.getHours();
                cellEndHours = cellEndTime.getHours();
        
            if (cellStartHours < resourceRecord.get('StartAvailability') || 
                cellStartHours > resourceRecord.get('EndAvailability'))  {
                //m.css = 'unavailable';
            }*/

            m.css = cellStartTime.getDay()%6==0 ? 'highlightWeekends' : '';
        },

        createScheduler: function (resourceStore, eventStore) {

            // show this week and the next four.
            var beginDate = new Date();
            var dow = beginDate.getDay();
            beginDate = beginDate.add(Date.DAY, -dow);
            var endDate = beginDate.add(Date.DAY, 4 * 7);
           

            /*var start = new Date();
            start.clearTime();
            start.setHours(0);

            var end = start.clone();
            end.setHours(24);*/

            var start = beginDate; // new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate());
            var end = endDate; // start.add(Date.DAY, 30);

            // Date line functionality
            var datelineStore = new Ext.data.JsonStore({
                autoDestroy: true,
                fields: [
                    'Text',
                    { name: 'Date', type: 'date' }
                ],
                data: [{
                    Text: 'Today',
                    Date: new Date()
                }]                
            });

            /*var task = {
            run: function () {
            datelineStore.getAt(0).set('Date', new Date());
            },
            interval: 10000 // Update line store every 10 seconds
            };
            var runner = new Ext.util.TaskRunner();
            runner.start(task);*/

            var crewMemberTemplate = new Ext.XTemplate(
                '<span>{Name}</span>'
            );

            var rotationTemplate = new Ext.XTemplate(
                '<div>&nbsp;</div>'
            );

            var g = new Sch.SchedulerPanel({

                id: 'schPnlRosterPlan',
                //Look and feel, basic settings
                resizeHandles: 'both',
                enableDragCreation: false,
                region: 'center',
                enabledHdMenu: false,
                stripeRows: true,
                border: true,
                trackMouseOver: true,            
                loadMask : true,
                allowOverlap: false,
                highlightWeekends: true,
                showTodayLine: true,
                maskDisabled: false,

                colModel: new Ext.ux.grid.LockingColumnModel({
                    columns: [
                        { header: 'Staff', sortable: true, width: 150, dataIndex: 'Name', locked: true,
                            renderer: function (v, m, r) {

                                if (r.id == -1) {//rotations row
                                    return rotationTemplate.apply(r.data);
                                } else {
                                    return crewMemberTemplate.apply(r.data);
                                }
                            }
                        },
                        { header: 'Type', sortable: true, width: 150, dataIndex: 'Group', locked: true, hidden: true }
                    ]
                }),

                view: new Sch.LockingGroupingSchedulerView({
                    forceFit: false,
                    showGroupName: false,
                    //emptyGroupText: 'Rotations',
                    resizable: true
                }),

                // Timline view configuration
                viewModel: {
                    start: start,
                    end: end,
                    columnType: 'day',
                    viewBehaviour: Sch.ViewBehaviour.DayView
                },
               
                viewConfig: { forceFit: true },


                //This is the event renderer. It renders the event after being dropped onto the scheduler.
                //The fields referenced between {} are the ones defined in eventRenderer
                eventTemplate: new Ext.XTemplate(
                    '<div id="{id}" style="top:{top}px;width:{width}px;left:{leftOffset}px" class="sch-event {cls}">' +
                        '<div class="{[values.resizeHandles ? "x-resizable-handle x-resizable-handle-west" : ""]}"></div>',
                        '<div class="sch-event-inner">' +
                            '<span class="sch-event-header">{[values.text]}</span>' +
                        '</div>' +
                        '<div class="{[values.resizeHandles ? "x-resizable-handle x-resizable-handle-east" : ""]}"></div>',
                    '</div>'
                ).compile(),

                //TODO: review these css classes
                tooltipTpl : new Ext.XTemplate(
                '<dl class="eventTip">', 
                    '<dt class="icon-task"></dt><dd><b>{Title}</b>{[values.Description ? ( values.eventTypeId == 2 ? "" : (" (" + values.Description + ")")) : ""]}</dd>',
                    '<dt class="icon-clock">Start: </dt><dd>{[values.StartDate.format("Y-m-d G:i")]}</dd>',
                    '<dt class="icon-clock">End: </dt><dd>{[values.EndDate.format("Y-m-d G:i")]}</dd>',
                '</dl>').compile(),

                timeCellRenderer : this.timeCellRenderer,

                eventRenderer: this.eventRenderer,

                store: resourceStore,

                eventStore: eventStore,
                
                //drag and drop validation event
                dndValidatorFn: function (dragEventRecords, targetRowRecord, b, c, e) {
                    window.status = 'dndValidatorFn: ' + targetRowRecord.get('Group') +
                    ', eventTypeId: ' + dragEventRecords[0].get('eventTypeId') + 
                    ', ResourceId: ' + dragEventRecords[0].get('ResourceId');

                    //Makes the rotation go back to top position 0 when inside the scheduler, after beiing dropped
                    if (dragEventRecords[0].get('eventTypeId') == 2) {//Rotation
                        dragEventRecords[0].data.top = 0;
                    }

                    //Avoid inserting events of type Activity in the Rotations row
                    if (dragEventRecords[0].get('eventTypeId') == 1//Activity
                        && targetRowRecord.get('Group') == "Rotations") {
                        return false;
                    }

                    /*alert(
                    'a: ' + targetRowRecord.get('Group') + '\n\n' +
                    //'b: ' + dragEventRecords[0].get('Group') + '\n\n' +
                    'c: ' + dragEventRecords[0].get('eventTypeId')
                    //'d: ' + targetRowRecord.get('eventTypeId')
                    );*/
                    // Only allow drops on the same row
                    //return dragEventRecords[0].get('ResourceId') === targetRowRecord.get('Id');
                },


                tbar: [
                    {
                        iconCls: 'icon-prev',
                        handler: function () {
                            g.setView(g.getStart().add(Date.MONTH, -1), g.getEnd().add(Date.MONTH, -1));
                        }
                    },

                    {
                        text: 'Days',
                        handler: function () {
                            var s = g.getStart(),
                            e = s.add(Date.DAY, 31);
                            g.setView(s, e, 'day', Sch.ViewBehaviour.DayView);
                        }
                    },

                    {
                        text: 'Hours and days',
                        handler: function () {
                            var s = g.getStart();
                            s.clearTime();
                            s.setHours(0);
                            var e = s.add(Date.HOUR, 24);
                            g.setView(s, e, 'hourAndDay', Sch.ViewBehaviour.HourView);
                        }
                    },

                    {
                        text: 'Months',
                        handler: function () {
                            var s = g.getStart(),
                            e = s.add(Date.MONTH, 12);
                            g.setView(s, e, 'month', Sch.ViewBehaviour.MonthView);
                        }
                    },

                    '->',
                    {
                        iconCls: 'icon-next',
                        handler: function () {
                            g.setView(g.getStart().add(Date.MONTH, 1), g.getEnd().add(Date.MONTH, 1));
                        }
                    }


                ],

                // paging bar on the bottom
                /*bbar: new Ext.PagingToolbar({
                    pageSize: 8,
                    store: resourceStore,
                    displayInfo: true,
                    displayMsg: 'Displaying tasks {0} - {1} of {2}',
                    emptyMsg: "No tasks to display"
                }),*/

                //resize event, limit event resizing or save event 
                resizeValidatorFn: function (resourceRecord, eventRecord, start, end) {
                    //return Date.getDurationInDays(start, end) <= 3;
                    /*if (resourceRecord.id = 'rotationsRow') {
                    return false;
                    }*/
                },

                //Scheduler event listeners
                listeners: {

                    beforeTooltipShow : function(scheduler, record) {
                        if (scheduler.gCtx) {
                            return scheduler.gCtx.isVisible() ? false : true;
                        }                       
                    },
       
                    //Event Drop: happens when the user drops any event on the scheduler
                    drop: function (scheduler, records, copy, valid) {
                       
                        window.status = 
                            'Id = ' + records[0].data.Id +  ' ,' +
                            'ResourceId = ' + records[0].data.ResourceId +  ' ,' + 
                            //'Duration = ' + records[0].data.Duration + ' ,' + 
                            //'RotationStartDate = ' + records[0].data.RotationStartDate.format("Y-m-d G:i:s") + ' ,' + 
                            //'RotationEndDate = ' + records[0].data.RotationEndDate.format("Y-m-d G:i:s") + ' ,' + 
                            //'Title = ' + records[0].data.Title + ' ,' + 
                            //'Description = ' + records[0].data.Description + ' ,' + 
                            //'Color = ' + records[0].data.Color + ' ,' + 
                            'eventTypeId = ' + records[0].data.eventTypeId + ' ,' + 
                            //'resizeHandles = ' + records[0].data.resizeHandles + ' ,' + 
                            'top = ' + records[0].data.top + ' ,';//+ 
                            //'ActivityId = ' + records[0].data.ActivityId + ' ,' + 
                            //'RotationId = ' + records[0].data.RotationId;                            

                        var record = records[0];

                        var resourceId = scheduler.store.getAt(24).id,
                          viewStart = scheduler.getStart(),
                          viewEnd = scheduler.getEnd();

                        var rowEvents = scheduler.eventStore.queryBy(function(ev) { 
                            return ev.get('ResourceId') === resourceId && 
                            Date.intersectSpans(ev.get('StartDate'), ev.get('EndDate'), viewStart, viewEnd); });

                        

                        //verify if there is no rotation event being dropped on a activity event
                        //we must check this because rotations can be dropped far from it's target and still
                        //go back to it's correct position, this position might have an event. we must check this.
                        //TODO: review this!!!
                        /*var r = g.eventStore.query("ResourceId", record.data.ResourceId);                
                        Ext.each(r.items, function (event) {
                            var event = event.data;
                            var rec = record.data;
                            
                            if(rec.ResourceId == event.ResourceId && event.eventTypeId == 2){
                                if(event.StartDate >= rec.StartDate 
                                //&& (event.EndDate < rec.EndDate || event.EndDate > rec.EndDate)
                                ){
                                    return false;
                                }
                            }
                        });*/

                        //Rotations only, if event being dropped is of type Rotation 
                        if(record.data.eventTypeId == 2 //rotations
                            && record.data.RotationId != null){

                            //Rotations only, this basically mekes the rotation stay allways in the same
                            //start and end date, while the user can only change the resource.
                            var rotations = g.eventStore.query("RotationId", records[0].data.RotationId);                                
                            Ext.each(rotations.items, function (rot) {
                                if(records[0].data.Id == rot.data.Id){
                                    g.eventStore.remove([rot]);

                                    rot.data.StartDate = rot.data.RotationStartDate;
                                    rot.data.EndDate = rot.data.RotationEndDate;
                                    rot.json.StartDate = rot.data.RotationStartDate;
                                    rot.json.EndDate = rot.data.RotationEndDate;

                                    //if the user is dropping on the rotations row we must restore top value.
                                    //TODO: calculate if there is overlapping of rotations and adjust
                                    //the top value accordingly
                                    if(record.data.ResourceId == -1){
                                        rot.json.top = 22;
                                        rot.data.top = 22;
                                    }
                                                                       

                                    g.eventStore.add([rot]);
                                }                      
                            })
                        }

                        //Whenever a Activity is dropped onto the scheduler, we must add it's data to
                        //the scheduler's store. We basically copy the record from the activities grid store.
                        if (g.eventStore.indexOf(record) < 0) {
                            g.eventStore.add([record.copy()]);
                            //TODO: remove this in the future
                            //record.store.remove(record);//we keep the ativity on the grid.
                        }

                    },

                    //View Change, wher the user changes the date interval(view) on the scheduler
                    viewChange: function (view) {                       
                    
                        window.status = 'Viewing events from ' + view.viewModel.start + ' to ' + view.viewModel.end;
                    },

                    //When the user clicks on an event
                    eventclick: function (scheduler, event, c, d) {
                        window.status = 'eventclick: ' + event.data.StartDate + " » " + event.data.EndDate;
                    },

                    eventdblclick: this.editEvent,

                    
                    celldblclick: function (scheduler, rowIndex, cellIndex, e) {
                        
                        if(cellIndex == 0){
                            var crewMemberId = scheduler.store.data.items[rowIndex].data.Id;
                            window.status = 'crewMemberId: ' + crewMemberId;
   
                            if(crewMemberId != -1){//rotations row
                                var crewRankCode = Ext.getCmp('crewRankField').getValue();
                                var RankGroupId = Ext.getCmp('crewRankGroupField').getValue();
                                var beginDateField = Ext.getCmp('beginDateField').getValue();
                                var endDateField = Ext.getCmp('endDateField').getValue();

                                resourceStore.load({
                                    params: {crewMemberId: crewMemberId, rankCode: crewRankCode, RankGroupId: RankGroupId},
                                    callback:function(records, options, success) { 
                                                                           
                                    }            
                                });
                            }
                        }

                    },
                    

                    //this event is fired where the user clicks on a resource row
                    //then the user will only see the resource he clicked
                    rowdblclick: function (scheduler, rowIndex, keys) {                    
                      
                    }

                },

                plugins: [

                    //vertical timeline that show at what itme of the we are
                    new Sch.plugins.Lines({
                        store: datelineStore
                    })
                ]



            });

        
            return g;
        },

        createRotationsScheduler : function() {

            // Store holding all the resources
            var storeRotationRow = new Ext.data.GroupingStore({
                sortInfo:{field: 'Name', direction: "ASC"},
                groupField : 'Group',
                idProperty : 'Id',
                data : [
                    {Id : 'r1', Group : 'Sales', Name : '', StartAvailability : 0, EndAvailability : 24}
                ],
                reader : new Ext.data.JsonReader({
                    idProperty : 'Id'
                }, [
                    'Id', 
                    'Group',
                    'Name',      
                    'StartAvailability',
                    'EndAvailability'
                    ]
                )
            });
        
            // Store holding all the tasks
            var storeRotationsEvents = new Ext.data.JsonStore({
                sortInfo:{field: 'ResourceId', direction: "ASC"},
                idProperty : 'Id',
                fields : [
                    {name: 'Id', type:'string'},
                    {name: 'ResourceId'},
                    {name: 'StartDate', type : 'date'},
                    {name: 'EndDate', type : 'date'},                
                    {name: 'Title'}
                ]
            });
        
            // show this week and the next four.
            var beginDate = new Date();
            var dow = beginDate.getDay();
            beginDate = beginDate.add(Date.DAY, -dow);
            var endDate = beginDate.add(Date.DAY, 4 * 7);
 
            var start = beginDate; // new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate());
            var end = endDate; // start.add(Date.DAY, 30);

            var schPnlRotations = new Sch.SchedulerPanel({
                resizeHandles : 'none',
                enableDragCreation : false,
                id: 'schPnlRotations',
                height : 100,
                width : '100%',
                stripeRows : true,
                enabledHdMenu : false,
                border : true,  
                store: storeRotationRow,
                eventStore : storeRotationsEvents,
                trackMouseOver : false,

                colModel: new Ext.ux.grid.LockingColumnModel({
                    columns: [
                        { header: 'Staff', sortable: true, width: 150, dataIndex: 'Name', locked: true}
                    ]
                }),
                //view: new Sch.LockingSchedulerView({
                //view: new Sch.LockingGroupingSchedulerView({
                view: new Sch.LockingSchedulerView({
                    forceFit: false,
                    //showGroupName: false,
                    //emptyGroupText: 'Rotations',
                    resizable: true
                }),

                // Timline view configuration
                viewModel: {
                    start: start,
                    end: end,
                    columnType: 'day',
                    viewBehaviour: Sch.ViewBehaviour.DayView
                },
               
                viewConfig: { forceFit: true }

            

            });

            return schPnlRotations;

        },

        editEvent: function (scheduler, event, c, d) {
            window.status = 'eventdblclick: ' + event.data.StartDate + " » " + event.data.EndDate;

            var activityStartTime = event.data.StartDate.format('G:i');
            var activityEndTime = event.data.EndDate.format('G:i');
            var activityStartDate = event.data.StartDate.format('Y-m-d');
            var activityEndDate = event.data.EndDate.format('Y-m-d');
            
            var windowEditEvent = new Ext.Window({
                title:"Edit Activity " + event.data.Title + ", Id: " + event.data.Id,
			    id:'windowEditEvent',
			    layout:'fit',
                width: 500,
                height:160,
			    minWidth:500,
			    minHeight:160,
                modal: true,                
                bodyStyle:'padding:5px 5px 0;',
                buttonAlign:'center',
                //autoHeight: true,
                plain: true,
			    closeAction:'close',       
                items: [{
                    layout:'column',
                    bodyStyle:'background-color:#c7d6e9;border-color:#c7d6e9;',   
                    items:[{
                        columnWidth:.5,
                        bodyStyle:'background-color:#c7d6e9;border-color:#c7d6e9;',
                        layout: 'form',
                           items: [{
                            id: 'beginTime',
                            xtype: 'timefield',
                            name: 'departureTime',
                            format: 'H:i',
                            fieldLabel: 'Start Time',
                            value:activityStartTime,
                            width: 100                            
                        }, {
                            xtype: 'datefield',
                            name: 'beginDateAct',
                            id: 'beginDateAct',
                            vtype: 'daterange',
                            endDateField: 'endDateField', // id of the end date field
                            format: 'Y-m-d',
                            value: activityStartDate,
                            width: 100,
                            fieldLabel: 'Start Date'
                        }]
                    },{
                        columnWidth:.5,
                        layout: 'form',
                        bodyStyle:'background-color:#c7d6e9;border-color:#c7d6e9;',
                        items: [{
                            id: 'endTime',
                            xtype: 'timefield',
                            name: 'departureTime1',
                            format: 'H:i',
                            fieldLabel: 'End Time',
                            bodyStyle: 'padding: 0 50px 0 0',
                            value:activityEndTime,  
                            width: 100                            
                        },{
                            xtype: 'datefield',
                            name: 'endDateAct',
                            id: 'endDateAct',
                            vtype: 'daterange',
                            endDateField: 'endDateField', // id of the end date field
                            format: 'Y-m-d',
                            width: 100,
                            value: activityEndDate,
                            fieldLabel: 'End Date'
                        }]
                    }]
                }],

                buttons: [{
                    text: "Confirm",
                    handler: function() {

                        //old: url: "/RosterPlan/SaveCrewMemberEvent",
                        //int Type, string BeginAtTime, string EndAtTime, int CrewMemberId, int RotationId, int ActivityId
                        var params = {
                            Id: event.data.Id,
                            Type: 3,
                            BeginDate: Ext.getCmp('beginDateAct').getValue(),
                            BeginTime: Ext.getCmp('beginTime').getValue(),
                            EndDate: Ext.getCmp('endDateAct').getValue(),
                            EndTime: Ext.getCmp('endTime').getValue(),
                            CrewMemberId: event.data.ResourceId,
                            RotationId: null,
                            ActivityId: event.data.ActivityId
                        };

                        Ext.Ajax.request({
                            url: '/rpmdata/SaveSchedulerEvent',
                            //url: "/RosterPlan/SaveCrewMemberEvent",
                            success: function(form, action) {
                      		    
                            },                    
                            failure: function(response, options) {                          
                                Ext.Msg.show({
                                    title: 'Error',
                                    msg: 'An error as occurred.',
                                    buttons: Ext.Msg.OK,
                                    icon: Ext.MessageBox.ERROR
                                });
                            },
                            params: params
                        });

                        /*beginTime = Ext.getCmp('beginTime').value;
                        endTime = Ext.getCmp('endTime').value;
                        
                        beginDateAct = Ext.getCmp('beginDateAct').value;
                        endDateAct = Ext.getCmp('endDateAct').value;*/                    
                                             
                        //self.fireEvent("confirm",rosterPlanView, event,beginTime,endTime,beginDateAct,endDateAct);
                        windowEditEvent.close();
                        return;                           
                    }
                }, {
                    text: "Cancel",
                    handler: function() {
                        windowEditEvent.close();
                    }
                }]


		    });
            windowEditEvent.show(false, function() {            
		        windowEditEvent.center();       
	        });  
        },


    };

    function initializeTaskDragZone(g, ddGroup) {
        g.dragZone = new Ext.dd.DragZone(g.getEl(), {
            ddGroup: ddGroup,

            getDragData: function (e) {
                var sourceEl = e.getTarget(),
                rec = g.store.getAt(g.view.findRowIndex(sourceEl));

                if (sourceEl && rec) {

                    var d = sourceEl.cloneNode(true);
                    d.id = Ext.id();

                    var wrap = Ext.get(Ext.DomHelper.createDom({
                        tag: 'div',
                        style: {
                            width: '200px'
                        },
                        children: [
                            {
                                tag: 'span',
                                cls: 'sch-dd-newtime',
                                html: '&nbsp;'
                            }
                        ]
                    }));

                    wrap.appendChild(d);
                    //Ext.fly(d).addClass('sch-event');
                    Ext.fly(d).update(rec.get('Title'));
                    Ext.fly(d).addClass(rec.get('Color'));                    

                    //Look and feel of an event while being dragged onto the Scheduler (events grid only)
                    return {
                        sourceEl: sourceEl,
                        repairXY: Ext.fly(sourceEl).getXY(),
                        duration: 1440, //rec.get('Duration') * 60,
                        ddel: wrap.dom,
                        sourceEventRecord: rec,
                        records: [rec]
                    };
                }
            },

            //Provide coordinates for the proxy to slide back to on failed drag.
            //This is the original XY coordinates of the draggable element.
            getRepairXY: function () {
                return this.dragData.repairXY;
            }

        });
    }

    //get all resources id's from store (crewmember id's)
    function getResources(store){

        var arrResourcedId = new Array();
        Ext.each(store, function (res) {
            if(res.id != "rotationsRow"){
                arrResourcedId.push(res.id);
            }
        });

        return arrResourcedId;
    }

    var nextId = function () {
        // if the time isn't unique enough, the addition 
        // of random chars should be
        var t = String(new Date().getTime()).substr(4);
        var s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        for (var i = 0; i < 4; i++) {
            t += s.charAt(Math.floor(Math.random() * 26));
        }
        return 'e' + t;
    };


Ext.override(Ext.data.GroupingStore, {
	groupDir : 'ASC',
	groupBy : function(field, forceRegroup, direction){
		direction = direction ? (String(direction).toUpperCase() == 'DESC' ? 'DESC' : 'ASC') : this.groupDir;
		if(this.groupField == field && this.groupDir == direction && !forceRegroup){
			return;
		}
		this.groupField = field;
		this.groupDir = direction;
		if(this.remoteGroup){
			if(!this.baseParams){
				this.baseParams = {};
			}
			this.baseParams['groupBy'] = field;
			this.baseParams['groupDir'] = direction;
		}
		if(this.groupOnSort){
			this.sort(field, direction);
			return;
		}
		if(this.remoteGroup){
			this.reload();
		}else{
			var si = this.sortInfo || {};
			if(si.field != field || si.direction != direction){
				this.applySort();
			}else{
				this.sortData(field, direction);
			}
			this.fireEvent('datachanged', this);
		}
	},
	applySort : function(){
		Ext.data.GroupingStore.superclass.applySort.call(this);
		if(!this.groupOnSort && !this.remoteGroup){
			if(this.groupField != this.sortInfo.field || this.groupDir != this.sortInfo.direction){
				this.sortData(this.groupField, this.groupDir);
			}
		}
	},
	applyGrouping : function(alwaysFireChange){
		if(this.groupField !== false){
			this.groupBy(this.groupField, true, this.groupDir);
			return true;
		}else{
			if(alwaysFireChange === true){
				this.fireEvent('datachanged', this);
			}
			return false;
		}
	}
});


</script>


Post by mats »

It's somewhat messy yes :). Let's try to clean it up, it seems that you're overnesting a bit, using containgin Panels when there's no need to. Replace:
{
                    width: '88%',
                    region: 'center',
                    layout: 'border',
                    border: true,
                    frame: true,
                    items: [

                        new Ext.Panel({
                            id: 'pnlRotationsScheduler',
                            border: false,
                            frame: false,
                            title: 'Rotations',                       
                            width: '100%',                 
                            region: 'north',
                            height: 140,
                            collapsible: true,
                            bodyStyle: 'padding:0px;margin:0px;',
                            margins: '0 0 0 0'
                        }),


                        //add tasks grid
                        this.scheduler
                       
                    ]
                },
with just

 this.scheduler
and move the config properties below to the instantiation of the "this.scheduler" instance. Remember, the SchedulerPanel _is_ a panel. No need to put it inside a parent panel.
                    region: 'center',
                    border: true

Post by xjpmauricio »

Hi, i understand what you said about the scheduler itself being a panel, etc but, i need to mantain this layout. Customer demand.
If i do what you said i can no longer mantain the layout and i still keep on having the same error. Just tried it out 5 minutes ago.

The error: "Line: 11
Error: 'this.config[...].width' is null or not an object"

If i just mantain the main scheduler everything is ok. :-(

Post by mats »

Ok, not sure I know what's going on then. Can you put together a reproducible test case that I can look at? HTML/CSS/JS in a zip file.

Post by xjpmauricio »

Hi, thanks for all the quick replies. I think i got it now except for one thing, when i invoque this:
schPnlRosterPlan.setView(beginDateField, endDateField, 'day', Sch.ViewBehaviour.DayView);
I get this error:
Line: 11
Error: 'this.config[...].width' is null or not an object
"schPnlRosterPlan" is the main Scheduler.

I think is something about the view. If i change 'day' to 'month' it works ok.

Any ideias!? :-(

Post by mats »

What are the values of beginDateField, endDateField?

Post Reply