In this chapter, we're going to go ahead and implement the completed state with the new summary which we generated in the previous chapter using background jobs. So let's go ahead into our project and ensure that we are on the main branch. Let's also make sure we are running npm run dev, run dev webhook and npx ingest cli. And then let's go ahead to our meetings page. Once in the meetings page ensure that you have at least one meeting with the completed status.
If not, have a new meeting and wait for it to be completed. You will know it's properly completed by going inside of your database and confirming that at least one of them has a summary like this. And now let's implement the completed state. Let's go inside of Meeting ID view and let's find the isCompleted which renders the completed text. Let's replace it with completed state and pass in the data which is our use suspense query data.
Now let's create the completed state. Inside of components create completed state DSX. Let's export const completed state right here and let's go ahead and let's create the interface for. Let's import MeetingGet1 from our types which are located in the meetings module right here. We have MeetingGet1.
And now in here we can go ahead and destructure the props and grab our data right here. Now let's go ahead and let's return a div with flex flex column and gap y of 4. Let's go ahead and add semi columns here and let's import the completed state so we can actually render it and you should have no errors with the types. When you refresh, nothing should be visible in your completed meeting because we haven't rendered any text yet. Now let's go ahead and let's import everything we need from our components UI scroll area and components UI tabs.
So we haven't used tabs yet, but you have them inside of your source components UI tabs. We've added that as well as scroll area when the initialize chat CN UI. Now let's go ahead inside of here. And the first thing we're going to do is activate the tabs like this with the default value of summary. Then inside of the tabs we are going to add a div with a background of white, rounded large, border and px of 3.
Then we are going to open the scroll area and inside we are going to render the scroll bar. The scroll bar will have an orientation of horizontal. Now inside of the scroll area let's go ahead and let's open the tabs list component with class name padding zero, bg background, justify start, rounded none, and height of 13. Now inside of here let's add the tabs trigger component and let's render an icon, hook open text icon which you can import from Lucid React make sure to add that and let's keep the tabs trigger a value of summary and now if you take a look at your app you will see the summary tab right here Now let's go ahead and let's import actually all icons we need from Lucid React. So that will be sparkles icon, file text icon, book open text icon, file video icon, and clock fading icon.
Let me just move them to the top here. And now let's go ahead inside of this tabs trigger and let's move this down here because we are going to add a pretty large class name here. So I'm going to paste it here And I will you can pause the video and copy it. So basically text muted foreground, rounded none, BG background. And now this is important.
Data state active, and shadow none are one class name, right. So let me just temporarily remove this so you can see. This is how the class name looks like. So whenever you write data state active shadow none, it's one class name. It's just that I use this collapse line settings which collapses them.
So if I zoom out, you will see that my data state active are all in one line. So whenever you see data state active, just know that they are supposed to be together with whatever comes after the column like this. Same is true for hover. Okay. So after you've added this, yours should look like that.
There we go. And now we're going to go ahead and open, copy the taps trigger again. And the second one will be transcript. And we're going to use the file text icon and transcript. And then we're going to have the recording one.
So I'm basically just copying exactly as it is. All of them have the same class names, right? This one will have a file video icon and the recording text and the value of recording, and this one has the value of transcript. And then the last one we're going to have is the chat value, sparkles icon and ask AI. So the class names are identical in all of them.
We are just copying from the first one, changing the icon value and the label inside. So now you have the transcript, the recording, and Ask AI. And now we have to actually create the content for each of these tabs. A pretty easy tab to make is the Recording tab. So let's just go outside of this div, but still inside of the tabs.
Oops. And let's render tabs content with value recording. Just make sure that it matches the value here that you don't have any typos. And the only thing we're gonna do is the following. We're gonna add a div with background white, rounded large, and border.
And then we're just going to add some padding as well. So let's just do that right here. Px4 and Py5. And the only thing we're going to do is we're going to render a video. So normal video and source data are recording URL with an exclamation point because without it, it can be undefined.
But we know that since it is in the completed state, it's always going to exist. So we can safely just put an exclamation point at the end. With full and rounded large and let's also add controls and when you click on recording now you will see the actual recording that exists in the recording URL here just one tip recordings storage for recording and transcription these URLs here they have an expiration date They are retained for two weeks before being automatically deleted. But don't worry, if you want to keep them forever or just not store them with stream, you can use your own storage like Amazon, Google Cloud or Azure. So go ahead and visit Video and Audio, Select Platform, and Recording Storage and Transcription Storage, and follow the documentation to add your own external storage if you don't want this to expire.
Great. So it was that easy to add the tabs recording and now we're gonna go ahead and add the summary tab. Before we can implement the summary tab, we have to install a package called react-markdown. So we can properly render our markdown because that's what we instruct our assistant to do. So now that we have that let's go ahead and let's make sure that we add an import for markdown from react markdown.
Let's also import link from next link. And let's also import generated avatar from components generated avatar. Now let's go down there inside of new tabs content. So I will add tabs content here. And I will give it the value of summary.
And inside I will render a div with background white, rounded lg, and border. Then inside I'm going to render another div with bx 4, py 5, gap y 5, flex, flex call, and call span of 5. Inside, I'm going to add an h2 element with text to Excel, font medium, and capitalize rendering the data name. And you can see that now when I click on Summary, I can see this meeting's name. Just make sure you didn't misspell the default value, the value in the trigger, and the value in the tabs content.
Below the meeting title, we're going to add the current agent that was inside. So add a div with flex gap x2 and items center and add a link element. We're going to give the link an href of forward slash agents and then data agent ID like this and then let's go ahead and give it a class name flex item center gap x2 underline underline offset for and capitalize and then inside of this link let's render the generated avatar which we imported previously with variant bots neutral seed as the agent name and class name size five and below it we can simply render the agent name and then add an empty space after the link here. And below that, let's go ahead and let's add a paragraph to check if we have started at. If we have it, let's use format from date FNS.
So just make sure you import that format from date FNS. And you're going to format data started at in this exact format. So there we go. Now you can see MathTutor and when I click on it, it will take me directly to that meeting here. Now below our agent here, outside of this div, let's render a new div with class name flex gap x2 and item center, render our imported sparkles icon, and whoops, not this one, and render the text general summary like this.
And then below that, let's go ahead and let's add a badge from components UI badge. And it's not going to be a self-closing tag. So make sure you just added the badge from components UI badge. Let's go ahead and let's give it some attributes here. So variant outline and class name flex item center gap x2 and target the SVG to give it size 4.
And inside of here go ahead and add it clock fading icon. Now let's go ahead and let's revisit our columns for the meetings here. And in here, we have a function format duration. Let's copy it and let's add it inside of our lib and inside of utils here. So after the function cn, let's add export function, format duration, and import humanized duration from humanized duration because I want to reuse it.
So inside of my source modules meetings components columns, I no longer have to have it here. I can instead go down to where I use it and just import format duration from lib utils. Right, like that. That means I no longer need this import. And there should be no errors in your columns here.
Now go inside of the completed state here, and now below clock fading icon, that's exactly what we're going to do. So we're going to check if we have the duration and then use our newly added format duration. Make sure to import it from libutilus, not from datefns. So we use the same name, that's unfortunate. You can change it if you want to.
Just make sure to import it from the correct place. So now in here you have the duration of the call. Now below this badge, let's go ahead outside of this div, let's go ahead and actually, sorry, inside of this div below the badge, open a new div and render the markdown. Let's close the markdown. So make sure you have imported markdown from React markdown here.
And inside of here, render data summary. So let's go ahead and refresh. In here, you can see that I have the summary. The problem is it's not formatted at all. So in order to format it, we have to go ahead and add components prop here, open an object, and let's simply add the elements.
So this is the h1 element, text to Excel, font, median, and margin bottom of six. And then I'm just going to copy from my source code. The same is for h2 elements h3 and h4. We are just reducing the size of the fonts. You can see it slowly changing now.
Now let's go ahead and let's add the paragraph, margin bottom of six and leading relaxed. There we go. And then you can go ahead and add more. For example, unordered list, or you can add ordered list. You can then add a normal list here.
And I would also suggest adding strong. I will also suggest adding code and block quote. This way you cover most of the things AI can generate in Markdown. So you can just pause the screen. These are very simple snippets, right?
And there we go. You now have a nice overview summary here. And I'm going to end the chapter earlier now, because I want to do the transcript together with the AI chat, actually. So we only did the simple ones now, and we'll do these a bit more complex ones in its own chapter. So let's go ahead and mark this as completed.
Let's revert this and recording tab is completed. So let's go ahead and merge all of these changes. I'm going to create a new branch, 25 completed state, I believe is the chapter name. And once I have confirmed I'm on the new branch I'm going to stage all of these changes and I will add 25 completed state commit message click commit and publish the branch once it's published let's go ahead and let's review our pull request. I'm only interested in any potential issues.
And here we have the summary. So this chapter was pretty simple. So not much going on here. We basically added a completed meeting view with summary, transcript, recording and ask AI tabs. And we added the markdown rendered a summary.
And in here we have one actionable comment and that is to add ternary in case recording URL does not exist, which is a good idea considering that it will disappear at one point unless you use your own storage as explained in the beginning of the chapter so you might consider doing this. Let's go ahead and let's merge the pull request and once it is merged let's go back to our main branch and let's synchronize the changes to ensure everything is up to date and in your source control here in the graph, you will now see 25 completed state. Amazing, amazing job and see you in the next chapter.