How to overwrite event insert/delete/edit with remote database?

Hello,


I'm trying to implement the Schedule component in my web app using Nodejs, React, and Supabase as my database. I've binded the ScheduleComponent data to my database. Now I want to overwrite the insert/delete/edit operations so that they work with my database not just local data. It works, but I keep getting a weird runtime error that I can't figure out how to fix. Here's my code. What should I do differently?

"use client";

import "../../../App.css";
import { useEffect, useState } from "react";
import GreyBeatLoader from "../BeatLoaders/GreyBeatLoader";
import styles from "../RoomListDetailView/RoomListDetailView.module.css";
import { useSession, useSupabaseClient, useSessionContext } from '@supabase/auth-helpers-react';
import { createClientComponentClient } from "@supabase/auth-helpers-nextjs";
import DateTimePicker from 'react-datetime-picker';
import { Day, Inject, Month, ScheduleComponent, ViewDirective, ViewsDirective, Week } from "@syncfusion/ej2-react-schedule";
import { registerLicense } from "@syncfusion/ej2-base";

registerLicense(
"Ngo9BigBOggjHTQxAR8/V1NBaF5cXmZCekx+WmFZfVpgdVVMZVpbRnFPMyBoS35RckVnWX5fcHFTRGdbVkRz"
);


const EventsDetailView = ({
listType,
roomId,
}) => {
const [events, setEvents] = useState([]);
const [loading, setLoading] = useState(true);
const [startDate, setStartDate] = useState(new Date());
const [endDate, setEndDate] = useState(new Date());
const [eventTitle, setEventTitle] = useState("");
const [eventDescription, setEventDescription] = useState("");
const fieldsData = {
id: { name: 'id' },
subject: { name: 'title' },
location: { name: 'location' },
description: { name: 'description' },
startTime: { name: 'startTime' },
endTime: { name: 'endTime' }
};
const eventSettings = {
includeFiltersInQuery: true,
dataSource: events,
fields: fieldsData,
allowDeleting: true
}

const getEvents = (shouldLoad) => {
fetch(`/api/events?roomId=${roomId}`)
.then((res) => res.json())
.then((data) => {
const dataArray = Object.values(data)
// Map to Syncfusion's event format
const mappedEvents = dataArray.map((event) => ({
id: event.id,
title: event.title,
description: event.description,
startTime: new Date(event.startTime),
endTime: new Date(event.endTime),
isAllDay: event.isAllDay
}));
setEvents(mappedEvents);
if (shouldLoad) {
setLoading(false);
}
});
};

const addEvent = async () => {
fetch(`/api/events?roomId=${roomId}`, {
method: "POST",
body: JSON.stringify({
roomId: roomId,
data: {
title: eventTitle,
description: eventDescription,
startDate: startDate,
endDate: endDate,
},
}),
}).then(() => {
getEvents(false);
setEventTitle("");
setEventDescription(null);
setStartDate(new Date());
setEndDate(new Date());
});
};

useEffect(() => {
getEvents(true);
}, [startDate, endDate]);

const handleActionBegin = async (args) => {
if (args.requestType === 'eventRemove') {
// Extract the event ID of the event being deleted
const eventId = args.deletedRecords[0].id;
try {
const response = await fetch(`/api/events`, {
method: "DELETE",
body: JSON.stringify({
eventId: eventId,
}),
});

if (response.ok) {
getEvents(false);
return response;
} else {
console.error('Error deleting event:', response.statusText);
}
console.log("Response", response)
return response;
} catch (error) {
console.error('Error deleting event:', error.message);
}
} else if (args.requestType === 'eventChange') {
const updatedEvent = args.changedRecords[0];
try {
console.log("EDIT EVENT", updatedEvent);

const response = await fetch(`/api/events`, {
method: "PUT",
body: JSON.stringify({
eventData: updatedEvent,
})
});

if (response.ok) {
getEvents(false);
return response;
} else {
console.error('Error editing event:', response.statusText);
}
} catch (error) {
console.error('Error editing event:', error.message);
}
}
};

return (
<div className="flex justify-center items-center min-h-screen">
<p className={styles.boxTitle}>{listType}</p>
{loading ? (
<GreyBeatLoader />
) : (
<>

<div>
<ScheduleComponent
// selectedDate={new Date()}
readonly={false}
eventSettings={eventSettings}
currentView="Week"
actionBegin={handleActionBegin}
>
<ViewsDirective>
<ViewDirective option="Day" />
<ViewDirective option="Week" />
<ViewDirective option="Month" />
</ViewsDirective>

<Inject services={[Day, Week, Month]} />
</ScheduleComponent>
</div>
< div style={{width: "400px", margin: "30px auto"}}>
<input
type="text"
placeholder={`Add title`}
className={styles.addTaskInput}
value={eventTitle}
onChange={(event) => setEventTitle(event.target.value)}
/>
<input
type="text"
placeholder={`Add description`}
className={styles.addTaskInput}
value={eventDescription}
onChange={(event) => setEventDescription(event.target.value)}
/>
<p> Start of your event</p>
<DateTimePicker onChange={setStartDate} value={startDate} />
<p> End of your event</p>
<DateTimePicker onChange={setEndDate} value={endDate} />
<div>
<button
className={styles.addTaskButton}
onClick={async () => await addEvent()}
>
Add Event
</button>
</div>
</div>

{/* < div style={{width: "400px", margin: "30px auto"}}>
{session ?
<>
<h2> Hey there {session.user.email}</h2>
<button onClick={() => signOut()}> Sign Out</button>
</>
:
<>
<button onClick={() => googleSignIn()}> Sign in with Google</button>
</>
}
</div> */}
</>
)}
</div>
);
};

