소스 검색

docs: Add docs on dashboard pages & update navigation docs

Michael Bromley 3 달 전
부모
커밋
e73e0afa58
25개의 변경된 파일416개의 추가작업 그리고 378개의 파일을 삭제
  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>
 </Tabs>
 
+![Schema command](./schema-command.webp)
+
 ### Non-Interactive Mode
 
 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 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.
 
 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.
-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:
 
@@ -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),
   [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 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:
 
 ```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.
@@ -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
 
 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
 - **`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
 
 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
 
@@ -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.
 :::
 
+## 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
 
 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
 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.
 :::
 
+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 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
 
-<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>.
 
@@ -19,6 +19,7 @@ Configuration options for the <a href='/reference/core-plugins/dashboard-plugin/
 interface DashboardPluginOptions {
     route: string;
     appDir: string;
+    viteDevServerPort?: number;
 }
 ```
 
@@ -33,8 +34,14 @@ The route to the Dashboard UI.
 
 <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>

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

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## 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
 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
 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*
 
@@ -41,6 +47,8 @@ const config: VendureConfig = {
     DashboardPlugin.init({
       route: '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
 
-<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.
 
@@ -30,7 +30,7 @@ Parameters
 
 ## 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.
 

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

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## 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.
 
@@ -20,7 +20,6 @@ interface DashboardLoginExtensions {
     logo?: LoginLogoExtension;
     beforeForm?: LoginBeforeFormExtension;
     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>`}   />
 
 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>
@@ -120,28 +114,4 @@ interface LoginAfterFormExtension {
 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>

+ 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>
 
 
-## NavMenuBaseItem
+## NavMenuItem
 
 <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"
-interface NavMenuBaseItem {
+interface NavMenuItem {
     id: string;
     title: string;
+    url: string;
     icon?: LucideIcon;
     order?: number;
     placement?: NavMenuSectionPlacement;
@@ -84,22 +85,31 @@ interface NavMenuBaseItem {
 
 <MemberInfo kind="property" type={`string`}   />
 
-
+A unique ID for this nav menu item
 ### title
 
 <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
 
 <MemberInfo kind="property" type={`LucideIcon`}   />
 
-
+An optional icon component to represent the item,
+which should be imported from `lucide-react`.
 ### order
 
 <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
 
 <MemberInfo kind="property" type={`NavMenuSectionPlacement`}   />
@@ -113,31 +123,4 @@ This can be used to restrict the menu item to the given
 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>

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

@@ -21,7 +21,8 @@ interface DashboardPageBlockDefinition {
     id: string;
     title?: React.ReactNode;
     location: PageBlockLocation;
-    component: React.FunctionComponent<{ context: PageContextValue }>;
+    component?: React.FunctionComponent<{ context: PageContextValue }>;
+    shouldRender?: (context: PageContextValue) => boolean;
     requiresPermission?: string | string[];
 }
 ```
@@ -32,27 +33,45 @@ interface DashboardPageBlockDefinition {
 
 <MemberInfo kind="property" type={`string`}   />
 
-
+An ID for the page block. Should be unique at least
+to the page in which it appears.
 ### title
 
 <MemberInfo kind="property" type={`React.ReactNode`}   />
 
-
+An optional title for the page block
 ### location
 
 <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
 
 <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
 
 <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>

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

@@ -23,7 +23,7 @@ function useAuth(): void
 
 ## 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
 status.

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

@@ -29,7 +29,7 @@ function useChannel(): void
 
 ## 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
 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
 
-<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.
 
@@ -48,6 +48,7 @@ const getArticleList = graphql(`
                 body
                 customFields
             }
+            totalItems
         }
     }
 `);
@@ -113,7 +114,7 @@ Parameters
 
 ## 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.
 

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

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## 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.
@@ -31,7 +31,7 @@ Parameters
 
 ## 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.
 
@@ -48,7 +48,7 @@ Parameters
 
 ## 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.
 

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

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## 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.
@@ -39,7 +39,7 @@ Parameters
 
 ## 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.
 
@@ -93,7 +93,7 @@ An optional set of CSS classes to apply to the block.
 
 ## 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**
 
@@ -113,7 +113,7 @@ Parameters
 
 ## 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.

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

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## 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.
 

+ 9 - 3
docs/sidebars.js

@@ -142,13 +142,19 @@ const sidebars = {
             items: [
                 'guides/extending-the-dashboard/getting-started/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/page-blocks/index',
                 'guides/extending-the-dashboard/action-bar-items/index',
                 'guides/extending-the-dashboard/theming/index',
-                'guides/extending-the-dashboard/cms-tutorial/index',
                 {
                     type: 'category',
                     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
- * The base configuration for navigation items and sections of the main app nav bar.
+ * Defines an items in the navigation menu.
  *
  * @docsCategory extensions-api
  * @docsPage Navigation
  * @since 3.4.0
  */
-interface NavMenuBaseItem {
+interface NavMenuItem {
+    /**
+     * @description
+     * A unique ID for this nav menu item
+     */
     id: string;
+    /**
+     * @description
+     * The title that will appear in the nav menu
+     */
     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;
+    /**
+     * @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;
+    /**
+     * Whether this item should appear in the top of bottom section
+     * of the nav menu.
+     */
     placement?: NavMenuSectionPlacement;
     /**
      * @description
@@ -27,23 +56,7 @@ interface NavMenuBaseItem {
     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;
     items?: NavMenuItem[];
 }