diff --git a/docs/AGENTS.md b/docs/AGENTS.md new file mode 100644 index 000000000..219697b51 --- /dev/null +++ b/docs/AGENTS.md @@ -0,0 +1,3 @@ +# Agent Guidelines + +When renaming or moving documentation files under `docs/admin-sdk/`, add redirect entries to `docs/admin-sdk/docs.yml` mapping old `.html` paths to new ones so the developer portal preserves external links. diff --git a/docs/admin-sdk/CHANGELOG.md b/docs/admin-sdk/CHANGELOG.md index adb4ce4ae..7ba3c6300 100644 --- a/docs/admin-sdk/CHANGELOG.md +++ b/docs/admin-sdk/CHANGELOG.md @@ -1,4 +1,4 @@ -# admin-sdk-docs +# Changelog ## 1.0.0 diff --git a/docs/admin-sdk/README.md b/docs/admin-sdk/README.md deleted file mode 100644 index 3afcca565..000000000 --- a/docs/admin-sdk/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Shopware release notes - -*This repository is embedded into [developer-portal](https://github.com/shopware/developer-portal) under the [/resources/admin-extension-sdk/](https://developer.shopware.com/resources/admin-extension-sdk). This repository is also connected to the Shopware Dev Docs connector GitHub app which manages commit status checks in PRs and triggers production deployments.* - -## Development - -1. Clone this repository - -``` -cd /www/ -git clone git@github.com:shopware/meteor.git -cd meteor/packages/admin-sdk -``` - -2. Make sure you have your local copy of the `developer-portal` repository in the same parent directory. - -``` -pnpm docs:env -``` - -3. Link articles from your local copy of the `meteor` into the `developer-portal`. - -``` -pnpm docs:link -``` - -4. Start the development server. - -``` -pnpm docs:preview -``` \ No newline at end of file diff --git a/docs/admin-sdk/api-reference/base-options.md b/docs/admin-sdk/api-reference/base-options.md index 2bf5f5f9a..f463b3a15 100644 --- a/docs/admin-sdk/api-reference/base-options.md +++ b/docs/admin-sdk/api-reference/base-options.md @@ -1,19 +1,89 @@ -# Base options -There are options that exist for every message type in the SDK. You'll find a list with all of them below. +# Base Options -| Name | Required | Default | Availability | Description | -| :----------- | :------- | :------------- | :------------------ | :---------------------------------------------------------------------------------------------- | -| `privileges` | false | | >= Shopware 6.6.3.0 | The privileges that will be checked before executing the message in the Shopware Administration | +Base options are shared configuration options that can be passed to SDK methods which accept a single payload object. They are provided as additional properties alongside the method's own parameters. -## Example privileges -```typescript -import * as sw from '@shopware-ag/meteor-admin-sdk'; +This applies to APIs such as `notification.dispatch()`, `toast.dispatch()`, many `ui.*` methods, `context.*`, `data.get()`, and `data.update()`. -// This notification will only be displayed if the user has `product:read` permissions. -sw.notification.dispatch({ +Higher-level data helpers such as `data.repository.*` and `data.subscribe()` currently use narrower method signatures and do not expose base options on their public API. + +More options may be added in the future. Currently, the following option is available: + +| Name | Required | Default | Availability | Description | +| :----------- | :------- | :------ | :------------------ | :----------------------------------------------------------------------------------------- | +| `privileges` | false | | >= Shopware 6.6.3.0 | Check the current user's privileges before executing the action. See [Privileges](#privileges). | + +#### Usage + +In this example, the base options are passed on the same object as the method-specific fields: + +```ts +import { notification } from '@shopware-ag/meteor-admin-sdk'; + +notification.dispatch({ + title: 'Product report ready', + message: 'Your product report is ready', + /* ... base options ... */ +}); +``` + +## Privileges + +The `privileges` option accepts an array of privilege strings. When provided, the SDK checks whether the current Administration user holds all listed privileges before executing the action. If any privilege is missing, the action is silently skipped. + +:::warning Not a security feature +Privilege checks happen client-side in the browser. They prevent UI elements from appearing for users who lack the required permissions, but they do not enforce access control on the server. Server-side authorization is still required for any sensitive operation. +::: + +#### Privilege string format + +Each privilege string follows the pattern `action:entity`: + +``` +'product:read' +'order:update' +'category:create' +'customer:delete' +``` + +Available actions: + +| Action | Description | +| :----------- | :----------------------------------- | +| `read` | Read access to the entity | +| `create` | Permission to create new entities | +| `update` | Permission to modify existing entities | +| `delete` | Permission to remove entities | +| `additional` | Custom additional privileges | + +#### Usage + +Pass the `privileges` array to any SDK method that supports base options: + +```ts +import { notification, ui } from '@shopware-ag/meteor-admin-sdk'; + +notification.dispatch({ + title: 'Product report ready', message: 'Your product report is ready', privileges: [ 'product:read', ], }); + +ui.actionButton.add({ + name: 'generate-report', + entity: 'product', + view: 'detail', + label: 'Generate Report', + privileges: [ + 'product:read', + 'order:read', + ], +}); ``` + +#### Parameters + +| Name | Type | Required | Description | +| :----------- | :--------- | :------- | :-------------------------------------------------------------------------- | +| `privileges` | `string[]` | false | Array of `action:entity` strings. All must match for the action to execute. | diff --git a/docs/admin-sdk/api-reference/cms/index.md b/docs/admin-sdk/api-reference/cms/index.md index bd17294ed..67e9cf69a 100644 --- a/docs/admin-sdk/api-reference/cms/index.md +++ b/docs/admin-sdk/api-reference/cms/index.md @@ -1,5 +1,15 @@ --- -title: "CMS" +title: "Extending the CMS" nav: position: 300 --- + + +# Extending the CMS + +These CMS extension features allow extensions to register new CMS blocks and CMS elements for use in the Shopware Shopping Experiences editor. + +Features in this section include: + +- [Register CMS Block](./registerCmsBlock.md): Add custom CMS blocks that define layout structures in the Shopping Experiences editor. +- [Register CMS Element](./registerCmsElement.md): Create custom CMS elements that render content or functionality inside CMS blocks. diff --git a/docs/admin-sdk/api-reference/cms/registerCmsBlock.md b/docs/admin-sdk/api-reference/cms/registerCmsBlock.md index 3f574e9bd..4cf522924 100644 --- a/docs/admin-sdk/api-reference/cms/registerCmsBlock.md +++ b/docs/admin-sdk/api-reference/cms/registerCmsBlock.md @@ -1,3 +1,9 @@ +--- +title: "Register CMS block" +sidebar_position: 20 +--- + + # Register CMS block > Available since Shopware v6.6.1.0 @@ -6,8 +12,13 @@ With `cms.registerCmsBlock` you can register CMS blocks to use in the Shopping E ![Register a CMS block in your Shopping Experiences Module via App](../assets/register-cms-block-example.png) -#### Usage: +## registerCmsBlock() + +#### Usage + ```ts +import { cms } from '@shopware-ag/meteor-admin-sdk'; + cms.registerCmsBlock({ name: 'dailymotion-dual-block', label: 'ex.cms.dailymotion.block.label', @@ -25,6 +36,7 @@ cms.registerCmsBlock({ ``` #### Parameters + | Name | Required | Description | | :------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `name` | true | The name of the cms block - Should have vendor prefix. It can be used in the Storefront for overriding the default layout. | @@ -34,12 +46,15 @@ cms.registerCmsBlock({ | `previewImage` | false | The URL of the preview image. This image is shown in the Shopping Experiences Module when selecting the CMS block. | | `slotLayout` | false | The layout of the slots. This is used to define the grid layout of the slots. You can use the [CSS grid shorthand syntax](https://developer.mozilla.org/en-US/docs/Web/CSS/grid) here. | +#### Return value + +Returns a promise without data. ## Storefront usage The CMS block will render automatically in the Storefront without any additional work. It renders the block as a CSS grid with the slots as grid items and the grid shorthand syntax you provided in the `slotLayout` property. -If you want you can override the default layout by creating a new template file in your app. The file should be named `cms-block-app-renderer.html.twig` and should be placed in the `/Resources/views/storefront/block` directory of your app folder. More details on how to customize the Storefront in your App can be found in this documentation: https://developer.shopware.com/docs/guides/plugins/apps/storefront/customize-templates.html +If you want you can override the default layout by creating a new template file in your app. The file should be named `cms-block-app-renderer.html.twig` and should be placed in the `/Resources/views/storefront/block` directory of your app folder. More details on how to customize the Storefront in your App can be found in the [Customize Templates](https://developer.shopware.com/docs/guides/plugins/apps/storefront/customize-templates.html) guide. Inside this file you need to define the block layout and the slots. The block which needs to be created follows this naming pattern: `block_app_renderer_${yourBlockName}`. The `${yourBlockName}` is the name of the block you registered with `cms.registerCmsBlock` except that you need to replace the hyphens with underscores. @@ -65,4 +80,4 @@ Example: {% endfor %} {% endblock %} -``` \ No newline at end of file +``` diff --git a/docs/admin-sdk/api-reference/cms/registerCmsElement.md b/docs/admin-sdk/api-reference/cms/registerCmsElement.md index b63f735b9..5aed06f9b 100644 --- a/docs/admin-sdk/api-reference/cms/registerCmsElement.md +++ b/docs/admin-sdk/api-reference/cms/registerCmsElement.md @@ -1,4 +1,10 @@ -# Register CMS element +--- +title: "Register CMS Element" +sidebar_position: 30 +--- + + +# Register CMS Element > Available since Shopware v6.4.17.0 @@ -7,8 +13,13 @@ More information on how to develop CMS elements can be found in these guides for ![Register a CMS element in your Shopping Experiences Module via App](../assets/register-cms-element-example.png) -#### Usage: +## registerCmsElement() + +#### Usage + ```ts +import { cms } from '@shopware-ag/meteor-admin-sdk'; + void cms.registerCmsElement({ name: 'dailymotionElement', label: 'Dailymotion Video', @@ -22,8 +33,13 @@ void cms.registerCmsElement({ ``` #### Parameters + | Name | Required | Description | |:----------------|:---------|:---------------------------------------------------------------------------------------------------------| | `name` | true | The name of the cms element, which will also be used to generate locationIds - Should have vendor prefix | | `label` | true | The label, which is visible when selecting the cms element - Use snippet keys here! | | `defaultConfig` | true | Object containing the defaultConfig; same like in plugin development. | + +#### Return value + +Returns a promise without data. diff --git a/docs/admin-sdk/api-reference/composables/getRepository.md b/docs/admin-sdk/api-reference/composables/getRepository.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/admin-sdk/api-reference/composables/index.md b/docs/admin-sdk/api-reference/composables/index.md index c3d9fb086..103992199 100644 --- a/docs/admin-sdk/api-reference/composables/index.md +++ b/docs/admin-sdk/api-reference/composables/index.md @@ -3,3 +3,15 @@ title: "Vue Composables" nav: position: 400 --- + + +# Vue Composables + +Composable APIs provide reusable Vue Composables for working with the Shopware Administration data layer and shared state inside extensions. + +They simplify common tasks such as accessing repositories or sharing reactive state between different parts of an extension. + +Currently, the SDK exposes: + +- [useRepository](./useRepository.md): Create a reactive repository instance for a given entity. +- [useSharedState](./useSharedState.md): Share reactive state between different extension components. diff --git a/docs/admin-sdk/api-reference/composables/useRepository.md b/docs/admin-sdk/api-reference/composables/useRepository.md index d858b9252..2f600f389 100644 --- a/docs/admin-sdk/api-reference/composables/useRepository.md +++ b/docs/admin-sdk/api-reference/composables/useRepository.md @@ -1,71 +1,45 @@ +--- +title: "useRepository" +nav: + position: 20 +--- + # useRepository -The `composables.useRepository` function is a reactive wrapper around the `getRepository` function. It creates a repository instance that automatically updates when its dependencies change. This is particularly useful when you need a repository that responds to reactive data changes in your Vue components. +The `composables.useRepository` function creates a reactive repository instance that automatically updates when its dependencies change. This is particularly useful when you need a repository that responds to reactive data changes in your Vue components. + +`useRepository` accepts reactive references (refs) or values as parameters and returns a computed repository that updates when those parameters change. -Unlike `getRepository`, which returns a static repository instance, `useRepository` accepts reactive references (refs) or values as parameters and returns a computed repository that updates when those parameters change. +## Usage -#### Usage: ```ts // Inside a Vue component setup -import { ref } from 'vue'; -import { composables } from '@shopware-ag/meteor-admin-sdk'; +import { ref } from "vue"; +import { composables } from "@shopware-ag/meteor-admin-sdk"; const { useRepository } = composables; // With a reactive entity name -const entityName = ref('product'); +const entityName = ref("product"); const productRepository = useRepository(entityName); // The repository updates automatically if entityName changes -entityName.value = 'category'; +entityName.value = "category"; // Now productRepository.value references a category repository -// With a reactive repository factory -const myFactory = ref(customRepositoryFactory); -const repository = useRepository('product', myFactory); +// With a custom repository factory +const repository = useRepository("product", myRepositoryFactory); // Search for products const products = await repository.value.search(criteria); ``` -## Dynamic Repository Creation - -The main advantage of `useRepository` is that it automatically recreates the repository when its inputs change: - -1. If the entity name changes, a new repository for the different entity type is created -2. If the repository factory changes, a new repository using the different factory is created - -This reactivity is implemented using Vue's computed properties, ensuring that the repository is only recreated when necessary. - -#### Parameters -| Name | Required | Description | -|:--------------------|:---------|:----------------------------------------------------------------| -| `entityNameRef` | true | The name of the entity type as a ref or static value | -| `repositoryFactory` | false | Optional repository factory as a ref or static value | +## Parameters -#### Return Value -A computed ref containing a repository that updates when its dependencies change. The repository provides the same methods as described in the `getRepository` documentation, but you need to access them through the `.value` property of the computed ref. +| Name | Required | Description | +| :------------------ | :------- | :--------------------------------------------------- | +| `entityNameRef` | true | The name of the entity type as a ref or static value | +| `repositoryFactory` | false | Optional repository factory as a ref or static value | -## Relationship with getRepository - -Under the hood, `useRepository` calls `getRepository` whenever its dependencies change. This means: - -- It uses the same repository factory resolution logic as `getRepository` -- It provides the same repository interface and functionality -- It adds reactivity, automatically updating when inputs change - -```ts -// Example implementation (simplified) -import { computed } from 'vue'; -import { getRepository } from './getRepository'; - -export function useRepository(entityNameRef, factoryRef) { - return computed(() => { - const entityName = unref(entityNameRef); - const factory = unref(factoryRef); - - return getRepository(entityName, factory); - }); -} -``` +## Return value -This pattern follows Vue's composition API conventions, where composables prefixed with "use" typically provide reactive wrappers around non-reactive functionality. +A computed ref containing a repository that updates when its dependencies change. Access the repository methods through the `.value` property of the computed ref. diff --git a/docs/admin-sdk/api-reference/composables/useSharedState.md b/docs/admin-sdk/api-reference/composables/useSharedState.md index cdaa9ce1d..d6dda99c8 100644 --- a/docs/admin-sdk/api-reference/composables/useSharedState.md +++ b/docs/admin-sdk/api-reference/composables/useSharedState.md @@ -1,24 +1,46 @@ -# useSharedState +--- +title: "useSharedState" +nav: + position: 30 +--- -The `composables.useSharedState` function allows you to create globally accessible state in your app. The state defined within this composable has a unique key, and any other part of the app that uses the same composable with the same key will access the same data. +# useSharedState -The shared state is reactive, meaning that when you update the data in one place, all other places that access the same shared state will be automatically updated as well. This feature is particularly useful when you need to pass data to different locations, such as modals or locations outside the current iFrame. The shared state is also saved locally to the user's machine using IndexedDB, ensuring persistence even after refreshes or when the user is using multiple tabs. +The `composables.useSharedState` function allows you to create shared state across SDK locations. In practice, this means state that can be accessed across the different iframes your extension runs in inside the Shopware Administration. The state defined within this composable has a unique key, and any other part of the extension that uses the same key will access the same data. The value stored within the shared state can be any data type that can be serialized to JSON. Additionally, we have added support for Entities and EntityCollections. -![useShardState demo](../assets/useSharedState-demo.gif) +`useSharedState` solves a different problem than a normal Vue store: + +- It synchronizes data between multiple SDK locations and iframes. +- It persists data in IndexedDB, so the state survives page reloads. +- It works without adding another dependency to your extension. +- It supports Shopware Entities and EntityCollections in addition to plain JSON data. + +If your state only lives inside a single Vue application, Pinia is still a good fit. If the same extension renders in multiple locations, `useSharedState` keeps them in sync. + +![useSharedState demo](../assets/useSharedState-demo.gif) + +## Usage -#### Usage: ```ts // Inside a Vue component setup -import { composables } from '@shopware-ag/meteor-admin-sdk'; +import { composables } from "@shopware-ag/meteor-admin-sdk"; const { useSharedState } = composables; -const mySharedStateValue = useSharedState('myUniqueKeyForTheSharedState', 'myInitialDataValue'); +const mySharedStateValue = useSharedState( + "myUniqueKeyForTheSharedState", + "myInitialDataValue", +); ``` -#### Parameters +## Parameters + | Name | Required | Description | | :------------- | :------- | :------------------------------------------------------------------------ | | `key` | true | The unique key used to share the state across different places | | `initial data` | true | The initial data value used when no data exists in the local shared state | + +## Return value + +Returns a reactive state object with a `value` property. Reading or updating `value` accesses the shared state for the given key across all matching SDK locations. diff --git a/docs/admin-sdk/api-reference/context.md b/docs/admin-sdk/api-reference/context.md index 31887b6bc..f0f673e1d 100644 --- a/docs/admin-sdk/api-reference/context.md +++ b/docs/admin-sdk/api-reference/context.md @@ -1,26 +1,43 @@ +--- +title: "Context" +sidebar_position: 40 +--- + # Context -## Language +The Context API provides read access to the current state of the Shopware Administration. Extensions can use these methods to retrieve information about the active language, locale, currency, environment, Shopware version, and more. + +This is useful for adapting extension behavior based on the current Administration context — for example, loading translations for the active language or checking the Shopware version before using a newer API. + +```ts +import { context } from "@shopware-ag/meteor-admin-sdk"; +``` + +## getLanguage() -### Get current language +Returns the current Administration language ID and the system default language ID. Use this to load the correct translations or filter data by language. + +#### Usage -#### Usage: ```ts -const language = await sw.context.getLanguage(); +const language = await context.getLanguage(); ``` #### Parameters + No parameters needed. -#### Return value: +#### Return value + ```ts Promise<{ - languageId: string, - systemLanguageId: string + languageId: string; + systemLanguageId: string; }> ``` -#### Example value: +#### Example value + ```ts { languageId: '2fbb5fe2e29a4d70aa5854ce7ce3e20b', @@ -28,21 +45,26 @@ Promise<{ } ``` -### Subscribe on language changes +## subscribeLanguage() + +Subscribes to language changes in the Administration. The callback fires whenever the user switches languages, allowing extensions to react immediately (e.g. reloading translated content). + +#### Usage -#### Usage: ```ts -sw.context.subscribeLanguage(({ languageId, systemLanguageId }) => { +context.subscribeLanguage(({ languageId, systemLanguageId }) => { // do something with the callback data }); ``` #### Parameters -| Name | Description | -| :------ | :------ | + +| Name | Description | +| :--------------- | :------------------------------------- | | `callbackMethod` | Called every-time the language changes | -#### Callback value: +#### Callback value + ```ts { languageId: string, @@ -50,7 +72,8 @@ sw.context.subscribeLanguage(({ languageId, systemLanguageId }) => { } ``` -#### Example callback value: +#### Example callback value + ```ts { languageId: '2fbb5fe2e29a4d70aa5854ce7ce3e20b', @@ -58,49 +81,57 @@ sw.context.subscribeLanguage(({ languageId, systemLanguageId }) => { } ``` -## Environment +## getEnvironment() -### Get current environment +Returns the current Administration environment mode. Use this to enable debug features or disable analytics in non-production environments. + +#### Usage -#### Usage: ```ts -const environment = await sw.context.getEnvironment(); +const environment = await context.getEnvironment(); ``` #### Parameters + No parameters needed. -#### Return value: +#### Return value + ```ts -Promise<'development' | 'production' | 'testing'> +Promise<"development" | "production" | "testing"> ``` -#### Example value: +#### Example value + ```ts -'development' +"development"; ``` -## Locale +## getLocale() -### Get current locale +Returns the browser locale used by the Administration UI, including a fallback locale. Use this to format dates, numbers, or currencies according to the user's regional settings. + +#### Usage -#### Usage: ```ts -const locale = await sw.context.getLocale(); +const locale = await context.getLocale(); ``` #### Parameters + No parameters needed. -#### Return value: +#### Return value + ```ts Promise<{ - locale: string, - fallbackLocale: string + locale: string; + fallbackLocale: string; }> ``` -#### Example value: +#### Example value + ```ts { locale: 'de-DE', @@ -108,21 +139,26 @@ Promise<{ } ``` -### Subscribe on locale changes +## subscribeLocale() + +Subscribes to locale changes in the Administration. The callback fires whenever the locale changes, allowing extensions to re-render locale-dependent content like formatted dates or currencies. + +#### Usage -#### Usage: ```ts -sw.context.subscribeLocale(({ locale, fallbackLocale }) => { +context.subscribeLocale(({ locale, fallbackLocale }) => { // do something with the callback data }); ``` #### Parameters -| Name | Description | -| :------ | :------ | + +| Name | Description | +| :--------------- | :----------------------------------- | | `callbackMethod` | Called every-time the locale changes | -#### Callback value: +#### Callback value + ```ts { locale: string, @@ -130,7 +166,8 @@ sw.context.subscribeLocale(({ locale, fallbackLocale }) => { } ``` -#### Example callback value: +#### Example callback value + ```ts { locale: 'de-DE', @@ -138,27 +175,31 @@ sw.context.subscribeLocale(({ locale, fallbackLocale }) => { } ``` -## Currency +## getCurrency() -### Get current currency +Returns the system currency configured for the Shopware instance. Use this when displaying prices or working with monetary values. + +#### Usage -#### Usage: ```ts -const currency = await sw.context.getCurrency(); +const currency = await context.getCurrency(); ``` #### Parameters + No parameters needed. -#### Return value: +#### Return value + ```ts Promise<{ - systemCurrencyId: string, - systemCurrencyISOCode: string + systemCurrencyId: string; + systemCurrencyISOCode: string; }> ``` -#### Example value: +#### Example value + ```ts { systemCurrencyId: 'b7d2554b0ce847cd82f3ac9bd1c0dfca', @@ -166,138 +207,153 @@ Promise<{ } ``` -## Shopware version +## getShopwareVersion() + +Returns the Shopware version as a string. Use this to conditionally enable features or check compatibility before using newer APIs. -### Get current Shopware version +#### Usage -#### Usage: ```ts -const shopwareVersion = await sw.context.getShopwareVersion(); +const shopwareVersion = await context.getShopwareVersion(); ``` #### Parameters + No parameters needed. -#### Return value: +#### Return value + ```ts string ``` -#### Example value: +#### Example value + ```ts -'6.4.0.0' +"6.4.0.0"; ``` -### Compare current Shopware version with a given version +## compareIsShopwareVersion() -In many cases you have to make sure that the shop you are communicating with has a certain Shopware version. For this purpose the Meteor Admin SDK provides the `context.compareIsShopwareVersion` function. +Compares the current Shopware version against a target version. The current Shopware version is always the left-hand side of the comparison — so `context.compareIsShopwareVersion('>=', '7.0.0')` reads as "is the current Shopware version equal to or greater than 7.0.0?" -The function always treats the current Shopware version of a shop as the left hand operator of the comparison. That means a call like `context.compareIsShopwareVersion('>=', '7.0.0')` can be read as "*Compare: is Shopware version equal or greater than 7.0.0*" +#### Usage -#### Usage: ```ts -const isRightVersion = await sw.context.compareShopwareVersion('>=', '7.0.0') +const isRightVersion = await context.compareIsShopwareVersion(">=", "7.0.0"); ``` #### Parameters -| Name | Description | -|:-------------|:------------------------------------------------------------------------------------------------------------------| -| `comparator` | The operator to compare. Possible values: `'='` `'!='` `'>'` `'<'` `'<='` `'>='`| -| `version` | The string with the version to compare +| Name | Description | +| :----------- | :------------------------------------------------------------------------------- | +| `comparator` | The operator to compare. Possible values: `'='` `'!='` `'>'` `'<'` `'<='` `'>='` | +| `version` | The string with the version to compare | -The function supports both, Shopware's four-digit version number and semver versions. The following calls are equivalent: +The function supports both Shopware's four-digit version number and semver versions. The following calls are equivalent: ```ts -await sw.context.compareShopwareVersion('>=', '6.6.4.0'); +await context.compareIsShopwareVersion(">=", "6.6.4.0"); -await sw.context.compareShopwareVersion('>=', '6.4.0'); +await context.compareIsShopwareVersion(">=", "6.4.0"); ``` -#### Return value: +#### Return value ```ts boolean ``` -#### Example value: +#### Example value + ```ts -true +true; ``` -## App information +## getAppInformation() + +Returns metadata about the current app or plugin, including its name, version, type, and granted privileges. Use this to adapt behavior based on the extension type or check which permissions were granted. -### Get app information +> The `privileges` property is available since Shopware v6.7.1.0. -> The privileges property will be available with Shopware v6.7.1.0 and higher +#### Usage -#### Usage: ```ts -const { name, version, type, privileges } = await sw.context.getAppInformation(); +const { name, version, type, privileges } = await context.getAppInformation(); ``` #### Parameters + No parameters needed. -#### Return value: +#### Return value + ```ts -Promise<{ name: string ; version: string ; type: 'app' | 'plugin', privileges: privileges }> +Promise<{ + name: string; + version: string; + type: "app" | "plugin"; + privileges: privileges; +}> ``` -#### Example value: +#### Example value + ```ts { name: 'my-extension', version: '1.2.3', - type: 'app' + type: 'app', privileges: { read: [ 'product', 'customer' ], - write: [ 'product' ], + update: [ 'product' ], additional: [ 'system.cache_clear' ] } } ``` -## User information +## getUserInformation() + +Returns details about the currently logged-in Administration user, including their roles, email, and admin status. Use this to personalize the extension UI or check user permissions. -### Get user information +> Available since Shopware v6.4.9.0 -:::caution -Do not use this feature yet. It is not implemented in a Shopware release yet. -::: +#### Usage -#### Usage: ```ts -const userInformation = await sw.context.getUserInformation(); +const userInformation = await context.getUserInformation(); ``` #### Parameters + No parameters needed. -#### Return value: +#### Return value + ```ts Promise<{ aclRoles: Array<{ - name: string, - type: string, - id: string, - privileges: Array, - }>, - active: boolean, - admin: boolean, - avatarId: string, - email: string, - firstName: string, - id: string, - lastName: string, - localeId: string, - title: string, - type: string, - username: string, + name: string; + type: string; + id: string; + privileges: Array; + }>; + active: boolean; + admin: boolean; + avatarId: string; + email: string; + firstName: string; + id: string; + lastName: string; + localeId: string; + title: string; + type: string; + username: string; }> ``` -#### Example value: +#### Example value + ```ts { "aclRoles": [], @@ -315,120 +371,123 @@ Promise<{ } ``` -## User Timezone +## getUserTimezone() -### Get user timezone +Returns the timezone setting of the currently logged-in user. Use this to display dates and times in the user's local timezone. -:::caution -This feature will be available with Shopware ^6.6.2.0 -::: +> Available since Shopware v6.6.2.0 -This feature allows you to get the timezone of the user. +#### Usage -#### Usage: ```ts -const userTimezone = await sw.context.getUserTimezone(); +const userTimezone = await context.getUserTimezone(); ``` #### Parameters + No parameters needed. -#### Return value: +#### Return value + ```ts Promise ``` This function returns a Promise that resolves to a string representing the user's timezone. -## Module information +## getModuleInformation() -### Get module information -Get information about all registered modules. These modules are created by adding new menu items, setting items, etc. +Returns the list of all registered extension modules (created by adding menu items, settings items, etc.). Use the module ID to navigate between extensions. -The ID can be used to change the current route to the module. +#### Usage -#### Usage: ```ts -const { modules } = await sw.context.getModuleInformation(); +import { window as swWindow } from "@shopware-ag/meteor-admin-sdk"; + +const { modules } = await context.getModuleInformation(); -sw.window.routerPush({ - name: 'sw.extension.sdk.index', +swWindow.routerPush({ + name: "sw.extension.sdk.index", params: { - id: modules[0].id // get the ID of the wanted module - } -}) + id: modules[0].id, + }, +}); ``` #### Parameters + No parameters needed. -#### Return value: +#### Return value + ```ts Promise<{ modules: Array<{ - displaySearchBar: boolean, - heading: string, - id: string, - locationId: string - }> + displaySearchBar: boolean; + heading: string; + id: string; + locationId: string; + }>; }> ``` -#### Example value: +#### Example value + ```ts { modules: [ { displaySearchBar: true, - heading: 'My module', - id: 'sd5aasfsdfas', - locationId: 'my-location-id' - } - ] + heading: "My module", + id: "sd5aasfsdfas", + locationId: "my-location-id", + }, + ]; } ``` -## ShopId +## getShopId() -### Get the shopId +Returns the unique shop ID used by Shopware's app system. Use this to identify the shop instance when communicating with external services. > Available since Shopware v6.7.1.0 -Get the shop's shop-id used by Shopware's app system - #### Usage ```ts -const shopId = await sw.context.getShopId(); +const shopId = await context.getShopId(); ``` #### Parameters -no parameters needed +No parameters needed. -#### Return value: +#### Return value ```ts -Promise +Promise ``` -## Check app's privileges +## can() -> Available since Shopware 6.7.1.0 +Checks whether a specific privilege is granted for the current app. Use this to conditionally show features that require specific permissions. -This lets you check if a specific privilege is granted for your app +> Available since Shopware v6.7.1.0 #### Usage ```ts -const isAllowed: boolean = await sw.context.can('product:read'); +const isAllowed: boolean = await context.can("product:read"); ``` #### Parameters -No parameters needed. + +| Name | Description | +| :---------- | :--------------------------------------------------- | +| `privilege` | The privilege string to check, e.g. `'product:read'` | #### Return value ```ts boolean -``` \ No newline at end of file +``` diff --git a/docs/admin-sdk/api-reference/data/get.md b/docs/admin-sdk/api-reference/data/get.md index e77c2cb3b..ed27cda40 100644 --- a/docs/admin-sdk/api-reference/data/get.md +++ b/docs/admin-sdk/api-reference/data/get.md @@ -1,22 +1,48 @@ +--- +title: "Get" +nav: + position: 30 +--- + # Get -With `data.get` you can receive datasets from the Shopware administration. -More information on how to find the unique identifiers can be found in [this guide](../../internals/datahandling.md). +With `data.get` you can receive datasets from the Shopware Administration. + +Compared to `data.subscribe`, `data.get` only gives you the current state of the data. If the data is not available yet, such as when opening a page, you won't receive any data. In these cases, it's better to subscribe to data changes instead. + +[The data handling guide](../../concepts/datahandling.md) explains how to find available datasets. + +## data.get() -Compared to data.subscribe, data.get only gives you the current state of the data. If the data is not available yet, -such as when opening a page, you won't receive any data. In these cases, it's better to subscribe to data changes instead. +`get()` returns the current value of a dataset once. Compared to `subscribe()`, it does not continue listening for updates. + +#### Usage -#### Usage: ```ts -data.get({ - id: 'sw-product-detail__product', - selectors: ['name', 'manufacturer.name'], -}).then((product) => { +import { data } from "@shopware-ag/meteor-admin-sdk"; + +data + .get({ + id: "sw-product-detail__product", + selectors: ["name", "manufacturer.name"], + }) + .then((product) => { console.log(product); -}); + }); ``` -#### Output: +#### Parameters + +| Name | Required | Description | +| :-------- | :------- | :------------------------------------------------------------------------------------------------------------------ | +| `options` | true | Containing the unique `id` and optional `selectors`. Read more about selectors [here](../../concepts/selectors.md). | + +#### Return value + +Returns a promise that resolves with the current dataset value. + +For example: + ```json { "name": "Ergonomic Copper Mr. Frenzy", @@ -25,8 +51,3 @@ data.get({ } } ``` - -#### Parameters -| Name | Required | Description | -| :-------- | :------- |:---------------------------------------------------------------------------------------------------------------------| -| `options` | true | Containing the unique `id` and optional `selectors`. Read more about selectors [here](../../concepts/selectors.md) | diff --git a/docs/admin-sdk/api-reference/data/index.md b/docs/admin-sdk/api-reference/data/index.md index 3cdc8aff9..5008fb911 100644 --- a/docs/admin-sdk/api-reference/data/index.md +++ b/docs/admin-sdk/api-reference/data/index.md @@ -1,5 +1,24 @@ --- -title: "Data" +title: "Working with Data" nav: position: 200 --- + + +# Working with Data + +The Meteor Admin SDK provides tools for accessing and manipulating Shopware data from within the Administration. These APIs allow extensions to interact with Shopware entities, react to changes in data, and update records using the same repository-based data layer used by the Administration itself. + +Typical data workflows follow this pattern: + +1. Access an entity repository +2. Retrieve entities or collections +3. Subscribe to updates or changes +4. Modify or persist data + +## Data access and operations + +- [Repository](./repository.md): Access Shopware entity repositories. +- [Get](./get.md): Retrieve entity data from the Administration data layer. +- [Subscribe](./subscribe.md): React to changes in entity data. +- [Update](./update.md): Modify and persist entity data. diff --git a/docs/admin-sdk/api-reference/data/repository.md b/docs/admin-sdk/api-reference/data/repository.md index f65c745ff..9e5a2693e 100644 --- a/docs/admin-sdk/api-reference/data/repository.md +++ b/docs/admin-sdk/api-reference/data/repository.md @@ -1,3 +1,10 @@ +--- +title: "Repository" +nav: + position: 20 +--- + + # Repository The data handling of the SDK allows you to fetch and write nearly everything in the database. The behavior matches the data handling in the main administration. The only difference is the implementation details because the data handling don't request the server directly. It communicates with the admin which handles the requests, changesets, saving and more. @@ -5,12 +12,13 @@ The data handling of the SDK allows you to fetch and write nearly everything in The data handling implements the repository pattern. You can create a repository for an entity simply like this: ```ts -sw.data.repository('your_entity_name') +import { data } from '@shopware-ag/meteor-admin-sdk'; + +const productRepository = data.repository('product'); ``` With this repository you can search for data, save it, delete it, create it or check for changes. - ### Permissions For every action on the repository, your app will need the matching permissions. Permissions are set in the app manifest file and are grouped by action. @@ -40,7 +48,10 @@ Remember everytime you adjust the permissions in your manifest you need to incre For requesting data you need to create a Criteria class which contains all information for the request: ```ts -const criteria = new sw.data.Classes.Criteria(); +import { data } from '@shopware-ag/meteor-admin-sdk'; + +const { Criteria } = data.Classes; +const criteria = new Criteria(); criteria.setPage(1); criteria.setLimit(10); @@ -72,158 +83,162 @@ criteria.getAssociation('categories') ``` -### Search +### repository.search() Sends a search request for the repository entity. -#### Usage: +#### Usage ```ts -const exampleRepository = sw.data.repository('your_entity'); +const exampleRepository = data.repository('your_entity'); const yourEntities = await exampleRepository.search(yourCriteria); ``` -#### Parameters: +#### Parameters | Name | Required | Default | Description | | :--------- | :------- | :------ | :--------------------------------------------- | | `criteria` | true | | Your criteria object | | `context` | false | {} | Change the [request context](#request-context) | -#### Return value: +#### Return value The return value is a EntityCollection which contains all entities matching the criteria. -### Get +### repository.get() Short hand to fetch a single entity from the server -#### Usage: +#### Usage ```ts -const exampleRepository = sw.data.repository('your_entity'); +const exampleRepository = data.repository('your_entity'); const yourEntity = await exampleRepository.get('theEntityId'); ``` -#### Parameters: +#### Parameters | Name | Required | Default | Description | | :--------- | :------- | :------ | :--------------------------------------------- | | `id` | true | | The id of the entity | | `context` | false | {} | Change the [request context](#request-context) | | `criteria` | true | | Your criteria object | -#### Return value: +#### Return value The return value is the entity result when a matching entity was found. -### Save +### repository.save() Detects all entity changes and send the changes to the server. If the entity is marked as new, the repository will send a POST create. Updates will be send as PATCH request. Deleted associations will be send as additional request -#### Usage: +#### Usage ```ts -const exampleRepository = sw.data.repository('your_entity'); +const exampleRepository = data.repository('your_entity'); await exampleRepository.save(yourEntityObject); ``` -#### Parameters: +#### Parameters | Name | Required | Default | Description | | :-------- | :------- | :------ | :--------------------------------------------- | | `entity` | true | | The entity object | | `context` | false | {} | Change the [request context](#request-context) | -#### Return value: +#### Return value This method does not have a return value. It just returns a Promise which is resolved when it was saved successfully. -### Clone +### repository.clone() Clones an existing entity -#### Usage: +#### Usage ```ts -const exampleRepository = sw.data.repository('your_entity'); +const exampleRepository = data.repository('your_entity'); -const clonedEntityId = await exampleRepository.clone('theEntityIdToClone'); +const clonedEntityId = await exampleRepository.clone( + 'theEntityIdToClone', + yourApiContext +); ``` -#### Parameters: +#### Parameters | Name | Required | Default | Description | | :--------- | :------- | :------ | :--------------------------------------------- | | `entityId` | true | | The entity id which should be cloned | -| `context` | false | {} | Change the [request context](#request-context) | +| `context` | true | | Change the [request context](#request-context) | +| `behavior` | false | | Configure the [clone behavior](#clone-behavior) | -#### Return value: +#### Return value This method returns the id of the cloned entity. -### Has changes +### repository.hasChanges() Detects if the entity or the relations has remaining changes which are not synchronized with the server -#### Usage: +#### Usage ```ts -const exampleRepository = sw.data.repository('your_entity'); +const exampleRepository = data.repository('your_entity'); const hasChanges = await exampleRepository.hasChanges(yourEntityObject); ``` -#### Parameters: +#### Parameters | Name | Required | Default | Description | | :------- | :------- | :------ | :---------------- | | `entity` | true | | The entity object | -#### Return value: +#### Return value This method returns a boolean value. If the entity has changes then it returns `true`. Otherwise it returns `false`. -### Save all +### repository.saveAll() Detects changes of all provided entities and send the changes to the server -#### Usage: +#### Usage ```ts -const exampleRepository = sw.data.repository('your_entity'); +const exampleRepository = data.repository('your_entity'); await exampleRepository.saveAll(yourEntityCollection); ``` -#### Parameters: +#### Parameters | Name | Required | Default | Description | | :--------- | :------- | :------ | :--------------------------------------------- | | `entities` | true | | Your entity collection which should be saved | | `context` | false | {} | Change the [request context](#request-context) | -#### Return value: +#### Return value This method does not have a return value. It just returns a Promise which is resolved when it was saved successfully. -### Delete +### repository.delete() Sends a delete request for the provided id. -#### Usage: +#### Usage ```ts -const exampleRepository = sw.data.repository('your_entity'); +const exampleRepository = data.repository('your_entity'); await exampleRepository.delete('yourEntityId'); ``` -#### Parameters: +#### Parameters | Name | Required | Default | Description | | :--------- | :------- | :------ | :--------------------------------------------- | | `entityId` | true | | The id of the entity which should be deleted | | `context` | false | {} | Change the [request context](#request-context) | -#### Return value: +#### Return value This method does not have a return value. It just returns a Promise which is resolved when it was deleted successfully. -### Create +### repository.create() Creates a new entity for the local schema. To Many association are initialed with a collection with the corresponding remote api route. This entity is not saved to the database yet. -#### Usage: +#### Usage ```ts -const exampleRepository = sw.data.repository('your_entity'); +const exampleRepository = data.repository('your_entity'); const yourNewEntity = await exampleRepository.create(); ``` -#### Parameters: +#### Parameters | Name | Required | Default | Description | | :--------- | :------- | :------ | :--------------------------------------------- | | `context` | false | {} | Change the [request context](#request-context) | | `id` | false | | You can provide a id of the new entity if wanted | -#### Return value: +#### Return value This method returns the newly created entity. ### Request Context @@ -239,3 +254,22 @@ const exampleContext = { liveVersionId: 'yourLiveVersionId' } ``` + +### Clone Behavior +You can optionally change the clone behavior of the request. The clone behavior controls how the entity is duplicated on the server. + +Use `overwrites` to replace values in the cloned entity before it is written, for example to set a different name or other field values on the copy. + +Use `cloneChildren` to control whether child entities are cloned as well. This value defaults to `true`. + +```ts +const exampleCloneBehavior = { + // Replace values in the cloned entity before it is saved + overwrites: { + name: 'Copy of the original entity', + active: false + }, + // Set to false if child entities should not be cloned + cloneChildren: true +} +``` diff --git a/docs/admin-sdk/api-reference/data/subscribe.md b/docs/admin-sdk/api-reference/data/subscribe.md index 716d55a93..632e41129 100644 --- a/docs/admin-sdk/api-reference/data/subscribe.md +++ b/docs/admin-sdk/api-reference/data/subscribe.md @@ -1,22 +1,49 @@ +--- +title: "Subscribe" +nav: + position: 40 +--- + # Subscribe -With `data.subscribe` you can subscribe to dataset changes. The callback will be called every time, the dataset with the matching id is changed. -More information on how to find the unique identifiers can be found in [this guide](../../internals/datahandling.md). +With `data.subscribe`, you can subscribe to changes in the dataset. +Every time the dataset you subscribed to changes, the callback will be called with the new data. +An individual dataset is referenced by an ID. [The data handling guide](../../concepts/datahandling.md) explains how to find available datasets. + +## data.subscribe() + +`subscribe()` listens for dataset changes and calls your callback every time Shopware publishes a new value for the given dataset id. + +#### Usage -#### Usage: ```ts +import { data } from "@shopware-ag/meteor-admin-sdk"; + data.subscribe( - 'sw-product-detail__product', - ({id, data}) => { - console.log(data); - }, - { - selectors: ['name', 'manufacturer.name'] - }, + "sw-product-detail__product", + ({ id, data }) => { + console.log(data); + }, + { + selectors: ["name", "manufacturer.name"], + }, ); ``` -#### Output: +#### Parameters + +| Name | Required | Description | +| :--------- | :------- | :---------------------------------------------------------------------------------------------------- | +| `id` | true | The unique id of the dataset you want to receive | +| `callback` | true | A callback function which will be called every time the Shopware Administration publishes the dataset | +| `options` | false | Allows to specify `selectors`. Read more about selectors [here](../../concepts/selectors.md) | + +#### Return value + +Returns an unsubscribe function. Call it to stop listening for dataset updates. + +For example: + ```json { "name": "Ergonomic Copper Mr. Frenzy", @@ -25,10 +52,3 @@ data.subscribe( } } ``` - -#### Parameters -| Name | Required | Description | -| :---------- | :------- |:------------------------------------------------------------------------------------------------------| -| `id` | true | The unique id of the dataset you want to receive | -| `callback` | true | A callback function which will be called every time the Shopware Administration publishes the dataset | -| `options` | false | Allows to specify `selectors`. Read more about selectors [here](../../concepts/selectors.md) | diff --git a/docs/admin-sdk/api-reference/data/update.md b/docs/admin-sdk/api-reference/data/update.md index 56181c035..70d9b489f 100644 --- a/docs/admin-sdk/api-reference/data/update.md +++ b/docs/admin-sdk/api-reference/data/update.md @@ -1,21 +1,40 @@ +--- +title: "Update" +nav: + position: 50 +--- + # Update -With `data.update` you can update datasets from the Shopware administration. -More information on how to find the unique identifiers can be found in [this guide](../../internals/datahandling.md). +With `data.update` you can update datasets from the Shopware Administration. [The data handling guide](../../concepts/datahandling.md) explains how to find available datasets. + +## data.update() + +`update()` sends new data for a registered dataset to the Shopware Administration. + +#### Usage -#### Usage: ```ts -data.update({ - id: 'sw-product-detail__product', +import { data } from "@shopware-ag/meteor-admin-sdk"; + +data + .update({ + id: "sw-product-detail__product", data: { - name: 'My updated name', + name: "My updated name", }, -}).then(() => { - console.log('success'); -}); + }) + .then(() => { + console.log("success"); + }); ``` #### Parameters + | Name | Required | Description | | :-------- | :------- | :------------------------------------------------- | | `options` | true | An object containing the id and the data to update | + +#### Return value + +Returns a promise without data. diff --git a/docs/admin-sdk/api-reference/in-app-purchases/in-app-purchases.md b/docs/admin-sdk/api-reference/in-app-purchases/in-app-purchases.md deleted file mode 100644 index c54329029..000000000 --- a/docs/admin-sdk/api-reference/in-app-purchases/in-app-purchases.md +++ /dev/null @@ -1,19 +0,0 @@ -# In-App Purchase Flow - -> Available since Shopware v6.6.9.0 -> -In-App purchases allow you to create different functionality based on purchases the user has made in your app. This guide will show you how to start the in-app purchase flow. - -### Opening modal with details of feature - -To open a modal with the details of the feature you want to purchase, you can use the following code: - -```ts -sw.iap.purchase({ - identifier: 'your-in-app-purchase-id', -}); -``` - -This will create a modal in admin which takes the user through the checkout flow in which the app will be purchased or subscribed to. - -Once the purchase has been completed, the amount will be added to the bill of the merchant, and the feature will be unlocked. diff --git a/docs/admin-sdk/api-reference/in-app-purchases/index.md b/docs/admin-sdk/api-reference/in-app-purchases/index.md deleted file mode 100644 index 0763ccc69..000000000 --- a/docs/admin-sdk/api-reference/in-app-purchases/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: "In App Purchases" -nav: - position: 350 ---- diff --git a/docs/admin-sdk/api-reference/index.md b/docs/admin-sdk/api-reference/index.md index 9e859ad9b..618e5674c 100644 --- a/docs/admin-sdk/api-reference/index.md +++ b/docs/admin-sdk/api-reference/index.md @@ -1,5 +1,91 @@ --- title: "API Reference" nav: - position: 50 + position: 40 --- + +# API Reference + +The Meteor Admin SDK provides APIs for extending the Shopware Administration UI and interacting with Administration data. + +These APIs allow extensions to add user interface elements, access and modify entity data, register CMS blocks and elements, and share state between components. + +Use the sections below to navigate the available APIs. + +## Extending the UI + +These components allow extensions to add UI elements to the Shopware Administration. They can be used to register modules, add navigation entries, extend existing pages, and display dialogs or side panels. + +Typical use cases include: + +- Creating custom modules in the Administration +- Extending entity detail pages +- Adding navigation or settings entries +- Displaying modals or contextual UI elements + +UI extension components in this section include: + +- [Main Module](./ui/mainModule.md) +- [Menu](./ui/menu.md) +- [Settings Item](./ui/settingsItem.md) +- [Action Button](./ui/actionButton.md) +- [Notification](./ui/notification.md) +- [Toast](./ui/toast.md) +- [Tabs](./ui/tabs.md) +- [Component Sections](./ui/component-sections.md) +- [Sidebars](./ui/sidebars.md) +- [Modals](./ui/modals.md) +- [Media Modal](./ui/mediaModal.md) + +**Purchases and payments** + +- [In-App Purchases](./ui/purchases-and-payments/in-app-purchases.md): Start an in-app purchase flow +- [Payment Overview Card](./ui/purchases-and-payments/paymentOverviewCard.md): Customize payment method cards + +## Working with Data + +These tools allow extensions to access and manipulate Shopware entity data from within the Administration. + +They follow the same repository-based data access pattern used internally by the Administration. + +Typical workflows include: + +- Accessing entity repositories +- Retrieving entity data +- Subscribing to changes +- Updating or persisting entities + +Tools this section include: + +- [Repository](./data/repository.md): Access Shopware entity repositories +- [Get](./data/get.md) +- [Subscribe](./data/subscribe.md) +- [Update](./data/update.md) + +## Composable APIs + +Composable APIs provide reusable helpers for working with the Administration state and data layer inside extensions. + +They simplify common patterns such as accessing repositories or sharing state between components. + +APIs in this section include: + +- [useRepository](./composables/useRepository.md) +- [useSharedState](./composables/useSharedState.md) + +## Extending the CMS + +These APIs allow extensions to add new CMS blocks and elements to the Shopware Shopping Experiences editor. + +They can be used to introduce custom content components that merchants can use when building storefront pages. + +APIs in this section include: + +- [Register CMS Block](./cms/registerCmsBlock.md) +- [Register CMS Element](./cms/registerCmsElement.md) + +## Shared Options + +Some SDK APIs support shared configuration options that control how actions are executed in the Administration. + +- [Base Options](./base-options.md) diff --git a/docs/admin-sdk/api-reference/location.md b/docs/admin-sdk/api-reference/location.md index 70cc5bb91..5ac20bd28 100644 --- a/docs/admin-sdk/api-reference/location.md +++ b/docs/admin-sdk/api-reference/location.md @@ -1,162 +1,200 @@ +--- +title: "Location" +sidebar_position: 30 +--- + # Location +Locations define where extension code is executed inside the Shopware Administration. + +Each location represents a specific UI context (for example a tab, modal, sidebar, or hidden entry point). Extensions typically check the current location before deciding which UI elements to register or which view to render. + +```ts +import { location } from "@shopware-ag/meteor-admin-sdk"; +``` + ## Prerequisites -We recommend you read the [concept](../concepts/locations.md) of locations first. -## Location checks -### Check the current location id +See [Locations](../concepts/locations.md) for a full explanation of the concept. + +## is() -Check if the current location matches the given location Id. +Check whether the current location matches the given location ID. Use this to decide which view to render or which extension logic to run for the active iframe. -#### Usage: +#### Usage ```ts -if (sw.location.is('my-location-id')) { - // Render view for location +if (location.is("my-location-id")) { + // Render view for location } ``` -#### Parameters: +#### Parameters + | Name | Required | Default | Description | | :----------- | :------- | :------ | :----------------------- | -| `locationId` | true | | The location Id to check | +| `locationId` | true | | The location ID to check | -#### Return value: -Returns a boolean. It is `true` if the location Id matches the current location. +#### Return value -### Get the current location id +Returns a boolean. It is `true` if the location ID matches the current location. -Get the name of the current location ID +## get() -#### Usage: +Get the current location ID. + +#### Usage ```ts -const currentLocation = sw.location.get() +const currentLocation = location.get(); ``` -#### Return value: +#### Return value + Returns a string with the name of the current location. -### Check if current location is inside iFrame +## isIframe() -Useful for hybrid extensions which are using plugin and Extension SDK functionalities together (Shopware 6.6 and lower). You can use this -check to separate code which should be executed inside the Extension SDK context and the plugin context. +Check whether the current code runs inside an SDK iframe. This is mainly useful for hybrid extensions that combine plugin logic with Extension SDK logic, especially in Shopware 6.6 and lower. -#### Usage: +#### Usage ```ts if (location.isIframe()) { - // Execute the code which uses the meteor-admin-sdk context - import('./extension-code'); + // Execute the code which uses the meteor-admin-sdk context + import("./extension-code"); } else { - // Execute the plugin code - import('./plugin-code'); + // Execute the plugin code + import("./plugin-code"); } ``` -## iFrame Heights +#### Parameters -#### Parameters: No parameters needed. -#### Return value: -Returns a boolean. If it is executed inside a iFrame it returns `true`. +#### Return value -### Update the height of the location iFrame +Returns `true` if the current code is executed inside an iframe. Otherwise returns `false`. -You can update the height of the iFrame with this method. +## updateHeight() -#### Usage: +Update the height of the current location iframe. If no value is provided, the height is calculated automatically from the current content. + +#### Usage ```ts -sw.location.updateHeight(750); +location.updateHeight(750); ``` -#### Parameters: +#### Parameters + | Name | Required | Default | Description | | :-------------- | :------- | :------------- | :------------------------------------------------------------------------------------------------------------- | -| `iFrame height` | false | Auto generated | The height of the iFrame. If no value is provided it will be automatically calculated from the current height. | +| `height` | false | Auto generated | The height of the iframe. If no value is provided, it is calculated automatically from the current content height. | + +#### Return value -#### Return value: This method does not have a return value. -### Start auto resizing of the iFrame height +## startAutoResizer() -This methods starts the auto resizer of the iFrame height. +Start automatically resizing the current location iframe whenever the content height changes. ![Auto resizing example](../concepts/assets/auto-resizer.gif) -#### Usage: +#### Usage ```ts -sw.location.startAutoResizer(); +location.startAutoResizer(); ``` -#### Parameters: +#### Parameters + No parameters needed. -#### Return value: +#### Return value + This method does not have a return value. -### Stop auto resizing of the iFrame height +## stopAutoResizer() -This methods stops the auto resizer of the iFrame height. +Stop the automatic iframe height updates started by `location.startAutoResizer()`. -#### Usage: +#### Usage ```ts -sw.location.stopAutoResizer(); +location.stopAutoResizer(); ``` -#### Parameters: -No parameters needed. +#### Parameters -#### Return value: -This method does not have a return value. +No parameters needed. -## URL changes inside your app +#### Return value -:::caution -Do not use this feature yet. It is not implemented in a Shopware release yet. -::: +This method does not have a return value. -Important: You can track and emit your URL changes only inside your own main module or settings page. +## updateUrl() -### Update URL +> Available since Shopware v6.6.8.0 -Send the current URL of your iFrame to the administration. When the user reloads the whole page your iFrame will get the -last page you sent to the administration. +Send the current iframe URL to the Administration. This only applies inside your own main module or settings page. When the user reloads the whole page, the iframe can be restored to the last URL you sent. -#### Usage: +#### Usage ```ts const currentUrl = window.location.href; -sw.location.updateUrl(new URL(currentUrl)) +location.updateUrl(new URL(currentUrl)); ``` -#### Parameters: -| Name | Required | Default | Description | -| :-------------- | :------- | :------ | :------------------------------------ | -| First parameter | true | | An URL object which contains your URL | +#### Parameters -### Start automatic URL updates +| Name | Required | Default | Description | +| :---- | :------- | :------ | :--------------------------- | +| `url` | true | | A `URL` object with your URL | -To avoid manually sending URL changes you can use this helper methods. It sends automatically changes in your URL to the -administration. +#### Return value -#### Usage: +This method does not have a return value. + +## startAutoUrlUpdater() + +> Available since Shopware v6.6.8.0 + +Start automatically sending URL changes from your iframe to the Administration. This only applies inside your own main module or settings page. + +#### Usage ```ts -sw.location.startAutoUrlUpdater(); +location.startAutoUrlUpdater(); ``` -### Stop automatic URL updates +#### Parameters + +No parameters needed. + +#### Return value + +This method does not have a return value. -If you had started an automatic URL updater before then you can stop it by calling this method. +## stopAutoUrlUpdater() -#### Usage: +> Available since Shopware v6.6.8.0 + +Stop the automatic URL updates started by `location.startAutoUrlUpdater()`. + +#### Usage ```ts -sw.location.stopAutoUrlUpdater(); -``` \ No newline at end of file +location.stopAutoUrlUpdater(); +``` + +#### Parameters + +No parameters needed. + +#### Return value + +This method does not have a return value. diff --git a/docs/admin-sdk/api-reference/toast.md b/docs/admin-sdk/api-reference/toast.md deleted file mode 100644 index 8778a393e..000000000 --- a/docs/admin-sdk/api-reference/toast.md +++ /dev/null @@ -1,37 +0,0 @@ -# Toast - -## Availability -This feature will be available with Shopware 6.6.2.0. - -### Dispatch a toast - -![toast example](./assets/toast-example.png) - -#### Usage: -```ts -function alertYes() { - alert('Yes'); -} - -sw.toast.dispatch({ - msg: 'Your message', - dismissible: true, - type: 'positive', - action: { - label: 'action', - callback: alertYes - }, -}) -``` - -#### Parameters: -| Name | Required | Default | Description | -|:--------------|:---------|:--------|:---------------------------------------------------------------------------------------------------------------| -| `msg` | true | | Defines a toast's main expression or message to the user. | -| `type` | true | | Defines the toast type. Available `types` are `positive`, `informal` and `critical`. | -| `icon ` | false | None | A icon that should be displayed in front of your message. | -| `dismissible` | false | `false` | Specifies if the toast can be manually dismmissed. | -| `action` | false | None | Adds a clickable button to the toast. The button receives a label and a callback wichs is called once clicked. | - -#### Return value: -Returns a promise without data. diff --git a/docs/admin-sdk/api-reference/ui/actionButton.md b/docs/admin-sdk/api-reference/ui/actionButton.md index 37bec482e..5e9700664 100644 --- a/docs/admin-sdk/api-reference/ui/actionButton.md +++ b/docs/admin-sdk/api-reference/ui/actionButton.md @@ -1,93 +1,116 @@ -# Action button +--- +title: "Action Button" +nav: + position: 50 +--- + +# Action Button + +An action button adds a clickable button to an existing area of the Shopware Administration. + +Action buttons are typically used to trigger extension-specific actions such as opening a modal, executing a workflow, or navigating to an extension module. + +## actionButton.add() + +#### Usage -#### Usage: ```ts -import { location, ui } from '@shopware-ag/meteor-admin-sdk'; - -if (location.is(sw.location.MAIN_HIDDEN)) { - ui.actionButton.add({ - action: 'your-app_customer-detail-action', - entity: 'customer', - view: 'detail', - label: 'Test action', - callback: (entity, entityIds) => { - // TODO: do something - }, - }); +import { location, ui } from "@shopware-ag/meteor-admin-sdk"; + +if (location.is(location.MAIN_HIDDEN)) { + ui.actionButton.add({ + name: "your-app_customer-detail-action", + entity: "customer", + view: "detail", + label: "Test action", + callback: (entity, entityIds) => { + // TODO: do something + }, + }); } ``` #### Parameters -| Name | Required | Description | -| :------------------- | :------- | :--------------------------------------------------------------------------------------------------------- | -| `action` | true | A unique name of your action | -| `entity` | true | The entity this action is for possible values: `product`, `order`, `category`, `promotion`, `customer` or `media`. Value `media` is available in Shopware version 6.7.1 | -| `view` | true | Determines if the action button appears on the listing or detail page, possible values: `detail`,`list` or item. View `item` is only used for entity `media` and in version 6.7.1 | -| `label` | true | The label of your action button | -| `meteorIcon` | false | Meteor icon before label, will be available in Shopware version 6.7.4.0 . Check icon name on https://developer.shopware.com/resources/meteor-icon-kit/ | -| `fileTypes` | false | Media file types you want the action button to be displayed for. Will be available in Shopware version 6.7.6. | -| `callback` | true | The callback function where you receive the entity and the entityIds for further processing | - -### Calling app actions + +| Name | Required | Description | +| :----------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | true | A unique identifier for your action | +| `entity` | true | The entity this action is for possible values: `product`, `order`, `category`, `promotion`, `customer` or `media`. Value `media` is available in Shopware version 6.7.1 | +| `view` | true | Determines if the action button appears on the listing or detail page, possible values: `detail`,`list` or item. View `item` is only used for entity `media` and in version 6.7.1 | +| `label` | true | The label of your action button | +| `meteorIcon` | false | Meteor icon before label. Available since Shopware v6.7.4.0. Check icon name on https://developer.shopware.com/resources/meteor-icon-kit/ | +| `fileTypes` | false | Media file types you want the action button to be displayed for. Available since Shopware v6.7.6.0. | +| `callback` | true | The callback function where you receive the entity and the entityIds for further processing | + +#### Return value + +Returns a promise without data. + +## Calling app actions + As an app developer you may want to receive the information of the callback function server side. The following example will render the same action button as the above example but once it gets clicked you will receive a POST request to your app server. -**This will only work for apps. Plugin developers need to use a api client directly in there callback.**. + +**This will only work for apps. Plugin developers must use an API client directly in their callback.**. ```ts -import { location, ui } from '@shopware-ag/meteor-admin-sdk'; - -if (location.is(sw.location.MAIN_HIDDEN)) { - ui.actionButton.add({ - action: 'your-app_customer-detail-action', - entity: 'customer', - view: 'detail', - label: 'Test action', - callback: (entity /* "customer" */, entityIds /* ["..."] */) => { - app.webhook.actionExecute({ - url: 'http://your-app.com/customer-detail-action', - entityIds, - entity, - }) - }, - }); +import { location, ui } from "@shopware-ag/meteor-admin-sdk"; + +if (location.is(location.MAIN_HIDDEN)) { + ui.actionButton.add({ + name: "your-app_customer-detail-action", + entity: "customer", + view: "detail", + label: "Test action", + callback: (entity /* "customer" */, entityIds /* ["..."] */) => { + app.webhook.actionExecute({ + url: "http://your-app.com/customer-detail-action", + entityIds, + entity, + }); + }, + }); } ``` -#### Example -- Add action button in customer detail page +## Example: Add action button in customer detail page ![Action button example](./assets/add-action-button-example.png) ```ts +import { ui } from "@shopware-ag/meteor-admin-sdk"; + ui.actionButton.add({ - action: 'your-app_customer-detail-action', - entity: 'customer', - view: 'detail', - meteorIcon: 'regular-analytics', - label: 'Test action', - callback: (entity /* "customer" */, entityIds /* ["..."] */) => { - app.webhook.actionExecute({ - url: 'http://your-app.com/customer-detail-action', - entityIds, - entity, - }) - }, + name: "your-app_customer-detail-action", + entity: "customer", + view: "detail", + meteorIcon: "regular-analytics", + label: "Test action", + callback: (entity /* "customer" */, entityIds /* ["..."] */) => { + app.webhook.actionExecute({ + url: "http://your-app.com/customer-detail-action", + entityIds, + entity, + }); + }, }); ``` -- Add action button in media item +## Example: Add action button in media item ![Action button media example](./assets/add-action-button-media-example.png) ```ts +import { ui } from "@shopware-ag/meteor-admin-sdk"; + ui.actionButton.add({ - action: 'test-media-button', - entity: 'media', - view: 'item', - meteorIcon: 'regular-tools-alt', - label: 'Open in Image editor', - callback: (entity /* "media" */, entityIds /* ["..."] */) => { - // TODO: Navigate to image editor app - }, + name: "test-media-button", + entity: "media", + view: "item", + meteorIcon: "regular-tools-alt", + label: "Open in Image editor", + callback: (entity /* "media" */, entityIds /* ["..."] */) => { + // TODO: Navigate to image editor app + }, }); -``` \ No newline at end of file +``` diff --git a/docs/admin-sdk/api-reference/assets/sidebar-example.png b/docs/admin-sdk/api-reference/ui/assets/sidebar-example.png similarity index 100% rename from docs/admin-sdk/api-reference/assets/sidebar-example.png rename to docs/admin-sdk/api-reference/ui/assets/sidebar-example.png diff --git a/docs/admin-sdk/api-reference/ui/component-section.md b/docs/admin-sdk/api-reference/ui/component-section.md deleted file mode 100644 index 380d1d2c8..000000000 --- a/docs/admin-sdk/api-reference/ui/component-section.md +++ /dev/null @@ -1,88 +0,0 @@ -# Component Section - -## Add -Add a new component to a component section. - -### General usage - -#### Usage: -```ts -import { ui } from '@shopware-ag/meteor-admin-sdk'; - -ui.componentSection.add({ - component: 'the-component', // Choose the component which you want to render at the component section - positionId: 'the-position-id-of-the-component-section', // Select the positionId where you want to render the component - props: { - ... // The properties are depending on the component - } -}) -``` - -#### Parameters -| Name | Required | Default | Description | -| :---------- | :------- | :------ | :--------------------------------------------- | -| `component` | true | | Choose the component which you want to render. | - -#### Return value: -This method does not have a return value. - -## Available components - -### Card - -#### Properties: -| Name | Required | Default | Description | -|:-------------|:---------|:--------|:-----------------------------------| -| `title` | false | | The main title of the card | -| `subtitle` | false | | The subtitle of the card | -| `locationId` | true | | The locationId for the custom view | -| `tabs` | false | | Render different content with tabs | - -#### Usage: -```js -import { ui } from '@shopware-ag/meteor-admin-sdk'; - -ui.componentSection.add({ - component: 'card', - positionId: 'sw-product-properties__before', - props: { - title: 'Hello from plugin', - subtitle: 'I am before the properties card', - locationId: 'my-awesome-app-card-before' - } -}) -``` - -#### Example -![Card component example](./assets/example-card.png) - -#### With tabs: -```js -import { ui } from '@shopware-ag/meteor-admin-sdk'; - -ui.componentSection.add({ - component: 'card', - positionId: 'sw-product-properties__before', - props: { - title: 'Hello from plugin', - subtitle: 'I am before the properties card', - locationId: 'my-awesome-app-card-before', - // Render tabs and custom tab content with the provided location id - tabs: [ - { - name: 'example-tab-1', - label: 'First tab', - locationId: 'example-tab-1' - }, - { - name: 'example-tab', - label: 'Second tab', - locationId: 'example-tab-2' - } - ], - } -}) -``` - -#### Example -![Card component with tabs example](./assets/example-card-with-tabs.png) diff --git a/docs/admin-sdk/api-reference/ui/component-sections.md b/docs/admin-sdk/api-reference/ui/component-sections.md new file mode 100644 index 000000000..393ebd4c4 --- /dev/null +++ b/docs/admin-sdk/api-reference/ui/component-sections.md @@ -0,0 +1,115 @@ +--- +title: "Component Sections" +nav: + position: 70 +--- + +# Component Sections + +Component sections allow extensions to render UI components inside existing Administration views. They are typically used together with tabs or other extension points that expose a `positionId`. + +See the [Component Sections concept](../../concepts/component-sections.md) for an overview. + +## componentSection.add() + +Add a new component to a component section. + +#### Usage + +```ts +import { ui } from '@shopware-ag/meteor-admin-sdk'; + +ui.componentSection.add({ + component: 'the-component', // Choose the component which you want to render at the component section + positionId: 'the-position-id-of-the-component-section', // Select the positionId where you want to render the component + props: { + ... // The properties are depending on the component + } +}) +``` + +#### Parameters + +| Name | Required | Default | Description | +| :---------- | :------- | :------ | :--------------------------------------------- | +| `component` | true | | Choose the component which you want to render. | + +#### Return value + +Returns a promise without data. + +## Available components + +### Card + +##### Properties + +| Name | Required | Default | Description | +| :----------- | :------- | :------ | :--------------------------------- | +| `title` | false | | The main title of the card | +| `subtitle` | false | | The subtitle of the card | +| `locationId` | true | | The locationId for the custom view | +| `tabs` | false | | Render different content with tabs | + +#### Example: Add a component to the product page + +```js +import { ui } from "@shopware-ag/meteor-admin-sdk"; + +ui.componentSection.add({ + component: "card", + positionId: "sw-product-properties__before", + props: { + title: "Hello from plugin", + subtitle: "I am before the properties card", + locationId: "my-awesome-app-card-before", + }, +}); +``` + +![Card component example](./assets/example-card.png) + +#### Example: Add tabs to the card + +```js +import { ui } from "@shopware-ag/meteor-admin-sdk"; + +ui.componentSection.add({ + component: "card", + positionId: "sw-product-properties__before", + props: { + title: "Hello from plugin", + subtitle: "I am before the properties card", + locationId: "my-awesome-app-card-before", + // Render tabs and custom tab content with the provided location id + tabs: [ + { + name: "example-tab-1", + label: "First tab", + locationId: "example-tab-1", + }, + { + name: "example-tab-2", + label: "Second tab", + locationId: "example-tab-2", + }, + ], + }, +}); +``` + +To render the tabs introduced in this example, add matching entry points in your extension code using the `locationId` values that you freely chose when registering the component section. Read more about this pattern in [Locations](../../concepts/locations.md). + +```js +import { location } from "@shopware-ag/meteor-admin-sdk"; + +if (location.is("example-tab-1")) { + document.body.innerHTML = "

