> Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Step 4 | Create a Dashboard Page to Manage the Catalog ## Article: Step 4 | Create a Dashboard Page to Manage the Catalog ## Article Link: https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-4-create-a-dashboard-page-to-manage-the-catalog.md ## Article Content: # Step 4 | Create a Dashboard Page to Manage the Catalog > [< Previous](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-3-create-an-item-page-in-blocks.md) | [Next >](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-5-implement-a-self-hosted-catalog-service-plugin.md) Instead of requiring the Wix user to enter the CMS and directly edit the Poems collection, we’ll create dashboard pages where they can add and remove poems. This is a more comfortable interface, especially for a user who doesn’t work frequently with databases. In Wix Blocks you can create [dashboard pages](https://dev.wix.com/docs/build-apps/develop-your-app/frameworks/wix-blocks/dashboard-pages/about-dashboard-pages-in-blocks.md) that are added to the dashboard of a Wix site upon installation. These pages appear under the **Apps** page in a Wix user’s dashboard. To add a dashboard page in the Blocks editor: 1. In the menu on the left, click on the **Dashboard Interface** ![dashboard interface](https://wixmp-833713b177cebf373f611808.wixmp.com/images/84cde788fc36d0c001c02a63979a724e.png) icon and click **+ Create Extension**. Select the **Dashboard page** option. 1. Blocks opens a new dashboard page for you in the editor. Next to the title of the dashboard page, click **More Actions** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7860e419024a15bd57bf0e117b2894a3.png) > **Rename** and change the name of the page to **Poems Manager**. The **Poems Manager** page already has some basic layout elements. You can see these elements more clearly by clicking the **Layers** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/14f5d81e1fb5bc4d047d63535be945ac.png) icon in the left menu. 1. Hover over each of the elements in the page to understand which label corresponds to what. First, locate the label that corresponds to the title. Click **More Actions** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7860e419024a15bd57bf0e117b2894a3.png) > **Rename** and rename the box to `pageTitle` so that we can distinguish it later. This first dashboard page will display the poems in the catalog database, and allow the Wix user to delete poems. So let’s give it a fitting title and subtitle: - **Title**: Poems Manager - **Subtitle**: Add and remove the poems your site offers 1. Delete the **Secondary Action** button, and edit the **Main Action** button settings to change the button text to **Add a poem**. Change the element ID of the **Add a poem** button to `#mainAddPoemButton`. ![Change the text on the button to add poem](https://wixmp-833713b177cebf373f611808.wixmp.com/images/95b5ba835a2ecb8d3913befa3820a03e.png) The upper box on the dashboard page should now look like this: ![Poems manager dashboard page title](https://wixmp-833713b177cebf373f611808.wixmp.com/images/016b7da2c0015ba4eb1616cb867e5f77.png) 1. Now locate the label that corresponds to the card on the dashboard page. ![Poems manager card](https://wixmp-833713b177cebf373f611808.wixmp.com/images/69e36a2542610d836faad1f4bf8010e4.png) In general, we want the **Poems Manager** dashboard page to display the poems listed in the product catalog. However, if the catalog is empty, it should instead display a message prompting users to add a poem. Since our dashboard page will look different depending on the state of the collection, we need to incorporate that state into the card by using a [multi-state box](https://dev.wix.com/docs/velo/velo-only-apis/$w/multi-state-box/introduction.md). Next to the box that corresponds to the card, click **More Actions** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7860e419024a15bd57bf0e117b2894a3.png), and select **Place in Multi-State Box**. ![Place in multi-state box](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f7db0b31e95a8288de2aa6270a51e640.png) In the box itself, first delete the subtitle and edit the title to say **Your Poems**. This can remain the same in both states. Now rename the box `emptyState` to distinguish it from the opposite state. In the lefthand menu, click **Add Elements** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ba9949aacd15bfd634da96e189283cc0.png )> **Design Patterns** > **Empty States** and select the **Add your first product** option. ![Choose Add your first product empty state](https://wixmp-833713b177cebf373f611808.wixmp.com/images/c8a2fbcf6272d82135e49481dc5c5725.png) Edit the text and the button so that the empty state element looks like this: ![Empty state title and button](https://wixmp-833713b177cebf373f611808.wixmp.com/images/dc9c8cf94ec4d4eee4434a5201ff6e5d.png) Then, select the button and change its ID to `#addFirstPoemButton` to distinguish it from the **Add a poem** button at the top of the page. This completes the dashboard UI for the empty state. We’ll add code shortly. Let’s create the second state next. 1. Add a second box to the multi-state box and rename it `poemsList`. Again, delete the subtitle and edit the title of the box to **Your Poems**. For this state, we’ll use a [repeater](https://dev.wix.com/docs/velo/velo-only-apis/$w/repeater/introduction.md) to display poems in the collection. Go to **Add Elements** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ba9949aacd15bfd634da96e189283cc0.png) > **Layout** > **Repeaters** and select the 4-square repeater. ![4-square repeater](https://wixmp-833713b177cebf373f611808.wixmp.com/images/74af3f79cea402ce80afc66fc59c502e.png) Adjust the repeater’s position on the page. In the **Properties & Events** panel, change the repeater ID to `listOfPoems` and the ID of each card to `poemCard`. Then add the following elements inside the cards: - A Heading 2 text element - A Heading 4 text element - An icon button like this: ![Trash can button](https://wixmp-833713b177cebf373f611808.wixmp.com/images/84d657d515aaef0eaaa4e42090fe50c9.png) 1. We can add functionality to the `poemsList` state simply by connecting it to the CMS. Select the repeater, and in the action bar click **Connect to CMS** ![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/378fd532-728d-473b-8f03-30140bda59af/2024/02/28/35acad32-03ed-4ea0-a201-df5ebcc37ceb/eb371f17-e215-41bc-9260-64ac656eade4.png). Though we already created a dataset for the item page, that dataset doesn’t apply to the dashboard interface. Therefore, we need to create a new one. In the menu on the right, click **+ Add a Dataset** to create a new dataset. Select the **Poems** collection as the one we want to connect to, and name the dataset **Poems Dashboard** so we can distinguish it from the item page dataset: ![Create a dataset for the dashboard](https://wixmp-833713b177cebf373f611808.wixmp.com/images/3bc2546d122d1c059102b064e973c1ba.png) Click **Create**. The righthand menu displays the new dataset. We first need to edit the dataset settings to allow the dashboard to write to the collection. From the righthand menu click **Dataset Settings**. ![Choose the dashboard dataset settings](https://wixmp-833713b177cebf373f611808.wixmp.com/images/4a224790e25cb159444633e60bb1090a.png) Set the dataset mode to **Read & Write**. ![Set dashboard dataset mode to Read & Write](https://wixmp-833713b177cebf373f611808.wixmp.com/images/ae594a48519ea224f97d153b4b1cfb67.png) Now connect the elements in the repeater card as follows: * Heading 2 text: Title * Heading 4 text: Description * Icon button: Under **Connections** > **Click action connects to**, select **Delete**. Our nonempty state now can display poems from the collection and delete them. Next we’ll create the capability to add poems from the dashboard. 1. In order to add poems to the collection, we’ll create an entirely new dashboard page. Return to the **Dashboard Interface** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/84cde788fc36d0c001c02a63979a724e.png) section and at the bottom of the menu click **+ Create Extension**. 1. Rename the new dashboard page to **Add poem**. We actually don’t want this dashboard page to be visible in the menu–we only want a Wix user to access it from the **Poems Manager** page. To set this behavior, click **More Actions** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7860e419024a15bd57bf0e117b2894a3.png) > **Hide from dashboard menu**. 1. To set up the UI for the **Add poem** page, first edit the existing elements as follows: - State 01: **Add a poem** - Page subtitle: delete - Card title: **Poem Details** - Card subtitle: delete - Main Action button: Change text to **Save** and change the ID to `#savePoemButton`. - Secondary Action button: Change text to **Cancel** and change the ID to `#cancelButton`. When you complete these changes, the top of your dashboard page should look like this: ![What your add a poem dashboard page should look like](https://wixmp-833713b177cebf373f611808.wixmp.com/images/2b01cd2398a8802042ca1424d7a96633.png) 1. Now we’ll create a form that will allow the user to add a poem’s details. To do so, add the following elements in the order shown and set their IDs in the **Properties & Events** panel: | | | | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | ---------------- | | Element type | ID | Text | | Text Input | #poemTitle | Poem title | | Text Input | #poemPrice | Price per line | | Text Input | #poemDescription | Poem description | | Text Input | #poemId | Catalog ID | | Upload **+ Add Product** Button ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/20778e023efe2facabb90df4657b138d.png) | #addVariantButton | Add Variant | Beneath these elements, add a repeater: ![Select repeater with 3 horizontal boxes](https://wixmp-833713b177cebf373f611808.wixmp.com/images/857d4e28ece30409a9c0c4d35860f5a8.png) We’ll use this repeater to give the Wix user control over how many variants they want to add. Adjust the size of the repeater to align with the other elements, and change the repeater’s ID to `variantsList`. With the repeater still selected, in the right menu edit the number of elements: ![Manage repeater items](https://wixmp-833713b177cebf373f611808.wixmp.com/images/f0d15b5712c270f456c73a8fef0f2977.png) We want to start with just one element in the repeater, so delete 2 of the boxes: ![Delete boxes from repeater](https://wixmp-833713b177cebf373f611808.wixmp.com/images/d8bacd54c841eee28683f2aa5192e3ee.png) Add the following elements to the remaining box in the repeater: | | | | | --------------------- | -------------------- | ------------- | | **Element type** | **ID** | **Text** | | Text Input | #variantLabel | Variant label | | Text Input | #variantId | Variant ID | | Trash can icon button | #deleteVariantButton | | The complete UI should look like this: ![Full add a poem dashboard page](https://wixmp-833713b177cebf373f611808.wixmp.com/images/cfc4d7f8a87acef573f21e0d3f1110a9.png) Now that we’ve designed the dashboard UI, in the next section we’ll add the code that will enable the user to actually add a new poem to the collection. ### Dashboard code Before we dive into the code, let’s consider exactly what we need the code to do: - Insert the user inputs into the collection when the user clicks **Save**. - Return to the **Poems Manager** page after the user clicks either **Save** or **Cancel**. - Add new fields for variant information each time the user clicks **+ Add Variant**. - Delete a set of variant fields when the user clicks `#deleteVariantButton`. As it turns out, the trickiest part of the code will be controlling the UI to add and remove variants, so we’ll start with that. 1. Open the code panel for the **Add poem** page. Delete the existing code and add the following: ```js import wixDashboard from 'wix-dashboard'; import wixData from 'wix-data'; $w.onReady(function () { let variantsArray = []; $w('#variantsList').hide(); }); ``` **Lines 1-2**. We import 2 packages for handling data and moving between dashboard pages. These packages aren’t necessary for the repeater code but will become important later. **Line 5**. Inside the `onReady()` handler, we first create a blank array called `variants` array. We’ll use this array to set the `#variantsList` repeater [data](https://dev.wix.com/docs/velo/velo-only-apis/$w/repeater/data.md), which will control the number of variants that appear in UI. **Line 6**. Hide the variants list when the **Add poem** page first loads. 1. Next, we add the logic to add a variant: ```js $w("#variantsList").onItemReady(($item, itemData, index) => { $item("#variantId").value = itemData.value; $item("#variantLabel").value = itemData.label; $item('#deleteVariantButton').onClick( () => { variantsArray.splice(itemData.realIndex,1) variantsArray.forEach((item, index) => { item.realIndex = index; }); $w('#variantsList').data = variantsArray; }) }); $w('#addVariantButton').onClick( () => { if ($w('#variantsList').hidden) { $w('#variantsList').show(); } let highestId = 0; variantsArray.forEach((item, index) => { if(Number(item._id) >= highestId) highestId = Number(item._id) + 1; }); const newItem = { _id: highestId.toString(), value: '', label: '', realIndex: variantsArray.length }; variantsArray.push(newItem); $w('#variantsList').data = variantsArray; }); ``` Let’s start by breaking down the `onClick()` handler code first: **Lines 14-16**. Unhide the repeater in case it was still hidden. **Lines 18-21**. Perform a calculation to track the correct index. This helps later if we need to delete a variant. **Lines 23-28**. Create a new item to insert into the array. The `value` and `label` fields, which correspond to the 2 input fields in the repeater item, are blank. The result is that a new set of blank fields is added to `#variantsList`. **Lines 29-30**. Push the new item to `variantsArray` and set the repeater data equal to the updated array. This triggers the [`onItemReady()`](https://dev.wix.com/docs/velo/velo-only-apis/$w/repeater/on-item-ready.md) handler above. This brings us back up to the top of the code: **Lines 2-3**. Create the 2 new input fields with blank values. **Lines 4-10**. Handle the behavior for when the `#deleteVariantButton` is clicked. Remove the selected item from `variantsArray` and set the repeater data equal to the reduced array. The end result of all this logic is behavior like this: ![Example of the add variant behavior](https://wixmp-833713b177cebf373f611808.wixmp.com/images/56c2e4efbfe9b946e9b81d97c7c18120.gif) 1. Let’s move on now to the part of the code that controls the save and cancel actions. When both of these actions complete, we want to navigate the user back to the **Poems Manager** dashboard page. Previously, we imported the `wix-dashboard` package. In Blocks, we can use the [`navigate()`](https://dev.wix.com/docs/sdk/host-modules/dashboard/navigate.md) function from this package to open a different dashboard page from our current one. Let’s start with the code for the **Cancel** button as it is simpler. When the user clicks the **Cancel** button, we don’t need to store anything. We can simply navigate back to **Poems Manager**: ```js $w('#cancelButton').onClick( ()=> { wixDashboard.navigate({ pageId: "1e6634d9-5584-48eb-950d-4cb867fed8bb" // Poems Manager page ID }) }) ``` The only piece of information we need here is the ID of the page we want to go to, in this case **Poems Manager**. To find this ID, open the **Dashboard Interface** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/84cde788fc36d0c001c02a63979a724e.png) and click **More Actions** ![](https://wixmp-833713b177cebf373f611808.wixmp.com/images/7860e419024a15bd57bf0e117b2894a3.png) > **Page settings** next to **Poems Manager**. Next to **Page ID** click **Copy** and close out of the modal. Paste the copied ID as the value for the `pageId` parameter. We can start the **Save** logic in a similar manner: ```js $w("#savePoemButton").onClick(() => { wixDashboard.navigate({ pageId: "1e6634d9-5584-48eb-950d-4cb867fed8bb" // Poems Manager page ID }); }); ``` However, when we save we also need to store the details that the user input. To do so, add the following code before `navigate()`: ```js const data = []; $w('#variantsList').forEachItem(($item, itemData, index) => { data.push({ _id: index.toString(), label: $item("#variantLabel").value, value: $item("#variantId").value }) }); $w('#variantsList').data = data; const toInsert = { title: $w('#poemTitle').value, price: $w('#poemPrice').value, description: $w('#poemDescription').value, mainProductId: $w('#poemId').value, variants: $w('#variantsList').data } try { await wixData.insert("", toInsert); wixDashboard.navigate({ pageId: "1e6634d9-5584-48eb-950d-4cb867fed8bb" // Return to Poems Manager }); } catch(error) { console.error(error); } ``` **Lines 1-10**. Get the values from each item in the `#variantsList` repeater. **Lines 11-17**. Create a new object containing the values of all the input fields, including all the variants. **Line 18**. Using the `wix-data` package we imported at the top of code, [insert](https://dev.wix.com/docs/sdk/backend-modules/data/items/insert.md) the new poem data into the **Poems** collection. This completes the code for the **Add poem** page. Here’s the full code: ```js import wixDashboard from 'wix-dashboard'; import wixData from 'wix-data'; $w.onReady(function () { let variantsArray = []; $w('#variantsList').hide(); $w("#variantsList").onItemReady(($item, itemData, index) => { $item("#variantId").value = itemData.value; $item("#variantLabel").value = itemData.label; $item('#deleteVariantButton').onClick( () => { variantsArray.splice(itemData.realIndex,1) variantsArray.forEach((item, index) => { item.realIndex = index; }); $w('#variantsList').data = variantsArray; }) }); $w('#addVariantButton').onClick( () => { if ($w('#variantsList').hidden) { $w('#variantsList').show(); } let highestId = 0; variantsArray.forEach((item, index) => { if(Number(item._id) >= highestId) highestId = Number(item._id) + 1; }); const newItem = { _id: highestId.toString(), value: '', label: '', realIndex: variantsArray.length }; variantsArray.push(newItem); $w('#variantsList').data = variantsArray; }); $w("#savePoemButton").onClick(() => { const data = []; $w('#variantsList').forEachItem(($item, itemData, index) => { data.push({ _id: index.toString(), label: $item("#variantLabel").value, value: $item("#variantId").value }) }); $w('#variantsList').data = data; const toInsert = { title: $w('#poemTitle').value, price: $w('#poemPrice').value, description: $w('#poemDescription').value, mainProductId: $w('#poemId').value, variants: $w('#variantsList').data } try { await wixData.insert("", toInsert); wixDashboard.navigate({ pageId: "1e6634d9-5584-48eb-950d-4cb867fed8bb" // Return to Poems Manager }); wixDashboard.navigate({ pageId: "1e6634d9-5584-48eb-950d-4cb867fed8bb" // Poems Manager page ID }); } catch(error) { console.error(error); } }); $w('#cancelButton').onClick( ()=> { wixDashboard.navigate({ pageId: "1e6634d9-5584-48eb-950d-4cb867fed8bb" // Poems Manager page ID }) }) }); ``` Next, we'll write the code for **Poems Manager**. 1. Return to the **Poems Manager** page in the dashboard interface and open the code panel. We’ll start with the import statements we added on the other page: ```js import wixDashboard from 'wix-dashboard'; import wixData from 'wix-data'; ``` 1. Recall that our **Poems Manager** page has 2 states: empty and nonempty. Whenever the page loads, it needs to determine which state to display. To determine the state, we simply query the **Poems** collection to check if there are items to display. Below the import statements, add the following function: ```js async function determineManagerState() { try { const result = await wixData .query("") .find(); if (result.items.length > 0) { $w('#multiStateBox1').changeState("poemsList"); } else { $w('#multiStateBox1').changeState("emptyState"); } } catch(error) { console.log(error); } } ``` `determineManagerState()` queries the collection to see if there are poems, and based on the length of the results sets the current state of the multi-state box. Beneath `determineManagerState()`, we add an `onReady()` handler: ```js $w.onReady(async function () { determineManagerState(); $w('#listOfPoems').onItemRemoved((itemData) => { determineManagerState(); }) $w('#mainAddPoemButton').onClick( () => { wixDashboard.navigate({ pageId: "9ceebb9d-1a74-4d40-ace5-56c558987353" }) }) $w("#addFirstPoemButton").onClick(() => { wixDashboard.navigate({ pageId: "9ceebb9d-1a74-4d40-ace5-56c558987353" }) }) }); ``` **Line 2**. Determine the correct state when the page first loads. **Line 4-6**. Determine the correct state after an item has been removed from the repeater that lists the poems. This handles the edge case of a user deleting the last poem in the list. **Lines 8-18**. When a user clicks any button to add a new poem–whether in the empty state or not–navigate to the **Add poem** page. The page ID can be located in the dashboard interface section under **More Actions**. Here’s the full code for the **Poems Manager** page: ```js import wixDashboard from 'wix-dashboard'; import wixData from 'wix-data'; async function determineManagerState() { try { const result = await wixData .query("") .find(); if (result.items.length > 0) { $w('#multiStateBox1').changeState("poemsList"); } else { $w('#multiStateBox1').changeState("emptyState"); } } catch(error) { console.log(error); } } $w.onReady(async function () { determineManagerState(); $w('#listOfPoems').onItemRemoved((itemData) => { determineManagerState(); }) $w('#mainAddPoemButton').onClick( () => { wixDashboard.navigate({ pageId: "9ceebb9d-1a74-4d40-ace5-56c558987353" }) }) $w("#addFirstPoemButton").onClick(() => { wixDashboard.navigate({ pageId: "9ceebb9d-1a74-4d40-ace5-56c558987353" }) }) }); ``` Our app now has 2 functioning dashboard pages, along with a working item page. You can preview some of the features in Blocks. We’ll test everything in the last step of this tutorial, but first, we need to complete the final part of our app and implement the Get Catalog service plugin. **Next up:** [Step 5 | Implement a Self-hosted Catalog](https://dev.wix.com/docs/build-apps/get-started/tutorials/tutorial-build-an-e-commerce-business-solution/step-5-implement-a-self-hosted-catalog-service-plugin.md)