In this chapter, we're going to go ahead and implement the UI for our widget integration. This will be a screen where our operator dashboard will find their organization ID, as well as code snippets if they want to add the widget chat box to their respective technology HTML react next JS or JavaScript. For now we're only going to be building the UI side of this so basically this screen and the model that opens when we click on each of these technologies and then in the next chapter I'm going to show you how we can actually develop the embed script. So let's go ahead and create the integrations view component. I'm going to go ahead inside of my apps web app dashboard and we should have integrations in here and the page.tsx.
Let's keep that open and let's go inside of our modules and let's create a new folder integrations and let's go ahead and create UI and views. Inside of here let's create integrations view.tsx. Let's mark this as used client and let's export const IntegrationsView. Let's go ahead and return a div IntegrationsView. Now let's go back inside of that page and let's simply render the integrations view component.
And now on your localhost 3000 when you click on integrations you should see the integrations view right here. Perfect. Now that we have that, we can focus on developing only within that module. So integrations view, integrations view. Now we can actually copy this from the plugins view, WAPI view here.
Let's go ahead and copy this div and this. So basically the title and the subtitle and let's add it here. And I think we need to close one div. There we go. Just like this.
And now let's just modify it slightly. So instead of WAPI plugin, this will say setup and integrations. And the description here can be something like choose the integration that's right for you. Like that. There we go.
Setup and integrations, Choose the integration that's right for you. There we go. And now in here, let's go ahead and let's render the current organization's ID. So the way we're going to do that is by going down here, opening a new div and rendering a class name margin-top-8 space y6. And in here let's go ahead and open a new div with a class name flex-items-center and gap-4.
Let's add a label component, so make sure you've imported a label. Let's give this label a class name with 34 HTML4 website dash ID and passing organization ID text here and change the HTML4 to actually be organization ID. Let's be consistent. Below that, let's add a self-closing input component and make sure you've added an import for it. Let's go ahead and give this a class name of flex1 bg-background-font-mono and text Small.
Let me just go ahead and collapse these attributes so that they are easier to read. So besides the class name I'm going to make this disabled. ID will be organization ID. It will be read only and the value can be 123. There we go.
We now have organization ID 123. Now let's populate it with the actual organization ID. So make sure you have use client at the top of this page and you can extract organization from use organization from clerk next JS. And once you have that you can go inside of here and change the value to be organization.id or an empty string and use a question mark here. And let's just fix the typo.
Let me see what I did wrong, organization. And now you should see your organization ID right here. Now let's add an ability to copy it in an easy way. So I'm going to add a button import. Make sure you have added the button import.
Let's go ahead and give this a class name Gap2. OnClick for now just an empty arrow function and the size of small. Let's render copy icon here. Let's give it a class name, size 4. And let's also give it a text, copy.
And now you should see the button to copy. Perfect. Let's go ahead and quickly develop the copy button here. Const handle copy is going to be an asynchronous method. In the trial, let's await navigator clipboard, write text, organization question mark ID or an empty string, toast.success, copied to clipboard and import toast from sonar, otherwise toast.error, failed to copy to clipboard in case browser API fails.
Now that we have this handle copy, we can just add it to the button on click. Let's refresh and let's try it out. There we go. Copied to clipboard and not sure where I can test this. Let me try and find a way to test it.
There we go. When I paste, you can see exactly where it is. Perfect. So let's go back inside of the integrations here and now let's go ahead and let's develop the integrations part, right? So outside of this div right here, let's add a separator component from Workspace UI separator.
So make sure you've added the import. Let's give this a class name my of 8. Let's add a div with a class name here, space Y6, inside a new div with a class name, space Y1. Give this a label of integrations and give it a class name of text large. Below that a paragraph add the following code to your website to enable the chatbox.
And let's give this a class name text muted foreground and text small. And then in here let's go ahead and let's create a grid grid columns to gap for on medium devices grid columns for. So nothing should really show at the moment. That's because we need to create an array of our integrations. So instead of the integrations module let's create constants.ts file like this.
So inside of the integrations folder, let's export const integrations. And now you would add every integration you want to instruct your users on how to add the chat box to. For example, HTML. And let's give it an icon of forward slash languages. Forward slash HTML5.svg.
We don't have this yet, but we will have in a moment. So now we can copy this a few times and in here you would add you know anything you want to support right so I'm adding react I'm adding next JS Even though these things don't actually make sense because all of this is just JavaScript, right? I'm just doing it to populate some space here. It would make more sense if you did like JavaScript and then you did Shopify and then you did WordPress for example, right? So those actually different things because if you can do it in JavaScript, you can do it in Next.js, you can do it in React, you can automatically do it in HTML.
So I'm just going to write the same script for all of these but I'm just trying to show you how this would look like. So what you have to do now is you have to use the link on the screen to get to my public assets folder and in here go ahead and find the languages folder where I prepared HTML5, JavaScript, Next.js, React and similar. And once you have the languages downloaded, you should go ahead inside of your app, web, public and create the languages folder in here and simply put HTML5, JavaScript, Next.js and React inside. Inside of your public folder, create a new folder called languages. And then in your constants in the integrations, it should match exactly the structure in your public folder.
So once we have that done, we can go back inside of the integrations view and we can go over the integrations which I just imported quickly from the constants. So integrations.map, get the individual integration and in here I'm going to return a native HTML button element and I'm going to import an image from next image. So make sure you've added the image here. Let's give this an alt property of integration.title. Let's give it the height of 32, source integration dot icon and width of 32.
And now in here you should start seeing HTML5, you should see react, next.js and JavaScript. And let's go ahead and give this button some class names. So that's going to be flex items center, gap 4, rounded large, border, bg background, padding 4 and hover bg accent. There we go, already looking better. Let's go ahead and give this a key of integration.id.
On click for now an empty arrow function and let's give it a type of button. After the image let's go ahead and create a paragraph here integration.title. There we go. HTML, React, Next.js and JavaScript. Perfect.
So now what we have to do is that when we click on one of these, a dialog opens and it shows us the code snippet or instructions which you would do to add this to your project. So Let's go ahead down here and let's quickly develop the integrations dialog. In order to implement this we're going to have to import some things from the dialog. So let's go ahead and prepare that. Dialog content description header and title all from Workspace UI components dialogue.
And let's go ahead and add some props here. So In here we're going to write the types. The types are going to be open, onOpenChange and snippet. So onOpenChange is a function which accepts the new value which is a boolean and returns a void. And then let's extract all of those and snip it now inside of here let's go ahead and let's return a dialog let's give it on open change let's give it open of open.
Let's create dialog content. Dialog header. Dialog title. Integrate with your website. And below that dialog description follow these steps to add the chat box to your website.
Outside of the dialog header, create a div with a class name space Y6, another div with a class name space Y2, and then a div with a class name rounded medium background accent padding of 2 and text small. And let's simply write the first step. Copy the following code. And now let's write our code snippet here using a div with a class name group and a relative. Then let's add a pre tag.
Let's go ahead and give this a class name maximum width, my apologies, maximum height of 300 pixels. Of 300 pixels, overflow X auto, like so, overflow Y auto, white space, pre wrap, break all, All, Rounded Medium, BG, Foreground, Padding to, Font Mono, Text Secondary, and Text Small. And inside of here you would render the snippet. Then add a native button, a ShadCNButton element here and give it a class name, absolute, top 4, right 6, Size 6, Opacity 0, Transition Opacity, Group-Hover, Opacity 100. So that's why we put the group in the parent div here.
So when we hover over the code snippet, a button will appear and this button will have an on click for now an empty function with the size of icon and a variant of secondary. It's going to be a copy button for the snippet. So just add copy icon in here with a class name size three to make it a little bit smaller. And then in here, let's go ahead and create, we can just copy this right here, space y2 like that. And let's go ahead and give this a second step which will be add the code in your page.
And below this add a paragraph. Paste the chat box code above in your page. You can add it in the HTML head section. And let's give this paragraph a class name text muted foreground and text small. Great.
Now let's go ahead and let's just quickly copy our handle copy method from here. Let's go ahead and add it inside of the integrations dialog here. But instead of organization ID it will copy the snippet. And then you can use the handle copy in this button just like that. Perfect.
Now we have our integration dialog. And now what we can do is we can render it. So let's go ahead and wrap our entire integrations view here within a fragment. Like this. Let's go ahead and render the integrations dialog.
Let's set on open to be set dialog open which doesn't exist yet. On open change. This will actually be set dialog open. The one above will be dialogue open. None of these exist yet.
And snippet will be selected snippet. Now let's go ahead and actually create all of those states. So just above the organization, user organization hook, let's add dialog open and set the dialog open using use state and selected snippet and set selected snippet using use state Like that. Now let's go ahead and let's develop const handleIntegrationClick integrationId will be a type of integrationId. Now let's go ahead and let's create the type integration ID so we can actually do that quite easily by just revisiting our integration constants here and then we're just do export type integration ID type of integrations number and then the ID field.
And let's import integration ID now just like that. So this way we will only be able to call this function with a supported ID. Next JS, React, HTML. So what I want to do now is go back inside of my constants and just simply prepare export const HTML script. And for now let's just add a simple script snippet like this.
And let's copy this. Let's change this to react script. Let's change this to next JS script. Let's change this to JavaScript script. We are later going to modify the actual script that happens, right?
So now what I want to do, Yeah, let's also do one more thing here. So the way this script integration will actually look like if we want to be a bit more realistic, it will have an attribute called data organization dash ID. The problem is, since this is hard coded in a string, we have to invent our own type of templating language. So let's add organization underscore id here. So yes each script snippet will have this attribute so go ahead and add it everywhere here.
So this is kind of a problem now right because we need to somehow populate this hard-coded string with our organization ID that it's rendered right here and then display that to the user. Well we can do that quite easily. Instead of the integrations, let's go ahead and create a new file called utils.ts. Let's go ahead and import HTML script, type integration id javascript script nextjs script react script all from constants and let's export const create script to be a function which accepts the integration ID which is a type of integration ID and organization ID which is a type of string. And once we have those two, we can check if integration id is equal to html, in that case let's return html underscore script dot replace and let's go ahead and add a forward slash open double square brackets like this curly brackets organization id forward slash g as in global and replace it with organization ID.
So what will happen is that this exact part will be replaced with one to three for example. And that's how we are going to dynamically generate each organization id script. Great. So now let's copy and paste this. If the integration id is react, let's change this to react script.
Then let's go ahead and copy this for nextjs, nextjs script. And let's copy it for JavaScript. This will be JavaScript.script. And all of them are the same. Otherwise just return an empty string.
And since we are using this safe, let's see, integration ID is a type of string. Oh, so this is not really working as I thought it will work. I thought that this was an enum. Looks like it's not an enum. Okay, in that case, just be super careful.
JavaScript, Next.js, React, HTML, and I'll try to find a way to make this an enum but just make sure that you're checking for the correct things here. Now let's go back and set up our handle integration script here. First things first, if there is no organization, let's go ahead and simply return and we can even show a little error to the user. Organization ID not found. I have no idea how this can happen except if it didn't load yet.
But yeah, let's at least tell the user why we cannot generate the snippet because we need something to replace the organization ID template from. Now let's use the new create script from our utils and let's pass in the integration ID which is right here and then let's pass organization.id set selected snippet, snippet, set dialog open to true. Let's copy handle integration click and let's add it to the on click button in here and pass in the integration dot ID. Yes that's the only thing it accepts. And there we go you can see that now when I click on any of these, it will open a script and the data organization ID here will be replaced with the actual organization ID that I have right here.
So I can either copy it manually from here, which is now finally useful because in your widget, you no longer have to visit the clerk dashboard. You can just add organization ID, paste it here and it will load into a working organization. Excellent. Amazing, amazing job. So that's the UI finished for this task.
In the next chapter we are going to work on actually developing this script right here that the users will be able to add to their plain new HTML landing page or React page or Next.js page and it will load this widget inside. So let's go ahead and see if we did what we intended to do. Correct and correct. One thing I dislike from here is that we don't have enum type safety so you can make easy typos here. As you can see this doesn't throw an error.
I somehow thought that this will create an enum but it didn't. I'm not sure how do I do that at the top of my mind right now I'll have to do it in the next chapter and research. So this type actually is either a type of JavaScript or Next.js or React or HTML. Because this doesn't really make sense now. Yes, I know it's a string.
So yeah, just make sure you didn't misspell the organization ID here. Make sure that you are consistent with your ID types here. Same here in the utils, don't misspell them. Same thing with the organization ID with the double curly brackets. And everything should work fine if you did.
Great, so no logic developed here really. We just developed a nice UI so we don't have to worry about that anymore and we can head into the final chapters of this very long tutorial. Let's go ahead and merge this now so I'm going to go ahead and I'm going to stage all of my changes. This is chapter 33 integrations UI 33 integrations UI. Let's go ahead and commit.
As always I'm going to open a new branch 33 integrations UI. I'm going to publish this branch and now let's go ahead and review the pull request. And since this was a fairly simple PR we can just read the summary and merge it. So we introduced an integrations page with a grid for HTML, React, Next.js and JavaScript integrations. View and copy your organization D with one click.
And generate framework specific install snippets, to do. And copy them from a model dialog exactly what they did. So yeah there are some things I want to improve here but we are mostly focused on just having the UI ready so that we can focus on developing the script. So good enough for me Let's go ahead and merge this pull request now and once we've merged it let's go ahead inside of main and let's click synchronize changes so our main local branch is up to date with our remote one. As always I like to double check with the graph to confirm I have merged section 33.
I believe that marks the end of this chapter amazing amazing job and see you in the next one.