First tab content

"; +} + +if (location.is("example-tab-2")) { + document.body.innerHTML = "

Second tab content

"; +} +``` + +![Card component with tabs example](./assets/example-card-with-tabs.png) diff --git a/docs/admin-sdk/api-reference/ui/index.md b/docs/admin-sdk/api-reference/ui/index.md index aa43b9db2..e9e24a39c 100644 --- a/docs/admin-sdk/api-reference/ui/index.md +++ b/docs/admin-sdk/api-reference/ui/index.md @@ -1,5 +1,33 @@ --- -title: "UI" +title: "Extending the UI" nav: position: 100 ---- \ No newline at end of file +--- + +# Extending the Administration UI + +The Meteor Admin SDK allows extensions to add UI elements to the Shopware Administration. + +These APIs let you integrate custom functionality into existing areas of the Administration, such as navigation menus, action buttons, settings pages, or custom modules. + +The following guides cover common UI extension patterns. + +## Adding new pages and navigation + +- [Main Module](./mainModule.md): Add a dedicated app area in the Administration +- [Menu](./menu.md): Add navigation entries to the sidebar +- [Settings Item](./settingsItem.md): Place configuration inside the Administration settings + +## Extending existing views + +- [Tabs](./tabs.md): Add tabs to entity detail pages such as products, customers, or orders +- [Component Sections](./component-sections.md): Inject custom components into extension points +- [Sidebars](./sidebars.md): Display additional contextual information + +## Actions and dialogs + +- [Action Button](./actionButton.md): Trigger extension functionality from existing pages +- [Notification](./notification.md): Show persistent feedback in the notification center +- [Toast](./toast.md): Show short, temporary feedback messages +- [Modals](./modals.md): Confirmations, forms, or multi-step workflows +- [Media Modal](./mediaModal.md): Select or manage media assets diff --git a/docs/admin-sdk/api-reference/ui/mainModule.md b/docs/admin-sdk/api-reference/ui/mainModule.md index a0e84e91d..b6e33a37c 100644 --- a/docs/admin-sdk/api-reference/ui/mainModule.md +++ b/docs/admin-sdk/api-reference/ui/mainModule.md @@ -1,18 +1,34 @@ -# Main module +--- +title: "Main Module" +nav: + position: 20 +--- -### Add main module -Add a main module to your extension. The content of the main module is determined by your `locationId`. -A specific view or a set of actions can be triggered based on the `locationId`. +# Main Module + +A main module registers a dedicated page for your extension inside the Administration. + +You reach it from `Extensions > My Extensions` via the `Configure` button of your extension. Use a main module when your extension provides its own application area with dedicated pages and functionality. + +```ts +import { ui } from "@shopware-ag/meteor-admin-sdk"; +``` + +## addMainModule() + +Add a main module to your extension. The content of the main module is determined by your `locationId`. A specific view or set of actions can be triggered based on the `locationId`. + +#### Usage -#### Usage: ```ts ui.mainModule.addMainModule({ - heading: 'My App', - locationId: 'main-location-id', + heading: "My App", + locationId: "main-location-id", }); ``` #### Parameters + | Name | Required | Default | Description | | :---------------------- | :------- | :------ | :------------------------------------- | | `heading` | true | | The heading displayed in your module | @@ -20,54 +36,64 @@ ui.mainModule.addMainModule({ | `displaySearchBar` | false | true | Toggles the sw-page search bar on/off | | `displayLanguageSwitch` | false | false | Toggles sw-page language switch on/off | +#### Return value + +Returns a promise without data. + #### Example + ![Main module example](./assets/add-main-module-example.png) + ```ts -import { location, ui } from '@shopware-ag/meteor-admin-sdk'; +import { location, ui } from "@shopware-ag/meteor-admin-sdk"; // General commands if (location.is(location.MAIN_HIDDEN)) { - // Add the main module - ui.mainModule.addMainModule({ - heading: 'My App', - locationId: 'main-location-id', - }); + // Add the main module + ui.mainModule.addMainModule({ + heading: "My App", + locationId: "main-location-id", + }); // If you want to provide some buttons for the smart bar of your main module ui.mainModule.addSmartbarButton({ - locationId: 'main-location-id', // locationId of your main module - buttonId: 'test-button', // The button id - label: 'Click me', // The button label - variant: 'primary', // The button variant - onClickCallback: () => {} + locationId: "main-location-id", // locationId of your main module + buttonId: "test-button", // The button id + label: "Click me", // The button label + variant: "primary", // The button variant + onClickCallback: () => {}, }); - ui.mainModule.hideSmartBar({ - locationId: 'main-location-id', - }); + ui.mainModule.hideSmartBar({ + locationId: "main-location-id", + }); } // Render your custom view -if (location.is('main-location-id')) { - document.body.innerHTML = '

