Просмотр исходного кода

feat(dashboard): Add handleNestedFormSubmit utility for nested forms (#3835)

David Höck 3 месяцев назад
Родитель
Сommit
ced9371b4b

+ 88 - 0
docs/docs/guides/extending-the-dashboard/custom-form-components/index.md

@@ -324,6 +324,94 @@ Always import UI components from the `@vendure/dashboard` package rather than cr
 
 The unified custom form elements system gives you complete flexibility in how data is presented and edited in the dashboard, while maintaining seamless integration with React Hook Form and the dashboard's design system.
 
+## Nested Forms and Event Handling
+
+When creating custom form components that contain their own forms (e.g., dialogs with forms inside detail pages), you need to prevent form submission events from bubbling up to parent forms. The dashboard provides the `handleNestedFormSubmit` utility for this purpose.
+
+### Why Use handleNestedFormSubmit?
+
+Detail pages in the dashboard are themselves forms. If you add a custom component with its own form (like a dialog with create/edit functionality), submitting the inner form will also trigger the outer detail page form submission. This can cause:
+
+- Unintended save operations on the detail page
+- Validation errors on unrelated fields
+- Loss of unsaved changes in the dialog
+
+### Using handleNestedFormSubmit
+
+The `handleNestedFormSubmit` utility prevents event propagation and properly handles form submission:
+
+```tsx title="src/plugins/my-plugin/dashboard/components/nested-form-dialog.tsx"
+import {
+    Button,
+    Dialog,
+    DialogContent,
+    DialogTrigger,
+    Form,
+    DashboardFormComponent,
+    handleNestedFormSubmit
+} from '@vendure/dashboard';
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { z } from 'zod';
+
+const formSchema = z.object({
+    title: z.string().min(1, 'Title is required'),
+    description: z.string().min(1, 'Description is required'),
+});
+
+type FormData = z.infer<typeof formSchema>;
+
+export const NestedFormDialogComponent: DashboardFormComponent = (props) => {
+    const form = useForm<FormData>({
+        resolver: zodResolver(formSchema),
+        defaultValues: {
+            title: '',
+            description: '',
+        },
+    });
+
+    const onSubmit = (data: FormData) => {
+        // Handle your form submission logic
+        console.log('Form submitted:', data);
+        // You might update the parent form value here
+        props.onChange(data);
+        form.reset();
+    };
+
+    return (
+        <Dialog>
+            <DialogTrigger asChild>
+                <Button variant="outline">Open Form</Button>
+            </DialogTrigger>
+            <DialogContent>
+                <Form {...form}>
+                    {/* Use handleNestedFormSubmit to prevent event bubbling */}
+                    <form onSubmit={handleNestedFormSubmit(form, onSubmit)}>
+                        {/* Your form fields here */}
+                        <Button type="submit">Save</Button>
+                    </form>
+                </Form>
+            </DialogContent>
+        </Dialog>
+    );
+};
+```
+
+### What handleNestedFormSubmit Does
+
+The utility function:
+1. Prevents the submit event from propagating to parent forms (`e.stopPropagation()`)
+2. Prevents the browser's default form submission behavior (`e.preventDefault()`)
+3. Properly triggers react-hook-form's handleSubmit with your custom handler
+4. Maintains type safety with TypeScript generics
+
+### When to Use It
+
+Use `handleNestedFormSubmit` whenever you have:
+- A dialog with a form inside a detail page
+- A custom component with its own form that's nested within another form
+- Any scenario where form submission events should not bubble up to parent forms
+
 ## Relation Selectors
 
 The dashboard includes powerful relation selector components for selecting related entities with built-in search and pagination:

+ 653 - 84
package-lock.json

@@ -5248,7 +5248,6 @@
     },
     "node_modules/@clack/prompts/node_modules/is-unicode-supported": {
       "version": "1.3.0",
-      "extraneous": true,
       "inBundle": true,
       "license": "MIT",
       "engines": {
@@ -15515,6 +15514,50 @@
         "node": ">=14"
       }
     },
