PowerApps Solution: Multi-Day Event Calendar

Scenario

I am working with a client who manages a small group of apartments. The client reserves these apartments on behalf of their guests and needed a way to be able to quickly understand who was staying in each room over the course of the month. While there are a ton of great tutorials out there for custom calendars in canvas apps, most of the ones I found were built around displaying events that last less than a day. The idea with these is to show an outer gallery of dates within a range and then add a nested gallery that lists the event(s) on that day. This technically met the client’s requirements. However, we wanted to enhance the experience by changing the UI to more clearly show events across separate days.

Alternate to wrapped calendar

Because canvas apps galleries treat each item as a separate entity, I could not create any sort of overlay across dates to show multi-day reservations. Therefore, I needed a different way to span across dates. I decided the best way to attempt this was to move to a grid view where the X axis shows dates, without a wrap to indicate separate weeks, similar to the mockup below.

Date Controls & Configuration

When the application loaded, we will want to default to the current month. In my app OnStart, I initialized variables for the first and last date of the current month and number of days in current month. The first day of month uses the Today() function to get today’s year and month and then 1 can be hard coded as the day as the first. Similarly, we are getting the last day of the month by getting the first day of next month (today’s month plus 1) and subtracting one day. The number of days in the month is simply the day of the last day of the month. I also added a collection of sample reservation data, called colReservations. This is a collection with columns for the Room, StartDate, EndDate, and ReservedBy.

Next we’ll set up the calendar grid. For this, I want a label to show the month name, a gallery containing all days in the month, and controls to move the calendar forward or backward one month.

Let’s start with the month and year name label. Although we are defaulting to the current month, we’ll use one of our variables to get the month name so that it will update when we change the month:

Text(gblFirstDayOfMonth, "mmmm yyy")

Now we need a horizontal gallery to represent each day in the month. This is simply an indicator for the user to be able to follow the column down and is not attached to any true date. Therefore, we can use the variable gblDaysInMonth within a Sequence formula to show 1 through however many days are in the selected month. A label can be added to the gallery with a Text property of ThisItem.Value and a width of Parent.TemplateWidth. This allows the labels to dynamically change size depending on how many days the month has. Additionally, I renamed the gallery to galMonthDays, set TemplateSize to Self.Width / CountRows(Self.AllItems), and set TemplatePadding to 0.

The last step for the calendar grid is to page forward or backward one month. I added two icons (Icon.BackArrow, Icon.NextArrow) on either side of the month name; one to go back and one to go forward. To page forward, I updated the icon’s OnSelect property: 

Set(
    gblFirstDayOfMonth,
    Date(
        Year(gblFirstDayOfMonth),
        Month(gblFirstDayOfMonth) + 1,
        1
    )
);
Set(
    gblLastDayOfMonth,
    DateAdd(
        Date(
            Year(gblFirstDayOfMonth),
            Month(gblFirstDayOfMonth) + 1,
            1
        ),
        -1
    )
);
Set(
    gblDaysInMonth,
    Day(gblLastDayOfMonth)
);

To page backward, I updated the other icon’s OnSelect property:

Set(
    gblFirstDayOfMonth,
    Date(
        Year(gblFirstDayOfMonth),
      Month(gblFirstDayOfMonth) - 1,
        1
    )
);
Set(
    gblLastDayOfMonth,
    DateAdd(
        Date(
            Year(gblFirstDayOfMonth),
            Month(gblFirstDayOfMonth) + 1,
            1
        ),
        -1
    )
);
Set(
    gblDaysInMonth,
    Day(gblLastDayOfMonth)
);

At this point, my completed calendar controls looked like this:

Reservation Controls & Configuration

Next it’s time to bring in our reservations. We’ll still be using nested galleries for this solution, where the outer gallery holds the room name and the inner gallery holds the dates in the month. We’ll indicate reservation using styling on labels within the inner gallery. 

First add a blank vertical gallery to the screen. It should be wider than the first MonthDays gallery as we need space for the room. I set the Items property to Distinct(colReservations, Room) and set TemplatePadding to 0, and then added a blank horizontal gallery. I was able to use the Distinct function to show the room names because of the size of my data set, but please remember this is not delegable so you may need to modify your approach here. 

Within the inner gallery, a similar approach to the first days gallery can be followed for styling purposes. A label can be added to the inner gallery with a Text property of ThisItem.Value and a width of Parent.TemplateWidth. Additionally, I renamed the gallery to galReservations, set TemplateSize to Self.Width / CountRows(Self.AllItems), and set TemplatePadding to 0. The major difference will be the Items property; we need to connect this gallery to actual dates and not just a day representation. We can still use the number of days in the month to get the number of items in the gallery, and then starting with the first day of the month, each iteration should be incremented by one day.

ForAll(Sequence(gblDaysInMonth),gblFirstDayOfMonth+Value-1)

The last step to show our reservations will be to add a label which we’ll shade to indicate a booked stay. There should be a label in the inner gallery and we need to update the Fill property.

If(
Not(
IsBlank(
LookUp(
colReservations,
And(
Room = lblRoomName.Text,
StartDate <= ThisItem.Value,
EndDate >= ThisItem.Value
)
)
)
),
Blue,
White
)

Once I plugged in the fill property, my app looked like this:

I did some additional testing to make sure the shaded labels aligned with my reservation data. Then I cleaned up the styling and added a pop up to show details for a selected reservation. At this point, you should have a fully functional grid view. Export of the canvas app can be found here, but let me know if you have any questions in the comments.


0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *