# Building a "year at a glance" calendar

I want my app to support planning and viewing plans for vacations and other sets of multiple day-long events. I created a mock-up in Excel. Visually, it does what I need done, but it’s tedious to work with.

My question is how to go about structuring data and then displaying it what look like repeating groups of repeating groups.

Here’s what the mock-up looks like:

The legend at the bottom shows the date range and name of each series of day-long commitments.

What would make sense as the data structure to support this kind of planning, and how should I go about mapping the data into repeating groups that would represent the plan data?

Any help or insights would be appreciated.

Hi @laurence - here’s a basic explanation to get the calendar view working.

1. Create an Option Set (Months), with 12 options in total (Jan-Dec) and each option has its own Number attribute (1-12).

2. Create a RG with for example 4 fixed columns, 3 fixed rows. Give it the Option Set Months data type, the data source is All Months.

3. In each cell you create the necessary UI and you add the Expression Element from the Toolbox plugin. In that expression you would put the following code first, then the dynamic expressions separated by a comma, and then the “);” at the end. To save you time, here’s the first part of the code.

``````function getDaysInMonth(e,t) {
var a=new Date(t,e,1),D=[],n=new Date(t,e,0).getDate(),b=a.getDay();
if(b == 0) b=7;
for(i=b-1;0<=i;i--)D.push(new Date(t,e-1,n-i));
for(;a.getMonth()===e;)D.push(new Date(a)),a.setDate(a.getDate()+1);
var g=new Date(t,e+1,1);
for(i=D[D.length-1].getDay();6!=i;i++)D.push(new Date(g)),g.setDate(g.getDate()+1);
for(i=D.length;42!=i;i++)D.push(new Date(g)),g.setDate(g.getDate()+1);
return D;
}

getDaysInMonth(
``````

See image:

1. Create another RG as shown in the images. Here are the settings I quickly used for this demo.

2. Gray out / hide the dates that are not in the corresponding month (just like on your excel example)

Here’s the finished result:

Since you’ve been Bubbling since 2017, I’m sure you can figure the rest out.

4 Likes

Thanks for all the time you put into this response. I genuinely appreciate it.

Although I’ve been bubbling since '17, I haven’t learned much beyond vanilla Bubble. My long history in software development really ended about 15 years ago and I picked up Bubble so I wouldn’t have to code again. Even so, I sort of recognize the Javascript you wrote.

I will put it to the test (my memory of code that is ) in a day or so.

Again, thanks for stepping up and providing so much for me to work with.

My pleasure @laurence !

Although I’ve been bubbling since '17, I haven’t learned much beyond vanilla Bubble

I see, let me help you a little more then. Since you’re working with day-long events, we don’t have to deal with hours, which makes this setup easier.

So based on your excel, here’s one way to set up your events:

1. Data type: Event

2. Each event has 5 Data fields: eventName (text), eventDates (list of dates), eventYear (number), eventBackgroundColor (text), makeFontColorWhite (yes/no)

3. Here is what you do with the Vegas example from Excel: you Name it “Vegas”, you set the list of Dates to Oct 8, 9, 10 and 11, the eventYear would be the year this event takes place, then a HEX color (there are great, lightweight plugins out there), and check a checkbox so the Event gets a white font. I don’t see the purpose in using a <-range-> expression or a range data field, as your dates don’t necessarily follow each other (the Workshop example).

4. in a hidden Repeatinggroup (preferably in a popup you never show so you don’t mess with your responsiveness of the page) you search for all the events where the eventYear is the relevant year you want to display. Let’s call this repeatinggroup ‘rg - events’.

5. This is the hardest part: Then you select the group of the first cell of the nested repeatinggroup. This is the repeatinggroup from bulletpoint 4 in my first response. In this group you want to create another group of type Event (this can be a 1px by 1px group). The data source of that group will be `rg - events:filtered <advanced: this event's each Dates formatted as 02/02/2024 contains parent's group date formatted as 02/02/2024>` (or something like that, I’m currently not in the editor).

6. When the above is set up correctly, your newly created 1px by 1px group will store the event in that group, for that date. Now you can simply change the background color of the parent group to that group’s Event backgroundColor through the conditional tab and change all the relevant things.

This covers a lot for your desired result, of course you need to make sure that your nested RG has 7 columns and other minor improvements. But that shouldn’t be the hard part, do let me know if you’re stuck.

Also, because we do the search once in the hidden repeatinggroup, and then refer to it with the “rg - events” expression from step 5, this setup is extremely performant.

PS: don’t forget your Privacy Rules!

EDIT: If you have events that overlap the following year, you’ll want to set your eventYear to a list of numbers. You can easily get these numbers by extracting the unique year(s) of the eventDates. Then step 4 would be `search for all events. With a constraint where eventYears contains current date/time extract year (for example)`

@oliviercoolen, great post ! Thank you. When you have the chance, can you also post a version of the getDays function where the week starts on Monday ?

1 Like

Here it is:

``````function getDaysInMonth(e,t) {
var a=new Date(t,e,1),D=[],n=new Date(t,e,0).getDate(),b=a.getDay();
if(b == 0) b=7;
for(i=b-2;0<=i;i--)D.push(new Date(t,e-1,n-i));
for(;a.getMonth()===e;)D.push(new Date(a)),a.setDate(a.getDate()+1);
var g=new Date(t,e+1,1);
for(i=D[D.length-1].getDay();6!=i;i++)D.push(new Date(g)),g.setDate(g.getDate()+1);
for(i=D.length;42!=i;i++)D.push(new Date(g)),g.setDate(g.getDate()+1);
return D;
}
``````
2 Likes

Thanks @stefanof !

Looks like @nico.dicagno beat me to it.

Anyways, I like to default to ChatGPT to explain the code. So if interested, here it is:

``````function getDaysInMonth(month, year) {
var firstDayOfMonth = new Date(year, month, 1),
days = [],
lastDayOfPreviousMonth = new Date(year, month, 0).getDate(),
weekdayOfFirstDay = firstDayOfMonth.getDay();

// Adjusting Sunday (0) to be 7 to make Monday as the first day of the week
weekdayOfFirstDay = weekdayOfFirstDay === 0 ? 7 : weekdayOfFirstDay;

// Adding the last few days of the previous month to the array
for (var i = weekdayOfFirstDay - 1; i > 0; i--) {
days.push(new Date(year, month - 1, lastDayOfPreviousMonth - i + 1));
}

// Adding the days of the current month
while (firstDayOfMonth.getMonth() === month) {
days.push(new Date(firstDayOfMonth));
firstDayOfMonth.setDate(firstDayOfMonth.getDate() + 1);
}

// Adding the first few days of the next month to complete the week
var firstDayOfNextMonth = new Date(year, month + 1, 1);
for (var i = days[days.length - 1].getDay(); i < 7; i++) {
days.push(new Date(firstDayOfNextMonth));
firstDayOfNextMonth.setDate(firstDayOfNextMonth.getDate() + 1);
}

// If the total days are less than 42, add extra days from the next month
for (var i = days.length; i < 42; i++) {
days.push(new Date(firstDayOfNextMonth));
firstDayOfNextMonth.setDate(firstDayOfNextMonth.getDate() + 1);
}

return days;
}
``````

PS: I didn’t check ChatGPT’s code - so just copy paste Nico’s version

1 Like