Browse Source

docs: Add docs on dashboard pages & update navigation docs

Michael Bromley 3 months ago
parent
commit
e73e0afa58
25 changed files with 416 additions and 378 deletions
  1. 2 0
      docs/docs/guides/developer-guide/cli/index.md
  2. BIN
      docs/docs/guides/developer-guide/cli/schema-command.webp
  3. 30 230
      docs/docs/guides/extending-the-dashboard/creating-pages/detail-pages.md
  4. 90 0
      docs/docs/guides/extending-the-dashboard/creating-pages/index.md
  5. 0 0
      docs/docs/guides/extending-the-dashboard/creating-pages/list-pages.md
  6. 106 0
      docs/docs/guides/extending-the-dashboard/data-fetching/index.md
  7. 23 1
      docs/docs/guides/extending-the-dashboard/extending-overview/index.md
  8. BIN
      docs/docs/guides/extending-the-dashboard/navigation/dev-mode-nav.webp
  9. 40 36
      docs/docs/guides/extending-the-dashboard/navigation/index.md
  10. BIN
      docs/docs/guides/extending-the-dashboard/navigation/unauthenticated-page.webp
  11. 7 0
      docs/docs/guides/getting-started/graphql-intro/index.mdx
  12. 10 3
      docs/docs/reference/core-plugins/dashboard-plugin/dashboard-plugin-options.md
  13. 12 4
      docs/docs/reference/core-plugins/dashboard-plugin/index.md
  14. 2 2
      docs/docs/reference/dashboard/detail-views/detail-page.md
  15. 1 31
      docs/docs/reference/dashboard/extensions-api/login.md
  16. 16 33
      docs/docs/reference/dashboard/extensions-api/navigation.md
  17. 23 4
      docs/docs/reference/dashboard/extensions-api/page-blocks.md
  18. 1 1
      docs/docs/reference/dashboard/hooks/use-auth.md
  19. 1 1
      docs/docs/reference/dashboard/hooks/use-channel.md
  20. 3 2
      docs/docs/reference/dashboard/list-views/list-page.md
  21. 3 3
      docs/docs/reference/dashboard/page-layout/page-action-bar.md
  22. 4 4
      docs/docs/reference/dashboard/page-layout/page-block.md
  23. 1 1
      docs/docs/reference/dashboard/page-layout/page-title.md
  24. 9 3
      docs/sidebars.js
  25. 32 19
      packages/dashboard/src/lib/framework/nav-menu/nav-menu-extensions.ts

+ 2 - 0
docs/docs/guides/developer-guide/cli/index.md

@@ -297,6 +297,8 @@ yarn vendure schema
 </TabItem>
 </TabItem>
 </Tabs>
 </Tabs>
 
 
+![Schema command](./schema-command.webp)
+
 ### Non-Interactive Mode
 ### Non-Interactive Mode
 
 
 To automate or quickly generate a schema in one command
 To automate or quickly generate a schema in one command

BIN
docs/docs/guides/developer-guide/cli/schema-command.webp


+ 30 - 230
docs/docs/guides/extending-the-dashboard/cms-tutorial/index.md → docs/docs/guides/extending-the-dashboard/creating-pages/detail-pages.md

@@ -1,246 +1,50 @@
 ---
 ---
-title: 'Tutorial: Building a CMS Plugin'
+title: 'Creating Detail Pages'
 ---
 ---
 
 
 import Tabs from '@theme/Tabs';
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
 import TabItem from '@theme/TabItem';
 
 
-Follow this guide to see how to extend the dashboard with custom pages, blocks, and components.
+## Setup
 
 
-We will create a brand new `CmsPlugin` that implements a simple content management system (CMS) for Vendure in
-order to demonstrate how to extend the dashboard.
+:::info
+This guide assumes you have a `CmsPlugin` with an `Article` entity, as covered in the [Extending the Dashboard: Plugin Setup](/guides/extending-the-dashboard/extending-overview/#plugin-setup) guide.
+:::
 
 
-## Creating the plugin
+Detail pages can be created for any entity which has been exposed via the Admin API. Following the
+above setup of the `CmsPlugin` will result in the following additions to your API schema:
 
 
-Let's create the plugin:
-
-```bash
-npx vendure add --plugin cms
-```
-
-Now let's add an entity to the plugin:
-
-```bash
-npx vendure add --entity Article --selected-plugin CmsPlugin
-```
-
-You now have your `CmsPlugin` created with a new `Article` entity. You can find the plugin in the `./src/plugins/cms` directory.
-
-Let's edit the entity to add the appropriate fields:
-
-```ts title="src/plugins/cms/entities/article.entity.ts"
-import { DeepPartial, HasCustomFields, VendureEntity } from '@vendure/core';
-import { Column, Entity } from 'typeorm';
-
-export class ArticleCustomFields {}
-
-@Entity()
-export class Article extends VendureEntity implements HasCustomFields {
-    constructor(input?: DeepPartial<Article>) {
-        super(input);
-    }
-
-    @Column()
-    slug: string;
-
-    @Column()
-    title: string;
-
-    @Column('text')
-    body: string;
-
-    @Column()
-    isPublished: boolean;
-
-    @Column(type => ArticleCustomFields)
-    customFields: ArticleCustomFields;
+```graphql
+type Article implements Node {
+    id: ID!
+    createdAt: DateTime!
+    updatedAt: DateTime!
+    slug: String!
+    title: String!
+    body: String!
+    isPublished: Boolean!
 }
 }
-```
-
-Now let's create a new `ArticleService` to handle the business logic of our new entity:
 
 
-```bash
-npx vendure add --service ArticleService --selected-plugin CmsPlugin --selected-entity Article
-```
-
-The service will be created in the `./src/plugins/cms/services` directory.
-
-Finally, we'll extend the GraphQL API to expose those CRUD operations:
-
-```bash
-npx vendure add --api-extension CmsPlugin --selected-service ArticleService --query-name ArticleQuery
-```
-
-Now the api extensions and resolver has been created in the `./src/plugins/cms/api-extensions` directory.
-
-The last step is to create a migration for our newly-created entity:
-
-```bash
-npx vendure migrate --generate article
-```
-
-## Setting up Dashboard extensions
-
-Dashboard extensions are declared directly on the plugin metadata. Unlike the old AdminUiPlugin, you do not need to separately
-declare ui extensions anywhere except on the plugin itself.
-
-```ts title="src/plugins/cms/cms.plugin.ts"
-@VendurePlugin({
-    // ...
-    entities: [Article],
-    adminApiExtensions: {
-        schema: adminApiExtensions,
-        resolvers: [ArticleAdminResolver],
-    },
-    // highlight-next-line
-    dashboard: './dashboard/index.tsx',
-})
-export class CmsPlugin {
-    // ...
+type Query {
+    # ...
+    article(id: ID!): Article
 }
 }
