Jump to content

Request for Input: What features should a PW calendar module have?


bernhard
 Share

Recommended Posts

For a current project I have the need to show and manage events. First, I thought I'd just show events as a list. The benefits of this approach are that it is very easy to do and it works well on desktop and mobile. Then I had a chat with @gebeer and we concluded that a real calendar interface is far more intuitive for the average user. I've built such a calendar once on an ancient site (https://www.mistelbach-mustangs.at/events/), but it was a lot of work and so I didn't do it again for any other project.

Then I thought let's use google calendars for managing events. That would have the benefit that we get a lot for free (the whole gui for managing events, the data storage, recurring events, etc). But it also comes with downsides: First, clients need to create an account for their calendar on a third party. We will hopefully have many clients on that project, so instructing them to create an account somewhere else and then connecting both apps together is a lot of overhead. Second, events handled by google have a totally different data structure than PW pages. What if I wanted to display those events? What if I wanted to add image galleries to those events? What if I wanted to have event descriptions powered by RockPageBuilder? What if I wanted the best performance one can get using the awesome ProCache?

These things are very easy to do when we are dealing with PW pages, but they are hard if we talk to a third party api like the one from google.

So I decided to build RockCalendar. A module where a FullCalendar shows PW pages and all the tedious things are already built in (like managing events, creating events, resizing events, managing locales, etc).

I've made good progress over the last few days and I think I'll have something to share next month, so I thought I'd ask for feedback what the community thinks would make sense for such a module? Have you ever had the need for such a module? If yes, what would be the key features that you'd want to see in such a module? What would be nice to have? What would be questions that come up in your head?

I can't promise, but I'll try my best to incorporate the community's feedback and suggestions into the RockCalendar module to make it as useful and versatile as possible.

  • Like 3
  • Thanks 1
Link to comment
Share on other sites

Calendaring and/or events management is a large undertaking, unless feature requirements are kept extremely minimal on purpose. The hardest, but often most requested aspects of a calendaring solution would likely already have discussions in the Recurme module's discussion topic, and Ryan's Date Range Fields (ProField) topic...so, essentially, recurrence (RRULEs), and how to handle them. I remember you even broke down the various problematic ways of attempting to solve searching, or retrieving, for events with, I think, a Fieldtype module, where the problems were with recurrence and ranges.

For storing recurrence of events, there are also two potential scenarios, architecturally:

  1. Create individual unique entries (PW pages, in this case) for each event that is part of a series - it can then be modified independently even if mildly associated to the original group.
  2. Create a single entry that stores metadata about the recurrence to extrapolate virtual entries from the original.

Both options have their pros and cons, but I'm not familiar enough with the research behind them to understand what would be best, or if there's even a hybrid approach. I'd imagine the first would be far easier to implement and maintain. Related to RRULE would also be vCalendar/iCal/ICS support.

Best suggestion:

If feature suggestions are calendar/event adjacent, but don't actually belong to calendaring, do not support it. I was just thinking how iCal is used to help customers remind themselves about events, but more often customers don't know how that technology works, so offering an email reminder for the event could be a feature suggestion. However, emailing could easily be handled by a developer that is taking advantage of your module and expanding on it, so although emailing a reminder about an event is related to an events calendar, emailing itself is not an integral part of an event calendar. 😉

@Jonathan Lahijani might have some suggestions too, with his use of one with the Geffen Playhouse. His solution was different, but issues he dealt with may be of use.

Good luck, Bernhard!! 💪

  • Like 2
Link to comment
Share on other sites

I can give a quick reply about the Geffen calendar, but most of what there is to know about it is in that article I wrote.

How the show performances (events) came to exist wasn't a concern as that was managed with a 3rd party service, obtained via an API and stored as child pages of a show.

The main takeaway from that project was that we needed to simply display upcoming shows in a monthly calendar format (large and small) and when I originally developed the site, I leaned on FullCalendar.  However that was total overkill and difficult to maintain because FullCalendar is more than just a way to display events in a calendar format, but an interactive component with dragging and dropping like what you'd get in a real calendaring system (Google Calendar, etc.).  It's also built with complex JS which I like to avoid.

I just needed an easy way to display information with simple AJAX loading between months.  To solve those, CSS Grid (ie, "display:grid") and HTMX fit perfectly with some straight-forward PHP code to build it.  One extra improvement that can be made to display a large and small calendar in different contexts while using the same HTML is to use container queries, but because browser support for it a couple years ago was still very low, I avoided it.

  • Like 4
Link to comment
Share on other sites

Thx @BrendonKoz and @Jonathan Lahijani that's very valuable input.

I have read your article briefly and it's an interesting idea to build the frontend calendar on your own. On my quick and dirty tests i added the fullcalendar to the frontend to see if everything works. Then I added the mentioned drag and drop functionality to see if editing events also works.

It did. But it needed quite a lot of not so easy code and to get something modular I had to refactor everything. Now I built the fullcalendar as a fieldtype for the backend, so anybody can easily add a calendar to the PW backend. This already works very well for managing events, resizing them, moving them, etc.; But the display part is currently also handled by FullCalendar which works, but also has downsides. For example the mobile view of the month is not ideal. I have to experiment more with it to finally decide, but there is room for improvement at the current state.

On the other hand the folks at FullCalendar have built a very good product and for example styling the calendar is a breeze with just setting css variables to your needs.

@Jonathan Lahijani how do you deal with multi-day events and how to you decide how they stack and what do you do if you have more events than what fits in one cell?

Link to comment
Share on other sites

6 minutes ago, bernhard said:

@Jonathan Lahijani how do you deal with multi-day events and how to you decide how they stack and what do you do if you have more events than what fits in one cell?

No need for multi-day events, so that wasn't even something I thought about.  That's actually something I would imagine would make the CSS Grid approach potentially unusable if you need to span the event over multiple days, but at the same time support for spanning was one thing that made FullCalendar difficult to style for my simple needs, at least in v3.

As for display order/stacking, that was another issue that I had to hack around with FullCalendar.  For example, if Geffen had a day with an events schedule like this (ordered by time):

  • Show A: 2:00pm
  • Show B: 3:00pm
  • Show A: 4:00pm
  • Show B: 5:00pm

They would actually want it to be displayed like this (ie, grouped by show, then time):

  • Show A:
    • 2:00 pm
    • 4:00 pm
  • Show B:
    • 3:00 pm
    • 5:00 pm

That's not the "natural" way it would work in a pure calendar sense (which orders strictly by time), so with FullCalendar I had to basically do-away with how it normally puts in an event and inject custom HTML, which kind of defeats the purpose of using a calendar library.  I believe when they switched from v3 to v4, they changed too much of how it worked internally and I didn't want to redo that work-around to be compatible with v4, so that's when I gave the whole approach a re-think (we launched in 2019 with FullCalendar, then I re-did it in HTMX in 2022).

  • Like 1
Link to comment
Share on other sites

Wow Bernard! Your timing is incredible 😃. I'm looking into 'a calendar solution' for a specific use case. Not sure if this helps your development, but let me mention my requirements: This app needs to have an interface for a number of freelancers to update their availability for jobs - and be able to provide them an overview of job's they've been booked on. (that means a lot of user specific data) 
On the back-end these entries from freelancers need to be accesale for planners, who will create 'jobs' and assign them to freelancers if they accept them.
Up until now i've been working with lists, but your view and interface sound very interesting to me. I've just subscribed to your newslette, eager to check it out when it's ready for sharing. 
My Big Thank You! in advance.

  • Like 1
Link to comment
Share on other sites

That's an interesting use case @Ellyot_Chase

1 hour ago, Ellyot_Chase said:

This app needs to have an interface for a number of freelancers to update their availability for jobs

Would that be just a single checkbox or a daterange or an hourly schedule?

1 hour ago, Ellyot_Chase said:

and be able to provide them an overview of job's they've been booked on

Don't see a problem here as my calendar will be able to list any page as long as it contains a RockDaterangePicker field (which stores all the information about start timestamp, end timestamp, allDay yes/no etc.

1 hour ago, Ellyot_Chase said:

On the back-end these entries from freelancers need to be accesale for planners, who will create 'jobs' and assign them to freelancers if they accept them.

That would be regular PW pages and page reference fields.

I'll keep your use case in mind! Thx

Link to comment
Share on other sites

40 minutes ago, bernhard said:

That's an interesting use case @Ellyot_Chase

Would that be just a single checkbox or a daterange or an hourly schedule?

Freelancers generaly work a 'morning' 'afternoon' or sometimes 'evening' Those are defined  loosly as 9:00-12:30,  13:00-17:00 and 18:00-22:00

The problem i'm trying to solves is that freelancers update their availability say for the coming three month period - and I need their input for each date in that range, per day, per timeslot. 
My thoughts are that a simple table list with dates and 'morning' 'afternoon' and 'evening' columns would provide them the quickest input for comparison with their other appointments and bookings. (Beeing freelancers!)

And I'm very gratefull you're willing to "keep my use case in mind!" 😁

Thank you!

Edited by Ellyot_Chase
Added thank you
Link to comment
Share on other sites

Sounds very promising @bernhard

I a past project I had the need of creating and managing recurring events, too. Back then there was no calendar module available for ProcessWire, so I wrote my own logic using PW repeater fields in combination with the RRULE PHP library.

The interface for a event page looks like this. In this example the event occurs every third tuesday in every month from 4pm - 6pm.
image.thumb.png.7bd31cb3b16e3064e479b369489f79b1.png

I handle the updates of the dates that are displayed in the frontend via a cronjob that runs every night. The date field then gets updated, based on the set of rules that are set for the event:

image.png.4f42673c9d9b1ce73e569281fc99c306.png

What is handy feature for me is to get an overview of upcoming events. Either in a classic "calendar view" or maybe in a small list overview like here. I list all events within the current week:

image.thumb.png.f0ce5a299759112481ff6f43b769c008.png

  • Like 3
Link to comment
Share on other sites

That's a very slick interface, congrats @Stefanowitsch

Unfortunately to support all possible options there are some more options and this is what I came up with so far:

U0tRk0L.png

This seems to work for almost all use cases that I could think of so far. The interface could even be improved by hiding options that don't make sense (but it's not so easy to define which they are and when).

