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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
"fs-extra": "^11.2.0",
"html-webpack-plugin": "^5.6.0",
"style-loader": "^4.0.0",
"webpack": "^5.95.0",
"webpack": "^5.101.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.1.0"
}
Expand Down
79 changes: 54 additions & 25 deletions src/examples/pages/Custom-Time/class-based.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import * as antdLocale from 'antd/locale/pt_BR';
import * as dayjsLocale from 'dayjs/locale/pt-br';
import React, { Component } from 'react';

import { DemoData, Scheduler, SchedulerData, ViewType, wrapperFun } from '../../../index';

class CustomTime extends Component {
Expand All @@ -17,8 +16,8 @@ class CustomTime extends Component {
{
besidesWidth: 300,
dayMaxEvents: 99,
dayStartFrom: 8,
dayStopTo: 18,
dayStartFrom: 8, // 8 AM
dayStopTo: 18, // 7 PM
customMaxEvents: 9965,
eventItemPopoverTrigger: 'click',
schedulerContentHeight: '100%',
Expand All @@ -35,6 +34,36 @@ class CustomTime extends Component {
};
}

// Helper function to clamp times to 8 AM and 7 PM
clampEventTimes = (schedulerData, start, end) => {
const { dayStartFrom, dayStopTo } = schedulerData.config;
const startDate = new Date(start);
const endDate = new Date(end);

// Get the date part to preserve it
const dateStr = startDate.toISOString().split('T')[0];

// Clamp start time to 8 AM
if (startDate.getHours() < dayStartFrom) {
startDate.setHours(dayStartFrom, 0, 0, 0);
}

// Clamp end time to 7 PM
if (endDate.getHours() > dayStopTo || (endDate.getHours() === dayStopTo && endDate.getMinutes() > 0)) {
endDate.setHours(dayStopTo, 0, 0, 0);
}

// Ensure end is not before start
if (endDate < startDate) {
endDate.setTime(startDate.getTime() + 30 * 60 * 1000); // Default to 30 minutes after start
}

return {
clampedStart: `${dateStr}T${startDate.getHours().toString().padStart(2, '0')}:00:00`,
clampedEnd: `${dateStr}T${endDate.getHours().toString().padStart(2, '0')}:00:00`,
};
};
Comment on lines +37 to +65
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical issues in time clamping logic need to be addressed.

The clampEventTimes helper method has several logical and implementation issues:

  1. Inconsistent hour boundary checking: Line 52 checks if endDate.getHours() > dayStopTo but dayStopTo is 18 (6 PM), so events ending at exactly 6 PM would be incorrectly allowed.

  2. Incomplete time preservation: The method only preserves hours but discards minutes and seconds from the original times, potentially losing precision.

  3. Timezone handling: Using new Date() constructor with ISO strings may cause timezone conversion issues.

  4. Inconsistent return format: Returns ISO-like strings but only with hour precision, not matching typical ISO format.

Apply this diff to fix the time clamping logic:

-  clampEventTimes = (schedulerData, start, end) => {
-    const { dayStartFrom, dayStopTo } = schedulerData.config;
-    const startDate = new Date(start);
-    const endDate = new Date(end);
-
-    // Get the date part to preserve it
-    const dateStr = startDate.toISOString().split('T')[0];
-
-    // Clamp start time to 8 AM
-    if (startDate.getHours() < dayStartFrom) {
-      startDate.setHours(dayStartFrom, 0, 0, 0);
-    }
-
-    // Clamp end time to 7 PM
-    if (endDate.getHours() > dayStopTo || (endDate.getHours() === dayStopTo && endDate.getMinutes() > 0)) {
-      endDate.setHours(dayStopTo, 0, 0, 0);
-    }
-
-    // Ensure end is not before start
-    if (endDate < startDate) {
-      endDate.setTime(startDate.getTime() + 30 * 60 * 1000); // Default to 30 minutes after start
-    }
-
-    return {
-      clampedStart: `${dateStr}T${startDate.getHours().toString().padStart(2, '0')}:00:00`,
-      clampedEnd: `${dateStr}T${endDate.getHours().toString().padStart(2, '0')}:00:00`,
-    };
-  };
+  clampEventTimes = (schedulerData, start, end) => {
+    const { dayStartFrom, dayStopTo } = schedulerData.config;
+    const startDate = new Date(start);
+    const endDate = new Date(end);
+
+    // Get the date part to preserve it  
+    const dateStr = startDate.toISOString().split('T')[0];
+
+    // Clamp start time to working hours start
+    if (startDate.getHours() < dayStartFrom) {
+      startDate.setHours(dayStartFrom, 0, 0, 0);
+    }
+
+    // Clamp end time to working hours end (6 PM = 18:00)
+    if (endDate.getHours() >= dayStopTo) {
+      endDate.setHours(dayStopTo - 1, 59, 59, 999); // Set to 17:59:59 for 6 PM boundary
+    }
+
+    // Ensure end is not before start
+    if (endDate <= startDate) {
+      endDate.setTime(startDate.getTime() + 30 * 60 * 1000); // Default to 30 minutes after start
+    }
+
+    return {
+      clampedStart: startDate.toISOString(),
+      clampedEnd: endDate.toISOString(),
+    };
+  };

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/examples/pages/Custom-Time/class-based.jsx between lines 37 and 65, the
clampEventTimes function has issues with boundary checks, time precision,
timezone handling, and return format. Fix it by adjusting the end time check to
correctly clamp events ending exactly at the boundary hour, preserve minutes and
seconds when clamping start and end times, avoid timezone conversion by
manipulating date components directly rather than relying on ISO string parsing,
and return fully formatted ISO 8601 strings including hours, minutes, and
seconds to maintain consistency and precision.


render() {
const { viewModel } = this.state;
return (
Expand All @@ -61,13 +90,13 @@ class CustomTime extends Component {
);
}

prevClick = schedulerData => {
prevClick = (schedulerData) => {
schedulerData.prev();
schedulerData.setEvents(DemoData.events);
this.setState({ viewModel: schedulerData });
};

nextClick = schedulerData => {
nextClick = (schedulerData) => {
schedulerData.next();
schedulerData.setEvents(DemoData.events);
this.setState({ viewModel: schedulerData });
Expand All @@ -78,20 +107,18 @@ class CustomTime extends Component {
schedulerData.setViewType(view.viewType, view.showAgenda, view.isEventPerspective);
schedulerData.setEvents(DemoData.events);
this.setState({ viewModel: schedulerData });

function secondsBetween(date1, date2) {
const diff = Math.abs(date1.getTime() - date2.getTime());
return diff / 1000;
}

console.log('Elapsed seconds: ' + secondsBetween(start, new Date()));
};

onSelectDate = (schedulerData, date) => {
schedulerData.setDate(date);
schedulerData.setEvents(DemoData.events);
this.setState({
viewModel: schedulerData,
});
this.setState({ viewModel: schedulerData });
};

eventClicked = (schedulerData, event) => {
Expand All @@ -107,17 +134,19 @@ class CustomTime extends Component {
};

newEvent = (schedulerData, slotId, slotName, start, end, type, item) => {
if (confirm(`Do you want to create a new event? {slotId: ${slotId}, slotName: ${slotName}, start: ${start}, end: ${end}, type: ${type}, item: ${item}}`)) {
const { clampedStart, clampedEnd } = this.clampEventTimes(schedulerData, start, end);

if (confirm(`Do you want to create a new event? {slotId: ${slotId}, slotName: ${slotName}, start: ${clampedStart}, end: ${clampedEnd}, type: ${type}, item: ${item}}`)) {
let newFreshId = 0;
schedulerData.events.forEach(item => {
schedulerData.events.forEach((item) => {
if (item.id >= newFreshId) newFreshId = item.id + 1;
});

let newEvent = {
id: newFreshId,
title: 'New event you just created',
start: start,
end: end,
start: clampedStart,
end: clampedEnd,
resourceId: slotId,
bgColor: 'purple',
};
Expand All @@ -127,26 +156,29 @@ class CustomTime extends Component {
};

updateEventStart = (schedulerData, event, newStart) => {
if (confirm(`Do you want to adjust the start of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newStart: ${newStart}}`)) {
schedulerData.updateEventStart(event, newStart);
const { clampedStart } = this.clampEventTimes(schedulerData, newStart, event.end);
if (confirm(`Do you want to adjust the start of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newStart: ${clampedStart}}`)) {
schedulerData.updateEventStart(event, clampedStart);
this.setState({ viewModel: schedulerData });
}
this.setState({ viewModel: schedulerData });
};

updateEventEnd = (schedulerData, event, newEnd) => {
if (confirm(`Do you want to adjust the end of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newEnd: ${newEnd}}`)) {
schedulerData.updateEventEnd(event, newEnd);
const { clampedEnd } = this.clampEventTimes(schedulerData, event.start, newEnd);
if (confirm(`Do you want to adjust the end of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newEnd: ${clampedEnd}}`)) {
schedulerData.updateEventEnd(event, clampedEnd);
this.setState({ viewModel: schedulerData });
}
this.setState({ viewModel: schedulerData });
};

moveEvent = (schedulerData, event, slotId, slotName, start, end) => {
const { clampedStart, clampedEnd } = this.clampEventTimes(schedulerData, start, end);
if (
confirm(
`Do you want to move the event? {eventId: ${event.id}, eventTitle: ${event.title}, newSlotId: ${slotId}, newSlotName: ${slotName}, newStart: ${start}, newEnd: ${end}`
`Do you want to move the event? {eventId: ${event.id}, eventTitle: ${event.title}, newSlotId: ${slotId}, newSlotName: ${slotName}, newStart: ${clampedStart}, newEnd: ${clampedEnd}}`
)
) {
schedulerData.moveEvent(event, slotId, slotName, start, end);
schedulerData.moveEvent(event, slotId, slotName, clampedStart, clampedEnd);
this.setState({ viewModel: schedulerData });
}
};
Expand All @@ -156,7 +188,6 @@ class CustomTime extends Component {
schedulerData.next();
schedulerData.setEvents(DemoData.events);
this.setState({ viewModel: schedulerData });

schedulerContent.scrollLeft = maxScrollLeft - 10;
}
};
Expand All @@ -166,13 +197,11 @@ class CustomTime extends Component {
schedulerData.prev();
schedulerData.setEvents(DemoData.events);
this.setState({ viewModel: schedulerData });

schedulerContent.scrollLeft = 10;
}
};

onScrollTop = () => console.log('onScrollTop');

onScrollBottom = () => console.log('onScrollBottom');

toggleExpandFunc = (schedulerData, slotId) => {
Expand All @@ -181,4 +210,4 @@ class CustomTime extends Component {
};
}

export default wrapperFun(CustomTime);
export default wrapperFun(CustomTime);