Hello from your main module

'; +if (location.is("main-location-id")) { + document.body.innerHTML = + '

Hello from your main module

'; } ``` -### Add smart bar button to main module +## addSmartBarButton() + Add a button to the smart bar of your main module. The button can be used to trigger actions, e.g. saving, cancel, etc. The location ID needs to be defined and have the same value as the `locationId` of the main module. -#### Usage: +#### Usage + ```ts -ui.mainModule.addSmartbarButton({ - locationId: 'main-location-id', // locationId of your main module - buttonId: 'test-button', // The button id - label: 'Click me', // The button label - variant: 'primary', // The button variant - onClickCallback: () => {} +ui.mainModule.addSmartBarButton({ + locationId: "main-location-id", // locationId of your main module + buttonId: "test-button", // The button id + label: "Click me", // The button label + variant: "primary", // The button variant + onClickCallback: () => {}, }); ``` #### Parameters + | Name | Required | Default | Description | | :---------------- | :------- | :-------- | :------------------------------------------------------------------------------------------------------------------ | | `locationId` | true | | The locationId of the module you want to display the smart bar button | @@ -77,17 +103,28 @@ ui.mainModule.addSmartbarButton({ | `onClickCallback` | true | | Callback function which will be called once the button is clicked | | `disabled` | false | false | Toggle disabled state of the button | -### Hide smart bar +#### Return value + +Returns a promise without data. + +## hideSmartBar() + Turn the smart bar off as needed. -#### Usage: +#### Usage + ```ts ui.mainModule.hideSmartBar({ - locationId: 'main-location-id', + locationId: "main-location-id", }); ``` #### Parameters -| Name | Required | Default | Description | Available at Shopware | -| :----------- | :------- | :-------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------| -| `locationId` | true | | The locationId of the module you want to hide the smart bar | v6.6.7.0 | + +| Name | Required | Default | Description | Available at Shopware | +| :----------- | :------- | :------ | :---------------------------------------------------------- | :-------------------- | +| `locationId` | true | | The locationId of the module you want to hide the smart bar | v6.6.7.0 | + +#### Return value + +Returns a promise without data. diff --git a/docs/admin-sdk/api-reference/ui/mediaModal.md b/docs/admin-sdk/api-reference/ui/mediaModal.md index 2b3ae72dc..96c22f8f8 100644 --- a/docs/admin-sdk/api-reference/ui/mediaModal.md +++ b/docs/admin-sdk/api-reference/ui/mediaModal.md @@ -1,19 +1,25 @@ -# Media modal +--- +title: "Media Modals" +nav: + position: 100 +--- -This method allows an app to interact with the Administration's media modal, which includes the Media modal and the Save media modal. +# Media Modals -Functionality of each modal: -- The Media modal is used for selecting existing media from the media library or uploading new media. This functionality has been available since version 6.7.1. +This method allows apps to interact with the Administration's media modal. Two modal types are supported: -- The Save media modal is used to choose a specific location to save the media, and this feature will be implemented in version 6.7.5. +- **Media modal**: Select existing media from the media library or upload new files. Available since Shopware 6.7.1. +- **Save media modal**: Choose a specific folder and filename when saving media. Available since Shopware 6.7.5. -## Media modal +```ts +import { ui } from "@shopware-ag/meteor-admin-sdk"; +``` -### Open modal +## mediaModal.open() -Open media modal in the current view. +Open the media modal in the current view. -#### Usage: +#### Usage ```ts ui.mediaModal.open({ @@ -21,13 +27,13 @@ ui.mediaModal.open({ allowMultiSelect: false, fileAccept: "image/png", selectors: ["fileName", "id", "url"], - callback: ({ fileName, id, url }) => {}, + callback: ([{ fileName, id, url }]) => {}, }); ``` #### Parameters -All parameters are similar to `sw-media-modal-v2` component's props +All parameters are similar to the `sw-media-modal-v2` component's props. | Name | Required | Default | Description | | :----------------- | :------- | :------------------------ | :----------------------------------------------------------------------------------- | @@ -36,8 +42,8 @@ All parameters are similar to `sw-media-modal-v2` component's props | `allowMultiSelect` | false | true | Define single or multiple selection | | `defaultTab` | false | library | Defines which tab should be opened by default | | `fileAccept` | false | image/\* | Define the file types which are allowed to be uploaded in Upload tab | -| `selectors` | false | ['fileName', 'id', 'url'] | Selected properties which should be returned in callback function | -| `callback` | true | | Callback function which will be called once the media item is selected. | +| `selectors` | false | ['fileName', 'id', 'url'] | Selected properties which should be returned for each item in the callback array | +| `callback` | true | | Callback function which receives an array of selected media items once a selection is made. | #### Example @@ -48,17 +54,15 @@ ui.mediaModal.open({ initialFolderId: "productMediaFolderId", allowMultiSelect: false, selectors: ["fileName", "id", "url"], - callback: ({ fileName, id, url }) => {}, + callback: ([{ fileName, id, url }]) => {}, }); ``` -## Save media modal - -### Open save media modal +## mediaModal.openSaveMedia() Open save media modal in the current view. -#### Usage: +#### Usage ```ts ui.mediaModal.openSaveMedia({ @@ -71,14 +75,14 @@ ui.mediaModal.openSaveMedia({ #### Parameters -All parameters are similar to `sw-media-save-modal` component's props +All parameters are similar to the `sw-media-save-modal` component's props. -| Name | Required | Default | Description | -| :----------------- | :------- | :------------------------ | :----------------------------------------------------------------------------------- | -| `initialFolderId` | false | null | Initial folder id where the media modal will open | -| `initialFileName` | false | null | Initial file name of media to set as initial value of file name input | -| `fileType` | false | null | File extension of media to display on file name input's suffix | -| `callback` | true | | This callback function is triggered when the "Save media" button is clicked. It returns the updated file name and the folderId where the media is stored. | +| Name | Required | Default | Description | +| :---------------- | :------- | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `initialFolderId` | false | null | Initial folder id where the media modal will open | +| `initialFileName` | false | null | Initial file name of media to set as initial value of file name input | +| `fileType` | false | null | File extension of media to display on file name input's suffix | +| `callback` | true | | This callback function is triggered when the "Save media" button is clicked. It returns the updated file name and the folderId where the media is stored. | #### Example diff --git a/docs/admin-sdk/api-reference/ui/menu.md b/docs/admin-sdk/api-reference/ui/menu.md index a28a6df7f..28739760a 100644 --- a/docs/admin-sdk/api-reference/ui/menu.md +++ b/docs/admin-sdk/api-reference/ui/menu.md @@ -1,62 +1,113 @@ +--- +title: "Menu" +nav: + position: 30 +--- + # Menu -### Toggle menu +Menu items allow extensions to add navigation entries to existing areas of the Shopware Administration menu. + +They are typically used to expose extension functionality inside existing admin modules. In practice, this refers to the left sidebar navigation of the Administration. + +```ts +import { ui } from "@shopware-ag/meteor-admin-sdk"; +``` + +## collapseMenu() > Available since Shopware v6.6.2.0 -The Admin SDK allows you to manipulate the Admin menu of your application. One of the features it provides is the ability to toggle the Admin menu. This is done using the `collapseMenu` and `expandMenu` methods. +Collapse the Administration menu. + +#### Usage -#### Usage: ```ts -ui.menu.collapseMenu(); // To collapse the Admin menu; +ui.menu.collapseMenu(); +``` + +#### Parameters + +This method does not accept parameters. + +#### Return value + +Returns a promise without data. + +## expandMenu() + +> Available since Shopware v6.6.2.0 + +Expand the Administration menu again after it has been collapsed. + +#### Usage -ui.menu.expandMenu(); // To expand the Admin menu; +```ts +ui.menu.expandMenu(); ``` -### Add menu item +#### Parameters + +This method does not accept parameters. + +#### Return value + +Returns a promise without data. + +## addMenuItem() + Add a new menu item to the Shopware admin menu. The content of the menu item module is determined by your `locationId`. A specific view or a set of actions can be triggered based on the `locationId`. -#### Usage: +#### Usage + ```ts ui.menu.addMenuItem({ - label: 'Test item', - locationId: 'your-location-id', - displaySearchBar: true, - displaySmartBar: true, - parent: 'sw-catalogue', -}) + label: "Test item", + locationId: "your-location-id", + displaySearchBar: true, + displaySmartBar: true, + parent: "sw-catalogue", +}); ``` #### Parameters -| Name | Required | Default | Description | -| :------------------- | :------- | :------------- | :------------------------------------------------------------ | -| `label` | true | | The label of the tab bar item | -| `locationId` | true | | The id for the content of the menu item module | -| `displaySearchBar` | false | true | Toggles the sw-page search bar on/off | -| `displaySmartBar` | false | true | Toggles the sw-page smart bar on/off | -| `parent` | false | 'sw-extension' | Determines under which main menu entry your item is displayed | -| `position` | false | 110 | Determines the position of your menu item | + +| Name | Required | Default | Description | +| :----------------- | :------- | :------------- | :------------------------------------------------------------ | +| `label` | true | | The label of the tab bar item | +| `locationId` | true | | The id for the content of the menu item module | +| `displaySearchBar` | false | true | Toggles the sw-page search bar on/off | +| `displaySmartBar` | false | true | Toggles the sw-page smart bar on/off | +| `parent` | false | 'sw-extension' | Determines under which main menu entry your item is displayed | +| `position` | false | 110 | Determines the position of your menu item | + +#### Return value + +Returns a promise without data. #### Example + ![Menu item example](./assets/add-menu-item-example.png) + ```ts -import { location, ui } from '@shopware-ag/meteor-admin-sdk'; +import { location, ui } from "@shopware-ag/meteor-admin-sdk"; // General commands -if (location.is(sw.location.MAIN_HIDDEN)) { - // Add the menu item to the catalogue module - ui.menu.addMenuItem({ - label: 'Test item', - displaySearchBar: true, - displaySmartBar: true, - locationId: 'your-location-id', - parent: 'sw-catalogue', - }); +if (location.is(location.MAIN_HIDDEN)) { + // Add the menu item to the catalogue module + ui.menu.addMenuItem({ + label: "Test item", + displaySearchBar: true, + displaySmartBar: true, + locationId: "your-location-id", + parent: "sw-catalogue", + }); } // Render your custom view -if (location.is('your-location-id')) { - document.body.innerHTML = '

