Build an Online Auction App with Notifications (Next.js, Shadcn, Typescript, Drizzle ORM)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
so I have a really cool full stack tutorial where I'm going to be showing you how to build out this auction site called bid buddy.com using nextjs with server actions and I'm going to be using drizzle orm to connect to a postest database that's running locally with Docker and Docker compose then I'm also using knock. apppp for notifications so when someone out bids you on this platform you can go and quickly out bid them as well so let's just do a quick overview of what we're going to build and then I'm going to walk you through how I built everything so right now I'm logged in as a user and I'm currently looking at an auction right and so I own this auction I can't actually bid on these shoes but other people can come in and start placing bids so let's just create a brand new auction to kind of demo and I'm going to go ahead and say um climbing shoes is what we're going to sell and I want to sell them for $85 you know they're mildly used a little bit moldy a little bit stinky but they're still good let's just go ahead and find a picture of those climbing shoes right I got some Las pivas and I'm going to go ahead and Mark this to end on the 6 so when this bid is created other people in the system can come on and they can start placing bids on it right so again I created this one so I can't go on and bid on this but if I were to log out I can go ahead and log into someone else and bid on this so let's do that I'm going to log in as someone else and by the way this is using next o in it's all set up with Google authentication let's log in as another account that I own let's go to the place bid button for the shoes and here you'll see there's a climbing Sho the current bid is at 85 the starting price is at 85 and the bid interval is $1 so if I were to place a bid that is going to bid at for $86 and now I have the highest bid on this item okay so now someone else can log in and they could also place a bid on this as well now one thing I do want to point out is that this has a bell icon and when someone else logs in and places a bid on this shoe I will get a live update when someone else did it on this and I'll also get an email notification to my own personal email saying that hey someone's out bid you on this let's sign out I'm going to sign in one more time as someone else and we'll place a bid on those shoes okay let's go back to De climbing shoes I'm going to go ahead and just place a bid and now I've out bided the last user by $1 I'm at 87 for the current bid and I want to show you if I log back out for the other user again if they're already logged into the system they will get this live update they will see this notification icon have one unread notification and then it says someone out bided you on climbing shoes by $87 if I click on this it'll take me to the item and I can just go ahead and outbid them once more so a lot of cool stuff in this tutorial obviously we have a page where you can see all the auctions that you have uploaded we don't have any auctions uploaded for this user and then we can go back and see all the auctions in the system so it's a really nice um starting tutorial I think which teaches you a lot about various things but we also have this IND date set up which again if I were to go over here to the item and if I go and change the end date for the climbing shoes to be I don't know like on the 12th so couple days ago then I go back and refresh the page notice that we have a badge that pops up that says bidding over the ability to bid on the item is gone now if I go back to all auctions you'll see that now we don't have a place bid item we have a view bid so there is a little bit of you know conditional logic that's set up to hide and show different buttons and also if you are an owner of the auction you can't bid on your own thing so that's all we're going to be building in this tutorial but before we jump into the code and start building this together I want to say that this video was made possible thanks for our sponsored KNC go check them out they have a really great notification system which allows you to have inapp notifications like I mentioned you can easily create templates where notifications will automatically send out emails to your users so this is the the email notification we got check out that auction if I click that it takes me to the item they have a really nice dashboard which allows you to create something called workflows where again you basically create a workflow you can come through here and you can create what happens when someone tries to send off an API notification from your system and in my case I have a user placed bid workflow and when I trigger that for my nextjs application that's going to go ahead and send off inapp notifications to all users who are associated with that auction and then also it uses resend to connect to a third party email service and send out emails as well there's a built-in way to basically do email templates so this is the template Builder that you saw on that email and sending out these notifications is as simple as basically writing this line of code right knock. workflow. trigger I choose what workflow I want to send out who's sending it who should receive it and then my users automatically get those notifications so really really great service definitely go check it out if you need a way to quickly add in inapp notifications or just email notifications they have Integrations with a bunch of different other services but I won't dive into all that right now probably later on in the video I'll talk more about this dashboard but with no further Ado let's just go ahead and dive in and start building out this bidding site application that I talked about hope you guys enjoy all right so the first thing I like to do in all my tutorials is I like to make a new repository so I'm logged into GitHub right now let's go over to the right and let's go ahead and move this so you can actually see what I'm doing let's click on a new repository here and I'm going to go ahead and call this bid buddy all right that is the application we're going to be building goad and make this an MIT license and it's going to be public go ahead and create it and then at this point we can go ahead and clone this repo down to my local laptop so let's go ahead and click on this clone button I am using SSH um I have an SSH key setup which I recommend doing if you're going to clone stuff but you can also use htps if you want to going over to vs code let's just go ahead and clone that repo so I'm going to go to my workspace I'll do a get clone we'll go ahead and bring in bid buddy and then I'm going to open up bid buddy and we can start setting up our next J s application so in the terminal if you want to get started with the nextjs application you can just do MPX create next app at latest and then I will just do in my current working directory like this that'll ask you a couple questions I'm just going to go ahead and say use all the defaults because I like typescript I like tailwind and uh we should be good to hit the ground running when this is done installing all right that is done installing and what we could do now is I'm going to go ahead and commit everything go ahead and add all these I'm say initial commit and by the way as you're going through this tutorial you can go back to that repo and you can follow along with my commits if you want to like get snapshots of progress over time I highly recommend when you're doing real development to do as many small incremental commits as you can all right let's just go aad and run this project and make sure that it's working in a browser so let's go to locost 3000 over here and we should be good here is our nextjs application and we are cooking so at this point we can't really build an application without having some type of database so I recommend using postgress and I like using a Docker compos file to spin up a database locally so I can do some development you're welcome to use something like super base if you want to find a free database or postest host but personally if you're doing development um I would just recommend getting Docker and Docker compose so let's make a new file called Docker compose yaml and do forgive me I'm just going to going to paste in uh snippet because it's just tedious to type this out by hand but I'm going to rename this we're going to call it bid buddy not big bid buddy database and we will use the default ports here and we will just basically spin up a postgress database so if you don't know how Docker compose Works basically you have this yaml file and this line four is the very important part this is saying go to Docker hub which is a website let's go to dockerhub and that's specifying that I want to use the postgress image so if you go ahead and search postgress you'll see that there is an image called postgress and that is exactly what this is referring to so I'm saying just give me the latest postgress image run it on this port mount it to this this volume so if you don't know how volumes work basically inside the docker image itself or the docker container there is a a file system that can mount to your own file system here so we're basically saying inside the container at data/ postgress I want you to mount to this volume I have basically created and then somewhere behind the scenes Docker Daman will figure out where to store that database information this might be way over your head you might not care but the cool thing is that now that you have this Docker compose file you can just go over here and you can say Docker compose up now again this is assuming you have Docker installed um hopefully you do if you don't just go to like Docker and install the desktop version of whatever you need and it should work okay so now you'll see that we have a database running and I like to go over here and just rename this I'll call this like database uh I put two A's there but it's fine let's just go aead and make this next and now that we have a running database we want to install some type of way to connect to that database so I'm going to go ahead and install something called drizzle om this is the or I like to use on my projects now pretty nice so let's go to or. old. team I'm going to go to documentation and they'll walk you through how to kind of get this set up we are going to be using postgress so let's just go to the postgress SQL walk through and we are going to click on postgress JS they have like different adapters for different types of datab bases but we are using postgress let's click on this and then we're going to install this go over here and run that on our bid buddy project then we'll go ahead and copy this one too and run that when this is done installing the drizzle omm I am getting some weird type of uh dependency error so I'm just going to say Force here sometimes mpm has issues and you just have to kind of like force it to install things um and then that'll probably get fixed later on let's also install drizzle kit so I'll show you what all the stuff is in a bit drizzle kit is basically a way to have a nice visual editor for your database if you don't have like your own special one like there's paage services like table plus you can use you want a nice postrest editor there's also extensions you can have in vs code code um but now that drizzle is installed what we're going to do is we're going to make a file that's going to kind of look like this but um we need to basically go over here to our source directory I going to make a folder called DB and inside that DB folder I'm going to make an index um actually I'm just going to call it database do yes so I'm just going to paste in some code um this is just boiler plate you kind of need this in nextjs because every time you save a file it basically tries to create a new connection to your database but there's a little bit of connection caching going on over here um it kind of initializes the drizzle library and it passes it a postgress uh connection so that you can start connecting and talking to your database when you import this file so this is the file that we're going to be using all throughout our nextjs application to read and write from the database and I apologize if you don't like me just copy and pasting code but honestly like come through here later and try to understand what's going on it's not too important to know all the details at this point but you will notice there's some red at the top and the reason this is Red is because I like using a package for setting up my en environment variables so I'm going to install another Library called T3 OSS EnV nextjs so let's just go ahead and install that like this and this is a way to have your environment variables be typed it's a it's a much nicer developer experience than just doing process. EnV everywhere okay so in order to get this working what we need to do is I'm going to make a env. TS file over here do Source env. TS and what we need to do is we're going to go ahead and import create EnV from at T3 and I'll go ahead and import that again this is like a package as part of the Theos Community um we're also going to bring in Zod okay so I think Zod is something we're going have to install as well let's just go ahead and install Zod here and we're going to go ahead and Export a constant called EnV and this is what we're going to use instead of doing process. EnV all throughout our codebase we're going to be using this we're going to Define some server variables we're going to Define some client variables and then we're also going to do a runtime EnV over here okay I know it's a lot of information but this will all click after we have this kind of setup so again if you go back to the database notice that we're trying to import it now but it's complaining because it doesn't know where node EnV is it doesn't know where database URL is so what the purpose of this package is is isign a server you can go ahead and Define that this needs to be a URL and then over here we can say runtime environment process at env. this actually URL I think string . URL so what we're doing is we're defining a requirement and saying that all the environment variables we need to get this running they're going to be defined here so now if we go back to database notice that the red squigglies went away for database URL we're going to do the same thing for node environment we're going to say this is z. string we'll say Min of one and then we're also going to go over here and say node environment is process EnV node environment okay so now all the type errors should go away except for schema this is another thing that we're going to have to end up bringing in um so let's go back to drizzle uh first I want to make a schema file over here say schema. TS so inside the schema file this is where you can Define your tables in your database remember we have the database running in that Docker compose but we need to use drizzle to Define like what our tables are going to look like what properties will live on those tables so let's go ahead and import some things I'm say import I'm going to say PG table from and then we should be able to bring in drizzle orm PG core so like as you saw there's a bunch of different packages depending on what type of database you're using but we are using post Crest so I'll say PG core I'm going to save that and then we can define a table so for example if I wanted to Define one called like U bids we can go ahead and use that PG table and we are going to Define what columns would be living on that PG table so I'm going to go ahead and just do ID and we'll say text and that's going to be ID um actually I think we could probably do serial so there's different like column types in this case we want to bring in the serial colum column type and once you define the columns I can make this a primary key like this go ahead and save that and let's make sure this is all good oh I forgot to name the table so one thing that's important is you need to name what your table's going to be so in our case we're calling this project bid buddy so I'm going to say bbor bids this is the internal variable you're going to use in your application and this is the name of your table that is going to be defined in your actual postgress database okay so now that we made that schema. TS file go back here and let's try to figure out why it can't find this module because it's literally in this directory so sometimes kind of just got to kick typescript a little bit let's just go ahead and force restart typescript hopefully you can find that EMA file and now it does so it's happy so the reason we Define the schema is because we want to create a table that we can start storing some information into so going back to our terminal one approach you can use is using something called drizzle kit to basically apply your changes to your table so uh I'm going to go ahead and show you how you can do this I'm going to say MPX drizzle it which I think we already mpm installed and then you can say we want to do a push we're going to use the postgress driver and then we're going to give it a config file we'll say drizzle. config.sys config file so let's go ahead and make that drizzle config.sys config does is again it's specifying where your migration scripts will live and some various other things that drizz will need so I'm going to go and just say export default Define config I thought I saw saw it show up but let's just go ahead and do this and I'll guess I'll import it up here because it didn't Auto Import for some reason so I need to import that from drizzle kit and then over here we need to say the schema is going to live in Source BB schema. TS and type and then driver we're going to use PG for postgress and then for out we're going to say we're going to say make the migration scripts live in a drizzle folder so like when we run this you'll see a drizzle folder probably get created the important part the database credentials we need to specify where our database will live so let's go ahead and import that EMB file that I mentioned before we'll pass database URL and then we're going to go ahead and say verbose true strict true actually it looks like there's a new version of drle Kit so that's pretty cool you guys got to learn this so they're basically saying we have to add dialect of postgress SQL here so let's go here like dialect post SQL and then we'll go back over here driver needs to only be set if you're using AWS data API turo okay so I don't need a set driver anymore and then for DB credentials just needs to be URL I guess okay so hopefully that works and now I don't even need to add a prefix I believe let's go back over here get rid of this prefix and let's try running this so again the whole point we're making this file is so we can say drizzle kit push and that's going to read this config file and it should hopefully look through our schema file and apply the changes to our database now notice we did get this error over here and this error is expected because that library that installed the EnV Library that's expecting certain things to be defined right so in our case we don't have database URL defined we don't have node environment defined so that's awesome it kind of lets us know that we have something misconfigured I'm going to make a EnV file and typically I think people say local now these days I'm going to say make sure database URL is defined and also make sure node EnV is equal to development now for database URL we need to put a URL to point to our database I'm going to go ahead and just do all that and then I'm going to try to rerun drizzle kit push sorry actually I need to get rid of the local um I guess this doesn't work with local so now you'll see that it asks you hey do you want to run this and we're going to go ahead and say we do want to run this so what this is doing if you're not familiar basically what drizzle and Prisma and other omms do is they look at your schema file and they try to figure out how to automatically write your migration scripts and apply them to your database so in our case it notices that we added a new table called BB bids it has this column in it and when we ran this drizzle kit command it's trying to basically apply those to our locally running postest database so I'm going to go ahead and say Yes apply those and now you'll see it says changes applied now what I like to do is I like to take this command and I like to put it in my package Json because I always forget what the commands are so I'm going to say DB push and we're going to just go ahead and paste that directly in here so we don't forget it in the future now there's also something else I really love adding to my projects and that is DB studio so what we can do is we can actually now run mpmr DB studio and that's going to host up a URL for us that we can click just go ahead and go to that and now we can see that that BB bids table exists with a single ID column so we can actually start adding records to this and the really cool thing is that now nextjs can connect to this database and start reading and writing data to it assuming I didn't miss anything but I think we should be good um I am going to go ahead and just commit what we have so I'm going to go ahead and say but we don't want to commit the EnV file I'm going to open up the get ignore and we do want to say EnV and just make sure that's defaulted as an ignore file and then we're going to go ahead and add everything and I'll say setting up the postgress plus drizzle configs and then we'll go ahead and save that and commit it but we've made some progress but we haven't actually like coded anything right we it' probably be nice if we actually coded something instead of just configuring stuff all day I went ahead and clean up this homepage and let me kind of walk you through how nextjs works so nextjs is a file based router where every directory is going to dictate your pathing in your Chrome browser so if you go to slash the root of your url that's going to load this page. TSX and then what we can do is we can start putting stuff here so say hello world and save this file um and then we'll go back over here and let's go to our app and notice that hello world pops up in the top cool so what we want to do is we want to create a form where someone can basically just create these bids I'm going to make a form here and I'm going to make an input and I'll say this is going to have a name of bid um could be a type number could be a placeholder all this is just kind of me explaining how we can connect to the database using drizzle and then we'll also add a button and then we'll go ahead and say like Place bid uh this could be a type theid over here say go ahead and save that so now that we have a form what we want to do is we want to run a server action when someone tries to submit this form so I'm going to go and just put a function here I'm going to inline this for right now but sometimes I like to add my actions into a different file so in xjs you can actually make functions these are called server fractions where you can actually run some backend code the moment someone tries to submit this form so what we want to do is we want to get the form data like this go ahead and just type that and then we can get the bid which is going to come from the form form data so I can say bid is equal to form data. git we can say bid as string um actually let me just go ahead and change this to be a not have a type so that should default to text we might not even use this because the way we have our table set up it just has a unique identifier and so what we want to do is we want to go ahead and import that database object that we have created inside of here remember we made this little boiler plate that copies some code in but there's a database object that we can use I'm going to say database and um we might have to Auto Import this or uh manually import this up here so I'm going to say import this from at SL database database okay make sure you do a from and now this is the the library the drizzle Library tool that we can use to basically do something with the database I can say database Dot and we're going to insert into a table so in our case we're going to say bids we're going to import that from the schema file that we have and then we're going to say values and then we're just going to go ahead and create a very empty record over here let's just save this and technically we don't even need this I'm going to comment out I don't know why I was doing that but now we actually have a way to create records when someone submiss this form so if you go back over here um it says I think I just need to refresh the page let's just refresh the page this functions cannot be passed directly to client components unless you Expos expose it by marking as use oh I I meant to say use server not use client I apologize okay so now we got the form over here and if I actually just click this I don't have any like confirmation or alerts but that should have created a record if I go back to the studio and just refresh let me zoom out one there's a refresh button up here I click it behind my head and notice that the number one pops up so we just successfully created a bid I hard refresh this you'll see it says one here we have one bid that was created and here is that Auto incrementing ID that's going to be attached to that table right so congratulations you just created a record and you stored it in a database but what would also be cool is if we could when this page basically loads we can fetch all the bids from the database and display them so with react in nextjs you have these react server components that allow you to fetch data when the page is first rendering on the server right so I'm going to say const bids is equal to a weit database make sure we go over here and we add an async in front of this react server component I'm going to say query do bids notice we get the auto completion here and I'm going to say find Min just go ahead and call that and that's going to fetch all the bids from that bids table and what we can do here uh there's there's a naming Collision here so I'm going to go ahead and say like bids EMA and I'm going to use that here instead so then we don't have that naming CLI but again what I was trying to do is over here we can go ahead and just Loop over those so I'm going to say bids map and then for every bid we have I'm going to make a div I'm going to give this a key of bid do ID think I need to call that bid not bind and then over here we can just go ahead and display bid. ID as well all right so a lot of information I'm going to take a step back at just a second and kind of recap but notice when this page loads it does display one and so if I were to click this one 2 3 4 five times refresh the page notice that we get back six total records awesome so recap what have we actually done here I know this is like Elementary but if you're new to nextjs and drizzle this is a good introduction to how it all kind of works so what we've done is we brought in Docker compose to spin up a postrest database we brought in drizzle orm so we can easily read and write to that database using a nicer convention uh rather than doing like SQL commands directly you have these cool functions you can call it's all type safe then we created a page which has a form on it where we can actually just click a button and have it insert some data into our database and then additionally when this page loads for the first time it fetches all the data from the database and we render that out to the page all right and this is the the convention once you understand this like we're literally going to be doing this for a majority of the pages in our application it's usually a bunch of fetch data make a form save some data Maybe show an alert or a toast or some error state but that's basically the pattern that a lot of web development does okay so I do want to kind of spruce this up a little bit because there's a couple things that this page doesn't look too good so to give you a crash course and Tailwind typically what you want to do is inside of your main page container I'm going to go ahead and say this is going to be a container and I'm going to say MX Auto and that'll kind of bring it in if I were to zoom out instead of being 100% notice that that brings it in a little bit which is good because if you have a really really large monitor you don't want stuff being all the way to the left of the monitor right so let's just go ahead and wrap stuff in containers and then also what I like to do is just do a py of 12 that's going to add padding to the top and the bottom of this container and so you can visualize it let's just go ahead and click on this and notice that if I hover over this we have some padding on the top padding on the bottom we got some margin on the left and the right so at this point if you watch my content you'll know that I like using Shad CN for my component Library so I think it's time to docs over here and then we're going to follow the installation guide it's pretty easy to get set up with nextjs I'm going to first just run this command here all right and then it's going to ask me some questions so let's just go ah and say use the defaults for why not and that's going to set up some files for us we got a components Json file we have some Library files and we also have some Global CSS let's go over here and follow this approach as well so let's go to the root layout let's just copy the lines that they tell us to do do this go down and we're going to add that to the body find the body over here add this class name to it and then Auto Import this CN Library so some new things that I should probably talk about it's first of all we're bringing in basically a custom Google web font called font Sans I believe actually it's called enter but we're renaming the font s for some reason and then down here at the bottom we are using another Library called CN if we look at this this is basically just using Tailwind merge in clsx so that we can kind of easily combine Tailwind Styles together you'll end up using this Library uh this little helper utility function all throughout our code base but you'll see basically you can pass it multiple strings that have ta and CSS classes it just combines them together and figures out how to like have ones that come later on override the ones that are prior all right so let's keep going down um I think we have that set up they're saying you can configure your theme inside the Tailwind config let's go over here look at that and make sure this thing is set up so let's go to font family and make sure that's somewhere here doesn't look like it is so let's just go ahead and copy this we'll go to theme we'll go to extend we'll paste that in and probably uh got to make sure we have the font family installed and they give you an overview of the app structure and stuff so the first thing you should probably do is just make sure this works so let's just go ahead and grab this button component this is how you can add new components to your application with shad Cen they have this like allart pick and choose what components you want you end up installing a lot of these at the end of the day but it's just nice because you don't have to like bring everything in at the get-go you can just bring in a button and kind of build upon it so let's just go and run that command so we can bring in that button component you'll see it installs the button and then it's going to put it inside of a components directory so over here we have components UI all the Shad CN components go in this directory and you can kind of scroll through here and basically look at the code um that's one reason I like Shaden is that you own all the code it brings in it just copies and pastes it into your project which allows you toine grain customize it as needed let's keep going we're going to use this button on our form so if we go back over here let's just go ahead and change this instead of a lowercase button let's make it a capital button and then I'm going to go ahead and import this from that components directory and if we did this correct we can go back to our app and notice we have a nicely styled daden button you hover over it it has hover States you click it um I don't know what happens if you click it but there we have it also the input um the input looks pretty bad right so again turns out you can just go over here and find an input we can just go ahead and install it uh make sure you do mpm load up the terminal go ahead and run that that'll install an input and then when that's done again we can just like rename this one go ahead and Auto Import that from components save it go back to our app there we have a nice looking input field so one thing you'll notice is as I'm like clicking the button nothing is showing up over here right we want this page to refresh and show the latest data that we just added so how you can do that in nextjs if you're using server actions you you can just call a method and you can say revalidate path so depending on what path you're currently on in our case we're on the root path I'm just going to call revalidate Path of Slash and that's going to tell nextjs that you need to reload the data it's going to rerun this component refetch your bids recompute your components and your jsx and send that down so now if I click it notice that the 13 14 15 16 17 getting appended down here at the bottom so very very nice feature where you can just kind of like modify some data mutate your database table and then just tell next to refresh basically all right let's do another commit over here I'm going to go ahead and say bring in Shad CN and revalidate after adding items add all those changes here and I'll commit those and I'll sync those up so before we get too much further into building out this application I need to bring in some type of off library that we can actually log in with Google and we can have cookies and sessions to keep track of what user is actually creating these various things in our application so I'm going to go to OJs atdev which is basically the latest greatest version of next o now I will say next off 5 is still in beta so if you're not comfortable with using beta stuff you can drop down to next four but at some point this should hopefully come out and it should be good to go so let's go ahead and just click on nextjs here and we want want to follow this installation guide so let's just go ahead and install next off Beta let's go down here load up a terminal and I'll just go ahead and run that and as that's running let's just go ahead and kind of read through this and figure out how to get this set up it seems like the only environment variable that is mandatory is off secret and you can create a secret by running this command and you need to put it in your EnV uh local they say um I'll probably just add it to my EnV and hopefully it still works the same way so let's just go ahead and run that and see what it gives us all right here is our off secret let's go to our EnV file let's go ahead and paste that in and save it let's go back and let's keep on going through this and try to figure out how to set this up so to configure next off you need to make an off TS file at the root of your app router so let's go here and I'm going to add in the app router I will make an aot yes paste that in and then it says add a route Handler under this location I can just copy that whole path here and that started at app so let's actually just go let's grab the API here and I'm going to go ahead say new file paste that in and there we go we got a file we can use let's just go ahead and paste this in um actually I think I need to put the O in the source directory let's do that because that was kind of referring to the wrong file here so now we need to make a middleware TS file let's copy that and I think I put the middleware here so let say middleware TS paste that in hopefully that's good and set up authentication me method so with that the basic setup is complete next we'll set up the first authentication method let's go ahead and go to the next section uh let's skip over this we do want to set up Google so I'm going to go ahead and click on Google and we need to make sure that we have a call back URL set up like this all right so I'm going to go to console. cloud.google.com and then we can create a new project this is where I have like all my random side projects set up let's make a new one we'll call it bid buddy and this doesn't take too long you kind of have to set up a new project every time you do some type of ooth with Google so we should be able to get through this pretty quick let's click on bid buddy as our project and select it and then we can go over here to API services and we can go to credentials okay so let's create a credential and let's go to ooth client ID and then we are going to go ahead and configure our consent screen that's the first thing you kind of need to do I'm going to say this is an external app go ahead and click create and then we're going to say the app name is Bid buddy and then we're going to go ahead and just put my email here we can skip all this other stuff we do need to add web devc gmail.com go ahead and Save that and then down at the bottom we will say save and continue save and continue back to dashboard okay so now we set up our consent screen that's kind of like the screen that people will see when they want to sign in with your app but let's go back to credentials and now we can actually create the ooth client ID so let's go ahead and click on this and say web application I'll say bid buddy scroll down here this is the important part for authorized JavaScript Origins you're going to add Local Host 3000 and then when you go to production you're going to have to add like your real domain if you do have one and for authorized redirect URLs this is where in the docs they're saying you have to have basically this so we're going to say HTTP Local Host 3000 slash and then I'm going to put that URL just like this okay let's just go ahead and create this and you're going to get a modal that has a client ID and a client secret so we do need to copy this stuff and then we're going to add that um hopefully the docs tell us what to do with this so yeah they they say add it here so let's go back copy this go here paste that in and go back to our UI I'm going to copy the client ID B right here copy the secret B right here and obviously don't leak any of these things if you leak these Keys your whole system is compromised um but we should be good we're just doing some stuff locally let's close that all right so now let's set up the provider when you're using OJs you basically have to go to your off config and we can add a Google provider so let's go to that off file here and then inside of providers we're going to go ahead and just put in Google make sure you Auto Import that provider like that and and maybe I should just kind of follow exactly what they're doing so I don't run into some random snag and waste 10 minutes let's go ahead and do that and then we can add a signin button so let's go ahead and looks like they give you a nice signin component you could do go to components here and then I'm going to go ahead and just say sign in TSX paste that in and we should hopefully be good I don't know why they're importing a TS file there that's not proper and then if you load up the app and click the signin button you should be redirected to Google and you should be good so let's go back to here refresh the page and we should see a signin button oh wait we haven't used it yet so let's go ahead and go to our page and I'm going to put a signin button at the top just like that go fingers crossed will this work we have a signin button with Google so click it takes you to Google select one of your accounts you should hopefully get this this is the oo consent screen we kind of configured and it's kind of saying like what things this application needs from the user uh let's just go ahead and click continue we should get redirected back to our app and now we are actually authenticated if you go go to your application tab in Chrome and you scroll down you'll see that we have an OJs session token this is the token I think it's encrypted so you can't really even look at it but this is a token that has your information such as like your user ID and some other things as well so let's just verify this a little bit more we want to actually conditionally show a sign out button if we are signed in now I don't really know how to do that cuz I haven't really messed with the beta before so let's go to sign in and sign out and read through the documentation so here's a signin button we just did but they also have a sign out button which you can do like this so basically you can uh oh sorry this is a signin button that tells you how to sign in um down here we have a sign in button okay okay where's sign out here is a sign out button let's just go ahead and import this and we're going to make a sign out button so sign out TSX paste that in and then again not sure why they're importing some weird location there this is beta after all so let me not bash on it too much uh let's let's conditionally show this so let's go ahead and first add a sign out button here there you go and then we're going to go ahead and just click it and make sure that that session token got deleted so now you'll notice it doesn't have OJs session anymore we are technically signed out at the application but typically with off JS there are some helper methods you can use such as get session to know if you're logged in or not so over here we can basically call this method and if session. user is not defined we know that we're not logged in okay so let's go over here and again I'm kind of just trying to teach you as much as I can we're not going to like make perfect code right now I just want to make sure you understand what we're doing cuz we're going to keep building upon it so let's just go ahead and try to import this off method from that location and then we have session here okay again this is how you check if a user is signed in or not technically um you could just just check uh if the session is set okay okay so that's how you check if you're signed in what we could do is just say well if session is defined then we will go ahead and show a sign out button if it's not defined we will show a sign-in button so let's go to our signin and sign out button I want to make them look a little bit better so I'm going to go ahead and just change those to buttons like this and then we look at it it says sign in with Google so it looks better and then also for sign up button let's go ahead and change those to be real shadan components and so if we were to sign in and sign out we should be able to sign in here take us back to Google and then we see a sign up iton so that's how you can conditionally show things on the page based on if you're logged in or not that's pretty cool right it's very important to understand that because you're going to use that all throughout our application to check if you're authenticated check if you have access to certain things um what you can also do is if you have the session defined there should be a user object defined and you can actually display the name of the user okay so right here displaying my name and you can end up putting that in the header if you want to okay so that's great but the issue is that when a user signed into our application we don't even track that we don't know if what users actually have signed in with Google or not so typically there's something called adapters and we are using drizzle if you go to adapters and click drizzle you will see a o drizzle adapter that we can install so let's just go ahead and run this there you go and then over here we need to set a off drizzle URL which I'm guessing is just going to be a location to our database that we might end up having like two URLs that point to the same exact database for some reason um but it is what it is so let's just go ahead and paste that in and then we are going to go back and we need to figure out how to set up our schema remember in the schema file we Define like what tables our database has if we want to track the user then we're going to have to create a user table An accounts table sessions table and a verification token table so let's go there let's go to our schema and then let's go ahead and paste this stuff in there's going to be a lot of information that we're going to have to import so let's just go ah and Auto Import some of this stuff so we make sure you always look at the package it needs to say PG in it PG core that's what we want um go ahead and just Auto Import what we can I don't know where that came from we have to figure that out let's go back to the docks that comes from here time stamp comes from drizzle PG core integer same thing primary key what is this needs to come from PG core as well and now there's only one error in our example so let's go ahead and look at this adapter account Type U I guess it's just called a c adapter account so these docs probably need to be updated and then we can use that type over here and hopefully this works so once you've have the schema set up what you can do is you can add that adapter over here to that off config so let's go to off and let's add it there make sure you Auto Import the adapter and database is actually what we want we don't want DB we want to make sure we import that database file which um it's not showing up so let's just go ahead and say import database from at SLB database all right that should be good so now let's do a mpm run DB push and that's how you can basically take all the schem of changes we just did I think I told you this already but this is how you kind of take the changes that you did and apply them to your database so run this it's going to tell you hey there's a bunch of things are you sure you want to run this and I'm going to go down and say yes now it says changes applied and if we were to go back through drizzle Studio refresh this notice that we have account session user and verification token the one thing I do want to point out though is I like to preix my tables with BB or whatever your project name is because sometimes there's certain keywords in whatever database you're using that you try to create a table and you use a Special Reserve keyword you're going to run into issues with your migration scripts so over here you can actually just put a comma and you can Define your own schema here so let's just go ahead and do this and and we're going to go ahead and just import accounts we're going to import users sessions and verification tokens you know they tell you exactly how to do this so maybe I should just copy what they have here I must have a misspelling somewhere I'm not sure what's going on there adapter account is not assignable to type string um let's go back to our schema and let's look at adapter account I'm just going to do an as any hopefully this works keep my fingers crossed but um it just doesn't seem my understands what this type is but I think it should still work anyway um so again the reason I did this is because I like to prefix my tables so I'm going to say BB user a BB account a BB session and then also BB verification token and then I should be able to run a push again and then go ahead and just say rename everything okay everything's been renamed and fingers crossed if you go to drizzle Studio you should see all those pop up awesome so we are getting this bug and if you go and read up on this discussion there's something going on with post grass and it just doesn't work when you put it in a middleware function so we're going to have to downgrade there's there's a couple Solutions you can do you can downgrade postest to 3.3 I believe so if I go over here um you know I am running into some weird issues with the middleware so I'm going to go ahead and delete it honestly I haven't really found too many use cases for middleware because we can still do authentication checks and authorization checks without the middleware file um I just don't like how nextjs is like so coupled to an edge runtime I should be able to tell the middleware that I want to use node because it just it errors out with some of these d adapters I believe I think I forgot to run a migration or something all right I'm having some issues running this migration I think I made too many changes and sometimes this is something that it's good to learn how to do is you want to set up a postgress Explorer so that you can just delete all your tables okay so let's go over here I'm going to add another connection here so I have this postest Explorer that's built directly in the vs code and this is very important to understand how to use I'm going to go ahead and add a connection and we can just go ahead and put local post we can go ahead and say postgress will be the user password is going to be example and then we're going to say the port is a default and then we're going to go ahead and say yes a standard connection and then we'll say the postgress database and then we're going to name this bid buddy go ahead and save that and then we can go and look at this table um the reason I'm doing this here is because you don't have the ability to like run drop tables from Grizzle Studio I don't believe like if I want to say delete a drop table BB user I don't think this will run so let's just go ahead and say command shift p I'm going to say create a new SQL query and then I'm going to select the connection so let's just go ahead and say select and then I'm going to say SQL select connection okay I want to make sure it's on bid buddy I want to select that database and then we can go ahead and say drop table bbor account and I think I need to say public and then I need to do a DOT let me go ahead and just put a Cascade will that work there we go that dropped that user tables refresh it do the same thing with user fres that bids session there's probably an easier way to do this honestly verification token all right everything's been deleted now the reason I'm doing this is because I'm having some issues running a migration when I do a DB push let's try running it again that should apply all those changes absolutely no problems and we should be good so maybe you just learn something new about cqel they right there but if you're going to be dealing with a postgress database you probably need to understand how to do some like like raw SQL queries manually especially when you get into running migration scripts okay let's try signing in again let's click on sign in with Google click on my name and now we are signing with Cody and if I go back and refresh this we get a user entry now so now we've actually tracked that user when they log in for the first time and we're going to be using this unique identifier to basically attach it to various types of entries we add to our table so our case we're creating a bid but it'd be nice if we could track like who this bid belong to again this is why we add authentication so we can track things so let's go back to the bids table and honestly I might just make a new table called items because an item is basically what we want to upload to the site and then people can come along and place bids on it or something so I'm going to go and say items and this will be called BB item and we're going to go ahead and grab this user ID CU we want to grab that go ahead and paste it in there and then maybe we could add like an an item name or something so let's go and say name be text that could be called name that could be not null all right let's do another DB push and that should apply that schema change to our table and we should now have a BB item table let's go back to our page instead of creating a bid let's just go ahead and change this a little bit this is going to be called name and this will be called name your item so if someone wants to sell a pair of shoes or something uh they can go ahead and do that and then we're going to call this post item and the difference here is now we're going to insert into the item table so we can just go ahead and say like items okay and then for Value we do want to do name so I'll say name and we'll say form data get name as string and then we're going to revalidate the path and we're also going to change this I'm going to say all items I'll change this query from bids to items go ahead and save this now the the one thing that we need to add in now is we have to basically keep track of the user who created this item notice how this is still red we're getting a type script error the reason that's happening is because we need to actually say user ID and then we can say session. user. ID um technically if you're not logged in we probably shouldn't even show like allow you to click the button and so adding these little question marks everywhere is not the best in terms of code maintainability so what I would do is either early up here in your function I'd say if there is no session just go ahead and return null and then also if there is no session. user also just return null and so that we don't have to come in here and add like these random question marks everywhere and there's other ways you can handle it let's say if like you're not logged in you just want to not show this form you could do it that way actually let me go ahead and pull this out to a constant here so I'll say like session. user because typescript is not understanding that this is potentially always going to be defined at this point you know actually I'm going to change my mind on all this okay I'm just going to put an exclamation mark at the end of this for right now and then I'm also going to refactor this a little bit to say items this will say item. name let's go and save this and hopefully this will be good again the whole reason we're doing this is so when someone wants to post an item so I just go and say shoes and click post if I go and check the database if I go to item now now notice that we have the user ID so what this allows us to do is now we can start having like a a custom dashboard for a user and fetch all of the items that they have put up for bidding and um it's just good to have the stuff associated with your your records so at this point I think let's go ahead and commit what we have I'm going to go ahead and just say you know we added in next o so let's say add and next o and items table ahead and commit that so I think we should try to add a header here and make this look more like a real application because right now it's looking like a prototype right let's go to the lay out here and we're going to go ahead and try to add a header above all of the children that we render out I'm going go ahead and just pend like we have a header we haven't made one yet but we're going to make one now and I like to put my components Co looc so if the header only used one place I'd probably have the header file live next to the layout that's the approach I use but you're welcome to do whatever approach that you want let do an export function header just return a div right now and over here I'm going to Auto Import that header okay and inside the header what we want to do is we're going to add a container and inside of this div I'm going to go ahead and say BG gray of 50 and the reason I'm adding the container is so that again when you're at a larger screen like stuff will scale properly on the left we want to kind of create a logo and display it and I'm going to make a div here and that will have an image bring a next image I going to say SL logo.png width can be um like 50 I guess okay that should be pretty good and then over here I'm going to go ahead and say bid buddy bid buddy.com let's just pretend we have a website like this and we're going to go ahead and just give this Flex the item Center we'll say gap of two now let's go look at how this is looking okay not too bad i' probably make the header a little bit darker so let's say gray of 100 there you go and then also we could give this some padding y of four okay maybe even make this gray of 200 so it's more apparent and then on the right side we could actually show a log out button so instead of just showing nothing here we're going to move this sign out button we're going to move into the header so let's go back to the page find the sign out button which was this one and we could probably actually just copy this whole logic so let's go to the header here I'm going to make another div I'm going to put that logic in I'm going to import this and import that and then for session we need to go here and say con session is equal to a wait off I believe actually how do we do it over here yeah we are doing a weit off but uh we need to put an async here for this to work and then Auto Import that so notice that this is getting put underneath this logo in the text not what we want what we actually want to do is we want to change this thing to have a flex and we'll say space between so that it'll push the buttons all the way to the right in fact it's not doing that I think uh oh it's not space of pH it's called justify between there you go now we got sign out over here and if we clicked it it would change to a signin button probably reduce the Gap over here a little bit I think gaap of two is a little bit too big do gaap of one there you have it now secondly when you click on this logo typically the approach is you should have it redirect so I'm going to go ahead and wrap this in a link like this and then we're going to go ahead and just give this a um an atref of the homepage we give it a class name too we can say text underline on a hover I think we can just do that there but now we should be able to hover over that and it becomes underlined um again we kind of lost all the styling here so we're actually going to move this directly into that class here we don't need to div anymore and of course you can just keep playing around with this like padding of four might be too much do padding of two and [Music] um that probably needs to also be item Center so like over here we could say item Center so some more cleanup let's go to the page and let's get rid that sign in and sign out button also get rid of our name because we can actually in the header we could just put that name so like I'll go over here and next to the sign in sign out button this could be its own div potentially and then over here we can have another div that's going to have session. user.name put some question marks here and then my name will pop up and then again we want to get this all one line so we could probably just give it a class name of flex item Center gap of two know I'll do a gap of four okay pretty good and let's go ahead and focus on this form so like we probably need an H1 on this page over here I'm going to say H1 I'll say class name is text to 4XL font to bold and then I'm going to go ahead and say post and item to sell so this form I think we can just style it a little bit and make it look a little bit better I'm going to give it a border I'm going to give it some padding I'm going to give it some rounding and then we should have a nice little rounded section here I think padding a four is too small and this could probably be an extra large and then secondly we should probably give this a space y of four or something okay and then we should probably also give it like a Max width of the input on the input itself we can say class name is equal to Max width of medium that'll make it so it doesn't scale so large and then secondly this spanning all the way we can fix this by just putting a w fit on this I I say w fit that should scale it down and that'll only contain the necessary width of the item that we're trying to like it contains right actually let me Swap this I'm going to put the max width here instead of fit that looks a little bit better and then underneath the H1 again we could just do some margin bottom or if you're a fan of doing like space y you can that as well and just like space these things out and yeah so now we have like the ability for us to come in here and post a new item but then we also probably want to show all the items that are currently forbidding so I'm going to go ahead and put an H2 here you have a class name say text of 2XL you do font and bold as well and then save that I'm going to say items listed or items for sale this button I'm probably going to move over so like instead of it being there I'm going to give it a class name and I'll say self end that should push it to the end hopefully uh it did not and that's probably because this thing needs a flex Flex column on it we go now I plan to add probably more stuff here like how much does your item cost what's the starting bid um we have the name we need to come in and add some validation to this but I'm I'm just trying to slowly evolve this application to make it look nice and for the items for sale again we could probably do some type of cards over here so let's just go ahead and make this have a class name say border we could say adding of four or maybe eight and we'll say rounded extra large save that and wrapping this whole thing I'm going to go ahead and say grid I'm say grid calls 4 I'm going to move this div down okay so that we'll have like as we add more items so like testing another item um what is up another test do that a couple times we probably want to add some gap between these cards right so let's just go back and we're going to go ahead and just put a gap of four between these cards and make sure that's enough maybe even an eight I just realized my head was hiding my name and my sign out but Buton that showed up over here so this is what it looks like in case you missed that part so I will say at some point like you have to kind of re-evaluate what you're building and try to understand from a user's perspective what is the most important thing for a user who comes onto the site is it viewing items for sale or is it placing new bid items and I would assume that you want to find items to bid on that's your number one concern so if that is the concern maybe we should move this to its own page so let's let's go ahead and make a new page here we haven't made another page but with the nextjs app router you can make a page and we can just call one uh we'll call it create bid like that you can also do bidc create if you want to maybe that's another approach we can do a bid SLC create whatever way works and inside of this bids SLC create page let's make a page. TSX and we want to move a lot of the stuff that we had over here with this form I'll just copy the whole page I'm going to paste it in here and I'm going to go ahead and just refactor a little bit okay we don't need the sign in and sign out button we don't need the session stuff just yet really we just want like a a container we can say post and [Music] item keep the form but the items for sale can all go away okay and then the logic for the action would stay the same now I'm going to go ahead and refactor this out like I said I like to have an actions file so I'm going go ahead and say action. TS sorry I forgot the S so actions oops and then I'm going to say use server and then we're going to go ahead and extract the way this server action here we'll say export async function sorry I'll call it create item and then we're going to go ahead and just like take form data that was previously there and we're going to go ahead and just make sure we import what we need all right go um now we do need the session so let's just go ahead and say const session is equal to wait off make sure we import that and then we want to add some authentication check so if there is no session we definitely want to throw an error we'll do that and that should help us know if someone's trying to hit this endpoint without permission I'm going to go ahead and add an action suffix to this this is how I like to name my actions and um at this point session should be defined we should probably get the user as well and then if the user is not defined for some reason I don't know why it wouldn't be defined but we can throw an error so that we can kind of get rid of that and we can do this and then also if there is no user ID so if there is no user. ID I think we can do that that should make that happy let's go back to our create page and instead of doing all this stuff in line and just call the action directly here and then we can go ahead and clean up some of this code so again we have a new page if we were to go up here and say bids SLC create fact I don't know why I call it bids it's more of an item so I can go ahead and just rename that to items so now in the homepage we should probably get rid of this go back to the main page we're going to delete the form in the one and we're actually going to rename this to items for sale this okay but how do we get to creating a new item well there's a couple ways we could probably have a something in the header here let's just go ahead and try that let's go to the the header we created we're going to add a link so let's just copy this link here and then we're going to go ahead and just put this link um where should we put the link I I want it to be like right here the way we could potentially do that is we probably want to wrap this whole thing in a div go ahead and just drag that down and then that could be a link over here and then we're going to go ahead and just get rid the image let's call this auction item and then let's save this that should show up somewhere and if you click it we want to go to items SLC create so that if I click on this we should go to the items create page okay so now we can navigate between these Pages which is pretty nice um again we want to get this on the same line so we could just go ahead and say like class name Flex item Center gap of eight and then I'm going to wrap this in a div as well in case we have like multiple items okay fact uh I might do a gap of 12 instead okay auction in item might be better now we can go back so I think there's a couple things I want to add to posting an item and that is we want to have a starting bid price let's go to our items page and inside of this form we probably want to add another input we'll call this start price and what to start your auction at and I think this could be a type of number there might be a way to like do some decimal places maybe there's a way to like limit it to only have two um but for right now we'll just stick with this 243 and then when we submit this I'm going to make these both required for right now so let's say required and go ahead and save this but now what we want to do is we want to track that item okay the the start price I think calling a starting price would be a little bit better starting price um so let's pretend like we have starting price on that CU right now we don't but we'll have to go back to our schema and find our item have to give it a starting price make it an integer awesome and then let's go ahead and run a DB push this um sometimes when you try to add data to drizzle it's going to ask you to basically truncate a a column or truncate a table and when this happens it's because if you read this you're trying to add a column that doesn't have a default value and drizzle default thing is just like delete your table and recreate it we're going to cancel because we don't want it to delete all the existing items that we have especially if you're doing this in production you might have thousands of items you don't want to just delete all them so we are we are going to say default and we can just default it zero or something so that when we actually try to run that it doesn't delete everything and that's typically the best approach for it is make sure you give a default to all your new columns um if you can and then if you need to you can write a script to come in and actually like populate them with the real values okay so now our action should persist that let's go back to our form I'm going to go ahead and say like some shoes $299 will be the starting auction or you could do $3 just go ahead and post the item and after we've successfully posted the item we should probably redirect back to the main items page or something so let's go instead of saying revalidate path I'm going to say redirect and we're going to go to the homepage okay you don't need to revalidate the path anymore you just redirect back to the homepage but click it there we go now we already have two shoes I don't know which one was like the one that we're uh doing so I'm going to say pencil that'll be $2.99 as a start price please enter a valid value the two nearest valid values are two and three um I think we can actually say type as a decimal here I forget how you do that type um type number men of one step any actually I'm going do a step of that that should be pretty cool let's try this if I submit this here we go invalid input Syntax for type integer 299 so the issue is that when we save this this is coming over as a string I believe and then we're trying to cast it to a number so we actually probably need to say pars um float and this is going to be a string and let's try this again I'm going to go ahead and just say whatever 199 save it and that's actually still not working so let's just debug this a little bit I'm going to go ahe and say like starting price is equal to that I I need to figure out what this is is this a string or is this something else let's just go ahead and say console log type of starting price and let's see what gets printed out let we do 299 go to our console down here and we should see that it is a string okay and the issue is because we're trying to store an integer when really this should be like a number or a float yeah I don't know if uh postest has that so another approach that I've seen a lot of systems do is you do keep it as integer but what you do is if someone sends over 299 you actually convert that to like currency I'm going to say as string and then I'm going to say const price as cents and then let GPT do that for us well I need the math. floor this and then we're going to go ahead and just store price as cents over here and hopefully this works I mean at some point we're going to bring like Zod and do some real validation here but now we should be able to save this go back to drizzle let's go to items and refresh this and one of these should have a starting price of $2.99 so basically just store the scent value don't store floats so you don't have any type of like floating Point issues or rounding issues just store the whole number there and then later in the UI if you want to format it you can just divide by 100 to show a decimal um yeah so at at this point what we could probably do is on all these items let's display what the starting bid is so let's go back to the page here and actually no where what page are we need to go to let's go over here to this page and then what we need to do is also display the starting price so I'm going to say starting starting price and then we'll say item dot starting price and again divide by 100 that might give you some really weird uh not a number Stuff um I'm not sure why that's doing that sometimes you actually need to restart your next server when you do database migrations because it doesn't like know how to figure out the numbers there we go yeah I just had to restart next and now it's it's working now obviously we could do more formatting make this look pretty you know probably put like a money sign in front of this go now it looks a little bit better but for right now let's just go ahead and commit what we have we've done a lot of changes I'm going to go ahead and just say like adding a header let's add all these changes I'm going to say add header and then I'm going to say create item page Factor homepage I think it'd be really important to be able to allow users to upload images for the items they're trying to auction so let's go ahead and do that now where I like to host images is on R2 you can also use S3 if you want to and once you've made an account you can come down and go to R2 over here and I'm going to create a new bucket and I'm going to call this bid buddy go ahead and click create bucket and R2 is just a file storage location right it has the same interface as S3 and so you can actually just upload images directly to the bucket uh from your UI so now that we have the bucket let's go to settings and what we need to do is we need to go down um actually let's go back to R2 over here and let's go to manage my API token so let's go and create a new API token and I'm going to call this bid buddy I want to make sure we have the ability to read and write from that bucket so let's click on that one and then let's go ahead and only allow this key to be used for the bid buddy bucket go and create that API key and now we can go down we have an access key ID and we also have a secret access key we're going to need both of those and we're also going to need this ID that's in our Cloud flare this is our cloudflare account ID so if you go to this documentation here you can use a AWS SDK client to basically upload images to this bucket so we're going to go ahead and install this go back to our code make sure that we can install that and install this and then also we want to install this pre-sign Library so while those are installing let's go ahead and just create a new file over here inside of our lib directory so I'm going to go to Source I'm going to go to lib and I'm going to say s3. TS and we want to basically copy uh we can actually could just copy all this right now copy all this and we're going to delete some of the comments that they have we don't need any of these comments delete that delete this and in order to upload to this bucket we have to make sure we have certain environment variables set so we need an account ID we need an access key ID and we need a secret access key let's go to the EnV file that we have I want to make sure that I have all these things set and I am going to put cloud flare account ID this and then we also want access key ID that and then also secret access key so go ahead and do this and then over here I can just make sure I pull those in let co-pilot kind of do all the work for me and then go back to this file let's go ahead and import that en package like this and we want cloudflare account ID we want en. cloudflare access key ID and cloudflare secret access key okay so that's how you can set up the client that can connect to the bucket I'm going to rename this S3 client and now we want a function to be able to upload an image to the bucket so I'm going to say export function um get signed URL here we go this probably works fine and then we can just go ahead and go back over here I think they have an example if you scroll down we need to do this one the put item command so let's just go ahead and run that and I'm going to return this and I'll say S3 client delete the rest okay so this is a function that basically creates a URL that only lasts for an hour and you give it to your clients and your clients can upload a file with a certain key to a bucket so we also want a bucket name so I'm going to say EnV do bucket name go back to that en file over there and I'm going to go ahead and just add a bucket name and for our bucket name we're going to do bid buddy CU that is the bucket that we created okay and then finally let's go over here we'll put a key so that we can call this function pass it whatever key we want and that should give us the ability to upload to it okay so all of this was so that we can go back to our app and in this post and item page we want to add like a file input to allow a user to select the file and then upload directly to this bucket so let's go here I'll add another input and we'll call this type of file and then we'll give it a name of file go ahead and save that we should see a file pop up over here and you know I forgot to add in the other environment variables so let's go back we need to add in the cloudflare access key ID so cloudflare access key ID is equal to This Cloud flare secret access key going to be equal to this one and then we need a cloudflare account ID which was up here in the URL you can just copy this paste it in save it and hopefully that's everything we need all right so now we have a file that's popping up and we can go ahead and just put my name or put a an item let's say shoes again we'll say 199 and we're going to select a file from my computer so I selected that bg. jpeg so going to this action if we want to get the file let's just try getting the file out from the form data like this and I'm going to console log it and see if we have access to it all right so we're consing the file here let's go ahead and just click this and let's see if we get the file back so over here you see we do have a file and I think we could actually upload that directly to our bucket there are some limitations with doing the file uploads directly from an action I think there's like a 6 megabyte limit or something like that I have to go and read the documentation I think this is also experimental so that function we added here to get the pre-sign post we don't necessarily need it if the files are already sent over to our backend um but we're going to kind of change our implementation because for larger files like you don't want to do this in a form action let's go over here and we're going to kind of modify this a little bit I'm going to say use client I'm going to get rid of the async for right now and instead of calling an action here we're going to say onsubmit and then we're going to pass this a callback function and we're going to say e. prevent default so that it doesn't submit the form and then we want to call the server action at some point we're going to go ahead and bring in a form like this we're going to get form data like this and then we can actually just pass in form data directly form data okay so this is working exactly how it did before but now it's a client component and we should probably actually do like an await on here but the main difference is we want to upload the file from the user's browser to bucket because that's going to save on bandwidth instead of that file having going have to go through our nextjs server and then our nextjs server has to again upload the same file to a bucket why not just cut out the middleman and upload directly to the bucket itself so I'm going go ahead and say like get the upload URL and we can simply call an action which we'll probably have to create another action for this so I'm going to go over here and say export async create upload URL and this needs to take in a key and then we're going to go ahead and just return await uh get signed URL for S3 object like this and that should hopefully return us back a URL we can use to do a multi-part upload so I'm going to go ahead and uh rename this to have action at the end so it's more apparent and then we're going to call it like this we can say file which we haven't grabbed from the form so we can just go ahead and do that and I'll say file dotame that could be the name for our key that we're storing in the bucket and now we want to upload that so you just do a fetch request you pass it that upload URL and I believe you can just pass it um I don't think you just pass it the body I think you need to make another form data so I'll say upload form data is equal to this and then we're going to add the file and then we're going to say upload form data here let's comment this one out I'm going to try this out I know there's a lot of information coming your way if you're watching this video but this is all important stuff you have to upload files somewhere and typically most people in production size large scale applications are going to be using S3 or R2 or some type of distributed file storage let's go back over here type in some information grab an image go ahead and click post image and we did get a chors issue so when you're trying to upload to R2 you do need to go through and update some cores um access ACC so let's go to R2 let's go to group or sorry bid buddy settings let's go to the ad cores policy and we're going to go ahead and add one for post also add one for put and then we could simply add um I think for allowed headers I'm going to say content type and then for exposed headers we're going to keep that blank for right now this should hopefully work let's just save this and let's see now it can take up the 30 seconds for this change to propagate to your bucket so give us some time um let's just live on the edge and try it again without waiting go and click this and notice that it does seem like it's doing something now I think it ran I just don't have the redirect happening anymore because I commented out so let's go back to our bucket and let's check so I can go to objects here and then I'm going to go ahead and just do hard Refresh on this page because for some reason the refresh button is not visible but you can see that it has our image now so we can click on this and we could potentially download it if you want to and look at it but it does have our image which is pretty cool go over here click download here we go now one thing we typically do when we upload images is that we want to actually attach a content type because right now the back end doesn't know is this an image so let's go back to our create upload URL action and we also want to pass in the content type I think I just type and then let's go here I'm going to pass in type that's going to be a string that's going to take in a type over here we have the type now so type string and then we basically want to pass that in like we can say content type and just set it okay so now next time we upload the file it's going to also pass in the content type CU right now if you look at the object it says multi-art form data not what we want I don't think so let's delete that I think there's something we're missing here I think maybe here we need to add headers let's try this let's try it one more time click post item refresh this okay there we go now it says image JPEG so we're we're cooking so let's go ahead and click on download it should show now hopefully okay so we verified it is getting stored and it does have a content type over here imagejpeg which is good so now let's see if after we submit this we need to store it to the database and we probably also want to store the key of the item so let's go ahead and bring this one back now since we're not doing like a form action anymore I'm actually going to refactor this action to just take in what it needs instead of form data so it could take in a file name a string it could take in a name which would be a string and then also a starting price could be a number and I'm going to wrap these in an object argument go ahead and pull those out starting price there we go and then down here we don't need form anymore we can get rid of any access to form don't need starting price from the form this should already be a number so we should just be able to like use whatever they pass in delete this delete this and then we want to store the file name or the key let just say file key is equal to file name because we are kind of using the same e that was passed in and this should be good we do need to update the schema again so let's go to schema let's go to items I'm going to say file key that's going to be a text that's not null it is required and of course every time you change your schema go down here you say mpm run DB push I should apply the changes um this will truncate the data because I said uh not all so I am just going to run the trunk gate it's fine we'll lose our data but now we can come in here we can actually just pass in the things that we had prior so we have like the name from the form we have the starting price which I think what we should be doing again is math. flooring this timesing up by 100 and there should be a Pars in um I think I have some type of syntax error somewhere so I think sometimes it's easier just like pull the stuff out okay just do that pull all this stuff out okay so let's try this again let's go ahead and say like Super Shoes 29 9 let's go ahead and select an image and I'll click post that should upload the image and then it should create the entry and now if we go back to drizzle and go to our items we should be able to see that now we have a file name somewhere I might have to do a hard Refresh on this here it is file name is BG of JPEG and so now on this page that should come back and we can use that file key to display the image right here where we are showing these cards right so let's try doing that let's go back to our main page and over here let's just go ahead and display an image uh it'll be a source pointed to item. file key do Alt with height there you go now this won't load because we actually need to get the location from the R2 bucket so I'm going to go ahead and make a function up here called like G image URL and that's going to take in a file key as a string and then we're going to return our bucket name which at this point we don't really have a URL so we're going to go to settings over here we're going to use the R2 Dev domain technically if you wanted to set up a custom domain you should probably do that because there are some limitations with this like custom bucket I'm going to grab this whole URL and then I'm going to click allow access and so now people should be able to kind of show these images inside their UI so again let's just go ahead and put this here for right now I'm going hardcode it and that is our bucket and now we're going to say file key here and just appin that key so hopefully this works I'm going to go ahead and just call that here so it is saying undefined so I'm wondering if I just need to restart nextjs remember whenever you do a migration for some reason and drizzle you have to restart your next server or else it can't seem to get the data back and map it properly some internal thing going on there so now it's saying invalid Source prop it's trying to access the image on this URL but you have to kind of give nextjs access to a subdomain so let's copy this subdomain let's go to next config and then over here we're going to say images and then I think we can say remote patterns or remote um I think it's remote patterns here it is and then we're going to go ahead and just say like host name is this what other options do we have path name okay we have port and protocol we'll say htps path name will be star we want to access any type of images from there and Port think we can just leave port blank let's try it again let's refresh and we should see the image show up hopefully okay I must have it misconfigured so let's go over here and try to figure this out uh for Port they leave leave it blank path name host name htps I mean this all seems fine add port in here all right so a little bit debugging let's try to figure out what's going on so we just set this bucket to public publicly accessible and and what we're trying to do is figure out like why can't we display this image so let's go back to the objects here I'm going to try to manually upload an object from my computer I want to see if maybe our application is just not correctly doing this um starter kit.png okay let's try that let's go to that URL I'll say starter kit PNG okay so that works awesome I'm going to take this URL I want to make sure that I can hardcode this into my page let's just return this entire URL for right now okay and go back to our app I get a path name maybe you don't need to put a path name there okay there you go I guess you don't need a path name um cool so this works it can show an image and remember this is coming from our bucket pretty awesome but I think there's an issue with the bg. jpeg so if I were to switch it back to the old approach so like this I think this won't show the image properly and I'm not sure why because also when I try to download the image from here it says that there's something wrong with the image so I think we're almost there we just got to figure out like what is going on when we try to upload this thing to our bucket so let's figure out where we're calling this so create upload action we pass in the key in the type and that should give us back a URL which we get back here but I think actually um you can just put the file here I think I'm just confusing some stuff so we don't need to get another form we're just going to go ahead and upload the file go back and try it again so I'm going to go to auction and item and then we're going to go ahead and just type in some stuff make sure we select that background click post item there you go so now it's working it's displaying those images correctly all right so we successfully uploaded and also displayed the images from the bucket we created a little bit of snags along the way but that's programming for you hopefully you learned a little bit from watching me try to debug this and then we could probably style this up a little bit like just make this look a little bit better cuz right now um doesn't look too good so we could probably go to this page here and somewhere we are displaying the cards let's go back to this page actually do a little little bit of cleanup this could probably come from an environment variable so let's do this and I'll call it bucket uh URL and this needs to be next public bucket URL like this fact let's just go ahead and put the https in front of it too and this is the first time that we've done like a public en environment variable so let's go here and then we're going to go into the client and we'll say next public bucket URL is equal to the string and then we're going to go ahead and just do that and so that we have it defined in an environment variable so if you're following along in your tutorial you can simply just reference it as yours is going to be different from mine more than likely and import that go ahead and delete some of this unused code and I would think this would be better in a utility function so let's just go ahead and cut this out let's go to lib uh actually no let's go to source and I'm going make a util then I'm going to make one called files files. TS go ahead and paste that in import this and now in case we need to use that anywhere else in our application we have a shared location we can import that from okay so sometimes in react you have these smaller components that you want to kind of componentize so like a good rule of thumb is if you see a map you could probably take this out and make it into a component so I can go over here and you can sometimes put it in a different file if you want to so like we could potentially make a card called uh item card. ttsx we're going to say export function item card we're going to return that jsx and then we're going to import whatever we need to make this work import that this needs to take in an item and again we have item that comes from the database so I don't think I've shown you how you can do this but if you go to schema when you have your table object defined you can actually export a type I can just call it item is equal to type of and I can say items dot infer select so now if you want to kind of use that same data structure in your typescript throughout your codebase you can just import that and it'll work pretty good let's take this item card and let's go back to our main page and let's go ahead and just kind of replace this with a key here the item. ID and then we'll pass in the item and all this stuff can just kind of go away and then make sure we import this okay so the code's a little bit cleaner now and now what this allows you to do is you have kind of a nice separation of like okay I just need to update the item card oh here it is it's all in this file and it's a little bit easier to maintain and now reason I did this is I wanted to put the title here it maybe in bold so here this could be like an H2 and we'll put the item name here and we're going to say class name text extra large font bold here we go probably also put some space y of two between these items and then over here we have the paragraph tag that describes what this is can go ahead and just say this is going to be text large and here we go let's go ahead and delete some of the stuff and bring it back if we end up needing it and then let's go ahead and commit so we just added the ability to upload to R2 I'm going to say adding ability to upload files to R2 all right let's try to add some more functionality so as a user if you were to create an auction like try to sell an item at some point people need to come in and bid on your item and so there's two ways we can go we could try to add the bid functionality or we could add a page where you can view your current items that are for auction let's start with the uh a page that shows like what items that you have put up for auction so I'll just go ahead and make another link here I'll say my auctions and then I'll just go ahead and say uh auctions I guess now let's go up here to the app directory I'm going to say auctions I want to make a page page. TSX and let's just go ahead and grab this I guess you know actually I think it'll look very similar to our main page let's grab this and I'm going to paste this in and I'll say my auction page okay so the idea is that if you click on my auctions it should take you to the page and it's going to show you only your auction items right now there's the main page that shows every single auction in our system but there's also my auctions which should just show you yours let's go ahead and fix up the styling a little bit we want to give this Flex item Center gap of four that they're spaced out maybe even more gap of eight and while we're at it we might as well just add a link for like everything but like let's just go here I'll say all auctions that'll just take us back to the the homepage uh I might rename this a crate auction there we go so now we can kind of navigate between these things okay so let's go back to the my auctions page what are we trying to do we want to only return the items that are associated with the currently logged in user okay so there is a way to do a query here with Drizzle we can do items um find mini and then we want to pass this a wear Clause so I'll say where and then here's how you can do it with Drizzle you pull in this EQ and then you say items dot remember items is that like database schema definition we want to find the items where the user ID is equal to user ID now we don't have user ID right now but we can get it if I say cons session is equal to await off and then I'm going to go ahead and say if there is no session then we should probably like return nothing or throw an error if there is no session. user should probably throw an error and I think I can just do this in one line hopefully then over here I can to say session. user. ID okay go ahead and Auto Import this and this is probably going to complain because it doesn't think that there's a user ID on that for some reason okay so now this is still going to show the same data as last time but if I were to log out and sign in as someone else I'm not sure why that just crashed I think in the header we do sign out what we probably need to do is give it a call back URL so call back uh let's go to sign out over here go to our sign out button and I believe we can just give this like a call back URL let's just do a redirect to and give it a slash because that wasn't supposed to crash our app when we signed out so let's test that out make sure we didn't break anything go back to my auctions click sign out and there we go we got redirected back to the homepage and we didn't have the app crash let's sign in to someone else in my other email click continue so now we have two users in our system and now if I go to my auctions you'll notice that nothing shows up for my own items now I think it'd be convenient if we had a button over here where someone could just create an auction if they don't have one I know we have create auction up here but it would be nice if they go to their items and like we have some type of placeholder state that says like hey you don't have any auctions um available so I like using undraw for like some not found illustrations let's just go ah and say like a 404 and let's see if there's any type of like not found illustration we can use they not found how about that all right let's search package maybe there's an image with like a package yeah let's just download this one looks pretty good so download the SVG and let's just put it in our project okay so now we have an SVG called package SVG and on any Pages where like a user doesn't have items I find it really beneficial to have like a empty State type of view so let's just go ahead and say like empty State PSX and then we're going to say export function empty State and then we're going to return a div that has that image that we just brought in so let's do image we'll say Source package SVG withth of like I don't know 200 ITA 200 Al could be a package okay and then we're going to go ahead and say H2 you have no auctions yet we'll add a button that's going to basically ask him create [Music] auction right we'll say as child and we're going to go ahead and just say link hre and then we're going to say items create I think it's the the path I cre created all right so now we can use this empty State object and we can put it here if there's no items let's just make a Boolean that says has items and if the length is greater than zero we can just go ahead and show uh this mapping otherwise we can show empty State like this let's just go ahead and pull this in and I'm going to put this in a fragment so it doesn't complain and then we should import State over here awesome this is the empty State placeholder I'm going to go ahead and just put this full screen we have no auction items yet so let's just style this a little bit in here I'm going to say class name we're going to say space y of 8 here we can say class name of text to Exel font bold and then for the button itself I think it should be fine now we do want to give this maybe Flex Flex column item Center okay maybe even ad justify Center as well let's see if this will pull it to the center of the screen oh I put this so notice that this is in a grid and it doesn't need to be in a grid so let's go ahead and move this out of the grid so I'm I'm actually going to pull this up one and then I'm going pull that down one over here prob going to screw up all this stuff get rid of that and there we go cool so now it says you have no auctions yet create an auctions to take you back to post an item and then instead of saing items for sale here let's just go ahead and say your Rand auctions save this and we could do some more styling to like Center this if you have nothing um there's some stuff I would probably clean up with this but I think it's a good start so what are we trying to do basically if I were to go here and create an auction for myself so shoes uh 199 and again let's just pick like go ah aad and grab some shoes and put them in here and I'll go ahead and upload those those shoes should pop up and if I go to my auctions you'll see that my shoe is right there and we also have all the different auctions that are currently happening so at this point it would be nice if we could actually allow people to bid on other people's items right so like let's add a button here where you can click on it and you can actually start bidding on this let's go to the card and then let's add a button here go ahead and import this and I'll say bid on item okay there we go and now the question I want to ask is should we go to a new page we probably should so let's go ahead and just make this a link as well say as child we'll say link you know instead of typing this all out by hand just go to empty State grab something we already have paste it in and then we'll say items we'll say Place bid here bring that in and we want to go to the actual item that we're clicking on so like we have to convert this ring interpolation and we'll say item. ID and that should be good enough so when we click Place bid it'll take us directly to the item which we don't have a route for this items 11 so we'll have to make a route we'll say items and then we're going to make a new folder called item id this is how you can do like ath parameters in xjs you do these brackets and then you can use that bracket what whatever is inside of it you can pull that out in your route and use it to do a query against your database so let's say page. TSX and I'm going to grab an existing change that to item I'll call this an item page and then we're going to go ahead and save this so I think on this page let's go ahead and try to get some of the same information displayed here like the picture of the item and also the name and stuff like that so on the item page page go over here we can get rid of the used client and we can just put an async here and we could fetch the information for this item so I'm going to say con item is equal to await and uh I think we can bring in database again let's just import database I I don't know why this isn't Auto importing uh for some reason my project but have to keep doing that manually for some reason and then we'll say database. query dot um items. find first and then we want items. ID is equal to an ID that we're going to get from here so in nextjs to get these props like this item ID from props you can go ahead and just do this and we can say Rams and then here we can say item id okay and that should be able allow us to do this and get the item id go ahead and Auto Import that as well okay and then we need to type this so I'm going to go ahead and just put a type here and then let co- pilot do the rest with me all right so that should get the item so that when we load this page we can actually display the name of the item um so now it's actually being displayed and there's a couple things we need to do to cast this so right now I think item id actually is a number so like we'll have to say pars int on that to make it a number and then I don't like how there's a chain here like an optional chaining I think I'd rather say if there is no item what we could do is we could return a different view so like I could just say um item not found just in case someone were to like type in the wrong item here it should say item not found at least it should have I'm not sure why it's not doing that right now oh I didn't return let's go ahead and return now it should say item not found and we could style this a little bit make make it look a little bit nicer um now one thing that you'll notice on all the pages is that like this is not really brought in like we should probably wrap this in a container so I'm going to go to the layout and just wrap everything for right now so wherever we render out these children for all the pages let's just wrap this in a container okay so that's just like every page will have like a default indentation on the left and the right we can also just give us some padding left and right too and so now like you go back to the main page I think we're doing that same thing over here so I'm going to get rid of that and then anywhere else we're doing stuff like on the crate page we don't need to keep copying and pasting this everywhere because it is applied on the layout and that'll just make the code a little bit more maintainable even the space y technically we could take that out but you might not want to apply this every single page go back over here do a class name I'll say text for Excel font fold there we go uh another thing that might be useful is that for all the H1s I have in all these different pages you'll notice that I'm using the same type of styles for them right so at some point when you start seeing that there's a pattern of like okay I'm doing this in every single page I would probably pull that out to like a Styles file so like you could put that in the app or the source directory I just say styles. yes and I'm going to go ahead and say export page title is equal to this string and just have a little bit tail innd there actually I'm going to pre suffix with Styles okay so then everywhere else in our code base just go ahead and import that style this is one approach the other approach is you can do a like a a global class and apply some tail one styles to that even the tail one docs recommends that you don't use applies for some reason the other approach is you can make a title component but I sometimes feel like that just causes more complexity the necessary so yeah let's just go ahead and just add this to all of our titles on our page add it there add it there by the way those who watch my tutorials you might feel like I'm jumping around but this is honestly like how I like the code as I'm kind of going through stuff I see where I can improve some things and I just improve it right so we could also do the same thing here go here I'll put that right there and now um we don't need this question mark because we know that if it's not defined we're going to go ahead and return this item not found um so let's put a paragraph tag here and I can say the item you are trying to view is invalid please go back and search for a different auction item okay so we'll do like a paragraph tag and then also we could do a button here um and again let's let's just find like where we do buttons and links somewhere else here we go good example let's go back over here and we're going to put that button save it awesome we'll let go back to the main items page I think is what we want unless we called it something else I don't remember I think it was called auctions maybe I'll just say like view auctions and then also we could probably have like a placeholder image like we do over here so it's a very similar approach that we do on like all the different like can't find placeholders we're going to do this [Music] um oh that's the wrong one I think somewhere else we had like a placeholder image didn't we like when something's loading feel like it was auctions oh here it is empty State you just grab that image okay so let's see item not found the item I'm trying to find is in bid please go back and search and you click view auctions it should take you back we should probably go back to the dashboard here so we'll go ahead and do the dashboard and all this we should give it like a space 8 y or sorry space Y8 we go give it a flex we could say items [Music] Center uh sorry Flex column as well there we go and then probably some padding top of 12 bring it down even more cool so now that's the whole reason doing that is if like you went to a bad item let's say someone bookmarked the link and then someone else deleted that auction item we want to show some type of graceful error State and not just let the page error out so definitely keep that in mind when you're building out all these Pages like what is the error state that could happen you want to just redirect the user or not give them any indication of what happened or do you want to actually show something like this and I would recommend probably showing something like this and I'm also going to put a break here so that these are on two different lines and then we could technically we could just say text Center here let's say text Center I'm not sure why that wasn't centering to begin with but that looks better okay so now if you click on on something that is defined it takes you to that item let's go back and let's focus on what we were originally trying to do we got kind of sidetracked but now the code is I think better and the application has a better user experience I'm going to say auction 4 and then we can say like this is the item name and we could technically wrap this in a span or something like this and we'll say class name font normal there we go auction for and then in bold it'll be the item that the person is trying to sell underneath this we could basically just display an image of what the item is and again like let's go back to the actual main page here and let's grab um the item card just grab this image this always good to reuse code instead of like retyping out the same thing over and over again so here's the image um let's just do the shoe one cuz that's better couple things we could do here is we make it larger so we could give this like a 400 by 400 we could probably round this so claps name um round extra large isn't it called round oh it's called rounded extra large there we go um probably don't want to put this in the H1 that was a mistake let's move it out we got the item here we got a picture of it and then what we could do is we could have some information about the item here and then the plan is over here we could have the bids like if people are bidding on this we could um have that a historical list of bids in the current bit at the very top maybe so over here we should probably put the item price so we'll say like make a div here we'll say starting yeah starting price of and we'll say item dot starting price here we go probably make this a little bit larger and also make this bold so class name bold save that and then we'll say class name X extra large here okay so it's coming along I mean there's obviously a lot of stuff we could do to slowly improve this and it does take time unless you have like a designer who's given you everything from the get-go the approach I would take if you're a web developer is just slowly iterate try to make it better try to think about like how would you restructure this to make it nicer so over on the right let's make a panel where people can start placing bids and so I'm going to go ahead and just pretend like we have a panel over there so the first thing we should probably do is get this to be split left and right over here I can give this whole thing um I'm wrapping all this in a div because this is like our left side panel then I'm going to wrap it in another div and then we'll say class name flex and that should push them left and right and I can give it a gap of eight and then over here we'll do a div and then it'll say like hello so now we have like if I Collapse this you'll see that we have all of this let me move this up we'll have this on the left and then the hello will be on the right okay so on the Hello probably do H2 and I'll say like current bids keep saying bigs let's do bids and then we could do an unorder list and then we could basically let's just prototype like we have some type of bids here so I'm going to save this and um we'll come up here and we'll say const bids is equal to I'll make an array and then we'll pretend like we have some data here so we could probably do something like this where we have a bids array it has how much the person bided on that item and it also has the username might just say username could probably even have like the user image too or we could have the user ID depends on if you want to normalize or denormalize your data so just for now I'll just say bids here okay every bid we're going to have a name we go oh we probably want a date too so I'll go ahead and say like uh time stamp we'll just do this we'll just put dates in here for right now and we could have those be displayed as well go ahead and copy this we don't want font bold here I'll just go ahead and say like time stamp now this might not render out because you'd have to like convert it so I'll say two ISO string right now but we're going to bring in like moment or some other Library where we can actually format these and make it look nice okay so now we have a list of bids and again this is like mocked out but we should connect this to the drizzle omm soon enough and have that work um so I do want to format this date so the one I like using is called Date FSN but there's a lot of different date libraries out there you could potentially use let's just copy date FSN or sorry FNS and go ahead and install that and then once you have it installed there is if you go to the documentation I think they tell you like some useful ways you can display like format distance yeah so I'll do this one go ahead and just grab this and I'm going to go ahead and just make a function up here called like format date I'll say function format time stamp we'll do this but I'm actually just going to call that format distance function which comes from date FNS and then we will pass [Music] in timestamp and then we'll also pass in the current dat so that's basically takes the difference between right now and then the time stamp that's on the record and then we can use that to get a date string and display it let's go down here and we want to wrap this instead of using ISO string we're going to go ahead and just do that and yeah let's go back to our app and let's see it says less than a minute ago I think if it's very old the if the bit is super old it'll probably be at the bottom we'll have to probably figure that out but let's continue to clean this up a little bit and make it look nice so current bids probably needs to be text of 2 XL we could font Bol it as well and then the space in between all this we probably need to increase so again we can go here on this right panel a space y of eight that'll move it down some maybe even six or four okay and then on this and give this a class name we could say space y of four as well that kind of put some space in between these and then over here like we need to put some space between the the date and the name so what I'm going to do is I'm going to move this into a div itself and then span here I'm going to convert to a div and then on this div itself I'll say class name Flex gap of four cool let's just continue on maybe we can um add some styling to this Li I could say BG gray of 100 let's see rounded XL let say padding of eight and that is what our bids are going to look like [Music] um I could type rounded correctly there we go and technically we can make this full width so if we sometimes like if stuff's not expanding full width get your little uh helper tool like this and we can hover over and try to figure out what is nonexpanding all the way and so it looks like it's the second thing here we need to probably add a flex grow to it so the current bids this right here you could say Flex one and that should expand it to the full width of whatever's left on the page now we got some auction for shoes the spacing here got really tight for some reason I'm not sure what happened to that probably because I wrapped it in a div so let's go back over here and this needs to be class name Flex Flex call Gap of six there add that spacing back all right so now we have like a scaffold of like the bid system right but like I said Remember the error States remember the empty States if someone were to create an auction from the get-go and there's no bid information on it what is this page going to look like well let's investigate that let's go here and I'm going to say comment all this out and then I'm going to say constant bids is equal to an empty array so that is what your page is going to look like when there's no data not the best experience at all like this looks pretty bad so again we could probably add some type of placeholder State and I'm going to go ahead and say like uh it's it's nice to have bullings here that says like has bids is equal to the length of that just so you don't have like all this stuff sprinkled to your jsx so I'm going to go down here I'm going to say if we have bids okay we'll just go ahead and do a Turner we're take all this UL stuff and we're going to go ahead and just put it there otherwise we're going to do a div here and we'll say no bids yet save that again like some placeholder image which I don't think we've made public so if we were to go over here to something where do we have the placeholder I think it was like on the auctions page here it is empty state where is that being defined it's over here so we should be able to bring in this image yeah let's just do that so I'll go over here I'll do that image and then I'll do an H2 now one thing that's good to keep in mind in terms of accessibility is that you need to keep track of which H1s and H2S are on the page you should only ever have one H1 on the page for H2S you should probably keep it to a minimum um but for screen readers people who are tabing through this when they first load the page it's going to go to an H1 and then as they step through it's going to go to the next level so H2 so logically it makes sense to have this be like an H2 in case someone's using a screen reader and trying to step through some stuff um but this isn't a a tutorial going to teach much about that but it's good to keep that in mind let's make this a flex column I'll say justify I'll say item Center um this we go and again we could probably like make this x of 2x font bold save that and then also we could say like gap of eight or something and then we have a button here that says like place a bid place a bid now the alignment here I feel like this could be maybe aligned a little bit better what we could probably do is just put this whole thing in a great background wrapper just to give it a nice like say BG 100 rounded extra large padding of 12 and I think that looks a little bit nicer otherwise it just looks kind of weird that like it was off-centered so when someone clicks this we want to create a bid we want to place a bid for them now some bid systems that I've seen is that they'll a starting price and then there will be a interval that the auctioner defines right so they could say the bids are by $5 or the bids are by $1 so I may actually try to add that in let's go back to the schema here and I'm going to go ahead and say like bid interval if that's proper I don't really do much auctions or bidding myself but we could potentially say that it's like a an interval of one now again this will be in sense so maybe we should do $1 and again that should be good let's just go ahead and run this I'll say mpm run DB push that should apply it to our database I'll click yes and um now we should have a bid interval that's attached to all of our data so if we go down to where we had the starting bid that's displayed what we could do is we could wrap this in a div and then we're going to have a d down a div down here that says bid interval and then I will make this a um interpolation item bid interval and we will go ahead and wrap this in a [Music] span so and then I'm going to pull this stuff out put it in there and then I could say space y4 and now the bit interval is not there because I don't know I think sometimes you do have to restart your next server whenever you do a Prisma or sorry ad drizzle migrations let's just go Ahad and restart that refresh the page and hopefully we should see the bid interval pop up now I forget did we do starting price in since as well I thought we thought we might have um I'm going to load up drizzle Studio real quick mpm run DB studio and then we're going to go look at our database real quick yeah so these are in sense I believe and so in the UI we're just playing $100 and really should be one $1 so I think a helper function somewhere um we could probably go back to we have like utils directory here and I'll say currency. TS and I'm going to say export function convert um since to dollar go ahead and take in a sense number and just do that maybe math. FL just in case um and I'll keep this Les ver Bose I'll say convert to dollar and then it takes in a sense argument so like that's the argument itself is self-explanatory of like what it takes in and now what we could do is wrap this stuff throughout our code base wherever we're displaying money we should probably display convert to dollar instead okay so now it's $1 and then you could also if you wanted to um change this I'm going to say format to dollar and then I'll say two fixed two and then I'm going to go ahead and just interpolate this to be a string so now instead of convert the dollar say format to Dollar that and now we have the decimal which is pretty nice um yeah so I think there's other places we were displaying that 100 so let me go back to all auctions and let's try to fix whever that was so remember on the auctions page we were doing this like divisible stuff in the item card I think right so now we want to say format $2 wait format doll wasn't that what it was called why is that not showing up and now it's showing up format the dollar okay so now that's going to be formatted um I think also under my auctions oh that's using the same card so we should be good technically we can come back and show like the bid interval I don't think showing the bid interval here would be as important I don't think um but let's go back to the auction page let's click on this shoe and again what are we trying to do we want to be able to allow users to place bids so if I were to click place a bid really all that should do is just add an entry or some type of record somewhere so we can keep track of what users have placed um what bids so let's try to implement that I don't think it should be too hard to do um but right before I do that I do want to commit because we've made quite a lot of changes and this is getting out of hand so let's go ahead and just commit everything and I will say formatting currency and working on the item page cool cool um yeah so how do you place a bid let's go find that place a bid uh button let's go over here and when someone clicks on this button what we want to do is we want to probably get their current user and just like insert a a record somewhere so if you remember we already have a bids table but we haven't really built much into it to it I think what we want to do now is link this to an items table and also keep track of the current bid so I'm going to say amount and we're going to keep track of integer here and then we're also going to say an item id but this thing needs to reference the other items so I'm going to copy user ID and I'm going to change it to item id like this that's going to reference items of ID and then I'm actually going to put this below I want items to be defined first and then I'm going to use it over here cool and then also the user who placed the bid like we should probably keep track of the user ID so again let's copy the user ID here paste that in and that should be good I think to think of anything else we might need yeah that should be good enough all right let's just go ahead and go back to here I'll say mpm run DB push that should create the new table and before I run I just want to double check I didn't screw anything up um because just easier to double check and do it right the first time than they have to like figure out a fix for your migrations but items users I mean it seems good to me just run it yes and it did fail why did this fail it says key colums Item ID and ID are incompatible and that is because I put text here this needs to be I think it needs to be serial I'm let's run this again and unfortunately I think I've already screwed this up so sometimes when you run a migration if you screw some columns up it'll create the table but then it doesn't create the constraints and so the the only fix I can figure out is you have to like blow away your database or you go back to your postrest Explorer and I'm going to go ahead and just write a new query and then I'm going to go ahead and say select the connection we'll do bid buddy and then we're going to go ahead and say delete or is it drop drop table bids I don't know if it's drop or delete uh it's BB bids try it again there we go so let's try this I dropped the table and now I should be able to rerun this hopefully everything worked fine so keep that in mind we are just doing DB pushes which is like a faster way to iterate locally um and then once you get a solidified migration plan you do like a migration generate okay so why do we do that we did that so we can keep track of the bids so go ahead and make a server action I'm going to make a file called actions. TS I'll say use server I'll say export a sync function create bid action and this needs to take in a item id which would be a number and they also probably it's an it's an action so we should know how what the user is so in order to get the user I think we've done this before we have to get the session so I can say session is equal to await off like this this is how you can get the session I believe and then if there is no session toine we should just throw an error I'll just go ahead and say Throw new error and then if there is no user again we'll just throw an error as well and then finally what we want to do is we want to create a bid so I'm going to say await database again I don't know why this is not importing we'll just do it manually I guess and then we'll say insert and then we're to say bids which is a table say values then we're going to go ahead and place in a bid um of some amount and an item id also probably user ID this needs to be session. user. ID right so a couple things here um it thinks that ID could potentially be undefined so uh I'm going to go go ahead and just say like if ID is not defined we might be able to put these all in one line and like make this a little cleaner I don't know why we have to check all these honestly this might be a next off thing um so although this will place a bid this isn't going to have the correct bid information right we have to increment the bid by a certain interval which means that we need to get the item using drizzle so I'm going to do a query. items. find first and I need to grab where the item matches the item id if there is no item we should probably throw an error go ahead and Auto Import EQ so that'll give us back the item but the amount that we place on the item needs to be the current item value the like [Music] um what is the current item value we don't know what the current item value is so let's take a step back we're going to go back to our schema we're going to go to the item and we're going to say current bid okay we're going to save that and we're going to apply that so that we can go over here and we can say item. current bid plus the bid interval so every time someone clicks on that button we want to take the the highest bid that was on the item add an interval to it and that is now the latest bid information okay so if we inserted this bid information we should probably go back and update the item so I'm going to say await DB update items and we want to set um current bid equal to that let's double check this is working so set the current bid equal to this where the item matches the item ID so we have duplicate code here I'm going to go ahead and say like um latest bid value is equal to this and then that's what we're going to use here now there's things we could do like we could wrap this in a promise all to make it fast more performant honestly I think it's fine for right now let's just keep it there's also probably some race conditions that we probably need to add in a transaction in case two people try to bid at the same time but we're not going to over complicate this again this is just a a tutorial to teach you some of the basics of nextjs and stuff that's more advanced but let's see if we can get this bid thing working so let's go over here I'm going to make a action actually what are we doing oh yeah we we made an action here which we need to call when we click the button so let's just go ahead and make a a form here and then I'm going to go ahead and just add an action here where we're going to call create bid action and then something that I haven't shown you yet is you actually bind so if you want to pass in that item id you can do item. ID here and that's actually going to just basically bind a new function that has this defined that when someone click clicks the button it's going to submit properly um still a couple things we want to do remember we have this hard coded to an empty array now instead what we want to do is we want to get all the bids that are associated with this item so we can go down here and we can actually do like constant bids is equal to a weit database query bids find many honestly I don't remember how to do an order buy so let's do order Buy and see what comes up for us um order by and then they do the table name and then they do oh sorry this is just the columns it also wrap in sending okay so let's go here and what they're saying we need to do is we'll say descending and we'll say bids dot ID now what makes this confusing is I'm going to go ahead and rename this to all bids because we have some like function overloading going on because we want this to come from the schema and I think this should be good over here call this all bids and some of these things what's missing here user ID yeah so before we thought there was going to be like a username on it now we're just storing a user ID we also have a time stamp so we have to go back and add a time stamp so now biggie go to the schema I'll just go ahead and say like Tim stamp we'll do this and then for username technically we could do like a SQL join and get the user image and name um so let's just go ahead and first run this there we go so now they should all have a time stamp on them so we can go back over here we can say Tim stamp and then for the username how do we get that well let's go back to where we're getting the bids and something that we haven't done yet is we can actually do joins so I can say with and then I can say um I don't think I can do anything yet because I actually have to create a relationship let's go back to the schema down here there's a way to define relationships in drizzle which we haven't done we need to relate items and bids together and we also need to relate bids with users so I think if I go to declaring relationships they'll tell you how to do it go here we're going to bring in a user relationship make sure we Auto Import that from drizzle and we want to define a relationship on bids and we want to say user is equal to a relationship to the user's table um Fields would be bids do user ID and that's going to reference users of ID does that make sense so basically we have to Define this thing and the moment you defined the relationships on the bids schema here you actually go back into your action uh where actually sorry the the page where we're calling with and now you'll see user pops up here so I'm going to say user true so as we fetch all these bids we also want to fetch all the user information with them um but just be careful because that's going to fetch back the emails too we really just want the image and the name okay so now all bids should have I look at it it should have like um bid. user.name and we should be able to use it a lot of stuff going on here let's see if this actually works go ahead and check this out I probably need to restart restart my nextjs server let's just do that and before I click place a bid there's something I forgot we need to revalidate the path after we've done this so I'm going to say revalidate path and then we want to revalidate the current item Item ID that we're on so that the page will refresh and download the latest bids there's some stuff that's missing here what's missing time stamp needs to be a new date I'll just do that and let's just try this out so if I click it let's try to figure out what's going on CU it's definitely not working so I'm going to go back to here and let's go go to bids there's a network error I don't have drizzle Studio running so let's go back over here say run drizzle Studio refresh this page this is all defined like we have bid information we have the item id of 13 the user who put the bid we have the timestamps that's working but for some reason it's still displaying no bids probably have something hardcoded to like nothing um so for a little bit debugging session let's conso log all bids here conso log all bids and then we're going to go to our next app and notice that it's still giving back an Mt array for all of our bids so we got to figure out why that's happening why is that happening first of all let's try to console log what is the item ID that we're trying to query for looks like it's a string of 13 um which seems like it's the correct item id okay and we are getting back an item otherwise this would would not be one so why is this not finding anything order by bids of ID where oh I think I need to do this bids of item ID I had the wear Claus incorrect make sure that we're actually looking at the correct column otherwise obviously you won't get back the information now we're getting back info see the stuff coming back we have all the information coming back now we see that it's showing up properly in our UI pretty cool and it's also in the right order I believe uh have to figure that out so we're missing place a bit here um or we could probably place it here cuz if you think about it if there's a going to be a lot of bids we probably want the user to be able to find the button quick we put it at the bottom of this list they might have to scroll down just to be able to place a bid not the best so let's find where we're doing that placeholder which was like here and then we're going to copy this and we're going to put it at the very top next to current bids so what I could do is wrap this in a flex they justify between go ahead and put that there now we got to place bid button now if you click it notice that it does place a bid and it did interval it did increment the interval by a dollar now again we have to format these money because that is not the right format here so let's go ahead and say format wrap the amount there we go so every time someone clicks it going to place a bid and that's going to be at the top of the list um also we should probably have the current price be displayed here so let's find where we have like starting price displayed and we're going to make another one called like current did go ahead and say dot current bid and that should be $5 every time we click this that should increment we got six S 8 n and now you can scroll down and see all the bids that are happening on this which is pretty cool now before we get to ah header ourselves there are some things that I want to do in terms of code cleanup I like having a separate data access layer I don't like having our components know about the OM that we're using so I would make a new folder here called Data access and inside of this I would just put something called bids bid. TS and I'm going to go ahead and just take some of this code out and I'm going to say export async function get bids or item this will take an item ID and I want to paste in all that code there like this and just return all bids make sure I can like Auto Import some of the stuff so like I need to import that I need to import Q descending Biz from the schema file this should already be a number so I don't need the parts the number again and now we can actually just call the data access layer from our component so that we kind of abstract the way like what are we using to fetch the data should your react components know that you're using drizzle my opinion is no they shouldn't know so let's just go ahead and do that we already have the item ID so we can just fetch it and technically you could do a join too like if you wanted to go over here and make this be a a you could do a join and get the bids and then for the bids you could do another join that actually might be more performant but we're going to keep this simple I'm just going to slowly make some better changes so I'll say items will be another one we'll go ahead and just copy our bids bid access layer going to cut this out go ahead and paste this in turn the item and I'm going to say get item by the item ID go ahead and Auto Import that there we go so again let's go back to our page and I'm going to say con item is equal to a weit that bars in item id go through here delete any unused Imports and now we don't have any traces of drizzle anywhere in this component which I think is a much cleaner approach it just allows you to maintain your application a little bit better because it's as your project gets larger and larger larger it gets a lot more complex to maintain and having these separations of concerns and these different three tier layers very useful in my opinion let's make sure this works I'm going to refresh the page and now we get all the bids I can still Place bids awesome so good work good work I'm going to go ahead and commit this I'm going to say ability to Place bids so there's still a lot we could add into this for example like at some point the bid has to close so we'd have to have like a an indate for the bid which we can add in another thing I want to add in is the ability to get notified if I start bidding on an auction I want a notification the moment someone out bids me and this is where we're going to bring in knock which is a really powerful notification system that hooks directly into your application to allow you to send notifications to other users and have those kind of show up for you automatically so so let me sign in and then this is the knock dashboard so this is where you can start configuring different things messages you can create users Etc but what we want to do is we want to set up our application to work with KNC so the first thing we need to do is get some API keys and you'll see here we have a secret key and a public key and also KNC has a built-in notification uh component library for nextjs so if you go to the documentation you'll see that they have a knock laabs SL react component library that you can start using to display a really nice notification panel with a notification widget um let's go ahead and try to uh install this I'm going goe and just install this over here get four tabs going and install that and now you can kind of just use it like this so the first thing we have to do is we're going to have to wrap all of our stuff with a knock provider and a knock feed provider so let's just go ahead and grab this now technically I can grab all this um that should be good let's just just uh we have any providers I don't think we even have providers do we let me go to layout real quick okay so we don't even have any providers so I'm actually going to grab all this code and then I'm going to make a new file here called providers. TSX I'm going paste this in and I'm going to say providers here and we're going to bring in this use ref and use State technically for the layout I'm actually going to move this up a level to the layout here and then we need to define a couple things so first of all API key this has to be a public key I believe so let's actually put it over in the client so I'll say next public knock API key like this what else do we need we need a feed ID so I'm going to say next public feed ID to that and then we're going to go ahead and bring those two in okay so now that we have those we're going to have to refactor this a little bit user. EnV library that we have and for user ID we have to get that from the session so again this is a client component or we should make it one and then we're going to have to go over here and I'll say export function providers uh go ahead and just refactor this a little bit we want to take this in with children okay and then we're going to go ahead and render out Children here maybe we can do it there and then here we'll prefix this with next public what else are we missing so user ID again we have next off set up so we can get the session by saying use session and then we can pass in the user ID by saying session data. user. ID okay now technically if this isn't defined um we could probably just render out the children so like this is what you want if you are logged in and like wrap everything with the knock provider and stuff otherwise I'm going to say if there is no session or there is no uh session. data. user like that let me do this I'm just going to go ahead and return the children like this data is also probably undefined let's do that that you know let's just check the ID too everything we'll check everything and this will allow us to hopefully only do this whole knock provider stuff once the user ID is set otherwise we'll just show the children like normal so now that we have the provider here let's go to our layout which is over here and I'm going to wrap all this stuff here so I'll say be providers go ahead and put that in there import that there we have it so last step is in the file we have to bring in the two things we just added so we added in the knock public API key and then we also added in the knock feed ID I might actually prefix this like this bear with me I'm just going to fix this up real quick I want this to say knock in it so I know all right so now let's go back to our dashboard which was this one we have the public key let's just go ahead and grab it and I'll put it right here and for the feed ID we'd have to make a new one so let's go ahead and go to Integrations and you'll notice over here if you click on Create channel you can create multiple things so like you can make a channel for sending on emails you can make a channel for sending out push notifications for doing chat directly the slack or Discord pretty cool what we want for right now is the ability to send off notifications inside of our application for other users to get uh a little notification Bell icon right so I'm going to click on inapp knock I'm going to click next I'm going to say bid buddy is the name of it go ahead and create that and now we have a channel ID here which we need to grab so let's go ahead and copy that channel ID and then I'm going to go and put it right here for feed ID okay so hopefully now if everything is configured correctly we can go up to our application and it says use session must be wrapped in a session provider so I do think there's something that we forgot to configure with next off I think the error told us what to do so if we just look at the error we need in a session provider so again if we just go over here and like I think we could probably just wrap this whole thing in a session provider okay so here's a tricky part we need use session so like so actually I'm going to go to the layout and I'm actually just going to wrap all this um in a session provider so that when we do the knock provider it has access to that so there could be some refactoring and renaming here but we're just going to go with it so now the App application is loading just fine um what I want to do though is I want to put a bell icon right here in the header so let's point out real quick we have a notification icon button that's inside of this providers but we should probably put these in the header itself so I'm going to cut these out let's go to our header I'm going to put these right here and then also I think the state variables that we have I'm going to cut those out I'm going to put them here as well and then I'm going to put use client at the top of this header like this and I think this needs to be a use session now instead of an await import that don't do off because o is only for Server render code and now we come over here we can import this and this needs to be data user.name okay doing some clean up clean that up a little bit you know at this point let's just clean everything up I'm going to call this a knock provider and then we're going to go over here and call this a knock um app knock provider like this go back to the layout call this app knock provider and uh go ahead and clean that up okay so if we go back to our app let's try to figure out it is not allowed to Define inline use server so I think there's something wrong with our header so unfortunately I think our signin and sign out buttons were written in a way to like not work with client components so one thing we could do is we could just put a button here and this will be our sign out button but instead we'll say onclick and I think we can just call a button that says sign out from next off react and I think you can pass this a call back URL to be that then we could do the same thing with sign in hopefully we'll say sign in and this will call sign in and then these two buttons can go away and hopefully that fixes our issue almost I think we got to get rid of the async here okay U session must be wrapped in a session provider so again the header there's something that we're doing wrong in the layout and that is because I didn't wrap it in the session provider like I was supposed to there was a little bug with this logic here basically when nextjs runs like on the server use session will never be defined with the value and so like we want to make sure that we always have the KN provider passed in and instead we should probably like fall back on I know nothing for the user ID and that should hopefully work now all right so as you can see in the application everything's working and we have this Bell icon and when you click it you get this nice drop down that has all the notifications that you'd see for a user you can actually filter by unread and red notifications and Mark everything as red so the next steps are let's try to log in as someone else and let's also place a bid and when you place a bid on an item we want to notify everyone who's also attached to this auction that someone has out bidded you let's go back to the page for the bidding okay and let's go to the and let's go to the actions over here and what we want to do is we want to send off a notification like I said send notifications to everyone everyone else on this item who has placed a bid we want to let people know that like hey someone out bided you so they have an opportunity to come in and actually like you know Place Another bid so how do we do that well first of all we need to bring in the NOC JavaScript API and secondly we need to bring in an API key so let's go back to API Keys here I'm going to grab this secret key because we're going to need that as well so let's go back to our EnV file and we're going to say knock secret key and then we're going to go ahead and just import that from process EnV and then in ourv file we'll say knock secret key paste that in so now we're going to bring in a library called knjs node so let's go ahead and install knock laab node now that that's installed we can go ahead and import it like so let's go and paste this in and then we're going to say EnV dot make sure we import this knock secret key and now we have a JavaScript library that we can use to send off notifications so we're going to grab this knock object and we want to basically get all the bids that currently exist on the item so I'm going to go ahead and say con bids is equal to a weit database query bids find Min we want to get where the bid is equal to the item ID that we're currently looking at okay so now that we have all the bids what we're going to do is we're going to Loop Over All them and I'm going to push them to a user ID's list so let's start this off with the empt and I'll say for um const bid of current bids and then I want to say if the current user ID does not exist in that user ID's list uh also yeah so if I'm going to say if the current user ID is not equal to the one that's in the bid then we're going to push it but I also want to check to make sure that user ID does not already have um the the user that we're trying to push in so this should give us a unique list of IDs and then what we could do is we going say a wait knock do notification or notify I mean and we're going to say user placed bid and then we want to paste in some things so for example we want actor and this will be the current user ID who's sending off the notification and then we want the recipients which would be all the user IDs and then for data we could just put a message here saying that um yeah we'll do Item ID and then bid amount okay so now when someone places a bid that should send a notification from that user to all the other different users who are on that item and that should show notifications in their uh dashboard now the actor may need to change a little bit we may need to say like this is going to be a system generated notification but it might make it more personal if it did come from like another user um now I'm going to make one more tweak because with knock in order to send off notifications the user you have to first identify them so this is how you can do it one off so like if a user first registers into your system you could potentially send this off but over time a user May update their user information and so you can do it with uh inlining which is what we're going to try right here so instead of just passing an array of user IDs we're going to actually pass it a structure that looks like this so we're going to go ahead and say user id. map and then we want to return data structure that has ID which will be user ID and then we need to get the name and the email of that user which at this point we don't have right now in the bids because we'd have to say with and then we'll say user true and now with every bid will have the information so we have to kind of keep track of that as we're pushing that into the array instead of just pushing the user ID here let's push in the ID and then we'll pass in the name bid. user.name and then also the email like this go ahead and save that I'm also going to change this to be called recipient so that it's more straightforward what we're doing here and then I'm going to try to find the recipient whose user ID matches the bid. user ID and we want to make sure that this doesn't come back with anything um and then also let's add a type to this let's save that ID name email recipient is an array of that um ID name and email so if this isn't defined I'll just say like anonymous um that and for ID this just needs to be casted to a string I think me do that I'll make that a string um and this will be called recipients all right hopefully this works I think it should be fine so we're basically making an array of recipients we Loop through them make sure that we only push unique recipients to that list and also recipients that aren't the current recipient and then we send it off so I'm going to sign out and I'm going to sign back into someone else all right so after signing out um I do notice that the sign out button is still saying sign out oh you know what um it's because session is always going to be defined I'm dumb so I think what we could do instead if like I could just say if there is no user ID like I think this is the issue if there's a user ID show the sign out if there's not show sign in there we go now show sign in and now that I think about it there's some additional issues with the headers like we shouldn't be showing these links if the user is signed out so I'm going to go back to the headers here and the ones that we care about showing would be like if there's a user ID then we can show these and I think I need to wrap this in a fragment there we go um and then for placing the bid I guess technically you could still go there if you're not signed in but there's some things we need to hide like this place a bid button should not show unless you're actually logged in so place a bid let's find that and let's wrap that in a conditional as well and yeah this whole thing here needs to be if there's a user ID okay let's do that I think I'm also showing that button somewhere else here yeah like here and then we're going to go up here and say const user ID to a wait off technically this will be session session is declared but value is never read yeah so instead we don't need to check user ID just make sure they're logged in do they have a session they do we'll say place a bit and show that button okay so now the button's hidden and also if you were to go to like another item that has no bids there's no button there great if you sign in now which is what we originally trying to do let's sign in with a different user let's go back to all of our auctions let's go back to shoes and we should be seeing the Bell icon so I screwed that up somehow let's go back and figure out how to get that Bell icon showing back up over here we're saying if there is a user ID then display the icons and the popovers but for some reason those are not displaying I'm guessing I have user ID incorrectly defined so I'm going to go ahead and say console log user ID and then we'll go and look at the console for some reason that is undefined all the way through there for some reason um so if we were to instead just onog what session is what does session look like session as session. dat. user and there's no ID on here so I think the issue that we're seeing here is that in next off some things are not going to be attach to your session by default so you actually need to add a call back that you can Define the user ID so let's go back to configuration um do they have callbacks here let's go down to with database and they give you a call back example so let's copy all this code and uh I'm going to paste that in and the reason I do this is they're trying to protect the client from leaking any information that it shouldn't see so you have to manually Define the ID here that you can use it in the front and once you've done that I think what you need to do in the front end is right now like if you look at session User it's potentially undefined and so I think we can extend the types here so I think they might have a way to extend types um let's do types script let's see all right so here's an example how you can declare an extension of the types go to nextjs though and we're going to do this put that right there make sure you import default session and this needs to be ID which will be a string I'm trying to see if there's anything special we have to do to use it I don't think so okay so hopefully now we can do session. dat user Dot and now ID will always be defined which is what we want so I'm I apologize for that that's just something I forgot about but now we should be able to use that ID as like a knowing when the hide and show stuff let's go back to our app here we go the the ID is showing up now great so another thing that we have to do is we have to create an actual workflow so user Place bid this is a custom workflow that we haven't created yet so let's go back to knock and we're going to go to our dashboard and we need to go to workflows and we're going to create another one here I'm going to call this user Place bid and then I'll say a user the user placed bid and then we're going to go ahead and just click create and so once we're here what we can do is basically from our node app we can trigger this workflow and we can Define knock to do different things so for example if I click on this trigger um we want to go and say add first step and what we want to do is we want to go down to the inapp channels which is something we defined earlier I'm going to say I want to send off an event to other people in my app let's just go ahead and select the bid buddy channel here okay and now the way knock works is like you can make changes in this UI but you actually need to commit them for them to take place so I'm going go and say commit to development and you'll see some differences in the diffs and they use this internal Json to kind of keep track of your workflow let's just go ahead and commit it and now this workflow has been committed I should be able to go back and if I go to my UI I should be able to send out these notifications to other users let's go back over here I'm going to do a refresh over here and we want to basically um wrap this I want to say if res c.length is greater than zero then we should probably do this otherwise we don't want to send off notifications to nobody okay so let's test this out I'm logged in as um you know I'm not really sure who I'm logged in as I have so many accounts that have my name so I think what we should do is probably either change this the email for this demo or display an icon so actually I'm going to go to the header real quick and where we display name I'm also going to display an image of the user's icon so I'm going to say image and they I'll say source is equal to session. data. user. image okay and this could be a width of 50 height of 50 I'll say alt is user Avatar inside here I can delete that I can make the self closing and I'll save this and then if it's not defined I will just go ahead and not display it so let's just go ahead and wrap this whole thing and only display it if it is defined okay so now we're getting an issue with nextjs where you actually have to give it permission to download images from a third party URL I'm going to go over to our next config and we already have remote patterns here but we need to add one for this Google image location so let say protocol htps port and then what else do we want yeah we'll keep path theme not defined so refresh the page hopefully we should see an icon pop up in the top right okay that took a while but now it's showing up and then we could probably make this a little bit smaller then we could say class name rounded full that should make it round technically I could probably bring in a shadan avatar component but that's probably good enough so now I can sign out and sign back in as different people so for example let's sign back in as this other email account here we go and I'm going to start fresh I'm going to just create a brand new auction because I think this is just getting a little bit too uh complicated and I'll say like t-shirt started at 2.99 and then we're going to find a picture of a t-shirt okay go and upload that okay so now we have a t-shirt let's go ahead and start um bidding on this now one thing I will say is you shouldn't be able to place a bid on your own item I feel like that's kind of like inside trading so again for this bid page if you are the owner of this we should probably hide place a bid let's go ahead and go back to that bid page and somewhere here instead of just having session as our conditional I'm going to make a consonant here that says can place bid and I'm going to say if session is defined and the current user ID of the auction item is not equal to the current sessions user ID because like like I said you don't want people to be able to outbid other people on their own items that's kind of weird let's go ahead and say can place bid in place of that one and then also in place of this one I'm going to hide that uh button for people who actually created this let's sign out and I'm going to sign back in as someone else who should be able to place a bit on that t-shirt go to here go to place bit on the T-shirt I'm going to place a bid and that that place the original bid but that didn't send off any notifications because we have logic to not send it off to our own user so let's sign out again let's sign in as different email let's go here place a bid go ahead and click it I think I might be doing this wrong I think this might be workflows dot trigger and then that's how you can trigger the actual workflow that we created uh same information we can pass and recipients and data okay so now it's getting an error saying that the actor in The workflow is missing okay so it says you can also inline identify actors the same way so like technically this if I just paste an ID here and then I said name I mean that would just come from the session right session user.name and email would be session. user. email okay so that should hopefully be able to work and then I'm GNA say Anonymous if they're not defined and then you do have to say collection users I think so let's let's try this again I'm going to go back to app go and start fresh click place a bid and there we go that created a bid and I want to show you if I go back to knock I want to go back to my messages and we should see that we sent a message on the bid buddy Channel and this is the message that we sent out so if you look down here it says that we sent out a bid amount for $4 the item ID is 14 and that sent from this user and who did it send to it sent it to the web dev Cod user okay so now if I were to log out and log back in as webd code user hopefully we should see a notification pop up for that user over here so let's go ahead click this over here and now we see one notification Bell icon con Popa if I click it it says Cody cybert completed an activity and you can click it to read the message you can also go here and just delete the notification archive it and again all these components are built out of the box using knjs which is pretty nice because I didn't really have to do much work to have a built-in notification system now one thing you might have noticed in the notification dropdown was there's no message so if you want to customize what's displayed in that cell you can go head and add this to your notification feed so let's go back to the header and let's find the feed and then in for render item we're going to go ahead and just make our own custom jsx component here so we can just go ahead and say like hello world just to test it out and see what it looks like go back to our app and again I'm just going to place a bti on the same t-shirt um I'm going to place a bit as this user and I'm going to log it back out and log in as a different user here back to the t-shirt and a notification icon popped up here if I click this notice that it says hello world so you can actually customize what shows up here as you'd want and if we were to actually like console log this item um I think we should have some information on this so like data you probably get the item id render that out and see what we get so now we got 14 which is pretty nice because now whatever we put in the message inside of a knock notification we can display that over here if we want to so that's pretty nice because now what we can do is we could simply put a link here I can go ahead and put a link and then I'm going to go ahead and whoops and do this we're going to use that item ID and we're going to put an HRA to slash items slash item id like this and I'll just go ahead and say someone out bitted you let's go back and click this now we have a link we can click it and it'll take you directly to that item so actually if I were to go over here to my all auctions and click it it takes you back to this page now I think we would have to close it if you manually were to click this like we would have to basically say onclick and then we need to manually close this like this so let's try it again I'm going to click on this someone out bitted you takes you to the page and you could also show the information about whose Avatar um out bited you and stuff you can style this let's just go here and just try to make this a little bit custom so BG gray 100 rounded XEL and then I'll say padding of eight okay and we could technically also add some like margin I think to this or if you wanted to actually wrap this in a div like this and put the padding on this itself ping of eight there we go someone out bitted you you can click on this it'll take you to the link we could also probably get um the users name here it' probably be nice if we put the name of the thing in the notification as well so like back to where we did the actions for creating the bid again you can put whatever you want here so let's just go ahead and put like the item name and we can say item. name just so there's a little bit more information and then I'm going to go ahead and just put that on the notification itself we'll say span we'll say class name text of bold and then I'll say item. item. dat. item name now right now it won't be defined because we haven't sent a new notification yet but if I were to go ahead and just click on this and sign back out as somebody else come back in someone out bidded you on t-shirt now why is this not bolded text bold oh I think it's font bold and then we could also do like uh item to item. dat do um what do we call it bid amount so here I'll just go ahead and say like that buy put a money sign and I think this will show it in yeah this is going to show it in sense so I have to say format format $2 there we go so that's how you can completely customize it but they do provide you in this Library a notification cell so instead of if you don't want to like completely override how knock gives you that component you can actually just use the notification cell I believe and let's kind of see what this looks like I think you need to pass it the item here um actually what does it say we need to do we need to give it props and items let's just go ahead and do this say props and then I think this doesn't need to be like doubled that I let's see how this looks like over here there you have it now it say says hey Cody Cody cyber completing an activity you can still archive it but now you have your custom thing that you want to display here so if you didn't want to do all this extra padding stuff which we probably shouldn't do anymore now that I figured out um the notification cell you could just put your custom message here probably get rid of the background gray like that and you can click on it to read it but then you can actually click on the item to go to it just like that and you can technically you could give this whole thing a class name and say no text blue of 400 hover text blue of 500 all right a lot of changes let's just go and commit what we have so I'm I'm going to go ahead and say like custom notification feed and I'll commit that the one thing that's important when it comes to auctions is is we need the ability for the user to create an IND date and we're going to keep this simple and just keep it to a date but to add in date time it's a little bit more complicated especially since Shad scene doesn't seem like it has a built-in component for date time or time stamps so we'll just keep it at um a date picker for right now so if you read through this it says the date picker is built off of the popover in the calendar so after you go and install both of those you can simply just copy the date picker component and we're going to go back down here and I think I already installed them but over here I'll just say like date picker ESX I'll paste in all that example code and then we should be able to use this date picker component uh somewhere application so like let's just go ahead and copy this we'll go to create and we're going to try to use the date picker somewhere here so I'm going to go down here paste it in Auto Import that and let's just at least look at what it looks like I'm going go to create auction here's a date picker we can pick whatever date we want the auction to end and then we get back May 16th 2024 pretty cool um but we're going to have to get that information and send it back to a higher level variable to keep track of it because right now it's in the state here so I think what we need to do is let this out and put this at a higher level so that we can pass it in so I'll say like date and then set date and this can take in some props of date set date that technically this would be like a dispatch date something like that there we go react State action I'm going to make sure date is always def okay so now what we could do is go back here we need to make sure that date is actually defined I'll just go and say like new date when this component first is created and then let's just make sure it says today's date May 13th cool and then when you change it and submit this form we want to send that to the back in so we can track it right right now I don't think we're sending that in so let's just go and say like in date and then we'll just go ahead and pass in that data object and then we need to make sure that in date is accepted here so I'll say end date and then indate the date object and then we're going to have to persist that so let's again say in date which means we need to go to our schema go to our items and we'll say in date is equal to timestamp that looks like this now again when we run this migration I think it's going to truncate all of our tables because basically adding a new column that's required so I'm going to truncate it we'll start fresh and then we'll go back over here and see what is this complaining about um it doesn't like the idea that we have dispatch set State action I think this just needs to say or undefined Maybe maybe I'll do this hold on I'm going to go back and make some of this stuff optional yeah for right now I'm going to say if there is no in dat um set so if date I'm just going to return keep it simple get this working um yeah so now if we go into uh drizzle actually I probably should restart this just in case let's go ahead and refresh this page just in case we're going to go go ahead and type in an item the shoes say $3 find some shoes pick a date of the 17th I'll click post item that should submit the item and then we could probably display the end date somewhere here on this so like let's go to the card and now we should have access to use date FNS to format when this thing is going to end um so let's go ahead and just copy this say ends on then we want to say item dot in dat let's just try passing format here and see what this output so I'm going to say format and then we can import that from day FSN I'm sorry date FNS and what is this complaining about it says it needs more arguments so today is a e so let's just try e and see what that prints out over here ends on Friday so maybe that works for your project needs maybe it works for ours I don't know ins on Friday we should probably actually put like a real date um it's like we could probably do like month day year use YY instead of capital y y okay there we go in zon 517 that I'm going to do lowercase M too I think I can just do one m and that'll just show a five instead of there you go cool so now you can see when bids are going to end and technically we could have it say that um if it's past a certain date then it's over like we could probably like disable this button or we could just say like uh ended here so let's go ahead and try doing that I'll just say function is Bid over and that's going to take in a item this and we can just go ahead and see if today's date is currently greater than the end date than the bid is over so we can just go ahead and say if the bid is over then we're going to go and display this otherwise we'll display the normal paragraph tag like we had before um I'll say bidding is over how do we verify this let's go to drizzle Studio let's go to items and this should have an indate on it now and I can actually just change this to 12 and let's refresh and now it says bidding is over instead of saying Place bid what we could do is just say view so just kind of copy the same logic here or what we could do yeah just copy it and then I'm going to say um yeah so instead of say Place bid here I'm just going to say is Bid over passet the item same thing do a tary and then I'll just say like view bid otherwise Place bid then we can also change the variant so again uh let's go here we'll say variant and is Bid over pass in the item if it's over we'll do primary I think it's default let's do outline and then we'll say default if it's not over okay that's what it looks like if the bid's over you can still click it and then you can get into the bid page but if it's still running changes back to 17 and refresh the page you'll see that it says Place bid and now secondly we can come back to this page and probably display uh our hide those buttons If the bid is over let's try the same thing um I'm going to go ahead and sign into someone else because this person is the one who uploaded the shoes go back and sign into to this one go to place bid and then instead of saying Place bid we should probably just not show those buttons at all um which means that we should probably move this to a utility go to utils and I'll say like bids export function is Bid over import the item schema and we're going to import that one instead of defining it in the file file and then let's copy this and let's go back to the place bid item that we saw other words other places and in fact we have a can place bid Boolean so we could probably just attach some additional logic here and then I'll say if the item.in date is less than new date then we should be able to place the bid otherwise we shouldn't be able to uh other around if the NAD is greater than the date there so now they show up and it'll probably be nice to say like auction ended somewhere so the user is not like looking at this page thinking they could still place a bid so maybe even underneath the title or after um the the title here let's go here I think we could probably add a shadan badge let's go here what a badge looks like I'm going to go ahead and just add one in this and let's look at how you can use it um you just basically import it like that and I'm going to show a badge here that says like um over bid bidding over import this one what variance do we have destructive okay and let's look at the page so if the bidding is over you'll see bidding over but what we want to do is I'm going to put this on a new line and then I want to give this a class name of w bit and then I only want to show it if the bidding is over so I'll go up here I'm going to say cost is bidding over is equal to um is bidding over item might as well bring that utility where's that utility that we made utils bids is Bid over yeah so I'll just do this if the bidding is not over we can just use that utility here but then down here we'll say only show that if the bidding is over cool so now I can place a bid on this item and then down the road if this bidding were to end let's just go ahead and put it in a an older date it says bidding over and those buttons no longer show and additionally what we should do is the logic in the action itself we should probably make that check the bidding is over or not because even if you have buttons that are hidden someone could still hit your actions I think and try to bid on something so let's go here and after we get the item I'm going to say if is Bid over passed the item then again we're going to throw an error this auction is already over and don't allow someone to try to bid on it again so the very last thing I want to do is with the notification and knock they do have the ability to send out emails so if I were to go to Integrations here I could add an integration to for example rein that's something I like using on my side projects and so you can go ahead and add in resin for sending out emails and in order to get this working you have to set up some configuration so I'm logged into resend and I'm just going to use an existing account I have just to kind of show you um but you can paste in your API key here I'm going to paste in my API key that I already have and then from email I'm going to say testing WDC starter kit.com that's a another domain I already have set up but if you had your own domain you would set that up as well um from name will say testing uh you could also add all these things and if you wanted to I'm not going to let's just go and say update settings hopefully this will work and we're going to go ahead and go back to that workflow that we originally created where it says user place a bid and using the editor here I'm going to go ahead and just say um how do I do this I'm going to click this okay so if I just click on the background here you can go add a step and then notice that we have resend now so I'm going to go and click resend and not only will that send a notification to my users who are using the app it'll also send out emails to my users and you can edit the message template so let's just go here I'm going to change the subject line to user out bitted you and then we want to bring in Block quote I guess and if you were to edit this you'll see that you have access to like recipient so you can say like recipient name and you can pipe it to some stuff to split it and like try to get the first name from that that's pretty cool but I'm going to say someone has out bitted you and then I'll click done I do want to save my changes before I lose them and then for I'm going to add a link so let's just go here and let's just add in an HTML link here and let's figure out if I can com edit this I'll say HRA is equal to http Local Host 3000 items 15 then I'll say like check out that auction save that again so now someone were to click this if I go to preview and click on this link it takes you directly to the bid obviously you'd have to replace that with a real domain name in the future but that works pretty well and I think there's more ways you can get information like that's just one of them recipient but I think you can see here you have Avatar you have ID email stuff like that looks like you also have access to data so I can say data Dot and then whatever you put in I think item id potentially would show up let's just save that and see what happens and then we're going to go back to the workflow so now we're going to commit it you'll see all these changes here I'll go ahead and say commit to the deployment and then I'm going to try to bid again let's just go here I'm going to place a bid and hopefully that should send off an email to my other email account in fact I don't know if anyone else has bid on this item other than myself so let's go and place a bid I think this is the account who created it so I need to sign in with a different account I don't think it matters who so I'm just going to go ahead and place a bid on this one place a bid so now if I go to my email notice that I got an email directly from KNC using my resend API and I can click on this and it takes me to the bid so it's super easy and knock to just like integrate with emails or slack or other thirdparty services when you need to send out different types of notifications when things happen in your system and that is very powerful and I actually like that a lot so that's pretty cool check that out so quick recap of the knock dashboard I think some of the stuff is important to cover the most important for this tutorial was ations that's how you define third party Services you might be integrating with and also defining how you're going to be integrating to your inapp notifications and that's what we kind of did in this bid buddy tutorial there are workflows where you define a workflow in when you send a notification to that workflow you can have it do a bunch of different stuff so in my case I had it trigger an inapp notification also send out emails using the resend API you can come in here you can view messages from all of your applications so like here I saw that it did send out a recent email but I also got some inapp notifications and you can dive into those and just get more information about who the event was sent by who the event was sent to what data was attached to it you can go into your users and manage those individually so you'll see I have three users registered some of these I think I could delete like I think this is for a different um application let's just delete those you have this commits tab where you can go back and see how things have changed over time so like over here you can see as I change templates and I change workflows that's all kind of maintained in a nice commit history so you can kind of go back in time and revert to that commit if you were to accidentally break something which is actually really powerful and I wish a lot of other third party Services had this type of stuff baked in to their configuration management because when you do a lot of Click Ops sometimes you mess stuff up and you break production because of just a simple misconfiguration so this is nice and once you've done all your changes you can actually commit these to a production configuration environment and you can point to that so it's kind of nice that you can roll out these configuration changes after you've tested them and then promote them to a production environment we did a custom email when we did stuff but you can also just set up some default emails when notifications are sent to users analytics if you want to view how many messages by what type are being sent you know daily in your application that can help give you a better insight into what features are being used and then some developer configuration obviously you have API Keys you have some logs web hooks so if you need to integrate your application with knock you could probably have knock send you web hook notifications when a message is seen archived Etc a lot of power in that where you can actually do different stuff depending on what your users do your events and then once you have everything set up development you can go to production and you can actually get this stuff deployed out so definitely go check out knock I think this is a really really cool notification service that you can hook into your own applications if you want to notify users when stuff happens in your app again I just want to thank KNC for sponsoring this video and allowing me to be able to make a tutorial for you all and like always all the code for this tutorial can be found at webd cbid buudy on GitHub and that link will be in the description so definitely go check it out clone it and try it for yourself if you want to see a nice template of how to get a nextjs application work with Shaden uh drizzle om with postgress and then knock for our notifications thanks for watching have a good day and happy coding
Info
Channel: Web Dev Cody
Views: 16,650
Rating: undefined out of 5
Keywords: web development, programming, coding, code, learn to code, tutorial, software engineering
Id: xF2WvGuI5Ww
Channel Id: undefined
Length: 212min 23sec (12743 seconds)
Published: Mon May 20 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.