Show cool things you have done with our products


Post by praveen.shastri »

Hello Mats,

I am using event editor example. I want to stop user from adding overlapping events. I am able to restrict user from extending event via dragging. But when he opens event edit form and modifies start date and end date then it adds the event even if it is overlapping.

Is it possible to avoid overlapping of events when user edits start date and end date from event edit form(not via dragging)?


Thanks in advance
Praveen

Post by mats »

Sure, before you set the new values you could check if the timespan is available for the resource using this method on SchedulerPanel:
/**
         * Checks if a date range is allocated or not for a given resource.
         * @param {Date} start The start date
         * @param {Date} end The end date
         * @param {Mixed} eventId The event id (or null)
         * @param {Mixed} resourceId The id of the resource 
         * @return {Boolean} True if the timespan is available for the resource
         */
        isDateRangeAvailable : function(start, end, eventId, resourceId) {
            var available = true;
            this.eventStore.each(function(r) {
                if (Date.intersectSpans(start, end, r.get('StartDate'), r.get('EndDate')) && (resourceId === r.get('ResourceId') && (!eventId || eventId !== r.id))){
                    available = false;
                    return false;
                }
            });
            return available;
        },

Post by praveen.shastri »

Hello Mats,

I am not able to figure out where exactly put the code given by you. Can you please help me out.

I have written my own custom editor below is the code
/**
 * 
 */
/*
* Ext Scheduler 1.7
* Copyright(c) 2009-2010 Mats Bryntse Consulting
* mats@ext-scheduler.com
* http://ext-scheduler.com/license.html
*
*/

Ext.apply(Ext.form.VTypes, {
	EngagementStatus: function(value,field)
	   {
			var engTyp = Ext.getCmp('Engagement');
			var engStat = Ext.getCmp('EngagementStatus');
			var engLoc = Ext.getCmp('location');
			if(engTyp.getValue().trim().toLowerCase()=='project' && engStat.getValue().trim().length ==0)
			{
				this.EngagementStatusText = 'Engagement status is required';
				engStat.markInvalid("Engagement status is required");
				engLoc.setReadOnly(false);
				engStat.setReadOnly(false);					
				return false;
			}
			else
			{
				if(engTyp.getValue().trim().toLowerCase()=='businessdevelopment' || engTyp.getValue().trim().toLowerCase()=='leave' || engTyp.getValue().trim().toLowerCase()=='training' || engTyp.getValue().trim().toLowerCase()=='business development' )
				{
					engLoc.setValue("");
					engStat.setValue("");
					engLoc.setReadOnly(true);
					engStat.setReadOnly(true);
				}
				engTyp.clearInvalid();
				engStat.clearInvalid();
				return true;
			}
	   },
	   EngagementStatusText : 'Engagement status is srequired'
	});

Ext.ns('Sch.plugins');