Hello from your menu item

'; +if (location.is("your-location-id")) { + document.body.innerHTML = + '

Hello from your menu item

'; } ``` diff --git a/docs/admin-sdk/api-reference/ui/modals.md b/docs/admin-sdk/api-reference/ui/modals.md index c57bc6ae3..edbec8d81 100644 --- a/docs/admin-sdk/api-reference/ui/modals.md +++ b/docs/admin-sdk/api-reference/ui/modals.md @@ -1,51 +1,67 @@ +--- +title: "Modals" +nav: + position: 90 +--- + # Modals -A modal can be displayed in front of all other elements. To return to the main content the user must engage -with the modal by completing an action or by closing it. It should be mainly opened when the user interacts with something. -We recommend that no modal gets opened without context. As an example, it would be bad practice if the user gets logged -in and directly see some modals (e.g. changelogs of extensions) which all need to be closed manually. +Modals display dialog windows in front of the Shopware Administration interface. + +They should be triggered by user interaction and used for confirmations, forms, or multi-step workflows. To return to the main content the user must engage with the modal by completing an action or by closing it. + +Avoid opening modals automatically without context, as this interrupts the user’s workflow. For example, it is poor practice to show modals immediately after login (such as changelogs) that users must manually dismiss. + +See also: [Base Options](../base-options.md) for shared configuration options supported by SDK message APIs. + +```ts +import { notification, ui } from "@shopware-ag/meteor-admin-sdk"; +``` + +## modal.open() -### Open modal Open a new modal in the current view. The content of the modal is determined by your `locationId` or by using plain text with `textContent`. -#### Usage: +#### Usage + ```ts ui.modal.open({ - title: 'Your modal title', - // Use locationId for rendering custom content inside modal - locationId: 'your-location-id', - // Use textContent when no locationId is needed - textContent: 'Do you really want to dispatch a notification?', - variant: 'large', - showHeader: true, - showFooter: false, - closable: true, - buttons: [ - { - label: 'Dispatch notification', - method: () => { - notification.dispatch({ - message: 'Hello from the modal', - title: 'Modal example' - }) - } - }, - { - label: 'Close modal', - variant: 'primary', - method: () => { - ui.modal.close({ - locationId: 'your-location-id' - }) - } - } - ], -}) + title: "Your modal title", + // Use locationId for rendering custom content inside modal + locationId: "your-location-id", + // Use textContent when no locationId is needed + textContent: "Do you really want to dispatch a notification?", + variant: "large", + showHeader: true, + showFooter: false, + closable: true, + buttons: [ + { + label: "Dispatch notification", + method: () => { + notification.dispatch({ + message: "Hello from the modal", + title: "Modal example", + }); + }, + }, + { + label: "Close modal", + variant: "primary", + method: () => { + ui.modal.close({ + locationId: "your-location-id", + }); + }, + }, + ], +}); ``` #### Parameters + | Name | Required | Default | Description | Available at Shopware | -|:--------------|:---------|:----------|:-----------------------------------------------------------------------------------------------|:----------------------| +| :------------ | :------- | :-------- | :--------------------------------------------------------------------------------------------- | :-------------------- | | `title` | true | | The title of the modal | | | `locationId` | false | | The id for the content of the modal. If not provided it will render the `textContent` | | | `textContent` | false | | The plain text content of the modal. Will only be rendered if no `locationId` is given | v.6.7.1 | @@ -56,77 +72,97 @@ ui.modal.open({ | `buttons` | false | [] | This array contains button configurations which will render buttons in the footer of the modal | | #### Example + ![Menu item example](./assets/modal-example.png) + ```ts ui.modal.open({ - title: 'Hello from the plugin', - locationId: 'my-awesome-app-hello-world-modal', - buttons: [ - { - label: 'Dispatch notification', - method: () => { - notification.dispatch({ - message: 'Hello from the modal', - title: 'Modal plugin' - }) - } - }, - { - label: 'Close modal', - variant: 'primary', - method: () => { - ui.modal.close({ - locationId: 'my-awesome-app-hello-world-modal' - }) - } - } - ] -}) + title: "Hello from the plugin", + locationId: "my-awesome-app-hello-world-modal", + buttons: [ + { + label: "Dispatch notification", + method: () => { + notification.dispatch({ + message: "Hello from the modal", + title: "Modal plugin", + }); + }, + }, + { + label: "Close modal", + variant: "primary", + method: () => { + ui.modal.close({ + locationId: "my-awesome-app-hello-world-modal", + }); + }, + }, + ], +}); ``` -### Update modal +#### Return value + +Returns a promise without data. + +## modal.update() + > Available since Shopware 6.7.1.0 Updates an existing modal with the given `locationId`. This can be used to modify the modal's properties after it has been opened, such as changing the title, buttons, or visibility of header/footer from inside the modal. -#### Usage: +#### Usage + ```ts ui.modal.update({ - locationId: 'your-location-id', - title: 'Updated modal title', - showHeader: true, - showFooter: true, - closable: true, - buttons: [ - { - label: 'New button', - method: () => { - // Your method here - } - } - ] -}) + locationId: "your-location-id", + title: "Updated modal title", + showHeader: true, + showFooter: true, + closable: true, + buttons: [ + { + label: "New button", + method: () => { + // Your method here + }, + }, + ], +}); ``` #### Parameters -| Name | Required | Default | Description | -|:-------------|:---------|:--------|:-----------------------------------------------------------------------------------------------| -| `locationId` | true | | The id of the modal which should be updated | -| `title` | false | | The new title of the modal | -| `showHeader` | false | | Enable or disable the header in the modal | -| `showFooter` | false | | Enable or disable the modal footer | -| `closable` | false | | If set to `false` then the modal can only be closed programmatically | -| `buttons` | false | | Array of button configurations which will render buttons in the footer of the modal | - -### Close modal + +| Name | Required | Default | Description | +| :----------- | :------- | :------ | :---------------------------------------------------------------------------------- | +| `locationId` | true | | The id of the modal which should be updated | +| `title` | false | | The new title of the modal | +| `showHeader` | false | | Enable or disable the header in the modal | +| `showFooter` | false | | Enable or disable the modal footer | +| `closable` | false | | If set to `false` then the modal can only be closed programmatically | +| `buttons` | false | | Array of button configurations which will render buttons in the footer of the modal | + +#### Return value + +Returns a promise without data. + +## modal.close() + Closes an opened modal. You need use the correct `locationId` of the modal which should get closed. If you don't provide a `locationId` the last modal without a `locationId` gets closed. -#### Usage: +#### Usage + ```ts -ui.modal.close({ locationId: 'your-location-id' }) +ui.modal.close({ locationId: "your-location-id" }); ``` #### Parameters + | Name | Required | Default | Description | -|:-------------|:---------|:--------|:--------------------------------------------------------------------------------------------------------------------------| +| :----------- | :------- | :------ | :------------------------------------------------------------------------------------------------------------------------ | | `locationId` | false | | The locationId of the modal which should get closed. If not provided, the last modal without a locationId will be closed. | + +#### Return value + +Returns a promise without data. diff --git a/docs/admin-sdk/api-reference/ui/module/index.md b/docs/admin-sdk/api-reference/ui/module/index.md deleted file mode 100644 index e0d98846a..000000000 --- a/docs/admin-sdk/api-reference/ui/module/index.md +++ /dev/null @@ -1,3 +0,0 @@ ---- -title: "Extending modules" ---- diff --git a/docs/admin-sdk/api-reference/notification.md b/docs/admin-sdk/api-reference/ui/notification.md similarity index 64% rename from docs/admin-sdk/api-reference/notification.md rename to docs/admin-sdk/api-reference/ui/notification.md index 1bd4fbb59..d88b50b07 100644 --- a/docs/admin-sdk/api-reference/notification.md +++ b/docs/admin-sdk/api-reference/ui/notification.md @@ -1,44 +1,58 @@ +--- +title: "Notification" +nav: + position: 55 +--- + # Notification -### Dispatch a notification +Notifications display messages in the Shopware Administration to inform users about events, errors, or completed actions. They remain visible in the notification center (bell icon) until dismissed by the user. + +See also: [Base Options](../base-options.md) for shared configuration options supported by SDK message APIs. + +## notification.dispatch() + +![notification example](../assets/notification-example.jpg) -![notification example](./assets/notification-example.jpg) +#### Usage -#### Usage: ```ts +import { notification } from "@shopware-ag/meteor-admin-sdk"; + function alertYes() { - alert('Yes'); + alert("Yes"); } -sw.notification.dispatch({ - title: 'Your title', - message: 'Your message', - variant: 'success', - appearance: 'notification', - growl: true, - actions: [ - { - label: 'Yes', - method: alertYes - }, - { - label: 'No', - method: () => { - alert('No') - } - }, - { - label: 'Cancel', - route: 'https://www.shopware.com', - disabled: false, - } - ] -}) +notification.dispatch({ + title: "Your title", + message: "Your message", + variant: "success", + appearance: "notification", + growl: true, + actions: [ + { + label: "Yes", + method: alertYes, + }, + { + label: "No", + method: () => { + alert("No"); + }, + }, + { + label: "Cancel", + route: "https://www.shopware.com", + disabled: false, + }, + ], +}); ``` -#### Parameters: +#### Parameters + | Name | Required | Default | Description | -|:-------------|:---------|:---------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| :----------- | :------- | :------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `title` | true | | Defines a notification's **title**. | | `message` | true | | Defines a notification's main expression or message to the user. | | `variant` | false | `info` | Defines the notification type. Available `variant` types are `success`, `info`, `warning` and `error`. | @@ -46,5 +60,6 @@ sw.notification.dispatch({ | `growl` | false | `true` | Displays a notification that is overlaying any module. Use `false` to display the notification in the notification center (bell symbol) only. | | `actions` | false | `[]` | Adds clickable buttons to the notification. Each button with a `label` can trigger a `method` or open a `route` (internal route or external link). Buttons can also be disabled using the attribute `disabled`. | -#### Return value: +#### Return value + Returns a promise without data. diff --git a/docs/admin-sdk/api-reference/ui/purchases-and-payments/in-app-purchases.md b/docs/admin-sdk/api-reference/ui/purchases-and-payments/in-app-purchases.md new file mode 100644 index 000000000..ea9ee6ad1 --- /dev/null +++ b/docs/admin-sdk/api-reference/ui/purchases-and-payments/in-app-purchases.md @@ -0,0 +1,44 @@ +--- +title: "In-App Purchases" +nav: + position: 10 +--- + +# In-App Purchases + +> Available since Shopware v6.6.9.0 + +In-App purchases allow apps to unlock different functionality based on purchases made by the user. This guide covers how to start the in-app purchase flow directly from the Administration. + +## purchase() + +Open a modal with details about the feature that can be purchased. + +#### Usage + +```ts +import { iap } from "@shopware-ag/meteor-admin-sdk"; + +iap.purchase({ + identifier: "your-in-app-purchase-id", +}); +``` + +#### Parameters + +| Name | Required | Description | +| :----------- | :------- | :-------------------------------------- | +| `identifier` | true | The id of the in-app purchase to start. | + +#### Return value + +Returns a promise without data. + +## Behavior + +When called, Shopware opens a purchase modal inside the Administration. The modal guides the merchant through the checkout flow for purchasing or subscribing to the feature. + +After a successful purchase: + +- The charge is added to the merchant's Shopware bill +- The purchased feature becomes available in the app. diff --git a/docs/admin-sdk/api-reference/ui/purchases-and-payments/index.md b/docs/admin-sdk/api-reference/ui/purchases-and-payments/index.md new file mode 100644 index 000000000..025f1cfc8 --- /dev/null +++ b/docs/admin-sdk/api-reference/ui/purchases-and-payments/index.md @@ -0,0 +1,13 @@ +--- +title: "Purchases and Payments" +nav: + position: 120 +--- + + +# Purchases and Payments + +These APIs allow extensions to integrate purchasing flows and customize how payment methods appear in the Shopware Administration. + +- [In-App Purchases](./in-app-purchases.md): Start an in-app purchase flow from the Administration +- [Payment Overview Cards](./paymentOverviewCard.md): Customize how payment methods appear in the payment overview diff --git a/docs/admin-sdk/api-reference/ui/module/paymentOverviewCard.md b/docs/admin-sdk/api-reference/ui/purchases-and-payments/paymentOverviewCard.md similarity index 60% rename from docs/admin-sdk/api-reference/ui/module/paymentOverviewCard.md rename to docs/admin-sdk/api-reference/ui/purchases-and-payments/paymentOverviewCard.md index 252f22451..cf09c3c25 100644 --- a/docs/admin-sdk/api-reference/ui/module/paymentOverviewCard.md +++ b/docs/admin-sdk/api-reference/ui/purchases-and-payments/paymentOverviewCard.md @@ -1,34 +1,64 @@ +--- +title: "Payment Overview Cards" +nav: + position: 20 +--- + + # Payment Overview Cards -### Add a custom payment method overview card in settings +A payment overview card allows extensions to customize how a payment method appears in the payment methods overview in the Shopware Administration. + +Starting with Shopware **6.4.14.0**, extensions can replace the default payment method card with a custom component. This makes it possible to add additional logic before a payment method can be activated. + +For example, you might require merchants to complete an onboarding process with your payment provider before enabling the payment method. + +## payment.overviewCard.add() + +#### Usage + +```ts +import { ui, location } from '@shopware-ag/meteor-admin-sdk'; + +if (location.is(location.MAIN_HIDDEN)) { + ui.module.payment.overviewCard.add({ + positionId: 'my-custom-payment-overview-position', + paymentMethodHandlers: [ + 'handler_my_custom_payment_method_one', + 'handler_my_custom_payment_method_two', + ], + }); +} +``` -Starting with Shopware 6.4.14.0, you can render a custom card in the new payment method overview. -With that, you can replace the default card, where you can toggle the active state of a payment method, with your own component. -This allows you, for example, to require an onboarding to your payment provider before activating the payment method. +#### Parameters -### Parameters | Name | Required | Default | Description | |:------------------------|:---------| :------------- |:------------------------------------------------------------------------------------------------------------------------------------| | `positionId` | true | | The position id that is created in the payment overview, where you can add a component section to | | `paymentMethodHandlers` | true | | A list of formatted payment method handlers, which are handled by your component and where the default card should not be rendered. | | `component` | false | | The component name of you custom payment overview card. Only useful, if you have a plugin with a registered component | -### Extension example +#### Return value + +Returns a promise without data. + +#### Example: Inside an extension + +Use the generated `positionId` together with `ui.componentSection.add()` to render your custom card content in the payment overview. + ```ts -import { ui } from '@shopware-ag/meteor-admin-sdk'; +import { ui, location } from '@shopware-ag/meteor-admin-sdk'; -if (sw.location.is(sw.location.MAIN_HIDDEN)) { - // create the position +if (location.is(location.MAIN_HIDDEN)) { ui.module.payment.overviewCard.add({ positionId: 'my-custom-payment-overview-position', paymentMethodHandlers: [ 'handler_my_custom_payment_method_one', 'handler_my_custom_payment_method_two', - // ... ], }); - // add your component to that position ui.componentSection.add({ component: 'card', positionId: 'my-custom-payment-overview-position', @@ -40,13 +70,15 @@ if (sw.location.is(sw.location.MAIN_HIDDEN)) { }) } -// render your view to that location -if (sw.location.is('my-custom-payment-overview-position-before')) { +if (location.is('my-custom-payment-overview-position-before')) { // your content here } ``` -### Custom plugin component example +#### Example: Custom plugin component + +Plugin extensions can also render a custom registered Administration component directly instead of using a component section. + ```ts import { ui } from '@shopware-ag/meteor-admin-sdk'; diff --git a/docs/admin-sdk/api-reference/ui/settingsItem.md b/docs/admin-sdk/api-reference/ui/settingsItem.md index 9ba00a746..65b39b3e4 100644 --- a/docs/admin-sdk/api-reference/ui/settingsItem.md +++ b/docs/admin-sdk/api-reference/ui/settingsItem.md @@ -1,11 +1,25 @@ +--- +title: "Settings Item" +nav: + position: 40 +--- + + # Settings Item -### Add settings item -Add a new settings item to the Shopware settings. The content of the settings item module is determined by your `locationId`. -A specific view or a set of actions can be triggered based on the `locationId`. +A settings item adds an entry to the Shopware Administration settings area. + +Use this when your extension provides configurable options that should appear in the central settings section. + +## addSettingsItem() + +Add a new settings item to the Shopware settings. The content of the settings item module is determined by your `locationId`. A specific view or a set of actions can be triggered based on the `locationId`. + +#### Usage -#### Usage: ```ts +import { ui } from '@shopware-ag/meteor-admin-sdk'; + ui.settings.addSettingsItem({ label: 'App Settings', locationId: 'settings-location-id', @@ -17,6 +31,7 @@ ui.settings.addSettingsItem({ ``` #### Parameters + | Name | Required | Default | Description | | :------------------- | :------- | :------------- | :------------------------------------------------------------ | | `label` | true | | The label of the tab bar item | @@ -26,12 +41,16 @@ ui.settings.addSettingsItem({ | `displaySmartBar` | false | true | Toggles the sw-page smart bar on/off | | `tab` | false | 'plugins' | Determines in which tab your settings item will be displayed | -### Getting the right icon -Assuming that your editor supports TypeScript, you should get auto-completion for valid `icon` values. -In case that doesn't work take a look at the list [here](https://github.com/shopware/meteor-admin-sdk/blob/trunk/src/icons.ts). +#### Return value + +Returns a promise without data. + +To browse available icons, see the [Meteor icon kit repository](https://github.com/shopware/meteor/tree/main/packages/icon-kit). If your editor supports TypeScript, you should also get auto-completion when importing icons from the Meteor icon package. #### Example + ![Settings item example](./assets/add-settings-item-example.png) + ```ts import { location, ui } from '@shopware-ag/meteor-admin-sdk'; diff --git a/docs/admin-sdk/api-reference/ui/sidebars.md b/docs/admin-sdk/api-reference/ui/sidebars.md index 406d2107d..39bb13d6f 100644 --- a/docs/admin-sdk/api-reference/ui/sidebars.md +++ b/docs/admin-sdk/api-reference/ui/sidebars.md @@ -1,74 +1,103 @@ +--- +title: "Sidebars" +nav: + position: 80 +--- + # Sidebars A sidebar provides a contextual panel that displays at the right edge of the Administration window. Unlike modals, sidebars allow users to view and interact with additional content or functionality without losing context of the main interface. You can open a sidebar programmatically, not only from direct UI clicks. As a best practice, still tie openings to clear user or application context: stacking many sidebars without a reason forces repeated dismissal and harms the experience. -### Add a sidebar +```ts +import { ui } from "@shopware-ag/meteor-admin-sdk"; +``` + +## sidebar.add() Add a new sidebar. The content of the sidebar is determined by your `locationId`. -#### Usage: +#### Usage ```ts -sw.ui.sidebar.add({ - title: 'Awesome Chat Bot', - locationId: 'sidebar-chat-bot', - icon: 'regular-sparkles', +ui.sidebar.add({ + title: "Awesome Chat Bot", + locationId: "sidebar-chat-bot", + icon: "regular-sparkles", }); ``` #### Parameters -| Name | Required | Description | Available at Shopware | -| :----------- | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------| -| `title` | true | The title of the sidebar | 6.7 | -| `locationId` | true | The id for the content of the sidebar | 6.7 | -| `icon` | true | The icon to display in the sidebar. You can use any icon from the Shopware icon library | 6.7 | -| `resizable` | false | Enables horizontal resizing of the sidebar | 6.7.2.0 | + +| Name | Required | Description | Available at Shopware | +| :----------- | :------- | :-------------------------------------------------------------------------------------- | :-------------------- | +| `title` | true | The title of the sidebar | 6.7 | +| `locationId` | true | The id for the content of the sidebar | 6.7 | +| `icon` | true | The icon to display in the sidebar. You can use any icon from the Shopware icon library | 6.7 | +| `resizable` | false | Enables horizontal resizing of the sidebar | 6.7.2.0 | + +#### Return value + +Returns a promise without data. #### Example -![Menu item example](../assets/sidebar-example.png) -### Close a sidebar +![Menu item example](./assets/sidebar-example.png) + +## sidebar.close() Close an existing sidebar programmatically. -#### Usage: +#### Usage ```ts -sw.ui.sidebar.close({ - locationId: 'sidebar-chat-bot', +ui.sidebar.close({ + locationId: "sidebar-chat-bot", }); ``` #### Parameters -| Name | Required | Description | Available at Shopware | -| :----------- | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------| -| `locationId` | true | The id of the sidebar to close | 6.7 | -### Remove a sidebar +| Name | Required | Description | Available at Shopware | +| :----------- | :------- | :----------------------------- | :-------------------- | +| `locationId` | true | The id of the sidebar to close | 6.7 | + +#### Return value + +Returns a promise without data. + +## sidebar.remove() Remove a sidebar completely from the DOM. -#### Usage: +#### Usage ```ts -sw.ui.sidebar.remove({ - locationId: 'sidebar-chat-bot', +ui.sidebar.remove({ + locationId: "sidebar-chat-bot", }); ``` #### Parameters + +| Name | Required | Description | Available at Shopware | +| :----------- | :------- | :------------------------------ | :-------------------- | +| `locationId` | true | The id of the sidebar to remove | 6.7 | + +#### Return value + +Returns a promise without data. | Name | Required | Description | Available at Shopware | | :----------- | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------| | `locationId` | true | The id of the sidebar to remove | 6.7 | -### Set an active sidebar +## sidebar.setActive() Set an active sidebar that was already registered with [`add`](#add-a-sidebar). Use this when the sidebar exists but is closed or not in front. The Administration shows the panel for the given `locationId` and loads the content you associated with that id. #### Usage: ```ts -sw.ui.sidebar.setActive({ +ui.sidebar.setActive({ locationId: 'sidebar-chat-bot', }); ``` @@ -76,4 +105,4 @@ sw.ui.sidebar.setActive({ #### Parameters | Name | Required | Description | Available at Shopware | | :----------- | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------| -| `locationId` | true | The id of the sidebar to active | 6.7.9 | \ No newline at end of file +| `locationId` | true | The id of the sidebar to active | 6.7.9 | diff --git a/docs/admin-sdk/api-reference/ui/tabs.md b/docs/admin-sdk/api-reference/ui/tabs.md index df99219e0..9d828e953 100644 --- a/docs/admin-sdk/api-reference/ui/tabs.md +++ b/docs/admin-sdk/api-reference/ui/tabs.md @@ -1,12 +1,25 @@ +--- +title: "Tabs" +nav: + position: 60 +--- + + # Tabs -### Add tab item -Add a new tab item to an existing tab bar. The content of the the new tab item +Tabs allow extensions to add additional tabs to existing Administration pages. + +They are commonly used to extend entity detail pages such as products, customers, or orders. + +## addTabItem() + +Add a new tab item to an existing tab bar. The content of the new tab item contains a component section. This works with tab bar's which have routing and also static tab bars. If the tab bar has routing then the route for the tab item will be generated automatically. -#### Usage: +#### Usage + ```ts import { ui } from '@shopware-ag/meteor-admin-sdk'; @@ -17,15 +30,21 @@ ui.tabs('sw-product-detail' /* The positionId of the tab bar*/).addTabItem({ ``` #### Parameters + | Name | Required | Default | Description | | :------------------- | :------- | :------ | :------------------------------------------------------ | | `label` | true | | The label of the tab bar item | | `componentSectionId` | true | | The Id for for the component section in the tab content | +#### Return value + +Returns a promise without data. + #### Example + ![Tab item example](./assets/add-tab-item-example.png) ```ts -import { ui, location } from '@shopware-ag/meteor-admin-sdk'; +import { location, notification, ui } from '@shopware-ag/meteor-admin-sdk'; // For general commands if (location.is(location.MAIN_HIDDEN)) { @@ -48,11 +67,18 @@ if (location.is(location.MAIN_HIDDEN)) { // Render custom view of the component if (location.is('my-example-product-view-tab-card')) { - document.body.innerHTML = ' + document.body.innerHTML = `

