Build guide — Power Platform
A hourly room-booking system for T3B, built entirely on SharePoint, Power Apps, Power Automate and Outlook — no third-party tools, no IT procurement beyond your existing Microsoft 365 licence.
Four Microsoft 365 components, each doing one job. SharePoint is the single source of truth; Power Apps is the front door for staff; Power Automate handles everything that needs to happen in the background; Outlook delivers the human-readable confirmations.
Build these first. Everything else (app, flows, QR links) references these lists by ID, so get the schema right before touching Power Apps.
| Column | Type | Notes |
|---|---|---|
| Title | Single line text | Display name, e.g. 3F - Meeting Room 1 |
| RoomID | Single line text | Unique short code used in the QR deep link, e.g. T3B-3F-MTG-01 |
| Floor | Choice | 2F / 3F |
| RoomType | Choice | Meeting Room / Quiet Room |
| Capacity | Number | Optional, shown on the card |
| QRDeepLink | Hyperlink | Auto-generated once the app is published — see Section 7 |
| Active | Yes/No | Untick to pull a room out of service without deleting it |
Seed this list with your 13 rooms:
| Floor | Room | RoomID |
|---|---|---|
| 2F | Meeting Room | T3B-2F-MTG-01 |
| 2F | Quiet Room 1 | T3B-2F-QR-01 |
| 2F | Quiet Room 2 | T3B-2F-QR-02 |
| 3F | Meeting Room 1 | T3B-3F-MTG-01 |
| 3F | Meeting Room 2 | T3B-3F-MTG-02 |
| 3F | Meeting Room 3 | T3B-3F-MTG-03 |
| 3F | Meeting Room 4 | T3B-3F-MTG-04 |
| 3F | Quiet Room 1–6 | T3B-3F-QR-01 … T3B-3F-QR-06 |
| Column | Type | Notes |
|---|---|---|
| Title | Single line text | Auto-built: RoomID_Date_StartTime |
| Room | Lookup → Rooms | Lookup on Title |
| BookingDate | Date only | No time portion — time lives in StartTime/EndTime |
| StartTime | Choice | 08:00, 09:00 … 17:00 (edit to match your actual operating hours) |
| EndTime | Choice | Calculated as StartTime + 1hr in the app |
| BookedByEmail | Single line text | Captured automatically from User().Email — never typed by the staff member |
| BookedByName | Single line text | From User().FullName |
| Purpose | Single line text | Optional free text |
| Status | Choice | Confirmed / Cancelled |
| Column | Type | Notes |
|---|---|---|
| Title | Single line text | Staff name |
| Single line text | Must match Entra ID login email exactly | |
| Department | Single line text | Optional, useful for reporting |
| Active | Yes/No | Untick to revoke booking access instantly — no app republish needed |
The app checks the PermittedUsers list on every launch — not SharePoint item-level permissions — so you can manage who can book from one list without touching site permissions.
// Runs once when the app opens. Sets a global flag used to gate every screen. Set(varCurrentUserEmail, Lower(User().Email)); Set( varIsPermitted, CountRows( Filter( PermittedUsers, Lower(Email) = varCurrentUserEmail && Active = true ) ) > 0 ); // Deep-link handling — see Section 7 for how RoomID arrives via QR code If( !IsBlank(Param("RoomID")), Set(varDeepLinkRoomID, Param("RoomID")); Navigate(scrRoomDetail, ScreenTransition.Fade), Navigate(scrOverview, ScreenTransition.Fade) );
One app, two entry points. Staff coming from the app icon land on the Overview screen. Staff scanning a QR code on a door land straight on that room's Detail screen. Both share the same booking logic underneath.
Today() + 30.StartsWith / "*" in Title).// Items property of the horizontal day gallery on scrRoomDetail
ForAll(
Sequence(7, 0),
{
TheDate: Today() + Value,
DayLabel: Text(Today() + Value, "ddd"),
DayNum: Text(Today() + Value, "dd"),
IsToday: (Today() + Value) = Today()
}
)
// Items property of the slot gallery — one row per operating hour
ForAll(
["08:00","09:00","10:00","11:00","12:00","13:00","14:00","15:00","16:00","17:00"],
With(
{
_existing: LookUp(
Bookings,
Room.RoomID = varSelectedRoomID
&& BookingDate = varSelectedDate
&& StartTime = Value
&& Status = "Confirmed"
)
},
{
SlotTime: Value,
IsBooked: !IsBlank(_existing),
BookedByName: If(!IsBlank(_existing), _existing.BookedByName, "")
}
)
)
// OnSelect of the "Confirm booking" button If( !IsBlank( LookUp( Bookings, Room.RoomID = varSelectedRoomID && BookingDate = varSelectedDate && StartTime = varSelectedSlot && Status = "Confirmed" ) ), // Someone booked it in the seconds since the screen loaded — refuse and refresh Notify("That slot was just taken — pick another.", NotificationType.Warning); Refresh(Bookings), Patch( Bookings, Defaults(Bookings), { Title: varSelectedRoomID & "_" & Text(varSelectedDate,"yyyy-mm-dd") & "_" & varSelectedSlot, Room: LookUp(Rooms, RoomID = varSelectedRoomID), BookingDate: varSelectedDate, StartTime: varSelectedSlot, BookedByEmail: varCurrentUserEmail, BookedByName: User().FullName, Purpose: txtPurpose.Text, Status: "Confirmed" } ); Notify("Room booked — check your email for confirmation.", NotificationType.Success); Navigate(scrConfirmation, ScreenTransition.Fade) )
LookUp immediately before Patch closes almost all of that window; the Power Automate flow in Section 6.2 is the backstop that catches the rare remaining case.The same flow whether staff start from the app icon or a QR code — they only differ in how they arrive at step 2.
Build these in the same environment as your SharePoint site. Assign each to a different team member (see Section 8) — they don't depend on each other, only on the Bookings list already existing.
Trigger: When an item is created on Bookings.
Status is equal to ConfirmedSend an email (V2) to BookedByEmail, subject Booking confirmed — {Room} {BookingDate} {StartTime}Create event (V4) on the booker's calendar (start = BookingDate + StartTime, end = +1 hour, location = Room) so it shows up in Outlook/Teams automaticallyTrigger: When an item is created on Bookings.
Room eq '@{RoomID}' and BookingDate eq '@{BookingDate}' and StartTime eq '@{StartTime}' and Status eq 'Confirmed'Created item, set every later duplicate's Status to Cancelled, and email the affected booker that their slot was already takenTrigger: Recurrence, every weekday at 07:30.
BookingDate eq '@{utcNow('yyyy-MM-dd')}' and Status eq 'Confirmed'Trigger: When an item is modified on Bookings.
Status changed to CancelledBookedByEmail; delete the matching calendar event created in 6.1Each printed QR code encodes a URL that opens the Power App and passes the room's code as a parameter, which App.OnStart (Section 3) reads and routes straight to that room's Detail screen.
https://apps.powerapps.com/play/e/{environment-id}/a/{app-id}?RoomID=T3B-3F-MTG-01
environment-id and app-id from the play link.Six work packages that can run largely in parallel once the data model (Section 2) is signed off. Suggested split across your 7 direct reports — adjust based on who already has Power Platform exposure.
Create the 3 lists exactly per Section 2, seed the 13 rooms and the permitted staff list.
Build scrOverview: room gallery, floor/type filters, date picker, slot strip.
Build scrRoomDetail: 7-day strip, today focus, hourly grid, booking dialog, deep-link handling.
Build all 4 flows from Section 6, test each against a dummy booking.
Generate, test, print and post 13 QR codes once the app is published.
Run the testing checklist (Section 9), write the one-page staff how-to, announce go-live.
Two audiences, two different needs.