export default EventsDetailView;


4 Replies

AK Ashokkumar Karuppasamy Syncfusion Team April 5, 2024 02:44 PM UTC

Hi Victoria,

Based on the details provided, we have attempted to create a runnable sample, but we are encountering difficulties understanding the issues you are facing on your end. Providing more information about the specific problems you're encountering will greatly assist us in understanding and resolving the issue effectively. Please share any error messages, unexpected behaviors, or specific challenges you are encountering so that we can better assist you.

Additionally, we suggest implementing event insertion, deletion, and editing functionalities with remote database CRUD operations using the DataManager. Below, we have provided sample code along with attached User Guide documentation and forum links for your reference. Please give it a try, and feel free to reach out if you need any further assistance.

Sample:
https://stackblitz.com/edit/react-front-end-sample-chasmr?file=index.js

UG:
https://ej2.syncfusion.com/react/documentation/data/getting-started
https://ej2.syncfusion.com/react/documentation/schedule/data-binding#scheduler-crud-actions

Forum:
https://forumassist.syncfusion.com/178606
https://forumassist.syncfusion.com/178161

Service: https://www.syncfusion.com/downloads/support/forum/170725/ze/ScheduleCRUD1166139915


Regards,

Ashok



VP Victoria Petrova April 5, 2024 03:38 PM UTC

Hello,


I'll share more about the current strategy I used to implement this. I bind the Schedule component to a variable `events`. This variable is empty at first but when the page loads we make an API call to our database to get all relevant events. Then these events are reformatted to match Syncfusion's expected structure for an event object. This all works great. 


Then, as you can see from the code I already shared I overwrote the delete and edit actions. They are executed as expected. However, the Schedule component throws a Runtime error. When I edit an event and click on the save button, it is in fact edited as expected but this error is thrown and the edit popup remains open:

Uncaught TypeError: e.replace is not a function

    at T (265-2af8373500626118.js:1:12848)

    at t.update (265-2af8373500626118.js:1:208708)

    at t.batchRequest (265-2af8373500626118.js:1:206623)

    at e.saveChanges (265-2af8373500626118.js:1:251007)

    at e.<anonymous> (265-2af8373500626118.js:1:1044478)

    at e.notify (265-2af8373500626118.js:1:45267)

    at t.trigger (265-2af8373500626118.js:1:393781)

    at e.saveEvent (265-2af8373500626118.js:1:1043983)

    at t.saveEvent (265-2af8373500626118.js:1:1138395)

    at e.processCrudActions (265-2af8373500626118.js:1:1003145)


