瀏覽代碼

fix(dashboard): Fix various build issues

Michael Bromley 9 月之前
父節點
當前提交
134fa381a0
共有 42 個文件被更改,包括 130 次插入70 次删除
  1. 2 0
      packages/dashboard/.gitignore
  2. 2 1
      packages/dashboard/package.json
  3. 16 0
      packages/dashboard/sample-vendure-config.ts
  4. 30 6
      packages/dashboard/src/framework/document-introspection/get-document-structure.ts
  5. 7 2
      packages/dashboard/src/framework/layout-engine/page-layout.tsx
  6. 1 0
      packages/dashboard/src/routes/_authenticated/_administrators/administrators.tsx
  7. 2 2
      packages/dashboard/src/routes/_authenticated/_administrators/administrators_.$id.tsx
  8. 1 1
      packages/dashboard/src/routes/_authenticated/_channels/channels_.$id.tsx
  9. 1 1
      packages/dashboard/src/routes/_authenticated/_collections/collections.tsx
  10. 1 1
      packages/dashboard/src/routes/_authenticated/_collections/collections_.$id.tsx
  11. 2 1
      packages/dashboard/src/routes/_authenticated/_countries/countries_.$id.tsx
  12. 1 1
      packages/dashboard/src/routes/_authenticated/_customer-groups/customer-groups_.$id.tsx
  13. 1 1
      packages/dashboard/src/routes/_authenticated/_customers/customers.tsx
  14. 1 1
      packages/dashboard/src/routes/_authenticated/_customers/customers_.$id.tsx
  15. 2 1
      packages/dashboard/src/routes/_authenticated/_facets/facets.tsx
  16. 1 1
      packages/dashboard/src/routes/_authenticated/_facets/facets_.$id.tsx
  17. 1 1
      packages/dashboard/src/routes/_authenticated/_global-settings/global-settings.tsx
  18. 1 1
      packages/dashboard/src/routes/_authenticated/_orders/orders.tsx
  19. 2 2
      packages/dashboard/src/routes/_authenticated/_orders/orders_.$id.tsx
  20. 1 1
      packages/dashboard/src/routes/_authenticated/_payment-methods/payment-methods_.$id.tsx
  21. 1 1
      packages/dashboard/src/routes/_authenticated/_product-variants/product-variants.tsx
  22. 1 1
      packages/dashboard/src/routes/_authenticated/_product-variants/product-variants_.$id.tsx
  23. 1 1
      packages/dashboard/src/routes/_authenticated/_products/products.tsx
  24. 1 1
      packages/dashboard/src/routes/_authenticated/_products/products_.$id.tsx
  25. 1 1
      packages/dashboard/src/routes/_authenticated/_profile/profile.tsx
  26. 1 1
      packages/dashboard/src/routes/_authenticated/_promotions/promotions_.$id.tsx
  27. 1 1
      packages/dashboard/src/routes/_authenticated/_roles/roles_.$id.tsx
  28. 3 9
      packages/dashboard/src/routes/_authenticated/_sellers/sellers_.$id.tsx
  29. 1 1
      packages/dashboard/src/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx
  30. 1 1
      packages/dashboard/src/routes/_authenticated/_stock-locations/stock-locations_.$id.tsx
  31. 1 1
      packages/dashboard/src/routes/_authenticated/_system/healthchecks.tsx
  32. 1 1
      packages/dashboard/src/routes/_authenticated/_system/job-queue.tsx
  33. 2 1
      packages/dashboard/src/routes/_authenticated/_tax-categories/tax-categories_.$id.tsx
  34. 2 0
      packages/dashboard/src/routes/_authenticated/_tax-rates/tax-rates.tsx
  35. 2 1
      packages/dashboard/src/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx
  36. 1 1
      packages/dashboard/src/routes/_authenticated/_zones/zones_.$id.tsx
  37. 0 14
      packages/dashboard/src/routes/about.tsx
  38. 2 3
      packages/dashboard/src/routes/login.tsx
  39. 1 1
      packages/dashboard/tsconfig.plugin.json
  40. 19 0
      packages/dashboard/vite.config.mts
  41. 5 3
      packages/dashboard/vite/config-loader.ts
  42. 6 1
      packages/dashboard/vite/vite-plugin-vendure-dashboard.ts