Hello in the example card

- - '; + + `; + + document + .getElementById('show-notification') + ?.addEventListener('click', () => { + notification.dispatch({ + title: 'Foo', + message: 'bar', + }); + }); } ``` diff --git a/docs/admin-sdk/api-reference/ui/toast.md b/docs/admin-sdk/api-reference/ui/toast.md new file mode 100644 index 000000000..efea5b298 --- /dev/null +++ b/docs/admin-sdk/api-reference/ui/toast.md @@ -0,0 +1,51 @@ +--- +title: "Toast" +nav: + position: 56 +--- + +# Toast + +> Available since Shopware v6.6.2.0 + +Toasts display short, temporary messages to provide feedback about user actions or system events. Unlike [notifications](./notification.md), which persist in the notification center until dismissed, toasts disappear automatically after a short time. + +See also: [Base Options](../base-options.md) for shared configuration options supported by SDK message APIs. + +## toast.dispatch() + +![toast example](../assets/toast-example.png) + +#### Usage + +```ts +import { toast } from "@shopware-ag/meteor-admin-sdk"; + +function alertYes() { + alert("Yes"); +} + +toast.dispatch({ + msg: "Your message", + dismissible: true, + type: "positive", + action: { + label: "action", + callback: alertYes, + }, +}); +``` + +#### Parameters + +| Name | Required | Default | Description | +| :------------ | :------- | :------ | :------------------------------------------------------------------------------------------------------------- | +| `msg` | true | | Defines a toast's main expression or message to the user. | +| `type` | true | | Defines the toast type. Available `types` are `positive`, `informal` and `critical`. | +| `icon` | false | None | An icon that should be displayed in front of your message. | +| `dismissible` | false | `false` | Specifies if the toast can be manually dismissed. | +| `action` | false | None | Adds a clickable button to the toast. The button receives a label and a callback which is called once clicked. | + +#### Return value + +Returns a promise without data. diff --git a/docs/admin-sdk/api-reference/window.md b/docs/admin-sdk/api-reference/window.md index de21e9ce2..7b175c843 100644 --- a/docs/admin-sdk/api-reference/window.md +++ b/docs/admin-sdk/api-reference/window.md @@ -1,115 +1,150 @@ +--- +title: "Window" +sidebar_position: 70 +--- + # Window -### Redirect to another URL +The Window API provides methods for navigation and window-related utilities inside the Shopware Administration. + +```ts +import { window as adminWindow } from "@shopware-ag/meteor-admin-sdk"; +``` + +## redirect() + +Redirect to an external URL. + +#### Usage + +Use this method to open an external URL either in the current tab or a new tab. -#### Usage: ```ts -sw.window.redirect({ - url: 'https://www.shopware.com, - newTab: true -}) +adminWindow.redirect({ + url: "https://www.shopware.com", + newTab: true, +}); ``` -#### Parameters: -| Name | Required | Default | Description | -| :------ | :------ | :------ | :------ | -| `url` | true | | The title of the notification | -| `newTab` | false | false | The message of the notification | +#### Parameters + +| Name | Required | Default | Description | +| :------- | :------- | :------ | :-------------------------------- | +| `url` | true | | The URL to open | +| `newTab` | false | false | Open the URL in a new browser tab | + +#### Return value -#### Return value: Returns a promise without data. -### Push to another page -For redirecting to other pages in the admin. +## routerPush() + +Navigate to another page inside the Shopware Administration. -#### Usage: -The usage matches the Vue Router push capabilities. Here are two examples how to use it for redirecting to your own modules: +#### Usage + +This method mirrors the behavior of Vue Router’s `push()`. + +Navigate using a named route: ```ts -sw.window.routerPush({ - name: 'sw.extension.sdk.index', - params: { - id: 'the_id_of_the_module' // can be get with context.getModuleInformation - } -}) +adminWindow.routerPush({ + name: "sw.extension.sdk.index", + params: { + id: "the_id_of_the_module", // can be retrieved with context.getModuleInformation + }, +}); ``` +Alternatively, navigate using a path: + ```ts -sw.window.routerPush({ - path: `/extension/${the_id_of_the_module}` // id can be get with context.getModuleInformation -}) +adminWindow.routerPush({ + path: `/extension/${the_id_of_the_module}`, // id can be retrieved with context.getModuleInformation +}); ``` -#### Parameters: -| Name | Required | Default | Description | -| :------ | :------ | :------ | :------ | -| `name` | false | undefined | The name of the route | -| `path` | false | undefined | The path of the route | -| `params` | false | undefined | Additional params for the new route | -| `replace` | false | false | Should not change the browser history | +#### Parameters + +| Name | Required | Default | Description | +| :-------- | :------- | :-------- | :------------------------------------ | +| `name` | false | undefined | Name of the route | +| `path` | false | undefined | Path of the route | +| `params` | false | undefined | Additional params for the new route | +| `replace` | false | false | Replace current browser history entry | + +#### Return value -#### Return value: Returns a promise without data. -### Reload page +## reload() -Useful for development. You can trigger a page reload on file changes. +Reload the current Administration page. This can be useful during development or when UI state must be reset. + +#### Usage -#### Usage: ```ts -sw.window.reload() +adminWindow.reload(); ``` -#### Parameters: +#### Parameters + No parameters required. -#### Return value: +#### Return value + Returns a promise without data. -### Get a unique identifier for the window +## getId() > Available since Shopware v6.7.1.0 -When it comes to session handling it can be useful to have a unique identifier for your window. +Returns a unique identifier for the current browser window. This is useful when working with session storage or detecting duplicated tabs. + +#### Usage -#### Usage: ```ts -sw.window.getId() +await adminWindow.getId(); ``` -#### Parameters: -No parameters required +#### Parameters + +No parameters required. + +#### Return value -#### Return value: -A `string` representing an unique identifier for the current window +A `Promise` that resolves to a unique identifier for the current window. -#### Example: -In this example we check if the `sessionStorage` contains data from a former window. This can happen if a user uses the *Duplicate Tab* feature of some browsers. +#### Example + +This example clears `sessionStorage` when a duplicated browser tab is detected. This can happen if a user uses the _Duplicate Tab_ feature of some browsers. ```ts -const windowId = sw.window.getId(); -const storedWindowId = globalThis.sessionStorage.getItem('window-id'); +const windowId = await adminWindow.getId(); +const storedWindowId = globalThis.sessionStorage.getItem("window-id"); if (windowId !== storedWindowId) { - globalThis.sessionStorage.clear(); - globalThis.sessionStorage.setItem('window-id', windowId); + globalThis.sessionStorage.clear(); + globalThis.sessionStorage.setItem("window-id", windowId); } - ``` -### Get the view router path +## getPath() > Available since Shopware v6.7.3.0 -You can get the view router full path. +Retrieve the current Administration router path. + +#### Usage -#### Usage: ```ts -sw.window.getPath() +await adminWindow.getPath(); ``` -#### Parameters: +#### Parameters + No parameters required. -#### Return value: -A `string` with the full path, or empty if router not found. +#### Return value + +Returns a `Promise` containing the full path, or an empty string if the router is not available. diff --git a/docs/admin-sdk/internals/how-it-works.md b/docs/admin-sdk/concepts/architecture.md similarity index 96% rename from docs/admin-sdk/internals/how-it-works.md rename to docs/admin-sdk/concepts/architecture.md index bcdcea3e6..6e20e542f 100644 --- a/docs/admin-sdk/internals/how-it-works.md +++ b/docs/admin-sdk/concepts/architecture.md @@ -1,4 +1,11 @@ -# How it works +--- +title: "Architecture" +nav: + position: 20 +--- + + +# Architecture The Meteor Admin SDK provides wrapper methods for a better development experience. It abstracts and hides the more complex logic behind a simple API. This makes it easier for app and plugin developers to create their solutions and focus @@ -22,7 +29,7 @@ The helper methods can be found in the `channel` file. It holds different method Here is an example to give you a better understanding of how that works. -### Example workflow +## Example workflow Let's imagine that an app or plugin calls the `context.getLanguage` method from the Extension SDK: @@ -71,6 +78,7 @@ handle('contextLanguage', () => { It uses the `handle` method, which is also a helper method of the `channel`. You see now, that the type matches the sender type. And in the second argument it provides a method that returns the data. This method reacts to every `contextLanguage` request and sends the data values back to the source of the request. It also creates an object that includes meta information which in turn are needed for the original `send` window: + ```js { _type: 'contextLanguage', @@ -93,9 +101,11 @@ const language = await sw.context.getLanguage(); And this is basically it! The app or plugin has now got the data from the Administration. It all just looks like a simple call, but there is a lot going on in the background. ## Sending methods + In normal cases you can't add methods to JSON objects which will get stringified. But in our case we are convinced it would make the many developers' lives much easier if they can also use their own methods in the calls. -To handle these edge-cases we are converting the methods to information objects like this: +To handle these edge cases we are converting the methods to information objects like this: + ```js { __type__: '__function__', @@ -116,5 +126,4 @@ send('__function__', { The sender gets the message back and executes the method with the matching ID and the given arguments. The return value will then be sent back to the converted method in the receiver. -This complex logic is also abstracted. To use it, just add methods to -the data. They will then be converted and handled automatically. \ No newline at end of file +This complex logic is also abstracted. To use it, just add methods to the data. They will then be converted and handled automatically. diff --git a/docs/admin-sdk/internals/assets/devtool-example.png b/docs/admin-sdk/concepts/assets/devtool-example.png similarity index 100% rename from docs/admin-sdk/internals/assets/devtool-example.png rename to docs/admin-sdk/concepts/assets/devtool-example.png diff --git a/docs/admin-sdk/internals/assets/post-message-communication.png b/docs/admin-sdk/concepts/assets/post-message-communication.png similarity index 100% rename from docs/admin-sdk/internals/assets/post-message-communication.png rename to docs/admin-sdk/concepts/assets/post-message-communication.png diff --git a/docs/admin-sdk/concepts/component-sections.md b/docs/admin-sdk/concepts/component-sections.md index a825dfa43..e52306a91 100644 --- a/docs/admin-sdk/concepts/component-sections.md +++ b/docs/admin-sdk/concepts/component-sections.md @@ -1,75 +1,45 @@ -# Component sections +--- +title: "Component Sections" +nav: + position: 50 +--- -In most cases extension developers will directly use the extension capabilities of the UI components (e.g. adding tab items, adding button to grid, ...). This will cover most needs of many extensions. But in cases where a extension need special solutions which aren't feasible with the given extension they can use a feature named `Component Sections`. These are sections where any extension developer can inject components. -These components are prebuilt (like cards) and contain in most cases custom [location](./locations.md) where the extension has the full freedom to render anything. +# Component Sections -### Example: +Component sections allow extensions to render custom UI components inside predefined extension points in the Shopware Administration. + +Unlike other extension APIs that modify existing UI elements (such as tabs or buttons), component sections allow extensions to inject full components into specific UI positions. + +Component sections are prebuilt (like cards) and usually work together with: + +- [Positions](./positions.md): identify where UI can be injected +- [Locations](./locations.md): determine where extension content should render + +## Example + +This example adds a card with custom content before the properties card on the manufacturer detail page: ```js +import { ui, location } from '@shopware-ag/meteor-admin-sdk'; + if (location.is(location.MAIN_HIDDEN)) { - sw.ui.componentSection.add({ - // Choose a position id where you want to render a custom component + ui.componentSection.add({ positionId: 'sw-manufacturer-card-custom-fields__before', - // The Component Sections provides different components out of the box component: 'card', - // Props are depending on the type of component props: { title: 'Hello from plugin', subtitle: 'I am before the properties card', - // Some components can render a custom view. In this case the extension can render custom content in the card. locationId: 'my-app-card-before-properties' } }) } -// Render the custom UI when the iFrame location matches your defined location -if (sw.location.is('my-app-card-before-properties')) { +if (location.is('my-app-card-before-properties')) { document.body.innerHTML = '

Hello World before

'; - document.body.style.background = 'blue'; } ``` ![Component Sections screenshot example](./assets/component-sections-example.png) -If you want to render tabs inside the `card` component section, we provide a way to do so: -```js -if (sw.location.is(sw.location.MAIN_HIDDEN)) { - // Choose a position id where you want to render a custom component - sw.ui.componentSection.add({ - // The Component Sections provides different components out of the box - component: 'card', - // Props are depending on the type of component - props: { - title: 'Hello from plugin', - subtitle: 'I am before the properties card', - // Render tabs and custom tab content with the provided location id - tabs: [ - { - name: 'example-tab-1', - label: 'First tab', - locationId: 'example-tab-1' - }, - { - name: 'example-tab', - label: 'Second tab', - locationId: 'example-tab-2' - } - ], - } - }) -} - -// Render the custom UI for different tab with the location id -if (sw.location.is('example-tab-1')) { - document.body.innerHTML = '

My first tab

'; - document.body.style.background = 'blue'; -} - -if (sw.location.is('example-tab-2')) { - document.body.innerHTML = '

My second tab

