In this chapter, we're going to develop the widget layout. This will include creating the widget view, creating the widget header, and creating the widget footer. We're going to use this as the base for building further widget screens. Let's go ahead and let's create a widget view. So I'm going to go inside of apps, inside of widget, and I'm going to go inside of modules, and instead of my widget module, I'm going to create UI.
And inside, views. And let's create widget view.tsx. I'm gonna go ahead and mark this view as US client because we're going to need it in the future and let's go ahead and let's create an interface props organization ID and let's go ahead and give it a string and then let's export const widget view here and let's go ahead and destructure the props which only hold the organization ID. And inside of here let's go ahead and let's return a div. Actually we can call this main.
Like so. Let's give the main element a class name of flex-full-height-full-width-flex-column-overflow-hidden-rounded-extra-large-border-and-background-color-muted. And now let's go ahead and simply write widget view inside that's all for now Now let's go ahead and let's create the actual page. So inside of our app, let's go ahead and go inside of page.tsx like that. And in here for Now we can remove the useWapi import and we can remove all the other elements inside.
And this does not have to be, actually leave this as useClient as well. And let's go ahead and let's import widgetView from modules, widget, UI, views, widgetView. And simply return the widgetView here. Now in here we expect the organization id prop so let's go ahead and add it here. So the way we can destructure The organization ID prop from here will be through search params.
So since this is a reserved page.tsx file, that means that we have some special access to the props here. So that means that we can actually create an interface props search params which is a type of promise and inside organization ID string like that and then in here let me just refactor this I don't like to use this method. And then let's just do export default page like that. And then props. And the structure search params.
And now let's go ahead and let's destructure the organization ID by using use from react make sure that you have use client at the top of this page.tsx and inside of here add search params and now you will be able to pass organization id like so and now just for fun let's go inside of the widget view and let's also render the organization ID just so we can see if we pass it successfully. Now let's go ahead and let's run TurboDev and let's go to localhost 3001 where our widget is running. So you can see that now we have the widget view but nothing inside. So if I go ahead and change my URL and added a question mark, organization ID, one, two, three, you will see it says one, two, three. Now I'm going to show you the entire URL.
So localhost 3001 and then question mark organization id equals one two three. So this organization id capitalization matters. Make sure that you didn't misspell it here in the search params, here and here and here. All of that matters. So this is how other users will load the widget.
We are going to create a script that users will add to their website and that script will load localhost 3001 with this organization ID in an iframe on their website rendering the widget view for that specific organization. That's how it's going to work. So once you've successfully managed to render 1, 2, 3, let's go ahead and edit this view. So now I'm going to go inside of my UI here in the widget and I will create components. And inside I will create widget-footer.tsx Let's go ahead and export const widget footer and we can actually use the footer element.
Give it a class name of FlexItemsCenter, justify between, border top and BG background. And now inside of here go ahead and add a button from workspace UI components button and render a home icon from Lucid React here. Go ahead and give this home icon a class name which will be dynamic using the CNUtil. Give it size 5 by default and then let's go ahead and do the following. Just for fun we're going to define the current screen that is active and let's go ahead and make that selection.
And then we're going to check if screen is equal to selection. In that case we're going to use text primary class name. Like that. Now let's go ahead and give this button some attributes. Class name, height 14, flex 1, rounded, none.
On click for now it can just be an empty function size will be icon variant will be ghost now let's go ahead and duplicate this entire thing like this And this one will be using an inbox icon from Lucid React and check if the screen is inbox. And yeah there is a type error here. So Yeah you can just ignore it for now it's because we are hard coding this to be selection. So this will never be true. So let's just go ahead and wrap it up.
Vigit footer. Perfect. And now let's go ahead and let's create a Vigit header component. Now the Vigit header will be quite similar. It's gonna be a very simple component, but it will accept some classes.
And those will be children, which are a type of React node, and class name, which will be an optional string. So that's the structure, the children and the class name. And instead of here, we're going to return a header element with a class name CN, which of course we have to import from our workspace UI libutils background gradient to bottom from primary to and then we're just going to use the same blue hex color that we used in our sidebar in the previous chapter. Give it a padding four and text primary foreground. And then in the second argument, pass an optional class name.
And inside, render the children, just like this. Now let's go inside of the widget view and let's go ahead and add widget footer at the bottom like that. So now you have this. Of course, depending on how zoomed in you are, You might see it very, very wide or you might see it like this. Just remember that this will be rendered inside a limited width and height iframe.
So this will not be rendered in a full screen like this. So if it looks weird, that's why. And now let's go ahead above here and let's add the widget header component. Header like this. And there we go.
We now have header, we have the body, and we have the footer. And let's just assimilate how this will actually look like here by adding a div around the body and giving this a class name of FlexFlex1. Or that is not enough it seems. So let's see Flex, FlexCall, HideFull. Let me check the page.
Okay, The page is good. Let me check the layout. Is there something that we have to do here. So we have the providers. Not 100% sure why this isn't taking full width.
Let me just debug a little bit. So I didn't really debug it, but I do think it's missing a minimum height of screen that you have to add here and then it will take the entire space. I'm just trying to remember why I did not need this class name in my original source code. So I'm trying to think if maybe there's a reason that when you render it in an iframe, it works differently. So I'm going to add a little to do confirm whether or not min age screen is needed.
And while we are here, we can also add a minimum width of screen as well. And let's go ahead and add that to the comment as well. And I'm pretty sure this is not how you spell weather. Weather. Maybe like that.
OK. Perfect. So what I want to do now is I want to build a hi there. How can we help you today type of widget header. So instead of this widget header, Let's go ahead and let's create a div inside.
Let's give this div a class name, flex, flex column, justify between, gap y2, px of 2 and py of 6. And then in here let's add first paragraph, hi there, and we can just use the waving emoji. And below that another paragraph, How can we help you today? I use Raycast to add emojis on my Macbook. You can just Google hand waving emoji and then add it here Or you just don't have to use the emoji.
Let's go ahead and add a class name, FontSemiBold and text 3XL. And for the bottom one, FontSemiBold, text large. So we can transfer FontSemiBold to this div to reduce repeating class names. Let's go ahead and try it out and there we go. Hi there.
How can we help you today? Perfect. And this is basically how we're going to build our future widget views. Basically we're going to have the out screen, the inbox screen, the chat screen, the phone number screen, and the voice agent screen, and all of them will reuse these components. And I will actually not be using the Next.js router here at all for a very simple reason that technically this widget app does not have to be server-side rendered or things like that.
So it could be a single page application without any routing whatsoever. So we're actually going to be using, I'm definitely pronouncing it incorrectly, Yotai state management to create our own little router. It's going to be very cool. And this way you will be able to migrate the widget app to to invite to, I mean, to Vite, to react, whatever you want. Right.
But I will continue building in Next.js simply because I'm familiar with it, but we're going to build it in a way that is Next.js agnostic. So you will be easily able to move it out of that if you want to. Perfect. So I think this is exactly what we wanted for this chapter. Obviously, if you zoom out a lot, it will look huge but Just remember this will be rendered in an iframe with fixed constraints.
Right. So it's just going to be limited to this wide and this high. So don't worry that it looks super weird. Right. If you want to make it more realistic you can I don't know display it in mobile mode?
This is how it's going to look like basically. Perfect. So let's go ahead and commit that. So this was a shorter chapter but a sweet one nonetheless. Let's go ahead and stage all changes.
Let me see. So 09 widget layout. 09 widget layout. Let's commit. I'm going to go ahead and open a new branch create new branch 0 9 widget layout like this.
And I'm going to click publish branch like that. And let's go ahead and review our changes just in case that we didn't do any catastrophic mistakes here even though this was just UI. So zero nine widget layout and while CodeRabbit is reviewing here I actually want to show you that there is another way you can use CodeRabbit. You've probably seen this a few times. So whenever I do a commit, I can actually review within my IDE, right?
And then I can go ahead and select CodeRabbit here and now CodeRabbit will review all of those changes that I've just done within my IDE as you can see right here. And the way this works is by a completely free extension. That's right. You can go ahead and install CodeRabbit and just connect your account and you will just get free code reviews. There's no catch.
That's it. It's literally free AI code reviews. And it's a super cool tool. Just for now I'm going to stop this review simply because we have the same thing happening here so no reason to review it twice. But yeah, feel free to install the CodeRabbit tool.
I think it's amazing. The reason I like the pull request one more is simply because it gives us a description, it gives us the sequence diagrams and it closes the chapter in a better way. And here we have the summary. We introduced a new widget interface featuring a header, footer, and main view displaying the organization ID. We added customizable header component for flexible content and styling as well as a footer component with navigation buttons for improved user interaction.
Now in here in the code review, it does tell me that I have to remove useClient. The reason I put useClient is because in here, we're actually going to use Dynamic Import to completely turn off server-side rendering for our widget view. But I didn't want to do that in advance because I wanted to wait until we have to do it so you see why we are doing it. So that's the only reason I put Use Client right now because in order to use Dynamic Imports, I'm pretty sure you need Use Client. So that's the only reason I put use client right now because in order to use dynamic imports I'm pretty sure you need use client.
So that's the only reason I put that. And in here it is telling me that we have to check if we have organization ID. That is exactly what we are going to do but in a different way. So we are just building a component here to set up the base components and we are now in the next chapter going to learn how to create screens which will check for organization ID and things like that. In fact we will have a special error screen that will show if organization ID is missing and if organization ID is invalid and of course we have a hard-coded selection screen so we will be changing that in the future.
This was a fairly simple pull request so let's go ahead and merge it and once you've merged it go ahead and change back to your main branch and you can press synchronize changes here and okay or use the little button below sometimes it can just look like this You can press it as many times as you want. And once you've done that make sure to check your graph to confirm you have detached 9 and then you've merged it. So it should look like this. Excellent! I believe that marks the end of this chapter.
We've created the widget view, widget header and widget footer as well as the GitHub workflow. Amazing job and see you in the next chapter.