I cloned TeamSeas.org using GraphQL, Prisma, NestJS, React, and more! #TeamSeas

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
so let's rerun this old mutation that we had before with mumbo and a thousand again our response updated to 4500 so now total donations in real time right so you can imagine as we implement the react ui and let's hit submit and there you go automatically updated on the right with 2 million the leaderboard is also updated so [Music] hey guys how's it going today we're on a mission to save the ocean with team c's team seas is very similar to team trees which was another movement that they had a while back this was led by mr beast and mark rober so they're trying to do basically the same exact thing but instead of planting trees they're trying to get trash out of the ocean so every dollar that you donate is effectively going to be a panel trash that they're going to take out of the ocean so they have a goal of reaching 30 million pounds which is basically 30 million dollars if you're in any way interested in joining this mission with us make sure to go to teamses.org and put a donation in today with that said you're probably wondering this is a programming channel why are we talking about team c's well i'm trying to think of a way to promote this mission and still make it sort of friendly to my developer audience so i thought why don't we go ahead and make a clone of the tmcs.org website now as you can see on the screen here it's still closed we don't actually know what it look what it's going to look like we have 20 hours left that's how much time i have to complete this tutorial and edit it and put it out but like i said it's very similar to team trees which has its own website at team trees.org so i have reasonable confidence to believe that the tmc's website is basically going to be the same thing as this one except it's going to have a slightly different brand to it so we're going to take a look at the team trees website and then we're going to see if we can clone that and make our own version of it so let's walk through some of the features that we're going to try and implement so obviously when you go to team trees.org it's got this like heather at the top just telling you what the uh the movement is there's a logo and then there's this number in the in the middle that actually dynamically updates i think as you as people put donations in i was thinking that we could implement this using graphql subscriptions which is going to allow us to watch that number go up in real time so i think that'll be pretty fun to build as we scroll down here in the middle you'll see that there's a picker to choose how many trees you want to donate or effectively plant right so 23 to 20 dollars so it really is it's a picker of how much donation you want to put in and then you can click next and then it presents you this other form that allows you to input the details of your donation or more specifically the details of you as a person making that donation and there's some things to pay attention to here so for example some things are required some things are optional and when i hit next step here to submit you'll see that there's validation that's running in so that's something that we're we're going to implement both server side and client side we're going to show you how to do that validation and you'll notice there's also another level of validation here where you can't just put in any string for the email it needs to be a valid email so that's another thing that we're going to try and emulate so effectively what we're going to try and do is we're going to build a graphql api that allows us to do this donation input and then ultimately we want to be able to calculate the total of all the donations so far so that we can present it on the screen in real time and of course we're going to build the ui itself and we're going to try our best to make it look very similar to what we have here and we're going to use react and chakra ui for that all right let's take a look at what other features is in here there is another page by the way when you hit next on so on the third page here is another form specifically for payment details where you can enter your card number and stuff like that we're actually going to skip this one i think that once you implement the api and the ui for this first form the details form you pretty much have all the fundamentals already that you need to build this next step for the payment details so i'm going to leave this one as an exercise for you if you're following along also on top of that i think in a realistic use case people are probably aren't going to be implementing the the payment infrastructure from scratch they're likely going to use a third party service i would imagine so that's not really worth our time right now so in our case you can think of it as we're just really building the the mvp almost like the the eighty percent of the application so we're not doing a pixel perfect clone of the same application and again we don't know yet what teamses.org is gonna look like we just know that more likely than not it's going to have a lot of the same features all right so let's keep moving forward what other features do they have here so there is a leaderboard at the bottom of the page here where it lists out all of the the donations that are coming in you know again each tree is a dollar so and then you'll notice that it has the name of the person the display name that they put in as well as a potential message so we're gonna look into how to build this again from the api side how we're going to be able to query donations as well as on the ui side how we're going to build a view that looks fairly similar to this and then another slight feature is this ability to toggle between most recent and most trees so that's really just a change in what we're ordering by and sorting so we're going to take a look at that as well and then there is also a search feature here which we'll see if we have time for but we're probably going to skip this one as well again i think that if you're following along with this tutorial i want to leave some stuff for you to implement as an exercise so that so i'll walk you through building maybe the 80 of the clone and then maybe you can build the 20 for yourself as an exercise all right so let's talk about what our tech stack is going to be uh we're going to use nesjs in the backend net sgs is just a really nice framework that i'm a huge fan of lots of content that i've made for it in my channel next we're going to use graphql with sjs and those two really i think work pretty well together and then finally on the back end we're going to use prisma to talk to our databases this is something that i think works really well with graphql you know especially since they have this way of defining the schema of things uh in a very graphql like syntax so you'll see that in a second so those are the core technologies we're going to use on the back end on the front end like i mentioned we're going to use react this is a no-brainer for me react is pretty awesome and then we're going to use erko i haven't actually talked about erko yet in my in my channel but it's a great alternative to something like react query when you're trying to talk to a graphql api specifically that's exactly what it's meant for and finally we're going to use chakra ui as our component library all right enough talking let's get into building this code let's get our terminal ready so the first half of this tutorial we're going to build the api first and in the second half we're going to focus on the client that talks to that api all right so to get started hopefully you already have the nesjs cli if not you can install that globally with npm install dash g at sjs cli then once you have that installed you can then use that to create a new project so i'm going to go to my desktop here and i'm going to do nest new and then you're going to give it the project name let's call this team c's api select your package manager i'm going to use npm and then that's going to start installing a bunch of stuff for us all right so once that installation is done and open it up in visual studio code as i have here on the screen and then we're actually going to go back into the terminal because we need to set up graphql first so to get started we're going to do npm install at nesjs graphql graphql itself and an apollo server express so one thing you need to understand with nesjs and using graphql with that is there's actually two ways to do it you can either do a code first or schema first and the gist of code first is exactly what it sounds like is you you write a bunch of typescript classes and then that generates the schema the graphql schema for you or you can do the inverse of that where you write the graphql schema and then you're going to write some code so that that turns into typescript definitions in this video we're going to take the schema first approach because i think that's going to play very well with prisma which we said we're also going to use and prisma itself is another schema first solution technically where you're writing the the schema for your data model and then we generate the typescript client off of that schema so so that's why we're going to use schema first here because i think it's just going to feel a little bit more cohesive when we're using it with prisma now if you are interested in learning how to do the code first approach with net suggestion graphql i do have a video for that specifically in my channel anyways let's get back into the code once you're done installing those dependencies we're going to go into the app module and we're going to add the graphql configuration that we need in here so in the imports we need to add graphql module from sgs graphql and then we're going to configure that using for root and we're going to put in an object in here for our settings all right so to get started the most important thing you need to add in here is this type paths property basically with this pattern we're saying that any file that ends in that graphql is a schema for graphql and what this module is going to do is it's going to try and combine all those individual schemas so even if you had various schema files across different folders different files as long as it ends in that graphql it's going to pick it up now another thing you can configure here is the playground which by default is actually true so when you run this application you'll be able to go to localhost 3000 slash graphql and that's going to spin up the you know the typical graphql playground that you can use to clear your application and this bullion allows you to basically turn that ability on or off in our case we're actually going to turn this off because we're going to use something else as our playground we're going to use the we're going to use the apollo sandbox which is a slightly more advanced version of the playground that's built specifically by the people behind apollo it's really cool you'll see in a second so the way to enable that is you have to bring in this crazy long import apollo server plug-in landing page local default from apollo server core and you got to add that as a plug-in in the configuration here so you'll see you can do plugins and then we're going to add in this crazy long name here and that's actually a function so you gotta call it all right so that's gonna enable us to work with apollo sandbox so that's the initial configuration we're gonna add to this more later there's another missing step that we need to add in here which is you know we mentioned that since we're doing the schema first ideally our process is able to find the schema and turn that into typescript definitions that our code can work with so the way we're going to be able to do that is we're going to create this new file in src and we're going to call it generate dash typings dot ts and then i'm going to paste some code in here that you can pause the video if you want to copy it but basically this just defines a lot of the same stuff where uh we tell it where exactly is our schema files again it's the things that end in that graphql and then we're gonna tell it where is it gonna generate the type definitions we're going to do that in src slash graphql.ts we're going to output things as classes i think by default they do it as interfaces classes in general just work really well in the nasjs ecosystem because of all the decorator use and finally we're going to call true here for watch so that every time you touch the graphql schema it's all automatically going to watch for those changes and then it's going to recreate the type definitions for you so that's really useful for development right so this is basically a script that we want to be able to call you know as we're developing so we're going to create an npm script for this open up package.json all right so let's add a new script here maybe under start dev let's call it gen typings and we're going to do ts node and then point that to the file that we just created so that should be generate typings and you'll notice ts node is already a dependency of our project here so you don't even have to install that yourself all right so we've added that new script now if you run that right now with npm gen typings you'll see that it's actually gonna fail because we don't yet have any graphql files in here so that's the next step where we're going to do is actually start putting together some of our initial resolvers and the graphql schema so back in the terminal something that nest gs is actually very good about is they have some really nice generators so one of the things that they provide is this command that you can call you can do nest generate or g for short nsg resource and then provided a name of the resource that you're trying to make an api for in our case let's start with donations because that's ultimately what we're trying to do right we're trying to simulate a an app for donations hit enter there and then it's going to let you pick which transport layer you want and like we said we're doing graphql schema first so we'll select enter there and then it's going to ask do you want crud entry points now we don't actually need the full crud because our app is really mostly going to do reading and creating but i'm still going to hit yes here because it's going to generate a good amount of the boilerplate for us and then we'll just clean it up a little bit to remove the update and the delete so hit enter there it's going to start installing some packages you'll see it created a lot of these files for us in a new donations folder so let's take a look at some of these so in src donations there's now new a new donations.graphql which is our first sdl for graphql and you'll see that it comes with an initial example schema by the way if your screen looks like this and it's all white or it's all one color that means you don't have the proper extension to enable syntax highlighting so i would look at a graphql extension just search for graphql and then typically whichever one has the the highest downloads here install that and that should enable syntax highlighting for you all right so back in the file now we have some nice syntax highlighting let's take a look at what we need to clean up in here so we've got the donation with an example field and then we got an input we got an update which we don't actually need so we're going to delete that one we'll keep the existing queries in here for now but we'll update that in a little bit and then the mutations we only really care about creating donations uh you can't update donations after you send them and we're not going to allow deleting donations so that's our initial schema and then of course we're going to add more to this in a bit and since we just deleted some stuff there we need to clean up the generated resolver so donations resolver here is now going to have some stuff that doesn't exist so we need to delete this update mutation which you're not going to use as well this as well as the remove mutation all right so at this point your resolver should only have really the create the find all the find one and that's it later on we're going to add subscriptions and some extra queries to this but we're going to take it one step at a time we also might as well clean up the donation service a little bit you'll see that it just has some initial mock implementations in here again we don't need to update and remove so let's clean that up all right so now we we're starting from this initial schema and let's go ahead and run our generate typings just to make sure that that works so back in the terminal i'm going to run npm run gen typings and if you get this error where it says it cannot find module ts morph which i ran into this in the past as well i just go ahead and install that and that should fix the problem so npm install ts-morph all right let me rerun that npm run gen typings again and now you'll see no errors and it's on it's in watch mode pretty much and it says that it generates some stuff for us if we take a look in src graphql you'll see that there is now that new file that is it says it was automatically generated and you'll see that it has basically the type definitions that's coming from that's generated from our graphql schema or graphql sdl so it has that that donation with the example field and the create donation input right so that's coming from this basically so it's taking that graphql seo and turning into the compatible or the corresponding typescript definitions that we can use in our development in another terminal let's actually go ahead and run our application with npm run start dev and once that's running you should be able to go to your browser if you go to localhost 3000 you should see a hello world message there which is coming from some of the boilerplate code that comes when you create the new application but the graphql itself if you go to slash graphql you're going to see this new page where it says ready to explore your graph and then click this button that says query your server and that's going to take us to apollo studio and this is going to be the apollo sandbox i was talking about it's kind of like the uh slightly better version of the graphql playground it's very powerful it allows you to sort of build your queries on the fly uh using these uh these buttons over here so for example all right imagine you have an empty query you can explore what queries do i have and then you can just pick and choose what you want to get in here so for example in our case oh we want to get a let's get the donations you'll notice that as i click into that it shows me oh you can also add this field example field so let's run a query here now this is going to fail because we haven't actually implemented the the resolver for this red so if we were to go back in the code right we're really trying to do we're trying to call this donations and it's expecting you to return an array that has this example field in it so let's just fix this real quick so that the we can test that our our basic api is already working so what i'm going to do is i'm going to open up donationservice.ts and i'm going to implement a mock version of this find all so that it's not just returning a string and that's why it's erroring out right so it's expecting an array that returns a set of objects that has example field in it and that needs to be a number right so if i return a a mock data like this and go back to our sandbox now we should be able to rerun this query by clicking the button here and then we get back our data right so this just makes sure that we all the things are wired up i highly recommend to to do the simple test as well if you can alright so we're going to keep going back and forth to the sandbox as we build out the code just to test out our api is working as expected all right so but obviously we're not actually using this example field right again it is just an example so let's go back into the donations.graphql and start implementing the actual schema that we're trying to represent for the application so if we go back to team trees.org and again we're trying to emulate the same application assuming that the team sees that org website is probably going to be fairly similar this is going to give us a hint to really what our data model should be so for example you know obviously there's some kind of count right in this case it's trees but you know in team c's it's gonna be pounds because you know one dollar equals one pound uh so we need some kind of data field that is representing the count of whatever that is whether it's pound trees whatever so in our case we're going to actually change example field on the left here to count and this is going to be a required field so we're going to add the exclamation point and since we know we're going to be pulling this from a database you know it's probably going to have an id as well and let's just assume that's also a number all right so what else do we need to add here when you hit next on the right side here on the application itself it then goes you it then puts you into this details form which again kind of gives you a hint on what our data model should be if you try to go to the next step it'll tell you what actually is required so display name and email address let's add that in play name should be a required string and email similarly right and then we also do mobile which is just an optional string we've got an optional team and then finally a message that's also optional and then there's a couple other things in here that are effectively boolean so you can add that in if you want uh we're just gonna stick with these simple fields uh just to keep it simple you know an exercise that you perhaps could implement on your own is the ability to turn on uh this checkbox here as an anonymous donation and then i'm guessing if it's anonymous then it shouldn't be showing up in the in the leaderboard so that could be a cool feature for you to to implement right so that pretty much gives us our base donation data model now there's one more field in here that's important for us to add which is some kind of timestamp so we're gonna do created at and we're gonna need some kind of scalar to represent the the actual type for this let's call it date time for now and to be able to actually use that scalar you need to have uh the definition for it so we're going to do scalar date time now our application here doesn't actually know yet what is the implementation of that scalar like what is that type really now an sgs actually allows you to define custom scalers if you wanted to or you can just find an existing one from npm so in our case here we're going to install graphql iso date here as our scalar and this is going to be able to represent basically your you know your typical date time timestamps you know such as potentially a a utc string which usually which we can use to store you know our timestamps when donations are created so in the terminal i'm going to go ahead and install graphql iso date now if you're using npm 7 and you get this pure dependency problem you'll notice that it's looking for graphql versions up to 14 but it doesn't yet know about version 15 which is what we're using uh so to get around that we're just going to use the legacy pure depths command here which is basically going to allow it to not complain about it so we're going to do legacy pure depths and then rerun that install if you run into this problem all right that should be good to go now to actually sort of enable that for our system here we need to go into our app module dot ts import graphql date time from that package then we're going to add it as a resolvers field here and make sure to use datetime as the key here that's going to correspond to the datetime scalar that we have here right so it's saying that this key really represents this scalar of graphql datetime so now that we have that out of the way back in the donations.graphql file we've got our donation schema here and we're actually just going to take all these fields and port that over to the create input here which is you know when you're creating we're really asking for these fields so that's all pretty much going to be the same except it won't have the id yet all right at this point you know we have sort of our our base api layer set up you know this graphql file really sort of is a description of the the contract that we're trying to expose as our our api our graphql api now it would be nice if we can actually start calling this and getting real data back right but we need a database for that and that's where prisma comes in so we're gonna go ahead and get prisma set up all right so before we set up prism i'm actually just gonna kill our running dev servers here i also cleared the generate typings command because i was watching our our code right so i i just closed all of that and we're going to go back to just an empty terminal here and we're going to do npx prisma init and we're going to enter air and this is actually the core of the setup for prisma it's very nice and it's going to ask me to install prisma we're going to hit yes here and you'll see it has all this information in the terminal on what you need to do next we're going to walk you through some of those stuff that you need to care about so you'll notice that there is now a prisma folder in our code here outside of src and that's going to have the the prisma schema so if you haven't worked with prisma before this is basically the the schema that's going to represent our database right so this is a description of all the potential tables that we have it's also going to describe the database that you're using as well as as you can see the connection strings and stuff like that now for the purposes of this tutorial we're actually going to switch to sqlite just because it's very easy to to set up so change the provider to sqlite and then notice that it's pulling a database url from the environment variables that's coming from dot env file here let's go into that env file and make sure you change this database url to something like this this is just basically telling sqlite the file name that we're going to save into it's going to save into dev.db inside our prisma folder all right so now that we have the database configuration out of the way back in the uh the schema.prisma file we're going to add a new donation model and you'll notice that this is actually going to be very similar to our donations.graphql file we can put it side by side so we're really trying to emulate a lot of the same fields basically the same fields right so exactly the same fields that we have here we're going to have in our donation table there's a couple other details here where we're saying that the id is an auto incrementing integer and the created ad is going to default to the current time and then finally notice that the mobile message and team has the question mark on it because that's how you do the nullable optional fields in prisma so this is one thing that make sure you don't get too confused about is that while the two syntaxes between the graphql sdl as well as the the syntax for the schema the prisma schema is very similar right they're not exactly the same so don't mix those two syntaxes together they are very similar but make sure you you kind of understand the difference right like notice here we're really working on spaces instead of like key value pairs like in over here and then similarly they use question marks for optional things whereas in the graphql schema they use the exclamation point for required things right so again very similar syntax just don't mix it up and it's probably also a good question to ask is why is it that we're basically sort of replicating the same schema in two places right well in most similar cases like this you have a database table that max that maps almost one to one to your api um definition which is our graphql on the right here but that's not always going to be the case in in other applications right like you might be saving you know additional fields in the database than what you actually expose through the ui so in a lot of cases yeah they might be the same but that might not always be the case for everything right so again to summarize the schema prisma file is a representation of our database schema the underlying actual schema for our data store and the graphql schema on the right here is a representation of our api layer which is you know it's describing what fields are we are available for us to query what mutations exists for me to be able to call and stuff like that right so again very similar uh syntax in but they each have their own purpose all right so now that we have our donation model here so this basically again represents a database table well we need to run a migration to actually create that table in our sqlite database so the way to do that is back in the terminal we're going to do npx prisma migrate dev and that is the the name of our database and then we're going to pass the name flag here and pass in whatever name you want to name your migration so we're just going to call it init because this is going to be our first migration hit enter there and you'll notice it did a couple things for us it generated a new sql file which is our our new migration and then it ran that migration and on top of that you'll also notice that it actually uh generated a prisma client for us which we're going to be using in just a second here to actually query our database so if you want to take a look under prisma migrations there should now be a bunch of new files in here uh you'll see a migration that's sql and that's going to describe the the thing we just uh the actual sql representation of the table that we just described in the schema which is donation and then it has all these actual database columns all right so now at this point we should have a new donation table in our sql lite database but it doesn't have any data one thing you can do for development purposes is you can also introduce uh some data seeding so as a quick example in the prisms folder we're going to create a new file called c.t.s and within this file you can actually just bring in the prismaclient create a new client and then use that to basically start querying your database in our case uh we're just gonna empty out the donation table and then we're gonna create some data right we're gonna insert uh one item here which is alice uh with a count of five as if she donated five dollars and then this is her email alice at prisma.io and then we're gonna console log that result right and then the bottom here is basically just running the script of doing the seating now how do we actually trigger this is what you have to do is go into your package.json and within here you need to add a new key let's call it's going to be prisma and then within that object provide the key of seed and then ts node prisma c.t.s right we're just pointing to that file that we just created and then to run our seed we just go back into the terminal and we're going to run the command npx prisma db seed and you'll notice that it runs that command talking to our c.t.s file it logs that alice record that got created notice that there's a timestamp generated here right and we got id1 so that's basically proving that our database are is working right it's doing its automate auto incrementing ids it's doing a timestamp so now we have a table with one item in it and if you want to you could use a database client like d beaver or something like that and open up your sqlite file in there and you should be able to see that entry but in our case we're actually just gonna do the query right so now we've got the database there's some data in there we've got the graphql layer set up so now we just need to connect the two to start actually building out this real api right so we've done a lot of setup so far now let's get into the actual meat and bones so one thing i need to mention real quick before we move forward here is remember that when we when we ran the migrations it auto-generated the prisma client for us which we're going to use in a second here but anytime you actually change this schema for prisma you need to regenerate that prisma client so the way you do that is through the command npx prisma generate right so if you run that command it's gonna parse through your schema and then it's gonna regenerate that that client for you right and that's the client that we're gonna use to do our queries and stuff so now there's a couple of things that you need to generate if you're if you're kind of following along is that our graphql schema needs to generate typescript typings our personal schema needs to generate the client the prisma client ideally we're able to run both of those things at the same time automatically as we're changing the code it would be kind of painful to have to remember to keep running those scripts right what we're going to do is we're going to create a simple npm script to kind of combine all of those uh thing generator things that we need to be running as we're developing code right so i'm going to introduce a new script in here called let's do prisma generate and basically it's just going to be that prisma generate command and then we're also going to add the watch command here because uh ideally we wanted to just watch the the file for changes as we're developing and then regenerate the client as it sees those changes so you probably see that these three scripts really need to be running in parallel so that we can do our development right so the server the net server needs to be running so that we can and that's watching our code so that it regenerates itself re-runs itself as we change the nexus code and then similarly uh this is watching the graphql schemas and this is watching the prisma schema so we're gonna combine all three of these into a single script so that it's not painful to have to keep running three things at once each time we're developing so we're gonna introduce a simpler dev script here and we're just gonna use concurrently to run all three all those things in parallel so actually we need to install that first so let's do npm install uh dash d concurrently and concurrently as the name implies just allows you to run your commands concurrently in parallel so and it has this nice shorthand syntax of if you provide npm colon then that just signals that oh that's an npm script so that's why we have npm start dev which is going to call this guy we have npm gen typings we're just going to call this die and then npm prisma generate which is going to call this guy right so and then remember to escape the the quotes in here but pretty much when we run dev here so let's go back to terminal and do npm run dev you should notice that that's going to run all three of our scripts side by side and you'll see it even has some markers here to tell you which part of the thing is running right so these logs are coming from start dev which is our nest earlier it also showed you the logs for the two other things the generate typings in the prisma client all right so now we can go back into actually writing our api so all of the the configuration the plumbing uh is in place all of those things are listening for our code changes so let's go back into our donations api and actually start implementing it so in our donations resolver we want to go ahead and let's implement this find all and ideally we should get back our single item in the database right which was alice donation so we need to actually implement this find all to be you know a call to our database so for us to actually be able to utilize prisma the prismaclient to make queries we first need to create a prisma service for necessities to kind of wire things up together for us so in the prisma folder i'm going to create a new prisma dot service and then i'm just going to paste some code in here go ahead and pause the video if you need to copy it this is also in the sgs documentation but pretty much this is just the the basic setup that you need to do to create a prisma service that you can inject into any of your other nexus services alright so once you have that we're going to make that prisma service be a import in our donations module all right so in here you notice that we already have two providers in here the resolver and the service we're going to add the prisma service in here and make sure to import that from the file that we just created and then i actually want to make this be a relative import just to make sure it doesn't get messed up alright so now effectively the prisma service becomes a dependency that we can inject anywhere so we can go into our donation service here and if you're familiar with nesjs you know that dependencies get injected through constructors so if i add a constructor here with prisma as a injected dependency here now we should be able to use that object this is effectively the prismaclient and we can use that to make our actual query so within the find all we can simply change this to this.prisma and notice that when i hit dot here donations should be available as an option here as a key because of the generated prisma client that we had and then we can do dot find many and this simply should just return everything in our donations table all right so now that we have that all right so now that we have that make sure our application is running uh and and by the way if you find that your application for some reason is not behaving correctly even though you know it's supposed to be behaving correctly i found that sometimes the all of the stuff that's being generated kind of messes things up so i would delete the dist folder and just rerun your application that seems to kind of clear things up so if you run into some kind of weird problem where it isn't behaving as like you think it should try that first delete the this folder and then rerun your dev script so that's what we're going to do exactly so i'm going to rerun my dev script here alright so my application is backup live we're gonna so back in our apollo studio here the sandbox we're gonna create a new query and then within that query we're gonna do donations within that we're gonna add in let's do id count display name and email all of the required things that we added to the database and then hit query and we should be able to get back alice you know we can even add created add here and do the clear again and you know you should be able to see that timestamp so at this point basically what happened is that we now have a uh you know all the pipeline is in place we've got the the graphql set up we've got the database set up and now they're talking together so the rest of what we need to do from here is really just start implementing the rest of the api hey guys let's take a quick break if you're still watching at this point i'm sure it's kind of a lot to take in right all this stuff putting it together take a moment to stretch i know i took a break here as you can see in the timer or maybe go to teamses.org and see what this whole mission is all about again this whole video really is for this mission to kind of promote it i encourage you to even just take a look at the website see what they're all about that said let's get back into it let's finish this api all right so we just finished wrapping up implementing the the find all query here for donations getting the list of donations let's quickly go through doing the find one which is actually pretty easy so in the corresponding find one method in donations.service we can simply do something similar here where we do this prisma donation dot find unique and this expects a object that looks like this where you do wear and then an object that has you know the thing you're trying to match in this case we're trying to do you know where the id is matching and uh one of the things you can actually do is if you want to one of the one of the types that prisma generates is actually what this object is so if we change our parameter here to let's call this uh donation where unique input kind of a mouthful but that corresponds to this type prisma dot donation you can see there's a bunch of types that it kind of generates for various queries and stuff we're going to use donation where unique input and if you take a look at what the shape of that object is or that type it's basically that you know that shape with the id in it so that's exactly what this is so you can just take this and do it like that right and that's the same exact thing so that's a quick example of utilizing some of the generated prisma types which can be pretty useful for a lot of typescript apps this this does mean that i think we need to update our donations resolver for the find one and we this needs to be an object now so we're going to do it like that right so let's do a quick test on our sandbox here if we switch to a different query of donation is the donation and you see that it has a parameter in here that or a variable that we can pass in at the bottom here we're just gonna pass in one because we're let's we only have that one item right alice so we're gonna try and extract that single donation and then in here uh just to make sure we got the right object let's get the fields for id count and display name hit query and then you see that it returns that single record of a donation so pretty easy right all right so let's get to some of the more interesting stuff let's try to implement this create donation mutation so one thing to notice here is uh notice how our our input argument type here is great donation input and it's using this type great donation input and that actually comes in from one of the files that nest generated when we created the crud originally so if we go into that it's actually just an empty class now as you saw earlier there's a bunch of classes that prisma sort of generated for us so we can actually just utilize one of those generated classes instead of that one so for example we can turn this into prisma dot donation create input right and if we take a look at what's in that shape that type you know it's basically all of the the fields that we're looking for right if we open up our graphql donations.graphql it basically matches this input type that we created so that donation create input is directly generated from the shape of this so it's just much easier that the fact that we don't have to write that class from scratch we can just use that generated type in our resolver which is in typescript all right so we're forwarding that object down to our create method here in the service so let's go back to the service and let's start implementing the create here and this is also going to be pretty simple as well actually so it's just this.prisma and then this expects a data property kind of similar to our seating file and then you just pass in your create create donation input input similarly over here we need to remember to update this type to be prisma dot donation create input right so that's the same exact type that we use in a resolver and then notice that again we don't have to use this dto anymore and in fact we can just delete that file right we can just delete this entire dto folder if you want we're not really using that and also actually this entities folder similarly it started out with a donation class in there we don't need that either usually the entities contains classes that represents you know the same data that we're working with which we're already representing that both in our graphql schema and our prism schema so there's really no need to define another class for that we already have that definition all right so let's clean up our make sure to clean up these imports because that file doesn't exist anymore both in the resolver and the service right so now our create should be working at this point let's do a quick test in the sandbox here just go all the way back until you get to mutation hit the plus there and then it'll recognize that oh we have this great donation mutation and then again kind of like any other query right we put in what fields do we do we want to query back uh let's just get the id and count and display name right and then notice that it also kind of pre-filled our create donation input here which you can actually also use the the argument section here click into that and also it'll help you to add those keys in for you so the things that are required uh count display name and email let's fill this in let's do let's go big right let's go a thousand provide a name in here and email and let's go ahead and hit the mutation here and you'll notice we get a response of great donation and then id2 because you know it's auto incrementing the id count a thousand display name areas and you can have multiple queries by the way on the operations here that you can run amount of time so we can actually go back and re-query that donations again uh let's go back and add in again id count and display name and hit query and now you'll see we have you know alice and my second donation here so now we got two and just for fun because we're gonna try doing uh sorting and stuff in a little bit here let's add another uh let's do another mutation let's just change up the the variables here a little bit and let's give this guy to be in the middle to be 500 count mutation all right so that'll be our third donation again if we go back to our donations query now we've got three items so this will lead us into uh if you remember in the team p's website there's that feature that i talked about earlier where you can switch between most recent and most trees so we ideally want to be able to uh have that sort by order by feature in our api even before we create our ui so let's start implementing that all right so what we're going to do is back in our donation service uh you know originally our find all was pretty simple right so we're going to add some parameters in here to allow us to do that uh multi-ordering or sorting so we're going to do let's add a order by in here in order by we're just going to give this the shape of we're going to find out what field do they want to order by so that's a string and what direction do we want that to be sorted and then we're just going to destructure those values out of this order by parameter where we're going to extract the field and the direction and we're going to initialize it to be created at descending this is going to match what we have in the ui right where by default we want to show the most recent donation at the top of the list right and uh so that's why we're going to sort by created at descending right so that's what we have here and the way that prisma expects you to pass this in is via the order by key and then uh you have to do basically the field and then the direction so for example you could do created at descending ascending right but we want this to be dynamic so we're going to change this to be the field that we're getting in as a parameter and this will be the direction so the reason that this is an object is you can actually order by multiple items if you wanted to but in our case it's pretty simple we're just allowing for you know basic order by one field given a you know a direction all right so for us to then uh implement this in the resolver we actually first we need to update our uh our graphql to represent that new input right so that means that our donations query now is going to accept a a optional parameter on uh the field and the direction for order by so i'm just going to add a simple a simple order by params where we allow the ability to define the field and the direction just like in the service and then we're going to add it to the query here as an optional parameter order by order by params right and then now that we have that we go back into our resolver to actually make that real so within this find all we're going to add an args for order by kind of similar to what we have in the find one here except this time we're getting an object in we've got that args order by and then the field and the direction and then we just gotta make sure to pass this order by down to the service call actually as i'm writing this i realize that we don't need to inline this type because it's one of the types that's in our generated uh type definitions from the schema right so this this input type is the same class that we have here in graphql the ts so we should be able to take this order by params and replace this guy with that it's going to make sure to import it and we'll also copy that over to our service up here and let's reuse that in here right so that's an example of utilizing the generated typings that you get from the schema to the graphql that he has here um you know same thing any if you need to use the donation um object you know similarly if you and anywhere else in the code you need to refer to the shape of the donation type you know you can import this as well all right so that's our implementation of adding order by let's do a quick test in the sandbox so right now if i rerun the query on donations notice that it's now in created at format or sort it's in the order of created at descending right so it was the 500 the 1000 and then the alice but now we can add an optional argument here the order by argument that we just added to the donations query so you'll notice that it got added here and then in our variables here we can then also add the input fields that we want in there so let's say that we want to switch to um ordering by count and also descending so if we rerun our query here i expect that now the highest one is at the top the lowest one is at the bottom right so this is gonna give us this uh functionality of getting the most trees or in team ceases version it'll be the most pounds right so we'll be able to show who has the most donation out of everyone so far and we'll be able to switch between these two and we'll see the implementation of that in react and oracle shortly there's one other thing that i want to add to our create mutation actually that i forgot which is validation right we talked about earlier that that's that's one of the features that we want to implement is some level of validation now to a to a degree there's already a little bit of built-in validation from the graphql schema so for example if you tried to run a mutation and you didn't include the count here it should complain at you it says uh you know got involved value bad user input but the the stack trace that it puts in there is pretty ugly um it's not super great and also we're not really validating that email is actually an email address right so what we're going to do is actually just discover this package recently and i have a small video on this as well uh it has this generic name with prisma nsgs graphql but uh the way it's supposed to work is it allows you to add a generator to your prisma schema and then it's gonna generate additional classes that's specifically meant for the you know the nes graphql module for for an sjs and i know it's probably confusing and with what i'm talking about here so far but uh in sjs if you didn't already know it has a built-in feature for validation through pipes right so there's this validation pipe that we're going to turn on in a second here and then the typical recommendation that they they want you to use that with is class validator so the gist of it is that if you have classes that represent your dtos right you can use class validator to add these decorators on those classes and then nest is automatically going to look into your request bodies and see if uh it turns it into this class an instance of this class and then runs the validation off of that so that's how it achieves very easy class validator but if you remember we're not really working with classes right if we go back to our resolver for the mutation we're using this generated prisma donation create input which is just a basic type it's not really a class so and again it's also generated so we can add custom decorators to it so that's where this package comes in so let me install this and then we're going to show you how this works so i'm going to copy this install script i'm going to go to my terminal i'm going to do that install and then once that's done we're going to go into our prisma schema.prisma and we're going to add a little bit of configuration here so at the top we're going to add another generator uh and you can pause the screen if you need to copy this but this is basically the the documentation what the documentation says you should do is you're going to define uh where is this coming from and where the output is going to go uh what's the validator class which we said we're trying to use class validator and then what we're trying to generate is the the input classes now with this in place what we can actually do is we can add we can add validation on the model itself via comments and it's kind of special you have to use triple uh three slashes so for example in here i can add three slashes at validator.min length three and that's going to add validation to display name to make sure that it's at least three letters and then we said that we want email to be an actual email string right and then you can look into class validator to see what other validations you can do uh there's a bunch of them but we'll stick with this simple example and remember that our npm script is watching for this file and then it's gonna rerun the prisma generate for us automatically so if you look into your src folder now you're actually going to see a generated folder in here and it's going to have a bunch of stuff in there so we're looking for the equivalent class of this donation create input so you'll see that's over here donation create input in the generated folder if we click into that take a look at what's in here it's effectively the the typescript version the class version of that same type except now it has the decorator so this means that we're going to be able to use this with the validation pipe to actually run those validations right so let's get this in act let's get this working so now that we have the input type we're actually not going to use this prisma type i know it's kind of confusing because we keep switching around but let's import donation create input from that generated folder you don't need this prisma type anymore and then i don't know why this keeps giving me absolute imports but i usually try to use the relative alright so that's effectively the same exact shape as this type except that it has that extra metadata for validation now how we actually need to how we actually enable that is in main.ts if you look at the documentation it says that uh you gotta install class validator and class for transformer so we'll take that so npm install classifier validator class transformer and then in here we're gonna enable the validation pipe globally and let's take our terminal looks like everything's green so let's go back to our apollo sandbox and actually try this out so let's bring back our count in here of let's do 1000 again and and remember we put in two validation rules one is that this plane name needs to be at least three letters so if we just made this two letters and then let's make this email invalid right let's remove the punctuation marks and let's run our mutation now you get back an error message that is a little bit more helpful right it's it actually is providing you some nice messages of what that problem is it says display name must be longer than or equal to three characters email must be an email so our validation actually is running so if we switch this back to mumbo and mumbo dot at email.com that one's gonna pass because it's all valid and then again you can go into class validator and there's a bunch of different rules that you can do for you know numbers and lengths and min max and stuff like that really whatever you want but i think for our use case this should be what we have here should be enough so that's pretty cool right um what else so thinking back to some of the capabilities that we want to enable a big one right is being able to display the total count of all the donations so far so right now our our api doesn't yet expose that information unless you get all the donations and manually calculate all of the you know the summing up yourself client side which because we know this is a capability that we want anyways our api might as well do that calculation for us and then we also said that ideally we can get that number in real time so that we can utilize uh perhaps subscriptions with graphql so that's what we're going to go into next so here's how we're going to start first let's update our schema our graphql schema to represent those new capabilities that we want to enable one is we probably want to add a a basic new query that gets us the total donations you know just as a regular query so let's go ahead and just implement that real quick so in our resolver we're just going to add a new query in here for total donations and we're just going to do something simple like this right so it's just similarly structure there's anything else in here you do query in the name of the query and then we're just gonna call a method in our donation service which is not yet implemented so in our donation service let's add a new method in here async method get total and in a database how might we get the total of uh of all the columns right well you can use well you can use basically an aggregate query and prismo has support for that so we can just do let's do response equals await this dot prisma dot donation dot aggregate and then within this method you have to define what do you want to aggregate um we're going to tell it that we want to get the sum of the count column so we're going to pass it in here true right and this is going to bring back a response object that is going to include that count but we just want to return that count directly so we're going to parse it out so we're going to do return response that underscore sum that count and i believe that should complete our implementation here so back in our sandbox again uh let's do a new query in here for a total donations let's add total donations in here which just returns a number so i think if we just hit uh run query here you know we get back all of our donations so far and then at the bottom here it says total donations 2505 which i believe is correct right so one thousand one thousand two thousand five hundred and five so there you go there's our there's our aggregator now that's pretty cool that we can get the number but ideally we we have the ability to listen for changes again if you imagine that this website is for like mr beast really for him to be able to just look at the website and see the number going up as people are donating right you know so that would be really cool to be able to update that in real time so how do you do something like that on a client well typically you have really two solutions one is you can do something with websockets so that you have a direct connection so that every time that value changes the server can push the updated value to the client in the graphql space that's usually implemented via subscriptions which is exactly what we're going to do the other alternative of course is you can just pull the uh this this total donations query that we have and then that's another way for you to sort of make it real time by just trying to update it on an interval but we're gonna do the cool the cooler solution which is subscriptions so we can actually get that value in real time all right so just like before we need to go back to our donations.graphql here and we need to add the description of our subscription here so let's say that we're uh do subscription and let's name that total updated right because that's what you're trying to subscribe to is i want to get the result of when that total got updated and then let's define the shape of this uh result to just be you know it's just a total right so the subscription when ideally when it's working you'll get back basically this object with the new total in it now how do we actually implement that uh well there's a couple uh setup things that we need to do first of all we have got we got to go back to our app module and add to our graphql module for root configuration here there is a subscriptions property that this accepts and this is going to allow you to configure the type of the websocket server that you're going to use with our apollo server behind the scenes so there's really two options one is sort of the legacy option which is this package subscription transfer ws and then the other is the newer one which is what is recommended to be used which is the graphql ws package and there's a note about this in the documentation if you want to take a look at it you know it over here it says that if you use the install subscription handlers true property that's actually turning on the subscriptions transfer ws which is being removed in favor of graphqlws right so you have the option to turn either one or both of those off or on if you want to now we're gonna turn both of them on actually because the the sandbox that we're using is still using the the old client the subscription transport ws in our react client later on we're going to use graphqlws so we kind of have to support both and because this is the newer one i wanted to show you how to use that one but we're using this guy as well because we needed to call our subscription in the apollo sandbox all right with that configuration in place there's one more library that we need to install we're going to install the graphql subscriptions package and this is like a mini pub sub system uh as it says here like redis that's gonna allow us to sort of uh communicate when our subscription needs to be triggered uh you know in our case we need to trigger the subscription to push data at every mutation right and also notice it says here that you need to use this with a network transport which we have configured previously so let's go ahead and install that package and the terminal and payment so graphql subscriptions and the way we use that is within the resolver i'm going to import the pub sub object and we're going to create a new instance of that like so and then like we said within the mutation this is the thing that we want to effectively publish the event that hey there's a new donation that came in update the total right so let's make this into an async function because we need to await some stuff in here what we're going to do is instead of directly returning the created value or donation we're gonna do created equals this guy and we're gonna wait it and why is that because what we wanna do is we wanna calculate the total after it's been added so we're going to do this dot donation service that get total right so we wanted to save the donation first and then calculate the updated total and then we want to communicate that new total to our subscription so we're gonna do pub sub dot publish total updated and then we're gonna provide an object here that has the shape of that subscription response that we we defined earlier so it's going to be total updated total and then you got to remember to return created here because we still want that to return the value as before but now it's actually calculating the total and then publishing that as an event saying hey subscription hey subscribers you now have a new total get this value right so the only missing piece here is that we need to add uh the actual subscription uh resolver right so let's add a new meta method here let's call it total updated and make sure to wrap this decorate this with subscription from sgs graphql and then we're going to return pub sub dot async iterator and then make sure to provide this with the same key that we're using on our publish here so this needs to be total updated right so to kind of explain again to summarize when you when someone creates a donation we save that in the database we calculate the new total we then publish an event with the shape of the result that we're returning right if we go back to our donations graphql remember our subscription returns this result which is just this object of the total right so that's what we're trying to do to really generate here so this object ultimately is what this responds with when there is a new publish so let's get see let's go back to our sandbox and see if we can uh try this out in action all right so i'm back in the sandbox this time i'm going to add a subscription total updated and i'm going to get the total field in here that's really the only thing we have i'm going to hit play here and you'll notice that it's just listening and you know and it says it's green so technically that means we made the connection if you didn't configure the the sub this uh transport layer inc correctly uh you'll actually see that that turns into red because it's unable to connect again that's why we have this one it's so that we specifically can test uh with the with the sandbox if you're not using the sandbox you can just turn this off and utilize the the more newer recommended one which is this graphqlws anyways so back to this now it's listening right uh so how do we trigger getting a new total from the subscription well we just need to create a new mutation so let's rerun this old mutation that we had before with mumbo and a thousand and hit the mutation here that created the new mutation and notice that we got a response a pushed the data to the client that now the total updated to 3500 right if we do another mutation again another 1000 notice the the id changed here so that's a different donation now again our response updated to 4500 so now we have the ability to listen for total donations in real time right so you can imagine as we implement the react ui we'll be able to watch that number go up in real time all right so isn't that pretty cool i think uh we're gonna stop here in terms of the api uh again we're just trying to implement a subset of the full uh capabilities of the website uh we have more than enough here to create a basic you know donations ui that's gonna talk to this um api make donations and then see that number updating in real time so that should be pretty cool on the react side hey guys quick pause so it's the next day for me already and i have three hours to finish this tutorial because i want to release it the same time teamses.org opens up but by the time you watch this video it should already be opened up so make sure to take a break and stop by teamses.org and see what the mission is all about so we're pretty much done with the api uh there's one little thing i'm going to change in here before we start with the client one is that i'm going to change the default port here to 3001 because uh we're going to use create react app and that usually starts on 3000 as well so we just want to make sure they don't conflict and then finally we're going to enable cores in here just so we don't have any issues when our client makes requests to our api you definitely can further configure this for your use case but for now we're just going to keep it simple and finally make sure to start your application again if it's not already running npm run dev all right at this point we're ready to start the client we said that we're going to use chakra with react create react app specifically what's really nice with chakra is actually comes with some templates ready to use for create react app so this is what we're going to do we're going to do npx create react app the name of our application template and then chakra ui typescript right so we're going to copy this and open up a terminal so let's go to our desktop again paste that command in and i'm just going to change this to team c's ui hit enter and that should initialize our application for us there we go we should be able to open this in vs code as well alright so what we get here is pretty much your typical create react app except it's already set up to run with chakra so you'll notice if you go into app.tsx it's already gonna have you know a bunch of stuff in here sort of pre-populated for you makes the setup very easy so let's go ahead and start our application we're just gonna run npm start and eventually the browser should open up automatically on localhost 3000 and you're gonna see the spinning chakra ui logo and it even comes with this dark mode switcher which is really nice but we're actually gonna get rid of that because we don't need it by the way whether or not you already know how to use chakra i think that as i'm using it uh you'll find it pretty intuitive or at least i'll try to explain it as i go but really it it's it's a lot similar to something like tailwind so in css where in tailwind right you have utility css classes in chakra you have something similar except instead of you putting in a bunch of utility classes there's actually just a bunch of utility props that represent css that you can change so you'll see that you know you have your typical props for things like text align font size uh width and height but you also have like short hands like we have h here for which is the shorthand for height you have min h for min height you have p for padding and another cool thing is that you'll see that you'll see this a lot is you'll see numbers being passed into props like this and this does not necessarily mean padding is three pixels this is actually saying padding is the third in the scale of sizing so let me open up some documentation here if you go into the default theme in chakra it explains this very well you'll see that there's a bunch of default colors that's already in here and there's a reference that you can use so already has a bunch of colors that you can use by default now it also has a scale for font sizes so you'll see that there's a lot of places where you can just change the font size by passing in you know different sizes like xs sm xl and so on similarly font weights you know you can just do font weight equals medium and that's going to change it to a size of 500 and then this is what i was talking about with the padding is that there's different numbers for different sizes but basically it's a scale you don't have to think about the the pixels anymore you just think oh what's the size in the scale that i should probably use this in and even if you don't know the scale you can just kind of play around with a number to see what looks right to you so for example in our code earlier right you saw uh p equals three this is saying that this is actually padding equals 0.75 ram you know similarly you'll see also that in other props like spacing with and something other similar right you see examples of the color here where you can pull in tl 500 font size to xl you know same thing for anything that uses the sizes prop so there is a lot of stuff in here border radius again they have props for that that sort of define a defined scale for you and the nice thing here is that all of this scale comes from the theme so at any given point you can just change the theme and it's going to globally update the rest of your application right so you don't have to worry about oh there's this little bits of these things this how many pixels on this one this one's another 10 pixels over here oh i got to change it over there no you just change it in one place in the theme and it kind of globally changes so again this is i know it's probably a lot to take in if you've never seen this before but i think again as i as i use it it'll i'm hoping that it's going to be fairly intuitive for you to understand all right with that explanation out of the way so again we have this uh code running in the app we're going to get rid of let's clean this up a little bit so we don't really need the color mode switcher this v stack is actually really interesting because it's it allows you to define uh spacing between things that are stacked right if you look up the stack documentation you notice that you can use it to define spacing between a stack of things that's horizontal or vertical right so this is really good for lists you can of course also use things like you know you can use flex with uh justify content space between or something like that so you can kind of play around with it and see which one works for your use case we'll see a little bit more examples of that as we go all right well let's also get rid of this link for learning chakra and then let's start messing around with this file to you know make it look like the things that we expect from the website again we're kind of using the team trees website sort of as a reference there's a good chance that tmc's website might look different but we should be you know reasonably close to what might be on that page is likely right some kind of mission statement probably the the numbers that count of how many uh donations have been made so far and then as we talked about the rest of the stuff in here so my why don't we actually start by changing the logo here i'm just going to paste in a logo from team sees and you can just go to teamsies.org to pull a copy of this yourself and then notice that we have a logo component here we're just going to change what that points to so we're going to change the logo here to be the one from team c's and then we don't actually want it to be spinning like that so let's get rid of this uh animation so we're just gonna simplify it to be something like that back in the app.tsx let's uh let's update this size to be a little bit smaller i think let's go with 32 and then we probably should have some kind of header in here so let's put in heading from chakra and let's update this text to be you know just your typical subtext and by the way the headings you can add various props on it so for example maybe we want this to be an h1 specifically and let's set the size to be an xl which i think will be a little bit bigger than what we have now and then i guess we're actually doing team c's right so planting doesn't really make sense here so let's do i don't know remove trash with us and track our progress all right so pretty basic we just have basically you know kind of have what we have over here we have a logo uh you can play around with the sizes and stuff uh we're keeping it pretty simple you know so uh maybe let's add a uh grade 50 background here just so it's a little bit off-white so we should probably think about the fonts real quick so that if you look into the the fonts are using in teamtrees.org and tmcs.org it's actually using monsterat so let's quickly look at how we're supposed to configure fonts with chakra all right so in another terminal there is actually a library that you can install a bunch of fonts from so almost a lot of the fonts that you can find on google fonts for example is in this library so we're gonna install uh at font source slash monsaret once there how do you say that monster whatever let's install that and once that's installed we go back to our app.tsx in your case wherever you have this chakra provider where you're changing the theme that's where you're going to extend the theme right so that's exactly what we're going to do we're going to extend the theme and then we're going to add the extension to change the font to be what we want which is monsterat and then we don't need to import this theme directly anymore because we're extending it so the theme we're passing into the provider is the extended theme and make sure to import extend theme from chakra now if you look in the app right now you'll see that i actually changed the font a little bit but it doesn't load in the fonts yet so there's one more step missing here is that you need to load in the different weights that you're going to use so uh you can load it in like this so import font source the name of the font and then the weight so right now we just imported 400 but really your app is probably going to use multiple weights at minimum you probably want something for a regular size a regular font weight a bold and then a light so in our case we're going to do 700 400 and 300 for the three of those and then in your case feel free to add more as you need but this should cover our use case well where you'll notice oh the the heading is usually bolded so that's probably going to use 700 the regular text might use i'm guessing 400 and then anywhere you're using a light font weight is probably going to use 300 right so for example i think if we change our text to be font weight light you should notice that that made the the text here slightly thinner right so again there's lots of like uh utility props that you can use all over the place to to change this around we're just going to change that back to regular alright so we got some base styling out of the way all right so we got some base styling out of the way what do we want to do next so remember at the top of the team tree site and likely also in the tmc site in the future it's probably going to have some kind of counter of how many donations has been made so far so let's try to emulate something like that another cool thing by the way is when you refresh the page you'll notice this number actually does this little counting animation it would be cool if you can replicate that so in our case maybe let's bring in another heading of let's say let's make this an h2 where the size of 4xl we want that to be pretty big and then let's put in some dummy numbers in here just to see what it looks like alright so we got some big numbers at the bottom here so how would we do the animation of it counting up now i'm actually not a huge animation expert but i do know that in our application one of the dependencies that it already has because of chakra i think is framer motion and framer motion is really useful for all sorts of animations and and in stack overflow if you search for how to do a basic count up animation using firmware motion you know i want to give credit too to this guy who gave this answer we're pretty much gonna copy that because that's that's basically what we need um and again because we already have frame promotion there's nothing for us to install all right so what i'm gonna do is i think i'm gonna make a new folder in src let's put a donations folder in here and maybe that's where we'll put some of the uh components related to the donation right so in our case we want to have a animated counter so i just added a counter.tsx in here and then i put in the code from that sac of lfo answer you can just copy it feel free to pause the video and what's going on here it's really simple it's just that we just have a div and that's where we're gonna put the number in but this is actually gonna go into that div and actually animate the whatever numbers it is in there it's going to animate that value and you'll see that we're also doing two local string which is going to format it with the commas for for a number right so if you provide this with a prop of from zero to whatever the current donation is it's just gonna animate it up to that from zero up to that number so let's take a look at this in action so if we were to wrap this number in our counter component right so import counter from donation slash counter and we're gonna do from zero to and actually this number should be passed in as a prop not not as a child all right and then we can just self-close this counter all right so that's what we should have it should be pretty simple let's take a look at how that looks in the ui all right so you see as we sort of refresh it's counting up nice little animation that's pretty cool right at the same time again we're getting the formatting so that should be pretty similar to what's going on here with the team trees.org website cool so obviously that's just a mach number uh eventually we should change that to the actual total donations number that we have from the api so that should be a good next step for us just let's talk to the api using oracle let's grab what that total number is all right so to set up oracle we're going to go into our base index.tsx and then we're going to do we need to install a couple of things so open up another terminal and we're going to do npm install oracle and then we're actually also going to install graphql.ws because you know remember we're trying to do subscriptions so we might as well install that now now it might be a good question why are we using oracle instead of uh something like apollo client oracle is very much similar but it starts out very small um and you'll see that you can add on to it so it's very good for uh you know small projects that you don't necessarily maybe need all the features you can just add the features in when you need them whereas when you use apollo client i think it starts out much bigger and it has a lot more features out of the box so it probably depends on your preference really alright so in our index.tsx we're going to import a bunch of stuff from oracle and really too big to create a simple client for oracle what you need to do is do create client and then pass in some configuration in here the very minimal configuration that we actually need is just a url so this is going to point to http localhost 3001 slash graphql right that's our running api that we just built earlier and then while we're here we might as well set up the subscriptions as well so we install graphqlws so let's make an import for that and we're going to also create a client for that so i think of it as sort of its own connection right because it's uh its own its own websocket connection so that's just going to be ws locals 3000 graphql now like i said the base arkle is actually extendable and they do that through what's called exchanges so to keep things simple basically we're just gonna extend though whatever current exchanges we have right that's what this default exchanges is so we're actually just gonna spread that so we wanna put in what already is there but we're going to extend it by adding the subscription exchange and this takes in a little bit of configuration as well and let me type up some code and then you can just copy it so basically in this subscription exchange we're just adding some configuration for it to use the specific ws client that we have from graph graphqlws right so we're just making the two sort of work together right and that's really pretty much the the configuration we need to have for oracle now we just need to use the provider so that we can provide this client across our entire application and then we can just create queries anywhere so to do that we're actually going to wrap our entire application with that provider and this is expecting a value of the client that we created all right now once you have that configuration we can just start making queries all right let's do something simple let's try to get this number the total donations that we have so far and let's dynamically get that from our api so in my case here i'm trying to keep it simple so that you can follow it easily and i'm just going to add a bunch of things pretty much in the same file but you know feel free to organize it in whatever file you want to do based on your preference so i'm just going to add in here a string of that represents our query right we just have we have the total donations query that we made earlier so we're just going to have that be inside this total donations query string and then with oracle we can actually bring in queries via hooks so we're going to update this code a little bit to make sure we can bring in a hook in here all right so within about the return we're just going to add a use query from oracle and notice that that returns an array and the first item in that array has these properties data fetching and error and what you can do with that is you know you can use that to present a loading spinner for example you know i'll just put in a simple if fetching in here shows some loading strings we can have obviously make that a little bit better later and then uh if there's an error maybe just print some kind of error message all right but at this point i expect that data is going to be basically the object that we get back from this query so we should be able to go into the counter here and do data dot total donations all right let's take a look at our ui and now we have 4505 in here i think that's where we left off with the api earlier right so that's now coming from our api that's no longer a hard-coded value now while we're at it we did say that we want to be able to see this number dynamically update in real time as we get new donations right so we might as well set up our subscription at the same time while we're here so so let's add another query in here and we're going to call it total updated query and it's going to be that query that we have to do the subscription right again we're pulling out the the total we also need to add in a handler for what happens when we actually get that value back you know when we our client is receiving a value from the subscription so we need to create a handle subscription method that is gonna do something with the previous and the new value right in our case we just wanna get back the new total so we're gonna return new total that total updated right so it's pulling it from this response object and we're just returning that total now just like there's a hook for making queries there's also a hook for making subscriptions so we're gonna do use subscription from oracle we're gonna pass in our query in here and our handle subscription and that's gonna bring us back this response object and within that response is actually our our value that we got got back from the handle subscription which is the total itself so we're just gonna do in the two here we're gonna do rest.data and remember initially when you make the subscription it doesn't return any value until it gets a new value pushed in from the pub sub so we're going to do rest.data or data.told donations so that it initializes to this value but as we get new donations it's gonna update it to whatever the newest value is from the subscription so now that we have that subscription wired up uh would be cool to test that out right we don't yet have the donation form but we do have the apollo sandbox right so now that'll be in localhost 3001 slash graphql and we still have our previous mutation in here from mambo let's do mumbo 2 1000 so let's do this side by side so that you can kind of see this updating in real time just to kind of prove it out right so if i hit the play button here on the left with the mutation that's going to send a new donation of a thousand and we should expect the number to update on the right side so if i hit submit here notice that now we have five thousand let's do another one just to check and now it's six thousand so it literally is updating in real time that's pretty cool isn't it so subscriptions are really cool that'll be really useful for anything that uh requires real-time connections you know that could be great for chat applications uh perhaps you have some kind of uh real-time data dashboard it'll be really useful for that kind of stuff all right so while we're at it uh what do we need to add here so remember that within the home page there is really the let's just mark it out right now there's the donation let's call it wizard right it's kind of like a multi-step form and then under that is the leader board which i think is a little bit easier to implement so maybe let's do that first actually so in our src let's do a new folder for leaderboard and in here let me just create a leaderboard.tsx and by the way with react there's a lot of snippet extensions you can use i use this one that allows me to pretty much do something like this real quick right just i just type in three letters hit tab and it gives me this very nice this is gonna be our leaderboard component let's go ahead and actually just import that in here right now so we can see in an action and make sure to import that from leaderboard leaderboard and we should start to see that at the bottom over here so what are we trying to build here so going back into the team trees website again i hope that the tmc's website is probably going to be similar otherwise this tutorial is going to be kind of unrelated but you'll notice that they have this list let me expand this they have this list where pretty much it displays the team name uh the display name of the person the message that they sent and then the uh number of trees or in our case for team cs it will be i guess the number of pounds and then they have a formatted uh date string which is going to be our created ad from the api so pretty much what we need to do is pull in that data and then display it in a list like this so let's take a look at how we're going to do that all right so let's start with let's bring in some chocolate components in here let's bring on the box a box is really just a div that allows you to define all sorts of css props so for example um you know maybe we want this to be width of a hundred percent right so it really is just a div but it allows you to use those really nice utility props and then let's probably bring in heading which is also from chakra ui and let's put in this leaderboard in here should probably do caps um and there's two ways to make things in caps really you can just type it out in caps or you can use i believe in css you can do text transform uppercase that'll be uh similar and then we need to sort of start designing a component that represents each of these cards right so let's maybe uh say that that's going to be called a leaderboard item so let's create a new component in our leaderboard folder let's call this leaderboard item.tsx and again let's initialize this to a component and we know that this leaderboard item pretty much represents a donation right so we probably need to have the types for that because it's going to come in as a prop so what i'm going to do is i'm going to add a types.t.s file and that's just going to have an interface that represents the the shape of our donations right coming from our api and what that's what we're going to use to provide the shape for some of our props like for example we want donation in here so let's bring in that type donation from types right and then we can destructure that here so you know perhaps let's start with donation.display name just so we know this is working and then let's go ahead and actually import that in here just so we can start developing it so let's do a leaderboard item and then let's pass in a mock donation this is a good way to design components i think is you just pass in some static data and then you can wire it up later right so if we pass in a mock object donation in here that has display name of and because this is expecting a more real object we also need to provide count and created at right and those are really the minimum fields that we need to display something uh technically there's also email right but that's not necessarily something that the api might expose that's why that's not in here so let's go back to our leaderboard item that's displaying the display name and we added that to our leaderboard we just a mock donation so we see mr beast right here right so our goal is to make this look something similar to the cards that we have here right it's gonna not gonna be a pixel perfect replication but we're gonna try to get close to that all right so how do we get there first uh let's probably wrap this into some kind of card right that has a little bit of shadow so similar to what they have here and what i'm going to do for that is i'm going to bring in because i know we're going to u using flex to kind of lay things out i'm going to do flex in here from chakra layout and flex is really equivalent to box except it has display flex already on it so it's just uh you know it's because you'll probably use that a lot in a lot of places they just have a defined specific flexbox component here's where you can start to really see the uh how amazing some of these utility props are are so for example we want some back shadow and remember there's different sort of sizes in there and there is some really nice autocomplete especially because we're using typescript so let's do box shadow medium and you can play around with that that's going to define sort of how strong the the box shadow is so you'll see there's now a little bit of a shadow on this on this card let's add a little bit of padding you can do uh just p there for short or you can type out the full word padding let's do three and then let's make sure this is a white i remember our app background is like a little bit of an off-white color so let's make sure this one is white and then put a little bit of border radius in here again nice uh props that you can use let's do maybe lg and this is not obviously exactly you can kind of just play around with it and see what works for your app or maybe you want to make it a little bit closer you can definitely do that so let's let's expand this and see how it looks so now we got sort of a you know a rounded box with a shadow but i don't want it to expand to the full width like that i wanted to do sort of similar to this where it kind of centers in but the component itself i still want it to expand the full width so what i'm going to do is maybe i'll add i'll add a max width here of xl um but again i still want the width to be a hundred percent right so the component itself is gonna take up all the space but it's gonna limit the uh the card to just be a certain size and then back in the leaderboard actually it might be good for us to display multiple of these items just so we can kind of see how it lays itself out right so if i add three in here we start to see that it looks like this which is kind of ugly right with spacing and remember one of the things that we can bring in for spacing is a stack so let me bring in a v stack which is the vertical stack we're going to wrap this list with a v stack and you can define how much spacing you want there and we're going to do spacing spacing 4 and let's see how that looks now you have this nice spacing and then notice that it also kind of centered our items nicely because that's kind of what we're going after here all right and then typically they have these sort of uh circle pictures in here which really could be any picture right but chakra actually has an avatar component that is really specifically for avatars but you can really use it for any picture that is rounded like this so in our leaderboard item we can bring in an avatar from chakra ui avatar size let's do lg and if you need to customize the picture for that later you can we're just going to use whatever the default is which is like this generic user picture thing all right but now we need to start uh presenting the other data so we have a couple things in here and in fact actually let me prep some of these in a text should come in from chakra ui right so we have the name we also need to display uh two other things in here right so we need to display at the top we actually want the team name at the bottom we want the message and we should let's add some extra mock data in here right so let's do team team message some message right so right now they're kind of uh all in the same line we'll fix that in a little bit so what we probably want to do is put that into a flex layout with a column so let's do uh flex direction column let's put these items in here right so it looks like this still not quite right let's do text align text align left and i think this is because some of the default styles that our chakra template brought in is a center aligned text i think which works for a lot of the things in the page right but for this not so much all right and then let's start styling some of these a little bit right so there is different styling based on the text as you can see here so the team let's make sure that that's bolded so let's do font weight bold and let's add some color let's do perhaps blue 500 make the size a little bit smaller from the rest of the text so let's do small here if you notice from the team trees website it's usually in capitals so let's do text transform uppercase and now we have this team right that's starting to look a lot like this but you know just we're using blue because of team c's brand and then similarly i think let's bring in font weight bold as well for the display name and then we'll make the message also small and right and i think you'll probably start to see some of the benefits of chakra right again it's making you not worry about specific pixels and sizing and stuff it's working off of a scale you just need to remember the different values of that scale like small medium large xl 2xl stuff like that and then you can just kind of play around with it and see uh you know what looks good for for your design all right so there's another sort of element to this card right is this right hand side of the the count as well as the uh the date so let's also bring that in so what we're going to do is we're going to add in another flex component here this one's also going to be a column so it's kind of similar to the above layout and let's add a little bit of spacing justify content space around right and what we're going to have in here uh we're going to have the count and chakra there's actually a badge component that is perfect for this let's take a look at what that looks like so we'll do badge from chakra and we're going to display the donation count in here as you can see on the right it has you know this look uh we'll we'll play around with the look and feel of that in a little bit the count actually i think we need to display pounds as the unit of measurement and then we also need to add the donation created at and we need to format this later so right now it's kind of ugly because it's just the actual iso string but let's make the font size really small on this one and then something there's a problem here you notice that it kind of gets squished right ideally it's it's sort of spread apart we know that there's space between those two things right if you look at this layout there's space between the left hand side and the right hand side so that's something that we can do with flex so if we wrap these two flex containers with another flex right so this will be a flex container that's i a row but we can say that we want to justify the content in here to have space between so let's move these two flex containers in there and see how that looks and then i'm actually just missing one more thing in here which is because the parent itself is a box a flexbox we need to make sure that this underlying container is you know eating up the the entire space so let's do a flex one and let's add a little bit of space so that it's kind of pushed a little bit away from the avatar so let's add a margin left of let's try four and now we have this right so it's starting to lay out really nicely it's pushed a little bit away from the avatar and then there's space between those two core components we need to style this badge to be a little bit better closer to what we have here which is like a rounded badge and then with a little bit of color and then we actually don't want that uppercase text transform so we're going to remove that so in our badge here let's do uh you can change the color scheme to be let's try uh green maybe we should do blue i don't know um border radius to make it rounded we're going to do border radius full which is pretty much you know as round as you can possibly get that effectively turns into like a circle right but with the text it kind of expands it out and then we're going to check change the text transform to be lowercase and let's add a little bit of padding so remember we can do p for padding for short but you can also uh have individual padding for like the the x and y right so padding top and padding bottom and then potting left and pining right so if you're trying to change top and bottom specifically you can do py so we can do py1 um because you know we want to have a little bit of space at the top and the bottom but we have we want to have a little bit more space on the left and the right so we're going to do px 3 to apply more space more padding on the left and right all right so it starts to look like that you can also change what the underlying element is here you can do let's do as div i think by default it's a span and then i want to make sure that everything in this box is text align right and then finally we want to have a little bit of formatting on that number right so an easy way to add that with numbers is to just do two local string all right so now right now we have something like that uh we need to fix whatever is happening it's happening with the spacing here uh but first let's uh maybe try to format the date now there's a couple of different ways you can format dates but one of the ones i would recommend using is date functions so we're going to go ahead and install date functions which is just it offers a lot of nice utility functions for you to format your dates and do all sorts of you know whatever operations you need to do for uh for dates and maybe let's create a new utils folder where we'll put in our string formatters and in here i just added a format date which takes format from date functions and then you can pass in different sort of symbols in here to define what kind of formatting you want to use in this case the ppp will result in basically what we're looking for which is uh well let's bring it in so you can see so let's do wrap this in a format date and import that from our utils and now our date looks like that which is like the the slash date and then uh the time on it all right so why don't we just change this a little bit more uh maybe let's do badges here and then i'm guessing that spacing issue will be fixed if i wrap this in a div perhaps because the flex is forcing it to eat up all the space so there you go simple fix to the the spacing problem and then we have the uh the blue and notice we still have a little bit of spacing problem here because uh in the scenarios where we don't have a team or a message like on the bottom here the spacing kind of gets whack on on the right side here and ideally this is kind of centered let's see if let's bring in a box in here and let's move this up there all right so that simple separation of containers i think just kind of improved it a little bit so i just moved the the flex one and the margin left to a higher parent div and then that pretty much fixes i think all of our layout problems right so we got it centered over here some nice spacing between the avatar nice spacing between the text when uh we have either one two or three items in there and then uh consistent spacing on the right so i think we're pretty much good there so i think at this point it'll be good for us to start bringing in some real data from our api right so because we're still using the static code here all right so what we're going to do in here is basically we're going to do another query right and remember from our api we built a donations query that allows us to define the the ordering so we're eventually going to get to this feature where we can order by most recent or most trees or most pounds in our case so we're going to create a query that defines that so similar to the other file we're going to bring in a query string in here and by the way this is something that you can pretty much copy and paste from the the query that's generated from apollo sandbox that's also why i like using that one because i can kind of just click around uh generate my query and then i can just copy it right into my client all right and then what we're going to do is as you know because we're probably going to try and flip that ordering we need to bring in some state in here so originally we're going to do let's do a state for the field and then and a way to change that we're just going to use some simple use state from react which initializes to create it at because by default we want to show the most recent donation at the top we don't really need to change the direction uh because i guess in our use case it's always going to be descending and then i'm also going to define a simple type here which is just going to which is just going to define the sort of the shape that we're going to get back from our query right it's our query is going to return back an object that has donations in it right donations and then what is that it's a an array of this donation type that we have so where that's going to come into play is we're going to add a another use query just like before except this time we're going to pass in our donations query here and make sure to import that from oracle this is how you kind of kind of define what the shape of the response data is going to be so that's why we actually define this type and then notice that we're passing the query and then this is how you pass in variables right so you just pass in an object and that's ultimately what's going to be fed into the variable the input here for the query itself so it's just going to forward it onto the server and in a little bit we're going to use the set order by field here to toggle between the two ordering that we want and similarly as before it's usually good to add you know some handling for when it's fetching or when there's an error we're just going to return a simple string for now you can improve this later if you'd like and finally what we're going to do is we don't want these statically defined anymore in here what we want to do is from the data and this is where the typing comes in when i do dot it knows that it has a type of donations right and i can map that to render list so for each donation and because it knows it's an array of donations we should be able to move this leaderboard item in here right and this should be a compatible donation type let's take a look at our ui and now we have our previous donations that we were making in the sandbox directly but now it's rendered in the ui quite nicely so let's try to get closer to this leaderboard on a team tree site uh let's add this header and then eventually let's add in a some kind of picker to switch between the two orderings here so that should be easy with the header let's just add a uh h1 in here with size 2xl that says leaderboard and then in the middle in here we can just use you know you can use anything really with chakra you can use a switch right you can use some kind of toggle thing like this or i think might be a little bit better is some kind of radio this might be a little bit simpler so we'll probably take uh basically this example radio group we'll copy that and then we'll just turn it into a simple picker between the two orderings that we have right so if i just add in a radio group in here import that from chakra and then we're going to utilize a stack and radios right so that's exactly like what they had in the example earlier and then for the radios we just have the two values so created at which is already what we're initializing to with our state and the other one is order by count so most pounds in this case is a display for that and then on change we just kind of flip flop the the value string between count and create it at and then the value of the the actual radio group it's just whatever the current selection is which again initializes to create it at so let's take a look at how this works in action in our ui now we have this radio so it doesn't look exactly like this really fancy toggler here you can try to animate that if you want i don't really think it's worth the time right now i'm more focused really on showing you the underlying functionality of how the ui and the api sort of talks together with the data how do you change your variables right so now we should be able to switch between most recent and most pounds which unfortunately we don't have enough good data here so let's do let's add another mutation here with a much larger number so let's do something really big let's make that mambo 3. and let's run that mutation and then let's actually put something really small uh so that we can see that the most recent is the smaller one right so the 10 so let's add a mutation for that both of them are going to be mumbo 3. so back in the the ui you see the most recent is the 10 pounds the small number with mumbo 3 and then when we do most pounds it's gonna do that first number that bigger number and then we got a bunch of thousands in here so it's hard to see so maybe let's do a 500 just so you can kind of see that this is really ordering properly and maybe let's do another i don't know 800. so if we refresh and we do most recent we'll see the 800 in here and then the the 500 the 10 right and if i do most pounds it's gonna be descending in size so we've got the one million one thousand one thousand eventually that's gonna go to our our 800 500 all the way down to our 10 pounds five pounds right so it's descending in uh pounds so i think that pretty much wraps up our leaderboard feature uh again it's not like the most prettier thing you can play around with the avatar with actual pictures and absolute positioning if you want it to look exactly like this in team trees again i'm not too concerned about it because uh maybe the tmc's website will be a little bit different perhaps you can go to teamsies.org check it out see how it actually looks and then improve what we currently have to look exactly like what we have what they have all right the only thing really missing here is for us to be able to call our mutation to actually provide a donation from the ui itself so that's the next thing that we're going to build all right so remember in our app.tsx here we had this sort of placeholder donation wizard that's where we're going to put the the actual sort of multi-page thing so let's go ahead and create a new component let's call it donation wizard let's initialize to something like this and let's replace our placeholder and app.tsx to be that actual donation wizard right so you should see this wizard text in the middle here that's where our thing is going to end up all right so let's talk about what's the responsibility of this donation wizard it's going to have a couple things in it one is that it needs to maintain the state of the different pages right so as we're clicking next here and previous it needs to maintain that state of what page you're currently on in our stepper basically and then it also needs to be able to render the specific component that's meant for that page and then finally uh it's probably also a good place to put our create donation mutation so that as you get to the last page it's gonna send that donation as a mutation and then it's gonna present some kind of hey thanks for donating message all right so first let's maybe build our our stepper a little bit here so let's add a use date let's add a use date here for what the step is we'll start with zero and then we you know we can just increment decrement that so we might have a method called next simply sets this step to the current step plus one right and then similarly we'll do previous which is just going to do step -1 and then we might want to define what our pages are going to be let's just do a basic array here and then let's put in some mock pages for now right and then we also want to make our wizard be in a in a box and kind of similar to our other you know layout and designs we let's add a little bit of box shadow to xl let's add some padding in here and i also want to make sure this is a white background a little bit of a rounded corner let's give it a minimum width and then within here we're going to render our current page right so we're going to do pages and then whatever our current step is all right so that should render whatever page we're currently on and then let's render some buttons to allow us to paginate i'm just going to add some code in here but i should be to save you some time really it's just two buttons one that says next it's gonna have a orange color and it's gonna be large and rounded and then the second button is just the previous and that's still a button but it has a more simpler design to it like you can see here in the team trees when you go to the next page right you have this nice rounded big orange button and then the previous is just like a simpler text button so that's exactly what we're doing over here i kind of just played around with uh the css props to get something fairly similar to that so let's take a look so right now it looks like this right kind of ugly we'll play around with it later but pretty much you should be able to hit next previous next previous right and we only have those two pages all right so what we need to do at this point is we need to create a component that's very similar to this number picker and pay attention to what the behavior looks like so notice that you can basically select these predefined choices but then you can also put in a custom amount and when you put a custom amount in it deselects any of the predefined choices so we want to emulate a similar behavior so what we're going to do is let's create a new file let's call this count selection dot tsx and let's also initialize that and that's basically going to be what we're going to use as page one right so let's replace the page one with count selection all right so let's start implementing this so there's a couple different ways that you can pull this off if you really think about it these look like buttons but knowing that this is a form you can probably assume that really this is actually a radio right and if you look around in chakra back to the radio page there is a way for us to basically define custom looking radio so here's a really good example of custom radio buttons in the documentation right which looks like buttons but really their radios behind the scenes so we can pretty much copy this and use that as a base and then we can just play around with the styles of it so notice that they create a radio card component and then uh these things in here so we're going to create our own radio card so i'll add that in the donations folder you'll see a radio card in here so this is pretty much the same exact code in that documentation so you can just go there if you need to copy it or feel free to you know pause the screen and copy as needed um it really is mostly the same thing i just changed uh the colors to be blue watercolor to be two um just so to go to go with the blue theme of team c's and this is really that's a good strategy that you can use when you're trying to build something quickly is just look for an example in some of these component libraries that's close to what you're already trying to achieve and then just play around with the props to achieve the actual look and feel that you want so back in count selection we need to provide a set of options right for the different number of predefined pounds that the user can select we'll just define a simple options array here with 520 50 and 100 and what we're going to do is we're just going to take those options and basically render a radio card for each one of them right so for like five we're going to render five pounds as an option and then this get radio props will come from if you take a look if we go back to the documentation real quick you'll notice that when they create that radio card to actually use the uh the radio in a radio group you know here's an example they pull in use radio group and that's where you can define the current value as well as the on change behavior and then you can just rent exactly what we're doing right we're doing we're mapping through the options and rendering that through the get radio props so we'll do something similar we need to bring in uh use radio group and we need to pull out get root props and get radio props from that and this is where we can configure some of the behavior so let's give this a name of pounds and then we need to have value right so there's something there's a value that there's a state that we need to control so we if we bring in some use date i wish will call this pounds and set pounds and then perhaps let's initialize this to be 20 so that it initially selects uh the 20 as the current option so the value of our radio group is going to be that selection next on change we're going to allow to change the selection right so let's do next value and then we're simply going to call our set pounds to be uh and because the value comes in as a string because it's on an input we actually need to parse that in so parse in next value and that's gonna update the pounds to be whatever number was selected and then let's we need to create a group to pull out the get root props and then really you need to pass in the group props to whatever the wrapping group of those radios are at this point let's take a look at what our app looks like so now it just looks like this right and we should be able to kind of select the predefined pounds now this doesn't look quite as good as what they have over here and team trees we're close to that so ideally we kind of are able to render this in some kind of grid now in chakra there's several ways that you can lay things out right you can use flexbox you can use css grid um you can use the the different stacking and stuff that they have in here but they do have a simple grid which is great for just very basic use cases like ours right you just want to have a basic four four by four grid two by two grid right so we're gonna bring in simple grid from chakra ui react and we wanna say that this is a two column grid with a spacing of two let's add a little bit of margin top now our application our picker looks like this right so that's significantly closer to what we want um we still want a little bit of spacing between you know these and the uh the buttons here uh we'll fix that as we go and then in we also need to allow for an input for a custom amount right so let's probably do that next so in chakra it does there is a number input in here and you'll notice that it's breaking up by a couple different components depending on what you want to show so for example you know these incrementers stepper things is its own component that you can choose to add or not uh in our case we're probably just going to render just the field itself just to to keep it simple i don't think we really need the stepper here right so let's introduce a a greater parent here so that we can start adding more things let's do another v stack just to keep things simple with let's do spacing of four where the stretch align make sure you import v-stack and we're gonna move our simple grid in here all right and then under the simple grid we can start to bring in our number input and within here we just want like we say we want the number input field with a placeholder right so those are a number of fields coming from chakra ui react so there needs to be some custom state as well for this right to track what the custom amount is sort of separate from this pound speaker that we already have so what we're going to do is just a basic use state again except what we're going to do is we're going to initialize that to be empty if it's any of the predefined options right so if they select uh if they use the button the radio buttons we're gonna empty this out but if it if not we're gonna set the whatever the person is typing in right and you'll see this in action in a little bit and also on change here when we select any of the the radio buttons we want to empty that the input out so we're going to do a custom amount empty string if they select any of the predefined options so we've got a lot of bit of configuration in our input here so similarly like the inverse if if they start focusing on this input remember we have this behavior from team trees where if there's a radio selection when they focus into this input we want to deselect that and the way we're going to achieve that is by setting the the number of pounds selection to be zero so that it doesn't activate any of the radios so the way we can achieve that is on focus of this input we're going to do set pounds 0 right and then we need to track what the user is typing in so we're going to do on change we still need to actually define what the pounds ultimately will be as they type it right so we're going to do set pounds again parse int value because that's coming in as a string and then we also need to update our custom amount state set custom amount to be that value to actually update the state of the input itself right so you can kind of think about it as we really have two states right which is the custom mount and the actual pounds that we really ultimately want to send to the server but we kind of have to do this sort of split state to get that same behavior that we have from team trees right and then there's one more thing we need to actually make sure that the value of this is the current custom amount so in our application now we have this other amount input box and when i click into it it should focus trigger focus it's going to send that set the pounds to zero and then as i'm typing in a number into it it's going to set the pounds to that new number right so we're really just syncing up the state but we're allowing this sort of dynamic behavior where oh if i click a radio option it's going to empty out the custom amount if i click on the custom amount it deselects the radio option right and i know that's probably pretty confusing but uh we're just trying to eliminate emulate the behavior of what they have in team trees all right so there's one other change we're gonna add in here to wrap this up uh let's add in and a horizontal line in here just a basic one and then we actually want to bring in the next button in here and this is not going to make sense right away because it is kind of nice to have it just be you know outside of the page right but uh once we get to the second page you'll notice that the the button the next button there is actually going to be a submit button which means that button needs to be in the page for it to trigger a submit so that's why we're bringing the buttons in to our pages so we're gonna bring in a that orange button that we created in here which means we need to provide a next step uh prop so we'll just define that method here as next step right and this needs to call in the next method that we have from the parent which means we need to pass that in right so we'll do this needs to be a function and we can just structure that out of props like so right and then as we click next our donation wizard needs to keep track of what did the user even select right so the state shouldn't the pounds shouldn't just live in this component we need a way to be able to communicate what was the final selection of donations that they want to provide so we can actually just pass that up via the next method so if we say that you know this takes in values let's just type this to be any right now we should be able to pass in a count in here and that's going to be the pounds value that we're keeping track of so that means back in our donation wizard let's get rid of the buttons here and then count selection is now going to require a next method we're going to provide next there and now we know that there's some kind of value that's being passed into our our next method right it's now just not just an empty uh so let's do values and initialize this one empty object because we don't yet know really what's going to be in there but we know that at at one point uh that's going to spit out the count and then at another page it's going to spit out the donation details right so we need to start adding in some state that keeps track of basically all of the values across the the pages so page one has the count page two has donation details we need to ultimately merge those two states into one so that we can actually send that as a mutation so what we're going to do is we're going to add another use date which is just the donation details this will be the merge state that we said we're keeping track of and we're going to initialize the account here to be 20. so since this state is going to have the initial count already in it we actually just want to pass that in via prop to our account selection so let's do initial count equals donation donationdetails.com so make sure to update the type of count selection to include that number and we can add that to our destructure here and then make sure to initialize the state to that value instead of the hard-coded 20. this is also going to allow us to maintain what the current selection is as you change the pages right so if you for example typed in or selected 50 and then it goes to the next page if you hit previous the state that is top level and the parent is keeping track of that number which is 50 meaning it's going to set the initial count to still be 50 that way as you paginate the count selection doesn't get erased right it always stays to whatever that current selection is and you'll see this in action in just a little bit so what we want to do here is that on next we want to sync up the values that's passed in to our donation details so if they change the account for example we need to sync it up so the way we're going to sync up or merge the data is basically basically we're just going to spread the values in here right and then also spread in the uh the previous donation details right so the combined of those two things is going to be the merged state so in here when you add a step we're also going to do set donation details to be the merge details so let's take a look at how this behaves in action right so you should be able to well we don't have a way to go back if we had a simple button in here right let's just add a simple go back button on page two so we can test this in an action you should be able to put in a custom amount hit next and we should be able to go back and that state is still maintained right so pay attention to what happens if you know if we didn't have the initial count being utilized right so if we brought back the initial count to always be 20 like we had before right so initializes to 20 if we put in a custom number in here hit next and then go back we just lost that state even though we're kind of merging it so that's why you need to maintain it at the parent and then actually feed it back into the child so that the the state is always synced up to what the selection was as the user changes their their current page right right so our number picker or pretty much looks pretty good uh let's just add a little bit of text to make it similar to you know what they have here with the join team trees uh in our case we're gonna do you know let's just add to our stack in here so i just added some heading as an h3 that says join tmcs and then some basic text saying a dollar removes a pound of trash our little picker looks like this right a little bit closer to what we have in this other app you know feel free to play around with the sizes colors whatever you want but i think that pretty much wraps up our our page one here let's move on to page two which is the donation details which is where we do the form which is a little bit more complex all right so in donation let's create a donation details that tsx now here is where we're going to add back the buttons that we had previously right if you remember we had the um the orange button and the previous so we need to make sure to pass in the previous and then the button here is actually going to be a submit uh so we need to make sure to actually call next on submit right so similarly that means that our props needs to be passing in those two things next and previous which similarly we're gonna destructure out of props all right so let's quickly review what we're ultimately trying to build towards right so in the next page of here of the details the details page we've got basically a couple form fields and then the ability to ultimately submit right in our case there's no set next step we're just going to submit right after that so that's what we need to emulate we need a way to maintain our form state and we need to a way to render you know uh input fields and labels now what i personally one of the recommendations i would make for maintaining form state that i've had good experience with is formic it does a lot of the the maintenance for you in terms of what is the current value of the input field is there an error and as well as how to do validation and it works really well with yup which is uh one of the ways you can define your validation schema so we're going to see this in action soon now in chakra we also want to bring in some kind of input right so if we look at the import component uh there already is a pretty nice input component here that we can use and they also have actually a better example might be if we go into the form uh control documentation you know there's a they have some examples here where notice they have an input field in here and they're using form control to you know provide whether or not it's valid or not so as an example if i empty this out and remove focus it's going to highlight in red and then it's going to present the uh the error message in here which is exactly what we need right it's basically this so we can pretty much utilize that and then i just add a little bit of styling to make it look similar to what we want so and they also also talk about how would you use this in formic right which is which is what we're going to bring in in a second and you can see that they show that oh you have to render a form control with a form label the the control the input and then the form message for each field right so that would be kind of painful so how many lines of code is just like one two three four five right that would be kind of painful if we had five lines of code for every single one of these fields so what we're actually gonna do is create a reusable formic field that is basically going to have these things in there so let's get right into that so what we're going to do so what we're going to do is install formic and then also install yup because we're going to use that invalidation shortly and then let's create a new folder for form and this is where we're going to use our reusable uh input fields so in here i'm going to add a uh input field which is really what we just saw in the chakra ui uh documentation right it has the form control the form label and then the input the message but what we're going to do here is we're going to use use field from formic to wire up the state right what is the current state of this field what's the current value is it does it have an error so we parse out if it has an error using this boolean we just pull it out of the meta of that field and then that's going to feed it into the form control to figure out what's the current state of this form control now i know that might be a little bit confusing feel free to pause the screen too if you need to type this out but once we have this in action you'll see why this is so powerful i'm also going to add one for text area field which is it's effectively the same exact component except we're going to use text area here for our messages later instead of an input or actually so let's go back to our donation details and let's bring in formic so we're actually going to have formic wrap the entire component here so we're going to do formic and the way formic works is it uses what's the term for the children as props api so it looks something like this where it's actually has a function and what that so it feeds in the props through that function and then the return of that should be what you're trying to render so we're going to bring in form from formic as well which basically just wraps our input fields and wires up the the state and stuff and then let's move our buttons inside there because again this is why we moved the buttons in the first place right because we need to be able to trigger a submit button so this submit button needs to be inside the form now formic needs to have a couple uh props in here you need to define what the initial values are so in our case it's really just the donation details all sort of initialized as empty strings and then finally you need to provide a submit function which we haven't defined yet so let's go ahead and do that and this is basically going to get back the values of your form submit so that's going to have our details basically and then remember we're trying to pass that information back up to the parent so we're just going to forward it up to the parent via next all right so the only thing missing in here now is our form field so this is where some of the magic is going to start coming into play so if we add in input fields and we do label equals display name you need to provide a name that matches the input field what key does this really fill in in the state which is in this case this value right this plane name so we're gonna give it a name of display name and also a placeholder that's just similar to the label right and let's see how that looks um go back to the donation wizard we're going to change the page 2 in here to be donation details and remember this is going to need the next function and the previous so back in our ui we now have an input field with the previous button right and the submit and you know that's maintaining the state so let's add in our other input fields all right so i just added in the other fields that we want to save you sometimes you don't have to watch me typing it out so we got the email address we've got the mobile phone we got the team and then finally the text area for the message i also added the hr here and then i guess we don't really need this wrapping div around the buttons anymore i do want to improve the spacing between these input fields so we're going to add another v stack with a spacing of 4 and make sure to wrap everything with that with all those in place how's our ui look now we have something like this right we got display name email address small phone team message submit and then i guess if you want you can also add in the heading in here let's change this to details right so it looks like that pretty close to what we have here not exactly but close enough but we do need to do something about you know our we need to be able to validate right we shouldn't be able to just submit details in here without validating it so that's where yup comes in if we do import yup star as yup now what yup is is basically it allows us to define a schema so let's type that in here again these are things you can maybe move to a different file if you'd like i'm just doing it all here for simplicity so let's define a detailed schema yup uh so that's an object which is really we're trying to define the validation for the object that formic is maintaining so it has the same exact shape as this initial values so we can define the shape using shape and then this is where you start to put in your keys so we have display name for example which is a string and it's required and you can pass in a message in here to say you know please enter a display name if they don't have it right and then one more let's do email so there's a couple other things you can do with uh yup so for example you know email is one of them right so we're actually we did the validation on the back end as well but it's always good to also have some validation on the front end so you can also pass in a custom in here let's do please enter a valid email and then this is also required so let's do please enter an email and then the rest of the fields we're just going to add them as nullable strings right pretty simple not really much to to do there so we're going to take this detail schema and you can actually provide that into formic as a validation schema it has direct support for yup which is really nice right and what this is going to do if we go back to our application remember that the submit button triggers a our form submit right so when i hit submit here it's gonna highlight things in red because we didn't fill it out as they're typing it it updates it and then you know we have the double rules on the email right it's required and it also needs to be valid so if i type in a non-valid email it's gonna say please enter a bad email and then as i correct that becomes good and again i kind of breezed through it but the a lot of the magic happens in these input fields is that it kind of allows you to auto register a new field with a given name and you'll see that formic kind of maintains what what is the current value of that input through its own sort of internal state management and then it also figures out was this field touched before and is there an error and that's got that's how it determines if there's an error right so for example for example on a fresh load if you go back to the page 2 if i click into this and click out it's gonna consider that as an error because we we touched it it's marked as touched and it shows that you have an error there because you didn't fill it out it has an empty value so the validation ran on that and it's all just kind of magically wired up it's really cool and then i did apply some custom css in here so text transform upper case and stuff like that to achieve a very similar label look to what we have in team trees all right so we're almost in the finish line here guys uh what's left is that we just need to trigger our mutation and then we'll be done right so on submit in our donation details remember the on submit we're just forwarding what the uh formic values are up back to the parent which is donation wizard so what we want to do is if we're in the last page we want to trigger our graphql mutation otherwise we want to do this stuff right so what we're going to do is add in a little bit of logic here to say if the step is the last page which is page's length minus one right because we're starting with zero in here let's just do a console log but in here is where we're gonna submit uh merge details else you're gonna do a next page right in that case there's really only two pages so maybe this is a little bit overkill but i think you get the point all right so how do we define the you know maybe let's just name this now to be submit donation how do we trigger that submit right so uh similar to what we've been doing before with oracle where you you've seen queries you've seen uh subscriptions you can also do mutations so what we're gonna do is we're gonna bring in another uh string for the mutation we're just going to call that create donation and notice we're just passing in the variables as great donation and the way we're going to be able to utilize this is by bringing in i'm going to do use mutation from oracle and pass in our query through that great donation like this and that brings up that returns uh the result so let's call this donation result and create donation now we could just use this great donation over here if we wanted to but i'm actually gonna define the submit donation to into a specific method because we have some logic that we also want to trigger after the donation happens so this will be an async function takes in the values and this is where we're going to call create donation which actually returns a a promise i believe that's why we're able to to await it and then we'll pass in our variables in here which is create donation input right so it's this thing where that we're passing into the query in here so this needs to be the same exact name as you have down here right and what is that that's just the values that we are passing in through submit the donation which is our merge details right so ultimately what this values is going to be is the the count and the form details put together in one state right so that's exactly what we have for example in our sandbox it's going to look like this it's create donation input with at minimum the display name email and account okay so and so let's before we test this out why did we put it into a method like this that's because after the donation we want to present uh some kind of confirmation right so let's add in some state for when we can toggle showing that confirmation so let's add another use date up here of simply show confirmation initialize to false once we've created the donation we're just gonna do flip that to true so then we can update our render logic here of if uh show configuration is true just show some kind of message like in our case here we'll just do hey thank you for uh donation and this is where you see we're utilizing that donation result from the mutation we're just gonna show how much the person donated so that's coming back from the response of the mutation here all right so i think at this point if we go back to our ui we should be able to fill in a donation and that should update the count and then actually you'll see that it's also going to update the leaderboard but let's take a look let's do donate a thousand pounds fake email do team c's and let's do let's go hit submit and there you go look the leaderboard updated automatically and i'll talk about how that happened in a second here and then also our number went up and then we got our message of thank you for your donation so how did that work right so let's think through the details here so first of all our donation uh when we sent it right it goes into submit donation it sends in the data it goes into our submit here it awaits and then it sets the confirmation to true that allows us to show the message of hey thank you for their your donation and then because we have a subscription this number got updated automatically and then the leaderboard itself automatically did a refresh in the background and why is that if you take a look at the documentation in erco the default caching that they have is document caching and you definitely can use a more sophisticated uh normalized cache i think if you want but the way it works is by default is it it tries to figure out which queries are related right so it uses the the type name property of our query so when we do a get donations that has the type name of donation and similarly when we perform the mutation since we're creating new donations that also has a type name of mutation so like in their example here the two queries here have the same type name of book and when the mutation runs it kind of automatically knows that oh the the query for the book needs to get updated that's the same thing that's happening for us when we do a mutation on donations oracle automatically knows that oh the the query for our donations list probably needs to be refreshed so it's going to automatically do that on the fly so we didn't even have to do anything to refresh that data so that's pretty cool that as a user when you put your donation in the number are here updates because there were subscription and then the leaderboard updates with the most recent donation of whatever you put in right and just as a final test let's see if we can get make sure our subscriptions running right so on the left here i have another set of the the client i have two instances of the client if i donate uh 1 million on the left side here i expect the right side to automatically update to 2 million and let's hit submit and there you go automatically updated on the right with 2 million the leaderboard is also updated so uh right if you're mr beast you'd probably want to see something like that where it updates automatically for you and that's it i think this wraps up our our clone again it's it's not the prettiest clone uh you definitely can do better i think uh you know take if you're following along you know definitely play around with the styles a little bit when the team sees opens up in 21 minutes here right play around with the styles and see if you can match it even closer or if it's not maybe if it's a completely different uh ui you can try to match it up yourself but i hope that you got value from this tutorial and you know if you kind of summarize right we learned how to put together a graphql api that talks to the bit a database using prisma and then we also learned how to create a simple ui with some fairly decent looking components right with chakra that you can still further customize and then we looked at how we can manage all the state the form state validation how to make calls to our graphql api and how all that kind of dynamically updates with queries mutations and subscriptions right and so i gotta release this video soon so i'm gonna edit it put it out there i hope you enjoyed the video uh please give me some feedback in the comments hit the like button and of course subscribe and you can and again finally uh i want to remind if you would like to donate to team c's make sure to go to teamseed.org and let's get this movement up to 30 million with all that said guys thanks for watching and i'll see you on the next one [Music]
Info
Channel: Marius Espejo
Views: 4,561
Rating: undefined out of 5
Keywords: prisma, prisma nestjs, prisma postgres, prisma nodejs, prisma typescript, typeorm, nestjs typeorm, typescript, nestjs tutorial, nestjs graphql, nestjs microservices, nestjs, node, nestjs database, prisma tutorial, prisma nestjs tutorial, prisma migrations, graphql, graphql tutorial, graphql react, reactjs graphql, graphql urql, react js, reactjs nodejs, formik react, react urql, graphql subscriptions, graphql mutations, graphql tutorial react, chakra ui, node js tutorial
Id: lddaR8Y-gko
Channel Id: undefined
Length: 161min 25sec (9685 seconds)
Published: Sat Oct 30 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.