'; - document.body.style.background = 'yellow'; -} -``` - -![Component Sections screenshot example](./assets/component-sections-with-tabs-example.png) +For the full API reference including available components, card properties, and tab support, see the [Component Sections API reference](../api-reference/ui/component-sections.md). diff --git a/docs/admin-sdk/internals/datahandling.md b/docs/admin-sdk/concepts/datahandling.md similarity index 79% rename from docs/admin-sdk/internals/datahandling.md rename to docs/admin-sdk/concepts/datahandling.md index 147667423..1cbdb594b 100644 --- a/docs/admin-sdk/internals/datahandling.md +++ b/docs/admin-sdk/concepts/datahandling.md @@ -1,12 +1,21 @@ -# Datahandling +--- +title: "Data Handling" +nav: + position: 70 +--- -This guide elaborates how the data handling works between extensions and the Shopware administration. + +# Data Handling + +This guide elaborates on how the data handling works between extensions and the Shopware Administration. ## What are datasets? + Datasets consist of a unique identifier, an `id` and some `data` which could be anything from a single value to a whole entity. The id gives some insight on what to expect as the value. For example `sw-product-detail__product` contains the product of the product detail page. ## How to find available datasets + You can explore all available datasets with the Vue Devtool extension we provide with the Shopware administration. - Open the Vue DevTools in the Shopware Administration @@ -14,6 +23,6 @@ You can explore all available datasets with the Vue Devtool extension we provide In this inspector you will see all published datasets if there are any in the current view. +## Example -#### Example ![Action button example](./assets/devtool-example.png) diff --git a/docs/admin-sdk/concepts/index.md b/docs/admin-sdk/concepts/index.md index 4aff59e17..11a809cc4 100644 --- a/docs/admin-sdk/concepts/index.md +++ b/docs/admin-sdk/concepts/index.md @@ -1,5 +1,20 @@ --- title: "Concepts" nav: - position: 500 + position: 20 --- + +# Concepts + +Before building Administration extensions, it helps to understand a few core concepts used by the Meteor Admin SDK. + +These concepts explain how extensions are rendered, where UI can be injected, and how data is accessed. + +## Concepts overview + +- [Architecture](./architecture.md): explains how the SDK communicates with the Shopware Administration and how requests are handled internally. +- [Locations](./locations.md): determine where extension code runs. +- [Positions](./positions.md): identify extendable areas of the Administration UI. +- [Component Sections](./component-sections.md): render custom components inside extension points. +- [Selectors](./selectors.md): retrieve specific fields from Administration data. +- [Data Handling](./datahandling.md): represent the data published by the Administration that extensions can read, subscribe to, or update. diff --git a/docs/admin-sdk/concepts/locations.md b/docs/admin-sdk/concepts/locations.md index 7fc602803..b3a80552a 100644 --- a/docs/admin-sdk/concepts/locations.md +++ b/docs/admin-sdk/concepts/locations.md @@ -1,39 +1,54 @@ +--- +title: "Locations" +nav: + position: 30 +--- + # Locations -Extensions can render custom views via iFrames. To support multiple views in different places every `location` of the iFrame gets a unique ID. These can be defined by the extension developer itself. +Locations define where an extension renders inside the Shopware Administration. Your SDK code is injected into every visible location, plus one hidden location that handles the main registration logic, and each of those locations runs inside its own iframe. Because every iframe executes the same JavaScript code, you need to check which location is currently active and render the appropriate view for it. + +## Mental model + +Each location is identified by a **location ID**. When you register a UI extension, you choose a `locationId`. Then in your code, you use `location.is()` to branch between the hidden iframe where you register UI and the visible iframe where you render content: + +```js +import { location } from '@shopware-ag/meteor-admin-sdk'; + +if (location.is(location.MAIN_HIDDEN)) { + // Register UI extensions here +} -*Example:* +if (location.is('my-app-card-before-properties')) { + // Render the content for this location here +} +``` -A extension wants to render a custom iFrame in a card in the dashboard. The `location` of the iFrame has then a specific `locationId` like `sw-dashboard-example-app-dashboard-card`. The app can also render another iFrames which also get `locationId`s. In our example it is a iFrame in a custom modal: `example-app-example-modal-content`. +Here is the same pattern with a full component section example: -The extension want to render different views depending on the `location` of the iFrame. So the extension developer can render the correct view depending on the `locationId`: ```js -// Add the ui extensions when your extension is loaded in the hidden iFrame -if (sw.location.is(sw.location.MAIN_HIDDEN)) { +import { ui, location } from '@shopware-ag/meteor-admin-sdk'; + +if (location.is(location.MAIN_HIDDEN)) { ui.componentSection.add({ component: 'card', positionId: 'sw-product-properties__before', props: { title: 'Hello from plugin', subtitle: 'I am before the properties card', - /** - * The locationId: - **/ locationId: 'my-app-card-before-properties' } }) } -// Render the custom UI when the iFrame location matches your defined location -if (sw.location.is('my-app-card-before-properties')) { - document.body.innerHTML = '

I am the in the location "my-app-card-before-properties"

'; +if (location.is('my-app-card-before-properties')) { + document.body.innerHTML = '

Custom content here

