{
+ const sessionOrder = await this.activeOrderService.getActiveOrder(ctx, {}, true);
+
+ const order = await this.orderService.findOne(ctx, sessionOrder.id);
+
+ if (!order) {
+ return null;
+ }
+
+ let orderLine = order.lines.length > 0 ? order.lines[0] : null;
+
+ if (!orderLine) {
+ const result = await this.orderService.addItemToOrder(ctx, sessionOrder.id, 1, 1);
+ if (isGraphQlErrorResult(result)) {
+ throw result.message;
+ } else {
+ orderLine = result.lines[result.lines.length - 1];
+ }
+ }
+
+ let cut = await this.connection.getRepository(ctx, Cut).findOne({ where: { name: 'my-cut' } });
+ if (!cut) {
+ cut = new Cut({ name: 'my-cut' });
+ }
+
+ cut.orderLine = orderLine;
+
+ let cutCode = await this.connection
+ .getRepository(ctx, CutCode)
+ .findOne({ where: { code: 'cut-code' } });
+
+ if (!cutCode) {
+ // Create dummy cutcode
+ const newCutCode = new CutCode({ code: 'cut-code' });
+ cutCode = await this.connection.getRepository(ctx, CutCode).save(newCutCode, { reload: true });
+ }
+
+ cut.code = cutCode;
+
+ // Save cut
+ cut = await this.connection.getRepository(ctx, Cut).save(cut, { reload: true });
+
+ const customFields = {
+ ...orderLine.customFields,
+ cuts: [cut],
+ };
+ orderLine.customFields = customFields;
+ // Save order line
+ const savedOrderLine = await this.connection.getRepository(ctx, OrderLine).save(orderLine);
+ await this.customFieldRelationService.updateRelations(
+ ctx,
+ OrderLine,
+ { customFields },
+ savedOrderLine,
+ );
+
+ return (await this.orderService.findOne(ctx, sessionOrder.id)) || null;
+ }
+}
+
+@VendurePlugin({
+ imports: [PluginCommonModule],
+ providers: [],
+ entities: [Cut, CutCode],
+ shopApiExtensions: {
+ resolvers: [EagerRelationsBugOrderResolver],
+ schema: commonApiExtensions,
+ },
+ adminApiExtensions: {
+ resolvers: [EagerRelationsBugOrderResolver],
+ schema: commonApiExtensions,
+ },
+ configuration: config => {
+ config.customFields.OrderLine.push(
+ {
+ name: 'cuts',
+ type: 'relation',
+ entity: Cut,
+ list: true,
+ eager: true, // ---> BUG: As soon as this relation is set to be loaded eagerly the eager relation to 'code' in the Cut entity won't be resolved anymore.
+ label: [
+ {
+ languageCode: LanguageCode.en,
+ value: 'Cuts',
+ },
+ ],
+ },
+ {
+ name: 'comment',
+ type: 'string',
+ label: [
+ {
+ languageCode: LanguageCode.en,
+ value: 'Comment',
+ },
+ ],
+ },
+ );
+ return config;
+ },
+ compatibility: '^2.1.0',
+})
+export class EagerRelationsBugPlugin {}
diff --git a/packages/dev-server/get-product-count.ts b/packages/dev-server/get-product-count.ts
new file mode 100644
index 0000000000..5cded810ac
--- /dev/null
+++ b/packages/dev-server/get-product-count.ts
@@ -0,0 +1,40 @@
+import { bootstrapWorker, Logger, ProductService, RequestContextService } from '@vendure/core';
+
+import { devConfig } from './dev-config';
+
+if (require.main === module) {
+ getProductCount()
+ .then(() => process.exit(0))
+ .catch(err => {
+ Logger.error(err);
+ process.exit(1);
+ });
+}
+
+async function getProductCount() {
+ // This will bootstrap an instance of the Vendure Worker, providing
+ // us access to all of the services defined in the Vendure core.
+ const { app } = await bootstrapWorker(devConfig);
+
+ // Using `app.get()` we can grab an instance of _any_ provider defined in the
+ // Vendure core as well as by our plugins.
+ const productService = app.get(ProductService);
+
+ // For most service methods, we'll need to pass a RequestContext object.
+ // We can use the RequestContextService to create one.
+ const ctx = await app.get(RequestContextService).create({
+ apiType: 'admin',
+ });
+
+ // We use the `findAll()` method to get the total count. Since we aren't
+ // interested in the actual product objects, we can set the `take` option to 0.
+ const { totalItems } = await productService.findAll(ctx, { take: 0 });
+
+ Logger.info(
+ [
+ '\n-----------------------------------------',
+ `There are ${totalItems} products in the database`,
+ '-----------------------------------------',
+ ].join('\n'),
+ );
+}
diff --git a/packages/dev-server/package.json b/packages/dev-server/package.json
index e816f8992b..95dba598d7 100644
--- a/packages/dev-server/package.json
+++ b/packages/dev-server/package.json
@@ -1,6 +1,6 @@
{
"name": "dev-server",
- "version": "2.2.5",
+ "version": "2.2.6",
"main": "index.js",
"license": "MIT",
"private": true,
@@ -15,17 +15,17 @@
},
"dependencies": {
"@nestjs/axios": "^3.0.2",
- "@vendure/admin-ui-plugin": "^2.2.5",
- "@vendure/asset-server-plugin": "^2.2.5",
- "@vendure/common": "^2.2.5",
- "@vendure/core": "^2.2.5",
- "@vendure/elasticsearch-plugin": "^2.2.5",
- "@vendure/email-plugin": "^2.2.5",
+ "@vendure/admin-ui-plugin": "^2.2.6",
+ "@vendure/asset-server-plugin": "^2.2.6",
+ "@vendure/common": "^2.2.6",
+ "@vendure/core": "^2.2.6",
+ "@vendure/elasticsearch-plugin": "^2.2.6",
+ "@vendure/email-plugin": "^2.2.6",
"typescript": "5.3.3"
},
"devDependencies": {
- "@vendure/testing": "^2.2.5",
- "@vendure/ui-devkit": "^2.2.5",
+ "@vendure/testing": "^2.2.6",
+ "@vendure/ui-devkit": "^2.2.6",
"commander": "^12.0.0",
"concurrently": "^8.2.2",
"csv-stringify": "^6.4.6",
diff --git a/packages/dev-server/test-plugins/experimental-ui/components/LocationMap.tsx b/packages/dev-server/test-plugins/experimental-ui/components/LocationMap.tsx
new file mode 100644
index 0000000000..10e7146203
--- /dev/null
+++ b/packages/dev-server/test-plugins/experimental-ui/components/LocationMap.tsx
@@ -0,0 +1,71 @@
+import { GoogleMap, useJsApiLoader } from '@react-google-maps/api';
+import { Card } from '@vendure/admin-ui/react';
+import React, { useEffect } from 'react';
+
+const containerStyle = {
+ width: '100%',
+ height: '400px',
+};
+
+const center = {
+ lat: 48.212616,
+ lng: 16.3230408,
+};
+
+export function LocationMap() {
+ const { isLoaded } = useJsApiLoader({
+ id: 'google-map-script',
+ googleMapsApiKey: 'AIzaSyCKxhBHUymQG7L57NeRhJRdzlvO4kcymXU',
+ });
+
+ const [map, setMap] = React.useState(null);
+
+ const onLoad = React.useCallback(function callback(map) {
+ // This is just an example of getting and using the map instance!!! don't just blindly copy!
+
+ const bounds = new window.google.maps.LatLngBounds(center);
+ map.fitBounds(bounds);
+ setMap(map);
+ new window.google.maps.Marker({
+ position: center,
+ map,
+ title: 'Hello World!',
+ });
+ }, []);
+
+ useEffect(() => {
+ setTimeout(() => {
+ (map as any)?.setZoom(9);
+ }, 1000);
+ }, [map]);
+
+ const onUnmount = React.useCallback(function callback(map) {
+ setMap(null);
+ }, []);
+
+ return isLoaded ? (
+
+
+
+ {/* Child components, such as markers, info windows, etc. */}
+ <>>
+
+
+
+ ) : (
+ <>>
+ );
+}
diff --git a/packages/dev-server/test-plugins/experimental-ui/providers/cms-data.service.ts b/packages/dev-server/test-plugins/experimental-ui/providers/cms-data.service.ts
new file mode 100644
index 0000000000..e9ed771878
--- /dev/null
+++ b/packages/dev-server/test-plugins/experimental-ui/providers/cms-data.service.ts
@@ -0,0 +1,14 @@
+import { Injectable } from '@angular/core';
+import { of } from 'rxjs';
+
+@Injectable()
+export class CmsDataService {
+ getDataFor(id: string) {
+ return of({
+ id,
+ blogPostUrl: 'https://example.com/blog/' + id,
+ blogPostTitle: 'Example Blog Post',
+ blogTags: ['tag1', 'tag2', 'tag3'],
+ });
+ }
+}
diff --git a/packages/dev-server/test-plugins/with-ui-extension/logos/custom-logo-large.webp b/packages/dev-server/test-plugins/with-ui-extension/logos/custom-logo-large.webp
new file mode 100644
index 0000000000..b753b3bf1c
Binary files /dev/null and b/packages/dev-server/test-plugins/with-ui-extension/logos/custom-logo-large.webp differ
diff --git a/packages/dev-server/test-plugins/with-ui-extension/logos/custom-logo-small.jpg b/packages/dev-server/test-plugins/with-ui-extension/logos/custom-logo-small.jpg
new file mode 100644
index 0000000000..08e220dc1a
Binary files /dev/null and b/packages/dev-server/test-plugins/with-ui-extension/logos/custom-logo-small.jpg differ
diff --git a/packages/dev-server/test-plugins/with-ui-extension/logos/custom-logo-small.webp b/packages/dev-server/test-plugins/with-ui-extension/logos/custom-logo-small.webp
new file mode 100644
index 0000000000..2987338ce1
Binary files /dev/null and b/packages/dev-server/test-plugins/with-ui-extension/logos/custom-logo-small.webp differ
diff --git a/packages/dev-server/test-plugins/with-ui-extension/logos/cyberdyne.png b/packages/dev-server/test-plugins/with-ui-extension/logos/cyberdyne.png
new file mode 100644
index 0000000000..2c1e432751
Binary files /dev/null and b/packages/dev-server/test-plugins/with-ui-extension/logos/cyberdyne.png differ
diff --git a/packages/dev-server/test-plugins/with-ui-extension/ui/custom-form-input.component.ts b/packages/dev-server/test-plugins/with-ui-extension/ui/custom-form-input.component.ts
new file mode 100644
index 0000000000..f31d17c883
--- /dev/null
+++ b/packages/dev-server/test-plugins/with-ui-extension/ui/custom-form-input.component.ts
@@ -0,0 +1,80 @@
+import { NgModule, Component } from '@angular/core';
+import { FormControl } from '@angular/forms';
+import {
+ SharedModule,
+ FormInputComponent,
+ registerFormInputComponent,
+ StringCustomFieldConfig, DataService,
+} from '@vendure/admin-ui/core';
+
+@Component({
+ template: `
+
+ `,
+ // styleUrls: ['./module-styles/checkboxes-form-inputs.component.scss'],
+})
+export class LoginOptionCheckboxes implements FormInputComponent {
+ isListInput = true;
+ readonly = false;
+ config: StringCustomFieldConfig;
+ formControl: FormControl;
+ data: string[] = [];
+ defaultLoginOptions: string[] = ['sso', 'password', 'magicLink'];
+ checkedOptions: string[] = [];
+
+ constructor(private dataService: DataService) {
+ }
+
+ ngOnInit() {
+ // this.data = JSON.parse(this.formControl.value);
+ console.log('formControl', this.formControl);
+ console.log('this.defaultLoginOptions', this.defaultLoginOptions);
+ this.checkedOptions = [...(this.formControl.value ?? [])];
+ }
+
+ onCheckboxChange(event: any, option: string) {
+
+ this.dataService.query(`
+ query GetCustomerAuthOptions {
+ customer {
+ id
+ customFields {
+ org{
+ authOptions
+ }
+ }
+ }
+ }
+ `).single$.subscribe(data => {
+ console.log('data', data);
+ }
+
+ if (this.checkedOptions.includes(option)) {
+ this.checkedOptions = this.checkedOptions.filter(item => item !== option);
+ } else {
+ this.checkedOptions.push(option);
+ }
+ console.log('this.checkedOptions', this.checkedOptions);
+ this.formControl.setValue(this.checkedOptions);
+ this.formControl.markAsDirty();
+ }
+}
+
+// @NgModule({
+// imports: [SharedModule],
+// declarations: [LoginOptionCheckboxes],
+// providers: [registerFormInputComponent('checkboxes-form-inputs', LoginOptionCheckboxes)],
+// })
+// export class LoginOptionsCheckboxesModule {}
diff --git a/packages/dev-server/test-plugins/with-ui-extension/ui/providers.ts b/packages/dev-server/test-plugins/with-ui-extension/ui/providers.ts
new file mode 100644
index 0000000000..e9a9de9d14
--- /dev/null
+++ b/packages/dev-server/test-plugins/with-ui-extension/ui/providers.ts
@@ -0,0 +1,60 @@
+import { Injectable } from '@angular/core';
+import { addActionBarItem, addNavMenuSection } from '@vendure/admin-ui/core';
+import { interval } from 'rxjs';
+import { map, switchMap } from 'rxjs/operators';
+
+@Injectable()
+class MyService {
+ greet() {
+ console.log('Hello!');
+ }
+}
+
+export default [
+ MyService,
+ addNavMenuSection(
+ {
+ id: 'greeter',
+ label: 'My Extensions',
+ items: [
+ {
+ id: 'greeter',
+ label: 'Greeter',
+ routerLink: ['/extensions/example/greet'],
+ // Icon can be any of https://clarity.design/icons
+ icon: 'cursor-hand-open',
+ },
+ ],
+ },
+ // Add this section before the "settings" section
+ 'settings',
+ ),
+ addActionBarItem({
+ id: 'test',
+ icon: 'cursor-hand-open',
+ label: 'Test',
+ locationId: 'order-detail',
+ buttonState: context => {
+ return context.route.data.pipe(
+ switchMap(data => data.detail.entity),
+ map((order: any) => {
+ context.injector.get(MyService).greet();
+ console.log(order);
+ return {
+ disabled: order.state === 'AddingItems',
+ visible: true,
+ };
+ }),
+ );
+ // return interval(1000).pipe(
+ // map(t => {
+ // console.log(t);
+ // return {
+ // disabled: t % 2 === 0,
+ // visible: t % 5 !== 0,
+ // };
+ // }),
+ // );
+ },
+ }),
+];
diff --git a/packages/dev-server/test-plugins/with-ui-extension/ui/routes.ts b/packages/dev-server/test-plugins/with-ui-extension/ui/routes.ts
new file mode 100644
index 0000000000..c866ef8f6d
--- /dev/null
+++ b/packages/dev-server/test-plugins/with-ui-extension/ui/routes.ts
@@ -0,0 +1,33 @@
+import { Component, inject } from '@angular/core';
+import { PageMetadataService, registerRouteComponent, SharedModule } from '@vendure/admin-ui/core';
+
+@Component({
+ selector: 'greeter',
+ template: `
+ {{ greeting }}
+
+
+
+
+ `,
+ standalone: true,
+ imports: [SharedModule],
+})
+export class GreeterComponent {
+ greeting = 'Hello!';
+ title = 'Greeter Page';
+ private pageMetadataService = inject(PageMetadataService);
+
+ setTitle() {
+ this.pageMetadataService.setTitle(this.title);
+ this.pageMetadataService.setBreadcrumbs(this.title);
+ }
+}
+
+export default [
+ registerRouteComponent({
+ component: GreeterComponent,
+ path: 'greet',
+ title: 'Greeter Page',
+ }),
+];
diff --git a/packages/elasticsearch-plugin/package.json b/packages/elasticsearch-plugin/package.json
index ae74707c0e..dc0ba05690 100644
--- a/packages/elasticsearch-plugin/package.json
+++ b/packages/elasticsearch-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/elasticsearch-plugin",
- "version": "2.2.5",
+ "version": "2.2.6",
"license": "MIT",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@@ -26,8 +26,8 @@
"fast-deep-equal": "^3.1.3"
},
"devDependencies": {
- "@vendure/common": "^2.2.5",
- "@vendure/core": "^2.2.5",
+ "@vendure/common": "^2.2.6",
+ "@vendure/core": "^2.2.6",
"rimraf": "^5.0.5",
"typescript": "5.3.3"
}
diff --git a/packages/email-plugin/package.json b/packages/email-plugin/package.json
index 5a67804651..a61e49eee6 100644
--- a/packages/email-plugin/package.json
+++ b/packages/email-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/email-plugin",
- "version": "2.2.5",
+ "version": "2.2.6",
"license": "MIT",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@@ -34,8 +34,8 @@
"@types/express": "^4.17.21",
"@types/fs-extra": "^11.0.4",
"@types/mjml": "^4.7.4",
- "@vendure/common": "^2.2.5",
- "@vendure/core": "^2.2.5",
+ "@vendure/common": "^2.2.6",
+ "@vendure/core": "^2.2.6",
"rimraf": "^5.0.5",
"typescript": "5.3.3"
}
diff --git a/packages/harden-plugin/package.json b/packages/harden-plugin/package.json
index dde92ae2e5..86b730b4dc 100644
--- a/packages/harden-plugin/package.json
+++ b/packages/harden-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/harden-plugin",
- "version": "2.2.5",
+ "version": "2.2.6",
"license": "MIT",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@@ -21,7 +21,7 @@
"graphql-query-complexity": "^0.12.0"
},
"devDependencies": {
- "@vendure/common": "^2.2.5",
- "@vendure/core": "^2.2.5"
+ "@vendure/common": "^2.2.6",
+ "@vendure/core": "^2.2.6"
}
}
diff --git a/packages/job-queue-plugin/package.json b/packages/job-queue-plugin/package.json
index e52baafeaf..b607799ebf 100644
--- a/packages/job-queue-plugin/package.json
+++ b/packages/job-queue-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/job-queue-plugin",
- "version": "2.2.5",
+ "version": "2.2.6",
"license": "MIT",
"main": "package/index.js",
"types": "package/index.d.ts",
@@ -23,8 +23,8 @@
},
"devDependencies": {
"@google-cloud/pubsub": "^2.8.0",
- "@vendure/common": "^2.2.5",
- "@vendure/core": "^2.2.5",
+ "@vendure/common": "^2.2.6",
+ "@vendure/core": "^2.2.6",
"bullmq": "^5.4.2",
"ioredis": "^5.3.2",
"rimraf": "^5.0.5",
diff --git a/packages/payments-plugin/package.json b/packages/payments-plugin/package.json
index d803b86868..a858389c62 100644
--- a/packages/payments-plugin/package.json
+++ b/packages/payments-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/payments-plugin",
- "version": "2.2.5",
+ "version": "2.2.6",
"license": "MIT",
"main": "package/index.js",
"types": "package/index.d.ts",
@@ -46,9 +46,9 @@
"@mollie/api-client": "^3.7.0",
"@types/braintree": "^3.3.11",
"@types/localtunnel": "2.0.4",
- "@vendure/common": "^2.2.5",
- "@vendure/core": "^2.2.5",
- "@vendure/testing": "^2.2.5",
+ "@vendure/common": "^2.2.6",
+ "@vendure/core": "^2.2.6",
+ "@vendure/testing": "^2.2.6",
"braintree": "^3.22.0",
"localtunnel": "2.0.2",
"nock": "^13.1.4",
diff --git a/packages/sentry-plugin/package.json b/packages/sentry-plugin/package.json
index 7363198a7b..09c13a3646 100644
--- a/packages/sentry-plugin/package.json
+++ b/packages/sentry-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/sentry-plugin",
- "version": "2.2.5",
+ "version": "2.2.6",
"license": "MIT",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@@ -22,7 +22,7 @@
},
"devDependencies": {
"@sentry/node": "^7.106.1",
- "@vendure/common": "^2.2.5",
- "@vendure/core": "^2.2.5"
+ "@vendure/common": "^2.2.6",
+ "@vendure/core": "^2.2.6"
}
}
diff --git a/packages/stellate-plugin/package.json b/packages/stellate-plugin/package.json
index 3514bf7e5c..62ba0e783a 100644
--- a/packages/stellate-plugin/package.json
+++ b/packages/stellate-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/stellate-plugin",
- "version": "2.2.5",
+ "version": "2.2.6",
"license": "MIT",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@@ -21,7 +21,7 @@
"node-fetch": "^2.7.0"
},
"devDependencies": {
- "@vendure/common": "^2.2.5",
- "@vendure/core": "^2.2.5"
+ "@vendure/common": "^2.2.6",
+ "@vendure/core": "^2.2.6"
}
}
diff --git a/packages/testing/package.json b/packages/testing/package.json
index e6d6229fdf..92f36e0f45 100644
--- a/packages/testing/package.json
+++ b/packages/testing/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/testing",
- "version": "2.2.5",
+ "version": "2.2.6",
"description": "End-to-end testing tools for Vendure projects",
"keywords": [
"vendure",
@@ -37,7 +37,7 @@
},
"dependencies": {
"@graphql-typed-document-node/core": "^3.2.0",
- "@vendure/common": "^2.2.5",
+ "@vendure/common": "^2.2.6",
"faker": "^4.1.0",
"form-data": "^4.0.0",
"graphql": "16.8.1",
@@ -50,7 +50,7 @@
"@types/mysql": "^2.15.26",
"@types/node-fetch": "^2.6.4",
"@types/pg": "^8.11.2",
- "@vendure/core": "^2.2.5",
+ "@vendure/core": "^2.2.6",
"mysql": "^2.18.1",
"pg": "^8.11.3",
"rimraf": "^5.0.5",
diff --git a/packages/ui-devkit/package.json b/packages/ui-devkit/package.json
index 40462967ce..e27da9fc15 100644
--- a/packages/ui-devkit/package.json
+++ b/packages/ui-devkit/package.json
@@ -1,6 +1,6 @@
{
"name": "@vendure/ui-devkit",
- "version": "2.2.5",
+ "version": "2.2.6",
"description": "A library for authoring Vendure Admin UI extensions",
"keywords": [
"vendure",
@@ -40,8 +40,8 @@
"@angular/cli": "^17.2.3",
"@angular/compiler": "^17.2.4",
"@angular/compiler-cli": "^17.2.4",
- "@vendure/admin-ui": "^2.2.5",
- "@vendure/common": "^2.2.5",
+ "@vendure/admin-ui": "^2.2.6",
+ "@vendure/common": "^2.2.6",
"chalk": "^4.1.0",
"chokidar": "^3.6.0",
"fs-extra": "^11.2.0",
@@ -52,7 +52,7 @@
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
"@types/fs-extra": "^11.0.4",
- "@vendure/core": "^2.2.5",
+ "@vendure/core": "^2.2.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"rimraf": "^5.0.5",