So now let's go ahead and let's actually iterate over our lists here. So we don't only have this button but we also have our newly created lists. So in order to do that let's go ahead and let's visit our actual board page. So go ahead inside of the app platform dashboard and you should have the board here and inside of board id we have page.csx and in here we actually load the lists and all of the lists cards but we don't actually go over them anywhere. So go inside the list container right here, which we created and let's go ahead and let's store this data inside of a state.
So const data, set data or actually we can call it orderedData and setOrderedData and go and write useState data. So the reason I'm doing this is because when we implement drag and drop we're going to need to have a kind of a source of truth which we are going to modify locally before we do it in the database. We could directly do it in the database and skip this local part but then it would kind of have a bad user experience because with drag and drop, it's very important that we have a working optimistic mutation. Usually optimistic mutation is only used, you know, for specific cases where you kind of want to improve the experience, like when you like something, but it's not the end of the world if you don't do optimistic updates when you like something, right? But when it comes to drag and drop, it's a really bad experience to have to wait for a second, and then, you know, your content switches back to where you dragged it from and then after a second it only updates.
So this way we're going to use optimistic update and we're going to immediately add the use effect here so that when the data updates we're gonna go ahead and change setOrderedData again and pass in the new data. So this is gonna ensure those optimistic updates. And when later we implement the actual drag and drop we can directly mutate the data using setOrderedData. Great! And now that we have that, let's go ahead inside of this ordered list and just before we do the list form, let's go ahead and let's iterate over data, actually ordered data.map and now we are working with an individual list and let's also pass in the index while we are here.
So go ahead and open this and return a list item which is gonna have a key which is list.id, index which is list.id as well. Sorry, index is going to be index, my apologies. And we're also going to have the actual data which is going to be the entire list. Great, and if you save you're going to get an error because we currently don't have the list item. So go inside of underscore components where we have the list container and create a new file list-item.csx and go ahead and mark this as used client and export const list-item here and return a div saying list-item and go back to the list container and now you can import the list item from .slash list item and when you save you should be having a list item here like this.
So now let's go ahead and let's just quickly give this ordered list a class name of FlexGapX3NHFull and now as you can see the list item has moved next to this button to add a list which is exactly what we want. Now let's create an interface for the list item so we don't have this props errors here. So interface list item props is going to accept the actual data which is a list with cards from types. So let me just go ahead. Oh my god, okay.
List with cards and we're going to have an index which is a number and we're going to use the index later when we implement drag and drop. And now let's assign those props so list item props and we can extract data and the index. Great so now let's go ahead and let's change this div to be a list item and give it a class name of shrink0 h-full and w272px and select none. Great! And now inside of here I want to go ahead and just write a simple div element and give it a class name of W-full roundedMD-BG- and go ahead and write hashtag 1F242F4 ShadowMD and Padding Bottom of 2.
Great! And now inside, well for now we can just add a List Header component like this. And if you save you're gonna get an error for that as well so let's go back inside of the components and create a list-header.tsx like that mark it as use client and export const list header and return a div saying list header and go back to the list item and then you can import the list header from .slash list header and when you save you should no longer be having an error. And I made a small mistake here in the list item component. So the background should be 1F and then it should be directly, sorry it should be F1, F2, F4 like this and there we go.
So now you should be seeing a whitish color like this. So not exactly white but just a slight gradient on it. Perfect and now we can go inside of our ListHeader component here and let's give this div a class name of PaddingTopOf2, PxOf2 as well TextSmall, FontSemiBold, Flex, JustifyBetween, ItemStart and GapX2 Great And now inside instead of rendering the list header let's go ahead and open up a new div which is gonna write list header here and give it a class name of W-full text small PX-2.5, PY-1, H-7, font-medium and border-transparent. Like that. Great!
And now let's go ahead and let's actually create an interface for the list header so interface list-header-props is going to accept a data which is a list like that and we can import the list from Prisma client and let's just assign those here so list header props let's extract the list and then we can go ahead sorry data not list and then we can go ahead and actually render data.title here and we forgot to pass that so go back to list item and pass data from here and there we go you can see how now if I attempt to create a new list it should appear right here there we go you can see how it's right next to this one and if I do it again it will appear here perfect so that is working now What I want to do now is I want to implement an ability to rename lists. So when we click on the title I want it to switch to an input. So inside of the list header component, let me just expand this, So go back inside of the List Header component and let's go ahead and let's import the things we need like useState, useRef, elementRef, fromReact.
And the first thing I want to do is I want to store the title inside of its own state so I can optimistically update that as well. So const title setTitle is going to be useState data.title and then let's replace this Data.title to use the state version of that. Great, besides this state we're obviously going to have the isEditing and setIsEditing from useState and by default is going to be false and now let's go ahead and let's add formRef to be useRef with element ref of form and give it a null for the default value copy and paste that and replace this one to be the input ref and the element ref for that is input. And then let's go ahead and let's create const, enable editing. The set is editing to be true and then setTimeout.
So setTimeout is obviously not the best solution for this. It's kind of like a hack, but it gets the job done for now. You can explore flushSync if you're interested in that, but the react docs mention that it should only be used as a last resort. In all of the attempts that I've tried using it, it worked perfectly. Of course, it can probably vary depending if a device is slower, but I think for a tutorial it's just enough.
And I mean there is nothing, no important functionality inside of this setTimeout, it just focuses on the input to save the user from clicking twice. So let's focus on the input ref using the current and focus and input ref current select I actually think we can only like we actually don't have to do both of them we're gonna try it out now I think we only actually need select great we're gonna try that in a minute. Let's now just continue and let's write the disable editing. So const disable editing. Set is editing is going to be false.
Great, and Now let's go ahead and let's just add the useEventListener from useHooks. So make sure you import that and let me just align those. So useEventListener from useHooks. And I'm going to go ahead and pass in the keyDown element and onKeyDown function. Now let's define const onKeyDown to accept the event, which is a type of keyboard event.
And then inside if e.key is escape, in that case, we're going to submit this request so form ref.current?requestSubmit that's what we're gonna do great and now let's go ahead and let's modify this a bit so inside of this div let's go ahead and add if is editing in that case just return a paragraph which says form otherwise and then wrap this entire div here like that and now let's just go ahead and let's give this div a on click to be enable editing like that and let me just indent this. Alright and already now when I try and click on this it should change to the text form. Like that. Perfect. And what we're gonna do now is we're gonna actually well make the input here but we're also gonna try and add the necessary padding and stuff so that when we click it doesn't get like this little jump, right?
I want it to stay the same size as the original card. So let's just refresh this so everything is in the original title form and let's add the actual native HTML form element here and let's go ahead and give it a class name of flex-1 and px 2px and let's write an input inside and let's go ahead and make it a hidden input with an id of id name of id and value data.id let's copy the hidden input and let's change this one to be board id and name board id and the value is data.boardid like that and then let's add the form input component from add slash components form form input so make sure you have this input right here which I've separated and inside of here go ahead and give it a ref of input ref give it an on blur well for now let's just make it an empty function, give it an id of title, a placeholder of enter list title, and a default value of title from the state. So let's try that out now when I refresh here and click here. There we go, you can see how it turns into an input.
And now we're just going to go ahead and kind of style it so it flushes with the original well the card right and it kind of doesn't jump as much with this outline thing so let's try and do that we're gonna give it a couple of class names here so focus on the form input and let's give it a class name of TextSmall. Px is going to be 7 pixels. Py is going to be 1. H is going to be 7. Font is going to be medium.
Border is going to be transparent. On hover, border is going to be input. On focus, border is going to be input, transition and truncate and let's also add BG transparent and focus BG white. Like that. So let's try that out now when I refresh and when I click here there we go you can see how it looks kind of flush perfect and what I want to do now is well the actual server action which is going to trigger the submit.
So let's go ahead and copy an existing server action. So let me just collapse everything here. Go inside of actions and let's copy update board and let's rename it to update list and then first let's go inside of schema.cs so what do we expect we definitely expect the title and the rules for the title can stay the same we expect the ID of the list, but we also expect a board ID so we know where to update this. Great, and now let's go ahead and let's modify the types from here. Sorry, the types inside of update list and we will not be expecting we will not be expecting the board to return we'll be expecting the list to be returned.
And it looks like we forgot to rename the schema. So it's not gonna be update board it's gonna be update list. And then we have to modify that from here and here as well so let me just copy this like that and now let's go ahead and inside of index here change this to update list as well. Go all the way down, change it to here and instead of update board is going to be update list. Perfect, so the authorization can stay the same and from the data we're going to extract the title, the ID and also the board ID and instead of updating the board we are updating the list so let's assign the list here and we're doing awaitdb.list.update where ID is ID board id is board id and board has the matching organization id and we are just passing in the title here.
Perfect and we're gonna change this to well this can stay the same failed to update and we're gonna make sure that the path we are we are revalidating is board ID like that and in the end let's just return the list. Perfect and now let's head back inside of our, where is it, Inside of our app folder, platform, dashboard, board, components and we have list header here. And let's go ahead and actually add this stuff. So after disable anything I'm going to add use action which we can import from add slash hooks use action and update list from actions update list so let me just show you that I added the use action and update list. Alright make sure you have those two and now here let's go ahead and let's extract the execute.
Execute like that. And let's go ahead. Oh, I misspelled it again, Execute. All right, fourth try. And now let's go ahead and add on success message here and a data.
And in here we can just display a nice message using our toast which you can import from Sonar like this. Let me just move it to the top here. And let's call toast.success here, open backdisk, renamed to open annotations and let's render the new name like that and then let's set the new title so we optimistically update it to data.title here and let's disable editing in case we have an error let's get that error and let's toast error error great and now let's go ahead and let's write the const handle submit here which accepts the form data which is form data and let's get the title so form data.getTitle as string let's copy this two more times The second one is going to be the ID and the third one is going to be board ID. And let's go ahead and let's do if title is equal to data.title, we can just disable editing, right? No need to do an API request here and then let's do execute and let's pass in the title ID and the board ID and now let's also add the const on blur here to call form current request submit and it's actually form ref.
All right and now Let's go ahead and let's assign all of these things. So our form needs to have... Let me just collapse these items. So our form needs to have a ref of form ref. It needs to have an action of handleSubmit And I think that should be it.
Our main input here has the ref but we also need to pass the onBlur here. And I think that should be it. Let's also just add a little button element here which is going to be a type of submit and hidden. All right and let's try that out. I'm going to refresh this.
I'm going to go and change the title and I can press enter and there we go it's updating and it's changed and we have a success message and when I refresh the title has been saved. Let's try another one and this time I will not press enter instead I'm just gonna blur and it still works and let's try if I click escape same thing it updates perfect so you have finished renaming and updating the list now we're gonna go ahead and create options tag so we can copy the list but also delete it if needed. Great, great job!