commerce-app-admin-ui
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseConfigure Commerce App Admin UI
配置Commerce应用Admin UI
Adds or modifies the block in an existing .
The Admin UI extension point () lets a Commerce app extend the Commerce Admin with custom grid columns, mass actions, order view buttons, and a menu entry.
Other extensibility domains (webhooks, events, business config) are added separately via their own skills.
adminUiapp.commerce.config.tscommerce/backend-ui/2添加或修改现有中的块。
Admin UI扩展点()允许Commerce应用通过自定义网格列、批量操作、订单视图按钮和菜单条目来扩展Commerce Admin。
其他扩展领域(webhooks、事件、业务配置)需通过各自的技能单独添加。
app.commerce.config.tsadminUicommerce/backend-ui/2Prerequisites
前提条件
- Verify the app is scaffolded and initialized, not merely that the config exists. Require both:
- present in the project root, and
app.commerce.config.ts - the project initialized — signalled by the generated directory and installed
src/commerce-extensibility-1/(thenode_modulesdependency).@adobe/aio-commerce-lib-app
- If is missing, stop and invoke
app.commerce.config.tsfirst (it writes the config, then runs init).commerce-app-init - If the config is present but the project is not initialized (no or
src/commerce-extensibility-1/), runnode_modulesbefore continuing. Init is idempotent — it finds the existing config, skips the interactive prompts, installs dependencies, and generates the project files.npx @adobe/aio-commerce-lib-app init - Actions can be authored in TypeScript only once the project has the TypeScript build setup (+ root
webpack-config.cjs) thattsconfig.jsonscaffolds for a TypeScript Commerce config — seeinit. Otherwise, author actions in JavaScript.commerce-app-init
- 验证应用已完成脚手架搭建并初始化,而不仅仅是存在配置文件。需同时满足以下两点:
- 项目根目录下存在,并且
app.commerce.config.ts - 项目已初始化——标志为生成了目录且已安装
src/commerce-extensibility-1/(包含node_modules依赖)。@adobe/aio-commerce-lib-app
- 项目根目录下存在
- 如果缺少,请先停止操作并调用
app.commerce.config.ts(该工具会写入配置文件,然后执行初始化)。commerce-app-init - 如果配置文件存在但项目未初始化(无或
src/commerce-extensibility-1/),请先运行node_modules再继续。初始化操作是幂等的——它会找到现有配置,跳过交互式提示,安装依赖并生成项目文件。npx @adobe/aio-commerce-lib-app init - 只有当项目具备为TypeScript Commerce配置搭建的TypeScript构建环境(
init+ 根目录webpack-config.cjs)时,才能使用TypeScript编写动作——详情请查看tsconfig.json。否则,请使用JavaScript编写动作。commerce-app-init
Extension points at a glance
扩展点概览
| Extension point | Entities | Variants | Server handler | Reference |
|---|---|---|---|---|
| Grid columns | order, product, customer | worker only | yes | grid-columns |
| Mass actions | order, product, customer | view / worker | worker only | mass-actions |
| Order view buttons | order only | view / worker | worker only | order-view-buttons |
| Menu | single entry ( | view (iframe) | no | menu |
viewweb-srcpathworker| 扩展点 | 实体类型 | 变体类型 | 服务器处理程序 | 参考文档 |
|---|---|---|---|---|
| Grid columns | order、product、customer | worker only | 是 | grid-columns |
| Mass actions | order、product、customer | view / worker | worker only | mass-actions |
| Order view buttons | order only | view / worker | worker only | order-view-buttons |
| Menu | 单个条目( | view (iframe) | 否 | menu |
viewpathweb-srcworkerStep 1 — Understand intent
步骤1 — 明确需求
For each thing the user wants to add, gather:
- Which extension point — grid columns, mass actions, order view buttons, or menu
- Which entity — ,
order, orproduct(grid columns and mass actions; view buttons are order-only; menu has no entity)customer - For mass actions and view buttons, the variant — (runtime action) or
worker(iframe intoview)web-src - The fields for that extension point (column definitions, button labels, menu parent, etc.) — see the reference file in the table above for the full field set
针对用户想要添加的每个功能,收集以下信息:
- 扩展点类型——grid columns、mass actions、order view buttons或menu
- 实体类型——、
order或product(grid columns和mass actions支持这些实体;view buttons仅支持order;menu无实体)customer - 对于mass actions和view buttons,指定变体类型——(运行时动作)或
worker(嵌入view的iframe)web-src - 该扩展点所需的字段(列定义、按钮标签、菜单父项等)——完整字段集请查看上方表格中的参考文档
Step 2 — Declare in app.commerce.config.ts
app.commerce.config.ts步骤2 — 在app.commerce.config.ts
中声明
app.commerce.config.tsAdd (or merge into) the top-level block, preserving all other domains. If already exists, merge into it rather than replacing — keep existing entities, the menu, and existing array entries.
adminUiadminUiThese fields are shared across the extension points:
| Field | Constraint |
|---|---|
| |
| Optional |
| Optional |
| Optional array (view/iframe entries); non-empty, no duplicates; each one of |
Minimal example covering each feature (use only the parts you need):
ts
import { MENU_SALES } from "@adobe/aio-commerce-sdk/admin-ui/menu";
// inside defineConfig({ ... }):
adminUi: {
order: {
// Custom column on the order grid (worker only)
gridColumns: {
label: "Fulfillment data",
description: "Fulfillment status from the warehouse system.",
runtimeAction: "my-app/order-grid", // <package>/<action> — declare in Step 4
columns: [
{ id: "fulfillment_status", label: "Fulfillment", type: "string", align: "left" },
],
},
// Bulk action on selected orders (worker variant shown)
massActions: [
{ type: "worker", id: "archive-orders", label: "Archive",
runtimeAction: "my-app/archive-orders", selectionLimit: 500 },
],
// Button on the order view page (worker variant shown)
viewButtons: [
{ type: "worker", id: "sync-inventory", label: "Sync inventory",
runtimeAction: "my-app/sync-inventory" },
],
},
// Custom Admin menu entry (iframe into the app)
menu: {
id: "my_app_dashboard", // letters, digits, / : _ only
label: "My Dashboard",
description: "Custom dashboard for my app.",
parentMenu: MENU_SALES,
},
}For the variants (iframe) and the complete field set and constraints of each extension point, read the matching reference file under References before writing.
view添加(或合并到)顶层的块,保留所有其他领域的配置。如果已存在,请合并配置而非替换——保留现有实体、菜单以及现有数组条目。
adminUiadminUi以下字段为各扩展点共享:
| 字段 | 约束条件 |
|---|---|
| |
| 可选的 |
| 可选的 |
| 可选数组(适用于view/iframe条目);非空且无重复项;每个值为 |
涵盖各功能的极简示例(仅使用你需要的部分):
ts
import { MENU_SALES } from "@adobe/aio-commerce-sdk/admin-ui/menu";
// inside defineConfig({ ... }):
adminUi: {
order: {
// 订单网格上的自定义列(仅worker变体)
gridColumns: {
label: "Fulfillment data",
description: "Fulfillment status from the warehouse system.",
runtimeAction: "my-app/order-grid", // <package>/<action> — 在步骤4中声明
columns: [
{ id: "fulfillment_status", label: "Fulfillment", type: "string", align: "left" },
],
},
// 选中订单的批量操作(展示worker变体)
massActions: [
{ type: "worker", id: "archive-orders", label: "Archive",
runtimeAction: "my-app/archive-orders", selectionLimit: 500 },
],
// 订单视图页面上的按钮(展示worker变体)
viewButtons: [
{ type: "worker", id: "sync-inventory", label: "Sync inventory",
runtimeAction: "my-app/sync-inventory" },
],
},
// 自定义管理菜单条目(嵌入应用的iframe)
menu: {
id: "my_app_dashboard", // 仅允许字母、数字、/ : _
label: "My Dashboard",
description: "Custom dashboard for my app.",
parentMenu: MENU_SALES,
},
}对于view变体(iframe)以及各扩展点的完整字段集和约束条件,请在编写前阅读参考文档中的对应文档。
Step 3 — Register the extension point
步骤3 — 注册扩展点
Run init so that is added to and , and the extension folder is generated. This is idempotent — safe to run even if the extension is already registered.
commerce/backend-ui/2app.config.yamlinstall.yamlsrc/commerce-backend-ui-2/sh
npx @adobe/aio-commerce-lib-app initThe build derives the extension's from your config: each worker becomes a operation, and when you declare a or any -type entry, a operation plus an explicit key are written. Those , , and sections are managed by the library — do not hand-edit them.
ext.config.yamladminUiruntimeActionworkerProcessmenuviewviewweb: web-srchooksoperationswebWhen a operation is present, init/generate also scaffolds the web frontend automatically (skipped if already exists). It generates — , , , ( plus an independent when the app config is TypeScript) — adds the import alias to , and declares and installs pinned versions of , , , and (React and Spectrum S2 are optional peer dependencies of the admin-ui library), plus some for proper TypeScript support/config. TypeScript scaffolds also add to the project’s composed script. Do not hand-pick different versions of these dependencies; the scaffold fails if incompatible versions are already installed.
viewweb-src/index.htmlsrc/commerce-backend-ui-2/web-src/index.htmlsrc/app.jsxsrc/pages/main-page.jsxsrc/components/welcome.jsx.tsxtsconfig.json#web/*package.jsonreactreact-dom@react-spectrum/s2@adobe/aio-commerce-lib-admin-uidevDependenciestypecheck:web-srctypecheckIf the scaffold is skipped because already exists, check its for classic React Spectrum ( or without ) instead of . The two are compatible, but S2 is the version Adobe recommends moving to and the one this scaffold targets — suggest upgrading. This skill only configures the Admin UI extension, it doesn't drive the upgrade itself, so point the user to the skill for the actual migration:
web-srcpackage.json@adobe/react-spectrum@react-spectrum/<component>s2@react-spectrum/s2commerce-app-migratesh
npx skills add adobe/skills --skill commerce-app-migrateDo not install it or perform the upgrade yourself unless the user asks.
运行init命令,将添加到和中,并生成扩展文件夹。此操作是幂等的——即使扩展已注册,运行该命令也是安全的。
commerce/backend-ui/2app.config.yamlinstall.yamlsrc/commerce-backend-ui-2/sh
npx @adobe/aio-commerce-lib-app init构建过程会从你的配置生成扩展的:每个worker类型的会变为操作;当你声明menu或任何view类型的条目时,会写入一个操作以及显式的键。这些、和部分由库管理——请勿手动编辑。
adminUiext.config.yamlruntimeActionworkerProcessviewweb: web-srchooksoperationsweb当存在view操作时,init/generate会自动搭建Web前端(如果已存在则跳过)。它会生成——包含、、、(当应用配置为TypeScript时,会生成文件以及独立的)——将导入别名添加到中,并声明安装、、和的固定版本(React和Spectrum S2是admin-ui库的可选对等依赖),同时添加一些以支持TypeScript配置。TypeScript脚手架还会将添加到项目的组合脚本中。请勿手动选择这些依赖的其他版本;如果已安装不兼容的版本,脚手架会失败。
web-src/index.htmlsrc/commerce-backend-ui-2/web-src/index.htmlsrc/app.jsxsrc/pages/main-page.jsxsrc/components/welcome.jsx.tsxtsconfig.json#web/*package.jsonreactreact-dom@react-spectrum/s2@adobe/aio-commerce-lib-admin-uidevDependenciestypecheck:web-srctypecheck如果因为已存在而跳过脚手架,请检查其中是否使用了经典React Spectrum(或不带的)而非。两者兼容,但S2是Adobe推荐迁移的版本,也是此脚手架的目标版本——建议进行升级。本技能仅配置Admin UI扩展,不负责驱动升级本身,因此请引导用户使用技能进行实际迁移:
web-srcpackage.json@adobe/react-spectrums2@react-spectrum/<component>@react-spectrum/s2commerce-app-migratesh
npx skills add adobe/skills --skill commerce-app-migrate除非用户要求,否则请勿自行安装或执行升级操作。
Step 4 — Implement the handlers
步骤4 — 实现处理程序
What you implement depends on the variant. Examples below are in TypeScript; if the project uses JavaScript, omit type imports and annotations.
你需要实现的内容取决于变体类型。以下示例为TypeScript版本;如果项目使用JavaScript,请省略类型导入和注解。
Worker variants (grid columns, worker mass actions, worker view buttons)
Worker变体(grid columns、worker类型mass actions、worker类型view buttons)
Each worker needs an action you declare and implement, inside the Admin UI extension folder .
runtimeActionsrc/commerce-backend-ui-2/- Put the handler source under .
src/commerce-backend-ui-2/actions/ - Declare it in under your own package in
src/commerce-backend-ui-2/ext.config.yaml(any package name). The build managesruntimeManifestandhooksin this file but preserves the packages you add underoperations, so your action survives rebuilds:runtimeManifest
yaml
undefined每个worker类型的都需要你在Admin UI扩展文件夹中声明并实现一个动作。
runtimeActionsrc/commerce-backend-ui-2/- 将处理程序源码放在下。
src/commerce-backend-ui-2/actions/ - 在的
src/commerce-backend-ui-2/ext.config.yaml下,在你自己的包中声明该动作(包名任意)。构建过程会管理此文件中的runtimeManifest和hooks,但会保留你在operations下添加的包,因此你的动作会在重建后保留:runtimeManifest
yaml
undefinedsrc/commerce-backend-ui-2/ext.config.yaml
src/commerce-backend-ui-2/ext.config.yaml
runtimeManifest:
packages:
my-app: # must match the <package> in runtimeAction
actions:
order-grid:
function: actions/order-grid/index.js # relative to src/commerce-backend-ui-2/
web: "yes"
runtime: nodejs:24
annotations:
require-adobe-auth: true # Commerce calls the action with an IMS token — validate it
final: true
The `<package>/<action>` in `runtimeAction` maps directly: `my-app/order-grid` → package `my-app`, action `order-grid`. Commerce invokes these worker actions with an Adobe IMS token, so they need `require-adobe-auth: true` (and `final: true` to lock the bound inputs).
Implement the handler with the wire-contract builders from the `@adobe/aio-commerce-sdk/admin-ui/*` entrypoints.
The builders differ per extension point — the reference file gives the exact request shape and response builders. Grid columns example:
```typescript
// src/commerce-backend-ui-2/actions/order-grid/index.ts
import {
parseGridRequest,
okGridResponse,
errorGridResponse,
} from "@adobe/aio-commerce-sdk/admin-ui/grid-columns";
import type { RuntimeActionParams } from "@adobe/aio-commerce-sdk/core/params";
export async function main(params: RuntimeActionParams) {
const { gridType, ids } = parseGridRequest(params);
try {
const rows = await fetchRows(gridType, ids);
// row keys must match the column ids declared in config
return okGridResponse(rows, { fulfillment_status: "unknown" });
} catch (error) {
return errorGridResponse(
500,
error instanceof Error ? error.message : String(error),
);
}
}runtimeManifest:
packages:
my-app: # 必须与runtimeAction中的<package>匹配
actions:
order-grid:
function: actions/order-grid/index.js # 相对于src/commerce-backend-ui-2/
web: "yes"
runtime: nodejs:24
annotations:
require-adobe-auth: true # Commerce会使用IMS令牌调用该动作——请验证令牌
final: true
`runtimeAction`中的`<package>/<action>`会直接映射:`my-app/order-grid` → 包`my-app`,动作`order-grid`。Commerce会使用Adobe IMS令牌调用这些worker动作,因此它们需要`require-adobe-auth: true`(以及`final: true`以锁定绑定输入)。
使用`@adobe/aio-commerce-sdk/admin-ui/*`入口点的 wire-contract builders实现处理程序。
不同扩展点的builders不同——参考文档给出了确切的请求格式和响应builders。Grid columns示例:
```typescript
// src/commerce-backend-ui-2/actions/order-grid/index.ts
import {
parseGridRequest,
okGridResponse,
errorGridResponse,
} from "@adobe/aio-commerce-sdk/admin-ui/grid-columns";
import type { RuntimeActionParams } from "@adobe/aio-commerce-sdk/core/params";
export async function main(params: RuntimeActionParams) {
const { gridType, ids } = parseGridRequest(params);
try {
const rows = await fetchRows(gridType, ids);
// 行键必须与config中声明的列id匹配
return okGridResponse(rows, { fulfillment_status: "unknown" });
} catch (error) {
return errorGridResponse(
500,
error instanceof Error ? error.message : String(error),
);
}
}View variants (view mass actions, view buttons, menu)
View变体(view类型mass actions、view类型buttons、menu)
No server handler. Commerce opens an iframe into the app's at the entry's — and that frontend was generated for you in Step 3 (, , , ). The generated mounts the iframe app with from , and its array must start with the index route:
web-srcpathindex.htmlsrc/app.jsxsrc/pages/main-page.jsxsrc/components/welcome.jsxsrc/app.jsxcommerce/backend-ui/2createExtensionApp@adobe/aio-commerce-lib-admin-ui/webroutesjsx
// src/commerce-backend-ui-2/web-src/src/app.jsx (generated)
import { createExtensionApp } from "@adobe/aio-commerce-lib-admin-ui/web";
import "@react-spectrum/s2/page.css";
import config from "#app.commerce.config";
import { MainPage } from "#web/pages/main-page.jsx";
createExtensionApp({
metadata: { extensionId: config.metadata.id },
routes: [{ index: true, element: <MainPage /> }],
});Just as the worker variants wire a to an action, each entry's must be wired to a page and a route. Scaffold that wiring per entry — but only create what is missing. Never overwrite or modify an existing page or route.
runtimeActionviewpathThe menu has no : it renders at the index route (), which the scaffold already created as a plain page. There is nothing to scaffold for the menu — leave in place (customize its content if you like). It needs only the constant for in the config (see menu).
pathsrc/pages/main-page.jsxmain-page.jsxMENU_*parentMenuFor each -type mass action and order view button (both carry a ):
viewpath-
Check for the route. Look infor a
src/app.jsxentry whoseroutesequals the entry's configpath, and inpathfor its page file. If a route for thatweb-src/src/pages/already exists, leave it and its page untouched and move on.path -
Create the placeholder page (missing only). Add— use
web-src/src/pages/<name>.jsx(and TypeScript) when the app config is TypeScript. Keep it minimal, matching the look of the generated.tsx/main-page(awelcomewith a heading). Do not add a copyright header — the generated<main>files carry none. Pre-wire the context hook for the entry type (table below).web-src -
Register the route in(missing only). Import the page via the
src/app.jsxalias and append a#web/pages/*entry to{ path, element }. Writeroutesas the exact same string as the configpath, including the leadingpath— copy it verbatim so the route and the config entry visibly line up. Keep the index route first.#/
Pre-wire the hook by view type — all from :
@adobe/aio-commerce-lib-admin-ui/web| View entry | Context hook | Also | Reference |
|---|---|---|---|
Mass action ( | | | mass-actions |
| Order view button | | | order-view-buttons |
| Menu | none (plain index page) | — | menu |
These hooks return errors instead of throwing them. A route can throw a returned error during render to send it to the SDK's error boundary, which replaces the extension content with its fallback UI. If the route must stay mounted, handle the error locally by rendering a message, offering a retry, disabling the affected feature, or providing another degraded state. The placeholder examples below throw because they don't define custom recovery UI.
A route component may also call and to reach the Commerce REST API or retrieve data directly from the host, respectively. Both return ; after handling , read and from , and from . The generated component demonstrates this result handling for . Add those hooks only when the page actually needs them.
useIms()useCommerce(){ data, error }errordata.imsTokendata.imsOrgIduseIms()data.commerceHostuseCommerce()WelcomeuseIms()Example — a mass action placeholder page and its route registration:
viewjsx
// src/commerce-backend-ui-2/web-src/src/pages/export-customers.jsx
import {
useHostConnection,
useMassActionContext,
} from "@adobe/aio-commerce-lib-admin-ui/web";
export function ExportCustomersPage() {
const { data, error: contextError } = useMassActionContext();
const { actions, error: hostError } = useHostConnection();
if (contextError) throw contextError;
if (hostError) throw hostError;
const { selectedIds } = data; // non-empty string[] — the selected record ids
const { close } = actions; // await close() (or actions.closeWithError()) when done
return (
<main>
<h1>Export customers</h1>
<p>{selectedIds.length} selected</p>
</main>
);
}jsx
// src/commerce-backend-ui-2/web-src/src/app.jsx
import { createExtensionApp } from "@adobe/aio-commerce-lib-admin-ui/web";
import "@react-spectrum/s2/page.css";
import config from "#app.commerce.config";
import { MainPage } from "#web/pages/main-page.jsx";
import { ExportCustomersPage } from "#web/pages/export-customers.jsx";
createExtensionApp({
metadata: { extensionId: config.metadata.id },
routes: [
{ index: true, element: <MainPage /> }, // keep the index route first
{ path: "#/export-customers", element: <ExportCustomersPage /> }, // path === config `path`
],
});For an order view button, swap the hook for and read after handling — see order-view-buttons.
useOrderViewButtonContext()data.orderIderror无需服务器处理程序。Commerce会在条目指定的位置,将iframe嵌入应用的中——该前端已在步骤3中为你生成(、、、)。生成的会使用中的挂载 iframe应用,其数组必须以索引路由开头:
pathweb-srcindex.htmlsrc/app.jsxsrc/pages/main-page.jsxsrc/components/welcome.jsxsrc/app.jsx@adobe/aio-commerce-lib-admin-ui/webcreateExtensionAppcommerce/backend-ui/2routesjsx
// src/commerce-backend-ui-2/web-src/src/app.jsx (generated)
import { createExtensionApp } from "@adobe/aio-commerce-lib-admin-ui/web";
import "@react-spectrum/s2/page.css";
import config from "#app.commerce.config";
import { MainPage } from "#web/pages/main-page.jsx";
createExtensionApp({
metadata: { extensionId: config.metadata.id },
routes: [{ index: true, element: <MainPage /> }],
});就像worker变体将连接到动作一样,每个view条目的必须连接到页面和路由。为每个条目搭建此连接——但仅创建缺失的内容。切勿覆盖或修改现有页面或路由。
runtimeActionpathMenu没有:它在索引路由()处渲染,脚手架已将其创建为普通页面。无需为Menu搭建额外内容——保留即可(可根据需要自定义其内容)。它仅需要config中对应的常量(请查看menu)。
pathsrc/pages/main-page.jsxmain-page.jsxparentMenuMENU_*对于每个view类型的mass action和order view button(均包含):
path-
检查路由是否存在。在中查找
src/app.jsx与条目配置中path相等的path条目,并在routes中查找对应的页面文件。如果该web-src/src/pages/的路由已存在,请保留其页面并继续下一步。path -
创建占位页面(仅当缺失时)。添加——当应用配置为TypeScript时,使用
web-src/src/pages/<name>.jsx(并使用TypeScript)。保持页面简洁,与生成的.tsx/main-page外观一致(包含welcome和标题)。请勿添加版权头——生成的<main>文件不包含版权头。根据条目类型预连接上下文钩子(如下表所示)。web-src -
在中注册路由(仅当缺失时)。通过
src/app.jsx别名导入页面,并将#web/pages/*条目追加到{ path, element }中。routes需与配置中的path完全相同,包括开头的path——直接复制该字符串,使路由和配置条目清晰对应。保持索引路由在首位。#/
根据view类型预连接钩子——所有钩子均来自:
@adobe/aio-commerce-lib-admin-ui/web| View条目 | 上下文钩子 | 补充说明 | 参考文档 |
|---|---|---|---|
Mass action( | | | mass-actions |
| Order view button | | | order-view-buttons |
| Menu | 无(普通索引页面) | — | menu |
这些钩子会返回错误而非抛出错误。路由在渲染期间可以抛出返回的错误,将其发送到SDK的错误边界,错误边界会用回退UI替换扩展内容。如果路由必须保持挂载,请在本地处理错误,例如渲染提示信息、提供重试选项、禁用受影响的功能或提供其他降级状态。以下占位示例会抛出错误,因为它们未定义自定义恢复UI。
路由组件也可以调用和来访问Commerce REST API或直接从主机获取数据。两者均返回;处理完后,从读取和,从读取。生成的组件展示了的结果处理方式。仅当页面实际需要时才添加这些钩子。
useIms()useCommerce(){ data, error }erroruseIms()data.imsTokendata.imsOrgIduseCommerce()data.commerceHostWelcomeuseIms()示例——view类型mass action的占位页面及其路由注册:
jsx
// src/commerce-backend-ui-2/web-src/src/pages/export-customers.jsx
import {
useHostConnection,
useMassActionContext,
} from "@adobe/aio-commerce-lib-admin-ui/web";
export function ExportCustomersPage() {
const { data, error: contextError } = useMassActionContext();
const { actions, error: hostError } = useHostConnection();
if (contextError) throw contextError;
if (hostError) throw hostError;
const { selectedIds } = data; // 非空字符串数组——选中的记录id
const { close } = actions; // 完成后调用await close()(或actions.closeWithError())
return (
<main>
<h1>Export customers</h1>
<p>{selectedIds.length} selected</p>
</main>
);
}jsx
// src/commerce-backend-ui-2/web-src/src/app.jsx
import { createExtensionApp } from "@adobe/aio-commerce-lib-admin-ui/web";
import "@react-spectrum/s2/page.css";
import config from "#app.commerce.config";
import { MainPage } from "#web/pages/main-page.jsx";
import { ExportCustomersPage } from "#web/pages/export-customers.jsx";
createExtensionApp({
metadata: { extensionId: config.metadata.id },
routes: [
{ index: true, element: <MainPage /> }, // 保持索引路由在首位
{ path: "#/export-customers", element: <ExportCustomersPage /> }, // path === 配置中的`path`
],
});对于order view button,将钩子替换为,处理完后读取——请查看order-view-buttons。
useOrderViewButtonContext()errordata.orderIdStep 5 — Validate
步骤5 — 验证
Build the project to confirm the updated config is valid:
sh
aio app buildA build failure with a validation error points directly to the offending field.
adminUi构建项目以确认更新后的配置有效:
sh
aio app build如果构建失败并出现验证错误,会直接指向有问题的字段。
adminUiCommon Issues
常见问题
- vs
viewmismatch: each variant is a strict object — aworkerentry requiresworker(and rejectsruntimeAction/path); asandboxPermissionsentry requiresview(and rejectspath/runtimeAction). They are discriminated bytimeout.type - Grid columns are worker-only: there is no grid column. Only
view,order, andproductsupportcustomer, and onlygridColumnssupportsorder.viewButtons - with no handler: a worker entry whose
runtimeActionis not declared under<package>/<action>inruntimeManifestleaves the generatedsrc/commerce-backend-ui-2/ext.config.yamlreference unresolved at deploy.workerProcess - Wrong action location: handler sources and their entry belong in the Admin UI extension folder
runtimeManifest— notsrc/commerce-backend-ui-2/(where webhook and event handlers live). Thesrc/commerce-extensibility-1/path is relative tofunction.src/commerce-backend-ui-2/ - Grid row keys must match column ids: keys in the rows must equal the
okGridResponses inid, or cells render empty (or fall back to the defaults bag).gridColumns.columns - View route : register the
pathin{ path }as the exact same string as the entry's configsrc/app.jsx— copy it verbatim, hash included, so the two line up.path - Menu charset: the menu
idallows only letters, digits,id,/, and:— no hyphens or spaces._ - not found: import
defineConfigfromdefineConfig.@adobe/aio-commerce-lib-app/config - Double renders/requests in development: wraps the app in React
createExtensionApp, so under<StrictMode>oraio app devcomponents render twice and effects run an extra setup + cleanup cycle on mount. Duplicate renders or effect-triggered requests in development are expected StrictMode behavior, not a bug to fix; production builds are unaffected.aio app run
- 与
view不匹配:每个变体都是严格的对象——worker条目需要worker(且不允许runtimeAction/path);view条目需要sandboxPermissions(且不允许path/runtimeAction)。它们通过timeout区分。type - Grid columns仅支持worker变体:不存在view类型的grid column。仅、
order和product支持customer,且仅gridColumns支持order。viewButtons - 无对应处理程序:如果worker条目的
runtimeAction未在<package>/<action>的src/commerce-backend-ui-2/ext.config.yaml下声明,会导致部署时生成的runtimeManifest引用无法解析。workerProcess - 动作位置错误:处理程序源码及其条目应放在Admin UI扩展文件夹
runtimeManifest中——而非src/commerce-backend-ui-2/(webhook和事件处理程序所在位置)。src/commerce-extensibility-1/路径是相对于function的。src/commerce-backend-ui-2/ - 网格行键必须与列id匹配:行中的键必须与
okGridResponse中的gridColumns.columns相等,否则单元格会显示为空(或回退到默认值)。id - View路由:在
path中注册的src/app.jsx必须与条目配置中的{ path }完全相同——直接复制该字符串,包括哈希符号,使两者保持一致。path - Menu 字符集:Menu的
id仅允许字母、数字、id、/和:——不允许连字符或空格。_ - 找不到:从
defineConfig导入@adobe/aio-commerce-lib-app/config。defineConfig - 开发环境中重复渲染/请求:会将应用包裹在React
createExtensionApp中,因此在<StrictMode>或aio app dev下,组件会渲染两次,副作用会在挂载时额外执行一次设置+清理周期。开发环境中的重复渲染或副作用触发的请求是StrictMode的预期行为,并非bug;生产构建不受影响。aio app run
Quality Bar
质量标准
- completes without errors
aio app build - Every worker has a matching action declared under
runtimeActioninruntimeManifestsrc/commerce-backend-ui-2/ext.config.yaml
- 无错误完成
aio app build - 每个worker类型的都在
runtimeAction的src/commerce-backend-ui-2/ext.config.yaml下有对应的动作声明runtimeManifest
Chaining
后续操作
After passes:
aio app build- Add merchant settings — invoke to expose configurable settings in Commerce Admin
commerce-app-business-config - Add webhook interceptors — invoke to intercept Commerce operations
commerce-app-webhooks - Add event subscriptions — invoke to subscribe to Commerce or external events
commerce-app-eventing - Add persistent storage — invoke to back worker actions with queryable DB storage
commerce-app-storage
在通过后:
aio app build- 添加商家设置——调用以在Commerce Admin中公开可配置的设置
commerce-app-business-config - 添加webhook拦截器——调用以拦截Commerce操作
commerce-app-webhooks - 添加事件订阅——调用以订阅Commerce或外部事件
commerce-app-eventing - 添加持久化存储——调用为worker动作提供可查询的数据库存储支持
commerce-app-storage
References
参考文档
- references/grid-columns.md — Grid column config and handler contract
- references/mass-actions.md — Mass action config (view and worker) and handler contract
- references/order-view-buttons.md — Order view button config (view and worker) and handler contract
- references/menu.md — Menu config and parent-menu constants
- references/grid-columns.md — 网格列配置和处理程序契约
- references/mass-actions.md — 批量操作配置(view和worker)和处理程序契约
- references/order-view-buttons.md — 订单视图按钮配置(view和worker)和处理程序契约
- references/menu.md — 菜单配置和父菜单常量