Build A Booking Calendar App From Scratch (Step by Step Tutorial)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in today's video we are going to be building a booking calendar type of app from scratch step by step and you can think of these kinds of apps that you might be using anytime you need to book maybe a appointment at your local barber shop or some kind of an appointment with another person that provides you a specific service maybe a type of consultation and things like that so this is going to be a fully functioning app this is going to be an A to Z tutorial and you're going to learn a ton of things when it comes to building very very useful no code apps we're going to be using Florida flow which is one of my favorite no code Builders right now and we're going to be storing data in fire basis firestore now before we begin as always all the apps that I demonstrate and or build on this channel are available from my patreon page and you can learn more about it using the link in the description option below now before we begin it's very very important to sit down and try to kind of figure out the main architecture and trust me if you spend some time doing this You're Gonna Save a lot of time and a lot of aggravation when you're actually building the app okay and so this is our booking app flow right now we have the customer the customer logs in and they immediately see a list of Barbers now I'm saying a list of Barbers because that's kind of what I am aiming at but you can replace Barbers with any kind of profession that you might be interested in building an app for or presenting to these kinds of profession so the user sees a list of Barbers and they can click and you know look at the Barber's detail kind of learn more about the specific Barber look at their reviews ratings or they can book a time okay so they can book a time they can also see available um the available uh times that they can book they can see the earliest available day the earliest available time things like that and they also have a page that allows them to see bookings so things like future and past bookings and on their past bookings they can leave a rating and a review so they can leave a rating from like one to five and they can also enter a review so say something like this was this is a great Barber completely recommend or they can write pretty much anything else this is our kind of beginner architecture obviously as we go along uh maybe we'll add or kind of alter some of the things but this is a good start right you always need this when you're building apps otherwise it's just gonna be it's gonna be a pain trust the next thing that you want to kind of design and kind of Envision is the database structure and this is very very important it's as important as the architecture because I like to call the database structure as kind of your home base and what I mean by that is when you design it and you're 100 sure that this is kind of what you want this is kind of how you want the apps to behave if you have any issues building the apps you can always come back to it and uh you know kind of figure out if it fits in as to what you're trying to do this is your home right here okay and so we got the users collect and inside the users collection we have the service providers in this in this case these are going to be Barbers and we also have the customers right so if you're logging in you need a haircut you're going to be a customer and you and the way we're going to be differentiating them is by roles so this is going to have a barber roll this is going to have a customer role and I'm just gonna have them all in the same collection now in The Barbers collection uh we're gonna have two sub collections we're gonna have the bookings and the reviews and that way a barber has its own um you know bookings that are you know related to that barber so Barbara a might have you know three bookings for today and maybe two bookings for tomorrow and you know obviously Barber needs to have reviews right this is going to be stored as a sub collection on The Barbers uh collection on the Barber's level and each review is gonna have a rating which is going to be an integer from one to five and uh it's gonna have a piece of text that's gonna you know specify you know what the user thought and and the bookings is just gonna have a one field which is going to be the time okay so you know maybe it's gonna be tomorrow you know 1pm or 2 p.m what's today at you know maybe 5 p.m this is just gonna be the time field which is dated time and actually these fields they play really really nicely in Florida flow as you're gonna see in just a few moments okay and that's kind of our main data structure obviously you know as we start building the app we might alter it change it around things like that all right so now that we looked at some of the architectures we kind of you know looked at them we internalized them we studied them it's now time to actually you know roll up our sleeves and start to build the app so I've logged in into my flutter flow and I'm going to create a new project I'm going to say create and I'm going to call it uh let's say and I'm gonna do a blank app okay now it's asking me to uh give it a name we have in the package name we're just going to leave it at default and we're gonna set up Firebase obviously because we need to store that data somewhere so I'm going to hit next and we need a Firebase project ID so the way you get this is uh you go to your Firebase console okay so if you if you already have been building apps for Florida flow you know exactly you have this already set up if you've not built app so flutter flow you've never used Firebase you can simply Google for Firebase and create your account once you do that you're going to be logged in onto a page that looks something like this now as you can see I already have three projects that I use over and over again in many of my apps but what I'm gonna do is I'm gonna create a new project with you and that way you guys can follow along if that's what you're doing as well so I'm going to say add a project and I'm just going to say um whatever Barber I'm going to say continue this doesn't really matter I'm gonna say continue um I'm just gonna pick default create an account we're gonna wait a few moments here while it's uh creating all right so the new project is ready we're gonna hit continue and now we are redirected to a dashboard for that specific project and inside of this thing we have lots of things that you can do so it's not just database there's lots of other functionality and I cover Firebase and one of my other videos you can see that video linked above but here we're just interested in firestore database so I'm going to click here and when I do that I don't see anything okay I don't see any databases that is because I need to create a database and here you can choose whether you want to do in production mode or test mode doesn't really matter of this specific example so I'm just going to do it in let's say test mode okay and what test mode does is if you choose test mode essentially what's going to happen it's gonna start stop functioning it's going to stop accepting requests uh after a certain date right typically it's going to be a month from now so we're just going to say next that's fine I'll leave that on default all right so our Firebase firestore project has been created and now what we're seeing is our Collections and documents inside of those Collections and that and right now I don't have anything right so I need to create a new collection we're going to get to it in just a second so now that we've set up our uh firestore database we can go back to our app and we can specify this project ID and what is the project ID well if you click here go to Project settings you're gonna see information about this project we called it Barbara and uh there's the project ID so you can rename the project name but you can rename the ID so I'm going to copy this paste it in here and when I do that I'm going to get an error flutter flow does not have access to this Firebase project Firebase account please add Firebase at flutterflow.io as an editor and so what you need to do is you need to go to users and permissions and I think it wants uh Firebase at flutterflow.io and we're going to give it an editor roll and what we've just done is we gave flutter flow access to this specific project in the very high privileged permission so meaning that they can create all kinds of things they can modify and that's absolutely fine so now that we we've done that we can click this again and hopefully we should not have an error so as you can see we have two new buttons we don't have an error the next thing you want to do is you want to click here you want to Auto generate config files and what what this does it creates configuration files for this specific flutter Flow app that we're building and so this specific app is going to have access to our our firestore dbn because it's creating those files and it's putting it there right and so if you go over here you click on Project settings and you refresh this you should see the new app right here so as you can see it created three project settings right these are settings uh for three different types of apps that you might be building so like it's gonna have there's a web app version there's gonna be an iOS version as well as an Android version as well so that is all created that is great we can go back to flutter flow we're going to enable authentication we're going to create user collection we're going to enable both of them and what this does is now we can use authentication okay so when you enable this you need to make sure that you go back to Firebase and you enable authentication here as well we only enable firestore database so I'm going to go into build and I'm going to go into authentication and what I'm going to do is I'm going to say get started and I'm going to enable this as well right um I'm gonna just enable email and password you can also do things like Google authentication Facebook Twitter all of these in this specific app tutorial we're only going to be using email and password which is enough so we have this enabled and so in this project this Barbara project we're just using two things we're using authentication and firestore database so we can go back to Florida flow and we've enabled both of these and now we need to do a couple of other things but more specifically we need to create an entry page which is a login page and a lot and a logged in page which is going to be the first page that the users see once they log in so for the login page I'm going to click here I'm going to go into all and I'm going to just pick a random page I like this page I've been using this page so I'm just going to click on use my template and I'm just going to type login I'm going to say create page so created the page and it's specified it here which is exactly what we want and now we need a logged in page and you have to ask yourself at this point what is the logged in page well remember we talked about the logged in page right here which is the list of Barbers right so this is going to be they're going to log in and they're going to see a list of service providers in this case they're they're not going to be barbers bought some you know this could be pretty much anybody right and so we're gonna go back to our app we're gonna come in here and because this is gonna be a list of something it's a good idea to come to list to click here on this tab and then you can pick a list you can obviously you know you can you can go here and you can create a blank page you can do that too that's fine but I like to save a little bit of time and that's why I'm going to start with the template so I'm going to come to list and I'm gonna pick a list page that kind of looks as to what we want so maybe something like this as you can see it's a list of people and we can change it around we can modify so I'm going to say use my theme and I'm gonna say um and now everything is fine right we've uh created our config files we've enabled this we specified the pages you just want to click on start building okay now what we have here so far is we have these pages that we created but right away you see that we have some project issues so I like to fix my issues right away because as we're building the app you know we're we're obviously going to have more and more issues that we're going to be fixing so it's better to fix it sooner than later so I'm going to click here and as you can see I have a couple of issues auth action it hasn't properly set confirmed password and the reason for that is this page that we're using this is a page that allows user to not only log in but also create accounts right and the reason and the reason I say that is because you see it says create account and login so this right here is the create account if you scroll down this tab is the login page now for the create account page uh it's giving us an error because it needs if you click here you can see that it has a improperly set confirmed password and that is because when you are doing a you know create an account page you can do all of that inside of Florida flow which is awesome but you do need another another field that's going to be um a confirmation password field okay so if we click here we see here I'm just going to duplicate it once I have it here once I have this one right here I can set it as the confirmation field Miser to password 2 to differentiate them and now if I go back here I should be able to set it so once I set it there's no project issues are fine and we still have two errors bottom sheet action it's doing a bottom sheet which means that it's configured to do another view to pull up another view uh we don't really need that so I'm just going to delete that we don't need a bottom sheet I mean we might implement it later but it's not important right now so I'm going to delete this and once I do that we should have no issues at all we only have five rules uh firestore rules setup that we can configure okay so before we do that the next thing that we need to do is we need to configure our backend we need to configure our firestore DB okay so by default plot of flow creates a user's collection and by the way you can get to the screen by clicking here and clicking here so by default it creates a user's column and what this means is that when we are creating users this user's collection right so I'm back in my um Firebase firestore dashboard we don't have a user's collection uh here but that's fine because it's here created by flutter flow automatically and what this means is that when we are creating our user accounts uh they're gonna be created automatically inside of our firestore DB because the schema is here once you have the schema you're gonna have stuff created here automatically okay and that's fine but we're not done yet remember when we were designing this whole thing we have lots of things going on right so we have our users divided into two roles but we also have bookings and review now at this point I like to run my app just to make sure that everything is fine okay that everything is working as expected what I'm going to do is I'm going to comment here and I'm going to run it in test mode we're going to start it out just to make sure that everything is running perfectly smooth all right so here's our app running for the first time and as you can see see we are seeing this initial screen this login screen and the reason we're seeing it is because we're not logged in if we were logged in we would not see the screen we would be seeing the home so we can log in or we can create an account so what I'm going to do is I'm going to create an account because we don't have any accounts I'm just going to create an account now there's a problem here as I'm typing it it's not displaying the text all right so if I go in here it's very very faint and that means I need to change some of the settings okay so I'm going to go back to the app and go back to the login screen and I'm going to click on email as you can see if I scroll down this text color is one I need to make sure it's black changes so that I can see it and this is probably a small bug in this template here okay so now it's black and while I'm at it I'm also going to remove the Google and the Apple uh logins because we're not going to be using that okay so I'm going to delete all that I'm just gonna leave just the uh the email authentication so we're going to delete all that just to keep things very very simple we can always re-enable that later so now our text fields are all black the the text the color of the text is all black and I'm gonna do that yeah this text color is actually set correctly so now we're going to reload our app and what's nice about flutter flow is that once you uh build the app you're running the app uh you can easily reload it after you've made changes and it's very very fast just a couple of sets that initial building might take some time but the reloading is very very quickly and so now if we type we can see the text so it's probably a smaller minor bug in this specific template so what I'm going to do is I'm going to create uh my account maybe a couple of other accounts okay just some random account and I've created the account and as you can see once I've created the account it automatically logged me and and we are seeing that home page that we've created now we obviously need to modify it we need to tweak it but at least uh we are seeing this page and if I go into my dashboard the firestore dashboard and I reload it I should see that users collection right here and I should see that one document with the account that we've just created okay so as you can see this created account this email uad and also if you go into authentication you should see that new account created here okay and the reason is in this this field right here this row this is what logs you in right the fact that it's here means you can log in this is the user ID and you can reset the password disable account delete account but it's also here because uh you know you may need to display this account okay then that's what we're going to be doing we're going to be displaying them as well so now at least we have this one account okay and we can start using it now in this app we need at least two accounts right so we need one consumer and one provider right one producer which is the barber and one consumer which is maybe me right because I'm looking for Barber services so what I'm gonna go what I'm gonna do is I'm gonna go back back to the app and what I need to do is I need to log out and I want to create another account just so that we have at least two accounts unfortunately I cannot log out because I don't see I don't have access to a page that allows me to log out and what I'm going to do is I'm going to create another page that's going to display my account and it also it's going to give me an option to log out so we're gonna go back to the app we're gonna click here new page we're gonna go to I believe settings yep settings right here and then you can pick a page that displays some user settings and gives you an option to log out so not that important what this page does so maybe something like this use this and I'm going to say profile this is our this is going to be our profile and so it's a built-in profile page that allows us to log out right so if you click here there's a there's a um an action that executes something called an auth log out right this is uh there's lots of things when it comes to logout login change password and but this one allows us to of the login and so what I'm gonna do is I'm going to customize this page real quick right so we want to display the name we want to display the email but I'm not gonna you know change any of these things I'm not gonna enable this because all I'm really looking for is log out so I'm gonna click this I'm gonna click here and I'm gonna pick authenticated user because this is the user that's logged in so this is kind of what we want to see we want to see information about the log then you I'm going to pick this display name I'm going to click here I'm going to pick authenticated user email and that's about it and then also the image as well click here and authenticated user photo URL and I don't think we have the photo configured because if you go in here we only have to refill so I'm going to add the photo later and you're gonna see the photo there now we do have one error I'm going to click on it there's a line column there's an issue with this line color somewhere let's figure that out so there's some line color let's clear that color let's see if that fixes and that fixed it okay so now we have that page the problem is if we load this app we're still not going to see the pin and what we need to do is we need to create a nav bar that way we can have access to pages that we don't have access when we are navigating the app right so because it's not part of kind of the list detail View and the the page hierarchy right this is a separate page and so what we need to do is we need to we need to come in here we need to select the page profile and we need to see show a nav bar and so navbar is currently off in the app settings we are going to enable it and it's telling us navbar requires at least two pages to have show a navbar send that's fine because we can enable home okay and now we should have an app bar so if we scroll down we see a nav except this profile I don't want it to have a home icon I wanted to have some kind of a so maybe something like this and that way we can have access to the space so I'm gonna come back here I'm gonna reload the page and we should see a nav bar that will allow us to access different pages that are not part of the current Pages hard okay so now this is the homepage can click here and we see the the profile page and as you can see there's my email I don't have an image and I don't even have a name I don't have a display name and that is because if we go back to our app and we come into our database schema this this there's this display name which is not set in fact I can set it right now I can add a new field call it display name so now I have James no call and if we go back to the app it's immediately being displayed that is because anytime you change fields and firestore DB change the data it's reflected automatically okay so now I can click on log out I'm logged out and I can create another account just like what we talked about so I have my consumer account which is James I'm gonna create a new account which is going to be our Barbara our hypothetical Barber and so this is going to be Barber at barber.com uh password a random password and now I should have that new account created and I'm logged in as this hypothetical Barber okay so if I go in here now I have two documents here okay so I'm also going to create a display name and I'm going to say job go back and we see Joe Barber here okay so now I'm logged in as the barber okay and I don't want to be logged in as the barber I want to be logged in as myself but another thing that I want to do is I want to make sure that we can differentiate producers and consumers so Joe Barber is a producer and what we need to do is I'm going to create another field called roll and I'm going to call it I'm just gonna name it producer okay just for just for a lack of a better term just going to call it producer because actually maybe provide a service provider right I'm gonna hit add so this is a provider and this is and I'm a consumer right so I'm going to say raw and the reason I need to do that is because if I go in here and this is the screen that's going to display The Barbers I don't want to display other users I only want to display people that are actually providing this service and speaking of the screen let's go ahead and configure it now I'm gonna go back here gonna come here here uh pick our UI go to our UI editor go into home page and we have this template here now we need to change things around right we don't want to display just random stuff we want to display actual data and the way we're going to do it is we're gonna come in here this is a list View and what this means is that this is a view that displays children as items okay so there's all of these widgets here that are being displayed and if you need the refresher uh on how flutter flow works I actually have a really really good tutorial that you can see in the corner right there and that's gonna explain to you everything you need to know when it comes to widgets and things like that because in this video I'm just going to gloss over all of the details so we have a list View and what we need to do is instead of displaying just random stuff we need to display the actual data so we're going to click on backend query we're going to add a back-end query we're going to do a query collection we're going to do users list of documents and we're going to do a film the filter is going to be roll we're don't have a role here yet that is because we haven't specified it in the schema so we're going to come here we're going to add it right here we're going to say roll roll is a string at backend query query collection users and what do we want to do we only want to display providers so we want to say equal to and we want to display providers it should be provide this is consumer provider so we're only going to be seeing Joe Barber we're not going to see ourselves because we're not we don't have a role of providing a comeback here we're going to say row is equal to provide and we can also enable this exclude current user this is good practice right because most of the apps you're going to be building you're going to be looking at a list of users and there's no reason to see yourself right because you know I'm seeing some you know other users that I may want to interact with whether you're building a social on social media app or you know any kind of any kind of app that displays a list of users you typically do not want to see yourself so most of the time you're going to enable this unless you have a good reason not we're going to say confirm confirm and now as you can see we're seeing some grayish that means this field is going to be uh it's going to be replicated it's going to be duplicate repeated it's going to be repeated with the data that we are getting from the database now there's a couple things I want to do I want to just clean up this thing because we have this container which is this thing but you know it's a template right so we have other containers I'm just going to delete the other containers we don't really need that because we just need one container to be repeated repeatable we're going to come here delete all this delete all this okay we're done so now we have one container which is exactly what we need and we have search patients we don't want to do that I'm just going to say uh Barbers this is our list of Barbers uh maybe search Barbers here we have patients matching we don't really need that next we can also reload the app just to see if we're getting the um the the right results right the right things are happening right the changes are being reflected we are seeing an error but this project does not have any errors and so what I'm going to those I'm going to reload it again and once we reload the app we see that there's one record that's being displayed okay obviously the information is not correct but that's fine we can easily fix that in just a second but at least the the record is being displayed and this record is actually that one provider that we have set okay and so if we go back to the app now we need to make sure it's displaying direct the correct information the first thing is we wanted to change the image we don't have an image set for that user yet but we're going to change that in just a second and so we want to select the image we want to click here and we have this users document that is being uh displayed right so if we click on users document we can pick photo URL pick choose the text click here users document the display name we have the number but we don't really need the number and maybe we want to display the email okay so click here so now if we come back and we reload our test environment reload the new version of the app which should see the right information so now if you go in here to our user profile and we log out and we log back in as a customer we should see the Right Barber so I'm going to log in as myself as a customer here and we are seeing Joe Barber here okay so Joe Barber does not have an image but everything else checks out and so let's go ahead and add an image to Joe Barber so I found the random image that we can use for Joe Barber I'm just going to copy it here go back to our firestore and I'm going to add a field I believe it's called photo URL so if you go in here our schema we have photo underscore URL we're going to add it to Joe Barber and now if we go to the app we see the image for Joe bar now this is great and all but I really don't see the purpose of displaying their email here and maybe this thing because what I really want to display is their next available dates that they're available so that you know the user can book and the other thing we're gonna do is we're gonna display a calendar up on top so that we can pick a date and see all the different Barbers and see their availability on that date or maybe you know the next available day and so for that we need to kind of figure out how we want to do it so remember we talked about the main architecture and for each Barber we have bookings okay and so we're gonna pull up these bookings and we're gonna see okay they're booked on let's say you know at this date at you know 2 p.m okay so that means we should not be displaying that date we should display all the other dates from let's say nine to five except the days that they're booked on and so what I like to do before I do everything is I like to go to my database and what I want to do is I want to create this sub collection okay this sub collection is going to be created for us automatically when we're going to be adding um adding uh Fields provided that we have the structure in place but for testing I like to create it myself first just see you know things things work as expected and so I'm gonna go to Barbara and I'm gonna create this sub collection I'm gonna call it bookings and I'm gonna create one document which is one booking and I'm just gonna call it time this is gonna be timestamp and I'm gonna pick let's say today's day and let's say they're booked for I don't know 3 P.M yes or something like this 3 P.M okay so now they have one booking for 3 P.M and that means when we are building the app we're building the functionality we can on you know we can see how it behaves if they have a booking okay so the next thing I want to do is I want to go back to my app and I want to create a schema for the sub collection okay so we're gonna come in here and this is going to be bookings it's a sub collection of users and it only has one actually it's gonna have more than one field it's gonna have two Fields okay so uh this is gonna be the time field and this is going to be date and time and I also want to store the uid and this is going to be the uid of the person of the customer that's booking it right remember these collections are this is a sub collection on the providers on the provider document right but we also need to keep track who is the person that's booking it so if I book it with Joe Barber this is going to be my ID so I'm going to come in here we're going to set uid document reference to use and we have two fields and while I'm at it I'm going to create an another sub collection called review which is a sub collection of users and it's gonna have two which is uh an integer one to five and a review which is just so now we have two sub collections that do completely different things we have the bookings and we have the reviews and so if we go back to our app right here go back to the sound and what I want to do is I want to display their information so I don't want to display the email I don't really care about email I don't want to display their name but here what I want to do is I want to display the next available date that they're available so let's say today is uh May 18 and they're available today but there available from 3 pm 3 to 6 PM or 3 to 5 PM that's kind of what I want to see or if they're not available today show me you know the next available day maybe they're available tomorrow all day or certain days I want to see that and that's kind of what I want to see here and for that we need to create a custom function that displays this information and so we're going to comment here and we're going to create a custom function I already created a custom function using chat GPT that I'm going to show you how I did it in just a second and this function is called get available slots okay because we want to get available slot that are not booked yet so all the slots that are not booked are available okay so we're going to add this function right here we're going to say add function and we're going to call this I have it called get available slots okay and this function takes two arguments right it takes the the slots that are already reserved that are stored in our firestore DB and it's also taking an argument called start a date R because we want to check which is the start date um you know by default it's going to be today but the user can also pick you know in a month from now right maybe they're traveling they're not available they don't need a haircut but they want to check in a month from now and we're going to have a calendar and all that to control that and so what I'm gonna do is I'm going to have two arguments called one is called reserved slots which is a uh which is a list of date end times is going to be called Reserve slots and we're going to be passing it from the database and the next argument we need is called start date right which is just a date and time and this is the date that we want to check on by default it's today but you know maybe the user wants to check next month or something like that and I'm gonna call start date R this is the inside that's what we have and this function is going to return a list of date and times which is a list of their you know available um available time we're gonna come in here date and time we're going to say list and now I have the function and now I can copy it and paste it in here and I'm going to explain to you everything that it does so we're going to paste this function and we're gonna save it okay you can also click here to kind of format it but now we have this function that returns us something called available slot and the way I generated this function is I went uh to chat GPT and I said write a dart function that takes an input of reserved requests specified as a timestamp and returns available bookings as our blog assuming the person works from 9 to 5 PM only list the next available day as long as there is at least one slot open so meaning that you know if they're available today I just want to see dates for today I don't want to see dates for you know two months in advance like all the dates they have right oh I just want to see for today if they're not available today but they're available tomorrow show me those slots just for tomorrow even if you know even if there is at least one slot open right even if there's you know one slot just show me that so this is exactly what this function does I pasted it in but we're not done yet we need to pass the correct information so we need to pass reserved slot and we need to pass start date in case they're using a calendar and they're changing um the dates that they're interested in okay now because this function returns an array of dates that are available right on the next available day there's a lot of things that we can do there's a lot of interesting things that we can do right so we can we can go back to our main home page here and we can display the dates right here right so if we click here we have a role but another thing that we can do is we can add a wrap right we can add a run and we can actually give it data and this wrap is going to display elements with the specific dates that this person is available on right so what we can do is uh inside the wrap we can add a text field okay we have a text field and that text field is going to display the individual day but the first thing we need to do is we need to go to the wrap and we need to load it up with data so I clicked on generate Dynamic children and for the value we're going to find our custom function get available slots we're going to pick this and we're gonna give it parameters so the reserve slot we're going to give it the slots that are DOT are already booked right we're gonna click here and we're gonna go into users but we don't have booked we don't have anything we haven't loaded order up that data yet so we're gonna come back out and what we want to do is we can do it on a container level so for each container which means for each user which means for each Barber we want to load up all the bookings right that way we have that information and we can feed it to that function in order to calculate the available dates and so what I want to do is I'm going to come to this uh I'm going to click on container go to uh backend query add backend query we're going to do a query collection and we're going to pick bookings and we're going to select from a variable we don't want all the bookings we just want bookings for that specific Barber and we're going to come here users reference we're going to say list of documents and now we have access to bookings and what this means is that we can go back to Ram we can click on this generate children from variable and now we can click here and we can pick bookings but we don't we're not passing bookings we're passing just the list of those we're going to click on available options map list items click here and we're going to pick time that's that's all we need to do and here we're gonna select no further changes because we don't really need to filter the confirm and now we have start date so what is the start date well right now we can use today right so we can use something like today or we can use uh we have Global properties we can just use current time that's because we want to search for available dates from the current sign we don't want to search them in the past right from current time so we're going to say confirm no further changes that's and now this function is going to give us back the available times from the next available date okay so you're gonna see how it works I'm just going to call it available available dates or available slots is even better confirmed okay and now we're going to have those items displayed here and now as you can see this rack is now loaded up with data okay so the next thing you want to do is click on this child inside the wrap click here because otherwise it's just going to display a real low World hello hello world and we're gonna go into available item and we're gonna pick it confirm and now it's going to display all the available items okay so let's go ahead and run this app before I do that let's uh compile our check our custom functions for errors we're going to click here all right there's no errors just like we expected let's go ahead and reload our create a new session so here's our list of Barbers and as you can see we are seeing the dates we are seeing right now it's 3 30 p.m and so we're seeing the next available time which is 4 P.M now let's go ahead and fix it up a little bit it's kind of hard to see what's happening let's go back to our app and the first thing that we want to do is we want to format the date and the nice thing about it because this is a date time field it's very very easy to format it right we can just select the field come back here and we want to uh select inside of this available options setting we want to select daytime format and now you can format it so how do we want to format it well we just want to format it like this time and or military time or something like this when we have that let's go ahead and reload it all right there you see four to five pm and that is because I set the Working Day in that custom function from nine to five PM so it's not gonna go beyond and you can see how I set it right here so if you look at this function I have this setting here so before it adds it to available bookings I am saying that it needs to be uh greater than or equal to 9 or less than or equal to 17 which is 5 PM okay and you can modify this right maybe your working day is you know nine to three PM then in this case you're gonna put 15 which is 3 P.M or maybe you have a long working day to eight eight pm then you're gonna put 20 but I just have 17 which is 5 p.m so I have that and that is why it's showing me this 4 to 5 p.m okay let's fix that up a little bit more there's some padding issues a lot payment issue so let's go back here and this display name has a padding so let's go ahead and add this padding as well let's add a 12 that adds a nice padding and then we want to space out give it a little bit of spacing we'll give it a little bit of padding maybe something like this let's see how it looks all right that looks a lot a lot better now it's not clear what day it's available this person is available and obviously I know it's today but the date is not being displayed let's go and fix that real quick let's add the date right here so if we come back here we have this row and what I want we have the wrap but I want to add another element I want that day to be displayed so I'm going to edit text add it add it right here drag and drop it to the top and this thing I want to display date that it's available and that's actually very very easy to do and the way you do it actually is you set it to that function again but this time instead of getting all the elements we just get the first element and format it as a date instead of formatting as a you know data as an hour and minute okay and so what we want to do is we want to click here we're going to come here we're gonna search for our function get available slot we're going to feed the arguments right so this is the reserve slot uh which is going to be the bookings and we're going to map them and we uh we wanna pass the the time slots here uh no further changes and then for the start date we're gonna pass current date but that's going to be configurable later on global properties current day and because this is a text field now this is not a wrap that expects a list we need to make a choice we have an array we just want you know the first item we're just going to say item and index I'm just going to say because we just want that first time but we want that sign to be formatted with as a date and time a month and day date and time format and a day of the week month and date confirm and now that should display the date let's go ahead and refresh okay perfect so it's showing us the date and it's showing us the time slots that it's available today let's go ahead and fix it and then we're going to test to make sure that it works so we're going to do a little bit of padding here let's do a 12 to match the stock one and let's go ahead and reload it and I'm gonna we're gonna test it to make sure that it's actually functioning correctly not just displaying random data now let's say go into my database and I create a record for 5 PM it should not be displayed anymore so let's go ahead here we have a booking for 3 pm but I'm going to add another booking I'm going to add a new document and I'm going to say time and I'm going to say timestamp I'm gonna do it for 5p now if I hit say this should not be displayed and remember it's automatic with Firebase firestore the changes are reflected automatically so let's go ahead and here let's come back here and it's gone you see it disappeared and that is because it's taken now if we also have a booking for 4 pm it should display the next date automatically so let's go ahead and try that I'm going to add a new document I'm going to say sign sign stamp and and we're gonna say today and I'm gonna say for 4 P.M May 18 boom and now we're seeing May 19th and now we're seeing the next available date and that is kind of how one of the apps that I use it works exactly like this it displays the next available date now there's some problem here right there's an overflowing issue let's go ahead and fix that real quick you can do that by picking this wrap and doing expansion here and let's see if that fixed it okay there it is now there's no overlapping issue and we are seeing the dates this way now it's up to you how you want to display the date uh that's very very trivial to change it but right now it looks good to me it looks fine we see that it's available tomorrow the next day and on these days so all dates of it it's available from nine to five the person the barber is free let's go ahead and fix a couple of other things we don't really need this because that's not how we are going to be booking this and it's just taking up space so I'm gonna delete this car and let's see let's take a look this wrap yeah so we deleted it let's go ahead and reload it and now we have the dates now let's say the person wants to book on this on a specific day how does it work well very very easy they can just click on it and it's gonna automatically book on that date by entering a new record in our bookings sub collection so let's go ahead and do that but before we do that let's create functionality where they can uh pick that the specific date that they want because right now it's looking at today right maybe you know that I want to book today they want to book next week so we need to have a calendar that controls how these dates are set up so let's go ahead and do that right now so we have this column when we have this list view let's go ahead and add a column we're going to add a calendar right here let's go ahead and drag it let's go drag this on top okay so we have the calendar you can change how this calendar is displayed by clicking it and you can specify a week view and I think a week view looks better in this case and I'm gonna do start week on Monday start from Monday because that's kind of what I'm used to starting from Monday and for the initial date um this is interesting because here you can specify the initial date so what is going to be the initial date well the initial date is going to be whatever the user picks it's going to be you know whatever the user decides okay and so in our case what we want to do is we want to have an initial date of today and then they can change it and that's going to reflect everything and so the way we want to do it is we're going to have a page State variable you can just click on home page come back over here and click on this far right tab icon here we're going to add a field and I'm just going to call it start that we're going to give a date and time okay now we have a field confirmed and this is our start date and so what we want is we want the start date to be today by default and then when the user changes it we're gonna change the date and that's going to recalculate everything okay so by default we want it to be set today right so I'm going to add an action at the top level when the page low and we're going to say page update page State and we're going to set it to Global parameters current time so now automatically the space State variable is set to today but if the user changes anything we want this to recalculate that right so there's a couple things that we need to do the first is that if you click on the calendar well the first thing is this initial day we're gonna do it from page State start date secondly if you click here you can add an action you can open an editor and this is uh gonna be triggered on date selected which is per and the date they pick we want to change our page State variable so we're going to search for page State variable update page State we're going to set the field to start date and we're going to update it set value whatever the user picked on the widget State on the calendar so we're going to do that and we're going to do a star okay so we have that and so now when they change the date the page State variable is going to be changed but we're not using that page State variable um and so what we need to do right now is we need to make sure these fields these custom functions are grabbing the right a date from page state right so if we come in here it's not current time anymore we want it to be page statement pick start date confirm I'm back here and start date confirm confirm all right let's go ahead and reload it hopefully it works so this is kind of what we're seeing let's say we click on 19 and as you can see it shows 419 right we're seeing Friday May this is all of 19. we can click on 20 and we're seeing all the dates for 20. and now we can browse this calendar and we can check availability on certain dates and so let's say we're interested on and Sunday the 21st and it's all available it's open but let's say somebody books it and so if we go ahead in and go into our firestore database and we create a new document for the 21st we're gonna say time and we're gonna say time stand the 21st and we're gonna say I don't know 10 p.m 10 a.m that should not be available anymore right so this 10 a.m should not be available it should disappear and as you can see it disappeared it's not there anymore and so we know for a fact that if this record gets added it's going to be reflected correctly by the app the next thing that we need to do is we need functionality for them to book on a certain day and that's very very easy to do we just need a flow where if they click on something it's just gonna book it so let's go ahead and try this out real quick let's see how this works come back here we're going to create a function if they click on it right on this text field and so let's create some kind of a dialog box that pops up and tells them that hey do you want to book it on the certain date or not so if we come back over here if you go in here and let's say we have this page and we come in here we have lots of different components we have lots of templates UI elements things like that so if you're going onto UI elements you can see some interesting uh interesting components interesting dialog boxes that you can use so if you scroll down let's go ahead oh how about this one right here okay that's a nice component change photo so what I'm going to do is I'm going to create a new page uh just called components and I'm going to add the component on this page right I'm going to comment here I'm going to pick this component in fact how about this one this one is not so maybe something like this will modify I'm just going to pick this I'm going to wrap it in column we have this thing right here and so once we have this component we can right click and say convert to component and I'm going to say create component and now we have this component and so now we can customize it first of all this is going to say and here is uh we're gonna have a message something like this and here you can specify the time and the person for instance right before we do that however we need to make sure we're passing the parameters right so we're going to be passing the the name of the barber and the time as well so if you click here we are going to say today we're going to add component parameters so we're going to add one parameter is going to be the barber the user I'm just going to say username username is going to be actually just used Okay add parameter and then we're gonna say this is going to be the date side okay string and date time and then I can specify them here and I can say something like book with we're gonna pick our user time and I'm gonna format date time format um verb and now we have the mess we have the message as well space though and when they click book it's gonna create the record in the database and so we might need a couple of other parameters I'm going to fix it and so now we can load up this component when they click on the specific time okay so we're gonna go back to our uh home page we're gonna come here and this is going to be an action and this action is going to load up it's going to load up our component right this bottom sheet we're going to say show we are going to say select component book action and we're gonna pass the parameters as well so we're going to pass the parameters what's the user the user is this person right so user document display name sign is what they clicked on right so time is this available slot this is what they clicked on that's pretty much it and then the button is going to create the document right and so that booking we need the time we need the barber and we need our own user ID and so if we click over here if you come over here we have everything that we need so if we come back to our component here this is our component and now we can set up an action here right to create that new booking document right so we're going to come here add an action this is going to be database create document this is going to be booking and we need the setting this is going to be for the barber user and so in this case we also need the user ID so we're going to go ahead and pass that as well we're going to come to our component right here and we're gonna add another parameter and this is going to be the user ID and this is the barber in this case so that we can have the booking so this is the document reference here and this is going to be users okay so we're gonna go back here we're gonna go into booking and now we have the user ID now we need to set the field time and the uid of the user the US the authenticated user so the time is going to be time and the authenticated user is going to be right here authenticated user user reference which is us so now everything is set up correctly and by the way we don't really need this anymore this page anymore we can delete this component here we don't really need that okay that's going to fix a lot of issues we still have one issues we have a required parameter and this required parameter is right here okay so we need to pass the user ID to that component right so we're passing the user passing the time and this is the user ID for the the bookings right so we're going to come here users documents okay this should go away and now we are in business so let's see if it works alright so here's the app let's say I want to book it for for tomorrow at 1pm we have this thing we still need to fix it all but at least we see what's happening book your appointment book with Joe Barber at 5 19 at 5 19 at 1pm we're gonna say book and it disappears it's not there anymore now we just need to close this drawer we're gonna come back here and this action here we need to close that drawer right so we're going to say close dialog box and that's it it closed up that existing dialog but the background color let's make it you can set it to black or maybe or maybe to White go into home page all right let's say we want to book it uh 3 P.M there it is that looks much better okay it's taking up the whole screen we can say book and it should close up let's see close dial and close that eyes actually should be bottom sheet dismissed okay bottom sheet dismissed and let's take a look at what's happening we can set the height let's set the height to 150 or maybe to all right let's say 20th 5 PM okay that's better but there's overflowing it needs to be bigger okay that's better that looks pretty nice book your book with your Barber at 521 and that should hopefully disappear actually I set this up incorrectly it should not be here it should be right here in case you have this it should be right here on the action on the on the dialog box itself not when you call the dialog box so we're gonna say dismiss this is our bottom sheet dismissed now it makes sense because it's on its own bottom sheet so quick tip right there let's go ahead and reload this all right pick this let's say 4 P.M there it is and it's gone and 4 pm is gone too and if we go in here you can see these are the bookings for Joe Barber this is Joe Barber and these are all the bookings and we're saving the user right this is me this is me this is my booking 8r is my user right so if you go to 8r this is James no code and that way I can see my own bookings uh in a different screen green so let's go ahead and create another page for bookings and then we're also going to create another page for uh the details for the border with some ratings and things like that so A couple of other things to do before we call it a day so let's go back here and let's create a new page this is going to be very very simple in fact we can just use the home page we can just duplicate it and I'm just gonna say okay and we don't need all this we don't need we don't need all this bookings and let's give it a and I don't even know let's say book about about something like this you guys can think of a better one okay so something like this and I want the profile to be last so if I go in here and I go into navbar app bar I want profile to be last okay so now we have bookings and this is going to be a list of bookings for me with all the with all different Barbers maybe right all across or maybe with the same body we're gonna come back here and we're gonna pull it up we're gonna do a not user so we're gonna do bookings and value Source we want to query all sub collections we want to query all documents across all Barbers okay we're all sub Collections and the filter and bookings the uad is us right so we're gonna say uad is equal to us authenticated user and these are going to be the bookings but we also need to display the actual um the actual uh Barbers right so we're going to remove this container we don't need a backend query here and in fact we do need a backing query but a different backend query document from reference uh this is going to be a user we're going to pull up the individual Barber right the individual um The Barber that that was that we booked with so we have users here and now we need the reference right so we have the uad and so we have the parent reference the parent reference is the body we have this we have this and now we can get the actual Barber we're gonna come back here we're gonna uh go to the bookings document this should not be it should be users document for a URL and this should be a text this should be the name uh yeah name of the barber maybe right so we're gonna go into users and display name and then the actual booking that we had right so maybe we don't need yeah we don't need all this we just needed the booking and when was the book well the booking we need to display the booking but we need the whole thing the month date and time and so we're gonna use we're gonna actually we're gonna remove all this and what we're gonna do is we're gonna come into booking and just have the time and we're gonna format date time format and now we want all the information now let's go ahead and run the app let's see if it works uh we need to do Firebase indexes let's fix that let's deploy index okay is this it's deployed let's go ahead and reload the app all right let's go into bookings okay so it's still running the indexes okay the indexes actually take a little bit of time so we need to wait a little bit oh actually it's almost done already it's almost done can see these are the 3 P.M Etc let's go ahead and sort these and remove this let's remove this and let's go ahead and sort uh this right here and here let's order this by time in increasing order and what do we have in there they are odd they're out of date let's go redeploy them all right there's the app let's go into bookings and there it is okay we have the bookings we have everything the index is currently building cannot be used yet okay so it will be done by the time you watch it but we are seeing all the bookings now another really cool thing to do is to display a button to review this particular Barber to review them right and we can do that very easily by coming in here and you see this container right this container this is a row right all of this is a row which means we can add another and that's going to be displayed right there and then we can say okay and now we can review the user and so how do we want to review well we can just display another um and other pop-up dialog box okay and so we can even duplicate this by uh this component here right so we can come in here duplicate component and say this is our rating action review and here we can delete this and we can say review your experience okay and here you can display you know you can display some more information but what we really want to do is we want to have two Fields right so we have a row we have another row and I'm going to duplicate that and I'm gonna duplicate that again and on the second one I'm going to delete that and I'm gonna say uh this is gonna be the rating right so maybe maybe a select drop down right here and we can rate it from one to five okay so maybe add an option three four and these are our options here and here you can have something where they can enter something and we they can enter actually not test it's going to be an input and here they can enter some information right they can let's say enter uh enter type your or review so something like this we can do that and we can actually display some information right so this is going to be a white remember the background of this is going to be white so it's going to be nice uh it's going to display nice and this booking is we're going to change this now what do we need to actually review it right we need different parameters right uh we have the booking information but we need the booking ID the booking reference and we also need the actual the this information which we're gonna get here so I'm gonna replace this input already and I'm going to replace this at input reviewer experience um yeah we don't need that let's keep it simple let's do a little bit of padding here write the large or something like this and yeah that looks good that looks pretty good we can also do a little bit of padding on the bottom okay so something like this looks really good let's do an action here so we have all the let's fix this up um we'll need user we don't need this we need the booking ID need the booking route going to be the document reference for the booking because we have all the other information here gonna do that and now we can actually create a flow right so we're going to create a backend document but not on book uh this is going to be reviews and actually it needs the user ID so we need the user ID not the booking reference the user ID the opponent parameters is going to be the user in fact this is going to be the the provider the person The Barber and we have user and we're going to add the field the rating is going to be from variable rating is which should stay input rating our rating is an integer okay and we have input ratings so we need to cast it we're going to create a quick custom function to shrink to an end we're going to return an integer we're going to pass a string okay we're going to say this function and we have this functioning to integer we're going to take off now we have the rating we're going to add the review okay and now we have our review and it also dismisses this sheet here okay compile let's check that custom function okay that's done let's take a look and see if it works let's come in here one thing I want to do though we only want to review bookings that have happened already we can't review future and so this button it needs to have conditional visibility we're going to come here we're going to create the condition and we're gonna say that this button will only be displayed if the booking is in the past right so if we come we pick the booking document and we say time is current okay so it's in the past it's less it's it's already passed and so if we come in here we reload let's see if that works all right we got this let's go into our booking and as you can see these are all future bookings we can't review them but if I enter a booking that happened in the past we should be able to review it so I'm going to come back into my database and what I'm going to do is I'm going to come here so I'm going to add a new document let's say this was uh which Fields do we need again time time and new idea well yeah Simon uad so I'm just gonna copy this uid so that it's associated with us we're going to add time uid which is a reference and the time is going to be time stamp and let's say we had a booking it doesn't really matter okay I'm Gonna Save it that should appear and as you can see it appears here now we can review and if we click on it we should be able to have that dialog box low and review it so we're gonna go back here we're gonna add an action action is going to be that um select component review action 200 is a background we want it to be and we're gonna pass a bunch of variables so user reference user reference is going to be the this the barber okay so we're gonna come in here this is the booking yeah this is the user right here and that's it that's all we need to do and now let's see if it will all right I'm gonna go into bookings okay so it works but there's an overflow so we're gonna make it a little bit bigger let's make it 300. all right perfect that looks perfect so let's say fine excellent Barber let's see if that works submit review okay closed it let's come back here let's go into our Barber Joe Barber there's reviews and there's our review excellent Barber okay so we have one review and the only thing that's left to do is have a page that displays information about the barber so we can click this go to the detail page and you know have some you know maybe description or something like that but also reviews and rating maybe an average of all the rest so let's do that real quick this is the last thing that we need to do and we can also remove this while we're at I'm back here home page remove this and let's add a new page it's going to be a detailed pay and we're gonna go into content and find a page that kind of looks like it how about this guy right here Barber detail and what is first thing about this page is that we need to pass the right this is the user document reference users confirm and then once we pass it the first thing we need to do is we need to load the day we're gonna do a document from reference users and now we have the data throughout the pay let's set the image or maybe we can delete that this is going to be there we don't need this this is where we want to show the rating which we're going to do in a second maybe their buy or something like we can do that and we also have a favorite function maybe we'll do that for the next iteration of the and there's the book appointment right so book appointment can lead us back to the page one thing I want to do is I want to display the reviews and I want to show the rating I want to show the rating okay so first let's display the review so where we want to display the reviews we want to display the reviews right here so we can add a list and there's the list perfect so yeah we can so how about something like this and we can have a row and inside the row we can have maybe a yeah like two text field I'm gonna Center the row I'm gonna do a little bit of padding on this list you and here's maybe some review and maybe this rating bar I'm gonna put it down here do a little bit of padding on the top of as well so something like this so their information the reviews book favorite Dr Barber and this list view is gonna have the review all right so we're gonna come back here query collection reviews from variable user run firm and now this thing is going to be the review on the review and okay and then this thing is going to show the rating bar right this is going to actually show uh the overall and for this to work we need a function that gets an average so we're going to come in here we're going to add a new function I already have this function ready it's called get average double it returns a double and it gets a list to you of integers of reviews so we get back a double we need a double because that those Stars they need a double right so a double remember is like you know 1.1 1.5 2.5 it's not like a whole number it's a whole number and a part of of a number and a part of a whole number and this thing is going to be a list of in which is the reviews and this is the phone right I also use GPT for this very very simple just paste it in here save this format it so this just sums it up and this right here just Returns the average right sum divided by the numbers by the number of items number of okay we need to still check it uh we're gonna do that in a second but we can come in here and then we can do the rating bar so for initial rating we're going to pass the data right we're going to come here and what we need to do is we need to pass the reviews we don't have reviews so before we can pass it we need to do a back-end query for review and we're going to do from variable uh user we're gonna get all the review confirm and now we have and we can come back here and for this initial rating we can run our which is cool it wants a list of integers just the reviews so we're gonna pick reviews but we need to map the items right we're gonna say map just to read this we don't need anything no further changes confirm confirm and that's it so I can count this five icon sizes 24. that's fine and then there's the rated color and the oh on okay you can you can kind of play around with it let's go ahead and come and check our two statement custom function here should be no problem and in this case there's one review which means all of these should be okay they should all be yo and in fact we don't have a detail screen linked yet so we're gonna go into our home page we are gonna click here and this thing is gonna link to their detail at an action navigate to we're gonna pass the user rep and now that's gonna redirect it uh to our detail if you go to detail we have the back button that's the back navigate back and maybe this one also I'm not gonna do this as to navigate back all right let it compile we still need to refresh it go ahead and look at our reviews we have the reviews screen let's come back here let's reload our um modified app because I made those changes after I started to compile Okay Okay click on it there it is excellent Barber we have the reviews we have this you can book an appointment come back that's going to come back there this goes back Joe Barber and let's say I review it what side go back and I review it again so let's go ahead let's say I add another document here rating and review let's say rating and review let's say do a rating of one and review then and now the overall rating should be two and a half 2.5 so we're going to reload the app in fact I don't think we even need to reload that it should see it immediately and we see 2.5 so it rounded it off to three right it needs to do it needs to round it also it rounded it up to three out of five okay so as you can see you see those two reviews uh I don't like the formatting too much let's do a left Justified but let's do a something like this and maybe a pattern okay how does this should all right there we see it we see we're logged in and we should probably remove this once we review it because we've done it there is no need to uh to have that and let's click on this that's a little bit better but still not ideal or so maybe something like this something a little bit better A lot now let's go ahead and add another barber just to see if everything works correctly I'm gonna come out and log out and I'm gonna create a new account this is going to be Barb at barbara.com all right so I'm logged in as uh the other Barber and so what I'm going to do is I'm gonna log out and log in as the user as the consumer before I do that I want to make sure I have the users we have the barber and we need to add a row right so roll is provider I'm going to add the display name okay come back here and now I'm seeing the second Barber here obviously I don't have a picture but as you can see he has different dates this one is you know it's not available in certain day but I can come in here and this one as you can see this one is not available at 3 P.M but this one is available all day but I can book him for 5 PM book with Barber 205 19 at 5 PM I'm gonna say boom and he's not available at 5 PM anymore and I can go to my bookings and as you can see I have these future bookings with Joe Barber but I have a book in with Barber too as well and then other bookings with Joe Barb and you can change the dates you can scroll through original date you can come to next month you can look at all of these dates and it's all going to function and obviously you can take this a lot further you can modify it you can change it around so many things that you can do but the hardest part was the booking functionality in my view the booking functionality and kind of making it all come together making it all kind of function together and so now that you have kind of the booking functionality and all that it's very trivial to customize it modify it take it as far as you want change it to whatever you want it's really really up to you and you can easily do that by joining our patreon community because when you join our incredible patreon Community you will be able to view and or clone this exact app and take it and change it to whatever you want maybe start a new business sell it to other companies you name it all our users right now all our amazing members can easily View and or cone all of the apps that I built on this channel and you will be able to do so as well once you join our amazing patreon Community using the link in the description below and remember you're going to get a ton of extra content q and A's live streams behind the scenes content as well as my amazing and exclusive as well as my amazing and supporter exclusive Master Class episodes where I do a comprehensive Deep dive of some topic areas that a lot of people are struggling with so if that sounds like something you may be interested in then definitely check out our amazing patreon community and consider becoming a member now if you guys want to see more tutorials of me building all kinds of interesting apps using flutter flow without any code check out this playlist in the corner of your screen you're going to see some amazing tutorials where I built all kinds of interesting apps and you're definitely gonna get a ton of value from that
Info
Channel: James NoCode
Views: 25,367
Rating: undefined out of 5
Keywords: flutterflow calendar booking app, calendar booking app, flutterflow training, flutterflow tutorial 2023, flutterflow
Id: DD2Ds3NOOXQ
Channel Id: undefined
Length: 72min 12sec (4332 seconds)
Published: Fri May 19 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.