/**
* @class Sch.plugins.EventEditor
* @extends Ext.form.FormPanel
* @requires Ext.ux.form.DateTime
* @requires Ext.ux.form.SpinnerField
* A FormPanel used to edit scheduled events
* @constructor
* Create a new instance of this plugin
* @param {Object} config The configuration options
*/
Sch.plugins.MyEditor = Ext.extend(Ext.form.FormPanel, {

    /**
    * @cfg {String} hoursText The text to show after the hour spinner field
    */
    hoursText: 'hrs',

    /**
    * @cfg {Boolean} hideOnBlur True to hide this panel if a click is detected outside the panel (defaults to true)
    */
    hideOnBlur: true,

    /**
    * @cfg {Object} timeConfig Config for the TimeField constructor.
    */
 /*   
    timeConfig: {
        allowBlank: false,
        editable: false,
        forceSelection: true
    },
 */   
    timeConfig: null,
    fieldsPanelConfig: null,

    /**
    * @cfg {String} dateFormat This config parameter is passed to the TimeField constructor.
    */
    dateFormat: 'Y-m-d',

    /* 
    * Expands the editor
    * @param {Record} eventRecord The record to show in the editor panel
    */
    show: function (eventRecord) {

        this.eventRecord = eventRecord;

        // Load form panel fields
        this.getForm().loadRecord(eventRecord);

        var eventEl = this.grid.getElementFromEventRecord(eventRecord);
        this.el.anchorTo(eventEl, 'bl', this.getConstrainOffsets(eventEl));
        this.expand();
    },

    // TODO implement support for constraining the editor panel to the viewport
    getConstrainOffsets: function (eventEl) {
        return [0, 0];
    },

    cls: 'sch-eventeditor',
    layout: 'border',
    border: false,
    height: 320,
    width: 350,
    buttonAlign: 'left',
    

    initComponent: function () {

        if (this.hideOnBlur) {
            // Hide when clicking outside panel
            this.mon(Ext.getBody(), 'click', function (e) {
                if (!this.collapsed && !e.within(this.getEl()) && !Ext.fly(e.getTarget()).is('.x-combo-list-item')) {
                    this.collapse(false);
                }
            }, this);
        }

        // Collapse after render, otherwise rendering is messed up
        this.on('render', this.collapse, this);

        Ext.apply(this, {
            items: [
                {
                    region: 'north',
                    layout: 'absolute',
                    height: 35,
                    border: false,
                    cls: 'sch-eventeditor-timefields',
                    items: [
                    	{
                    		xtype:'label',
                    		html:'Start',
                    		x: 5,
                            y: 7,
                    		width:20
                    	},
                        this.startDateField = new Ext.form.DateField({
                            name: 'StartDate',
                            width: 100,
                            allowBlank: false,
                            fieldLabel  : 'Start',
                            x: 40,
                            y: 7,
                            editable : false,
                            format : this.dateFormat                            
/*                            x: 10,
                            y: 7,
                            timeFormat: this.timeFormat,
                            timeWidth: 60,
                            timeConfig: this.timeConfig,
                            dateFormat: this.dateFormat,
                            dateConfig: {
                                allowBlank: false
                            }
*/                            
                        }),
/*
                        this.endDateField = new Ext.ux.form.DateTime({
                            name: 'EndDate',
                            x: 190,
                            y: 7,
                            width: 160,
                           timeFormat: this.timeFormat,
                           timeWidth: 60,
                          timeConfig: this.timeConfig,
                            dateFormat: this.dateFormat,
                            dateConfig: {
                                allowBlank: false
                            }
                        })
 */                     {
                    		xtype:'label',
                    		html:'End',
                    		x: 160,
                            y: 7,
                    		width:20
                    	},
                        this.endDateField = new Ext.form.DateField({
                            name: 'EndDate',
                            width: 100,
                            allowBlank: false,
                            editable : false,
                            fieldLabel  : 'End',
                            x: 192,
                            y: 7,
                            format : this.dateFormat                            
                        })

                    ]
                },
                {
                    region: 'center',
                    layout: 'form',
                    style: 'background:#fff',
                    border: false,
                    cls: 'editorpanel',
                    labelAlign: 'left',
                    defaults: {
                        width: 135
                    },
                    items : [
                             engagementField = new Ext.form.ComboBox({
                                 typeAhead: true,
                                 triggerAction: 'all',
                                 lazyRender:true,
                 		        editable:false,
                 		        vtype: 'EngagementStatus',
                                 mode: 'local',
                                 emptyText :'Select Engagement',
                                 store:managerEngagementTypeStore,
                                 valueField: 'dataFieldName',
                                 displayField: 'displayFieldName',
                                 name : 'Engagement',
                                 id : 'Engagement',
                                 fieldLabel : 'Engagement Type',
                                 initialengagementStatusField: 'Engagement',
                                 allowBlank:false
                             }),
                             titleField = new Ext.form.TextField({
                                 name : 'Title',
                                 id:'title',
                                 fieldLabel : 'Engagement Name',
                                 allowBlank:false
                             }),
                             locationField = new Ext.form.TextField({
                                 name : 'Location',
                                 id:'location',
                                 fieldLabel : 'Engagement Location'
                             }),
                             engagementStatusField = new Ext.form.ComboBox({
                                 typeAhead: true,
                                 triggerAction: 'all',
                                 lazyRender:true,
                 		        editable:false,
                 		        vtype: 'EngagementStatus',
                                 mode: 'local',
                                 emptyText :'Select Status',
                                 store:engagementStatusStore,
                                 valueField: 'dataFieldName',
                                 displayField: 'displayFieldName',
                                 name : 'EngagementStatus',
                                 id : 'EngagementStatus',
                                 fieldLabel : 'Engagement Status'
//                                 allowBlank:false
                             }),
                             descriptionField = new Ext.form.TextArea({
                                 name : 'Description',
                                 fieldLabel : 'Description'
                             }),
                             creatorIdField = new Ext.form.Hidden({
                                 name : 'creatorId',
                                 fieldLabel : 'creatorId',
                                 value : empId
                             })

                        ],

                        
                    buttons : [
                        {
                            text : 'Save',
                            scope : this,
                            id : 'saveButton',
                            name : 'saveButton',
                            handler : function() 
                            {
                            	if(this.getForm().isValid())
                            	{
	                            	recTemp = this.eventRecord;
	                            	this.eventRecord.beginEdit();
	                            	this.getForm().updateRecord(this.eventRecord);
	 //                               this.eventRecord.set('StartDate', newStart);
	 //                               this.eventRecord.set('EndDate', newEnd);
	                                this.eventRecord.endEdit();
	                                this.collapse();
	//                              Ext.Ajax.on("requestcomplete", RequestComplete);
	//                                formPanel.getForm().submit
	                         //       ({
	                                
	                                	Ext.Ajax.request
	                                	({	
	                                	url:"schedule.do",
	                                	params:
	                                	{
	                                		id:this.eventRecord.get("Id"),
	                                		resourceId:this.eventRecord.get("ResourceId"),
	                                		title:this.eventRecord.get("Title"),
	                                		location:this.eventRecord.get("Location"),
	                                		engagement:this.eventRecord.get("Engagement"),
	                                		startDate:this.eventRecord.get("StartDate"),
	                                		description:this.eventRecord.get("Description"),
	                                		engagementStatus:this.eventRecord.get("EngagementStatus"),
	                                		endDate:this.eventRecord.get("EndDate"),
	                                		creatorId:this.eventRecord.get("creatorId")
	                                	},
	                                	success: function(objServerResponse)
	                                	{
	                                		eval("var resultSet = " +objServerResponse.responseText);
	//                                		this.eventRecord.beginEdit();
	//                                		this.eventRecord = recTemp;
	//                                        this.getForm().updateRecord(this.eventRecord);
	//                                		alert("this.eventRecord 212nwe var resultSet.id---"+resultSet.id);
	//                               		alert("this.recTemp---"+recTemp);
	//                                		this.eventRecord.endEdit();
	//                                		this.eventRecord.set('Id',resultSet.id);
	//                                        this.eventRecord.endEdit();
	                                		loadEventStore();
	//                                		this.collapse();
	                                	}
	                                	});	
                            	}	
         
                            }
                        },
                        {
                            text : 'Cancel',
                            scope : this,
                            handler : function () 
                            { 
                            	//this.eventRecord.store.remove(this.eventRecord);
                            	this.collapse(); 
                            }
                        }
                    ]
                }],
                listeners : {
                    expand : function() {
                        titleField.focus(true);
                    },
                    beforeexpand: function() {
//                    	alert('employeeType val ---'+(employeeType.trim()));
//                    	alert('employeeType == employee---'+(employeeType.trim() == 'employee'));
//                   	alert('(engagementField.getValue() == Leave)---'+(engagementField.getValue().trim().length));
//                   	if(!(engagementField.getValue().trim() == 'leave') && employeeType.trim() == 'employee' && engagementField.getValue().trim().length > 0)
                    	if(!(employeeType.trim()=='admin'))
                    	{
                        	if((!(isPartOfEmployeeStore(engagementField.getValue().trim())) && employeeType.trim() == 'employee' && engagementField.getValue().trim().length > 0) || !(isEventCreator(creatorIdField.getValue())))
                        	{
                            	engagementField.store = managerEngagementTypeStore;
                        		titleField.setReadOnly(true);
                        		engagementField.setReadOnly(true);
                        		descriptionField.setReadOnly(true);
                        		locationField.setReadOnly(true);
                        		engagementStatusField.setReadOnly(true);
                        		var svBt = Ext.getCmp('saveButton');
                        		svBt.setVisible(false);                        		
                        	}
                        	else
                        	{
                        		if(employeeType.trim() == 'employee')
                        			engagementField.store = employeeEngagementTypeStore;
                        		titleField.setReadOnly(false);
                        		engagementField.setReadOnly(false);
                        		descriptionField.setReadOnly(false);
                        		locationField.setReadOnly(false);
                        		engagementStatusField.setReadOnly(false);
                        		var svBt = Ext.getCmp('saveButton');
                        		svBt.setVisible(true);                        		
                        	}
                  		}
                    }
                }
//            })
        });
        Sch.plugins.MyEditor.superclass.initComponent.call(this);
    },

    init: function (grid) {
        grid.on('eventdblclick', this.onEventDblClick, this);
        grid.on('render', this.onGridRender, this);

        grid.on('beforedestroy', function () {
            grid.un('eventdblclick', this.onEventDblClick, this);
        });

        this.grid = grid;
    },

    onEventDblClick: function (g, evtRecord) {
        this.show(evtRecord);
    },

    onGridRender: function () {
        this.render(Ext.getBody());
    }
});