-```
-
-Now we'll create the entry point of our dashboard extension:
-
-```tsx title="src/plugins/cms/dashboard/index.tsx"
-import { defineDashboardExtension } from '@vendure/dashboard';
-
-export default defineDashboardExtension({
-    // Let's add a simple test page to check things are working
-    routes: [
-        {
-            component: () => <div>Test Page Works!</div>,
-            path: '/test',
-            navMenuItem: {
-                id: 'test',
-                title: 'Test Page',
-                sectionId: 'catalog',
-            },
-        },
-    ],
-});
-```
-
-Restart the Vite server (`q, enter` to quit if still running), and then you should be able to see your new test page!
-
-![Test Page](../getting-started/test-page.webp)
 
 
-## Creating a list page
-
-Now that the test page is working, let's create a list page for our `Article` entity.
-
-First we'll create a new `article-list.tsx` file in the `./src/plugins/cms/dashboard` directory:
-
-```tsx title="src/plugins/cms/dashboard/article-list.tsx"
-import {
-    Button,
-    DashboardRouteDefinition,
-    ListPage,
-    PageActionBarRight,
-    DetailPageButton,
-} from '@vendure/dashboard';
-import { Link } from '@tanstack/react-router';
-import { PlusIcon } from 'lucide-react';
-
-// This function is generated for you by the `vendureDashboardPlugin` in your Vite config.
-// It uses gql-tada to generate TypeScript types which give you type safety as you write
-// your queries and mutations.
-import { graphql } from '@/gql';
-
-// The fields you select here will be automatically used to generate the appropriate columns in the
-// data table below.
-const getArticleList = graphql(`
-    query GetArticles($options: ArticleListOptions) {
-        articles(options: $options) {
-            items {
-                id
-                createdAt
-                updatedAt
-                isPublished
-                title
-                slug
-                body
-                customFields
-            }
-        }
-    }
-`);
-
-const deleteArticleDocument = graphql(`
-    mutation DeleteArticle($id: ID!) {
-        deleteArticle(id: $id) {
-            result
-        }
-    }
-`);
-
-export const articleList: DashboardRouteDefinition = {
-    navMenuItem: {
-        sectionId: 'catalog',
-        id: 'articles',
-        url: '/articles',
-        title: 'CMS Articles',
-    },
-    path: '/articles',
-    loader: () => ({
-        breadcrumb: 'Articles',
-    }),
-    component: route => (
-        <ListPage
-            pageId="article-list"
-            title="Articles"
-            listQuery={getArticleList}
-            deleteMutation={deleteArticleDocument}
-            route={route}
-            customizeColumns={{
-                title: {
-                    cell: ({ row }) => {
-                        const post = row.original;
-                        return <DetailPageButton id={post.id} label={post.title} />;
-                    },
-                },
-            }}
-        >
-            <PageActionBarRight>
-                <Button asChild>
-                    <Link to="./new">
-                        <PlusIcon className="mr-2 h-4 w-4" />
-                        New article
-                    </Link>
-                </Button>
-            </PageActionBarRight>
-        </ListPage>
-    ),
-};
-```
-
-Let's register this route (and we can also remove the test page) in our `index.tsx` file:
-
-```tsx title="src/plugins/cms/dashboard/index.tsx"
-import { defineDashboardExtension } from '@vendure/dashboard';
-
-// highlight-next-line
-import { articleList } from './article-list';
-
-export default defineDashboardExtension({
-    routes: [
-        // highlight-next-line
-        articleList,
-    ],
-});
+type Mutation {
+    # ...
+    createArticle(input: CreateArticleInput!): Article!
+    updateArticle(input: UpdateArticleInput!): Article!
+    deleteArticle(id: ID!): DeletionResponse!
+}
 ```
 ```
 
 
-You should now be able to see the list view, which will be empty:
-
-![Empty List](../getting-started/list-view-empty.webp)
-
-## Creating a detail page
+## Simple Detail Pages
 
 
 Now let's create a detail page so we can start adding articles.
 Now let's create a detail page so we can start adding articles.
 
 
 We'll begin with the simplest approach, where the form will be auto-generated for us based on the GraphQL schema
 We'll begin with the simplest approach, where the form will be auto-generated for us based on the GraphQL schema
 using the [DetailPage](/reference/dashboard/detail-views/detail-page) component.
 using the [DetailPage](/reference/dashboard/detail-views/detail-page) component.
-This is useful for quickly getting started, but you will probably want to customize the form later on.
+This is useful for quickly getting started, but you can also to customize the form later on.
 
 
 Create a new file called `article-detail.tsx` in the `./src/plugins/cms/dashboard` directory:
 Create a new file called `article-detail.tsx` in the `./src/plugins/cms/dashboard` directory:
 
 
@@ -507,10 +311,6 @@ In the above example, we have:
 - Used the [Page](/reference/dashboard/page-layout/page), [PageTitle](/reference/dashboard/page-layout/page-title),
 - Used the [Page](/reference/dashboard/page-layout/page), [PageTitle](/reference/dashboard/page-layout/page-title),
   [PageActionBar](/reference/dashboard/page-layout/page-action-bar) and [PageLayout](/reference/dashboard/page-layout) components to create a layout for our page.
   [PageActionBar](/reference/dashboard/page-layout/page-action-bar) and [PageLayout](/reference/dashboard/page-layout) components to create a layout for our page.
 - Used [PageBlock](/reference/dashboard/page-layout/page-block) components to structure the page into blocks.
 - Used [PageBlock](/reference/dashboard/page-layout/page-block) components to structure the page into blocks.
-- Used custom form components (such as the `RichTextInput`) to better represent the data.
-
-## API Reference
-
-A partial API reference of the new Dashboard API can be found here:
-
-- [Dashboard API Reference](/reference/dashboard/extensions-api/)
+- Used [FormFieldWrapper](/reference/dashboard/form-components/form-field-wrapper) around form components for consistent styling and
+  layout of inputs.
+- Used custom form components (such as the [RichTextInput](/reference/dashboard/form-components/rich-text-input)) to better represent the data.

