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
Support Forum
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;
},
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
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());
}
});
Below is the code which calls the isDateRangeAvailable method.
isDateRangeAvailable method
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
if(this.getForm().isValid() && App.Scheduler.isDateRangeAvailable(stDate,enDate,this.eventRecord.id,this.eventRecord.get("ResourceId")))
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;
}
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