diff --git a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/Angular/app/app.component.html b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/Angular/app/app.component.html index 5d72aef3ca89..d65ccc55673b 100644 --- a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/Angular/app/app.component.html +++ b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/Angular/app/app.component.html @@ -70,7 +70,7 @@
- Overlapping Rule + Allow Overlapping Appointments { formRef.current?.option('elementAttr.class', show ? '' : 'hide-informer'); }, []); - const alertConflictIfNeeded = useCallback(( + const handleConflict = useCallback(( e: SchedulerTypes.AppointmentAddingEvent | SchedulerTypes.AppointmentUpdatingEvent, appointmentData: Appointment, ) => { @@ -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) => { @@ -254,7 +254,7 @@ const App = () => {
- Overlapping Rule + Allow Overlapping Appointments { 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); @@ -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( () => ({ @@ -238,7 +238,7 @@ const App = () => {
- Overlapping Rule + Allow Overlapping Appointments
- Overlapping Rule + Allow Overlapping Appointments { @@ -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) => { diff --git a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/description.md b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/description.md new file mode 100644 index 000000000000..f0982a91a1f2 --- /dev/null +++ b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/description.md @@ -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. + + +### 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. \ No newline at end of file diff --git a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/jQuery/index.html b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/jQuery/index.html index 515568bf97e7..6caac5df7604 100644 --- a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/jQuery/index.html +++ b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/jQuery/index.html @@ -18,7 +18,7 @@
- Overlapping Rule + Allow Overlapping Appointments
diff --git a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/jQuery/index.js b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/jQuery/index.js index 7b07dd1a5189..b6a6887daf51 100644 --- a/apps/demos/Demos/Scheduler/ResolveTimeConflicts/jQuery/index.js +++ b/apps/demos/Demos/Scheduler/ResolveTimeConflicts/jQuery/index.js @@ -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'); @@ -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; @@ -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',