Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
</dx-scheduler>
<div class="options">
<div class="option">
<span>Overlapping Rule</span>
<span>Allow Overlapping Appointments</span>
<dx-select-box
[items]="overlappingRuleItems"
valueExpr="value"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ export class AppComponent {
assignees: Assignee[] = assignees;

overlappingRuleItems = [
{ value: 'sameResource', text: 'Allow across resources' },
{ value: 'allResources', text: 'Disallow all overlaps' },
{ value: 'sameResource', text: 'Different Resources' },
{ value: 'allResources', text: 'Never' },
];

assigneeIdEditorOptions = {
Expand Down Expand Up @@ -163,7 +163,7 @@ export class AppComponent {
);
}

private alertConflictIfNeeded(
private handleConflict(
e: DxSchedulerTypes.AppointmentAddingEvent | DxSchedulerTypes.AppointmentUpdatingEvent,
appointmentData: Appointment,
): void {
Expand Down Expand Up @@ -195,11 +195,11 @@ export class AppComponent {
}

onAppointmentAdding(e: DxSchedulerTypes.AppointmentAddingEvent): void {
this.alertConflictIfNeeded(e, e.appointmentData as Appointment);
this.handleConflict(e, e.appointmentData as Appointment);
}

onAppointmentUpdating(e: DxSchedulerTypes.AppointmentUpdatingEvent): void {
this.alertConflictIfNeeded(e, { ...e.oldData, ...e.newData } as Appointment);
this.handleConflict(e, { ...e.oldData, ...e.newData } as Appointment);
}

onOverlappingRuleChanged(e: DxSelectBoxTypes.ValueChangedEvent): void {
Expand Down
16 changes: 8 additions & 8 deletions apps/demos/Demos/Scheduler/ResolveTimeConflicts/React/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ const currentDate = new Date(2026, 1, 10);
const views: SchedulerTypes.ViewType[] = ['day', 'week', 'workWeek', 'month'];

const overlappingRuleItems = [
{ value: 'sameResource', text: 'Allow across resources' },
{ value: 'allResources', text: 'Disallow all overlaps' },
{ value: 'sameResource', text: 'Different Resources' },
{ value: 'allResources', text: 'Never' },
];

function getNextDay(date: Date): Date {
Expand Down Expand Up @@ -112,7 +112,7 @@ const App = () => {
formRef.current?.option('elementAttr.class', show ? '' : 'hide-informer');
}, []);

const alertConflictIfNeeded = useCallback((
const handleConflict = useCallback((
e: SchedulerTypes.AppointmentAddingEvent | SchedulerTypes.AppointmentUpdatingEvent,
appointmentData: Appointment,
) => {
Expand Down Expand Up @@ -144,12 +144,12 @@ const App = () => {
}, [setConflictError]);

const onAppointmentAdding = useCallback((e: SchedulerTypes.AppointmentAddingEvent) => {
alertConflictIfNeeded(e, e.appointmentData as Appointment);
}, [alertConflictIfNeeded]);
handleConflict(e, e.appointmentData as Appointment);
}, [handleConflict]);

const onAppointmentUpdating = useCallback((e: SchedulerTypes.AppointmentUpdatingEvent) => {
alertConflictIfNeeded(e, { ...e.oldData, ...e.newData } as Appointment);
}, [alertConflictIfNeeded]);
handleConflict(e, { ...e.oldData, ...e.newData } as Appointment);
}, [handleConflict]);

const popupOptions = useMemo(() => ({
onInitialized: (e: PopupTypes.InitializedEvent) => {
Expand Down Expand Up @@ -254,7 +254,7 @@ const App = () => {

<div className="options">
<div className="option">
<span>Overlapping Rule</span>
<span>Allow Overlapping Appointments</span>
<SelectBox
items={overlappingRuleItems}
valueExpr="value"
Expand Down
16 changes: 8 additions & 8 deletions apps/demos/Demos/Scheduler/ResolveTimeConflicts/ReactJs/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { data, assignees } from './data.js';
const currentDate = new Date(2026, 1, 10);
const views = ['day', 'week', 'workWeek', 'month'];
const overlappingRuleItems = [
{ value: 'sameResource', text: 'Allow across resources' },
{ value: 'allResources', text: 'Disallow all overlaps' },
{ value: 'sameResource', text: 'Different Resources' },
{ value: 'allResources', text: 'Never' },
];
function getNextDay(date) {
const next = new Date(date);
Expand Down Expand Up @@ -80,7 +80,7 @@ const App = () => {
showConflictErrorRef.current = show;
formRef.current?.option('elementAttr.class', show ? '' : 'hide-informer');
}, []);
const alertConflictIfNeeded = useCallback(
const handleConflict = useCallback(
(e, appointmentData) => {
if (!detectConflict(e.component, appointmentData, overlappingRuleRef.current)) {
setConflictError(false);
Expand Down Expand Up @@ -113,15 +113,15 @@ const App = () => {
);
const onAppointmentAdding = useCallback(
(e) => {
alertConflictIfNeeded(e, e.appointmentData);
handleConflict(e, e.appointmentData);
},
[alertConflictIfNeeded],
[handleConflict],
);
const onAppointmentUpdating = useCallback(
(e) => {
alertConflictIfNeeded(e, { ...e.oldData, ...e.newData });
handleConflict(e, { ...e.oldData, ...e.newData });
},
[alertConflictIfNeeded],
[handleConflict],
);
const popupOptions = useMemo(
() => ({
Expand Down Expand Up @@ -238,7 +238,7 @@ const App = () => {

<div className="options">
<div className="option">
<span>Overlapping Rule</span>
<span>Allow Overlapping Appointments</span>
<SelectBox
items={overlappingRuleItems}
valueExpr="value"
Expand Down
12 changes: 6 additions & 6 deletions apps/demos/Demos/Scheduler/ResolveTimeConflicts/Vue/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@

<div class="options">
<div class="option">
<span>Overlapping Rule</span>
<span>Allow Overlapping Appointments</span>
<DxSelectBox
:items="overlappingRuleItems"
value-expr="value"
Expand Down Expand Up @@ -114,8 +114,8 @@ const views: DxSchedulerTypes.ViewType[] = ['day', 'week', 'workWeek', 'month'];
const formElementAttr = { class: 'hide-informer', id: 'form' };

const overlappingRuleItems = [
{ value: 'sameResource', text: 'Allow across resources' },
{ value: 'allResources', text: 'Disallow all overlaps' },
{ value: 'sameResource', text: 'Different Resources' },
{ value: 'allResources', text: 'Never' },
];

function getNextDay(date: Date): Date {
Expand Down Expand Up @@ -189,7 +189,7 @@ const popupOptions = {
},
};

const alertConflictIfNeeded = (
const handleConflict = (
e: DxSchedulerTypes.AppointmentAddingEvent | DxSchedulerTypes.AppointmentUpdatingEvent,
appointmentData: Appointment,
) => {
Expand Down Expand Up @@ -221,11 +221,11 @@ const alertConflictIfNeeded = (
};

const onAppointmentAdding = (e: DxSchedulerTypes.AppointmentAddingEvent) => {
alertConflictIfNeeded(e, e.appointmentData as Appointment);
handleConflict(e, e.appointmentData as Appointment);
};

const onAppointmentUpdating = (e: DxSchedulerTypes.AppointmentUpdatingEvent) => {
alertConflictIfNeeded(e, { ...e.oldData, ...e.newData } as Appointment);
handleConflict(e, { ...e.oldData, ...e.newData } as Appointment);
};

const onFormInitialized = (e: DxFormTypes.InitializedEvent) => {
Expand Down
26 changes: 26 additions & 0 deletions apps/demos/Demos/Scheduler/ResolveTimeConflicts/description.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
This example prevents appointment time conflicts in DevExtreme Scheduler. Use the **Allow Overlapping Appointments** select-box to select a time conflict resolution mode.
<!--split-->

### Detect Conflicts

Handle the [onAppointmentAdding](/Documentation/ApiReference/UI_Components/dxScheduler/Configuration/#onAppointmentAdding) and [onAppointmentUpdating](/Documentation/ApiReference/UI_Components/dxScheduler/Configuration/#onAppointmentUpdating) events to check if a new or updated appointment creates a time conflict. Set `e.cancel = true` to block the operation if necessary.

Call [getOccurrences](/Documentation/ApiReference/UI_Components/dxScheduler/Methods/#getOccurrences) to expand [recurring appointments](/Documentation/Guide/UI_Components/Scheduler/Appointments/Appointment_Types/#Recurring_Appointments) into individual occurrences within the target range. Check for overlapping time ranges.

### Conflict Detection Modes

The demo supports two modes:

- **Different resources**: appointments assigned to different resources (assignees) can overlap.
- **Never**: overlapping appointments are not allowed, regardless of resource assignment.

To implement resource-aware checks, access appointments and compare their `assigneeId` field values.

### Display Errors

When a conflict is detected, the demo displays the error in the following ways:

- A message box.
- An inline validation message (if an appointment edit form is active).

To display inline validation, use the `customizeItem` function to add a custom form item inside [editing.form](/Documentation/ApiReference/UI_Components/dxScheduler/Configuration/editing/form/) and attach custom `validationRules` to time editors.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<div id="scheduler"></div>
<div class="options">
<div class="option">
<span>Overlapping Rule</span>
<span>Allow Overlapping Appointments</span>
<div id="overlapping-rule"></div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,10 @@ $(() => {
},
},
onAppointmentAdding(e) {
alertConflictIfNeeded(e, e.appointmentData);
handleConflict(e, e.appointmentData);
},
onAppointmentUpdating(e) {
alertConflictIfNeeded(e, e.newData);
handleConflict(e, e.newData);
},
}).dxScheduler('instance');

Expand All @@ -121,7 +121,7 @@ $(() => {
form?.option('elementAttr.class', show ? '' : 'hide-informer');
}

function alertConflictIfNeeded(e, appointmentData) {
function handleConflict(e, appointmentData) {
if (!detectConflict(appointmentData)) {
setConflictError(false);
return;
Expand Down Expand Up @@ -198,8 +198,8 @@ $(() => {

$('#overlapping-rule').dxSelectBox({
items: [
{ value: 'sameResource', text: 'Allow across resources' },
{ value: 'allResources', text: 'Disallow all overlaps' },
{ value: 'sameResource', text: 'Different Resources' },
{ value: 'allResources', text: 'Never' },
],
valueExpr: 'value',
displayExpr: 'text',
Expand Down
Loading