BoldDeskWe are launching BoldDesk on Product Hunt soon. Learn more & follow us.
Hi there,
I am trying to create custom quick pop up's for appointments using functional components in React. It seems all the questions about this were previously answered using class components, so I'm having some trouble. I was able to figure out how to create the quick pop up for an "event" but I cannot figure out how to create a custom quick pop up for a "cell" that includes all the functionality I want it to:
Hi Jessica,
Query 1, 3 & 4:
We have customized the quick popup for appointments using a functional component as per your requirements. Refer to the below demo and UG links for more details.
Sample: https://stackblitz.com/edit/react-schedule-functional-quick-info?file=package.json
Demo: https://ej2.syncfusion.com/react/demos/#/material/schedule/quick-info-template
UG: https://ej2.syncfusion.com/react/documentation/schedule/editor-template#customizing-quick-popups
Query 2:
Before proceeding further with your query, we need some more details to validate further. Can you please share what information you want to pull from the custom resource header? or the use case scenario of your requirement it will help us to provide the solution for your query earlier.
Satheesh Kumar B
Thank you! This has been tremendously helpful.
With regards to the Resource Header, see my code below. The data that is passed in is an array of objects, so there is one row in the scheduler per object. When I add a new event within the scheduler, I click on a specific time slot in a specified row (the row contains a specific individuals information, aka the resource header). So, if a row is designated to show appointments for "Jessica Newman", I want that individual's information to automatically fill in to a <span></span> in the quick pop up when I create a new event via a pop up. Does that make sense?
Jessica,
You can add the resource details in the quick popup when the cell clicks by using the Schedule’s cellClick event. you can get the resource details by passing the groupIndex as a parameter to the getResoucesByIndex method, and then use the details in the template, as shown in the below snippet.
Sample: https://stackblitz.com/edit/react-schedule-functional-quick-info-4mie28?file=index.js
Api: https://ej2.syncfusion.com/react/documentation/api/schedule/#getresourcesbyindex
https://ej2.syncfusion.com/react/documentation/api/schedule/#cellclick
[index.js]
function headerTemplate(props) { return (<div className="quick-info-header"> <div className="quick-info-header-content" style={getHeaderStyles(props)}> <div className="quick-info-title" >{getHeaderTitle(props)}</div> {props.elementType==="cell" && <div className="quick-info-resource" style={{ background: `${Resource.Color}` }}>{Resource.Name}</div> } <div className="duration-text">{getHeaderDetails(props)}</div> </div> </div>); }
function cellClick(args) { Resource = scheduleObj.getResourcesByIndex(args.groupIndex).resourceData; } |
Great! One additional question: I separated my header, content and footer functions into their own components for reusability purposes. Just like in your stackblitz example, I declare a titleObj in my schedule component. I pass that variable into my content component and use it in my TextBoxComponent:
titleObj remains undefined. In my footer component, I also pass in titleObj and when I handle my add event, I get the error: "Uncaught TypeError: Cannot read properties of undefined (reading 'previousValue') at handleAddEvent". How can I pass the titleObj between components? I was trying to use the useRef() hook, but the previousValue remained null upon typing in my textbox and adding my event. Please advise!
Hi there - Wondering I can get a response by the end of the week? This is impacting a project deadline I have. Thanks!
Jessica,
Sample: https://stackblitz.com/edit/ej2-react-schedule-quick-info-template-lifting-state-up?file=index.js
Based on your query we suspect that you require the titleObj instance to add an appointment directly from the quick info popup and open the editor based on the value titleObj value. You can achieve this with React lifting state up way as shown in the below code snippets.
[index.js]
import HeaderTemplate from './header-template'; import ContentTemplate from './content-template'; import FooterTemplate from './footer-template';
function QuickInfoTemplate() { let scheduleObj; let quickPopupData = {};
function headerTemplate(props) { return <HeaderTemplate data={props}></HeaderTemplate>; }
function contentTemplate(props) { return <ContentTemplate data={props} handleChange={handleTextChange}></ContentTemplate>; }
function footerTemplate(props) { return <FooterTemplate data={props} getQuickData={getQuickPopupData} scheduleObj={scheduleObj}></FooterTemplate>; }
function handleTextChange(quickData) { quickPopupData = quickData; }
function getQuickPopupData() { return quickPopupData; }
return (<div className='schedule-control-section'> <div className='col-lg-12 control-section'> <div className='control-wrapper'> <ScheduleComponent ref={(schedule) => scheduleObj = schedule} quickInfoTemplates={{ header: headerTemplate.bind(this), content: contentTemplate.bind(this), footer: footerTemplate.bind(this)}}> <Inject services={[Day, Week, WorkWeek, Month, Agenda, MonthAgenda]} /> </ScheduleComponent> </div> </div> </div> ); } export default QuickInfoTemplate; |
[content-template.js]
function ContentTemplate(props) { const [quickData, setQuickData] = useState({ Subject: "", Description: "" });
return ( <div className="quick-info-content"> {props.data.elementType === 'cell' ? <div className="e-cell-content"> <div className="content-area"> <TextBoxComponent id="title" onChange={(event) => { quickData.Subject = event.value; props.handleChange(quickData); }} placeholder="Title" /> </div> <div className="content-area"> <TextBoxComponent id="notes" onChange={(event) => { quickData.Description = event.value; props.handleChange(quickData); }} placeholder="Notes" /> </div> </div> : <div className="event-content"> <div className="meeting-type-wrap"> <label>Subject</label>: <span>{props.data.Subject}</span> </div> <div className="notes-wrap"> <label>Notes</label>: <span>{props.data.Description}</span> </div> </div>} </div> ); };
export default ContentTemplate; |
[footer-template.js]
function FooterTemplate(props) {
function buttonClickActions(e) { var appDetail = props.getQuickData(); var scheduleObj = props.scheduleObj; const getSlotData = () => { const addObj = {}; addObj.Id = scheduleObj.getEventMaxID(); addObj.Subject = appDetail && appDetail.Subject ? appDetail.Subject : 'Add title'; addObj.StartTime = new Date(scheduleObj.activeCellsData.startTime); addObj.EndTime = new Date(scheduleObj.activeCellsData.endTime); addObj.IsAllDay = scheduleObj.activeCellsData.isAllDay; addObj.Description = appDetail && appDetail.Description ? appDetail.Description : 'Add notes'; return addObj; }; if (e.target.id === 'add') { const addObj = getSlotData(); props.scheduleObj.addEvent(addObj); } else if (e.target.id === 'delete') { const eventDetails = scheduleObj.activeEventData.event; let currentAction = 'Delete'; if (eventDetails.RecurrenceRule) { currentAction = 'DeleteOccurrence'; } scheduleObj.deleteEvent(eventDetails, currentAction); } else { const isCellPopup = props.data.elementType === "cell"; const eventDetails = isCellPopup ? getSlotData() : scheduleObj.activeEventData.event; let currentAction = isCellPopup ? 'Add' : 'Save'; if (eventDetails.RecurrenceRule) { currentAction = 'EditOccurrence'; } scheduleObj.openEditor(eventDetails, currentAction, true); } scheduleObj.closeQuickInfoPopup(); };
return ( <div className="quick-info-footer"> {props.data.elementType == "cell" ? <div className="cell-footer"> <ButtonComponent id="more-details" cssClass='e-flat' content="More Details" onClick={buttonClickActions} /> <ButtonComponent id="add" cssClass='e-flat' content="Add" isPrimary={true} onClick={buttonClickActions} /> </div> : <div className="event-footer"> <ButtonComponent id="delete" cssClass='e-flat' content="Delete" onClick={buttonClickActions} /> <ButtonComponent id="more-details" cssClass='e-flat' content="More Details" isPrimary={true} onClick={buttonClickActions} /> </div>} </div> ); }; export default FooterTemplate; |