The only case I don't have a solution so far is "every sunday or holiday at 20:00"

Anybody knows or has a good idea how to support holidays as recurrence rule? That's obviously a challenge, because holidays are very specific by region...

My best idea so far is to add a "clone" feature to the month calendar view where the client could just drag and drop the event (a guided tour through a cave) from sunday to a holiday and click "clone" instead of "move". That way they could quickly add all events for all sundays and then drag them over to all the holidays...

  • Like 1
Link to comment
Share on other sites

@bernhard This is an amazing undertaking and truly appreciated! I started planning a calendar module a few months ago but just couldn't afford the time to implement it. I think the interface you designed is a great direction. I'm 100% willing to be a guinea pig and help test if it helps.

1 hour ago, bernhard said:

Anybody knows or has a good idea how to support holidays as recurrence rule? That's obviously a challenge, because holidays are very specific by region...

This is definitely a tough one. I could see being hesitant to rely on a third party service to pull this information but the complexity of region and tracking dates might make managing it locally a real beast. Third party service eliminates the need to create complex rrules by just updating holidays locally according to up to date information. Maybe using a service to pull data for current year +/- x number of years and on demand or annually beyond that.

Free service, queryable by region and year: https://date.nager.at/Api

  • Thanks 1
Link to comment
Share on other sites

