Now let's implement the delete functionality. So let's go inside of our projects API that is located inside of app, API, route, projects and let's go ahead and let's chain a .delete slash id like this. So as always we only want verified users to visit this endpoint. Let's add a z validator here which will check for param and it will do a very simple Z object ID string and then we have an asynchronous controller right here. So as usually let's go ahead and let's copy this from our id duplicate because it is exactly the same here and then let's go ahead and do const data await database and we're going to chain dot delete and we have to specify from projects, of course, where, and then we're gonna have and with two equals inside.
The first one will be project's ID to match ID and the second one project's user ID to match auth token ID. Like this. And then let's also add dot returning. If data dot length is zero, we are gonna return C dot Jason with an error, unauthorized. And let's add a not unauthorized, my apologies, not found.
That's the one we are looking for. Other than that, let's go ahead and let's return, well, let's very simply just return the ID. Like this. We don't even need to return the entire deleted data. We can just return back the item, the ID of the item which was deleted.
And for that we can honestly just use the ID from the params. Great! Now let's go ahead and let's duplicate our useDuplicate method. So that's inside of the features projects. So copy and paste the useDuplicate project and rename this one to use delete project.
I'm gonna go ahead and zoom out and now I'm gonna go ahead and let me go ahead and remove the duplicate here and we are simply calling the delete method. So that's the one we are looking for. ID stays the same but we are looking for the delete method. Like this. So client API projects ID delete.
In here we are looking for 200 and in here for the request type we are still only focusing on the params here. Great. Let's go ahead and call this use delete project. Let's go ahead and change this to remove the duplicate method and directly call the delete method and we still pass in the param and let's go ahead and say fail to delete project and inside of here we're going to invalidate queries for the projects but also for individual project which has that id and we don't have the id so what we can do here is very simply destructure the data from here because remember inside of our projects here, we are going to return back the ID which we need to re-invalidate. So we can use the data in the onSuccess here to define that ID is data.id.
Like this. And in the error here we can say fail to delete project. Great, so this should now be enough. Just make sure that inside of the mutation function you're actually calling the delete method because you will not get any errors if you are still, if it is unchanged from the duplicate one because the param is exactly the same. So there are no TypeScript errors to be thrown here.
So just double check that you're using the delete methods here. Excellent. And now let's go inside of our project section here. And what I want to do is I want to add const removeMutation to be useDeleteProject or deleteMutation, however you want to call it. Let's go ahead and add it here.
And now that we have the remove mutation, let's go ahead and let's actually add it here. So const onDelete accepts an ID, which is a string. And let's simply call the removeMutation.mutate and pass in the ID. Like this. Now let's go ahead and let's use this onDelete right here where we have the trash icon.
So I'm gonna go ahead and use the onDelete and pass in project.id and it will be disabled if our remove mutation is pending. Let's go ahead and try this out now. So I'm going to choose a random project. Let me just refresh first for good luck And I'm going to click on delete. And there we go.
My projects are now getting deleted. So the last one is this. And let's just delete a few more. Until I'm no longer getting the load more. There we go.
Even after I refresh, they stay deleted. So what I want to add now is a confirmation because this delete has no undo so let's go ahead and implement a confirmation dialog and the way I want to do it is by creating a use confirm hook so that we can programmatically call that dialogue wherever we need to confirm a method. So first of all, let's go inside of the terminal and let's add bunx-chat-cmui-at-latest-add-dialogue. That's the component we need at the moment. Let's do bun run dev again.
Refresh your localhost if you've shut down the app like I did, and let's go inside of source. And inside of the root of our application let's create hooks so these are going to be completely reusable hooks that don't belong to any feature and we're gonna create a useConfirm.tsx hooks make sure it is .tsx because we're gonna write a component inside as well. So let's go ahead and let's import useState from React here. Let's go ahead and let's import button from components UI button and let's also add everything we need from the dialog which is dialog, dialog content, dialog description, footer, header and title from components UI dialog. And now let's export const useConfirm And let's define the types for the use confirm here.
So we're gonna have the title of the confirmation model, which will be a string. For the second parameter, we're gonna have the message, which will also be a string, and it's going to further explain what the confirmation is about. And our return method will be an array. And the array itself will have in the first argument, a method which returns back a JSX element. And in the second, it returns a promise, which can be unknown.
And now let's go ahead and fulfill this query. So first of all let's define the promise and set promise to be useState and by default let's make it null like this and now let's go ahead and give this useState the proper type for the promise so we can open the pointy brackets and we can add a resolve and let's see can I collapse this so it's more readable? So the result of itself will have a value, which is a Boolean, and it's gonna return a void. So it can be that, or it can be null, like this. So however you find it easier to type this, right?
You can also write it all in one line like this. Great. And now that we've defined that, let's add a confirm method. The confirm method will call a new promise. The promise itself will have a resolve and reject and let's go ahead and simply set promise and add the resolve.
Now let's define the handle close method Don't worry we're gonna go through this again once it's functional. It's easier to explain it that way of how it works. Basically we are creating a promise so that we can await the opening of the model. So before I call the onDelete method I'm going to await the confirm method, right? And depending on whether the user clicks in the model confirm or deny, that's what I'm going to return back to the await method.
So it's either going to be okay, the user confirmed this action, or false, the user did not confirm that method. So that's why we are creating this part. And now we're just gonna go ahead and reset the promise to null if user decides to close the model. And if user decides to confirm the model we're gonna go ahead and call the promise and we're going to resolve it with true as value and then we're gonna go ahead and handle close and let's go ahead and add a handle cancel so this will also call the promise and resolve it with false and then call handle close as well and now let's add the actual confirmation dialogue make sure you add both make sure you name this with a capital C in the beginning because it will be used as a JSX element So we can immediately return this Let's go ahead and add a dialogue. The dialogue will be open if promise is not null.
Let's add a dialogue content inside, a dialog header, a dialog title, and inside we're gonna have the title from our params. Below that we have a dialog description so inside render the message prop and then outside of the dialog header inside of the dialog footer which will have a class name padding top of 2 like this we're going to render two buttons one will be cancel and the other one will be confirm this one will have an onclick handle confirm and this one will have an on click handle cancel and it will also have a variant of outline and then let's return an array of confirmation dialog and the confirm method. There we go. So if you've done everything correctly, you should no longer have any errors in your types. Now we have this very useful use confirm hook, which is going to open this dialog and wait for us to click cancel or confirm before it returns back an awaited promise in a form of true or false depending on whether the user confirmed or not.
So now let's go ahead and use this in action. So let's go back to our projects section right here and let's go ahead and add this. So I'm going to go ahead and add const confirm dialog and the confirm method use confirm from hooks use confirm in the first argument here we're gonna add are you sure and in the second you are about to delete this project as further definition of what is going to happen. Make sure you've added the hooks use confirm here. And now, for example, in the on delete, we can turn this into an asynchronous method.
And we can block the execution of this by adding const OK await confirm. And then if OK and only then do we execute the remove mutation. And in order for this to work, we also have to render the confirm dialogue simply anywhere inside of here. Just make sure that it is in the final return right here and not in any other. So let's go ahead and simply add a confirm dialogue here.
It's going to be portaled so you don't have to worry. Let's go ahead and try it out. I'm now going to go ahead and try and remove this method and there we go. Are you sure? So if I click cancel, nothing happens.
But if I click delete, it gets deleted. So this is why right here we need drop down menu model false. If you try and remove this, if you are simply interested in what happens, let me refresh this, What happens after I click cancel is, well, not always, but what happened to me is that the entire page gets locked. It gets frozen and you can't use it. And Sometimes I could reproduce it, sometimes I couldn't.
But ultimately what fixed it is adding model folds to the drop-down menu here. So make sure you have that whenever you're opening models with drop-down menu items. Great! Amazing job! So you just finished the actions for these projects here.
So now that we have all of these advanced ways of controlling our projects, it's gonna be much easier for us to finally create some projects, right? Which we are then going to go in the database and mark them as a template. And then once we've marked them as a template, we're going to create the API endpoint which will look for those and render them in the design tab here and then we have to create a functionality to load them into the canvas. Great, great job!