+ 90 - 0
docs/docs/guides/extending-the-dashboard/creating-pages/index.md

@@ -0,0 +1,90 @@
+---
+title: 'Creating Pages'
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Page Structure
+
+All pages in the Dashboard follow this structure:
+
+```tsx title="src/plugins/example/dashboard/test-page.tsx"
+import { Page, PageBlock, PageLayout, PageTitle } from '@vendure/dashboard';
+
+export function TestPage() {
+    return (
+        <Page pageId="test-page">
+            <PageTitle>Test Page</PageTitle>
+            <PageLayout>
+                <PageBlock column="main" blockId="main-stuff">
+                    This will display in the main area
+                </PageBlock>
+                <PageBlock column="side" blockId="side-stuff">
+                    This will display in the side area
+                </PageBlock>
+            </PageLayout>
+        </Page>
+    )
+}
+```
+
+- [Page component](/reference/dashboard/page-layout/page)
+  - [PageTitle component](/reference/dashboard/page-layout/page-title)
+  - [PageLayout component](/reference/dashboard/page-layout/page-layout)
+    - [PageBlock components](/reference/dashboard/page-layout/page-block)
+
+Following this structure ensures that:
+- Your pages look consistent with the rest of the Dashboard
+- Your page content is responsive
+- Your page can be further extended using the [pageBlocks API](/guides/extending-the-dashboard/page-blocks/)
+
+:::info
+Note that the [ListPage](/reference/dashboard/list-views/list-page) and [DetailPage](/reference/dashboard/detail-views/detail-page)
+components internally use this same structure, so when using those top-level components you don't need to wrap them
+in `Page` etc.
+:::
+
+## Page Routes & Navigation
+
+Once you have defined a page component, you'll need to make it accessible to users with:
+
+- A route (url) by which it can be accessed
+- Usually a navigation bar entry in the main side navigation of the Dashboard
+
+Both of these are handled using the [DashboardRouteDefinition API](/reference/dashboard/extensions-api/routes):
+
+```tsx title="src/plugins/example/dashboard/index.tsx"
+import { defineDashboardExtension } from '@vendure/dashboard';
+
+import { TestPage } from './test-page';
+
+defineDashboardExtension({
+    routes: [
+        {
+            // The TestPage will be available at e.g. 
+            // http://localhost:5173/dashboard/test
+            path: '/test',
+            // The loader function is allows us to define breadcrumbs
+            loader: () => ({ breadcrumb: 'Test Page' }),
+            // Here we define the nav menu items
+            navMenuItem: {
+                // a unique ID
+                id: 'test',
+                // the nav menu item label
+                title: 'Test Page',
+                // which section it should appear in
+                sectionId: 'catalog',
+            },
+            component: TestPage,
+        },
+    ],
+});
+```
+
+:::info
+For a complete guide to the navigation options available, see the [Navigation guide](/guides/extending-the-dashboard/navigation/)
+:::
+
+
+

+ 0 - 0
docs/docs/guides/extending-the-dashboard/creating-list-pages/index.md → docs/docs/guides/extending-the-dashboard/creating-pages/list-pages.md


+ 106 - 0
docs/docs/guides/extending-the-dashboard/data-fetching/index.md

@@ -0,0 +1,106 @@
+## API Client
+
+The API client is the primary way to send queries and mutations to the Vendure backend. It handles channel tokens and authentication automatically.
+
+### Importing the API Client
+
+```tsx
+import { api } from '@vendure/dashboard';
+```
+
+The API client exposes two main methods:
+
+- `query` - For GraphQL queries
+- `mutate` - For GraphQL mutations
+
+### Using with TanStack Query
+
+The API client is designed to work seamlessly with TanStack Query for optimal data fetching and caching:
+
+#### Query Example
+
+```tsx
+import { useQuery } from '@tanstack/react-query';
+import { api } from '@vendure/dashboard';
+import { graphql } from '@/gql';
+
+const getProductsQuery = graphql(`
+    query GetProducts($options: ProductListOptions) {
+        products(options: $options) {
+            items {
+                id
+                name
+                slug
+            }
+            totalItems
+        }
+    }
+`);
+
+function ProductList() {
+    const { data, isLoading, error } = useQuery({
+        queryKey: ['products'],
+        queryFn: () =>
+            api.query(getProductsQuery, {
+                options: {
+                    take: 10,
+                    skip: 0,
+                },
+            }),
+    });
+
+    if (isLoading) return <div>Loading...</div>;
+    if (error) return <div>Error: {error.message}</div>;
+
+    return <ul>{data?.products.items.map(product => <li key={product.id}>{product.name}</li>)}</ul>;
+}
+```
+
+#### Mutation Example
+
+```tsx
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { api } from '@vendure/dashboard';
+import { graphql } from '@/gql';
+import { toast } from 'sonner';
+
+const updateProductMutation = graphql(`
+    mutation UpdateProduct($input: UpdateProductInput!) {
+        updateProduct(input: $input) {
+            id
+            name
+            slug
+        }
+    }
+`);
+
+function ProductForm({ product }) {
+    const queryClient = useQueryClient();
+
+    const mutation = useMutation({
+        mutationFn: input => api.mutate(updateProductMutation, { input }),
+        onSuccess: () => {
+            // Invalidate and refetch product queries
+            queryClient.invalidateQueries({ queryKey: ['products'] });
+            toast.success('Product updated successfully');
+        },
+        onError: error => {
+            toast.error('Failed to update product', {
+                description: error.message,
+            });
+        },
+    });
+
+    const handleSubmit = data => {
+        mutation.mutate({
+            id: product.id,
+            ...data,
+        });
+    };
+
+    return (
+        // Form implementation
+        <form onSubmit={handleSubmit}>{/* Form fields */}</form>
+    );
+}
+```

+ 23 - 1
docs/docs/guides/extending-the-dashboard/extending-overview/index.md

@@ -24,7 +24,7 @@ npx vendure add --plugin cms
 Now let's add an entity to the plugin:
 Now let's add an entity to the plugin:
 
 
 ```bash
 ```bash
-npx vendure add --entity Article --selected-plugin CmsPlugin
+npx vendure add --entity Article --selected-plugin CmsPlugin --custom-fields
 ```
 ```
 
 
 You now have your `CmsPlugin` created with a new `Article` entity. You can find the plugin in the `./src/plugins/cms` directory.
 You now have your `CmsPlugin` created with a new `Article` entity. You can find the plugin in the `./src/plugins/cms` directory.
