So now that we finished our list header I want to go ahead and add a little options here on the right side so that when we click here it opens a little popover and allows us to you know add a new card to this list, delete this list or copy this list. In order to do that let's make sure we are in the list header component which we just wrapped up and after this if else clause all the way here at the bottom let's add list options component. Of course if you save you're going to get an error because it doesn't exist. But let's already prepare and let's give it a data of data like that. And let's also give it onAddCard.
And for now, Let's just make this an empty function. We don't have the form to add a card, so there's nothing we can do here, but I just want to make sure we don't forget about this functionality. And now inside of this underscore components folder, create a new file listoptions.tsx. Go ahead and mark this as useClient and export const listOptions and return a div listOptions. And now let's quickly create an interface listOptionsProps and let's define the data to be list from Prisma Client and onAddCard let's just make it an empty void and now in here let's go ahead and assign those props to list options props and let's destructure the data and on add card.
Great! Let's go back to the list header and now we can import this list options from .slash list options like this and you should no longer be having any errors and now in your card on the right side you should have the text which says list options and we're now going to modify it so it's actually well a drop down menu so first thing I want to do is I want to go ahead and import everything we need from add slash components UI popover. So let's go ahead and import the popover itself. Let's import the popover content, the popover trigger and the popover close. And now inside of here I want to replace the div with the popover component and inside I want to add a popover trigger and I want to render a button component from add slash components UI button so just make sure you add this as well and let's mark this as child and let's give this button a class name of h-auto w-auto and padding 2 and a variant of ghost and now let's go ahead and let's add more horizontal from Lucid React so just make sure you have this import from Lucid React more horizontal and let's give it a class name of H-4 and W-4 and now you should have a nice little button here which will open the popover content which we are going to create in a minute.
So below the popover trigger let's add the popover content here and let's give it a class name of Px0, padding top 3 and padding bottom of 3. Let's give it a side of bottom and a line of start. Great! Inside of the popover content now let's add a div which is going to say list actions. And let's give this div a class name of text small, font medium, text center, text neutral 600 and padding bottom of 4.
And now you can already try and click on one of these and you should have a nice list actions popover. So now we're going to go ahead and actually add some items here. So before we do that let's add our popover close here and let's go ahead and add a button inside and let's render the X from Lucid React so make sure you add the X here. Alright, let's give this X a class name of h-4 and w-4 and let's give this button a class name of h-auto, w-auto, padding to top, sorry, absolute, top to, right to, and text-neutral-600 and the variant of ghost. And let's also just go ahead and give this a prop as child to this popover close.
And now our popover should be having a close button here which works. Great! And now we are ready to actually render some actions inside. So below this popover close add a button component and write add card like this. And now let's go ahead and give this an on click on add card which is as you remember an empty function for now and let's write a class name here rounded-none w-full h-auto padding to PX5 justify start font normal text small and give it a variant of ghost and let's check that out now and there we go we have a nice button which says add a card.
Perfect! So besides add a card we're gonna have two more options one is to copy a list and the other one is to delete a list. So just below this button let's actually open a little form element here. So there are obviously multiple ways we can do this. We can directly add a button, right?
And then we can use our use action hook and just call the execute on click or we can use the form, right? So this server actions have enabled us to do a lot more. So let's go ahead and give this an input inside which is a hidden, has a name of ID an id of id and value of data id. And let's copy and paste it and let's replace this one to have a name of board id and this one board id as well and value is going to be data.board.id and inside we're just going to render the form submit which we imported from add slash components form, form submit and inside let's render copy list like this and now what we have to do is give this form submit a variant of ghost and we can just actually copy the class name from this button which has add a card so you can just paste it here and let's try that out now so when I open there we go we now have a copy list which is going to trigger a form if you click now it's just going to refresh the entire page but later we're going to add an action to the form which is going to properly execute what it needs to execute and since we are using the form submit option this is automatically going to be disabled while it is loading.
Perfect! And now let's go ahead and let's add a little separator after we end this form. So separator which you can import from s slash components UI separator so just confirm that this is your import and not the radix one and let's go ahead and just copy and paste the entire form like this and just paste it below the separator. And we can keep the ID and the board ID and this one is not going to say copy list but delete this list. Like that and let's try it out now.
And when we click here as you can see we have options to add a card, copy the list or delete the list. What we have to do now is create these two actions, copy list and delete this list. So the easier one of the two options to do first is the delete list, especially because we already have a function to delete the board. So let's go ahead and let's copy the server action for deleting the board so we have delete board go ahead and copy it and let's rename it to delete list. And first let's go inside of the schema to define everything we need for it.
So we need the ID but we also need the board ID. So make sure you add that and let's rename this to delete list. Now let's go inside of the types and let's change this to delete list as well and let's change this to be z infer type of delete list and the same is for our return type we are expecting a list and not a board. Now we can go back inside of index.cs for the delete list. And first let's fix this wrong import.
So now it is delete list, and we can copy that and paste it all the way here at the bottom and change this from delete board to be delete list. Great. And now let's go ahead and modify this. So this can of course stay the same and from the data we now destructure the ID and board ID. And we are working with list not board so let's define let list here and let's assign it here and let's go ahead and attempt to delete a list so db.list.delete where we have the matching id we have the matching board ID and also the board relation has the matching organization ID as our current user.
So no foul play can be done here. We can leave the fail to delete here and let's change this to revalidate the board and the board ID. And instead of redirecting let's return data list. Just like that. Perfect.
And we can remove the redirect import from here. Now that we have this finished let's go ahead and let's actually use it inside of our list options. So let's find list options as you can see it's located in platform dashboard board board id underscore components we have list options here and let's go ahead and let's import everything we need here so we need the import use action from hooks use action and we also need the delete list from actions delete list so make sure you have use action and delete list here Now let's go ahead outside of the return function and let's call the execute and let's remap it to executeDelete because we're gonna have multiple executes here in this component. Let's call the useAction here. Let's pass in the deleteList and let's add some callbacks so onSuccess.
We're gonna get the data and let's go ahead and call toast which we can import from Sonar so make sure you have the toast which I will add here there we go so let's call toast.success and let's go ahead and open backticks and write list data.title deleted like that and let's add onError so in case we have a server error here let's do toast.error and log that error perfect And now let's go ahead and let's create a function const on delete to get the form data which is a type of form data. Let's get the id to be form data get id as string, copy and paste this and replace it with board ID for the second one. And then execute delete ID and board ID. Great, And now let's go ahead and let's add this on delete as the form action for our last form here which says delete this list. So in this form give it an option action on delete and let's now try it out here.
So I'm going to refresh. I'll pick this last list. I will click delete this list. There we go. It's pending and it says list has been deleted.
Perfect. One thing that can happen though is that this popover stays open after we delete something. I've seen it happen a couple of times and it will certainly be visible once we do the copy list. So I already want to prepare for that by creating a ref for the close button and then programmatically calling it. So let's go ahead and add const closeRef to be useRef from react elementRef again from react button and give it default value of null so let me just confirm that you have this imports useRef and ElementRef from React and I will import them right here.
So ElementRef and UseRef. Now let's go ahead and assign the CloseRef here to the Popover Close component where we provided the AsChild prop and we have the button with the X icon inside and let's go ahead and give it a ref close ref and then inside of our onSuccess right here we can call the close ref.current? Click and right now I think nothing should change everything should still work as we expect but we just ensure that that popover always gets closed great what I want to do next is create the server action for copying a specific list. So we can just copy this delete list server action which we just created. So let's go into actions, delete list and let's copy it and let's rename it to copy list like that.
Let's first resolve the schema which we need so that actually stays exactly the same but let's just rename it to copy list like that. Now let's go inside of our types and Let's fix the schema so it is copy list and let's properly assign it for our input type here. The return type is correct because we are going to be expecting a list. And now let's go inside of the index.ts here and let's change the copy list here as well. Go all the way to the bottom and place it here and rename the function to copy list.
Great! And now let's go ahead and actually copy the list. So this can of course stay the same. We are also extracting the ID and board ID we are defining the list here and now we're just going to do things slightly different in the try block. So first I want to find the exact list we are trying to copy so const list to copy is await db list find unique where id is matching board id is matching and the board relation has the matching organization id as our current user outside of the where object let's also add include cards true because when we copy a list we also want to copy all the cards, right?
So let's go ahead and check if there is no list to copy in that case return an error list not found because we cannot proceed further and then let's go ahead and let's see which one is the last list in this board because when we copy a list we need to give it a proper order position So let's write const lastList to be await dbListFindFirst where we have a matching board id order by the order property in this sending mode and let's just select the order that is all we need and make sure you put order true. Great and now let's write const new order to be last list question mark last list dot order plus one or just one and now we can finally create a new list so list is equal to await DB list create data board ID is list to copy dot board ID title is gonna be be OpenBactics the original title from list2copy.title but we're going to append a little copy annotation so we know it's copied and order is going to be the new order and Then we're gonna also create all of the cards So let's go ahead and open the cards object create many here and pass in the data To be list to copy dot cards dot map get the individual card from here open an immediate object and return the title to be card.title.
Let's also add a description to be card.description and order to be card.order. Great! And as you can see the object fully matches what we expect and let's also return include cards true at the end. Great And now let's go ahead and inside of this instead of failed to delete we're gonna say failed to copy. Perfect!
And the revalidated path is correct and the return data is correct as well. Great! Now let's go back inside of our list options component where we just added the executed elite and we can just copy and paste the entire thing here and this time let's rename the execute to be execute copy and let's use the copy list from at slash actions copy list which we just created so I'm just going to move it here alongside the delete list so make sure you have the copy list imported and now let's go ahead and let's write list data.title copied and we can leave the close ref to be closed onError should yield onError, that is alright and now as we did with onDelete, let's copy and paste it here and let's just call onCopy this time We also extract the ID and board ID and let's just call the proper execution so execute copy. Great and now let's go ahead and assign this on copy to this first form where we have the copy list text so give it an action on copy. Perfect and now let's try this out so I'm gonna refresh my page I will create an original list here and then I'm gonna go ahead and click copy list and let's see if that is working and it is working perfect exactly what we wanted great great job but we're gonna do next is we're gonna create a form to create cards or tasks in an individual list.
And then we're finally going to go and learn how to reorder these using drag and drop.