2 hours ago, FireWire said:

This is an amazing undertaking and truly appreciated!

Thanks! Yeah I've been there several times but never really managed to get something solid.

2 hours ago, FireWire said:

I think the interface you designed is a great direction. I'm 100% willing to be a guinea pig and help test if it helps.

Great!! I'll send you a copy as soon as I have something usable.

2 hours ago, FireWire said:

Free service, queryable by region and year: https://date.nager.at/Api

Wow, that will be very helpful for my project! Thank you. The company behind is even Austrian 😍 I can think of adding "holiday" as an option to the rrule interface. There one could select "Austria" and then I could add a callback that loops over all rrule dates (eg "every day until 1.1.2025") and filters out non-holiday days, which would result in only holiday dates in the events grid of to-be-created events.

  • Like 1
Link to comment
Share on other sites

On 9/2/2024 at 10:25 AM, bernhard said:

Anybody knows or has a good idea how to support holidays as recurrence rule? That's obviously a challenge, because holidays are very specific by region...

Using a service to determine holidays is one option, but then which holiday(s) do you want to support? Governmentally observed holidays, or perhaps even the silly ones, too? Things can get tricky when assumptions start to be made. A (potentially) simpler approach would be to have two other option areas:

  • Dates to Include
  • Dates to Exclude

Since you're attempting to tackle recurrence: it may be desirable to have events occur within a range, but then a business will be closed due to an observable organizational holiday (like an owner's birthday), or alternatively a date exception is made where an additional entry would want to be added, and it's known ahead of time.

Alternatively, it's just as easy to delete some after creation, or similarly clone. 🙂

  • Like 1
Link to comment
Share on other sites