Post by mats »

Call that method as part of validating your form in your Save button handler:
if(this.getForm().isValid() && scheduler.isDateRangeAvailable(foo, bar, etc...))
                               {

Post by praveen.shastri »

Hello Mats,

The above code works fine but for one case. i.e when start date and end date are same it allows user to add event, is there any way to avoid this.

Regards,
Praveen

Post by mats »

Hmm, how are you calling that method? Could you post your code and the date parameters that fail?

Post by praveen.shastri »

Below is the code which calls the isDateRangeAvailable method.
if(this.getForm().isValid() && App.Scheduler.isDateRangeAvailable(stDate,enDate,this.eventRecord.id,this.eventRecord.get("ResourceId")))
isDateRangeAvailable method
function(start, end, eventId, resourceId)
{

var g = this.grid;
var available = true;
g.eventStore.each(

function(r) {
if (Date.intersectSpans(start, end, r.get('StartDate'), r.get('EndDate')) && (resourceId === r.get('ResourceId') && (!eventId || eventId !== r.id)))
{

available =false;
alert("This engagement overlaps with existing one or more engagements");
return false;
}
});
return available;
}
Below is the scenario where it is failing:

There is schedule 'A' whose End date is 20 Dec 2010 and if we try to add another schedule say 'B' with Start date as 20 Dec 2010 and end Date as 30 Dec 2010, it allows. Here end date of a schedule 'A' is overlapping with start date of schedule 'B'.

Regards,
Praveen

Post by mats »

Hmm, I can't reproduce this. If I add the exact same check the isDateRangeAvailable will return false. Can you put together a ZIP with your test case so that I can try it?

Post by praveen.shastri »

Hello Mats,

You are correct. The method isDateRangeAvailable is returning false in the scenario described above, which I feel is incorrect.

The dates (End date of event 'A' and Start date of event 'B') are overlapping, hence it should return true.

Post by mats »

There's some kind of misunderstanding here I think, could you post some screenshots showing what you're doing? If the dates overlap for the same resource (and not the samt event id), then the date-range is _not_ available, right? :)

Post Reply