How can I prevent this error or just force close the edit popup menu?


Analogously, when I delete an event it all works. And the popup is closed which is great. But there is another runtime error:

Uncaught TypeError: e.indexOf is not a function

    at e.getObject (265-2af8373500626118.js:1:265847)

    at t.remove (265-2af8373500626118.js:1:208512)

    at t.batchRequest (265-2af8373500626118.js:1:206680)

    at e.saveChanges (265-2af8373500626118.js:1:251007)

    at e.<anonymous> (265-2af8373500626118.js:1:1053219)

    at e.notify (265-2af8373500626118.js:1:45267)

    at t.trigger (265-2af8373500626118.js:1:393781)

    at e.processDelete (265-2af8373500626118.js:1:1052338)

    at e.deleteEvent (265-2af8373500626118.js:1:1045330)

    at e.dialogButtonClick (265-2af8373500626118.js:1:701859)



What are the steps to apply the data manager. I have a database where we store all events. To get the relevant events we call this endpoint: 

const getEvents = (shouldLoad) => {
fetch(`/api/events?roomId=${roomId}`)
.then((res) => res.json())
.then((data) => {
const dataArray = Object.values(data)
// Map to Syncfusion's event format
const mappedEvents = dataArray.map((event) => ({
id: event.id,
title: event.title,
description: event.description,
startTime: new Date(event.startTime),
endTime: new Date(event.endTime),
isAllDay: event.isAllDay
}));
setEvents(mappedEvents);
if (shouldLoad) {
setLoading(false);
}
});
};



Step 1: Create DataManager. Should we create it like this:

const dataManger = new DataManager({
url: `/api/events?roomId=${roomId}`,
adaptor: new UrlAdaptor(),
});
const eventSettings = {
includeFiltersInQuery: true,
dataSource: dataManager,
fields: fieldsData,
allowDeleting: true
}




Step 2: Store the events in our database with Syncfusion's structure for an event object. 

Is that right? Should our database events have the same fields as the event object that the Schedule Component expects? If so, what are they?


Are there any other steps after that? The documentation is confusing. There isn't a straightforward tutorial that explains all the steps and how to properly connect to a database to simply feed into the schedule component. 






VP Victoria Petrova replied to Ashokkumar Karuppasamy April 5, 2024 03:38 PM UTC

Hello,


I'll share more about the current strategy I used to implement this. I bind the Schedule component to a variable `events`. This variable is empty at first but when the page loads we make an API call to our database to get all relevant events. Then these events are reformatted to match Syncfusion's expected structure for an event object. This all works great. 


Then, as you can see from the code I already shared I overwrote the delete and edit actions. They are executed as expected. However, the Schedule component throws a Runtime error. When I edit an event and click on the save button, it is in fact edited as expected but this error is thrown and the edit popup remains open:

Uncaught TypeError: e.replace is not a function

    at T (265-2af8373500626118.js:1:12848)

    at t.update (265-2af8373500626118.js:1:208708)

    at t.batchRequest (265-2af8373500626118.js:1:206623)

    at e.saveChanges (265-2af8373500626118.js:1:251007)

    at e.<anonymous> (265-2af8373500626118.js:1:1044478)

    at e.notify (265-2af8373500626118.js:1:45267)

    at t.trigger (265-2af8373500626118.js:1:393781)

    at e.saveEvent (265-2af8373500626118.js:1:1043983)

    at t.saveEvent (265-2af8373500626118.js:1:1138395)

    at e.processCrudActions (265-2af8373500626118.js:1:1003145)


How can I prevent this error or just force close the edit popup menu?


Analogously, when I delete an event it all works. And the popup is closed which is great. But there is another runtime error:

Uncaught TypeError: e.indexOf is not a function

    at e.getObject (265-2af8373500626118.js:1:265847)

    at t.remove (265-2af8373500626118.js:1:208512)

    at t.batchRequest (265-2af8373500626118.js:1:206680)

    at e.saveChanges (265-2af8373500626118.js:1:251007)

    at e.<anonymous> (265-2af8373500626118.js:1:1053219)

    at e.notify (265-2af8373500626118.js:1:45267)

    at t.trigger (265-2af8373500626118.js:1:393781)

    at e.processDelete (265-2af8373500626118.js:1:1052338)

    at e.deleteEvent (265-2af8373500626118.js:1:1045330)

    at e.dialogButtonClick (265-2af8373500626118.js:1:701859)



What are the steps to apply the data manager. I have a database where we store all events. To get the relevant events we call this endpoint: 

const getEvents = (shouldLoad) => {
fetch(`/api/events?roomId=${roomId}`)
.then((res) => res.json())
.then((data) => {
const dataArray = Object.values(data)
// Map to Syncfusion's event format
const mappedEvents = dataArray.map((event) => ({
id: event.id,
title: event.title,
description: event.description,
startTime: new Date(event.startTime),
endTime: new Date(event.endTime),
isAllDay: event.isAllDay
}));
setEvents(mappedEvents);
if (shouldLoad) {
setLoading(false);
}
});
};



Step 1: Create DataManager. Should we create it like this:

const dataManger = new DataManager({
url: `/api/events?roomId=${roomId}`,
adaptor: new UrlAdaptor(),
});
const eventSettings = {
includeFiltersInQuery: true,
dataSource: dataManager,
fields: fieldsData,
allowDeleting: true
}




Step 2: Store the events in our database with Syncfusion's structure for an event object. 

Is that right? Should our database events have the same fields as the event object that the Schedule Component expects? If so, what are they?


Are there any other steps after that? The documentation is confusing. There isn't a straightforward tutorial that explains all the steps and how to properly connect to a database to simply feed into the schedule component.



VR Vijay Ravi Syncfusion Team April 12, 2024 03:55 PM UTC

Hi Victoria,


Query 1: How can I prevent this error or just force close the edit popup menu?


We suspect that the CRUD actions for the schedule are not properly completed, and the API calls at your end may be causing the runtime errors you are experiencing during edit and delete events. Therefore, we suggest using the DataManager to render events with insert, update, and delete operations as expected.


Query 2: What are the steps to apply the data manager. I have a database where we store all events. To get the relevant events we call this endpoint:


UrlAdoptor() will be binding, likely to access or retrieve data from an external source. storing events in an object, which allows for easier manipulation and management of the data. It also mentions the importance of defining field mappings, which establish the relationship between fields in the data source and the scheduler component, ensuring proper display and functionality. Refer the below shared documentation.

Field mapping documentation: https://ej2.syncfusion.com/react/documentation/schedule/appointments#built-in-fields


Query 3: Create DataManager and Store the events in our database with Syncfusion's structure for an event object.


We suggest using the crudURL property in the DataManager. When you insert, edit, or remove an event, a POST request will be sent to the crudURL. In that http request you can get all added, changed and deleted event details as shown in the below shared code snippet. By using that you can modify your database. Refer the shared sample.


 

 const [dataManager, setDataManager] = useState(new DataManager({

    url: `http://localhost:54738/Home/LoadData?view=${this.id}`,

    crudUrl: 'http://localhost:54738/Home/UpdateData',

    crossDomain: true,

    adaptor: new UrlAdaptor(),

  }));

    const fieldsData = {

      id: { name: 'Id' },

      subject: { name: 'Subject' },

      location: { name: 'Location' },

      description: { name: 'Description' },

      startTime: { name: 'StartTime' },

      endTime: { name: 'EndTime' }

    };


Sample: https://stackblitz.com/edit/react-enf4ba-wn22t8?file=index.js



Please get back to us with the required details for further assistance.


Regards,

Vijay


Loader.
Up arrow icon