@open-mercato/core 0.5.1-develop.3036.f02c281f23 → 0.5.1-develop.3043.1a796c3920
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +13 -1
- package/dist/helpers/integration/api.js +29 -16
- package/dist/helpers/integration/api.js.map +2 -2
- package/dist/helpers/integration/auth.js +11 -6
- package/dist/helpers/integration/auth.js.map +3 -3
- package/dist/modules/auth/commands/roles.js +9 -12
- package/dist/modules/auth/commands/roles.js.map +2 -2
- package/dist/modules/catalog/ai-agents-context.js +147 -0
- package/dist/modules/catalog/ai-agents-context.js.map +7 -0
- package/dist/modules/catalog/ai-agents.js +383 -0
- package/dist/modules/catalog/ai-agents.js.map +7 -0
- package/dist/modules/catalog/ai-tools/_shared.js +318 -0
- package/dist/modules/catalog/ai-tools/_shared.js.map +7 -0
- package/dist/modules/catalog/ai-tools/authoring-pack.js +391 -0
- package/dist/modules/catalog/ai-tools/authoring-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/categories-pack.js +167 -0
- package/dist/modules/catalog/ai-tools/categories-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/configuration-pack.js +120 -0
- package/dist/modules/catalog/ai-tools/configuration-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/media-tags-pack.js +107 -0
- package/dist/modules/catalog/ai-tools/media-tags-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/merchandising-pack.js +429 -0
- package/dist/modules/catalog/ai-tools/merchandising-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/mutation-pack.js +576 -0
- package/dist/modules/catalog/ai-tools/mutation-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/prices-offers-pack.js +208 -0
- package/dist/modules/catalog/ai-tools/prices-offers-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/products-pack.js +298 -0
- package/dist/modules/catalog/ai-tools/products-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/stats-pack.js +57 -0
- package/dist/modules/catalog/ai-tools/stats-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools/types.js +10 -0
- package/dist/modules/catalog/ai-tools/types.js.map +7 -0
- package/dist/modules/catalog/ai-tools/variants-pack.js +75 -0
- package/dist/modules/catalog/ai-tools/variants-pack.js.map +7 -0
- package/dist/modules/catalog/ai-tools.js +28 -0
- package/dist/modules/catalog/ai-tools.js.map +7 -0
- package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js +466 -0
- package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js.map +7 -0
- package/dist/modules/catalog/backend/catalog/products/page.js +7 -1
- package/dist/modules/catalog/backend/catalog/products/page.js.map +2 -2
- package/dist/modules/catalog/components/CatalogStatsCard.js +91 -0
- package/dist/modules/catalog/components/CatalogStatsCard.js.map +7 -0
- package/dist/modules/catalog/components/products/ProductsDataTable.js +23 -3
- package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
- package/dist/modules/catalog/events.js +7 -4
- package/dist/modules/catalog/events.js.map +2 -2
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js +59 -0
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js.map +7 -0
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.js +17 -0
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.js.map +7 -0
- package/dist/modules/catalog/widgets/injection/product-seo/widget.client.js +1 -1
- package/dist/modules/catalog/widgets/injection/product-seo/widget.client.js.map +2 -2
- package/dist/modules/catalog/widgets/injection-table.js +13 -1
- package/dist/modules/catalog/widgets/injection-table.js.map +2 -2
- package/dist/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.client.js +94 -0
- package/dist/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.client.js.map +7 -0
- package/dist/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.js +17 -0
- package/dist/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.js.map +7 -0
- package/dist/modules/customer_accounts/widgets/injection-table.js +9 -0
- package/dist/modules/customer_accounts/widgets/injection-table.js.map +2 -2
- package/dist/modules/customers/ai-agents-context.js +96 -0
- package/dist/modules/customers/ai-agents-context.js.map +7 -0
- package/dist/modules/customers/ai-agents.js +244 -0
- package/dist/modules/customers/ai-agents.js.map +7 -0
- package/dist/modules/customers/ai-tools/activities-tasks-pack.js +1015 -0
- package/dist/modules/customers/ai-tools/activities-tasks-pack.js.map +7 -0
- package/dist/modules/customers/ai-tools/addresses-tags-pack.js +134 -0
- package/dist/modules/customers/ai-tools/addresses-tags-pack.js.map +7 -0
- package/dist/modules/customers/ai-tools/companies-pack.js +249 -0
- package/dist/modules/customers/ai-tools/companies-pack.js.map +7 -0
- package/dist/modules/customers/ai-tools/deals-pack.js +348 -0
- package/dist/modules/customers/ai-tools/deals-pack.js.map +7 -0
- package/dist/modules/customers/ai-tools/people-pack.js +261 -0
- package/dist/modules/customers/ai-tools/people-pack.js.map +7 -0
- package/dist/modules/customers/ai-tools/settings-pack.js +102 -0
- package/dist/modules/customers/ai-tools/settings-pack.js.map +7 -0
- package/dist/modules/customers/ai-tools/types.js +10 -0
- package/dist/modules/customers/ai-tools/types.js.map +7 -0
- package/dist/modules/customers/ai-tools.js +20 -0
- package/dist/modules/customers/ai-tools.js.map +7 -0
- package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js +469 -0
- package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js.map +7 -0
- package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js +17 -0
- package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js.map +7 -0
- package/dist/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.client.js +117 -0
- package/dist/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.client.js.map +7 -0
- package/dist/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.js +17 -0
- package/dist/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.js.map +7 -0
- package/dist/modules/customers/widgets/injection-table.js +26 -0
- package/dist/modules/customers/widgets/injection-table.js.map +7 -0
- package/dist/modules/inbox_ops/ai-tools.js +4 -0
- package/dist/modules/inbox_ops/ai-tools.js.map +2 -2
- package/dist/modules/inbox_ops/lib/llmProvider.js +52 -7
- package/dist/modules/inbox_ops/lib/llmProvider.js.map +2 -2
- package/dist/modules/notifications/setup.js +13 -0
- package/dist/modules/notifications/setup.js.map +7 -0
- package/jest.config.cjs +1 -0
- package/jest.setup.ts +18 -0
- package/package.json +5 -3
- package/src/helpers/integration/api.ts +38 -16
- package/src/helpers/integration/auth.ts +13 -6
- package/src/modules/auth/commands/roles.ts +10 -12
- package/src/modules/catalog/AGENTS.md +11 -0
- package/src/modules/catalog/ai-agents-context.ts +239 -0
- package/src/modules/catalog/ai-agents.ts +525 -0
- package/src/modules/catalog/ai-tools/_shared.ts +487 -0
- package/src/modules/catalog/ai-tools/authoring-pack.ts +600 -0
- package/src/modules/catalog/ai-tools/categories-pack.ts +192 -0
- package/src/modules/catalog/ai-tools/configuration-pack.ts +218 -0
- package/src/modules/catalog/ai-tools/media-tags-pack.ts +127 -0
- package/src/modules/catalog/ai-tools/merchandising-pack.ts +608 -0
- package/src/modules/catalog/ai-tools/mutation-pack.ts +761 -0
- package/src/modules/catalog/ai-tools/prices-offers-pack.ts +376 -0
- package/src/modules/catalog/ai-tools/products-pack.ts +387 -0
- package/src/modules/catalog/ai-tools/stats-pack.ts +84 -0
- package/src/modules/catalog/ai-tools/types.ts +81 -0
- package/src/modules/catalog/ai-tools/variants-pack.ts +147 -0
- package/src/modules/catalog/ai-tools.ts +78 -0
- package/src/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.tsx +597 -0
- package/src/modules/catalog/backend/catalog/products/page.tsx +23 -2
- package/src/modules/catalog/components/CatalogStatsCard.tsx +118 -0
- package/src/modules/catalog/components/products/ProductsDataTable.tsx +54 -6
- package/src/modules/catalog/events.ts +7 -4
- package/src/modules/catalog/i18n/de.json +17 -0
- package/src/modules/catalog/i18n/en.json +17 -0
- package/src/modules/catalog/i18n/es.json +17 -0
- package/src/modules/catalog/i18n/pl.json +17 -0
- package/src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.tsx +109 -0
- package/src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.ts +29 -0
- package/src/modules/catalog/widgets/injection/product-seo/widget.client.tsx +1 -1
- package/src/modules/catalog/widgets/injection-table.ts +12 -0
- package/src/modules/customer_accounts/i18n/de.json +5 -0
- package/src/modules/customer_accounts/i18n/en.json +5 -0
- package/src/modules/customer_accounts/i18n/es.json +5 -0
- package/src/modules/customer_accounts/i18n/pl.json +5 -0
- package/src/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.client.tsx +136 -0
- package/src/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.ts +43 -0
- package/src/modules/customer_accounts/widgets/injection-table.ts +9 -0
- package/src/modules/customers/AGENTS.md +13 -0
- package/src/modules/customers/ai-agents-context.ts +150 -0
- package/src/modules/customers/ai-agents.ts +355 -0
- package/src/modules/customers/ai-tools/activities-tasks-pack.ts +1248 -0
- package/src/modules/customers/ai-tools/addresses-tags-pack.ts +145 -0
- package/src/modules/customers/ai-tools/companies-pack.ts +362 -0
- package/src/modules/customers/ai-tools/deals-pack.ts +505 -0
- package/src/modules/customers/ai-tools/people-pack.ts +369 -0
- package/src/modules/customers/ai-tools/settings-pack.ts +121 -0
- package/src/modules/customers/ai-tools/types.ts +76 -0
- package/src/modules/customers/ai-tools.ts +34 -0
- package/src/modules/customers/i18n/de.json +25 -0
- package/src/modules/customers/i18n/en.json +25 -0
- package/src/modules/customers/i18n/es.json +25 -0
- package/src/modules/customers/i18n/pl.json +25 -0
- package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.tsx +580 -0
- package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.ts +36 -0
- package/src/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.client.tsx +191 -0
- package/src/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.ts +37 -0
- package/src/modules/customers/widgets/injection-table.ts +41 -0
- package/src/modules/inbox_ops/ai-tools.ts +4 -0
- package/src/modules/inbox_ops/lib/llmProvider.ts +83 -7
- package/src/modules/notifications/setup.ts +11 -0
|
@@ -1,13 +1,34 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
|
+
import * as React from 'react'
|
|
3
4
|
import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
4
|
-
import ProductsDataTable
|
|
5
|
+
import ProductsDataTable, {
|
|
6
|
+
type ProductsDataTableSnapshot,
|
|
7
|
+
} from '../../../components/products/ProductsDataTable'
|
|
5
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Step 5.15 — Phase 3 WS-D.
|
|
11
|
+
*
|
|
12
|
+
* The catalog merchandising AI trigger moved behind the widget-injection
|
|
13
|
+
* system and now mounts in `data-table:catalog.products:header`. The
|
|
14
|
+
* products list page no longer imports `MerchandisingAssistantSheet`,
|
|
15
|
+
* `hasAllFeatures`, or the `/api/auth/feature-check` polling helper —
|
|
16
|
+
* feature gating is handled by the injection widget's `features`
|
|
17
|
+
* metadata (`catalog.products.view` + `ai_assistant.view`). The snapshot
|
|
18
|
+
* subscription is kept so host-side observability hooks the DataTable's
|
|
19
|
+
* current filter/total count for future extensions.
|
|
20
|
+
*/
|
|
6
21
|
export default function CatalogProductsPage() {
|
|
22
|
+
const [, setSnapshot] = React.useState<ProductsDataTableSnapshot>({
|
|
23
|
+
search: '',
|
|
24
|
+
filterValues: {},
|
|
25
|
+
total: 0,
|
|
26
|
+
})
|
|
27
|
+
|
|
7
28
|
return (
|
|
8
29
|
<Page>
|
|
9
30
|
<PageBody>
|
|
10
|
-
<ProductsDataTable />
|
|
31
|
+
<ProductsDataTable onSnapshotChange={setSnapshot} />
|
|
11
32
|
</PageBody>
|
|
12
33
|
</Page>
|
|
13
34
|
)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Demo dynamic AI UI part.
|
|
5
|
+
*
|
|
6
|
+
* Registered as `catalog.stats-card`. Tools (`catalog.show_stats`) emit a
|
|
7
|
+
* `{ uiPart: { componentId: 'catalog.stats-card', payload: { ... } } }`
|
|
8
|
+
* envelope and the chat client renders this card inline. Serves as the
|
|
9
|
+
* canonical example for how third-party modules contribute custom AI UI
|
|
10
|
+
* parts: define a presentational React component, register it on the
|
|
11
|
+
* shared `defaultAiUiPartRegistry` once at module load, and the dispatcher
|
|
12
|
+
* needs zero special handling.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as React from 'react'
|
|
16
|
+
import { Boxes, FolderTree, PackageCheck, Tags } from 'lucide-react'
|
|
17
|
+
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
18
|
+
import {
|
|
19
|
+
defaultAiUiPartRegistry,
|
|
20
|
+
type AiUiPartProps,
|
|
21
|
+
} from '@open-mercato/ui/ai'
|
|
22
|
+
|
|
23
|
+
export interface CatalogStatsCardPayload {
|
|
24
|
+
products?: number
|
|
25
|
+
activeProducts?: number
|
|
26
|
+
categories?: number
|
|
27
|
+
tags?: number
|
|
28
|
+
generatedAt?: string
|
|
29
|
+
note?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function formatCount(value: unknown): string {
|
|
33
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
34
|
+
return value.toLocaleString()
|
|
35
|
+
}
|
|
36
|
+
return '—'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function StatTile({
|
|
40
|
+
icon,
|
|
41
|
+
label,
|
|
42
|
+
value,
|
|
43
|
+
}: {
|
|
44
|
+
icon: React.ReactNode
|
|
45
|
+
label: string
|
|
46
|
+
value: string
|
|
47
|
+
}) {
|
|
48
|
+
return (
|
|
49
|
+
<div className="flex flex-col gap-1 rounded-md border border-border bg-card p-3">
|
|
50
|
+
<div className="flex items-center gap-1.5 text-xs uppercase tracking-wide text-muted-foreground">
|
|
51
|
+
{icon}
|
|
52
|
+
<span>{label}</span>
|
|
53
|
+
</div>
|
|
54
|
+
<div className="text-2xl font-semibold leading-none">{value}</div>
|
|
55
|
+
</div>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function CatalogStatsCard({ payload }: AiUiPartProps) {
|
|
60
|
+
const t = useT()
|
|
61
|
+
const data = (payload ?? {}) as CatalogStatsCardPayload
|
|
62
|
+
return (
|
|
63
|
+
<div
|
|
64
|
+
className="rounded-lg border border-border bg-muted/30 p-3"
|
|
65
|
+
data-ai-ui-part="catalog.stats-card"
|
|
66
|
+
>
|
|
67
|
+
<div className="mb-2 flex items-center gap-2 text-sm font-medium">
|
|
68
|
+
<Boxes className="size-4 text-primary" aria-hidden />
|
|
69
|
+
<span>{t('catalog.stats.title', 'Catalog overview')}</span>
|
|
70
|
+
</div>
|
|
71
|
+
<div className="grid grid-cols-2 gap-2 sm:grid-cols-4">
|
|
72
|
+
<StatTile
|
|
73
|
+
icon={<Boxes className="size-3" aria-hidden />}
|
|
74
|
+
label={t('catalog.stats.products', 'Products')}
|
|
75
|
+
value={formatCount(data.products)}
|
|
76
|
+
/>
|
|
77
|
+
<StatTile
|
|
78
|
+
icon={<PackageCheck className="size-3" aria-hidden />}
|
|
79
|
+
label={t('catalog.stats.active', 'Active')}
|
|
80
|
+
value={formatCount(data.activeProducts)}
|
|
81
|
+
/>
|
|
82
|
+
<StatTile
|
|
83
|
+
icon={<FolderTree className="size-3" aria-hidden />}
|
|
84
|
+
label={t('catalog.stats.categories', 'Categories')}
|
|
85
|
+
value={formatCount(data.categories)}
|
|
86
|
+
/>
|
|
87
|
+
<StatTile
|
|
88
|
+
icon={<Tags className="size-3" aria-hidden />}
|
|
89
|
+
label={t('catalog.stats.tags', 'Tags')}
|
|
90
|
+
value={formatCount(data.tags)}
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
93
|
+
{data.note ? (
|
|
94
|
+
<p className="mt-2 text-xs text-muted-foreground">{data.note}</p>
|
|
95
|
+
) : null}
|
|
96
|
+
{data.generatedAt ? (
|
|
97
|
+
<p className="mt-2 text-[10px] text-muted-foreground">
|
|
98
|
+
{t('catalog.stats.snapshotAt', 'Snapshot at {time}').replace('{time}', new Date(data.generatedAt).toLocaleString())}
|
|
99
|
+
</p>
|
|
100
|
+
) : null}
|
|
101
|
+
</div>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let registered = false
|
|
106
|
+
/**
|
|
107
|
+
* Idempotent self-registration on the module-global UI-part registry.
|
|
108
|
+
* Mirrors the pattern in `@open-mercato/ui/ai/records/registry` —
|
|
109
|
+
* importing this file from a client module is enough for the registry
|
|
110
|
+
* to know about `catalog.stats-card`.
|
|
111
|
+
*/
|
|
112
|
+
export function registerCatalogStatsCard(): void {
|
|
113
|
+
if (registered) return
|
|
114
|
+
registered = true
|
|
115
|
+
defaultAiUiPartRegistry.register('catalog.stats-card', CatalogStatsCard)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
registerCatalogStatsCard()
|
|
@@ -17,6 +17,7 @@ import { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'
|
|
|
17
17
|
import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
18
18
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
19
19
|
import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
20
|
+
import { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'
|
|
20
21
|
import { E } from '#generated/entities.ids.generated'
|
|
21
22
|
import { ProductImageCell } from './ProductImageCell'
|
|
22
23
|
|
|
@@ -139,7 +140,31 @@ function renderPrice(pricing: PricingInfo | undefined, currency?: string | null,
|
|
|
139
140
|
)
|
|
140
141
|
}
|
|
141
142
|
|
|
142
|
-
export
|
|
143
|
+
export type ProductsDataTableSnapshot = {
|
|
144
|
+
search: string
|
|
145
|
+
filterValues: FilterValues
|
|
146
|
+
total: number
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export type ProductsDataTableProps = {
|
|
150
|
+
/**
|
|
151
|
+
* Extra actions rendered alongside the built-in Create button in the
|
|
152
|
+
* DataTable header. Used by the Step 4.9 AI merchandising sheet
|
|
153
|
+
* trigger without coupling DataTable to the AI module.
|
|
154
|
+
*/
|
|
155
|
+
extraActions?: React.ReactNode
|
|
156
|
+
/**
|
|
157
|
+
* Optional callback invoked whenever the table's search / filter /
|
|
158
|
+
* total-matching snapshot changes. Used by the Step 4.9 AI merchandising
|
|
159
|
+
* sheet to form a selection-aware pageContext per spec §10.1.
|
|
160
|
+
*/
|
|
161
|
+
onSnapshotChange?: (snapshot: ProductsDataTableSnapshot) => void
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export default function ProductsDataTable({
|
|
165
|
+
extraActions,
|
|
166
|
+
onSnapshotChange,
|
|
167
|
+
}: ProductsDataTableProps = {}) {
|
|
143
168
|
const t = useT()
|
|
144
169
|
const { confirm, ConfirmDialogElement } = useConfirmDialog()
|
|
145
170
|
const scopeVersion = useOrganizationScopeVersion()
|
|
@@ -153,6 +178,15 @@ export default function ProductsDataTable() {
|
|
|
153
178
|
const [filterValues, setFilterValues] = React.useState<FilterValues>({})
|
|
154
179
|
const [isLoading, setIsLoading] = React.useState(false)
|
|
155
180
|
const [reloadToken, setReloadToken] = React.useState(0)
|
|
181
|
+
// Step 5.18 (spec §10 line 836, D18 demo): refresh the list when a
|
|
182
|
+
// catalog.product.* event arrives via the DOM event bridge. Confirmed
|
|
183
|
+
// AI bulk mutations (one `ai.action.confirmed` + one
|
|
184
|
+
// `catalog.product.updated` per record) and direct API writes both
|
|
185
|
+
// surface here so the table reflects the new state without a manual
|
|
186
|
+
// reload.
|
|
187
|
+
useAppEvent('catalog.product.*', () => {
|
|
188
|
+
setReloadToken((token) => token + 1)
|
|
189
|
+
})
|
|
156
190
|
const [customFieldsetFilter, setCustomFieldsetFilter] = React.useState<string | null>(null)
|
|
157
191
|
const { data: customFieldDefs = [] } = useCustomFieldDefs(ENTITY_ID, {
|
|
158
192
|
keyExtras: [scopeVersion, reloadToken],
|
|
@@ -599,6 +633,11 @@ export default function ProductsDataTable() {
|
|
|
599
633
|
}
|
|
600
634
|
}, [confirm, t])
|
|
601
635
|
|
|
636
|
+
React.useEffect(() => {
|
|
637
|
+
if (!onSnapshotChange) return
|
|
638
|
+
onSnapshotChange({ search, filterValues, total })
|
|
639
|
+
}, [onSnapshotChange, search, filterValues, total])
|
|
640
|
+
|
|
602
641
|
const currentParams = React.useMemo(() => Object.fromEntries(new URLSearchParams(queryParams)), [queryParams])
|
|
603
642
|
|
|
604
643
|
const exportConfig = React.useMemo(() => ({
|
|
@@ -624,11 +663,14 @@ export default function ProductsDataTable() {
|
|
|
624
663
|
isRefreshing: isLoading,
|
|
625
664
|
}}
|
|
626
665
|
actions={(
|
|
627
|
-
<
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
666
|
+
<div className="flex items-center gap-2">
|
|
667
|
+
{extraActions}
|
|
668
|
+
<Button asChild>
|
|
669
|
+
<Link href="/backend/catalog/products/create">
|
|
670
|
+
{t('catalog.products.actions.create', 'Create')}
|
|
671
|
+
</Link>
|
|
672
|
+
</Button>
|
|
673
|
+
</div>
|
|
632
674
|
)}
|
|
633
675
|
columns={columns}
|
|
634
676
|
data={rows}
|
|
@@ -649,6 +691,12 @@ export default function ProductsDataTable() {
|
|
|
649
691
|
page,
|
|
650
692
|
sorting,
|
|
651
693
|
scopeVersion,
|
|
694
|
+
// Step 5.15: surface `total` so the merchandising AI widget
|
|
695
|
+
// (rendered in `data-table:catalog.products:header`) can build
|
|
696
|
+
// a selection-aware pageContext per spec §10.1 without taking a
|
|
697
|
+
// dependency on the host page.
|
|
698
|
+
total,
|
|
699
|
+
totalMatching: total,
|
|
652
700
|
}}
|
|
653
701
|
pagination={{
|
|
654
702
|
page,
|
|
@@ -6,10 +6,13 @@ import { createModuleEvents } from '@open-mercato/shared/modules/events'
|
|
|
6
6
|
* Declares all events that can be emitted by the catalog module.
|
|
7
7
|
*/
|
|
8
8
|
const events = [
|
|
9
|
-
// Products
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
// Products — Step 5.18 (spec §10 line 836, §9.8): catalog CRUD events
|
|
10
|
+
// bridge to the DataTable on /backend/catalog/catalog/products via the
|
|
11
|
+
// DOM event bridge so confirmed mutations (AI or otherwise) auto-refresh
|
|
12
|
+
// the list without a round-trip.
|
|
13
|
+
{ id: 'catalog.product.created', label: 'Product Created', entity: 'product', category: 'crud', clientBroadcast: true },
|
|
14
|
+
{ id: 'catalog.product.updated', label: 'Product Updated', entity: 'product', category: 'crud', clientBroadcast: true },
|
|
15
|
+
{ id: 'catalog.product.deleted', label: 'Product Deleted', entity: 'product', category: 'crud', clientBroadcast: true },
|
|
13
16
|
{ id: 'catalog.product_unit_conversion.created', label: 'Product Unit Conversion Created', entity: 'product_unit_conversion', category: 'crud' },
|
|
14
17
|
{ id: 'catalog.product_unit_conversion.updated', label: 'Product Unit Conversion Updated', entity: 'product_unit_conversion', category: 'crud' },
|
|
15
18
|
{ id: 'catalog.product_unit_conversion.deleted', label: 'Product Unit Conversion Deleted', entity: 'product_unit_conversion', category: 'crud' },
|
|
@@ -85,6 +85,17 @@
|
|
|
85
85
|
"catalog.errors.id_required": "Datensatzkennung ist erforderlich.",
|
|
86
86
|
"catalog.errors.organization_required": "Organisationskontext ist erforderlich.",
|
|
87
87
|
"catalog.errors.tenant_required": "Mandantenkontext ist erforderlich.",
|
|
88
|
+
"catalog.merchandising_assistant.context.filteredByCategory": "Filtered by category",
|
|
89
|
+
"catalog.merchandising_assistant.context.tags": "{count} tags",
|
|
90
|
+
"catalog.merchandising_assistant.dock.subtitle": "Catalog",
|
|
91
|
+
"catalog.merchandising_assistant.popover.heading": "AI assistants",
|
|
92
|
+
"catalog.merchandising_assistant.sheet.composerPlaceholder": "Frage nach Beschreibungen, Attributen, Titeln oder Preisideen…",
|
|
93
|
+
"catalog.merchandising_assistant.sheet.description": "Nur-Lese-Demo. Schlägt Beschreibungen, Attribute, Titel und Preisanpassungen für die aktuelle Auswahl vor. In dieser Phase werden keine Änderungen angewendet.",
|
|
94
|
+
"catalog.merchandising_assistant.sheet.dock": "Dock to side",
|
|
95
|
+
"catalog.merchandising_assistant.sheet.selectionPill": "Bearbeitet {count} Produkte",
|
|
96
|
+
"catalog.merchandising_assistant.sheet.title": "Katalog-Merchandising-Assistent",
|
|
97
|
+
"catalog.merchandising_assistant.trigger.ariaLabel": "KI-Merchandising-Assistent öffnen",
|
|
98
|
+
"catalog.merchandising_assistant.trigger.label": "AI",
|
|
88
99
|
"catalog.module.description": "Verwalte wiederverwendbare Produkte, Varianten und Preise für Vertriebsdokumente.",
|
|
89
100
|
"catalog.module.title": "Produktkatalog",
|
|
90
101
|
"catalog.nav.group": "Katalog",
|
|
@@ -540,6 +551,12 @@
|
|
|
540
551
|
"catalog.search.priceKind.promotion": "Aktion",
|
|
541
552
|
"catalog.search.status.inactive": "Inaktiv",
|
|
542
553
|
"catalog.search.variant.default": "Standard",
|
|
554
|
+
"catalog.stats.active": "Aktiv",
|
|
555
|
+
"catalog.stats.categories": "Kategorien",
|
|
556
|
+
"catalog.stats.products": "Produkte",
|
|
557
|
+
"catalog.stats.snapshotAt": "Stand: {time}",
|
|
558
|
+
"catalog.stats.tags": "Tags",
|
|
559
|
+
"catalog.stats.title": "Katalogübersicht",
|
|
543
560
|
"catalog.variants.errors.skuExists": "SKU wird bereits verwendet.",
|
|
544
561
|
"catalog.variants.form.barcodeLabel": "Barcode",
|
|
545
562
|
"catalog.variants.form.barcodePlaceholder": "EAN, UPC usw.",
|
|
@@ -85,6 +85,17 @@
|
|
|
85
85
|
"catalog.errors.id_required": "Record identifier is required.",
|
|
86
86
|
"catalog.errors.organization_required": "Organization context is required.",
|
|
87
87
|
"catalog.errors.tenant_required": "Tenant context is required.",
|
|
88
|
+
"catalog.merchandising_assistant.context.filteredByCategory": "Filtered by category",
|
|
89
|
+
"catalog.merchandising_assistant.context.tags": "{count} tags",
|
|
90
|
+
"catalog.merchandising_assistant.dock.subtitle": "Catalog",
|
|
91
|
+
"catalog.merchandising_assistant.popover.heading": "AI assistants",
|
|
92
|
+
"catalog.merchandising_assistant.sheet.composerPlaceholder": "Ask for descriptions, attributes, titles, or price ideas...",
|
|
93
|
+
"catalog.merchandising_assistant.sheet.description": "Read-only demo. Proposes descriptions, attributes, titles, and price adjustments for the current selection. No writes are applied in this phase.",
|
|
94
|
+
"catalog.merchandising_assistant.sheet.dock": "Dock to side",
|
|
95
|
+
"catalog.merchandising_assistant.sheet.selectionPill": "Acting on {count} products",
|
|
96
|
+
"catalog.merchandising_assistant.sheet.title": "Catalog merchandising assistant",
|
|
97
|
+
"catalog.merchandising_assistant.trigger.ariaLabel": "Open AI merchandising assistant",
|
|
98
|
+
"catalog.merchandising_assistant.trigger.label": "AI",
|
|
88
99
|
"catalog.module.description": "Manage reusable products, variants, and pricing for sales documents.",
|
|
89
100
|
"catalog.module.title": "Product Catalog",
|
|
90
101
|
"catalog.nav.group": "Catalog",
|
|
@@ -540,6 +551,12 @@
|
|
|
540
551
|
"catalog.search.priceKind.promotion": "Promotion",
|
|
541
552
|
"catalog.search.status.inactive": "Inactive",
|
|
542
553
|
"catalog.search.variant.default": "Default",
|
|
554
|
+
"catalog.stats.active": "Active",
|
|
555
|
+
"catalog.stats.categories": "Categories",
|
|
556
|
+
"catalog.stats.products": "Products",
|
|
557
|
+
"catalog.stats.snapshotAt": "Snapshot at {time}",
|
|
558
|
+
"catalog.stats.tags": "Tags",
|
|
559
|
+
"catalog.stats.title": "Catalog overview",
|
|
543
560
|
"catalog.variants.errors.skuExists": "SKU already in use.",
|
|
544
561
|
"catalog.variants.form.barcodeLabel": "Barcode",
|
|
545
562
|
"catalog.variants.form.barcodePlaceholder": "EAN, UPC, etc.",
|
|
@@ -85,6 +85,17 @@
|
|
|
85
85
|
"catalog.errors.id_required": "Se requiere el identificador del registro.",
|
|
86
86
|
"catalog.errors.organization_required": "Se requiere el contexto de la organización.",
|
|
87
87
|
"catalog.errors.tenant_required": "Se requiere el contexto del inquilino.",
|
|
88
|
+
"catalog.merchandising_assistant.context.filteredByCategory": "Filtered by category",
|
|
89
|
+
"catalog.merchandising_assistant.context.tags": "{count} tags",
|
|
90
|
+
"catalog.merchandising_assistant.dock.subtitle": "Catalog",
|
|
91
|
+
"catalog.merchandising_assistant.popover.heading": "AI assistants",
|
|
92
|
+
"catalog.merchandising_assistant.sheet.composerPlaceholder": "Pide descripciones, atributos, títulos o ideas de precios…",
|
|
93
|
+
"catalog.merchandising_assistant.sheet.description": "Demo de solo lectura. Propone descripciones, atributos, títulos y ajustes de precios para la selección actual. En esta fase no se aplican cambios.",
|
|
94
|
+
"catalog.merchandising_assistant.sheet.dock": "Dock to side",
|
|
95
|
+
"catalog.merchandising_assistant.sheet.selectionPill": "Actuando sobre {count} productos",
|
|
96
|
+
"catalog.merchandising_assistant.sheet.title": "Asistente de merchandising del catálogo",
|
|
97
|
+
"catalog.merchandising_assistant.trigger.ariaLabel": "Abrir el asistente de merchandising IA",
|
|
98
|
+
"catalog.merchandising_assistant.trigger.label": "AI",
|
|
88
99
|
"catalog.module.description": "Administra productos reutilizables, variantes y precios para los documentos de ventas.",
|
|
89
100
|
"catalog.module.title": "Catálogo de productos",
|
|
90
101
|
"catalog.nav.group": "Catálogo",
|
|
@@ -540,6 +551,12 @@
|
|
|
540
551
|
"catalog.search.priceKind.promotion": "Promoción",
|
|
541
552
|
"catalog.search.status.inactive": "Inactivo",
|
|
542
553
|
"catalog.search.variant.default": "Predeterminado",
|
|
554
|
+
"catalog.stats.active": "Activos",
|
|
555
|
+
"catalog.stats.categories": "Categorías",
|
|
556
|
+
"catalog.stats.products": "Productos",
|
|
557
|
+
"catalog.stats.snapshotAt": "Captura en {time}",
|
|
558
|
+
"catalog.stats.tags": "Etiquetas",
|
|
559
|
+
"catalog.stats.title": "Resumen del catálogo",
|
|
543
560
|
"catalog.variants.errors.skuExists": "El SKU ya está en uso.",
|
|
544
561
|
"catalog.variants.form.barcodeLabel": "Código de barras",
|
|
545
562
|
"catalog.variants.form.barcodePlaceholder": "EAN, UPC, etc.",
|
|
@@ -85,6 +85,17 @@
|
|
|
85
85
|
"catalog.errors.id_required": "Wymagany jest identyfikator rekordu.",
|
|
86
86
|
"catalog.errors.organization_required": "Wymagany jest kontekst organizacji.",
|
|
87
87
|
"catalog.errors.tenant_required": "Wymagany jest kontekst dzierżawy.",
|
|
88
|
+
"catalog.merchandising_assistant.context.filteredByCategory": "Filtered by category",
|
|
89
|
+
"catalog.merchandising_assistant.context.tags": "{count} tags",
|
|
90
|
+
"catalog.merchandising_assistant.dock.subtitle": "Catalog",
|
|
91
|
+
"catalog.merchandising_assistant.popover.heading": "AI assistants",
|
|
92
|
+
"catalog.merchandising_assistant.sheet.composerPlaceholder": "Poproś o opisy, atrybuty, tytuły lub propozycje cen…",
|
|
93
|
+
"catalog.merchandising_assistant.sheet.description": "Demo tylko do odczytu. Proponuje opisy, atrybuty, tytuły i korekty cen dla bieżącego wyboru. Na tym etapie żadne zmiany nie są zapisywane.",
|
|
94
|
+
"catalog.merchandising_assistant.sheet.dock": "Dock to side",
|
|
95
|
+
"catalog.merchandising_assistant.sheet.selectionPill": "Działa na {count} produktach",
|
|
96
|
+
"catalog.merchandising_assistant.sheet.title": "Asystent merchandisingu katalogu",
|
|
97
|
+
"catalog.merchandising_assistant.trigger.ariaLabel": "Otwórz asystenta merchandisingu AI",
|
|
98
|
+
"catalog.merchandising_assistant.trigger.label": "AI",
|
|
88
99
|
"catalog.module.description": "Zarządzaj produktami, wariantami i cennikami wykorzystywanymi w sprzedaży.",
|
|
89
100
|
"catalog.module.title": "Katalog produktów",
|
|
90
101
|
"catalog.nav.group": "Katalog",
|
|
@@ -540,6 +551,12 @@
|
|
|
540
551
|
"catalog.search.priceKind.promotion": "Promocja",
|
|
541
552
|
"catalog.search.status.inactive": "Nieaktywny",
|
|
542
553
|
"catalog.search.variant.default": "Domyślny",
|
|
554
|
+
"catalog.stats.active": "Aktywne",
|
|
555
|
+
"catalog.stats.categories": "Kategorie",
|
|
556
|
+
"catalog.stats.products": "Produkty",
|
|
557
|
+
"catalog.stats.snapshotAt": "Migawka o {time}",
|
|
558
|
+
"catalog.stats.tags": "Tagi",
|
|
559
|
+
"catalog.stats.title": "Przegląd katalogu",
|
|
543
560
|
"catalog.variants.errors.skuExists": "SKU jest już używany.",
|
|
544
561
|
"catalog.variants.form.barcodeLabel": "Kod kreskowy",
|
|
545
562
|
"catalog.variants.form.barcodePlaceholder": "EAN, UPC itp.",
|
package/src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.tsx
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Step 5.15 — Catalog merchandising AiChat injection widget.
|
|
5
|
+
*
|
|
6
|
+
* Reuses the Step 4.9 `MerchandisingAssistantSheet` component but loads
|
|
7
|
+
* it through the widget-injection system instead of being imported
|
|
8
|
+
* directly from the products list page. `pageContext` follows spec §10.1
|
|
9
|
+
* exactly and is derived from the DataTable's injection context (filter
|
|
10
|
+
* snapshot + total-matching count). Selection data is not exposed by the
|
|
11
|
+
* shared DataTable today (Phase 2 contract); `selectedCount` ships as 0
|
|
12
|
+
* until the host lifts selection into injection context.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as React from 'react'
|
|
16
|
+
import MerchandisingAssistantSheet, {
|
|
17
|
+
type MerchandisingPageContext,
|
|
18
|
+
type MerchandisingPageContextFilter,
|
|
19
|
+
} from '../../../backend/catalog/products/MerchandisingAssistantSheet'
|
|
20
|
+
|
|
21
|
+
interface HostInjectionContext {
|
|
22
|
+
search?: string
|
|
23
|
+
filters?: {
|
|
24
|
+
categoryIds?: unknown
|
|
25
|
+
tagIds?: unknown
|
|
26
|
+
status?: unknown
|
|
27
|
+
}
|
|
28
|
+
customFieldset?: string | null
|
|
29
|
+
page?: number
|
|
30
|
+
sorting?: unknown
|
|
31
|
+
scopeVersion?: unknown
|
|
32
|
+
total?: number | string
|
|
33
|
+
totalMatching?: number | string
|
|
34
|
+
/** Selected row IDs from DataTable (auto-enriched when bulk actions are present). */
|
|
35
|
+
_selectedRowIds?: string[]
|
|
36
|
+
_selectedCount?: number
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface MerchandisingAssistantTriggerProps {
|
|
40
|
+
context?: HostInjectionContext
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readString(value: unknown): string | null {
|
|
44
|
+
return typeof value === 'string' && value.length > 0 ? value : null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function readNumber(value: unknown): number {
|
|
48
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value
|
|
49
|
+
if (typeof value === 'string') {
|
|
50
|
+
const parsed = Number.parseInt(value, 10)
|
|
51
|
+
if (Number.isFinite(parsed)) return parsed
|
|
52
|
+
}
|
|
53
|
+
return 0
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function normalizeFilters(context: HostInjectionContext | undefined): MerchandisingPageContextFilter {
|
|
57
|
+
const rawCategories = context?.filters?.categoryIds
|
|
58
|
+
const categoryIds = Array.isArray(rawCategories) ? rawCategories : []
|
|
59
|
+
const firstCategoryId = categoryIds
|
|
60
|
+
.map(readString)
|
|
61
|
+
.find((value): value is string => value !== null && value.length > 0) ?? null
|
|
62
|
+
|
|
63
|
+
const rawTags = context?.filters?.tagIds
|
|
64
|
+
const tags = Array.isArray(rawTags)
|
|
65
|
+
? rawTags.map(readString).filter((value): value is string => value !== null)
|
|
66
|
+
: []
|
|
67
|
+
|
|
68
|
+
const status = readString(context?.filters?.status)
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
categoryId: firstCategoryId,
|
|
72
|
+
priceRange: null,
|
|
73
|
+
tags,
|
|
74
|
+
status,
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Exposed for unit tests so the page-context derivation is exercisable
|
|
80
|
+
* without mounting the widget.
|
|
81
|
+
*/
|
|
82
|
+
export function computeCatalogMerchandisingPageContext(
|
|
83
|
+
context: HostInjectionContext | undefined,
|
|
84
|
+
): MerchandisingPageContext {
|
|
85
|
+
const totalMatching = readNumber(context?.totalMatching ?? context?.total)
|
|
86
|
+
const selectedRowIds = Array.isArray(context?._selectedRowIds) ? context._selectedRowIds : []
|
|
87
|
+
const selectedCount = selectedRowIds.length > 0 ? selectedRowIds.length : readNumber(context?._selectedCount)
|
|
88
|
+
return {
|
|
89
|
+
view: 'catalog.products.list',
|
|
90
|
+
entityType: 'catalog.products.list',
|
|
91
|
+
recordType: null,
|
|
92
|
+
recordId: selectedRowIds.join(','),
|
|
93
|
+
extra: {
|
|
94
|
+
filter: normalizeFilters(context),
|
|
95
|
+
totalMatching,
|
|
96
|
+
selectedCount,
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default function MerchandisingAssistantTriggerWidget({
|
|
102
|
+
context,
|
|
103
|
+
}: MerchandisingAssistantTriggerProps) {
|
|
104
|
+
const pageContext = React.useMemo(
|
|
105
|
+
() => computeCatalogMerchandisingPageContext(context),
|
|
106
|
+
[context],
|
|
107
|
+
)
|
|
108
|
+
return <MerchandisingAssistantSheet pageContext={pageContext} />
|
|
109
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { InjectionWidgetModule } from '@open-mercato/shared/modules/widgets/injection'
|
|
2
|
+
import MerchandisingAssistantTriggerWidget from './widget.client'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Step 5.15 (Phase 3 WS-D) — Catalog merchandising AiChat injection.
|
|
6
|
+
*
|
|
7
|
+
* Moves the Step 4.9 demo embed behind the widget-injection system so the
|
|
8
|
+
* products-list page no longer hosts the trigger directly. The widget
|
|
9
|
+
* targets `data-table:catalog.products:header` (owned by the shared
|
|
10
|
+
* `DataTable` primitive). The existing `MerchandisingAssistantSheet`
|
|
11
|
+
* component is reused verbatim so the Phase 2 read-only contract is
|
|
12
|
+
* preserved.
|
|
13
|
+
*
|
|
14
|
+
* Feature-gated behind `catalog.products.view` + `ai_assistant.view`.
|
|
15
|
+
*/
|
|
16
|
+
const widget: InjectionWidgetModule<Record<string, unknown>, Record<string, unknown>> = {
|
|
17
|
+
metadata: {
|
|
18
|
+
id: 'catalog.injection.merchandising-assistant-trigger',
|
|
19
|
+
title: 'Catalog Merchandising Assistant Trigger',
|
|
20
|
+
description:
|
|
21
|
+
'Renders an "AI Merchandising" button in the products list header that opens a sheet embedding the catalog merchandising assistant.',
|
|
22
|
+
features: ['catalog.products.view', 'ai_assistant.view'],
|
|
23
|
+
priority: 100,
|
|
24
|
+
enabled: true,
|
|
25
|
+
},
|
|
26
|
+
Widget: MerchandisingAssistantTriggerWidget,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default widget
|
|
@@ -79,7 +79,7 @@ export default function ProductSeoWidget({ data }: InjectionWidgetComponentProps
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
return (
|
|
82
|
-
<div className="space-y-3 rounded-lg border bg-card p-4 shadow-sm">
|
|
82
|
+
<div className="mt-4 w-full space-y-3 rounded-lg border bg-card p-4 shadow-sm">
|
|
83
83
|
<div className="flex items-start justify-between gap-3">
|
|
84
84
|
<div>
|
|
85
85
|
<div className="text-sm font-semibold text-foreground">{t('catalog.products.create.seoWidget.title', 'SEO Optimization')}</div>
|
|
@@ -26,6 +26,18 @@ export const injectionTable: ModuleInjectionTable = {
|
|
|
26
26
|
widgetId: 'catalog.injection.product-bulk-delete',
|
|
27
27
|
priority: 40,
|
|
28
28
|
},
|
|
29
|
+
// Step 5.15 — Phase 3 WS-D.
|
|
30
|
+
// Merchandising assistant trigger moved behind the injection system so the
|
|
31
|
+
// products list page no longer imports `MerchandisingAssistantSheet`
|
|
32
|
+
// directly. The DataTable's `injectionSpotId="data-table:catalog.products"`
|
|
33
|
+
// exposes the `:search-trailing` variant that this widget targets — the
|
|
34
|
+
// round icon-only trigger lives next to the products list search input.
|
|
35
|
+
'data-table:catalog.products:search-trailing': [
|
|
36
|
+
{
|
|
37
|
+
widgetId: 'catalog.injection.merchandising-assistant-trigger',
|
|
38
|
+
priority: 100,
|
|
39
|
+
},
|
|
40
|
+
],
|
|
29
41
|
}
|
|
30
42
|
|
|
31
43
|
export default injectionTable
|
|
@@ -202,6 +202,11 @@
|
|
|
202
202
|
"customer_accounts.notifications.user.locked.title": "Customer account locked",
|
|
203
203
|
"customer_accounts.notifications.user.signup.body": "A new customer account was created.",
|
|
204
204
|
"customer_accounts.notifications.user.signup.title": "New customer signup",
|
|
205
|
+
"customer_accounts.portal_ai_assistant.sheet.composerPlaceholder": "Frage zu deinem Konto stellen...",
|
|
206
|
+
"customer_accounts.portal_ai_assistant.sheet.description": "Read-only-Assistent für Portalkunden. Fragen zu deinem Konto und zur aktuellen Aktivität.",
|
|
207
|
+
"customer_accounts.portal_ai_assistant.sheet.title": "Portal-AI-Assistent",
|
|
208
|
+
"customer_accounts.portal_ai_assistant.trigger.ariaLabel": "Portal-KI-Assistent öffnen",
|
|
209
|
+
"customer_accounts.portal_ai_assistant.trigger.label": "KI fragen",
|
|
205
210
|
"customer_accounts.search.badge.customerRole": "Kundenrolle",
|
|
206
211
|
"customer_accounts.search.badge.customerUser": "Kundenbenutzer",
|
|
207
212
|
"customer_accounts.settings.back": "Kundenbenutzer",
|
|
@@ -202,6 +202,11 @@
|
|
|
202
202
|
"customer_accounts.notifications.user.locked.title": "Customer account locked",
|
|
203
203
|
"customer_accounts.notifications.user.signup.body": "A new customer account was created.",
|
|
204
204
|
"customer_accounts.notifications.user.signup.title": "New customer signup",
|
|
205
|
+
"customer_accounts.portal_ai_assistant.sheet.composerPlaceholder": "Ask about your account...",
|
|
206
|
+
"customer_accounts.portal_ai_assistant.sheet.description": "Read-only assistant for portal customers. Ask about your account and recent activity.",
|
|
207
|
+
"customer_accounts.portal_ai_assistant.sheet.title": "Portal AI assistant",
|
|
208
|
+
"customer_accounts.portal_ai_assistant.trigger.ariaLabel": "Open portal AI assistant",
|
|
209
|
+
"customer_accounts.portal_ai_assistant.trigger.label": "Ask AI",
|
|
205
210
|
"customer_accounts.search.badge.customerRole": "Customer role",
|
|
206
211
|
"customer_accounts.search.badge.customerUser": "Customer user",
|
|
207
212
|
"customer_accounts.settings.back": "Customer Users",
|
|
@@ -202,6 +202,11 @@
|
|
|
202
202
|
"customer_accounts.notifications.user.locked.title": "Customer account locked",
|
|
203
203
|
"customer_accounts.notifications.user.signup.body": "A new customer account was created.",
|
|
204
204
|
"customer_accounts.notifications.user.signup.title": "New customer signup",
|
|
205
|
+
"customer_accounts.portal_ai_assistant.sheet.composerPlaceholder": "Pregunta sobre tu cuenta...",
|
|
206
|
+
"customer_accounts.portal_ai_assistant.sheet.description": "Asistente de solo lectura para clientes del portal. Pregunta sobre tu cuenta y actividad reciente.",
|
|
207
|
+
"customer_accounts.portal_ai_assistant.sheet.title": "Asistente AI del portal",
|
|
208
|
+
"customer_accounts.portal_ai_assistant.trigger.ariaLabel": "Abrir asistente AI del portal",
|
|
209
|
+
"customer_accounts.portal_ai_assistant.trigger.label": "Preguntar a la IA",
|
|
205
210
|
"customer_accounts.search.badge.customerRole": "Rol de cliente",
|
|
206
211
|
"customer_accounts.search.badge.customerUser": "Usuario de cliente",
|
|
207
212
|
"customer_accounts.settings.back": "Usuarios de clientes",
|
|
@@ -202,6 +202,11 @@
|
|
|
202
202
|
"customer_accounts.notifications.user.locked.title": "Customer account locked",
|
|
203
203
|
"customer_accounts.notifications.user.signup.body": "A new customer account was created.",
|
|
204
204
|
"customer_accounts.notifications.user.signup.title": "New customer signup",
|
|
205
|
+
"customer_accounts.portal_ai_assistant.sheet.composerPlaceholder": "Zapytaj o swoje konto...",
|
|
206
|
+
"customer_accounts.portal_ai_assistant.sheet.description": "Asystent tylko do odczytu dla klientów portalu. Zapytaj o swoje konto i ostatnią aktywność.",
|
|
207
|
+
"customer_accounts.portal_ai_assistant.sheet.title": "Asystent AI portalu",
|
|
208
|
+
"customer_accounts.portal_ai_assistant.trigger.ariaLabel": "Otwórz asystenta AI portalu",
|
|
209
|
+
"customer_accounts.portal_ai_assistant.trigger.label": "Zapytaj AI",
|
|
205
210
|
"customer_accounts.search.badge.customerRole": "Rola klienta",
|
|
206
211
|
"customer_accounts.search.badge.customerUser": "Użytkownik klienta",
|
|
207
212
|
"customer_accounts.settings.back": "Użytkownicy klientów",
|