@@ -183,6 +183,28 @@ defineDashboardExtension({
 });
 });
 ```
 ```
 
 
+## IDE GraphQL Integration
+
+When extending the dashboard, you'll very often need to work with GraphQL documents for fetching data and executing mutations.
+
+Plugins are available for most popular IDEs & editors which provide auto-complete and type-checking for GraphQL operations
+as you write them. This is a huge productivity boost, and is **highly recommended**.
+
+- [GraphQL extension for VS Code](https://marketplace.visualstudio.com/items?itemName=GraphQL.vscode-graphql)
+- [GraphQL plugin for IntelliJ](https://plugins.jetbrains.com/plugin/8097-graphql) (including WebStorm)
+
+:::cli
+Run the `npx vendure schema` to generate a GraphQL schema file that your IDE plugin
+can use to provide autocomplete.
+:::
+
+1. Install the GraphQL plugin for your IDE
+2. Run `npx vendure schema --api admin` to generate a `schema.graphql` file in your root directory
+3. Create a `graphql.config.yml` file in your root directory with the following content:
+   ```yaml title="graphql.config.yml"
+   schema: 'schema.graphql'
+   ```
+
 ## Dev Mode
 ## Dev Mode
 
 
 Once you have logged in to the dashboard, you can toggle on "Dev Mode" using the user menu in the bottom left:
 Once you have logged in to the dashboard, you can toggle on "Dev Mode" using the user menu in the bottom left:

BIN
docs/docs/guides/extending-the-dashboard/navigation/dev-mode-nav.webp


+ 40 - 36
docs/docs/guides/extending-the-dashboard/navigation/index.md

@@ -41,6 +41,12 @@ The dashboard comes with several built-in sections:
 - **`marketing`** - For promotions and marketing tools
 - **`marketing`** - For promotions and marketing tools
 - **`settings`** - For configuration and admin settings
 - **`settings`** - For configuration and admin settings
 
 
+### Finding Section IDs & Ordering
+
+You can find the available IDs & their order value for all navigation sections and items using [Dev mode](/guides/extending-the-dashboard/extending-overview/#dev-mode):
+
+![Dev mode navigation ids](./dev-mode-nav.webp)
+
 ## Creating Custom Navigation Sections
 ## Creating Custom Navigation Sections
 
 
 You can create entirely new navigation sections with their own icons and ordering:
 You can create entirely new navigation sections with their own icons and ordering:
@@ -90,31 +96,10 @@ export default defineDashboardExtension({
 });
 });
 ```
 ```
 
 
-## Navigation Section Properties
-
-### Required Properties
-
-- **`id`**: Unique identifier for the section
-- **`title`**: Display name shown in the navigation
-
-### Optional Properties
-
-- **`icon`**: Lucide React icon component to display next to the section title
-- **`order`**: Number controlling the position of the section within its placement area (lower numbers appear first)
-- **`placement`**: Either `'top'` or `'bottom'` - determines which area of the sidebar the section appears in
-
-## Navigation Item Properties
+For documentation on all the configuration properties available, see the reference docs:
 
 
-### Required Properties
-
-- **`sectionId`**: ID of the section where this item should appear
-- **`id`**: Unique identifier for the menu item
-- **`title`**: Display text for the menu item
-
-### Optional Properties
-
-- **`url`**: Custom URL if different from the route path
-- **`icon`**: Icon for the individual menu item (rarely used as sections typically have icons)
+- [DashboardNavSectionDefinition](/reference/dashboard/extensions-api/navigation#dashboardnavsectiondefinition)
+- [NavMenuItem](/reference/dashboard/extensions-api/navigation#navmenuitem)
 
 
 ## Section Placement and Ordering
 ## Section Placement and Ordering
 
 
@@ -177,6 +162,37 @@ This means if you want to add a section between Catalog and Sales in the top are
 If you don't specify a `placement`, sections default to `'top'` placement.
 If you don't specify a `placement`, sections default to `'top'` placement.
 :::
 :::
 
 
+## Unauthenticated Routes
+
+By default, all navigation is assumed to be for authenticated routes, i.e. the routes are only accessible to administrators
+who are logged in.
+
+Sometimes you want to make a certain route accessible to unauthenticated users. For example, you may want to implement
+a completely custom login page or a password recovery page, which must be accessible to everyone.
+
+This is done by setting `authenticated: false` in your route definition:
+
+```tsx
+import { defineDashboardExtension } from '@vendure/dashboard';
+
+defineDashboardExtension({
+    routes: [
+        {
+            path: '/public',
+            component: () => (
+                <div className="flex h-screen items-center justify-center text-2xl">This is a public page!</div>
+            ),
+            // highlight-next-line
+            authenticated: false
+        },
+    ]
+});
+```
+
+This page will then be accessible to all users at `http://localhost:4873/dashboard/public`
+
+![Unauthenticated page](./unauthenticated-page.webp)
+
 ## Complete Example
 ## Complete Example
 
 
 Here's a comprehensive example showing how to create a complete navigation structure for a content management system:
 Here's a comprehensive example showing how to create a complete navigation structure for a content management system:
@@ -304,15 +320,3 @@ Common icons for navigation sections:
 5. **Keep section counts reasonable**: Avoid creating too many sections as it can clutter the navigation
 5. **Keep section counts reasonable**: Avoid creating too many sections as it can clutter the navigation
 6. **Use consistent naming**: Follow consistent patterns for menu item names within sections
 6. **Use consistent naming**: Follow consistent patterns for menu item names within sections
    :::
    :::
-
-## Finding Existing Sections
-
-:::info Discovering Section IDs
-To see what navigation sections are available, you can:
-
-1. Enable [Dev Mode](/guides/extending-the-dashboard/extending-overview/#dev-mode) in the dashboard
-2. Examine the navigation sidebar to see section IDs
-3. Look at other plugins' navigation configuration for examples
-   :::
-
-This gives you complete control over how your dashboard extensions integrate with the navigation system, allowing you to create a cohesive and well-organized user experience.

BIN
docs/docs/guides/extending-the-dashboard/navigation/unauthenticated-page.webp


+ 7 - 0
docs/docs/guides/getting-started/graphql-intro/index.mdx

@@ -547,6 +547,13 @@ Run the `npx vendure schema` to generate a GraphQL schema file that your IDE plu
 can use to provide autocomplete.
 can use to provide autocomplete.
 :::
 :::
 
 
+1. Install the GraphQL plugin for your IDE
+2. Run `npx vendure schema --api admin` to generate a `schema.graphql` file in your root directory
+3. Create a `graphql.config.yml` file in your root directory with the following content:
+   ```yaml title="graphql.config.yml"
+   schema: 'schema.graphql'
+   ```
+
 ## Code generation
 ## Code generation
 
 
 Code generation means the automatic generation of TypeScript types based on your GraphQL schema and your GraphQL operations. This is a very powerful feature that allows you to write your code in a type-safe manner, without you needing to manually write any types for your API calls.
 Code generation means the automatic generation of TypeScript types based on your GraphQL schema and your GraphQL operations. This is a very powerful feature that allows you to write your code in a type-safe manner, without you needing to manually write any types for your API calls.

+ 10 - 3
docs/docs/reference/core-plugins/dashboard-plugin/dashboard-plugin-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 
 ## DashboardPluginOptions
 ## DashboardPluginOptions
 
 
-<GenerationInfo sourceFile="packages/dashboard/plugin/dashboard.plugin.ts" sourceLine="27" packageName="@vendure/dashboard" />
+<GenerationInfo sourceFile="packages/dashboard/plugin/dashboard.plugin.ts" sourceLine="29" packageName="@vendure/dashboard" />
 
 
 Configuration options for the <a href='/reference/core-plugins/dashboard-plugin/#dashboardplugin'>DashboardPlugin</a>.
 Configuration options for the <a href='/reference/core-plugins/dashboard-plugin/#dashboardplugin'>DashboardPlugin</a>.
 
 
@@ -19,6 +19,7 @@ Configuration options for the <a href='/reference/core-plugins/dashboard-plugin/
 interface DashboardPluginOptions {
 interface DashboardPluginOptions {
     route: string;
     route: string;
     appDir: string;
     appDir: string;
+    viteDevServerPort?: number;
 }
 }
 ```
 ```
 
 
@@ -33,8 +34,14 @@ The route to the Dashboard UI.
 
 
 <MemberInfo kind="property" type={`string`}   />
 <MemberInfo kind="property" type={`string`}   />
 
 
-The path to the dashboard UI app dist directory. By default, the built-in dashboard UI
-will be served. This can be overridden with a custom build of the dashboard.
+The path to the dashboard UI app dist directory.
+### viteDevServerPort
+
+<MemberInfo kind="property" type={`number`} default={`5173`}   />
+
+The port on which to check for a running Vite dev server.
+If a Vite dev server is detected on this port, requests will be proxied to it
+instead of serving static files from appDir.
 
 
 
 
 </div>
 </div>

+ 12 - 4
docs/docs/reference/core-plugins/dashboard-plugin/index.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 
 ## DashboardPlugin
 ## DashboardPlugin
 
 
-<GenerationInfo sourceFile="packages/dashboard/plugin/dashboard.plugin.ts" sourceLine="101" packageName="@vendure/dashboard" />
+<GenerationInfo sourceFile="packages/dashboard/plugin/dashboard.plugin.ts" sourceLine="119" packageName="@vendure/dashboard" />
 
 
 This plugin serves the static files of the Vendure Dashboard and provides the
 This plugin serves the static files of the Vendure Dashboard and provides the
 GraphQL extensions needed for the order metrics on the dashboard index page.
 GraphQL extensions needed for the order metrics on the dashboard index page.
@@ -25,10 +25,16 @@ GraphQL extensions needed for the order metrics on the dashboard index page.
 First you need to set up compilation of the Dashboard, using the Vite configuration
 First you need to set up compilation of the Dashboard, using the Vite configuration
 described in the [Dashboard Getting Started Guide](/guides/extending-the-dashboard/getting-started/)
 described in the [Dashboard Getting Started Guide](/guides/extending-the-dashboard/getting-started/)
 
 
-Once set up, you run `npx vite build` to build the production version of the dashboard app.
+## Development vs Production
 
 
-The built app files will be output to the location specified by `build.outDir` in your Vite
-config file. This should then be passed to the `appDir` init option, as in the example below:
+When developing, you can run `npx vite` (or `npm run dev`) to start the Vite development server.
+The plugin will automatically detect if Vite is running on the default port (5173) and proxy
+requests to it instead of serving static files. This enables hot module replacement and faster
+development iterations.
+
+For production, run `npx vite build` to build the dashboard app. The built app files will be
+output to the location specified by `build.outDir` in your Vite config file. This should then
+be passed to the `appDir` init option, as in the example below:
 
 
 *Example*
 *Example*
 
 
@@ -41,6 +47,8 @@ const config: VendureConfig = {
     DashboardPlugin.init({
     DashboardPlugin.init({
       route: 'dashboard',
       route: 'dashboard',
       appDir: './dist/dashboard',
       appDir: './dist/dashboard',
+      // Optional: customize Vite dev server port (defaults to 5173)
+      viteDevServerPort: 3000,
     }),
     }),
   ],
   ],
 };
 };

+ 2 - 2
docs/docs/reference/dashboard/detail-views/detail-page.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 
 ## DetailPage
 ## DetailPage
 
 
-<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/page/detail-page.tsx" sourceLine="150" packageName="@vendure/dashboard" since="3.3.0" />
+<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/page/detail-page.tsx" sourceLine="147" packageName="@vendure/dashboard" since="3.3.0" />
 
 
 Auto-generates a detail page with a form based on the provided query and mutation documents.
 Auto-generates a detail page with a form based on the provided query and mutation documents.
 
 
@@ -30,7 +30,7 @@ Parameters
 
 
 ## DetailPageProps
 ## DetailPageProps
 
 
-<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/page/detail-page.tsx" sourceLine="42" packageName="@vendure/dashboard" since="3.3.0" />
+<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/page/detail-page.tsx" sourceLine="43" packageName="@vendure/dashboard" since="3.3.0" />
 
 
 Props to configure the <a href='/reference/dashboard/detail-views/detail-page#detailpage'>DetailPage</a> component.
 Props to configure the <a href='/reference/dashboard/detail-views/detail-page#detailpage'>DetailPage</a> component.
 
 

+ 1 - 31
docs/docs/reference/dashboard/extensions-api/login.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 
 ## DashboardLoginExtensions
 ## DashboardLoginExtensions
 
 
-<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/extension-api/types/login.ts" sourceLine="76" packageName="@vendure/dashboard" since="3.4.0" />
+<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/extension-api/types/login.ts" sourceLine="60" packageName="@vendure/dashboard" since="3.4.0" />
 
 
 Defines all available login page extensions.
 Defines all available login page extensions.
 
 
@@ -20,7 +20,6 @@ interface DashboardLoginExtensions {
     logo?: LoginLogoExtension;
     logo?: LoginLogoExtension;
     beforeForm?: LoginBeforeFormExtension;
     beforeForm?: LoginBeforeFormExtension;
     afterForm?: LoginAfterFormExtension;
     afterForm?: LoginAfterFormExtension;
-    loginImage?: LoginImageExtension;
 }
 }
 ```
 ```
 
 
