 So welcome everyone, again, to the web app workshop. This is day two. I'm just going to quickly mention what we're going to do today. First, we will review the exercises from yesterday just to show you the solution. Austin will go over the, yeah, the code box, code sandbox project. And then we'll have a little break. I will just give an introduction or an overview of how to build generic DHS2 applications. We will take a break. And then this time, we will split the second part of the presentations. So first, Austin will give a presentation on translations. We will have an exercise, a very simple exercise on translations. Then we'll take a little break. And then we will talk about the data store. And same thing, we will have an exercise for the data store. And then we'll close for the day. So this is what I have now. I think we can get started with the review of the exercises from yesterday. And if you have any questions also from any of the things that we covered yesterday, this is the time to do so. So I'll stop sharing. I don't know, Austin, are you ready? Sure. Oh, thank you. Hi, everyone. Welcome. Maybe you grew up in the window here. So you should be able to see a window with the Workshop 2 project here. We're going to go through the exercise that we worked on yesterday. So there is a solution that's been committed to this repository as well. So you can go ahead and look at that if you want to see the final solution. But we're going to go ahead and do it together quickly as well. So I'm going to start with this exercises. I'm sorry. So the code sandbox is actually if you go back. Yeah, there we go. If you go back to the folder. Yeah. Yes. Yep. So just opening up the code sandbox here. And you'll see that it's running the app platform to build the server in the code sandbox, which is kind of cool that it's running completely in the browser. But you get a full terminal environment. You can interact with the application in any way you'd want. One thing that I did see some people having some trouble with yesterday or looking at yesterday was some issues with logging in. And it can be kind of difficult sometimes when you have a very small screen over here that you're trying to work on code and also this demo on the right side. To one tip for code sandbox is if in the top right corner here you can click on open a new window. When you do that, it will open this just the actual running code in a new window that you can use for testing and debugging. And when I do that, now I can make this a lot smaller and focus just on the code here on the left side. So I'm going to go ahead and fork the sandbox actually before I do anything else. Let me see where I do this. Fork sandbox. So this will create a new, I think it might have been. I'm not sure, but I think it was already forked, apologies. But basically this is creating a copy for me so that I can create a new version and then it gives me a unique URL for that as well. I think we've been through this on some of the other projects. So I'm going to go ahead and look at the code here. So I'm just sort of exploring. There are some comments that say exactly what we need to do in each of these files. But I'm going to start with the app.js file because that's going to be the beginning of our application. I can see that it's rendering just a program list, as Kai said yesterday when he was demonstrating this. So I'm going to look at the program list file which has a program list component and a number of different to-dos items in this component itself. So there's three in this file that I'm seeing. I'm going to start with the first one and we'll go ahead and get started with that. So I'm going to open this in a new window again. So I had the other one open, but that was the other fork so that I can test here. And now I'm going to scoot this over so that I have lots of room to show what's going on. So here we have to convert a static query into a dynamic one. We have a static query here that is basically hard-coded page size of two. And we want to be able to make this dynamic so that it comes the number of programs that are returned is dynamic based on what's going on in the application. In order to do that, this is a static query now. We have to make it a dynamic query. In order to do that, we turn the params field in this query into a function. So you can do this as the easiest way or the most succinct way, I guess, is to do this as an arrow function. So I'm going to say page size, which is going to take that as a variable. And then this is actually the body of the function. So we don't want this to be the body of the function. We want it to be a return value. So I can actually just do this. And this will return an object from this function that has order, page size, and page values in it. A little bit more verbose way of doing the same exact thing is to have a body of this function and say return an object. So this is maybe a little bit more verbose, a little bit easier. Sorry, that should be return. A little bit easier to read or understand potentially. So that's another way to write the same thing. And you could have some logic in this function as well if you wanted to. And finally, just to make this absolutely clear, you could actually call this a function as well. So it does not necessarily need to be an arrow function, though there are some advantages to using an arrow function in this situation. I'm going to go ahead and keep it an arrow function for now. So we now have this page size variable that's being passed to this function. This will come from two different places, potentially. One is in useDataQuery itself. We can pass the initial variables that come from, for instance, properties or props that are passed as programs list. Or we could also pass new variables to this query when we call refetch. For now, we're going to do just passing the initial variables on load of this component. But before that, I'm not actually using this variable yet. So I'm going to change this to actually use the page size. So I'm going to be very explicit here. I'm going to say page size is coming from these variables that are passed to this params function. And then the solution here is we need to basically pass variables to this hook. In the object options object, which is the second argument to the useDataQuery hook, we have a variables parameter that we can pass. And we can go ahead and say page size is going to be 5. So this is going to be a solution to that task 1. So we've done everything that it says here. We've converted this static query into a dynamic one, changing the page size to dynamically be updated. And we have defined a variables key in the options object to be able to render the first five programs. So if I go ahead and save this, I'm going to reload this. And actually, I'm going to get the Academy instance. And I'll log into that. Oops, and I have a problem. I have using Brave and I have ShieldsUp, which is problematic. It doesn't let me have cookies across sites. So if I turn off my Shields in Brave, I now have this list. And you can see that I have five programs listed. This may not seem like it's, well, actually changes to three. And we can see that that changes, oops, should change to three. Now we have three programs listed. This may not seem super interesting when you're hard coding the value here rather than here. But what would happen if we had count or something as the variable that's passed to the program list function? We can then pass that as the initial variable. And what this will do is count right now is undefined. But we can define it back in this app.js file as another prop. So we can say count equals 10. And now we're going to see that this, we don't need to change anything in this program list file in order to change the number of programs that are returned. So if I refresh this page, you should see a list of 10. Now if I go back to this and I change this to three or five again, that's the result that we wanted. We see that we have five. So that's the first task on this list. I'm going to go through the rest of them fairly quickly as well. So we're going to now add an alert when a program has been created using the Use Alert hook. There are actually two different ways to do this. So there's actually two to-dos here. I'm going to use the one mechanism first and then the other one second. So I have actually here, I'm going to import. So first I'm going to, oh, it's already imported. But this Use Alert hook comes from the app runtime dependency. And I'm going to initialize that and get back a show function equals Use Alert. And then I can go ahead and write a message. So the message can be a string. And I can say Program Created. Exhibition point. So this is very simple, very straightforward, just as it creates an alert that has a program created message. It returns a function called show, which we can call when we want this alert to actually show up. And we're going to do that here. So we're calling the refetch function, which returns a promise. And then I can do then and say show. And I could actually rename this. I often like to rename this. So I could say show success alert or something like that so that it has a little bit of a better name. And I'm going to do that here, show success alert. So now if I save this, refresh this. Actually, I think I'm missing something. No, maybe I'm not. I'm going to create a new program, a new program. A new program so that it shows up at the top. Click Add. It shows up here at the top. And we see this program created alert down here at the bottom. So that is what we just added. That basically waits until the program has been created. And then it creates this alert that shows up. By default, that alert shows up for eight seconds. You can change that if you would like. When they use alert hook, you can pass some options to the second object here. One of those is duration. And we can say duration is 2,000 milliseconds. Means it will show up for two seconds. This one already exists. That's why. Oh, we haven't added the delete. So then we have this program created. Lasts for two seconds. And then it should go away. So this should work for two seconds. But that wasn't part of this program exercise anyway. I do want to show another way of doing this, which is, so right now we're manually calling this show success alert after we call refetch, which maybe isn't exactly what we want to do. We could also have done, because this refetch happens after the show success, or after the creation, which happens in the ad program, we could actually say show success alert asynchronously or in parallel with the refetch. The refetch takes some time, but we don't really need to wait for the refetch to finish before we show success alert. So we could do it like this. But there's one other way that we could do this. It might be a little bit cleaner. And that would be to move it into the ad program. So if we go to our ad program here and we have this mutate, we have this onCreate function. We could actually move the alert into this component so that the onComplete of this mutation does the alert for us. So we don't actually need to know anything about what this ad program component is doing in the program list. And that would be another way of solving this problem. But we have this done now in two different ways in the program list component. So let's go ahead and move on to to do number three. So I'm going to go ahead and get rid of these to dos for one and two. And now I'm going to look at to do three. So to do three says pass props to the delete program component, you only need to pass two props, ID and refetch function. So let's just take a look at this first. So I can go to definition of this delete program. I assume that's going to work in this code sandbox. Maybe it's not. I'm just going to go to the delete program function because it seems to be faster. It's still loading here. I'm not sure why. Here we have a delete program, which needs to take a couple props. So we need the ID and the refetch, as it said before. And we need to complete and uncomment the onClick function below to be able to delete the ID. The then method in JavaScript. Yeah, using the then method in JavaScript, which will do something after the successful completion of our promise. So we're going to go ahead and take our two props here. We're going to say ID and refetch. Save that. Going to go ahead and go back to the programs list and pass those here, as we saw, ID equals. And let's see what we have here. We want the ID of this particular program that we're looking at. So that's going to be prog.id. And we also want the refetch function. So we're going to say refetch equals refetch. Refetch is coming from up here on the use data query. So basically we want to refetch the list after we've deleted a program. So now we have this delete program component, which takes an ID and a refetch function. The mutate is passing, is using a mutation, which isn't defined yet. So we're going to need to define the delete mutation. Let's go ahead and do that. So our resource for this mutation is going to be programs. Our type is going to be delete. And then we need an ID. But we don't know the ID. This is another case of the dynamic query or mutation. We don't know the ID when we're writing this code. So we need to take this as a variable. So we're going to take an ID variable and we're going to return that ID variable when we generate this mutation. So this is going to delete the program with ID that we pass in. In order to do that, we're going to pass the variables. As we mentioned before, you could do this here with variables ID. But for a mutation, that doesn't make as much sense because we want to actually tell the mutation which program to delete when we're calling it. And so when we do this mutate function, we can pass an ID there as well. I'm going to go ahead and uncomment this. So this is our on click function, which we can add down here to the button. So on click equals on click. And you'll see that there's an empty object that's passed the mutate right now. That's the variables that will be passed for this mutation. So we want our ID to be passed here. So our ID should be the one that we get from our props. This on click function is recreated every time we render with new props. So that should be fine. And it says, yeah, it's using the then function to, or method to refetch the entire list of programs after this mutation completes. So that should be okay. Let's go ahead and save this and let's see what happens. So we've got a couple programs that we added here that we don't really want anymore. I'm going to go ahead and delete that. And you'll see that it not only did it show a loading spinner and deactivate the, or disable the delete button when I clicked that, but it also refetched this entire list without that program that we just deleted as soon as it was completed. So let's go ahead and do that again. And we've now deleted a couple of programs in from this list using the tools that we just used. I believe that's it. I think that's all of the tasks that we had. Let's go ahead and double check that in the readme. We have task one, task two, and task three. And I believe we've completed all of those successfully.