+    "node_modules/@prisma/instrumentation": {
+      "version": "6.15.0",
+      "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-6.15.0.tgz",
+      "integrity": "sha512-6TXaH6OmDkMOQvOxwLZ8XS51hU2v4A3vmE2pSijCIiGRJYyNeMcL6nMHQMyYdZRD8wl7LF3Wzc+AMPMV/9Oo7A==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0 || ^0.57.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.8"
+      }
+    },
+    "node_modules/@prisma/instrumentation/node_modules/@opentelemetry/api-logs": {
+      "version": "0.57.2",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.2.tgz",
+      "integrity": "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@prisma/instrumentation/node_modules/@opentelemetry/instrumentation": {
+      "version": "0.57.2",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz",
+      "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/api-logs": "0.57.2",
+        "@types/shimmer": "^1.2.0",
+        "import-in-the-middle": "^1.8.1",
+        "require-in-the-middle": "^7.1.1",
+        "semver": "^7.5.2",
+        "shimmer": "^1.2.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
     "node_modules/@protobufjs/aspromise": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
@@ -17818,89 +17861,644 @@
       "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==",
       "license": "MIT"
     },
-    "node_modules/@sentry-internal/tracing": {
-      "version": "7.120.4",
-      "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.120.4.tgz",
-      "integrity": "sha512-Fz5+4XCg3akeoFK+K7g+d7HqGMjmnLoY2eJlpONJmaeT9pXY7yfUyXKZMmMajdE2LxxKJgQ2YKvSCaGVamTjHw==",
-      "dev": true,
+    "node_modules/@sentry-internal/node-cpu-profiler": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@sentry-internal/node-cpu-profiler/-/node-cpu-profiler-2.2.0.tgz",
+      "integrity": "sha512-oLHVYurqZfADPh5hvmQYS5qx8t0UZzT2u6+/68VXsFruQEOnYJTODKgU3BVLmemRs3WE6kCJjPeFdHVYOQGSzQ==",
+      "hasInstallScript": true,
       "license": "MIT",
       "dependencies": {
-        "@sentry/core": "7.120.4",
-        "@sentry/types": "7.120.4",
-        "@sentry/utils": "7.120.4"
+        "detect-libc": "^2.0.3",
+        "node-abi": "^3.73.0"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=18"
       }
     },
     "node_modules/@sentry/core": {
-      "version": "7.120.4",
-      "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.120.4.tgz",
-      "integrity": "sha512-TXu3Q5kKiq8db9OXGkWyXUbIxMMuttB5vJ031yolOl5T/B69JRyAoKuojLBjRv1XX583gS1rSSoX8YXX7ATFGA==",
-      "dev": true,
+      "version": "10.15.0",
+      "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.15.0.tgz",
+      "integrity": "sha512-J7WsQvb9G6nsVgWkTHwyX7wR2djtEACYCx19hAnRbSGIg+ysVG+7Ti3RL4bz9/VXfcxsz346cleKc7ljhynYlQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@sentry/nestjs": {
+      "version": "10.15.0",
+      "resolved": "https://registry.npmjs.org/@sentry/nestjs/-/nestjs-10.15.0.tgz",
+      "integrity": "sha512-33um+ToGkbLEcnsxFOjt4X0hrcn0rNJsOK18pspKo5C1o9FW5q6FaNSwXxte7Tx0QD4rF+KxSSOJcPTUh5mdxg==",
       "license": "MIT",
       "dependencies": {
-        "@sentry/types": "7.120.4",
-        "@sentry/utils": "7.120.4"
+        "@opentelemetry/api": "^1.9.0",
+        "@opentelemetry/core": "^2.1.0",
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/instrumentation-nestjs-core": "0.50.0",
+        "@opentelemetry/semantic-conventions": "^1.37.0",
+        "@sentry/core": "10.15.0",
+        "@sentry/node": "10.15.0"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0",
+        "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0"
       }
     },
-    "node_modules/@sentry/integrations": {
-      "version": "7.120.4",
-      "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-7.120.4.tgz",
-      "integrity": "sha512-kkBTLk053XlhDCg7OkBQTIMF4puqFibeRO3E3YiVc4PGLnocXMaVpOSCkMqAc1k1kZ09UgGi8DxfQhnFEjUkpA==",
-      "dev": true,
-      "license": "MIT",
+    "node_modules/@sentry/nestjs/node_modules/@opentelemetry/api-logs": {
+      "version": "0.204.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.204.0.tgz",
+      "integrity": "sha512-DqxY8yoAaiBPivoJD4UtgrMS8gEmzZ5lnaxzPojzLVHBGqPxgWm4zcuvcUHZiqQ6kRX2Klel2r9y8cA2HAtqpw==",
+      "license": "Apache-2.0",
       "dependencies": {
-        "@sentry/core": "7.120.4",
-        "@sentry/types": "7.120.4",
-        "@sentry/utils": "7.120.4",
-        "localforage": "^1.8.1"
+        "@opentelemetry/api": "^1.3.0"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/@sentry/nestjs/node_modules/@opentelemetry/instrumentation": {
+      "version": "0.204.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.204.0.tgz",
+      "integrity": "sha512-vV5+WSxktzoMP8JoYWKeopChy6G3HKk4UQ2hESCRDUUTZqQ3+nM3u8noVG0LmNfRWwcFBnbZ71GKC7vaYYdJ1g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/api-logs": "0.204.0",
+        "import-in-the-middle": "^1.8.1",
+        "require-in-the-middle": "^7.1.1"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/nestjs/node_modules/@opentelemetry/instrumentation-nestjs-core": {
+      "version": "0.50.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.50.0.tgz",
+      "integrity": "sha512-10u2Gjw260W8vdUem6pM7ENrb8i+UAyrgouhjN7HRdQYh9rcit51tRhgrI52fxTsRjrrBNIItHkX0YM8WnEU2w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/semantic-conventions": "^1.30.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
       }
     },
     "node_modules/@sentry/node": {
-      "version": "7.120.4",
-      "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.120.4.tgz",
-      "integrity": "sha512-qq3wZAXXj2SRWhqErnGCSJKUhPSlZ+RGnCZjhfjHpP49KNpcd9YdPTIUsFMgeyjdh6Ew6aVCv23g1hTP0CHpYw==",
-      "dev": true,
+      "version": "10.15.0",
+      "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.15.0.tgz",
+      "integrity": "sha512-5V9BX55DEIscU/S5+AEIQuIMKKbSd+MVo1/x5UkOceBxfiA0KUmgQ0POIpUEZqGCS9rpQ5fEajByRXAQ7bjaWA==",
       "license": "MIT",
       "dependencies": {
-        "@sentry-internal/tracing": "7.120.4",
-        "@sentry/core": "7.120.4",
-        "@sentry/integrations": "7.120.4",
-        "@sentry/types": "7.120.4",
-        "@sentry/utils": "7.120.4"
+        "@opentelemetry/api": "^1.9.0",
+        "@opentelemetry/context-async-hooks": "^2.1.0",
+        "@opentelemetry/core": "^2.1.0",
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/instrumentation-amqplib": "0.51.0",
+        "@opentelemetry/instrumentation-connect": "0.48.0",
+        "@opentelemetry/instrumentation-dataloader": "0.22.0",
+        "@opentelemetry/instrumentation-express": "0.53.0",
+        "@opentelemetry/instrumentation-fs": "0.24.0",
+        "@opentelemetry/instrumentation-generic-pool": "0.48.0",
+        "@opentelemetry/instrumentation-graphql": "0.52.0",
+        "@opentelemetry/instrumentation-hapi": "0.51.0",
+        "@opentelemetry/instrumentation-http": "0.204.0",
+        "@opentelemetry/instrumentation-ioredis": "0.52.0",
+        "@opentelemetry/instrumentation-kafkajs": "0.14.0",
+        "@opentelemetry/instrumentation-knex": "0.49.0",
+        "@opentelemetry/instrumentation-koa": "0.52.0",
+        "@opentelemetry/instrumentation-lru-memoizer": "0.49.0",
+        "@opentelemetry/instrumentation-mongodb": "0.57.0",
+        "@opentelemetry/instrumentation-mongoose": "0.51.0",
+        "@opentelemetry/instrumentation-mysql": "0.50.0",
+        "@opentelemetry/instrumentation-mysql2": "0.51.0",
+        "@opentelemetry/instrumentation-pg": "0.57.0",
+        "@opentelemetry/instrumentation-redis": "0.53.0",
+        "@opentelemetry/instrumentation-tedious": "0.23.0",
+        "@opentelemetry/instrumentation-undici": "0.15.0",
+        "@opentelemetry/resources": "^2.1.0",
+        "@opentelemetry/sdk-trace-base": "^2.1.0",
+        "@opentelemetry/semantic-conventions": "^1.37.0",
+        "@prisma/instrumentation": "6.15.0",
+        "@sentry/core": "10.15.0",
+        "@sentry/node-core": "10.15.0",
+        "@sentry/opentelemetry": "10.15.0",
+        "import-in-the-middle": "^1.14.2",
+        "minimatch": "^9.0.0"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=18"
       }
     },
-    "node_modules/@sentry/types": {
-      "version": "7.120.4",
-      "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.120.4.tgz",
-      "integrity": "sha512-cUq2hSSe6/qrU6oZsEP4InMI5VVdD86aypE+ENrQ6eZEVLTCYm1w6XhW1NvIu3UuWh7gZec4a9J7AFpYxki88Q==",
-      "dev": true,
+    "node_modules/@sentry/node/node_modules/@opentelemetry/api-logs": {
+      "version": "0.204.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.204.0.tgz",
+      "integrity": "sha512-DqxY8yoAaiBPivoJD4UtgrMS8gEmzZ5lnaxzPojzLVHBGqPxgWm4zcuvcUHZiqQ6kRX2Klel2r9y8cA2HAtqpw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation": {
+      "version": "0.204.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.204.0.tgz",
+      "integrity": "sha512-vV5+WSxktzoMP8JoYWKeopChy6G3HKk4UQ2hESCRDUUTZqQ3+nM3u8noVG0LmNfRWwcFBnbZ71GKC7vaYYdJ1g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/api-logs": "0.204.0",
+        "import-in-the-middle": "^1.8.1",
+        "require-in-the-middle": "^7.1.1"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-amqplib": {
+      "version": "0.51.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.51.0.tgz",
+      "integrity": "sha512-XGmjYwjVRktD4agFnWBWQXo9SiYHKBxR6Ag3MLXwtLE4R99N3a08kGKM5SC1qOFKIELcQDGFEFT9ydXMH00Luw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^2.0.0",
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-connect": {
+      "version": "0.48.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.48.0.tgz",
+      "integrity": "sha512-OMjc3SFL4pC16PeK+tDhwP7MRvDPalYCGSvGqUhX5rASkI2H0RuxZHOWElYeXkV0WP+70Gw6JHWac/2Zqwmhdw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^2.0.0",
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/semantic-conventions": "^1.27.0",
+        "@types/connect": "3.4.38"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-dataloader": {
+      "version": "0.22.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.22.0.tgz",
+      "integrity": "sha512-bXnTcwtngQsI1CvodFkTemrrRSQjAjZxqHVc+CJZTDnidT0T6wt3jkKhnsjU/Kkkc0lacr6VdRpCu2CUWa0OKw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.204.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-express": {
+      "version": "0.53.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.53.0.tgz",
+      "integrity": "sha512-r/PBafQmFYRjuxLYEHJ3ze1iBnP2GDA1nXOSS6E02KnYNZAVjj6WcDA1MSthtdAUUK0XnotHvvWM8/qz7DMO5A==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^2.0.0",
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-fs": {
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.24.0.tgz",
+      "integrity": "sha512-HjIxJ6CBRD770KNVaTdMXIv29Sjz4C1kPCCK5x1Ujpc6SNnLGPqUVyJYZ3LUhhnHAqdbrl83ogVWjCgeT4Q0yw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^2.0.0",
+        "@opentelemetry/instrumentation": "^0.204.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-generic-pool": {
+      "version": "0.48.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.48.0.tgz",
+      "integrity": "sha512-TLv/On8pufynNR+pUbpkyvuESVASZZKMlqCm4bBImTpXKTpqXaJJ3o/MUDeMlM91rpen+PEv2SeyOKcHCSlgag==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.204.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-graphql": {
+      "version": "0.52.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.52.0.tgz",
+      "integrity": "sha512-3fEJ8jOOMwopvldY16KuzHbRhPk8wSsOTSF0v2psmOCGewh6ad+ZbkTx/xyUK9rUdUMWAxRVU0tFpj4Wx1vkPA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.204.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-hapi": {
+      "version": "0.51.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.51.0.tgz",
+      "integrity": "sha512-qyf27DaFNL1Qhbo/da+04MSCw982B02FhuOS5/UF+PMhM61CcOiu7fPuXj8TvbqyReQuJFljXE6UirlvoT/62g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^2.0.0",
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-http": {
+      "version": "0.204.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.204.0.tgz",
+      "integrity": "sha512-1afJYyGRA4OmHTv0FfNTrTAzoEjPQUYgd+8ih/lX0LlZBnGio/O80vxA0lN3knsJPS7FiDrsDrWq25K7oAzbkw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "2.1.0",
+        "@opentelemetry/instrumentation": "0.204.0",
+        "@opentelemetry/semantic-conventions": "^1.29.0",
+        "forwarded-parse": "2.1.2"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-ioredis": {
+      "version": "0.52.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.52.0.tgz",
+      "integrity": "sha512-rUvlyZwI90HRQPYicxpDGhT8setMrlHKokCtBtZgYxQWRF5RBbG4q0pGtbZvd7kyseuHbFpA3I/5z7M8b/5ywg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/redis-common": "^0.38.0",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-kafkajs": {
+      "version": "0.14.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.14.0.tgz",
+      "integrity": "sha512-kbB5yXS47dTIdO/lfbbXlzhvHFturbux4EpP0+6H78Lk0Bn4QXiZQW7rmZY1xBCY16mNcCb8Yt0mhz85hTnSVA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/semantic-conventions": "^1.30.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-knex": {
+      "version": "0.49.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.49.0.tgz",
+      "integrity": "sha512-NKsRRT27fbIYL4Ix+BjjP8h4YveyKc+2gD6DMZbr5R5rUeDqfC8+DTfIt3c3ex3BIc5Vvek4rqHnN7q34ZetLQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/semantic-conventions": "^1.33.1"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-koa": {
+      "version": "0.52.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.52.0.tgz",
+      "integrity": "sha512-JJSBYLDx/mNSy8Ibi/uQixu2rH0bZODJa8/cz04hEhRaiZQoeJ5UrOhO/mS87IdgVsHrnBOsZ6vDu09znupyuA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^2.0.0",
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-lru-memoizer": {
+      "version": "0.49.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.49.0.tgz",
+      "integrity": "sha512-ctXu+O/1HSadAxtjoEg2w307Z5iPyLOMM8IRNwjaKrIpNAthYGSOanChbk1kqY6zU5CrpkPHGdAT6jk8dXiMqw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.204.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-mongodb": {
+      "version": "0.57.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.57.0.tgz",
+      "integrity": "sha512-KD6Rg0KSHWDkik+qjIOWoksi1xqSpix8TSPfquIK1DTmd9OTFb5PHmMkzJe16TAPVEuElUW8gvgP59cacFcrMQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-mongoose": {
+      "version": "0.51.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.51.0.tgz",
+      "integrity": "sha512-gwWaAlhhV2By7XcbyU3DOLMvzsgeaymwP/jktDC+/uPkCmgB61zurwqOQdeiRq9KAf22Y2dtE5ZLXxytJRbEVA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^2.0.0",
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-mysql": {
+      "version": "0.50.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.50.0.tgz",
+      "integrity": "sha512-duKAvMRI3vq6u9JwzIipY9zHfikN20bX05sL7GjDeLKr2qV0LQ4ADtKST7KStdGcQ+MTN5wghWbbVdLgNcB3rA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/semantic-conventions": "^1.27.0",
+        "@types/mysql": "2.15.27"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-mysql2": {
+      "version": "0.51.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.51.0.tgz",
+      "integrity": "sha512-zT2Wg22Xn43RyfU3NOUmnFtb5zlDI0fKcijCj9AcK9zuLZ4ModgtLXOyBJSSfO+hsOCZSC1v/Fxwj+nZJFdzLQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/semantic-conventions": "^1.27.0",
+        "@opentelemetry/sql-common": "^0.41.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-pg": {
+      "version": "0.57.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.57.0.tgz",
+      "integrity": "sha512-dWLGE+r5lBgm2A8SaaSYDE3OKJ/kwwy5WLyGyzor8PLhUL9VnJRiY6qhp4njwhnljiLtzeffRtG2Mf/YyWLeTw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^2.0.0",
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/semantic-conventions": "^1.34.0",
+        "@opentelemetry/sql-common": "^0.41.0",
+        "@types/pg": "8.15.5",
+        "@types/pg-pool": "2.0.6"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-redis": {
+      "version": "0.53.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.53.0.tgz",
+      "integrity": "sha512-WUHV8fr+8yo5RmzyU7D5BIE1zwiaNQcTyZPwtxlfr7px6NYYx7IIpSihJK7WA60npWynfxxK1T67RAVF0Gdfjg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/redis-common": "^0.38.0",
+        "@opentelemetry/semantic-conventions": "^1.27.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-tedious": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.23.0.tgz",
+      "integrity": "sha512-3TMTk/9VtlRonVTaU4tCzbg4YqW+Iq/l5VnN2e5whP6JgEg/PKfrGbqQ+CxQWNLfLaQYIUgEZqAn5gk/inh1uQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/instrumentation": "^0.204.0",
+        "@opentelemetry/semantic-conventions": "^1.27.0",
+        "@types/tedious": "^4.0.14"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-undici": {
+      "version": "0.15.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.15.0.tgz",
+      "integrity": "sha512-sNFGA/iCDlVkNjzTzPRcudmI11vT/WAfAguRdZY9IspCw02N4WSC72zTuQhSMheh2a1gdeM9my1imnKRvEEvEg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "^2.0.0",
+        "@opentelemetry/instrumentation": "^0.204.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.7.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/redis-common": {
+      "version": "0.38.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.38.0.tgz",
+      "integrity": "sha512-4Wc0AWURII2cfXVVoZ6vDqK+s5n4K5IssdrlVrvGsx6OEOKdghKtJZqXAHWFiZv4nTDLH2/2fldjIHY8clMOjQ==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@opentelemetry/sdk-trace-base": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.1.0.tgz",
+      "integrity": "sha512-uTX9FBlVQm4S2gVQO1sb5qyBLq/FPjbp+tmGoxu4tIgtYGmBYB44+KX/725RFDe30yBSaA9Ml9fqphe1hbUyLQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "2.1.0",
+        "@opentelemetry/resources": "2.1.0",
+        "@opentelemetry/semantic-conventions": "^1.29.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.3.0 <1.10.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/@sentry/node-core": {
+      "version": "10.15.0",
+      "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.15.0.tgz",
+      "integrity": "sha512-X6QAHulgfkpONYrXNK2QXfW02ja5FS31sn5DWfCDO8ggHej/u2mrf5nwnUU8vilSwbInHmiMpkUswGEKYDEKTA==",
       "license": "MIT",
+      "dependencies": {
+        "@sentry/core": "10.15.0",
+        "@sentry/opentelemetry": "10.15.0",
+        "import-in-the-middle": "^1.14.2"
+      },
       "engines": {
-        "node": ">=8"
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.9.0",
+        "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0",
+        "@opentelemetry/core": "^1.30.1 || ^2.1.0",
+        "@opentelemetry/instrumentation": ">=0.57.1 <1",
+        "@opentelemetry/resources": "^1.30.1 || ^2.1.0",
+        "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0",
+        "@opentelemetry/semantic-conventions": "^1.37.0"
       }
     },
-    "node_modules/@sentry/utils": {
-      "version": "7.120.4",
-      "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.120.4.tgz",
-      "integrity": "sha512-zCKpyDIWKHwtervNK2ZlaK8mMV7gVUijAgFeJStH+CU/imcdquizV3pFLlSQYRswG+Lbyd6CT/LGRh3IbtkCFw==",
-      "dev": true,
+    "node_modules/@sentry/node/node_modules/@sentry/opentelemetry": {
+      "version": "10.15.0",
+      "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.15.0.tgz",
+      "integrity": "sha512-j+uk3bfxGgsBejwpq78iRZ+aBOKR/fWcJi72MBTboTEK3B4LINO65PyJqwOhcZOJVVAPL6IK1+sWQp4RL24GTg==",
       "license": "MIT",
       "dependencies": {
-        "@sentry/types": "7.120.4"
+        "@sentry/core": "10.15.0"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.9.0",
+        "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0",
+        "@opentelemetry/core": "^1.30.1 || ^2.1.0",
+        "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0",
+        "@opentelemetry/semantic-conventions": "^1.37.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/brace-expansion": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+      "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/@sentry/node/node_modules/minimatch": {
+      "version": "9.0.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@sentry/profiling-node": {
+      "version": "10.15.0",
+      "resolved": "https://registry.npmjs.org/@sentry/profiling-node/-/profiling-node-10.15.0.tgz",
+      "integrity": "sha512-W75RnJI8VPJHLmf6TzqcUaJo16/hljzWn1PmTeZnyTNviISQvkzAw1Sk1iTwQt/4FHJEmziFwP1iZOZExfq78A==",
+      "license": "MIT",
+      "dependencies": {
+        "@sentry-internal/node-cpu-profiler": "^2.2.0",
+        "@sentry/core": "10.15.0",
+        "@sentry/node": "10.15.0"
+      },
+      "bin": {
+        "sentry-prune-profiler-binaries": "scripts/prune-profiler-binaries.js"
+      },
+      "engines": {
+        "node": ">=18"
       }
     },
     "node_modules/@sigstore/bundle": {
@@ -21398,7 +21996,6 @@
       "version": "2.15.27",
       "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz",
       "integrity": "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "@types/node": "*"
@@ -31940,13 +32537,6 @@
         "node": ">=16.x"
       }
     },
-    "node_modules/immediate": {
-      "version": "3.0.6",
-      "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
-      "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/immutable": {
       "version": "3.7.6",
       "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz",
@@ -35784,16 +36374,6 @@
         }
       }
     },
-    "node_modules/lie": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
-      "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "immediate": "~3.0.5"
-      }
-    },
     "node_modules/lightningcss": {
       "version": "1.30.1",
       "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
@@ -36623,16 +37203,6 @@
         "url": "https://github.com/sponsors/antfu"
       }
     },
-    "node_modules/localforage": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
-      "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "lie": "3.1.1"
-      }
-    },
     "node_modules/localtunnel": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/localtunnel/-/localtunnel-2.0.2.tgz",
@@ -39295,7 +39865,6 @@
       "version": "3.77.0",
       "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.77.0.tgz",
       "integrity": "sha512-DSmt0OEcLoK4i3NuscSbGjOf3bqiDEutejqENSplMSFA/gmB8mkED9G4pKWnPl7MDU4rSHebKPHeitpDfyH0cQ==",
-      "devOptional": true,
       "license": "MIT",
       "dependencies": {
         "semver": "^7.3.5"
@@ -52960,16 +53529,16 @@
       "name": "@vendure/sentry-plugin",
       "version": "3.4.2",
       "license": "GPL-3.0-or-later",
+      "dependencies": {
+        "@sentry/nestjs": "^10.2.0",
+        "@sentry/profiling-node": "^10.2.0"
+      },
       "devDependencies": {
-        "@sentry/node": "^7.106.1",
         "@vendure/common": "3.4.2",
         "@vendure/core": "3.4.2"
       },
       "funding": {
         "url": "https://github.com/sponsors/michaelbromley"
-      },
-      "peerDependencies": {
-        "@sentry/node": "^7.106.1"
       }
     },
     "packages/stellate-plugin": {

+ 34 - 0
packages/dashboard/src/lib/framework/form-engine/utils.ts

@@ -19,6 +19,8 @@ import {
     StructField,
     TextCustomFieldConfig,
 } from '@/vdb/framework/form-engine/form-engine-types.js';
+import { FormEvent } from 'react';
+import { UseFormReturn } from 'react-hook-form';
 
 import { FieldInfo } from '../document-introspection/get-document-structure.js';
 
@@ -320,3 +322,35 @@ export function hasPermissionRequirement(input: ConfigurableFieldDef): boolean {
 export function isNullableField(input: ConfigurableFieldDef): boolean {
     return isCustomFieldConfig(input) && Boolean(input.nullable);
 }
+
+/**
+ * Handles nested form submission to prevent event bubbling in nested forms.
+ * This is useful when you have a form inside a dialog that's within another form.
+ *
+ * @param form - The react-hook-form instance
+ * @param onSubmit - The submit handler function
+ * @returns An event handler that prevents propagation and handles the form submission
+ *
+ * @example
+ * ```tsx
+ * const form = useForm<FormSchema>({ resolver: zodResolver(formSchema) });
+ *
+ * return (
+ *   <form onSubmit={handleNestedFormSubmit(form, (data) => {
+ *     // Handle form submission
+ *   })}>
+ *     ...
+ *   </form>
+ * );
+ * ```
+ */
+export function handleNestedFormSubmit<TFieldValues extends Record<string, any>>(
+    form: UseFormReturn<TFieldValues>,
+    onSubmit: (data: TFieldValues) => void | Promise<void>,
+) {
+    return (e: FormEvent<HTMLFormElement>) => {
+        e.preventDefault();
+        e.stopPropagation();
+        void form.handleSubmit(onSubmit)(e);
+    };
+}

Разница между файлами не показана из-за своего большого размера
+ 2 - 1
packages/dashboard/src/lib/graphql/graphql-env.d.ts


+ 15 - 0
packages/dashboard/src/lib/index.ts

@@ -22,6 +22,7 @@ export * from './components/data-input/relation-input.js';
 export * from './components/data-input/relation-selector.js';
 export * from './components/data-input/rich-text-input.js';
 export * from './components/data-input/select-with-options.js';
+export * from './components/data-input/slug-input.js';
 export * from './components/data-input/struct-form-input.js';
 export * from './components/data-input/text-input.js';
 export * from './components/data-input/textarea-input.js';
@@ -29,6 +30,7 @@ export * from './components/data-table/add-filter-menu.js';
 export * from './components/data-table/data-table-bulk-action-item.js';
 export * from './components/data-table/data-table-bulk-actions.js';
 export * from './components/data-table/data-table-column-header.js';
+export * from './components/data-table/data-table-context.js';
 export * from './components/data-table/data-table-faceted-filter.js';
 export * from './components/data-table/data-table-filter-badge.js';
 export * from './components/data-table/data-table-filter-dialog.js';
@@ -41,11 +43,20 @@ export * from './components/data-table/filters/data-table-datetime-filter.js';
 export * from './components/data-table/filters/data-table-id-filter.js';
 export * from './components/data-table/filters/data-table-number-filter.js';
 export * from './components/data-table/filters/data-table-string-filter.js';
+export * from './components/data-table/global-views-bar.js';
+export * from './components/data-table/global-views-sheet.js';
 export * from './components/data-table/human-readable-operator.js';
+export * from './components/data-table/manage-global-views-button.js';
+export * from './components/data-table/my-views-button.js';
 export * from './components/data-table/refresh-button.js';
+export * from './components/data-table/save-view-button.js';
+export * from './components/data-table/save-view-dialog.js';
 export * from './components/data-table/types.js';
 export * from './components/data-table/use-all-bulk-actions.js';
 export * from './components/data-table/use-generated-columns.js';
+export * from './components/data-table/user-views-sheet.js';
+export * from './components/data-table/views-sheet.js';
+export * from './components/date-range-picker.js';
 export * from './components/labeled-data.js';
 export * from './components/layout/app-layout.js';
 export * from './components/layout/app-sidebar.js';
@@ -98,9 +109,11 @@ export * from './components/shared/form-field-wrapper.js';
 export * from './components/shared/history-timeline/history-entry-date.js';
 export * from './components/shared/history-timeline/history-note-checkbox.js';
 export * from './components/shared/history-timeline/history-note-editor.js';
+export * from './components/shared/history-timeline/history-note-entry.js';
 export * from './components/shared/history-timeline/history-note-input.js';
 export * from './components/shared/history-timeline/history-timeline-with-grouping.js';
 export * from './components/shared/history-timeline/history-timeline.js';
+export * from './components/shared/history-timeline/use-history-note-editor.js';
 export * from './components/shared/icon-mark.js';
 export * from './components/shared/language-selector.js';
 export * from './components/shared/logo-mark.js';
@@ -183,6 +196,7 @@ export * from './framework/dashboard-widget/metrics-widget/chart.js';
 export * from './framework/dashboard-widget/metrics-widget/metrics-widget.graphql.js';
 export * from './framework/dashboard-widget/orders-summary/order-summary-widget.graphql.js';
 export * from './framework/dashboard-widget/widget-extensions.js';
+export * from './framework/dashboard-widget/widget-filters-context.js';
 export * from './framework/data-table/data-table-extensions.js';
 export * from './framework/defaults.js';
 export * from './framework/document-extension/extend-detail-form-query.js';
@@ -259,6 +273,7 @@ export * from './hooks/use-mobile.js';
 export * from './hooks/use-page-block.js';
 export * from './hooks/use-page.js';
 export * from './hooks/use-permissions.js';
+export * from './hooks/use-saved-views.js';
 export * from './hooks/use-server-config.js';
 export * from './hooks/use-theme.js';
 export * from './hooks/use-user-settings.js';

+ 1 - 1
packages/dev-server/graphql/graphql-env.d.ts

@@ -510,4 +510,4 @@ declare module 'gql.tada' {
   interface setupSchema {
     introspection: introspection
   }
-}
+}

+ 5 - 0
packages/dev-server/test-plugins/reviews/dashboard/index.tsx

@@ -19,6 +19,7 @@ import {
 import { CustomWidget } from './custom-widget';
 import { reviewDetail } from './review-detail';
 import { reviewList } from './review-list';
+import { ReviewSelectWithCreate } from './review-select-with-create';
 
 defineDashboardExtension({
     login: {
@@ -101,6 +102,10 @@ defineDashboardExtension({
                 id: 'review-multi-select',
                 component: ReviewMultiSelect,
             },
+            {
+                id: 'review-multi-select-with-create',
+                component: ReviewSelectWithCreate,
+            },
         ],
     },
     detailForms: [

+ 0 - 7
packages/dev-server/test-plugins/reviews/dashboard/review-detail.tsx

@@ -30,13 +30,6 @@ const reviewDetailDocument = graphql(`
                 id
                 languageCode
                 text
-                customFields {
-                    reviewerName
-                }
-            }
-            customFields {
-                verifiedReviewerName
-                reviewerName
             }
         }
     }

+ 0 - 3
packages/dev-server/test-plugins/reviews/dashboard/review-list.tsx

@@ -27,9 +27,6 @@ const getReviewList = graphql(`
                 state
                 response
                 responseCreatedAt
-                customFields {
-                    reviewerName
-                }
             }
         }
     }

+ 88 - 0
packages/dev-server/test-plugins/reviews/dashboard/review-select-with-create.tsx

@@ -0,0 +1,88 @@
+import { Button } from '@/vdb/components/ui/button';
+import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/vdb/components/ui/dialog';
+import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/vdb/components/ui/form';
+import { Input } from '@/vdb/components/ui/input';
+import { Textarea } from '@/vdb/components/ui/textarea';
+import { DashboardFormComponentProps } from '@/vdb/framework/form-engine/form-engine-types';
+import { handleNestedFormSubmit } from '@/vdb/framework/form-engine/utils';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { useForm } from 'react-hook-form';
+import { z } from 'zod';
+
+import { ReviewMultiSelect } from './custom-form-components';
+
+const formSchema = z.object({
+    title: z.string().min(1, 'Title is required'),
+    body: z.string().min(1, 'Body is required'),
+});
+
+type FormSchema = z.infer<typeof formSchema>;
+
+export function ReviewSelectWithCreate(props: DashboardFormComponentProps) {
+    const form = useForm<FormSchema>({
+        resolver: zodResolver(formSchema),
+        defaultValues: {
+            title: '',
+            body: '',
+        },
+    });
+
+    const onSubmit = (data: FormSchema) => {
+        // TODO: Handle form submission
+        form.reset();
+    };
+
+    return (
+        <div>
+            <ReviewMultiSelect {...props}></ReviewMultiSelect>
+            <Dialog>
+                <DialogTrigger asChild>
+                    <Button variant="outline">Create new</Button>
+                </DialogTrigger>
+
+                <DialogContent>
+                    <DialogHeader>
+                        <DialogTitle>Create new review</DialogTitle>
+                    </DialogHeader>
+                    <Form {...form}>
+                        <form onSubmit={handleNestedFormSubmit(form, onSubmit)} className="space-y-4">
+                            <FormField
+                                control={form.control}
+                                name="title"
+                                render={({ field }) => (
+                                    <FormItem>
+                                        <FormLabel>Title</FormLabel>
+                                        <FormControl>
+                                            <Input placeholder="Enter review title" {...field} />
+                                        </FormControl>
+                                        <FormMessage />
+                                    </FormItem>
+                                )}
+                            />
+                            <FormField
+                                control={form.control}
+                                name="body"
+                                render={({ field }) => (
+                                    <FormItem>
+                                        <FormLabel>Body</FormLabel>
+                                        <FormControl>
+                                            <Textarea
+                                                placeholder="Enter review body"
+                                                className="min-h-[100px]"
+                                                {...field}
+                                            />
+                                        </FormControl>
+                                        <FormMessage />
+                                    </FormItem>
+                                )}
+                            />
+                            <div className="flex justify-end gap-2">
+                                <Button type="submit">Create Review</Button>
+                            </div>
+                        </form>
+                    </Form>
+                </DialogContent>
+            </Dialog>
+        </div>
+    );
+}

+ 12 - 0
packages/dev-server/test-plugins/reviews/reviews-plugin.ts

@@ -19,6 +19,18 @@ import { ProductReview } from './entities/product-review.entity';
         schema: shopApiExtensions,
         resolvers: [ProductEntityResolver, ProductReviewShopResolver, ProductReviewEntityResolver],
     },
+    configuration: config => {
+        config.customFields.Product.push({
+            name: 'reviews',
+            type: 'relation',
+            list: true,
+            entity: ProductReview,
+            inverseSide: (review: ProductReview) => review.product,
+            ui: { component: 'review-multi-select-with-create' },
+        });
+
+        return config;
+    },
     dashboard: './dashboard/index.tsx',
 })
 export class ReviewsPlugin {}

Некоторые файлы не были показаны из-за большого количества измененных файлов