+ 2 - 0
packages/dashboard/.gitignore

@@ -9,7 +9,9 @@ lerna-debug.log*
 
 node_modules
 dist
+dist-plugin
 dist-ssr
+.temp
 *.local
 
 # Editor directories and files

+ 2 - 1
packages/dashboard/package.json

@@ -8,6 +8,7 @@
     "build:lib": "tsc --project tsconfig.lib.json",
     "build:plugin": "tsc --project tsconfig.plugin.json",
     "watch:plugin": "tsc --project tsconfig.plugin.json --watch",
+    "test": "vitest run",
     "lint": "eslint .",
     "preview": "vite preview",
     "generate-index": "node ./generate-index.js"
@@ -16,7 +17,7 @@
   "main": "./src/index.ts",
   "exports": {
     ".": "./src/index.ts",
-    "./plugin": "./dist/plugin/index.js"
+    "./plugin": "./dist-plugin/index.js"
   },
   "files": [
     "dist",

+ 16 - 0
packages/dashboard/sample-vendure-config.ts

@@ -0,0 +1,16 @@
+import { VendureConfig } from '@vendure/core';
+
+export const config: VendureConfig = {
+    apiOptions: {
+        port: 3000,
+    },
+    authOptions: {
+        tokenMethod: 'bearer',
+    },
+    dbConnectionOptions: {
+        type: 'postgres',
+    },
+    paymentOptions: {
+        paymentMethodHandlers: [],
+    },
+};

+ 30 - 6
packages/dashboard/src/framework/document-introspection/get-document-structure.ts

@@ -78,7 +78,10 @@ function processPaginatedList(
     if (!isPaginatedList) {
         throw new Error(`Could not determine type of items in ${fieldInfo.name}`);
     }
-    const itemsType = getObjectFieldInfo(fieldInfo.type, 'items').type;
+    const itemsType = getObjectFieldInfo(fieldInfo.type, 'items')?.type;
+    if (!itemsType) {
+        throw new Error(`Could not determine type of items in ${fieldInfo.name}`);
+    }
     for (const item of itemsField.selectionSet?.selections ?? []) {
         if (item.kind === 'Field' || item.kind === 'FragmentSpread') {
             collectFields(itemsType, item, fields, fragments);
@@ -95,6 +98,9 @@ function findNestedPaginatedLists(
     for (const selection of field.selectionSet?.selections ?? []) {
         if (selection.kind === 'Field') {
             const fieldInfo = getObjectFieldInfo(parentType, selection.name.value);
+            if (!fieldInfo) {
+                continue;
+            }
             if (fieldInfo.isPaginatedList) {
                 processPaginatedList(selection, fieldInfo, fields, fragments);
             } else if (selection.selectionSet && !fieldInfo.isScalar) {
@@ -109,6 +115,9 @@ function findNestedPaginatedLists(
                 for (const fragmentSelection of fragment.selectionSet.selections) {
                     if (fragmentSelection.kind === 'Field') {
                         const fieldInfo = getObjectFieldInfo(parentType, fragmentSelection.name.value);
+                        if (!fieldInfo) {
+                            continue;
+                        }
                         if (fieldInfo.isPaginatedList) {
                             processPaginatedList(fragmentSelection, fieldInfo, fields, fragments);
                         } else if (fragmentSelection.selectionSet && !fieldInfo.isScalar) {
@@ -260,6 +269,9 @@ function findPaginatedListPath(
         if (selection.kind === 'Field') {
             const fieldNode = selection;
             const fieldInfo = getObjectFieldInfo(parentType, fieldNode.name.value);
+            if (!fieldInfo) {
+                continue;
+            }
             const newPath = [...currentPath, fieldNode.name.value];
 
             if (fieldInfo.isPaginatedList) {
@@ -329,9 +341,15 @@ export function getOperationTypeInfo(
 }
 
 export function getTypeFieldInfo(typeName: string): FieldInfo[] {
-    return Object.entries(schemaInfo.types[typeName]).map(([fieldName, fieldInfo]) => {
-        return getObjectFieldInfo(typeName, fieldName);
-    });
+    return Object.entries(schemaInfo.types[typeName])
+        .map(([fieldName]) => {
+            const fieldInfo = getObjectFieldInfo(typeName, fieldName);
+            if (!fieldInfo) {
+                return;
+            }
+            return fieldInfo;
+        })
+        .filter(x => !!x);
 }
 
 function getQueryInfo(name: string): FieldInfo {
@@ -371,8 +389,11 @@ export function isEnumType(type: string): boolean {
     return schemaInfo.enums[type] != null;
 }
 
-function getObjectFieldInfo(typeName: string, fieldName: string): FieldInfo {
-    const fieldInfo = schemaInfo.types[typeName][fieldName];
+function getObjectFieldInfo(typeName: string, fieldName: string): FieldInfo | undefined {
+    const fieldInfo = schemaInfo.types[typeName]?.[fieldName];
+    if (!fieldInfo) {
+        return undefined;
+    }
     const type = fieldInfo[0];
     const isScalar = isScalarType(type);
     return {
@@ -393,6 +414,9 @@ function collectFields(
 ) {
     if (fieldNode.kind === 'Field') {
         const fieldInfo = getObjectFieldInfo(typeName, fieldNode.name.value);
+        if (!fieldInfo) {
+            return;
+        }
         fields.push(fieldInfo);
         if (fieldNode.selectionSet) {
             fieldNode.selectionSet.selections.forEach(subSelection => {

+ 7 - 2
packages/dashboard/src/framework/layout-engine/page-layout.tsx

@@ -38,11 +38,16 @@ export type PageLayoutProps = {
 };
 
 function isPageBlock(child: unknown): child is React.ReactElement<PageBlockProps> {
+    if (!child) {
+        return false;
+    }
+    if (!React.isValidElement(child)) {
+        return false;
+    }
     const props = (child as React.ReactElement<PageBlockProps>).props;
-    const isReactElement = React.isValidElement(child);
     const hasColumn = 'column' in props;
     const hasBlockId = 'blockId' in props;
-    return isReactElement && (hasColumn || hasBlockId);
+    return hasColumn || hasBlockId;
 }
 
 export function PageLayout({ children, className }: PageLayoutProps) {

+ 1 - 0
packages/dashboard/src/routes/_authenticated/_administrators/administrators.tsx

@@ -9,6 +9,7 @@ import { Trans } from '@lingui/react/macro';
 import { Link, createFileRoute } from '@tanstack/react-router';
 import { PlusIcon } from 'lucide-react';
 import { administratorListDocument, deleteAdministratorDocument } from './administrators.graphql.js';
+
 export const Route = createFileRoute('/_authenticated/_administrators/administrators')({
     component: AdministratorListPage,
     loader: () => ({ breadcrumb: () => <Trans>Administrators</Trans> }),

+ 2 - 2
packages/dashboard/src/routes/_authenticated/_administrators/administrators_.$id.tsx

@@ -24,7 +24,7 @@ import {
     administratorDetailDocument,
     createAdministratorDocument,
     updateAdministratorDocument,
-} from './administrators.graphql.js';
+} from './administrators.graphql.js'; 
 import { RolePermissionsDisplay } from './components/role-permissions-display.js';
 
 export const Route = createFileRoute('/_authenticated/_administrators/administrators_/$id')({
@@ -42,7 +42,7 @@ export const Route = createFileRoute('/_authenticated/_administrators/administra
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function AdministratorDetailPage() {
+function AdministratorDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_channels/channels_.$id.tsx

@@ -42,7 +42,7 @@ export const Route = createFileRoute('/_authenticated/_channels/channels_/$id')(
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function ChannelDetailPage() {
+function ChannelDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_collections/collections.tsx

@@ -14,7 +14,7 @@ export const Route = createFileRoute('/_authenticated/_collections/collections')
     loader: () => ({ breadcrumb: () => <Trans>Collections</Trans> }),
 });
 
-export function CollectionListPage() {
+function CollectionListPage() {
     return (
         <ListPage
             pageId="collection-list"

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_collections/collections_.$id.tsx

@@ -55,7 +55,7 @@ export const Route = createFileRoute('/_authenticated/_collections/collections_/
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function CollectionDetailPage() {
+function CollectionDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 2 - 1
packages/dashboard/src/routes/_authenticated/_countries/countries_.$id.tsx

@@ -23,6 +23,7 @@ import { Trans, useLingui } from '@lingui/react/macro';
 import { createFileRoute, useNavigate } from '@tanstack/react-router';
 import { toast } from 'sonner';
 import { countryDetailQuery, createCountryDocument, updateCountryDocument } from './countries.graphql.js';
+
 export const Route = createFileRoute('/_authenticated/_countries/countries_/$id')({
     component: CountryDetailPage,
     loader: detailPageRouteLoader({
@@ -35,7 +36,7 @@ export const Route = createFileRoute('/_authenticated/_countries/countries_/$id'
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function CountryDetailPage() {
+function CountryDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_customer-groups/customer-groups_.$id.tsx

@@ -40,7 +40,7 @@ export const Route = createFileRoute('/_authenticated/_customer-groups/customer-
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function CustomerGroupDetailPage() {
+function CustomerGroupDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_customers/customers.tsx

@@ -14,7 +14,7 @@ export const Route = createFileRoute('/_authenticated/_customers/customers')({
     loader: () => ({ breadcrumb: () => <Trans>Customers</Trans> }),
 });
 
-export function CustomerListPage() {
+function CustomerListPage() {
     return (
         <ListPage
             title="Customers"

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_customers/customers_.$id.tsx

@@ -60,7 +60,7 @@ export const Route = createFileRoute('/_authenticated/_customers/customers_/$id'
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function CustomerDetailPage() {
+function CustomerDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 2 - 1
packages/dashboard/src/routes/_authenticated/_facets/facets.tsx

@@ -11,12 +11,13 @@ import { facetListDocument, deleteFacetDocument } from './facets.graphql.js';
 import { DetailPageButton } from '@/components/shared/detail-page-button.js';
 import { ResultOf } from 'gql.tada';
 import { FacetValuesSheet } from './components/facet-values-sheet.js';
+
 export const Route = createFileRoute('/_authenticated/_facets/facets')({
     component: FacetListPage,
     loader: () => ({ breadcrumb: () => <Trans>Facets</Trans> }),
 });
 
-export function FacetListPage() {
+function FacetListPage() {
     return (
         <ListPage
             pageId="facet-list"

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_facets/facets_.$id.tsx

@@ -38,7 +38,7 @@ export const Route = createFileRoute('/_authenticated/_facets/facets_/$id')({
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function FacetDetailPage() {
+function FacetDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_global-settings/global-settings.tsx

@@ -38,7 +38,7 @@ export const Route = createFileRoute('/_authenticated/_global-settings/global-se
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function GlobalSettingsPage() {
+function GlobalSettingsPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_orders/orders.tsx

@@ -12,7 +12,7 @@ export const Route = createFileRoute('/_authenticated/_orders/orders')({
     loader: () => ({ breadcrumb: () => <Trans>Orders</Trans> }),
 });
 
-export function OrderListPage() {
+function OrderListPage() {
     return (
         <ListPage
             pageId="order-list"

+ 2 - 2
packages/dashboard/src/routes/_authenticated/_orders/orders_.$id.tsx

@@ -26,7 +26,7 @@ import { PaymentDetails } from './components/payment-details.js';
 import { orderDetailDocument } from './orders.graphql.js';
 
 export const Route = createFileRoute('/_authenticated/_orders/orders_/$id')({
-    component: FacetDetailPage,
+    component: OrderDetailPage,
     loader: detailPageRouteLoader({
         queryDocument: orderDetailDocument,
         breadcrumb(_isNew, entity) {
@@ -36,7 +36,7 @@ export const Route = createFileRoute('/_authenticated/_orders/orders_/$id')({
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function FacetDetailPage() {
+function OrderDetailPage() {
     const params = Route.useParams();
     const { i18n } = useLingui();
 

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_payment-methods/payment-methods_.$id.tsx

@@ -42,7 +42,7 @@ export const Route = createFileRoute('/_authenticated/_payment-methods/payment-m
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function PaymentMethodDetailPage() {
+function PaymentMethodDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_product-variants/product-variants.tsx

@@ -12,7 +12,7 @@ export const Route = createFileRoute('/_authenticated/_product-variants/product-
     loader: () => ({ breadcrumb: () => <Trans>Product Variants</Trans> }),
 });
 
-export function ProductListPage() {
+function ProductListPage() {
     const { formatCurrencyName } = useLocalFormat();
     return (
         <ListPage

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_product-variants/product-variants_.$id.tsx

@@ -57,7 +57,7 @@ export const Route = createFileRoute('/_authenticated/_product-variants/product-
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function ProductVariantDetailPage() {
+function ProductVariantDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_products/products.tsx

@@ -13,7 +13,7 @@ export const Route = createFileRoute('/_authenticated/_products/products')({
     loader: () => ({ breadcrumb: () => <Trans>Products</Trans> }),
 });
 
-export function ProductListPage() {
+function ProductListPage() {
     return (
         <ListPage
             pageId="product-list"

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_products/products_.$id.tsx

@@ -45,7 +45,7 @@ export const Route = createFileRoute('/_authenticated/_products/products_/$id')(
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function ProductDetailPage() {
+function ProductDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_profile/profile.tsx

@@ -34,7 +34,7 @@ export const Route = createFileRoute('/_authenticated/_profile/profile')({
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function ProfilePage() {
+function ProfilePage() {
     const { i18n } = useLingui();
 
     const { form, submitHandler, isPending } = useDetailPage({

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_promotions/promotions_.$id.tsx

@@ -46,7 +46,7 @@ export const Route = createFileRoute('/_authenticated/_promotions/promotions_/$i
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function PromotionDetailPage() {
+function PromotionDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_roles/roles_.$id.tsx

@@ -37,7 +37,7 @@ export const Route = createFileRoute('/_authenticated/_roles/roles_/$id')({
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function RoleDetailPage() {
+function RoleDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 3 - 9
packages/dashboard/src/routes/_authenticated/_sellers/sellers_.$id.tsx

@@ -4,7 +4,6 @@ import { Button } from '@/components/ui/button.js';
 import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form.js';
 import { Input } from '@/components/ui/input.js';
 import { NEW_ENTITY_PATH } from '@/constants.js';
-import { addCustomFields } from '@/framework/document-introspection/add-custom-fields.js';
 import {
     CustomFieldsPageBlock,
     Page,
@@ -13,21 +12,16 @@ import {
     PageLayout,
     PageTitle,
 } from '@/framework/layout-engine/page-layout.js';
-import { getDetailQueryOptions, useDetailPage } from '@/framework/page/use-detail-page.js';
+import { detailPageRouteLoader } from '@/framework/page/detail-page-route-loader.js';
+import { useDetailPage } from '@/framework/page/use-detail-page.js';
 import { Trans, useLingui } from '@lingui/react/macro';
 import { createFileRoute, useNavigate } from '@tanstack/react-router';
 import { toast } from 'sonner';
-import { CustomerGroupMembersTable } from './components/customer-group-members-table.js';
 import {
     createSellerDocument,
     sellerDetailDocument,
     updateSellerDocument,
 } from './sellers.graphql.js';
-import { CustomerSelector } from '@/components/shared/customer-selector.js';
-import { api } from '@/graphql/api.js';
-import { addCustomerToGroupDocument } from '../_customers/customers.graphql.js';
-import { useMutation } from '@tanstack/react-query';
-import { detailPageRouteLoader } from '@/framework/page/detail-page-route-loader.js';
 
 export const Route = createFileRoute('/_authenticated/_sellers/sellers_/$id')({
     component: SellerDetailPage,
@@ -41,7 +35,7 @@ export const Route = createFileRoute('/_authenticated/_sellers/sellers_/$id')({
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function SellerDetailPage() {
+function SellerDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx

@@ -47,7 +47,7 @@ export const Route = createFileRoute('/_authenticated/_shipping-methods/shipping
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function ShippingMethodDetailPage() {
+function ShippingMethodDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_stock-locations/stock-locations_.$id.tsx

@@ -41,7 +41,7 @@ export const Route = createFileRoute('/_authenticated/_stock-locations/stock-loc
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function StockLocationDetailPage() {
+function StockLocationDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_system/healthchecks.tsx

@@ -23,7 +23,7 @@ interface HealthcheckResponse {
     details: Record<string, HealthcheckItem>;
 }
 
-export function HealthchecksPage() {
+function HealthchecksPage() {
     const { data, refetch, dataUpdatedAt } = useQuery({
         queryKey: ['healthchecks'],
         queryFn: async () => {

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_system/job-queue.tsx

@@ -48,7 +48,7 @@ const STATES = [
     },
 ];
 
-export function JobQueuePage() {
+function JobQueuePage() {
     return (
         <ListPage
             pageId="job-queue-list"

+ 2 - 1
packages/dashboard/src/routes/_authenticated/_tax-categories/tax-categories_.$id.tsx

@@ -40,7 +40,8 @@ export const Route = createFileRoute('/_authenticated/_tax-categories/tax-catego
     }),
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
-export function TaxCategoryDetailPage() {
+
+function TaxCategoryDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 2 - 0
packages/dashboard/src/routes/_authenticated/_tax-rates/tax-rates.tsx

@@ -9,6 +9,8 @@ import { Trans } from '@lingui/react/macro';
 import { Link, createFileRoute } from '@tanstack/react-router';
 import { PlusIcon } from 'lucide-react';
 import { deleteTaxRateDocument, taxRateListQuery } from './tax-rates.graphql.js';
+import { zoneListQuery } from '../_zones/zones.graphql.js';
+import { taxCategoryListQuery } from '../_tax-categories/tax-categories.graphql.js';
 
 export const Route = createFileRoute('/_authenticated/_tax-rates/tax-rates')({
     component: TaxRateListPage,

+ 2 - 1
packages/dashboard/src/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx

@@ -41,7 +41,8 @@ export const Route = createFileRoute('/_authenticated/_tax-rates/tax-rates_/$id'
     }),
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
-export function TaxRateDetailPage() {
+
+function TaxRateDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 1 - 1
packages/dashboard/src/routes/_authenticated/_zones/zones_.$id.tsx

@@ -37,7 +37,7 @@ export const Route = createFileRoute('/_authenticated/_zones/zones_/$id')({
     errorComponent: ({ error }) => <ErrorPage message={error.message} />,
 });
 
-export function ZoneDetailPage() {
+function ZoneDetailPage() {
     const params = Route.useParams();
     const navigate = useNavigate();
     const creatingNewEntity = params.id === NEW_ENTITY_PATH;

+ 0 - 14
packages/dashboard/src/routes/about.tsx

@@ -1,14 +0,0 @@
-import * as React from 'react';
-import { createFileRoute } from '@tanstack/react-router';
-
-export const Route = createFileRoute('/about')({
-    component: AboutComponent,
-});
-
-function AboutComponent() {
-    return (
-        <div className="p-2">
-            <h3>About</h3>
-        </div>
-    );
-}

+ 2 - 3
packages/dashboard/src/routes/login.tsx

@@ -1,7 +1,6 @@
-import { useAuth } from '@/hooks/use-auth.js';
 import { LoginForm } from '@/components/login/login-form.js';
+import { useAuth } from '@/hooks/use-auth.js';
 import { createFileRoute, Navigate, redirect, useRouterState } from '@tanstack/react-router';
-import * as React from 'react';
 import { z } from 'zod';
 
 const fallback = '/dashboard' as const;
@@ -18,7 +17,7 @@ export const Route = createFileRoute('/login')({
     component: LoginPage,
 });
 
-export default function LoginPage() {
+function LoginPage() {
     const auth = useAuth();
     const isLoading = useRouterState({ select: s => s.isLoading });
     const navigate = Route.useNavigate();

+ 1 - 1
packages/dashboard/tsconfig.plugin.json

@@ -7,7 +7,7 @@
     "skipLibCheck": true,
     "plugins": [],
     "declaration": true,
-    "outDir": "./dist/plugin"
+    "outDir": "./dist-plugin"
   },
   "include": ["vite/**/*"],
 }

+ 19 - 0
packages/dashboard/vite.config.mts

@@ -0,0 +1,19 @@
+import { vendureDashboardPlugin } from './vite/vite-plugin-vendure-dashboard.js';
+import path from 'path';
+import { pathToFileURL } from 'url';
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+    test: {
+        globals: true,
+        environment: 'jsdom',
+    },
+    plugins: [
+        vendureDashboardPlugin({
+            vendureConfigPath: pathToFileURL('./sample-vendure-config.ts'),
+            adminUiConfig: { apiHost: 'http://localhost', apiPort: 3000 },
+            // gqlTadaOutputPath: path.resolve(__dirname, './graphql/'),
+            tempCompilationDir: path.resolve(__dirname, './.temp'),
+        }) as any,
+    ],
+});

+ 5 - 3
packages/dashboard/vite/config-loader.ts

@@ -33,8 +33,9 @@ export async function loadVendureConfig(
     const { vendureConfigPath, vendureConfigExport, tempDir } = options;
     const outputPath = tempDir;
     const configFileName = path.basename(vendureConfigPath);
+    const inputRootDir = path.dirname(vendureConfigPath);
     await fs.remove(outputPath);
-    await compileFile(vendureConfigPath, outputPath);
+    await compileFile(inputRootDir, vendureConfigPath, outputPath);
     const compiledConfigFilePath = pathToFileURL(path.join(outputPath, configFileName)).href.replace(
         /.ts$/,
         '.js',
@@ -95,6 +96,7 @@ function isBindingIdentifier(id: Pattern): id is BindingIdentifier {
 }
 
 export async function compileFile(
+    inputRootDir: string,
     inputPath: string,
     outputDir: string,
     compiledFiles = new Set<string>(),
@@ -140,7 +142,7 @@ export async function compileFile(
     const result = await transform(source, config);
 
     // Generate output file path
-    const relativePath = path.relative(process.cwd(), inputPath);
+    const relativePath = path.relative(inputRootDir, inputPath);
     const outputPath = path.join(outputDir, relativePath).replace(/\.ts$/, '.js');
 
     // Ensure the subdirectory for the output file exists
@@ -174,6 +176,6 @@ export async function compileFile(
 
     // Recursively compile all relative imports
     for (const importPath of importPaths) {
-        await compileFile(importPath, outputDir, compiledFiles);
+        await compileFile(inputRootDir, importPath, outputDir, compiledFiles);
     }
 }

+ 6 - 1
packages/dashboard/vite/vite-plugin-vendure-dashboard.ts

@@ -29,7 +29,12 @@ export type VitePluginVendureDashboardOptions = {
      * This is only required if the plugin is unable to auto-detect the name of the exported variable.
      */
     vendureConfigExport?: string;
+    /**
+     * @description
+     * The path to the directory where the generated GraphQL Tada files will be output.
+     */
     gqlTadaOutputPath?: string;
+    tempCompilationDir?: string;
 } & UiConfigPluginOptions;
 
 /**
@@ -37,7 +42,7 @@ export type VitePluginVendureDashboardOptions = {
  * This is a Vite plugin which configures a set of plugins required to build the Vendure Dashboard.
  */
 export function vendureDashboardPlugin(options: VitePluginVendureDashboardOptions): PluginOption[] {
-    const tempDir = path.join(import.meta.dirname, './.vendure-dashboard-temp');
+    const tempDir = options.tempCompilationDir ?? path.join(import.meta.dirname, './.vendure-dashboard-temp');
     const normalizedVendureConfigPath = getNormalizedVendureConfigPath(options.vendureConfigPath);
     const packageRoot = getDashboardPackageRoot();
     const linguiConfigPath = path.join(packageRoot, 'lingui.config.js');