4 minutes ago, BrendonKoz said:

Things can get tricky when assumptions start to be made.

Absolutely true, thx! I think you are right and I'll not add this. Instead I'll try to make the editing interface via FullCalendar as smooth as possible. I think that's the best common ground that every installation will have. Once they have added their holidays to ther calendar they will see them in the month view and then they can move/clone events to the desired dates by drag&drop.

7 minutes ago, BrendonKoz said:

Dates to Exclude

That's already possible by just clicking on the trash icon before creating recurring events 😎

BlwmbRR.png

12 minutes ago, BrendonKoz said:

Since you're attempting to tackle recurrence: it may be desirable to have events occur within a range, but then a business will be closed due to an observable organizational holiday (like an owner's birthday), or alternatively a date exception is made where an additional entry would want to be added, and it's known ahead of time.

Yep, and I've made good progress so far 🙂 What you mention will be supported.

13 minutes ago, BrendonKoz said:

Alternatively, it's just as easy to delete some after creation, or similarly clone. 🙂

That's how it will work (or as mentioned one can exclude items before creation) 🙂 

  • Like 2
Link to comment
Share on other sites

19 minutes ago, FireWire said:

@bernhard I can't tell you how unreasonably excited I am for this module.

Great to hear that! I'm also very motivated as this has been an issue for me (and I guess many of us) for a long time.

Things are really going well so far. I had some troubles today with timezone issues (events starting at 10:00 suddenly started at 11:00 from one month to another etc.) and also with the performance of creating recurring events.

Turned out I set a generic pagename and PW had to append a unique suffix for every created page and that got slower and slower as pages under that parent grew in count. I've just quick-fixed that by setting a random name via uniqid() and the performance is quite impressive imho (real world speed on my macbook air with ddev):

8AqceCD.gif

How cool is the live progress bar??? 😎 It works with realtime feedback from the server using SSE. No polling. No sending hundreds of requests to the server 🚀

I also wanted to update the grid above the progress bar, but that also turned out to slow things down a lot, so I turned that off for now. I have to look into that tomorrow. I think it should be an easy fix. But that's it for today 🙂 

  • Like 6
Link to comment
Share on other sites

1 minute ago, bernhard said:

How cool is the live progress bar??? 😎 It works with realtime feedback from the server using SSE. No polling. No sending hundreds of requests to the serve

How cool? REALLY COOL.

My next project needs a calendar and this is going to save the day. I'd like to buy you a beer, or six. Hell, you can have an entire keg 🍻

  • Like 2
Link to comment
Share on other sites

What I’m finding particularly useful is your date Inputfield on steroids allowing you to cover almost all cases. On a recent project I went with a FieldtypeCombo + FieldtypeTable to achieve something similar:

1635510021_ScreenShot2024-09-03at21_06_58.thumb.png.c6e87fbc9ee02c3b107a4e1a2f13021a.png

1745168175_ScreenShot2024-09-03at21_09_31.thumb.png.0477c1ee88a7979bd9457254837a4299.png

1095511836_ScreenShot2024-09-03at21_07_04.thumb.png.0a06579b2f5d282e9cff8940a007070e.png

If the event is happening indefinitely, I create the first 1000 occurences and have a button to create a 1000 more, like in your screencast (though not live). The one thing missing in my contraption is hiding past occurrences (but keep them just in case).

In your example, does clicking on the trash icon delete the occurrence or just deactivate it?

Having a calendar UI would of a course be a really nice thing to have as well for clients 🙂

  • Like 2
Link to comment
Share on other sites

Another thing got me curious:

In your gif it looks like you have a calendar in a page, would the idea be that you could have a calendar in a template and then (maybe) show events that could be child pages or pages that match a selector with a page reference field (e.g. for event categories)? This has a lot of potential I think.

  • Like 1
Link to comment
Share on other sites

1 hour ago, monollonom said:

What I’m finding particularly useful is your date Inputfield on steroids allowing you to cover almost all cases. On a recent project I went with a FieldtypeCombo + FieldtypeTable to achieve something similar:

Thx for sharing! Yeah that's basically the way I have been building things as well for a long time. But adding all the fields and getting every single detail done correctly is so much work, so I decided to finally do it better in a reusable way that has a good and compact ui for clients and does not rely on a multitude of fields.

1 hour ago, monollonom said:

The one thing missing in my contraption is hiding past occurrences (but keep them just in case).

What do you mean by hiding past occurrences? In my case I'm creating events via rrule.js and the start date is always the first event page's date. So recurring events will always be in the future (relative to the first event).