@@ -41,11 +40,6 @@ Component to render before the login form.
 <MemberInfo kind="property" type={`<a href='/reference/dashboard/extensions-api/login#loginafterformextension'>LoginAfterFormExtension</a>`}   />
 <MemberInfo kind="property" type={`<a href='/reference/dashboard/extensions-api/login#loginafterformextension'>LoginAfterFormExtension</a>`}   />
 
 
 Component to render after the login form.
 Component to render after the login form.
-### loginImage
-
-<MemberInfo kind="property" type={`<a href='/reference/dashboard/extensions-api/login#loginimageextension'>LoginImageExtension</a>`}   />
-
-Custom login image component to replace the default image panel.
 
 
 
 
 </div>
 </div>
@@ -120,28 +114,4 @@ interface LoginAfterFormExtension {
 A React component that will be rendered after the login form.
 A React component that will be rendered after the login form.
 
 
 
 
-</div>
-
-
-## LoginImageExtension
-
-<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/extension-api/types/login.ts" sourceLine="59" packageName="@vendure/dashboard" since="3.4.0" />
-
-Defines a custom login image component that replaces the default image panel.
-
-```ts title="Signature"
-interface LoginImageExtension {
-    component: React.ComponentType;
-}
-```
-
-<div className="members-wrapper">
-
-### component
-
-<MemberInfo kind="property" type={`React.ComponentType`}   />
-
-A React component that will replace the default login image panel.
-
-
 </div>
 </div>

+ 16 - 33
docs/docs/reference/dashboard/extensions-api/navigation.md

@@ -61,16 +61,17 @@ Optional order number to control the position of this section in the sidebar.
 </div>
 </div>
 
 
 
 
-## NavMenuBaseItem
+## NavMenuItem
 
 
 <GenerationInfo sourceFile="packages/dashboard/src/lib/framework/nav-menu/nav-menu-extensions.ts" sourceLine="16" packageName="@vendure/dashboard" since="3.4.0" />
 <GenerationInfo sourceFile="packages/dashboard/src/lib/framework/nav-menu/nav-menu-extensions.ts" sourceLine="16" packageName="@vendure/dashboard" since="3.4.0" />
 
 
-The base configuration for navigation items and sections of the main app nav bar.
+Defines an items in the navigation menu.
 
 
 ```ts title="Signature"
 ```ts title="Signature"
-interface NavMenuBaseItem {
+interface NavMenuItem {
     id: string;
     id: string;
     title: string;
     title: string;
+    url: string;
     icon?: LucideIcon;
     icon?: LucideIcon;
     order?: number;
     order?: number;
     placement?: NavMenuSectionPlacement;
     placement?: NavMenuSectionPlacement;
@@ -84,22 +85,31 @@ interface NavMenuBaseItem {
 
 
 <MemberInfo kind="property" type={`string`}   />
 <MemberInfo kind="property" type={`string`}   />
 
 
-
+A unique ID for this nav menu item
 ### title
 ### title
 
 
 <MemberInfo kind="property" type={`string`}   />
 <MemberInfo kind="property" type={`string`}   />
 
 
+The title that will appear in the nav menu
+### url
 
 
+<MemberInfo kind="property" type={`string`}   />
+
+The url of the route which this nav item links to.
 ### icon
 ### icon
 
 
 <MemberInfo kind="property" type={`LucideIcon`}   />
 <MemberInfo kind="property" type={`LucideIcon`}   />
 
 
-
+An optional icon component to represent the item,
+which should be imported from `lucide-react`.
 ### order
 ### order
 
 
 <MemberInfo kind="property" type={`number`}   />
 <MemberInfo kind="property" type={`number`}   />
 
 
-
+The order is an number which allows you to control
+the relative position in relation to other items in the
+menu.
+A higher number appears further down the list.
 ### placement
 ### placement
 
 
 <MemberInfo kind="property" type={`NavMenuSectionPlacement`}   />
 <MemberInfo kind="property" type={`NavMenuSectionPlacement`}   />
@@ -113,31 +123,4 @@ This can be used to restrict the menu item to the given
 permission or permissions.
 permission or permissions.
 
 
 
 
-</div>
-
-
-## NavMenuItem
-
-<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/nav-menu/nav-menu-extensions.ts" sourceLine="38" packageName="@vendure/dashboard" since="3.4.0" />
-
-Defines an items in the navigation menu.
-
-```ts title="Signature"
-interface NavMenuItem extends NavMenuBaseItem {
-    url: string;
-}
-```
-* Extends: <code><a href='/reference/dashboard/extensions-api/navigation#navmenubaseitem'>NavMenuBaseItem</a></code>
-
-
-
-<div className="members-wrapper">
-
-### url
-
-<MemberInfo kind="property" type={`string`}   />
-
-The url of the route which this nav item links to.
-
-
 </div>
 </div>

+ 23 - 4
docs/docs/reference/dashboard/extensions-api/page-blocks.md

@@ -21,7 +21,8 @@ interface DashboardPageBlockDefinition {
     id: string;
     id: string;
     title?: React.ReactNode;
     title?: React.ReactNode;
     location: PageBlockLocation;
     location: PageBlockLocation;
-    component: React.FunctionComponent<{ context: PageContextValue }>;
+    component?: React.FunctionComponent<{ context: PageContextValue }>;
+    shouldRender?: (context: PageContextValue) => boolean;
     requiresPermission?: string | string[];
     requiresPermission?: string | string[];
 }
 }
 ```
 ```
@@ -32,27 +33,45 @@ interface DashboardPageBlockDefinition {
 
 
 <MemberInfo kind="property" type={`string`}   />
 <MemberInfo kind="property" type={`string`}   />
 
 
-
+An ID for the page block. Should be unique at least
+to the page in which it appears.
 ### title
 ### title
 
 
 <MemberInfo kind="property" type={`React.ReactNode`}   />
 <MemberInfo kind="property" type={`React.ReactNode`}   />
 
 
-
+An optional title for the page block
 ### location
 ### location
 
 
 <MemberInfo kind="property" type={`<a href='/reference/dashboard/extensions-api/page-blocks#pageblocklocation'>PageBlockLocation</a>`}   />
 <MemberInfo kind="property" type={`<a href='/reference/dashboard/extensions-api/page-blocks#pageblocklocation'>PageBlockLocation</a>`}   />
 
 
-
+The location of the page block. It specifies the pageId, and then the
+relative location compared to another existing block.
 ### component
 ### component
 
 
 <MemberInfo kind="property" type={`React.FunctionComponent&#60;{ context: PageContextValue }&#62;`}   />
 <MemberInfo kind="property" type={`React.FunctionComponent&#60;{ context: PageContextValue }&#62;`}   />
 
 
+The component to be rendered inside the page block.
+### shouldRender
+
+<MemberInfo kind="property" type={`(context: PageContextValue) =&#62; boolean`}  since="3.5.0"  />
+
+Control whether to render the page block depending on your custom
+logic.
+
+This can also be used to disable any built-in blocks you
+do not need to display.
 
 
+If you need to query aspects about the current context not immediately
+provided in the `PageContextValue`, you can also use hooks such as
+`useChannel` in this function.
 ### requiresPermission
 ### requiresPermission
 
 
 <MemberInfo kind="property" type={`string | string[]`}   />
 <MemberInfo kind="property" type={`string | string[]`}   />
 
 
+If provided, the logged-in user must have one or more of the specified
+permissions in order for the block to render.
 
 
+For more advanced control over rendering, use the `shouldRender` function.
 
 
 
 
 </div>
 </div>

+ 1 - 1
docs/docs/reference/dashboard/hooks/use-auth.md

@@ -23,7 +23,7 @@ function useAuth(): void
 
 
 ## AuthContext
 ## AuthContext
 
 
-<GenerationInfo sourceFile="packages/dashboard/src/lib/providers/auth.tsx" sourceLine="16" packageName="@vendure/dashboard" since="3.3.0" />
+<GenerationInfo sourceFile="packages/dashboard/src/lib/providers/auth.tsx" sourceLine="17" packageName="@vendure/dashboard" since="3.3.0" />
 
 
 Provides information about the current user & their authentication & authorization
 Provides information about the current user & their authentication & authorization
 status.
 status.

+ 1 - 1
docs/docs/reference/dashboard/hooks/use-channel.md

@@ -29,7 +29,7 @@ function useChannel(): void
 
 
 ## ChannelContext
 ## ChannelContext
 
 
-<GenerationInfo sourceFile="packages/dashboard/src/lib/providers/channel-provider.tsx" sourceLine="63" packageName="@vendure/dashboard" since="3.3.0" />
+<GenerationInfo sourceFile="packages/dashboard/src/lib/providers/channel-provider.tsx" sourceLine="65" packageName="@vendure/dashboard" since="3.3.0" />
 
 
 Provides information about the active channel, and the means to set a new
 Provides information about the active channel, and the means to set a new
 active channel.
 active channel.

+ 3 - 2
docs/docs/reference/dashboard/list-views/list-page.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 
 ## ListPage
 ## ListPage
 
 
-<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/page/list-page.tsx" sourceLine="452" packageName="@vendure/dashboard" since="3.3.0" />
+<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/page/list-page.tsx" sourceLine="459" packageName="@vendure/dashboard" since="3.3.0" />
 
 
 Auto-generates a list page with columns generated based on the provided query document fields.
 Auto-generates a list page with columns generated based on the provided query document fields.
 
 
@@ -48,6 +48,7 @@ const getArticleList = graphql(`
                 body
                 body
                 customFields
                 customFields
             }
             }
+            totalItems
         }
         }
     }
     }
 `);
 `);
@@ -113,7 +114,7 @@ Parameters
 
 
 ## ListPageProps
 ## ListPageProps
 
 
-<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/page/list-page.tsx" sourceLine="30" packageName="@vendure/dashboard" since="3.3.0" />
+<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/page/list-page.tsx" sourceLine="36" packageName="@vendure/dashboard" since="3.3.0" />
 
 
 Props to configure the <a href='/reference/dashboard/list-views/list-page#listpage'>ListPage</a> component.
 Props to configure the <a href='/reference/dashboard/list-views/list-page#listpage'>ListPage</a> component.
 
 

+ 3 - 3
docs/docs/reference/dashboard/page-layout/page-action-bar.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 
 ## PageActionBar
 ## PageActionBar
 
 
-<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx" sourceLine="312" packageName="@vendure/dashboard" since="3.3.0" />
+<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx" sourceLine="322" packageName="@vendure/dashboard" since="3.3.0" />
 
 
 *
 *
 A component for displaying the main actions for a page. This should be used inside the <a href='/reference/dashboard/page-layout/page#page'>Page</a> component.
 A component for displaying the main actions for a page. This should be used inside the <a href='/reference/dashboard/page-layout/page#page'>Page</a> component.
@@ -31,7 +31,7 @@ Parameters
 
 
 ## PageActionBarLeft
 ## PageActionBarLeft
 
 
-<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx" sourceLine="334" packageName="@vendure/dashboard" since="3.3.0" />
+<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx" sourceLine="344" packageName="@vendure/dashboard" since="3.3.0" />
 
 
 The PageActionBarLeft component should be used to display the left content of the action bar.
 The PageActionBarLeft component should be used to display the left content of the action bar.
 
 
@@ -48,7 +48,7 @@ Parameters
 
 
 ## PageActionBarRight
 ## PageActionBarRight
 
 
-<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx" sourceLine="427" packageName="@vendure/dashboard" since="3.3.0" />
+<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx" sourceLine="437" packageName="@vendure/dashboard" since="3.3.0" />
 
 
 The PageActionBarRight component should be used to display the right content of the action bar.
 The PageActionBarRight component should be used to display the right content of the action bar.
 
 

+ 4 - 4
docs/docs/reference/dashboard/page-layout/page-block.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 
 ## PageBlock
 ## PageBlock
 
 
-<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx" sourceLine="552" packageName="@vendure/dashboard" since="3.3.0" />
+<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx" sourceLine="562" packageName="@vendure/dashboard" since="3.3.0" />
 
 
 *
 *
 A component for displaying a block of content on a page. This should be used inside the <a href='/reference/dashboard/page-layout/#pagelayout'>PageLayout</a> component.
 A component for displaying a block of content on a page. This should be used inside the <a href='/reference/dashboard/page-layout/#pagelayout'>PageLayout</a> component.
@@ -39,7 +39,7 @@ Parameters
 
 
 ## PageBlockProps
 ## PageBlockProps
 
 
-<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx" sourceLine="501" packageName="@vendure/dashboard" since="3.3.0" />
+<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx" sourceLine="511" packageName="@vendure/dashboard" since="3.3.0" />
 
 
 Props used to configure the <a href='/reference/dashboard/page-layout/page-block#pageblock'>PageBlock</a> component.
 Props used to configure the <a href='/reference/dashboard/page-layout/page-block#pageblock'>PageBlock</a> component.
 
 
@@ -93,7 +93,7 @@ An optional set of CSS classes to apply to the block.
 
 
 ## FullWidthPageBlock
 ## FullWidthPageBlock
 
 
-<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx" sourceLine="597" packageName="@vendure/dashboard" since="3.3.0" />
+<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx" sourceLine="609" packageName="@vendure/dashboard" since="3.3.0" />
 
 
 **Status: Developer Preview**
 **Status: Developer Preview**
 
 
@@ -113,7 +113,7 @@ Parameters
 
 
 ## CustomFieldsPageBlock
 ## CustomFieldsPageBlock
 
 
-<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx" sourceLine="627" packageName="@vendure/dashboard" since="3.3.0" />
+<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx" sourceLine="639" packageName="@vendure/dashboard" since="3.3.0" />
 
 
 *
 *
 A component for displaying an auto-generated form for custom fields on a page.
 A component for displaying an auto-generated form for custom fields on a page.

+ 1 - 1
docs/docs/reference/dashboard/page-layout/page-title.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 
 ## PageTitle
 ## PageTitle
 
 
-<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx" sourceLine="297" packageName="@vendure/dashboard" since="3.3.0" />
+<GenerationInfo sourceFile="packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx" sourceLine="307" packageName="@vendure/dashboard" since="3.3.0" />
 
 
 A component for displaying the title of a page. This should be used inside the <a href='/reference/dashboard/page-layout/page#page'>Page</a> component.
 A component for displaying the title of a page. This should be used inside the <a href='/reference/dashboard/page-layout/page#page'>Page</a> component.
 
 

+ 9 - 3
docs/sidebars.js

@@ -142,13 +142,19 @@ const sidebars = {
             items: [
             items: [
                 'guides/extending-the-dashboard/getting-started/index',
                 'guides/extending-the-dashboard/getting-started/index',
                 'guides/extending-the-dashboard/extending-overview/index',
                 'guides/extending-the-dashboard/extending-overview/index',
-                'guides/extending-the-dashboard/creating-list-pages/index',
-
+                {
+                    type: 'category',
+                    label: 'Creating Pages',
+                    link: { type: 'doc', id: 'guides/extending-the-dashboard/creating-pages/index' },
+                    items: [
+                        'guides/extending-the-dashboard/creating-pages/list-pages',
+                        'guides/extending-the-dashboard/creating-pages/detail-pages',
+                    ],
+                },
                 'guides/extending-the-dashboard/navigation/index',
                 'guides/extending-the-dashboard/navigation/index',
                 'guides/extending-the-dashboard/page-blocks/index',
                 'guides/extending-the-dashboard/page-blocks/index',
                 'guides/extending-the-dashboard/action-bar-items/index',
                 'guides/extending-the-dashboard/action-bar-items/index',
                 'guides/extending-the-dashboard/theming/index',
                 'guides/extending-the-dashboard/theming/index',
-                'guides/extending-the-dashboard/cms-tutorial/index',
                 {
                 {
                     type: 'category',
                     type: 'category',
                     label: 'Custom Form Elements',
                     label: 'Custom Form Elements',

+ 32 - 19
packages/dashboard/src/lib/framework/nav-menu/nav-menu-extensions.ts

@@ -7,17 +7,46 @@ export type NavMenuSectionPlacement = 'top' | 'bottom';
 
 
 /**
 /**
  * @description
  * @description
- * The base configuration for navigation items and sections of the main app nav bar.
+ * Defines an items in the navigation menu.
  *
  *
  * @docsCategory extensions-api
  * @docsCategory extensions-api
  * @docsPage Navigation
  * @docsPage Navigation
  * @since 3.4.0
  * @since 3.4.0
  */
  */
-interface NavMenuBaseItem {
+interface NavMenuItem {
+    /**
+     * @description
+     * A unique ID for this nav menu item
+     */
     id: string;
     id: string;
+    /**
+     * @description
+     * The title that will appear in the nav menu
+     */
     title: string;
     title: string;
+    /**
+     * @description
+     * The url of the route which this nav item links to.
+     */
+    url: string;
+    /**
+     * @description
+     * An optional icon component to represent the item,
+     * which should be imported from `lucide-react`.
+     */
     icon?: LucideIcon;
     icon?: LucideIcon;
+    /**
+     * @description
+     * The order is an number which allows you to control
+     * the relative position in relation to other items in the
+     * menu.
+     * A higher number appears further down the list.
+     */
     order?: number;
     order?: number;
+    /**
+     * Whether this item should appear in the top of bottom section
+     * of the nav menu.
+     */
     placement?: NavMenuSectionPlacement;
     placement?: NavMenuSectionPlacement;
     /**
     /**
      * @description
      * @description
@@ -27,23 +56,7 @@ interface NavMenuBaseItem {
     requiresPermission?: string | string[];
     requiresPermission?: string | string[];
 }
 }
 
 
-/**
- * @description
- * Defines an items in the navigation menu.
- *
- * @docsCategory extensions-api
- * @docsPage Navigation
- * @since 3.4.0
- */
-export interface NavMenuItem extends NavMenuBaseItem {
-    /**
-     * @description
-     * The url of the route which this nav item links to.
-     */
-    url: string;
-}
-
-export interface NavMenuSection extends NavMenuBaseItem {
+export interface NavMenuSection extends Omit<NavMenuItem, 'url'> {
     defaultOpen?: boolean;
     defaultOpen?: boolean;
     items?: NavMenuItem[];
     items?: NavMenuItem[];
 }
 }