'; } ``` -## Base location -Every extension gets rendered in a hidden iFrame. In this iFrame the extension can execute different commands to extend -the administration and add custom locations to different extension points. To check if the script will be executed in this -location you can use the predefined constant: +## Base location (hidden iframe) + +Every extension is initially loaded in a hidden iframe. This is where you register all your UI extensions — adding tabs, component sections, menu entries, and so on. To check whether the current code is running in the hidden iframe, use the `MAIN_HIDDEN` constant: ```js import { location } from '@shopware-ag/meteor-admin-sdk'; @@ -43,29 +58,32 @@ if (location.is(location.MAIN_HIDDEN)) { } ``` -## Change height of location iFrame -The iFrame height is by default fixed. You can update the height with the location helper: +## Change iframe height + +The iframe height is fixed by default. You can set it explicitly: + ```js -location.updateHeight(750); // change iFrame height to 750px +location.updateHeight(750); // set iframe height to 750px ``` -If you use a parameter then the height will automatically be calculated so that your whole view gets rendered. In most cases -you don't want to update the height manually. To watch for height changes you can use the auto resizer. It updates the iFrame -height everytime the height of the view changes: +In most cases you want the height to adjust automatically. The auto resizer watches for height changes and updates the iframe whenever the content size changes: + ```js // watch for height changes and update the iFrame location.startAutoResizer(); ``` + ![Auto Resizer example](./assets/auto-resizer.gif) ## Avoiding scrollbars -If you render custom locations it is useful to disable the scroll behavior in your view. Otherwise scrollbars are visible -which aren't needed in most cases. To avoid this you can add the css property `overflow: hidden;` to the `body` element. -## For existing plugin migrations: render Vue components instead of iFrames -In some cases you just want to use specific features from the SDK and some features from the existing plugin system which works with Twig and Component overriding. In this case you can do some things with the SDK but render components from the Shopware Component Factory instead of iFrames. +When rendering custom locations, add `overflow: hidden;` to the `body` element to prevent unnecessary scrollbars inside the iframe. + +## Render Vue components instead of iframes (plugin migration) + +When migrating existing plugins, you can mix the SDK with the existing plugin system. Instead of rendering an iframe, you can render a Vue component registered via `Shopware.Component.register` at a location. -To do this you need to register the component in the existing plugin system: +Register the component: ```js Shopware.Component.register('your-component-name', { @@ -73,7 +91,8 @@ Shopware.Component.register('your-component-name', { }) ``` -Now if you want to render the component in a location you need to add the name of the component to the current location. This can be done with the `sdkLocation` store: +Then map it to a location ID using the `sdkLocation` store: + ```js Shopware.State.commit('sdkLocation/addLocation', { locationId: 'your-location-id', @@ -81,7 +100,7 @@ Shopware.State.commit('sdkLocation/addLocation', { }) ``` -With this feature you can create mix the usage of the SDK and the existing plugin system. A complete example could be looking like this. It creates a new tab item in the product detail page, renders a card with the componentSection renderer and inside the card it renders the location. But instead of the traditional location it renders a Vue component which was registered in the Shopware Component Factory. +A complete example that adds a tab to the product detail page and renders a Vue component inside a card: ```js // in a normal plugin js file without a HTML file @@ -117,4 +136,4 @@ if (!location.isIframe()) { componentName: 'your-component-name' }) } -``` \ No newline at end of file +``` diff --git a/docs/admin-sdk/concepts/positions.md b/docs/admin-sdk/concepts/positions.md index 427539bbf..99e0ffd92 100644 --- a/docs/admin-sdk/concepts/positions.md +++ b/docs/admin-sdk/concepts/positions.md @@ -1,19 +1,32 @@ +--- +title: "Positions" +nav: + position: 40 +--- + + # Positions -Extension developer can extend existing areas or create new areas in the administration. It is so flexible that there are way to many Id's to remember. To identify the positions which the developer want to extend we need a unique ID for every position. These Id's are the `positionId`s. +Positions define where UI components can be injected into the Shopware Administration. + +Each extendable area exposes a unique `positionId`. Extensions use this identifier to tell the Administration where a UI component or extension should be rendered. -### Example: +Extension developers can extend existing areas or create new areas in the Administration. Memorizing all available `positionId`s is impractical. Instead, the SDK provides tooling to help discover them dynamically. + +## Example + +Suppose an extension wants to add a new tab to the product detail page. The extension must target the correct `positionId` for the tab bar. -A extension wants to add a new tab item to a tab-bar. In the administration are -many tab-bars available. So the developer needs to choose the correct `positionId` to tell the admin which tab-bar should be extended. In this example the developer adds a new tab item to the tab-bar in the product detail page. ```js sw.ui.tabs('sw-product-detail').addTabItem({ ... }) ``` +In this example, `sw-product-detail` is the `positionId` that identifies the tab bar in the product detail page. + +## Finding position IDs with Vue DevTools -### Vue Devtools Plugin for finding the PositionId's -It is impossible to create a list of all potential position Id's. And they would be hard to manage. To solve this problem the SDK provides a custom plugin for the Vue Devtools. It makes identifying the position Id's very easy. +Because the number of available positions is large and varies across views, the Meteor Admin SDK provides a plugin for [Vue DevTools](../develop/devtools.md) to help discover them. -Just open the plugin in the Devtools (It is available directly when you open the Administration). Then you can see all positions at the current administration view which are available for extending. If you click at one position Id you get more information about it. Like the property in the meteor-admin-sdk so that you directly know what functionality this position has. +Open the plugin in the Administration. When the DevTools plugin is open, it shows all extendable positions for the current Administration view. Selecting a position displays additional information, including the corresponding SDK API that can be used to extend it. -In summary: the Devtool plugin provides a visual way to see which parts can be extended and what are the positionIDs for the extension position. You can find a detailed guide in the tooling section of this documentation: [Vue Devtools](../tooling/vue-devtools.md) \ No newline at end of file +This provides a visual way to identify which parts of the Administration can be extended and which positionId should be used. diff --git a/docs/admin-sdk/concepts/selectors.md b/docs/admin-sdk/concepts/selectors.md index 7781f5605..1c8311a4f 100644 --- a/docs/admin-sdk/concepts/selectors.md +++ b/docs/admin-sdk/concepts/selectors.md @@ -1,12 +1,46 @@ +--- +title: "Selectors" +nav: + position: 60 +--- + + # Selectors -Selectors are a powerful tool to reduce the payload and minimize the needed privileges. -They are used in `data.subscribe` and `data.get`. Selectors are an array of strings. Each string represents a path to a property in the dataset. +Selectors allow extensions to request only specific fields from Administration data. + +By selecting only the required properties, selectors reduce payload size and limit the privileges required to access data. + +They are used in `data.subscribe` and `data.get`. Selectors are an array of strings, where each string represents the path to a property in the dataset. + +## Selector syntax + +A selector is a dot-separated string. Each segment between dots is one of the following: + +| Segment | Syntax | Description | +| :--------- | :--------- | :------------------------------------------------------------------- | +| Property | `name` | Access a named property on the current object | +| Nested | `a.b` | Traverse into a nested object — each dot moves one level deeper | +| Array index| `[N]` | Access a specific element in an array by its zero-based index | +| Wildcard | `*` | Iterate over all elements in an array | + +These segments combine to form a path from the root of the dataset to the value you want. For example: + +| Selector | Meaning | +| :----------------------- | :--------------------------------------------------------- | +| `name` | The `name` property at the root level | +| `manufacturer.name` | The `name` property of the `manufacturer` object | +| `variants.[0].name` | The `name` of the first element in the `variants` array | +| `variants.*.name` | The `name` of every element in the `variants` array | +| `manufacturer.id` | The `id` property of the `manufacturer` object | -### Example: +When multiple selectors are passed, their results are merged into a single object. Properties from the same parent are combined — for example `['manufacturer.id', 'manufacturer.name']` returns one `manufacturer` object containing both `id` and `name`. -Imagine this payload: -```json +## Example + +Consider the following example payload: + +```js { "name": "My Product", "manufacturer": { @@ -24,46 +58,95 @@ Imagine this payload: } ``` -If you are only interested in the names of the product and manufacturer, you can use the following selectors: -```javascript +If only the product name and manufacturer name are needed, request them using selectors: + +```js data.get({ id: 'sw-product-detail__product', selectors: ['name', 'manufacturer.name'], }).then((product) => { - console.log(product); // prints { name: "My Product", manufacturer: { name: "My Manufacturer" } } + console.log(product); }); ``` -### Combining selectors +Result: + +```js +{ + "name": "My Product", + "manufacturer": { + "name": "My Manufacturer" + } +} +``` + +## Combining selectors + +It is possible to request multiple properties from the same object: -Again for the above payload, if you are interested in multiple properties of the manufacturer, you can use the following selectors: -```javascript +```js data.get({ id: 'sw-product-detail__product', selectors: ['manufacturer.id', 'manufacturer.name'], }).then((product) => { - console.log(product); // prints { manufacturer: { id: '065e71ab94d778a980008e8c3e890270', name: "My Manufacturer" } + console.log(product); }); ``` -### Arrays +Result: -If you are interested in a specific variant, you can use the following selectors: -```javascript +```js +{ + "manufacturer": { + "id": "065e71ab94d778a980008e8c3e890270", + "name": "My Manufacturer" + } +} +``` + +## Arrays + +Selectors can also access array values: + +```js data.get({ id: 'sw-product-detail__product', selectors: ['variants.[0].name'], }).then((product) => { - console.log(product); // prints { variants: [ { name: "First Variant" } ] } + console.log(product); }); ``` -If you are interested in all variants, you can use wildcards. A wildcard is the asterix symbol (`*`) -```javascript +Result: + +```js +{ + "variants": [ + { "name": "First Variant" } + ] +} +``` + +## Wildcards + +To retrieve values from all items in an array, use the `*` wildcard: + +```js data.get({ id: 'sw-product-detail__product', selectors: ['variants.*.name'], }).then((product) => { - console.log(product); // prints { variants: [ { name: "First Variant" }, // same structure for all entries ] } + console.log(product); }); ``` + +Result: + +```js +{ + "variants": [ + { "name": "First Variant" }, + { "name": "Second Variant" } + ] +} +``` diff --git a/docs/admin-sdk/tooling/assets/devtools-plugin-extension-point-selection.png b/docs/admin-sdk/develop/assets/devtools-plugin-extension-point-selection.png similarity index 100% rename from docs/admin-sdk/tooling/assets/devtools-plugin-extension-point-selection.png rename to docs/admin-sdk/develop/assets/devtools-plugin-extension-point-selection.png diff --git a/docs/admin-sdk/tooling/assets/devtools-plugin-settings-list.png b/docs/admin-sdk/develop/assets/devtools-plugin-settings-list.png similarity index 100% rename from docs/admin-sdk/tooling/assets/devtools-plugin-settings-list.png rename to docs/admin-sdk/develop/assets/devtools-plugin-settings-list.png diff --git a/docs/admin-sdk/tooling/assets/devtools-plugin-settings.png b/docs/admin-sdk/develop/assets/devtools-plugin-settings.png similarity index 100% rename from docs/admin-sdk/tooling/assets/devtools-plugin-settings.png rename to docs/admin-sdk/develop/assets/devtools-plugin-settings.png diff --git a/docs/admin-sdk/tooling/assets/devtools-plugin-tab-shopware-extension.png b/docs/admin-sdk/develop/assets/devtools-plugin-tab-shopware-extension.png similarity index 100% rename from docs/admin-sdk/tooling/assets/devtools-plugin-tab-shopware-extension.png rename to docs/admin-sdk/develop/assets/devtools-plugin-tab-shopware-extension.png diff --git a/docs/admin-sdk/getting-started/assets/devtools-plugin.png b/docs/admin-sdk/develop/assets/devtools-plugin.png similarity index 100% rename from docs/admin-sdk/getting-started/assets/devtools-plugin.png rename to docs/admin-sdk/develop/assets/devtools-plugin.png diff --git a/docs/admin-sdk/getting-started/assets/devtools-usage.png b/docs/admin-sdk/develop/assets/devtools-usage.png similarity index 100% rename from docs/admin-sdk/getting-started/assets/devtools-usage.png rename to docs/admin-sdk/develop/assets/devtools-usage.png diff --git a/docs/admin-sdk/develop/devtools.md b/docs/admin-sdk/develop/devtools.md new file mode 100644 index 000000000..d2967a0a2 --- /dev/null +++ b/docs/admin-sdk/develop/devtools.md @@ -0,0 +1,80 @@ +--- +title: "Finding Extension Points with Vue DevTools" +sidebar_position: 20 +--- + + +# Finding Extension Points with Vue DevTools + +This guide describes the recommended tooling for developing and debugging Administration extensions built with the Meteor Admin SDK. + +The Shopware Administration provides many extension points, often implemented as components identified by a unique `positionId`. Identifying the correct `positionId` manually can require searching through the core source code, and can become time-consuming. + +A more efficient approach is to use the [Vue DevTools](https://devtools.vuejs.org/) plugin. The Shopware Administration integrates a Shopware Extension API plugin that works with Vue DevTools, enabling interactive discovery of extension points. + +Vue DevTools is **optional** but strongly recommended. + +## Prerequisites + +- A running Shopware instance +- A working app or plugin +- Vue DevTools installed in the browser. This tool is available for Chrome, Firefox, Edge, or as a standalone application, and can be used for inspecting Vue components and extension points inside the Shopware Administration. + +The Shopware Extension API plugin for Vue DevTools is available starting with Vue DevTools version 6+ (currently distributed via the [beta channel](https://chrome.google.com/webstore/detail/vuejs-devtools/ljjemllljcmogpfapbkkighbhhppjdbg)). It can be installed alongside older versions. If using a version prior to 6, the plugin will not work. + +## 1. Start the Administration watcher + +Before inspecting extension points, start the Administration build watcher: + +```bash +composer run watch:admin +``` + +Wait until the compilation finishes successfully. This rebuilds the Administration and enables development mode features. + +After installing the browser extension, open the Shopware Administration in development/watch mode. + +To verify that the plugin is active, open the DevTools settings and ensure the Shopware Admin plugin is installed and enabled. + +## 2. Open the Shopware Extension API plugin + +Open the Shopware Administration, then open the browser’s developer tools and navigate to the Vue tab. Select the Shopware Extension API plugin. + +![Devtools plugin](./assets/devtools-plugin.png) + +![Vue Devtools plugin settings](./assets/devtools-plugin-settings.png) + +![Vue Devtools plugin settings list](./assets/devtools-plugin-settings-list.png) + +## 3. Find extension points + +The plugin displays all available extension points for the current page. It is possible to: + +- Select an extension point to highlight the corresponding area in the Administration. +- Inspect available properties and metadata. +- Identify where and how to extend the UI using the Meteor Admin SDK. + +This helps you to understand: + +- Which extension points are available +- What data is exposed +- How the extension interacts with the Administration. + +![Devtools usage](./assets/devtools-usage.png) + +Navigate to the page you want to extend. For example, open the product detail page and switch to the **Specifications** tab. + +Open the Shopware Extension API plugin from the Vue DevTools dropdown: + +![Vue Devtools plugin tab Shopware extension](./assets/devtools-plugin-tab-shopware-extension.png) + +On the left side is a list of available extension points. Selecting one highlights the corresponding area in the Administration. + +![Vue Devtools plugin extension point selection](./assets/devtools-plugin-extension-point-selection.png) + +The DevTools inspector displays additional details about the selected extension point, including: + +- The `property` value (referenced in the API documentation) +- The `positionId`, which uniquely identifies the extension location (required in most cases) + +Use the `positionId` to target a specific extension point instead of extending all instances globally. diff --git a/docs/admin-sdk/develop/entity-types.md b/docs/admin-sdk/develop/entity-types.md new file mode 100644 index 000000000..f549fc53b --- /dev/null +++ b/docs/admin-sdk/develop/entity-types.md @@ -0,0 +1,94 @@ +--- +title: "Adding Entity Types" +sidebar_position: 100 +--- + + +# Adding Entity Types (TypeScript) + +The Meteor Admin SDK supports TypeScript typings for Shopware entities. Adding entity types enables type-safe access, editing, and saving of entities when working with repositories. + +To enable typings, create a global declaration file such as `global.d.ts` and extend the `EntitySchema` namespace. + +## Option 1: Use Shopware’s generated entity types (recommended) + +Shopware provides generated TypeScript definitions for all core entities. + +Install the package that matches your Shopware version: + +```bash +npm install @shopware-ag/entity-schema-types@5.0.0 +``` + +The package version corresponds to the Shopware version without the leading `6.`. Examples: + +`Shopware 6.5.0.0` → `@shopware-ag/entity-schema-types@5.0.0` +`Shopware 6.5.1.2` → `@shopware-ag/entity-schema-types@5.1.2` +`Shopware 6.6.3.1` → `@shopware-ag/entity-schema-types@6.3.1` + +Then import the types in your global declaration file: + +```ts +// global.d.ts +import '@shopware-ag/entity-schema-types'; +``` + +## Option 2: Use a fallback `any` type + +This is the simplest solution, but it removes type safety. If strict typing isn't required, it's possible to define a fallback type for all entities by setting the type to `any`: + +```ts +// global.d.ts +declare namespace EntitySchema { + interface Entities { + [entityName: string]: any; + } +} +``` + +This avoids type errors but disables type safety. + +## Option 3: Define custom entity types + +This option provides full control by defining entity types manually for each property and association. The drawback is the additional effort required to maintain the definitions. + +```ts +// global.d.ts +declare namespace EntitySchema { + interface Entities { + // using product_manufacturer as an example + product_manufacturer: product_manufacturer; + // in this case 'media', 'product' and 'product_manufacturer_translation' is also needed + ... + } + + interface product_manufacturer { + id: string; + versionId: string; + mediaId?: string; + link?: string; + name: string; + description?: string; + customFields?: unknown; + /* + * Entity and EntityCollection is defined in the namespace and can directly be used. + * The value in the generic (here 'media', 'product' and 'product_manufacturer_translation') must + * also be defined in this file. + */ + media?: Entity<'media'>; + products?: EntityCollection<'product'>; + translations: EntityCollection<'product_manufacturer_translation'>; + createdAt: string; + updatedAt?: string; + translated?: {name?: string, description?: string, customFields?: unknown}; + } + + ... +} +``` + +It is necessary to define types for any referenced entities, such as: + +- `media` +- `product` +- `product_manufacturer_translation` diff --git a/docs/admin-sdk/develop/index.md b/docs/admin-sdk/develop/index.md new file mode 100644 index 000000000..2d04f4557 --- /dev/null +++ b/docs/admin-sdk/develop/index.md @@ -0,0 +1,19 @@ +--- +title: "Start Developing" +nav: + position: 30 +--- + + +# Start Developing + +This section covers advanced development topics for building Administration extensions with the Meteor Admin SDK. + +For a full overview of available APIs, see the [API Reference](../api-reference/index.md). + +## Guides + +- **[DevTools](./devtools.md)**: Discover extension points and inspect the Administration UI with Vue DevTools. +- **[Translations](./translations.md)**: Localize extension UI using snippet files and synchronize language changes with the Administration. +- **[Entity Types](./entity-types.md)**: Configure and extend TypeScript typings for entity access and SDK usage. +- **[Migrating Existing Admin Plugins](./migrating-admin-plugins.md)**: Learn how to gradually migrate existing Twig-based Admin plugins to the Meteor Admin SDK. diff --git a/docs/admin-sdk/develop/migrating-admin-plugins.md b/docs/admin-sdk/develop/migrating-admin-plugins.md new file mode 100644 index 000000000..89714e3cc --- /dev/null +++ b/docs/admin-sdk/develop/migrating-admin-plugins.md @@ -0,0 +1,96 @@ +--- +title: "Migrating Existing Admin Plugins" +sidebar_position: 110 +--- + + +# Migrating Existing Admin Plugins + +The Meteor Admin SDK can be adopted gradually in existing Shopware Admin plugins. This guide shows how to combine the traditional Twig-based extension system with the SDK so you can migrate functionality step by step. + +## Combining the Meteor Admin SDK with existing plugins + +Shopware 6 has a rich Admin plugin extension system based on Twig and the concepts of component overriding and component extending. These concepts are very powerful, but they can also come with a steep learning curve. For this reason, you can migrate gradually to the Meteor Admin SDK. + +Both approaches can work together. This allows you to start by converting only parts of your plugins and then gradually migrate more functionality as new SDK features become available. + +This approach can also help simplify your plugins and prepare them for long-term maintenance. + +### Example + +```js +// Use existing extension capabilities +Shopware.Component.override('sw-dashboard-index', { + methods: { + async createdComponent() { + // Can also use Meteor Admin SDK features + await sw.notification.dispatch({ + title: 'Hello from the plugin', + message: 'I am combining the existing approach with the new SDK approach', + }) + + this.$super('createdComponent'); + } + } +}); +``` + +## Using locations with normal Vue components without iFrame rendering + +This feature is useful when you want to partially migrate from the Twig plugin system to the SDK extension system and use both systems together. Instead of rendering an iFrame view for a location, you can render a normal Vue component directly inside the Shopware Administration. + +To do this, register the component in the existing plugin system: + + +```js +Shopware.Component.register('your-component-name', { + // your component +}) +``` + +To render the component in a location, add the component name to the location using the `sdkLocation` store: + +```js +Shopware.State.commit('sdkLocation/addLocation', { + locationId: 'your-location-id', + componentName: 'your-component-name' +}) +``` + +With this feature you can mix the usage of the Meteor Admin SDK and the existing plugin system. A complete example could look like this: It creates a new tab in the product detail page, renders a card using the `componentSection` renderer, and displays a location inside the card. Instead of rendering the traditional location iFrame, it renders a Vue component registered in the Shopware Component Factory. + +```js +// in a normal plugin js file without a HTML file +import { ui, location } from '@shopware-ag/meteor-admin-sdk'; + +if (!location.isIframe()) { + const myLocationId = 'my-example-location-id'; + + // Create a new tab entry + ui.tabs('sw-product-detail').addTabItem({ + label: 'Example tab', + componentSectionId: 'example-product-detail-tab-content' + }) + + // Add a new card to the tab content which renders a location + ui.componentSection.add({ + component: 'card', + positionId: 'example-product-detail-tab-content', + props: { + title: 'Component section example', + locationId: myLocationId + } + }) + + // Register your component which should be rendered inside the location + Shopware.Component.register('your-component-name', { + // your component + }) + + // Add the component name to the specific location + Shopware.State.commit('sdkLocation/addLocation', { + locationId: myLocationId, + componentName: 'your-component-name' + }) +} +``` diff --git a/docs/admin-sdk/develop/translations.md b/docs/admin-sdk/develop/translations.md new file mode 100644 index 000000000..0431a0269 --- /dev/null +++ b/docs/admin-sdk/develop/translations.md @@ -0,0 +1,53 @@ +--- +title: "Translations" +sidebar_position: 80 +--- + + +# Translations + +Extensions can localize text displayed in the Shopware Administration using snippet files and a frontend translation library. + +For content rendered inside your extension UI, you can use any translation solution supported by your frontend framework (for example, the Vue i18n plugin). To keep translations synchronized with the Administration language, listen for language changes using the [Context API](../api-reference/context.md#subscribe-on-language-changes). + +For text rendered [inside](../concepts/locations.md) native Administration UI components (such as titles inside [component sections](../concepts/component-sections.md)), Shopware supports snippet files inside the app. + +## Creating snippet files + +Create one snippet file per supported language in the app. These files should reside in the `Resources/app/administration/snippet` directory. + +Use the language code as the filename—for example: `en-GB.json` for English language support. The file structure mirrors that of administration snippets. Example snippet file: + +```json +// /Resources/app/administration/snippet/en-GB.json +{ + "my-app-name": { + "example-card": { + "title": "My app", + "subtitle": "This is my app" + } + } +} +``` + +Snippet files follow the same structure used by the Shopware Administration. Overriding existing Administration snippets is possible, if needed. + +## Using snippets in an extension + +Reference snippet keys directly in the UI configuration. Example: + +```js +import { ui } from '@shopware-ag/meteor-admin-sdk'; + +ui.componentSection.add({ + component: 'card', + positionId: 'sw-manufacturer-card-custom-fields__before', + props: { + title: 'my-app-name.example-card.title', + subtitle: 'my-app-name.example-card.subtitle', + locationId: 'my-app-card-before-properties' + } +}) +``` + +When the Administration language changes, the corresponding snippet file is automatically used. diff --git a/docs/admin-sdk/docs.yml b/docs/admin-sdk/docs.yml new file mode 100644 index 000000000..280d0a016 --- /dev/null +++ b/docs/admin-sdk/docs.yml @@ -0,0 +1,21 @@ +redirects: + api-reference/ui/component-section.html: api-reference/ui/component-sections.html + api-reference/ui/module/paymentOverviewCard.html: api-reference/ui/purchases-and-payments/paymentOverviewCard.html + internals/how-it-works.html: concepts/architecture.html + internals/datahandling.html: concepts/datahandling.html + api-reference/in-app-purchases/in-app-purchases.html: api-reference/ui/purchases-and-payments/in-app-purchases.html + api-reference/in-app-purchases/index.html: api-reference/data/index.html + api-reference/ui/module/index.html: api-reference/ui/index.html + faq/index.html: develop/translations.html + getting-started/devTools.html: develop/devtools.html + getting-started/installation.html: getting-started/installation-apps.html + internals/index.html: concepts/index.html + tooling/index.html: develop/index.html + tooling/vue-devtools.html: develop/devtools.html + assets/sidebar-example.png: ui/assets/sidebar-example.png + README.html: introduction.html + api-reference/data/in-app-purchases.html: api-reference/ui/purchases-and-payments/in-app-purchases.html + api-reference/ui/paymentOverviewCard.html: api-reference/ui/purchases-and-payments/paymentOverviewCard.html + api-reference/notification.html: api-reference/ui/notification.html + api-reference/toast.html: api-reference/ui/toast.html + api-reference/composables/getRepository.html: api-reference/composables/useRepository.html diff --git a/docs/admin-sdk/faq/index.md b/docs/admin-sdk/faq/index.md deleted file mode 100644 index ce7b1372b..000000000 --- a/docs/admin-sdk/faq/index.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: "FAQ" -nav: - position: 1100 ---- - -# FAQ - -## Can I use the same domain with subfolders for multiple apps? -No, for technical reasons, it is not possible to use the same domain with subfolders to host multiple apps. Each app must have its own separate domain. -The preferred solution is to use subdomains for each app. For example, you can use subdomains like "app-one.your-company.com", "app-two.your-company.com", and so on. Using subdomains allows you to have separate domains for each app, which avoids the technical limitations associated with using subfolders. - -## How can I use components that resemble the original components in the administration? -While it is not possible to use the exact same components in the Shopware administration, there is a component library called Meteor Component Library that offers similar components. The Shopware administration components are not native Vue components because they have extension capabilities, Twig templates, and other features that cannot be directly used. However, by utilizing the Meteor Component Library, you can achieve a native look and feel for your app that seamlessly integrates with the original Shopware administration. - -To access the Meteor Component Library, visit the following link: https://github.com/shopware/meteor-component-library - -## How can I use snippets to translate my app? - -You can manage all texts rendered within your [locations](../concepts/locations.md) with a translation plugin of your choice. If you're utilizing Vue.js as your frontend framework, you can use the i18n plugin. Additionally, to ensure consistency between your app and the Shopware Administration, you can synchronize language changes by [subscribing to them through the context API](../api-reference/context.md#subscribe-on-language-changes). - -For text elements in native Shopware Administration components, such as titles within [component sections](../concepts/component-sections.md), you can employ snippet files within your app. This is supported since the Shopware Version 6.6. Here's a how to accomplish this: - -1. **Create Snippet Files:** Begin by generating a snippet file for each supported language within your app. These files should reside in the `Resources/app/administration/snippet` directory. Naming conventions follow the language code format, for instance, `en-GB.json` for English language support. The file structure mirrors that of administration snippets. However it is not impossible to overwrite Shopware Administration snippets. - -```json -// /Resources/app/administration/snippet/en-GB.json -{ - "my-app-name": { - "example-card": { - "title": "My app", - "subtitle": "This is my app" - } - } -} -``` - -2. **Integrate Snippets:** Utilize these snippets within your app by referencing their paths directly in your code. For example: - -```js -sw.ui.componentSection('sw-manufacturer-card-custom-fields__before').add({ - component: 'card', - props: { - title: 'my-app-name.example-card.title', - subtitle: 'my-app-name.example-card.subtitle', - locationId: 'my-app-card-before-properties' - } -}) -``` diff --git a/docs/admin-sdk/getting-started/devTools.md b/docs/admin-sdk/getting-started/devTools.md deleted file mode 100644 index 5eec0f712..000000000 --- a/docs/admin-sdk/getting-started/devTools.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "Vue dev tools" -sidebar_position: 2 ---- - -# Vue dev tools - -## Prerequisites -We assume that you got the [Vue dev tools](https://devtools.vuejs.org/) installed for your browser. -The extension is available for Chrome, Firefox, Edge and as a standalone app. - -## Development setup -Furthermore you should have [Shopware](https://github.com/shopware/platform) setup. -To make use of the Extension API plugin for the Vue dev tools start the watcher of the administration: - -```bash -$ composer run watch:admin -``` - -## Vue dev tool plugin -Once you logged into your administration, open up the development tools of your browser and choose the Vue tab. -Inside the Vue tab choose the Shopware Extension API plugin. - -![Devtools plugin](./assets/devtools-plugin.png) - -## Usage -The plugin will show you all extension points for the current page you visit. -Once you select an extension point it will highlight the corresponding area in the viewport and give detailed information how to extend the highlighted property. - -![Devtools usage](./assets/devtools-usage.png) \ No newline at end of file diff --git a/docs/admin-sdk/getting-started/index.md b/docs/admin-sdk/getting-started/index.md index 3deb6836a..74544ff1a 100644 --- a/docs/admin-sdk/getting-started/index.md +++ b/docs/admin-sdk/getting-started/index.md @@ -1,5 +1,52 @@ --- -title: "Getting started" +title: "Getting Started" nav: position: 10 --- + +# Getting Started + +This guide helps you set up the Meteor Admin SDK and start building extensions for the Shopware Administration. + +If you want to learn what the SDK is and what it can do first, see the [Introduction](../introduction.md). + +## Choose your extension type + +Shopware supports two extension types: **apps** and **plugins**. Both can use the Meteor Admin SDK. + +### Apps + +Apps run on an external server and communicate with Shopware through a defined API. They are the recommended approach because: + +- They work with **Shopware Cloud** and self-hosted instances, and they are the only extension type available for **Shopware SaaS** +- The frontend and backend are fully decoupled from the Shopware codebase + +Set up an app: [App Installation Flow](./installation-apps.md) + +### Plugins + +Plugins run directly inside the Shopware instance. They have full access to the Shopware PHP codebase but are limited to **self-hosted** environments. + +Set up a plugin: [Plugin Installation Flow](./installation-plugins.md) + +### Migrating an existing plugin + +If you already have a Twig-based Administration plugin and want to adopt the Meteor Admin SDK incrementally, see the [Migration Guide](../develop/migrating-admin-plugins.md). + +## Install the SDK + +```bash +npm install @shopware-ag/meteor-admin-sdk +``` + +This is the recommended setup. It enables bundling and tree-shaking so only the code you use is shipped. + +If you want to use a CDN instead, see [Without npm](./without-npm.md). + +## Next steps + +- [App Installation Flow](./installation-apps.md): Full walkthrough for app setup +- [Plugin Installation Flow](./installation-plugins.md): Full walkthrough for plugin setup +- [Without npm](./without-npm.md): Alternative setup using a plain ` + + +``` + +Create `demo-app/meteor-app/src/main.js`: + +```js +import { notification } from "@shopware-ag/meteor-admin-sdk"; + +notification.dispatch({ + title: "Meteor Admin SDK installed", + message: "Your app is connected successfully", +}); +``` + +### 3. Mount Vite on the app server + +Instead of serving a standalone HTML file, let the Hono app server serve the Vite frontend under `/admin/`. + +In the scaffolded `demo-app/index.ts`, replace the default server startup `serve(...)` with a custom HTTP server that forwards `/admin` requests to Vite and everything else to Hono: + +```ts +import { readFileSync } from "node:fs"; +import { createServer } from "node:http"; +import { getRequestListener } from "@hono/node-server"; + +// Keep your existing Hono app and configureAppServer(...) setup above. + +const PORT = 3000; + +async function startServer() { + const honoListener = getRequestListener(app.fetch); + const { createServer: createViteServer } = await import("vite"); + + const httpServer = createServer(); + + const vite = await createViteServer({ + root: "./meteor-app", + base: "/admin/", + appType: "custom", + server: { + middlewareMode: true, + hmr: { server: httpServer }, + }, + }); + + httpServer.on("request", (req, res) => { + if (req.url?.startsWith("/admin")) { + vite.middlewares(req, res, async () => { + try { + let html = readFileSync("./meteor-app/index.html", "utf-8"); + html = await vite.transformIndexHtml(req.url, html); + res.writeHead(200, { "Content-Type": "text/html" }); + res.end(html); + } catch (error) { + console.error(error); + res.writeHead(500); + res.end(error instanceof Error ? error.message : "Unknown error"); + } + }); + return; + } + + honoListener(req, res); + }); + + httpServer.listen(PORT, () => { + console.log(`App server running at http://localhost:${PORT}`); + console.log(`Administration frontend: http://localhost:${PORT}/admin/`); + }); +} + +void startServer(); +``` + +This setup serves the Administration entry page at `/admin/`. Because Vite runs in middleware mode on the same path, requests for JavaScript modules, CSS, `@vite/client`, and HMR also work without adding separate static routes. + +### 4. Register the Administration page in `manifest.xml` + +After the registration handshake is working, add the `` field inside the `` section of the [manifest file](https://developer.shopware.com/docs/guides/plugins/apps/app-base-guide#manifest-file). It must point to the path where Vite serves the frontend, so in this example that is `/admin/`. + +As required by Shopware's app system, the `` section must already contain the `registrationUrl` and `secret`. + +:::tip Docker reminder +Remember: the `registrationUrl` must be reachable by the Shopware server. If Shopware runs in Docker, use `host.docker.internal` for that URL. The `base-app-url` is loaded by the browser, so `localhost` works fine there. +::: + +Create the file at `/custom/apps/MyExampleApp/manifest.xml` in your Shopware installation. Example for a local dev setup (app server running on port 3000): + +```xml + + + + MyExampleApp + + This is my first example app + Developer + (c) Developer + 1.0.0 + MIT + + + + + http://host.docker.internal:3000/app/register + S3cr3tf0re$t + + + + + http://localhost:3000/admin/ + + +``` + +The `` and `` in the manifest must match the `appName` and `appSecret` in your app server. Open `demo-app/index.ts` and update the `configureAppServer` call to match: + +```ts +configureAppServer(app, { + appName: "MyExampleApp", + appSecret: "S3cr3tf0re$t", + shopRepository: new BetterSqlite3Repository("shop.db"), +}); +``` + +If name or secret don't match between manifest and app server, the registration handshake will fail. + +### 5. Start the dev server + +```bash +npm start +``` + +When the server starts successfully, the Administration frontend should be reachable at `http://localhost:3000/admin/`. + +### 6. Install and activate the app + +```bash +# if you are using Docker, run the following commands inside the container: docker compose exec -it web /bin/bash +bin/console app:install --activate MyExampleApp +bin/console cache:clear +``` + +### 7. Verify installation + +Log in to the Shopware Administration. The notification should appear in the top-right corner on any page (the notification is not bound to a specific module — it appears on every page load). + +## Next steps + +- Explore the [API Reference](../api-reference/index.md) for all available SDK features +- Learn about [Concepts](../concepts/index.md) like locations, positions, and data handling +- See the [Usage Guide](./usage.md) for more detailed examples diff --git a/docs/admin-sdk/getting-started/installation-plugins.md b/docs/admin-sdk/getting-started/installation-plugins.md new file mode 100644 index 000000000..9cccc165c --- /dev/null +++ b/docs/admin-sdk/getting-started/installation-plugins.md @@ -0,0 +1,93 @@ +--- +title: "Plugin Installation Flow" +sidebar_position: 30 +--- + +# Plugin Installation Flow + +Plugins are supported on self-hosted Shopware instances only. + +:::info +This guide assumes **Shopware 6.7 or later**. Shopware 6.7 introduced a new extension architecture (`meteor-app`) with modern frontend build tooling. If you are running an older version, differences are noted inline. +::: + +### 1. Create the administration entry + +Create the folder `custom/plugins/yourPluginName/src/Resources/app/meteor-app`. This is the base path for all new files for your extension. + +:::info Shopware below 6.7 +Use the path `custom/plugins/yourPluginName/src/Resources/app/administration` instead. +::: + +### 2. Install the SDK + +```bash +cd custom/plugins/yourPluginName/src/Resources/app/meteor-app +npm install @shopware-ag/meteor-admin-sdk +``` + +### 3. Implement your entry file + +Create a new base `index.html` file. Shopware loads this file as a hidden iframe when the plugin is activated. + +For the underlying iframe-based architecture, see [Architecture](../concepts/architecture.md) and [Locations](../concepts/locations.md). + +Then create a JavaScript file in the subfolder `src/main.js` and reference it in the `index.html`: + +```html + + + + + + Your extension + + +
+ + + +``` + +:::info Shopware below 6.7 +Leave out `` — it is injected automatically in older versions. +::: + +In `src/main.js`, add a quick test to verify the SDK works: + +```js +import { notification } from "@shopware-ag/meteor-admin-sdk"; + +notification.dispatch({ + title: "Hello from your plugin", + message: "Meteor Admin SDK is working", +}); +``` + +### 4. Install the plugin + +```bash +# if you are using Docker, run the following commands inside the container: docker compose exec -it web /bin/bash +bin/console plugin:install --activate yourPluginName +bin/console cache:clear +``` + +### 5. Build and watch + +You don't need to set up Vite on your own — Shopware already takes care of bundling. Rerun the Administration watcher to rebuild the frontend: + +```bash +composer watch:admin +``` + +Wait until the compilation finishes successfully. + +### 6. Verify installation + +Log in to the Administration. A notification should appear in the top-right corner. + +## Next steps + +- Explore the [API Reference](../api-reference/index.md) for all available SDK features +- Learn about [Concepts](../concepts/index.md) like locations, positions, and data handling +- See the [Usage Guide](./usage.md) for more detailed examples diff --git a/docs/admin-sdk/getting-started/installation.md b/docs/admin-sdk/getting-started/installation.md deleted file mode 100644 index 5bee2811a..000000000 --- a/docs/admin-sdk/getting-started/installation.md +++ /dev/null @@ -1,229 +0,0 @@ ---- -title: "Installation" -sidebar_position: 1 ---- - -# Installation - -## Prerequisites: - -You need to have an working [app](https://developer.shopware.com/docs/guides/plugins/apps/app-base-guide) or [plugin](https://developer.shopware.com/docs/guides/plugins/plugins/plugin-base-guide) installed on your Shopware 6 instance. - -## Prepare your app or plugin - -### App: - -You need to create a HTML page with an JS file for your app. This page needs to be served by your app-server as it needs to be accesible via URL. -For development purposes you can use [App server sdk](https://github.com/shopware/app-sdk-js). - -Once you got the registration/ handshake working you need to add the `` field to the `` section of the [manifest](https://developer.shopware.com/docs/guides/plugins/apps/app-base-guide#manifest-file) file. This field should contain the public URL of your app. Let's assume your app HTML page is served under `http://localhost/my-example-app.html`: - -```xml - - - - MyExampleApp - - - - - http://link-to-your-local-app-server/register - S3cr3tf0re$t - - - - - http://localhost/my-example-app.html - - -``` - -In your new HTML file you need inject a JS file. This file can use the Meteor Admin SDK via CDN or if you want to use a build tools then you -can use the NPM package. - -### Plugin: -**Notice:** Plugins will work on self-hosted instances only. You won't be able to use a Shopware 6 cloud instance with plugins. - -#### For Shopware 6.7 and higher: -Create the folder `custom/plugins/yourPlugin/src/Resources/app/meteor-app`. This is the base path for all new files -for your extension. - -Create a new base `index.html` file. This file will be automatically injected as a hidden iFrame to the administration -when the plugin is activated. Then you need to create a JavaScript file in the subfolder `src/main.js` and -add it to your `index.html`: - -```html - - - - - - - Your extension - - -
- - - -``` - -Then you initialize a new Node project with `npm init --yes` and install the SDK via NPM -with `npm i --save @shopware-ag/meteor-admin-sdk`. - -If you want to have a custom Vite configuration you can install Vite with `npm i --save vite` and -create a `vite.config.js` file in the `meteor-app` folder with your custom configuration. - -This should result in a folder structure like this: -```plaintext -custom/plugins/yourPlugin/src/Resources/app/meteor-app -├── index.html -├── vite.config.js (optional) -├── package.json -├── package-lock.json -├── src -│ ├── main.js -``` - -#### For Shopware 6.6 and lower: -Open the path `custom/plugins/yourPlugin/src/Resources/app/administration`. This is the base path for all new admin files. - -Create a new base `index.html` file. This file will be automatically injected to the administration when the plugin is -activated. Then you need to create a JavaScript file in the subfolder `src/main.js`. This file will be automatically -injected into the created HTML file. - -For plugins the best way is to install the SDK via NPM. But first you need to initialize a new NPM project in your plugin folder with -`npm init --yes`. - -This should result in a folder structure like this: -```plaintext -custom/plugins/yourPlugin/src/Resources/app/administration -├── index.html -├── package.json -├── package-lock.json -├── src -│ ├── main.js -``` - -## Installing the SDK: - -The preferred way of using the library is with a NPM package. This guarantees the smallest bundle size for your apps and plugins, since this way only necessary functions are bundled together. - -The CDN method is easy to use and fast to implement. It is best used for quick prototyping or if you don't want to work with building tools. - -### Using NPM (require bundling): -Install it to your `package.json` -``` -npm i --save @shopware-ag/meteor-admin-sdk -``` - -and import it into your app or plugin: -```js -// import everything as one big object -import * as sw from '@shopware-ag/meteor-admin-sdk'; - -// or import only needed functionality scope -import { notification } from '@shopware-ag/meteor-admin-sdk'; - -// or the direct method (here with an alias) -import { dispatch as dispatchNotification } from '@shopware-ag/meteor-admin-sdk/es/notification' - -``` - -### Using CDN: -Import the source from the CDN - -```js -// use the latest version available - - -// use a fix version (example here: 1.2.3) - -``` - -and access it with the global variable `sw`. - -```js -sw.notification.dispatch({ - title: 'My first notification', - message: 'This was really easy to do' -}) -``` - -## Adding types for Entities (TS only) - -The data management inside the SDK supports complete TypeScript support. This allows complete type safety when getting -entities, editing or saving them. - -For adding the types you need to create a global type definition file like `global.d.ts`. Inside this file you can -add the types for the entities by extending the global namespace. - -### Using auto-generated types from Shopware -This is the easiest solution. Just install the correct type definition for the matching shopware version: - -`npm install @shopware-ag/entity-schema-types@5.0.0` - -The version number should match the Shopware version number without the `6.` in the beginning. Examples: - -`Shopware 6.5.0.0` → `@shopware-ag/entity-schema-types@5.0.0` -`Shopware 6.5.1.2` → `@shopware-ag/entity-schema-types@5.1.2` -`Shopware 6.6.3.1` → `@shopware-ag/entity-schema-types@6.3.1` - -```ts -// global.d.ts -import '@shopware-ag/entity-schema-types'; -``` - -### Using "any" fallback - -This is the easiest solution. You set the type to `any` for every entity. The downside of this is the missing type safety. - -```ts -// global.d.ts -declare namespace EntitySchema { - interface Entities { - [entityName: string]: any; - } -} -``` - -### Using custom types - -This is the safest solution. You define for every needed entity every property and association. The downside of this is -that it takes time to write the definitions. - -```ts -// global.d.ts -declare namespace EntitySchema { - interface Entities { - // using product_manufacturer as an example - product_manufacturer: product_manufacturer; - // in this case 'media', 'product' and 'product_manufacturer_translation' is also needed - ... - } - - interface product_manufacturer { - id: string; - versionId: string; - mediaId?: string; - link?: string; - name: string; - description?: string; - customFields?: unknown; - /* - * Entity and EntityCollection is defined in the namespace and can directly be used. - * The value in the generic (here 'media', 'product' and 'product_manufacturer_translation') need - * also to be defined in this file. - */ - media?: Entity<'media'>; - products?: EntityCollection<'product'>; - translations: EntityCollection<'product_manufacturer_translation'>; - createdAt: string; - updatedAt?: string; - translated?: {name?: string, description?: string, customFields?: unknown}; - } - - // 'media', 'product' and 'product_manufacturer_translation' also needs to be added - ... -} -``` \ No newline at end of file diff --git a/docs/admin-sdk/getting-started/usage.md b/docs/admin-sdk/getting-started/usage.md index 09db0849c..00aa53847 100644 --- a/docs/admin-sdk/getting-started/usage.md +++ b/docs/admin-sdk/getting-started/usage.md @@ -1,119 +1,78 @@ --- -sidebar_position: 3 +title: "Usage" +sidebar_position: 40 --- # Usage -After [installing](./installation) the Meteor Admin SDK successfully you can use it in your apps and plugins. +After installing the Meteor Admin SDK, you can use it in your extensions. Most extensions should use the NPM package with a bundler such as Vite. This provides proper dependency management, type support, and better integration with modern development workflows. -## Adding functionality to new apps or plugins -You can use the SDK features directly in your JS file. Just import the specific feature (NPM method) or use the method in the -`sw` object (CDN method). You can find all features in the API reference documentation. +## Show a notification + +One of the simplest SDK interactions is showing a notification in the Shopware Administration: -### NPM example: ```js // import notification toolkit from the SDK -import { notification } from '@shopware-ag/meteor-admin-sdk'; +import { notification } from '@shopware-ag/meteor-admin-sdk'; // dispatch a new notification notification.dispatch({ title: 'My first notification', message: 'This was really easy to do' -}) +}); ``` -### CDN example: +## Add a UI extension + +Use UI APIs to place your extension inside existing Administration views: + ```js -// access the "notification" toolkit in the global "sw" object and dispatch a new notification -sw.notification.dispatch({ - title: 'My first notification', - message: 'This was really easy to do' -}) +import { ui } from '@shopware-ag/meteor-admin-sdk'; + +ui.componentSection.add({ + component: 'card', + positionId: 'sw-product-properties__before', + props: { + title: 'Extra product details', + locationId: 'product-extra-details' + } +}); ``` +See [Component Sections](../api-reference/ui/component-sections.md) and [Locations](../concepts/locations.md) for the full flow. -## Adding functionality to existing plugins -Shopware 6 has a rich plugin extension system for the Admin based on Twig and the concepts of component overriding and component extending. These -concepts are very powerful, but may also come with a steep learning curve. That's why you can migrate gradually to the new Meteor Admin SDK, if you want. -Both approaches can work together. This way you can start by converting only parts of your plugins at first and then gradually converting more and more of your plugins as new features are added to the SDK. -This approach is also going to help with simplifying your plugins and preparing them for long term usage. +## Read Administration context -#### Example: +Use context APIs when your extension needs information about the current Administration session: ```js -// Use existing extension capabilties -Shopware.Component.override('sw-dashboard-index', { - methods: { - async createdComponent() { - // Can also use Meteor Admin SDK features - await sw.notification.dispatch({ - title: 'Hello from the plugin', - message: 'I am combining the existing approach with the new SDK approach', - }) - - this.$super('createdComponent'); - } - } -}); -``` +import { context } from '@shopware-ag/meteor-admin-sdk'; -### Using locations with normal Vue components without iFrame rendering +const language = await context.getLanguage(); +console.log(language.languageId); +``` -**This feature is not yet released in Shopware. -It's only available with the development enviroment or `dev-trunk` version of Shopware.** +See the [Context API reference](../api-reference/context.md) for all available methods. -It is useful when you want to migrate partially from the twig plugin system to the SDK extension system that you use both systems together. To make this happen you can render normal Vue components in the Shopware administration for the locations instead of your iFrame view. +## Subscribe to data changes -To do this you need to register the component in the existing plugin system: +Use data APIs when your extension should react to entity changes in the current view: ```js -Shopware.Component.register('your-component-name', { - // your component -}) -``` +import { data } from '@shopware-ag/meteor-admin-sdk'; -Now if you want to render the component in a location you need to add the name of the component to the current location. This can be done with the `sdkLocation` store: -```js -Shopware.State.commit('sdkLocation/addLocation', { - locationId: 'your-location-id', - componentName: 'your-component-name' -}) +data.subscribe('sw-product-detail', (payload) => { + console.log('Product data changed', payload); +}); ``` -With this feature you can create mix the usage of the SDK and the existing plugin system. A complete example could be looking like this. It creates a new tab item in the product detail page, renders a card with the componentSection renderer and inside the card it renders the location. But instead of the traditional location it renders a Vue component which was registered in the Shopware Component Factory. +See [Subscribe](../api-reference/data/subscribe.md), [Get](../api-reference/data/get.md), and [Repository](../api-reference/data/repository.md) for more data access patterns. -```js -// in a normal plugin js file without a HTML file -import { ui, location } from '@shopware-ag/meteor-admin-sdk'; - -if (!location.isIframe()) { - const myLocationId = 'my-example-location-id'; - - // Create a new tab entry - ui.tabs('sw-product-detail').addTabItem({ - label: 'Example tab', - componentSectionId: 'example-product-detail-tab-content' - }) - - // Add a new card to the tab content which renders a location - ui.componentSection.add({ - component: 'card', - positionId: 'example-product-detail-tab-content', - props: { - title: 'Component section example', - locationId: myLocationId - } - }) - - // Register your component which should be rendered inside the location - Shopware.Component.register('your-component-name', { - // your component - }) - - // Add the component name to the specific location - Shopware.State.commit('sdkLocation/addLocation', { - locationId: myLocationId, - componentName: 'your-component-name' - }) -} -``` +## Find more examples + +For more complete examples and API details, continue with: + +- [API Reference](../api-reference/index.md) +- [Concepts](../concepts/index.md) +- [App Installation Flow](./installation-apps.md) +- [Plugin Installation Flow](./installation-plugins.md) diff --git a/docs/admin-sdk/getting-started/without-npm.md b/docs/admin-sdk/getting-started/without-npm.md new file mode 100644 index 000000000..70bb29c95 --- /dev/null +++ b/docs/admin-sdk/getting-started/without-npm.md @@ -0,0 +1,67 @@ +--- +title: "Without npm" +sidebar_position: 35 +--- + + +# Without npm + +If you want to try the Meteor Admin SDK without setting up npm or a bundler, you can load it directly with a ` +``` + +This exposes the SDK globally as `sw`, for example `sw.notification.dispatch(...)`. + +## Plugins + +In a plugin, you can place the script tag directly into your `index.html` and skip `npm install @shopware-ag/meteor-admin-sdk`. + +```html + + + + + + Your extension + + + + + + + +``` + +## Apps + +In an app, you can also load the SDK directly in `my-example-app.html`. This lets you skip the bundling step from the app installation flow. + +```html + + + + + + + + + + + +``` + +Apps still need the rest of the normal setup: the app server, the HTML file being served, and the `manifest.xml` registration. diff --git a/docs/admin-sdk/index.md b/docs/admin-sdk/index.md index e2b4b06f1..67b3b21d7 100644 --- a/docs/admin-sdk/index.md +++ b/docs/admin-sdk/index.md @@ -1,91 +1,19 @@ --- id: "index" -title: "Introduction" +title: "Meteor Admin SDK" slug: "/guide/" -sidebar_label: "Introduction" +sidebar_label: "Meteor Admin SDK" sidebar_position: 0.5 custom_edit_url: null --- -# Introduction +# Meteor Admin SDK -The Meteor Admin SDK is an NPM library for Shopware 6 apps and plugins that need an easy way of extending or customizing the Administration. +The Meteor Admin SDK is an npm library for extending the Shopware Administration with custom UI, data access, and integrations — for both apps and plugins. -It contains helper functions to communicate with the Administration, execute actions, subscribe to data or extend the user interface. - -- 🏗 **Works with Shopware 6 Apps and Plugins:** you can use the SDK for your plugins or apps. API usage is identical. -- 🎢 **Shallow learning curve:** you don't need to have extensive knowledge about the internals of the Shopware 6 Administration. Our SDK hides the complicated stuff behind a beautiful API. -- 🧰 **Many extension capabilities:** from throwing notifications, accessing context information or extending the current UI. The feature set of the SDK will gradually be extended, providing more possibilities and flexibility for your ideas and solutions. -- 🪨 **A stable API with great backwards compatibility:** don't fear Shopware updates anymore. Breaking changes in this SDK are an exception. If you use the SDK, your apps and plugins will stay stable for a longer time, without any need for code maintenance. -- 🧭 **Type safety:** the whole SDK is written in TypeScript which provides great autocompletion support and more safety for your apps and plugins. -- 💙 **Developer experience:** have a great development experience right from the start. And it will become better and better in the future. -- 🪶 **Lightweight:** the whole library is completely tree-shakable and dependency-free. Every functionality can be imported granularly to keep your bundle as small and fast as possible. - -Go to [Installation](./getting-started/installation.md) to get started. Or check out the quick start guide: - -## Quick start - -Understand the Shopware Extension SDK by learning how to throw a notification. - -Requirements for this quick start guide are: -- [Shopware 6 self-hosted instance](https://developer.shopware.com/docs/guides/installation) or a [Shopware 6 cloud instance](https://www.shopware.com/en/shopware-cloud/) -- [clean Shopware 6 Plugin or App](https://developer.shopware.com/docs/guides/plugins/overview) which is activated - -### App -1. Create an HTML file with following content: -```html - - - - - - - - - - - -``` - -2. Add the link to the webpage and to the [manifest.xml](https://developer.shopware.com/docs/guides/plugins/apps/app-base-guide#manifest-file) of your app. For local files you can use [ngrok](https://ngrok.com/) to create a public URL for your HTML file. - -3. Visit the Administration. After you have logged in you should see the notification from your app. - -Congratulation 🎉 You just created your first interaction with the Administration via the Meteor Admin SDK. - -### Plugin -**Notice:** Plugins will only be working on self-hosted instances. You can't use a Shopware 6 cloud instance for plugins. - -1. Create a new `index.html` file to your new plugin in the following path: `custom/plugins/yourPlugin/src/Resources/app/administration/index.html`. The HTML file should have the following content: -```html - - - - - - - - - - - -``` - -2. Start the Shopware 6 Administration watcher using the following command: -```bash -$ bin/watch-administration.sh -``` - -After all files have been compiled, a new browser window should open, in which you should see the Administration. After logging in, you should see the notification from your plugin. - -Congratulations 🎉 You just created your first interaction with the Administration via the Meteor Admin SDK. +- [Introduction](./introduction.md): What the SDK does, why to use it, and prerequisites +- [Getting Started](./getting-started/index.md): Choose your setup (app or plugin) and install the SDK +- [Concepts](./concepts/index.md): Locations, positions, component sections, selectors, and data handling +- [Start Developing](./develop/index.md): DevTools, translations, entity types, and migration guides +- [API Reference](./api-reference/index.md): Full reference for all SDK methods and components +- [Changelog](./CHANGELOG.md) diff --git a/docs/admin-sdk/internals/index.md b/docs/admin-sdk/internals/index.md deleted file mode 100644 index 6e5beed3e..000000000 --- a/docs/admin-sdk/internals/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: "Internals" -nav: - position: 1000 ---- \ No newline at end of file diff --git a/docs/admin-sdk/introduction.md b/docs/admin-sdk/introduction.md new file mode 100644 index 000000000..925dc8474 --- /dev/null +++ b/docs/admin-sdk/introduction.md @@ -0,0 +1,36 @@ +--- +title: "Introduction" +nav: + position: 1 +--- + + +# Introduction + +The Meteor Admin SDK is an npm library for building Shopware Administration UI extensions. It enables [apps](https://developer.shopware.com/docs/guides/plugins/apps/) and [plugins](https://developer.shopware.com/docs/guides/plugins/plugins/) to extend the Shopware Administration through a stable, typed API that runs in the browser context. + +What you can do with the SDK: + +- Build custom Administration modules and context-aware UI extensions +- Extend the Administration UI with notifications, modals, tabs, and more +- Access and modify entity data through the Administration data layer +- Create entity-driven workflows and admin-driven integrations with external services + +## Why use the SDK + +- Provides a stable, backwards-compatible API for extending the Administration and reducing complexity during Shopware updates +- Abstracts internal complexity, so deep knowledge of the Admin internals is not required +- Written in [TypeScript](https://www.typescriptlang.org/) with full type safety and auto-completion support +- Lightweight and tree-shakable, allowing granular imports and small bundle sizes + +## Prerequisites + +Using the Meteor Admin SDK requires: + +- A Shopware instance +- A working [app](https://developer.shopware.com/docs/guides/plugins/apps/app-base-guide) or [plugin](https://developer.shopware.com/docs/guides/plugins/plugins/plugin-base-guide) installed in Shopware 6 +- The SDK installed via npm (recommended) or CDN + +## Next steps + +See the [Getting Started guide](./getting-started/index.md) to choose your setup and start building. diff --git a/docs/admin-sdk/tooling/index.md b/docs/admin-sdk/tooling/index.md deleted file mode 100644 index 3a4905d48..000000000 --- a/docs/admin-sdk/tooling/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: "Tooling" -nav: - position: 75 ---- \ No newline at end of file diff --git a/docs/admin-sdk/tooling/vue-devtools.md b/docs/admin-sdk/tooling/vue-devtools.md deleted file mode 100644 index d73127c2a..000000000 --- a/docs/admin-sdk/tooling/vue-devtools.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: "Vue Devtools" -sidebar_position: 1 ---- - -# Vue Devtools -The administration has many extension capabilties. Many of them are components with an unique positionId. It can be -difficult to find out their id to extend them. You need to manually look in the core source code to find out the id. - -The better way is using the Vue devtools plugin. It is preinstalled in every Shopware administration so that you can -find out the ids in an interactive and visual way. - -## Prerequisites: -You need to have the Vue Devtools installed. The plugin API for the Vue Devtools is only available in the versions 6+ (Currently only in the [beta channel](https://chrome.google.com/webstore/detail/vuejs-devtools/ljjemllljcmogpfapbkkighbhhppjdbg). You can install both parallel.). If you are using an older version then this plugin will not work. - -After installing the browser extensions you should be able to open the devtools in the development/watch mode of the administration. To check if the admin plugin works you can go to the settings and check if the Shopware Admin plugin is installed and enabled: - -![Vue Devtools plugin settings](./assets/devtools-plugin-settings.png) - -![Vue Devtools plugin settings list](./assets/devtools-plugin-settings-list.png) - -## Finding extension capabilites -Navigate to the page which you want to extend. In our example we go to the product detail page in the tab specifications. - -Now you can open the plugin in the top dropdown menu: - -![Vue Devtools plugin tab Shopware extension](./assets/devtools-plugin-tab-shopware-extension.png) - -You should see a list on the left side where all extension capabilities are listed. If you click on any of them you will directly see them highlighted in the administration. - -![Vue Devtools plugin extension point selection](./assets/devtools-plugin-extension-point-selection.png) - -In the inspector of the devtools you will see more information about the extension point. You can see the `Property` value which you can look in the API Reference documentation. Then you know how to use your selected extension point and which capabilities are available. And in most cases you need the `positionId` which is also shown in the inspector. The positionId is a unique identifer so that you extend not every area but only your selected one. - diff --git a/examples/admin-sdk-plugin/README.md b/examples/admin-sdk-plugin/README.md index 8c01956ad..79f241275 100644 --- a/examples/admin-sdk-plugin/README.md +++ b/examples/admin-sdk-plugin/README.md @@ -3,6 +3,7 @@ This package contains an example plugin. It uses the [Meteor Admin SDK](https://github.com/shopware/meteor/tree/main/packages/admin-sdk) to extend the administration. ## Prerequisites + We assume that you have a functioning Shopware 6 setup on your local machine. ## Plugin setup @@ -21,5 +22,3 @@ Now you should see the plugin installed when opening the Shopware Admin and look 1. Create a `.env` file in `/examples/admin-sdk-plugin/tests/acceptance` 2. Specify your Shopware instance app url: `APP_URL=https://dev.local/` 3. Run the tests: `cd && pnpm --filter @shopware-ag/meteor-admin-sdk-example-plugin run test:ats` - - diff --git a/review/resolved.md b/review/resolved.md new file mode 100644 index 000000000..dd27525fa --- /dev/null +++ b/review/resolved.md @@ -0,0 +1,196 @@ +# Resolved Review Comments + +PR: [shopware/meteor#1075](https://github.com/shopware/meteor/pull/1075) + +Items are moved here after todos from a comment are completed. + +- [ ] = pending human verification +- [x] = verified by human, ready for GH thread resolution + +## Already Resolved on GitHub + +### #2953581776 + +**File:** `docs/admin-sdk/getting-started/index.md` | [View on GitHub](https://github.com/shopware/meteor/pull/1075#discussion_r2953581776) + +> These two links are relative paths and therefore are referencing to a non existing file. Remove the redundant `getting-started/` prefix + +**Status:** Resolved on GitHub prior to this process. + + +### #2953586858 + +**File:** `docs/admin-sdk/develop/migrating-admin-plugins.md` | [View on GitHub](https://github.com/shopware/meteor/pull/1075#discussion_r2953586858) + +> Typo `capabilities` + +**Status:** Resolved on GitHub prior to this process. + + +### #2953601227 + +**File:** `docs/admin-sdk/develop/index.md` | [View on GitHub](https://github.com/shopware/meteor/pull/1075#discussion_r2953601227) + +> Several files that contain pure API reference material (parameters, return values, usage signatures) were moved from `api-reference/` into `develop/`: + +- `develop/context.md` +- [...] + +**Status:** Resolved on GitHub prior to this process. + + +### #2953609821 + +**File:** `docs/admin-sdk/faq/index.md` | [View on GitHub](https://github.com/shopware/meteor/pull/1075#discussion_r2953609821) + +> Where was this content moved to? These are very important information we should not lose or delete + +**Status:** Resolved on GitHub prior to this process. + + +### #2953656853 + +**File:** `docs/admin-sdk/getting-started/index.md` | [View on GitHub](https://github.com/shopware/meteor/pull/1075#discussion_r2953656853) + +> It doesn't need to be in the entry file. You need to import it where you need it. + +**Status:** Resolved on GitHub prior to this process. + + +## Resolved During This Process + +### `docs/admin-sdk/api-reference/cms/index.md` + +- [x] **#2966698897** — Set CMS position to 300. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966698897) + +### `docs/admin-sdk/api-reference/composables/index.md` + +- [x] **#2966697591** — Set composables position to 400. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966697591) +- [x] **#2966712098** — Updated composable description to mention Vue Composables. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966712098) +- [x] **#2966741402** — Renamed title to 'Vue Composables'. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966741402) + +### `docs/admin-sdk/api-reference/composables/useRepository.md` + +- [ ] **#2966728289** — Recreated getRepository documentation page and re-added getRepository references in useRepository "How it works" section with code example. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966728289) + +### `docs/admin-sdk/api-reference/context.md` + +- [ ] **#2974427282** — Added overall introduction and brief descriptions for each context section. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2974427282) + +### `docs/admin-sdk/api-reference/data/get.md` + +- [ ] **#2966845584** — Replaced with data handling guide link. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966845584) + +### `docs/admin-sdk/api-reference/data/index.md` + +- [ ] **#2966696276** — Set data position to 200. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966696276) + +### `docs/admin-sdk/api-reference/data/repository.md` + +- [ ] **#2966818177** — Renamed to repository.search(). [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966818177) +- [ ] **#2966819560** — Renamed to repository.get(). [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966819560) +- [ ] **#2966824040** — Renamed to repository.save(). [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966824040) +- [ ] **#2966825119** — Renamed to repository.clone(). [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966825119) +- [ ] **#2966826682** — Renamed to repository.hasChanges(). [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966826682) +- [ ] **#2966828136** — Renamed to repository.saveAll(). [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966828136) +- [ ] **#2966829119** — Renamed to repository.delete(). [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966829119) +- [ ] **#2966830321** — Renamed to repository.create(). [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966830321) + +### `docs/admin-sdk/api-reference/data/subscribe.md` + +- [ ] **#2965086703** — Rewrote subscribe description with data handling guide link. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965086703) + +### `docs/admin-sdk/api-reference/data/update.md` + +- [ ] **#2965091293** — Rewrote update description with data handling guide link. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965091293) + +### `docs/admin-sdk/api-reference/index.md` + +- [ ] **#2966197662** — Applied suggestion. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966197662) +- [ ] **#2966371533** — Changed to 'Trigger an In-App Purchase'. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966371533) +- [ ] **#2974303496** — Applied h3→h4 for Usage/Parameters/Return across all API reference files (70+ changes). [View](https://github.com/shopware/meteor/pull/1075#discussion_r2974303496) +- [ ] **#2974341610** — Changed heading to '## Extending the UI'. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2974341610) + +### `docs/admin-sdk/api-reference/notification.md` + +- [ ] **#2974454354** — Rewrote to clarify notifications persist in notification center but can be dismissed. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2974454354) + +### `docs/admin-sdk/api-reference/toast.md` + +- [ ] **#2974466629** — Added explanation of toast vs notification difference. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2974466629) +- [ ] **#2974470481** — Fixed icon column formatting. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2974470481) +- [ ] **#2974757040** — Converted availability heading to info box. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2974757040) + +### `docs/admin-sdk/api-reference/ui/component-sections.md` + +- [ ] **#2966156633** — Removed redundant 'Example' heading. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966156633) +- [ ] **#2966162956** — Changed to 'Example: Add a component to the product page'. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966162956) +- [ ] **#2966175089** — Changed to 'Example: Add tabs to the card'. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966175089) +- [ ] **#2966178429** — Removed redundant 'Example' heading. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966178429) + +### `docs/admin-sdk/api-reference/ui/index.md` + +- [ ] **#2966693593** — Set UI position to 100. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966693593) +- [ ] **#2974343217** — Renamed title to 'Extending the UI'. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2974343217) + +### `docs/admin-sdk/api-reference/ui/mainModule.md` + +- [ ] **#2966914130** — Changed to addMainModule(). [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966914130) +- [ ] **#2966916677** — Changed to addSmartbarButton(). [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966916677) +- [ ] **#2966919080** — Changed to hideSmartBar(). [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966919080) + +### `docs/admin-sdk/api-reference/ui/mediaModal.md` + +- [ ] **#2973864833** — Changed to ui.mediaModal.open() with updated description. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2973864833) +- [ ] **#2974288657** — Same as above — both suggestions targeted same line. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2974288657) + +### `docs/admin-sdk/concepts/component-sections.md` + +- [ ] **#2974917759** — Updated to 'render custom UI components inside predefined extension points'. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2974917759) + +### `docs/admin-sdk/concepts/datahandling.md` + +- [ ] **#2974938338** — Fixed typo ('Sdministration' → 'Administration') and improved wording. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2974938338) + +### `docs/admin-sdk/concepts/index.md` + +- [ ] **#2966184142** — Set concepts position to 20. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966184142) + +### `docs/admin-sdk/develop/index.md` + +- [ ] **#2975080837** — Removed broken links to api-reference files; simplified to list only develop/ files. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2975080837) + +### `docs/admin-sdk/getting-started/index.md` + +- [ ] **#2965625728** — Getting Started position (10) is already above Concepts (20). [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965625728) + +### `docs/admin-sdk/getting-started/installation-apps.md` + +- [ ] **#2965277799** — Clarified backend is required, not optional. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965277799) +- [ ] **#2965286416** — Changed to 'frontend layer for the administration'. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965286416) +- [ ] **#2965295630** — Added Cross-Origin Security explanation and dev tip with Docker note. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965295630) +- [ ] **#2965308179** — Rewrote App Server SDK description. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965308179) +- [ ] **#2965321387** — Removed 'across runtimes' link. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965321387) +- [ ] **#2965329814** — Replaced with npx tiged scaffold command. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965329814) +- [ ] **#2965338350** — Clarified first two steps are mandatory, third is optional. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965338350) +- [ ] **#2965344679** — Added php-sdk recommendation and official SDK list link. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965344679) +- [ ] **#2965460313** — Added manifest path and install commands. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965460313) +- [ ] **#2965462871** — Clarified notification appears on every page load. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965462871) +- [ ] **#2965766407** — Added Docker host.docker.internal note in dev tip. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965766407) +- [ ] **#2966119941** — Added Next Steps section with links to API Reference, Concepts, and Usage. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966119941) + +### `docs/admin-sdk/getting-started/installation-plugins.md` + +- [ ] **#2965470006** — Restructured to assume 6.7+ with collapsible <6.7 details. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965470006) +- [ ] **#2965629922** — Updated folder path with <6.7 details block. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965629922) +- [ ] **#2965670128** — Added install command with correct cd path. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965670128) +- [ ] **#2965693461** — Added plugin install commands, bundling explanation, and <6.7 details. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965693461) +- [ ] **#2965699891** — Changed to 'Log in to the Administration. A notification should appear...' [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965699891) +- [ ] **#2965773310** — Removed legacy 6.6 section; consolidated into single flow with spoilers. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965773310) +- [ ] **#2966121229** — Added Next Steps section. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966121229) +- [ ] **#2966688213** — Added info box about 6.7 version differences. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966688213) + +### `docs/admin-sdk/index.md` + +- [ ] **#2965812015** — Combined 'What you can build' and 'What you can do' into single section. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2965812015) +- [ ] **#2966139483** — Replaced 'Go here' with explicit link to Getting Started guide. [View](https://github.com/shopware/meteor/pull/1075#discussion_r2966139483)