1 hour ago, monollonom said:

In your example, does clicking on the trash icon delete the occurrence or just deactivate it?

This is the UI for creating events and that table shows the items to be created once the user clicks on "create events". That view is really nice as it updates instantly, so anyone should get a very quick understanding of what all the settings do:

p0u3GEd.gif

1 hour ago, monollonom said:

In your gif it looks like you have a calendar in a page, would the idea be that you could have a calendar in a template and then (maybe) show events that could be child pages or pages that match a selector with a page reference field (e.g. for event categories)? This has a lot of potential I think.

The idea is to get all the necessary tools for handling events with as minimum effort as possible. So I've already built the RockDaterangePicker field last month. The next step was to add a calendar view. I first added that to the frontend, but then I realised how much effort it is to setup all necessary ajax endpoints and handling all clicks and all that, so I decided to move that into the backend.

If anybody needs to show these events on the frontend, this will be as easy as a foreach + page selector or just adding a simple fullcalendar, which is also easy as long as you don't have to deal with managing events (edit, move, change etc).

And then I realised that when having all that ready in the backend it might be the right moment to also tackle recurring events.

So at the moment the concept is that you add a calendar field to one parent page. There you define which pages to show as events (which will likely be the children of the parent page, but could be any other selector or hook as well). Then for the events all you have to do is to add the daterange field which will be created during installation of the module. This field will tell the calendar where to show events (on which day and time) and it will also save all the info whether the event is part of a series etc...

  • Like 2
Link to comment
Share on other sites

34 minutes ago, bernhard said:

This is the UI for creating events and that table shows the items to be created once the user clicks on "create events". That view is really nice as it updates instantly, so anyone should get a very quick understanding of what all the settings do:

I think I missed something: is the "Recurring" option creating copies of the current page (event)?

In my case there is no copy, just an array of occurences (array of dates basically) that remain bound to the page and that I’m using when querying events. This does mean I always display the same content.

34 minutes ago, bernhard said:

What do you mean by hiding past occurrences? In my case I'm creating events via rrule.js and the start date is always the first event page's date. So recurring events will always be in the future (relative to the first event).

That’s why I’m asking: the dates are generated and then remain, I can disable some and if a selector involves a disabled occurrence, the event will not show up. Thus some occurences can be in the past. But again my question comes from me misunderstanding what your module is doing (even though it’s clearly specified in the fieldset’s label 😅).

Another question then: once you created the events, what happens if you save the page? Are the copies displayed in your table or does it start from scratch?

(thank you for taking the time to answer)

Edited by monollonom
Link to comment
Share on other sites

On 8/30/2024 at 10:46 AM, BrendonKoz said:

For storing recurrence of events, there are also two potential scenarios, architecturally:

  1. Create individual unique entries (PW pages, in this case) for each event that is part of a series - it can then be modified independently even if mildly associated to the original group.
  2. Create a single entry that stores metadata about the recurrence to extrapolate virtual entries from the original.

Both options have their pros and cons, but I'm not familiar enough with the research behind them to understand what would be best, or if there's even a hybrid approach. I'd imagine the first would be far easier to implement and maintain. Related to RRULE would also be vCalendar/iCal/ICS support.

It looks like you've each chosen the differing options. 😉

  • Haha 1
Link to comment
Share on other sites

I've tried to tackle the recurrence part (to some success, I am still finding bugs) with: https://github.com/elabx/FieldtypeRecurringDates but it's not a calendar solution, I actually ended up building a calendar with HTMX like @Jonathan Lahijani 's and also a combined field solution like @monollonom 's. So interesting to see everyone's solutions!

@bernhard I  think I'd request the "Never" option, since there are situations where end users don't really think about when something is going to end at least that's why I ended up implementing a "Never" fake option. I haven't really solved it in my module in a way that would seem optimal, "Never" just means an absurd amount of events (eg 5,000 hard limit). The drawback I have faced is performance in queries of this absurdly large tables (At least they always look absurd taking into account that's it's just a few pages of the site sometimes).

And as some feedback on the UI I have thrown this similar set of fields to the common folk editor and they just figure their way out, so I think you're on your way to success! 

I have thought of rewriting my module to use the second approach you propose @BrendonKoz doing more rrule calculations on execution and maybe relying more on cache instead of throwing it all at the db, I also read somewhere about saving the first and last date of the occurrences and save them for db querying purposes.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...