@open-mercato/core 0.6.3-develop.3734.1.766f3e785b → 0.6.3-develop.3753.1.29e9a20dde
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/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js +2 -2
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js.map +2 -2
- package/dist/modules/customers/widgets/injection/ai-deal-analyzer-trigger/widget.client.js +1 -0
- package/dist/modules/customers/widgets/injection/ai-deal-analyzer-trigger/widget.client.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.tsx +4 -4
- package/src/modules/customers/widgets/injection/ai-deal-analyzer-trigger/widget.client.tsx +1 -1
package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js
CHANGED
|
@@ -29,8 +29,8 @@ function normalizeFilters(context) {
|
|
|
29
29
|
}
|
|
30
30
|
function computeCatalogMerchandisingPageContext(context) {
|
|
31
31
|
const totalMatching = readNumber(context?.totalMatching ?? context?.total);
|
|
32
|
-
const selectedRowIds = Array.isArray(context?.
|
|
33
|
-
const selectedCount = selectedRowIds.length > 0 ? selectedRowIds.length : readNumber(context?.
|
|
32
|
+
const selectedRowIds = Array.isArray(context?.selectedRowIds) ? context.selectedRowIds : [];
|
|
33
|
+
const selectedCount = selectedRowIds.length > 0 ? selectedRowIds.length : readNumber(context?.selectedCount);
|
|
34
34
|
return {
|
|
35
35
|
view: "catalog.products.list",
|
|
36
36
|
entityType: "catalog.products.list",
|
package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\n/**\n * Step 5.15 \u2014 Catalog merchandising AiChat injection widget.\n *\n * Reuses the Step 4.9 `MerchandisingAssistantSheet` component but loads\n * it through the widget-injection system instead of being imported\n * directly from the products list page. `pageContext` follows spec \u00A710.1\n * exactly and is derived from the DataTable's injection context (filter\n * snapshot + total-matching count). Selection data is not exposed by the\n * shared DataTable today (Phase 2 contract); `selectedCount` ships as 0\n * until the host lifts selection into injection context.\n */\n\nimport * as React from 'react'\nimport MerchandisingAssistantSheet, {\n type MerchandisingPageContext,\n type MerchandisingPageContextFilter,\n} from '../../../backend/catalog/products/MerchandisingAssistantSheet'\n\ninterface HostInjectionContext {\n search?: string\n filters?: {\n categoryIds?: unknown\n tagIds?: unknown\n status?: unknown\n }\n customFieldset?: string | null\n page?: number\n sorting?: unknown\n scopeVersion?: unknown\n total?: number | string\n totalMatching?: number | string\n /** Selected row IDs from DataTable (auto-enriched when bulk actions are present). */\n
|
|
5
|
-
"mappings": ";AA2GS;AA7FT,YAAY,WAAW;AACvB,OAAO,iCAGA;AAwBP,SAAS,WAAW,OAA+B;AACjD,SAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;AACjE;AAEA,SAAS,WAAW,OAAwB;AAC1C,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO;AAChE,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,SAAS,OAAO,SAAS,OAAO,EAAE;AACxC,QAAI,OAAO,SAAS,MAAM,EAAG,QAAO;AAAA,EACtC;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,SAA2E;AACnG,QAAM,gBAAgB,SAAS,SAAS;AACxC,QAAM,cAAc,MAAM,QAAQ,aAAa,IAAI,gBAAgB,CAAC;AACpE,QAAM,kBAAkB,YACrB,IAAI,UAAU,EACd,KAAK,CAAC,UAA2B,UAAU,QAAQ,MAAM,SAAS,CAAC,KAAK;AAE3E,QAAM,UAAU,SAAS,SAAS;AAClC,QAAM,OAAO,MAAM,QAAQ,OAAO,IAC9B,QAAQ,IAAI,UAAU,EAAE,OAAO,CAAC,UAA2B,UAAU,IAAI,IACzE,CAAC;AAEL,QAAM,SAAS,WAAW,SAAS,SAAS,MAAM;AAElD,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,EACF;AACF;AAMO,SAAS,uCACd,SAC0B;AAC1B,QAAM,gBAAgB,WAAW,SAAS,iBAAiB,SAAS,KAAK;AACzE,QAAM,iBAAiB,MAAM,QAAQ,SAAS,
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\n/**\n * Step 5.15 \u2014 Catalog merchandising AiChat injection widget.\n *\n * Reuses the Step 4.9 `MerchandisingAssistantSheet` component but loads\n * it through the widget-injection system instead of being imported\n * directly from the products list page. `pageContext` follows spec \u00A710.1\n * exactly and is derived from the DataTable's injection context (filter\n * snapshot + total-matching count). Selection data is not exposed by the\n * shared DataTable today (Phase 2 contract); `selectedCount` ships as 0\n * until the host lifts selection into injection context.\n */\n\nimport * as React from 'react'\nimport MerchandisingAssistantSheet, {\n type MerchandisingPageContext,\n type MerchandisingPageContextFilter,\n} from '../../../backend/catalog/products/MerchandisingAssistantSheet'\n\ninterface HostInjectionContext {\n search?: string\n filters?: {\n categoryIds?: unknown\n tagIds?: unknown\n status?: unknown\n }\n customFieldset?: string | null\n page?: number\n sorting?: unknown\n scopeVersion?: unknown\n total?: number | string\n totalMatching?: number | string\n /** Selected row IDs from DataTable (auto-enriched when bulk actions are present). */\n selectedRowIds?: string[]\n selectedCount?: number\n}\n\ninterface MerchandisingAssistantTriggerProps {\n context?: HostInjectionContext\n}\n\nfunction readString(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\nfunction readNumber(value: unknown): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string') {\n const parsed = Number.parseInt(value, 10)\n if (Number.isFinite(parsed)) return parsed\n }\n return 0\n}\n\nfunction normalizeFilters(context: HostInjectionContext | undefined): MerchandisingPageContextFilter {\n const rawCategories = context?.filters?.categoryIds\n const categoryIds = Array.isArray(rawCategories) ? rawCategories : []\n const firstCategoryId = categoryIds\n .map(readString)\n .find((value): value is string => value !== null && value.length > 0) ?? null\n\n const rawTags = context?.filters?.tagIds\n const tags = Array.isArray(rawTags)\n ? rawTags.map(readString).filter((value): value is string => value !== null)\n : []\n\n const status = readString(context?.filters?.status)\n\n return {\n categoryId: firstCategoryId,\n priceRange: null,\n tags,\n status,\n }\n}\n\n/**\n * Exposed for unit tests so the page-context derivation is exercisable\n * without mounting the widget.\n */\nexport function computeCatalogMerchandisingPageContext(\n context: HostInjectionContext | undefined,\n): MerchandisingPageContext {\n const totalMatching = readNumber(context?.totalMatching ?? context?.total)\n const selectedRowIds = Array.isArray(context?.selectedRowIds) ? context.selectedRowIds : []\n const selectedCount = selectedRowIds.length > 0 ? selectedRowIds.length : readNumber(context?.selectedCount)\n return {\n view: 'catalog.products.list',\n entityType: 'catalog.products.list',\n recordType: null,\n recordId: selectedRowIds.join(','),\n extra: {\n filter: normalizeFilters(context),\n totalMatching,\n selectedCount,\n },\n }\n}\n\nexport default function MerchandisingAssistantTriggerWidget({\n context,\n}: MerchandisingAssistantTriggerProps) {\n const pageContext = React.useMemo(\n () => computeCatalogMerchandisingPageContext(context),\n [context],\n )\n return <MerchandisingAssistantSheet pageContext={pageContext} />\n}\n"],
|
|
5
|
+
"mappings": ";AA2GS;AA7FT,YAAY,WAAW;AACvB,OAAO,iCAGA;AAwBP,SAAS,WAAW,OAA+B;AACjD,SAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;AACjE;AAEA,SAAS,WAAW,OAAwB;AAC1C,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO;AAChE,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,SAAS,OAAO,SAAS,OAAO,EAAE;AACxC,QAAI,OAAO,SAAS,MAAM,EAAG,QAAO;AAAA,EACtC;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,SAA2E;AACnG,QAAM,gBAAgB,SAAS,SAAS;AACxC,QAAM,cAAc,MAAM,QAAQ,aAAa,IAAI,gBAAgB,CAAC;AACpE,QAAM,kBAAkB,YACrB,IAAI,UAAU,EACd,KAAK,CAAC,UAA2B,UAAU,QAAQ,MAAM,SAAS,CAAC,KAAK;AAE3E,QAAM,UAAU,SAAS,SAAS;AAClC,QAAM,OAAO,MAAM,QAAQ,OAAO,IAC9B,QAAQ,IAAI,UAAU,EAAE,OAAO,CAAC,UAA2B,UAAU,IAAI,IACzE,CAAC;AAEL,QAAM,SAAS,WAAW,SAAS,SAAS,MAAM;AAElD,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,EACF;AACF;AAMO,SAAS,uCACd,SAC0B;AAC1B,QAAM,gBAAgB,WAAW,SAAS,iBAAiB,SAAS,KAAK;AACzE,QAAM,iBAAiB,MAAM,QAAQ,SAAS,cAAc,IAAI,QAAQ,iBAAiB,CAAC;AAC1F,QAAM,gBAAgB,eAAe,SAAS,IAAI,eAAe,SAAS,WAAW,SAAS,aAAa;AAC3G,SAAO;AAAA,IACL,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,UAAU,eAAe,KAAK,GAAG;AAAA,IACjC,OAAO;AAAA,MACL,QAAQ,iBAAiB,OAAO;AAAA,MAChC;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEe,SAAR,oCAAqD;AAAA,EAC1D;AACF,GAAuC;AACrC,QAAM,cAAc,MAAM;AAAA,IACxB,MAAM,uCAAuC,OAAO;AAAA,IACpD,CAAC,OAAO;AAAA,EACV;AACA,SAAO,oBAAC,+BAA4B,aAA0B;AAChE;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/modules/customers/widgets/injection/ai-deal-analyzer-trigger/widget.client.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/widgets/injection/ai-deal-analyzer-trigger/widget.client.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\n// See /framework/ai-assistant/agents \u2192 \"Deal Analyzer demo\" for the\n// pageContext contract (selectedRowIds \u2192 recordId comma-list) and the\n// loop primitives this widget surfaces.\n\nimport * as React from 'react'\nimport { Handshake, PanelRightOpen, TrendingDown } from 'lucide-react'\nimport { AiChat, type AiChatSuggestion, type AiChatContextItem } from '@open-mercato/ui/ai/AiChat'\nimport { AiIcon } from '@open-mercato/ui/ai/AiIcon'\nimport { useAiDock } from '@open-mercato/ui/ai/AiDock'\nimport { useAiChatSessions } from '@open-mercato/ui/ai/AiChatSessions'\nimport { ChatPaneTabs } from '@open-mercato/ui/ai/ChatPaneTabs'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport const DEAL_ANALYZER_AGENT_ID = 'customers.deal_analyzer'\n\nexport interface DealAnalyzerPageContext {\n view: 'customers.deals.list'\n recordType: null\n recordId: string | null\n extra: {\n selectedCount: number\n totalMatching: number\n }\n}\n\ninterface HostInjectionContext {\n tableId?: string | null\n selectedRowIds?: string[]\n selectedCount?: number\n total?: number\n totalMatching?: number\n rowCount?: number\n}\n\nfunction readNumber(value: unknown): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string') {\n const parsed = Number.parseInt(value, 10)\n if (Number.isFinite(parsed)) return parsed\n }\n return 0\n}\n\nfunction readString(value: unknown): string {\n return typeof value === 'string' ? value : ''\n}\n\nfunction buildDealAnalyzerPageContext(context: HostInjectionContext | undefined): DealAnalyzerPageContext {\n const selectedIdsRaw = Array.isArray(context?.selectedRowIds) ? context?.selectedRowIds ?? [] : []\n const selectedIds = selectedIdsRaw.map(readString).filter((id) => id.length > 0)\n const selectedCount = selectedIds.length > 0\n ? selectedIds.length\n : readNumber(context?.selectedCount)\n const totalMatching = readNumber(context?.totalMatching ?? context?.total ?? context?.rowCount)\n const recordId = selectedIds.length > 0 ? selectedIds.join(',') : null\n return {\n view: 'customers.deals.list',\n recordType: null,\n recordId,\n extra: {\n selectedCount,\n totalMatching,\n },\n }\n}\n\nfunction useDealAnalyzerSuggestions(\n hasSelection: boolean,\n selectedCount: number,\n): AiChatSuggestion[] {\n const t = useT()\n return React.useMemo(() => {\n if (hasSelection) {\n return [\n {\n label: t(\n 'customers.deal_analyzer.suggestions.analyzeSelected',\n 'Analyze selected deals',\n ),\n prompt: `Analyze my ${selectedCount} selected deals and surface any that are stalled`,\n icon: <TrendingDown className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.deal_analyzer.suggestions.proposeStageMove',\n 'Propose stage moves for selected deals',\n ),\n prompt: `Analyze my ${selectedCount} selected deals and propose stage moves for stalled high-value ones`,\n icon: <Handshake className=\"size-4\" />,\n },\n ]\n }\n return [\n {\n label: t(\n 'customers.deal_analyzer.suggestions.analyzeStalledDeals',\n 'Analyze stalled deals',\n ),\n prompt: 'Analyze stalled deals from the last 30 days and propose a stage move for the highest value one',\n icon: <TrendingDown className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.deal_analyzer.suggestions.showAtRiskPipeline',\n 'Show at-risk pipeline',\n ),\n prompt: 'Show me deals with no activity in the last 14 days worth more than $5,000',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.deal_analyzer.suggestions.overviewByStage',\n 'Deal health overview',\n ),\n prompt: 'Give me a health overview of all open deals ranked by activity recency',\n icon: <AiIcon className=\"size-4\" />,\n },\n ]\n }, [hasSelection, selectedCount, t])\n}\n\nfunction useDealAnalyzerContextItems(pageContext: DealAnalyzerPageContext): AiChatContextItem[] {\n const t = useT()\n return React.useMemo(() => {\n const items: AiChatContextItem[] = []\n const { selectedCount, totalMatching } = pageContext.extra\n if (selectedCount > 0) {\n items.push({\n label: t(\n 'customers.deal_analyzer.context.selectedDeals',\n '{count} deals selected',\n ).replace('{count}', String(selectedCount)),\n })\n } else if (totalMatching > 0) {\n items.push({\n label: t(\n 'customers.deal_analyzer.context.dealsInView',\n '{count} deals in view',\n ).replace('{count}', String(totalMatching)),\n })\n }\n return items\n }, [pageContext, t])\n}\n\ninterface DealAnalyzerTriggerProps {\n context?: HostInjectionContext\n}\n\nexport default function DealAnalyzerTriggerWidget({ context }: DealAnalyzerTriggerProps) {\n const t = useT()\n const dock = useAiDock()\n const [open, setOpen] = React.useState(false)\n const pageContext = React.useMemo(() => buildDealAnalyzerPageContext(context), [context])\n\n const selectedCount = pageContext.extra.selectedCount\n const hasSelection = selectedCount > 0\n const suggestions = useDealAnalyzerSuggestions(hasSelection, selectedCount)\n const contextItems = useDealAnalyzerContextItems(pageContext)\n\n const handleTriggerClick = React.useCallback(() => {\n if (dock.state.assistant?.agent === DEAL_ANALYZER_AGENT_ID) {\n dock.dock(dock.state.assistant)\n setOpen(false)\n return\n }\n setOpen(true)\n }, [dock])\n\n const handleDock = React.useCallback(() => {\n dock.dock({\n agent: DEAL_ANALYZER_AGENT_ID,\n label: t('customers.deal_analyzer.sheet.title', 'Deal Analyzer'),\n description: t('customers.deal_analyzer.dock.subtitle', 'Deals'),\n pageContext: pageContext as unknown as Record<string, unknown>,\n placeholder: t(\n 'customers.deal_analyzer.sheet.composerPlaceholder',\n 'Analyze stalled deals, propose stage moves...',\n ),\n suggestions,\n contextItems,\n welcomeTitle: t('customers.deal_analyzer.sheet.welcomeTitle', 'Deal Analyzer'),\n welcomeDescription: hasSelection\n ? t(\n 'customers.deal_analyzer.sheet.welcomeDescriptionSelection',\n 'Ready to analyze your {count} selected deals:',\n ).replace('{count}', String(selectedCount))\n : t(\n 'customers.deal_analyzer.sheet.welcomeDescriptionAll',\n 'Analyze deal pipeline health and propose stage moves:',\n ),\n })\n setOpen(false)\n }, [contextItems, dock, hasSelection, pageContext, selectedCount, suggestions, t])\n\n const triggerLabel = t(\n 'customers.deal_analyzer.trigger.ariaLabel',\n 'Open Deal Analyzer AI agent',\n )\n const labelText = t('customers.ai_assistant.trigger.label', 'AI')\n\n return (\n <>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleTriggerClick}\n data-ai-deal-analyzer-trigger=\"\"\n aria-label={triggerLabel}\n title={triggerLabel}\n className={cn('relative', 'hover:bg-brand-violet/10')}\n >\n <AiIcon className=\"size-4\" />\n <span>{labelText}</span>\n {hasSelection ? (\n <span\n className=\"absolute -top-1 -right-1 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-medium leading-none text-primary-foreground\"\n data-ai-deal-analyzer-selected-count={selectedCount}\n >\n {selectedCount}\n </span>\n ) : null}\n </Button>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent\n className={cn(\n 'top-0 left-0 right-0 bottom-0 translate-x-0 translate-y-0 max-w-none w-screen h-svh max-h-svh rounded-none',\n 'sm:top-0 sm:bottom-0 sm:right-0 sm:left-auto sm:translate-x-0 sm:translate-y-0',\n 'sm:max-w-xl sm:w-[36rem] sm:rounded-l-2xl sm:h-screen sm:max-h-screen',\n 'flex flex-col gap-3 p-4',\n )}\n data-ai-deal-analyzer-sheet=\"\"\n >\n <DialogHeader>\n <div className=\"flex items-center gap-3 pr-8\">\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('customers.deal_analyzer.sheet.dock', 'Dock to side')}\n title={t('customers.deal_analyzer.sheet.dock', 'Dock to side')}\n onClick={handleDock}\n data-ai-deal-analyzer-dock=\"\"\n className=\"hidden lg:inline-flex shrink-0\"\n >\n <PanelRightOpen className=\"size-4\" aria-hidden />\n </IconButton>\n <DialogTitle className=\"flex-1 min-w-0 truncate\">\n {t('customers.deal_analyzer.sheet.title', 'Deal Analyzer')}\n </DialogTitle>\n {hasSelection ? (\n <span\n className=\"shrink-0 inline-flex items-center rounded-full border border-border bg-secondary px-2 py-0.5 text-xs text-secondary-foreground\"\n data-ai-deal-analyzer-selection-pill=\"\"\n data-ai-deal-analyzer-selected-count={selectedCount}\n >\n {t(\n 'customers.deal_analyzer.sheet.selectionPill',\n 'Acting on {count} deals',\n ).replace('{count}', String(selectedCount))}\n </span>\n ) : null}\n </div>\n <DialogDescription>\n {hasSelection\n ? t(\n 'customers.deal_analyzer.sheet.descriptionWithSelection',\n 'Analyzing {count} selected deals for pipeline health and stage move proposals.',\n ).replace('{count}', String(selectedCount))\n : t(\n 'customers.deal_analyzer.sheet.description',\n 'Multi-step deal health analyzer. Surfaces stalled deals and proposes stage transitions for approval.',\n )}\n </DialogDescription>\n </DialogHeader>\n <DealAnalyzerChatBody\n pageContext={pageContext}\n suggestions={suggestions}\n contextItems={contextItems}\n hasSelection={hasSelection}\n selectedCount={selectedCount}\n />\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\ninterface DealAnalyzerChatBodyProps {\n pageContext: DealAnalyzerPageContext\n suggestions: AiChatSuggestion[]\n contextItems: AiChatContextItem[]\n hasSelection: boolean\n selectedCount: number\n}\n\nfunction DealAnalyzerChatBody({\n pageContext,\n suggestions,\n contextItems,\n hasSelection,\n selectedCount,\n}: DealAnalyzerChatBodyProps) {\n const t = useT()\n const sessions = useAiChatSessions()\n const session = sessions.getActiveSession(DEAL_ANALYZER_AGENT_ID)\n\n React.useEffect(() => {\n if (!session) sessions.ensureSession(DEAL_ANALYZER_AGENT_ID)\n }, [session, sessions])\n\n return (\n <>\n <ChatPaneTabs agentId={DEAL_ANALYZER_AGENT_ID} className=\"border-b\" />\n <div className=\"min-h-0 flex-1\" data-ai-deal-analyzer-chat-container=\"\">\n {session ? (\n <AiChat\n key={session.id}\n agent={DEAL_ANALYZER_AGENT_ID}\n conversationId={session.conversationId}\n pageContext={pageContext as unknown as Record<string, unknown>}\n className=\"h-full\"\n placeholder={t(\n 'customers.deal_analyzer.sheet.composerPlaceholder',\n 'Analyze stalled deals, propose stage moves...',\n )}\n suggestions={suggestions}\n contextItems={contextItems}\n welcomeTitle={t('customers.deal_analyzer.sheet.welcomeTitle', 'Deal Analyzer')}\n welcomeDescription={\n hasSelection\n ? t(\n 'customers.deal_analyzer.sheet.welcomeDescriptionSelection',\n 'Ready to analyze your {count} selected deals:',\n ).replace('{count}', String(selectedCount))\n : t(\n 'customers.deal_analyzer.sheet.welcomeDescriptionAll',\n 'Analyze deal pipeline health and propose stage moves:',\n )\n }\n />\n ) : null}\n </div>\n </>\n )\n}\n"],
|
|
5
|
-
"mappings": ";AA4FgB,SA0HZ,UA1HY,KA2HV,YA3HU;AAtFhB,YAAY,WAAW;AACvB,SAAS,WAAW,gBAAgB,oBAAoB;AACxD,SAAS,cAA6D;AACtE,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAC1B,SAAS,yBAAyB;AAClC,SAAS,oBAAoB;AAC7B,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,YAAY;AACrB,SAAS,UAAU;AAEZ,MAAM,yBAAyB;AAqBtC,SAAS,WAAW,OAAwB;AAC1C,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO;AAChE,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,SAAS,OAAO,SAAS,OAAO,EAAE;AACxC,QAAI,OAAO,SAAS,MAAM,EAAG,QAAO;AAAA,EACtC;AACA,SAAO;AACT;AAEA,SAAS,WAAW,OAAwB;AAC1C,SAAO,OAAO,UAAU,WAAW,QAAQ;AAC7C;
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\n// See /framework/ai-assistant/agents \u2192 \"Deal Analyzer demo\" for the\n// pageContext contract (selectedRowIds \u2192 recordId comma-list) and the\n// loop primitives this widget surfaces.\n\nimport * as React from 'react'\nimport { Handshake, PanelRightOpen, TrendingDown } from 'lucide-react'\nimport { AiChat, type AiChatSuggestion, type AiChatContextItem } from '@open-mercato/ui/ai/AiChat'\nimport { AiIcon } from '@open-mercato/ui/ai/AiIcon'\nimport { useAiDock } from '@open-mercato/ui/ai/AiDock'\nimport { useAiChatSessions } from '@open-mercato/ui/ai/AiChatSessions'\nimport { ChatPaneTabs } from '@open-mercato/ui/ai/ChatPaneTabs'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport const DEAL_ANALYZER_AGENT_ID = 'customers.deal_analyzer'\n\nexport interface DealAnalyzerPageContext {\n view: 'customers.deals.list'\n recordType: null\n recordId: string | null\n extra: {\n selectedCount: number\n totalMatching: number\n }\n}\n\ninterface HostInjectionContext {\n tableId?: string | null\n selectedRowIds?: string[]\n selectedCount?: number\n total?: number\n totalMatching?: number\n rowCount?: number\n}\n\nfunction readNumber(value: unknown): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string') {\n const parsed = Number.parseInt(value, 10)\n if (Number.isFinite(parsed)) return parsed\n }\n return 0\n}\n\nfunction readString(value: unknown): string {\n return typeof value === 'string' ? value : ''\n}\n\nexport function buildDealAnalyzerPageContext(context: HostInjectionContext | undefined): DealAnalyzerPageContext {\n const selectedIdsRaw = Array.isArray(context?.selectedRowIds) ? context?.selectedRowIds ?? [] : []\n const selectedIds = selectedIdsRaw.map(readString).filter((id) => id.length > 0)\n const selectedCount = selectedIds.length > 0\n ? selectedIds.length\n : readNumber(context?.selectedCount)\n const totalMatching = readNumber(context?.totalMatching ?? context?.total ?? context?.rowCount)\n const recordId = selectedIds.length > 0 ? selectedIds.join(',') : null\n return {\n view: 'customers.deals.list',\n recordType: null,\n recordId,\n extra: {\n selectedCount,\n totalMatching,\n },\n }\n}\n\nfunction useDealAnalyzerSuggestions(\n hasSelection: boolean,\n selectedCount: number,\n): AiChatSuggestion[] {\n const t = useT()\n return React.useMemo(() => {\n if (hasSelection) {\n return [\n {\n label: t(\n 'customers.deal_analyzer.suggestions.analyzeSelected',\n 'Analyze selected deals',\n ),\n prompt: `Analyze my ${selectedCount} selected deals and surface any that are stalled`,\n icon: <TrendingDown className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.deal_analyzer.suggestions.proposeStageMove',\n 'Propose stage moves for selected deals',\n ),\n prompt: `Analyze my ${selectedCount} selected deals and propose stage moves for stalled high-value ones`,\n icon: <Handshake className=\"size-4\" />,\n },\n ]\n }\n return [\n {\n label: t(\n 'customers.deal_analyzer.suggestions.analyzeStalledDeals',\n 'Analyze stalled deals',\n ),\n prompt: 'Analyze stalled deals from the last 30 days and propose a stage move for the highest value one',\n icon: <TrendingDown className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.deal_analyzer.suggestions.showAtRiskPipeline',\n 'Show at-risk pipeline',\n ),\n prompt: 'Show me deals with no activity in the last 14 days worth more than $5,000',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.deal_analyzer.suggestions.overviewByStage',\n 'Deal health overview',\n ),\n prompt: 'Give me a health overview of all open deals ranked by activity recency',\n icon: <AiIcon className=\"size-4\" />,\n },\n ]\n }, [hasSelection, selectedCount, t])\n}\n\nfunction useDealAnalyzerContextItems(pageContext: DealAnalyzerPageContext): AiChatContextItem[] {\n const t = useT()\n return React.useMemo(() => {\n const items: AiChatContextItem[] = []\n const { selectedCount, totalMatching } = pageContext.extra\n if (selectedCount > 0) {\n items.push({\n label: t(\n 'customers.deal_analyzer.context.selectedDeals',\n '{count} deals selected',\n ).replace('{count}', String(selectedCount)),\n })\n } else if (totalMatching > 0) {\n items.push({\n label: t(\n 'customers.deal_analyzer.context.dealsInView',\n '{count} deals in view',\n ).replace('{count}', String(totalMatching)),\n })\n }\n return items\n }, [pageContext, t])\n}\n\ninterface DealAnalyzerTriggerProps {\n context?: HostInjectionContext\n}\n\nexport default function DealAnalyzerTriggerWidget({ context }: DealAnalyzerTriggerProps) {\n const t = useT()\n const dock = useAiDock()\n const [open, setOpen] = React.useState(false)\n const pageContext = React.useMemo(() => buildDealAnalyzerPageContext(context), [context])\n\n const selectedCount = pageContext.extra.selectedCount\n const hasSelection = selectedCount > 0\n const suggestions = useDealAnalyzerSuggestions(hasSelection, selectedCount)\n const contextItems = useDealAnalyzerContextItems(pageContext)\n\n const handleTriggerClick = React.useCallback(() => {\n if (dock.state.assistant?.agent === DEAL_ANALYZER_AGENT_ID) {\n dock.dock(dock.state.assistant)\n setOpen(false)\n return\n }\n setOpen(true)\n }, [dock])\n\n const handleDock = React.useCallback(() => {\n dock.dock({\n agent: DEAL_ANALYZER_AGENT_ID,\n label: t('customers.deal_analyzer.sheet.title', 'Deal Analyzer'),\n description: t('customers.deal_analyzer.dock.subtitle', 'Deals'),\n pageContext: pageContext as unknown as Record<string, unknown>,\n placeholder: t(\n 'customers.deal_analyzer.sheet.composerPlaceholder',\n 'Analyze stalled deals, propose stage moves...',\n ),\n suggestions,\n contextItems,\n welcomeTitle: t('customers.deal_analyzer.sheet.welcomeTitle', 'Deal Analyzer'),\n welcomeDescription: hasSelection\n ? t(\n 'customers.deal_analyzer.sheet.welcomeDescriptionSelection',\n 'Ready to analyze your {count} selected deals:',\n ).replace('{count}', String(selectedCount))\n : t(\n 'customers.deal_analyzer.sheet.welcomeDescriptionAll',\n 'Analyze deal pipeline health and propose stage moves:',\n ),\n })\n setOpen(false)\n }, [contextItems, dock, hasSelection, pageContext, selectedCount, suggestions, t])\n\n const triggerLabel = t(\n 'customers.deal_analyzer.trigger.ariaLabel',\n 'Open Deal Analyzer AI agent',\n )\n const labelText = t('customers.ai_assistant.trigger.label', 'AI')\n\n return (\n <>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleTriggerClick}\n data-ai-deal-analyzer-trigger=\"\"\n aria-label={triggerLabel}\n title={triggerLabel}\n className={cn('relative', 'hover:bg-brand-violet/10')}\n >\n <AiIcon className=\"size-4\" />\n <span>{labelText}</span>\n {hasSelection ? (\n <span\n className=\"absolute -top-1 -right-1 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-medium leading-none text-primary-foreground\"\n data-ai-deal-analyzer-selected-count={selectedCount}\n >\n {selectedCount}\n </span>\n ) : null}\n </Button>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent\n className={cn(\n 'top-0 left-0 right-0 bottom-0 translate-x-0 translate-y-0 max-w-none w-screen h-svh max-h-svh rounded-none',\n 'sm:top-0 sm:bottom-0 sm:right-0 sm:left-auto sm:translate-x-0 sm:translate-y-0',\n 'sm:max-w-xl sm:w-[36rem] sm:rounded-l-2xl sm:h-screen sm:max-h-screen',\n 'flex flex-col gap-3 p-4',\n )}\n data-ai-deal-analyzer-sheet=\"\"\n >\n <DialogHeader>\n <div className=\"flex items-center gap-3 pr-8\">\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('customers.deal_analyzer.sheet.dock', 'Dock to side')}\n title={t('customers.deal_analyzer.sheet.dock', 'Dock to side')}\n onClick={handleDock}\n data-ai-deal-analyzer-dock=\"\"\n className=\"hidden lg:inline-flex shrink-0\"\n >\n <PanelRightOpen className=\"size-4\" aria-hidden />\n </IconButton>\n <DialogTitle className=\"flex-1 min-w-0 truncate\">\n {t('customers.deal_analyzer.sheet.title', 'Deal Analyzer')}\n </DialogTitle>\n {hasSelection ? (\n <span\n className=\"shrink-0 inline-flex items-center rounded-full border border-border bg-secondary px-2 py-0.5 text-xs text-secondary-foreground\"\n data-ai-deal-analyzer-selection-pill=\"\"\n data-ai-deal-analyzer-selected-count={selectedCount}\n >\n {t(\n 'customers.deal_analyzer.sheet.selectionPill',\n 'Acting on {count} deals',\n ).replace('{count}', String(selectedCount))}\n </span>\n ) : null}\n </div>\n <DialogDescription>\n {hasSelection\n ? t(\n 'customers.deal_analyzer.sheet.descriptionWithSelection',\n 'Analyzing {count} selected deals for pipeline health and stage move proposals.',\n ).replace('{count}', String(selectedCount))\n : t(\n 'customers.deal_analyzer.sheet.description',\n 'Multi-step deal health analyzer. Surfaces stalled deals and proposes stage transitions for approval.',\n )}\n </DialogDescription>\n </DialogHeader>\n <DealAnalyzerChatBody\n pageContext={pageContext}\n suggestions={suggestions}\n contextItems={contextItems}\n hasSelection={hasSelection}\n selectedCount={selectedCount}\n />\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\ninterface DealAnalyzerChatBodyProps {\n pageContext: DealAnalyzerPageContext\n suggestions: AiChatSuggestion[]\n contextItems: AiChatContextItem[]\n hasSelection: boolean\n selectedCount: number\n}\n\nfunction DealAnalyzerChatBody({\n pageContext,\n suggestions,\n contextItems,\n hasSelection,\n selectedCount,\n}: DealAnalyzerChatBodyProps) {\n const t = useT()\n const sessions = useAiChatSessions()\n const session = sessions.getActiveSession(DEAL_ANALYZER_AGENT_ID)\n\n React.useEffect(() => {\n if (!session) sessions.ensureSession(DEAL_ANALYZER_AGENT_ID)\n }, [session, sessions])\n\n return (\n <>\n <ChatPaneTabs agentId={DEAL_ANALYZER_AGENT_ID} className=\"border-b\" />\n <div className=\"min-h-0 flex-1\" data-ai-deal-analyzer-chat-container=\"\">\n {session ? (\n <AiChat\n key={session.id}\n agent={DEAL_ANALYZER_AGENT_ID}\n conversationId={session.conversationId}\n pageContext={pageContext as unknown as Record<string, unknown>}\n className=\"h-full\"\n placeholder={t(\n 'customers.deal_analyzer.sheet.composerPlaceholder',\n 'Analyze stalled deals, propose stage moves...',\n )}\n suggestions={suggestions}\n contextItems={contextItems}\n welcomeTitle={t('customers.deal_analyzer.sheet.welcomeTitle', 'Deal Analyzer')}\n welcomeDescription={\n hasSelection\n ? t(\n 'customers.deal_analyzer.sheet.welcomeDescriptionSelection',\n 'Ready to analyze your {count} selected deals:',\n ).replace('{count}', String(selectedCount))\n : t(\n 'customers.deal_analyzer.sheet.welcomeDescriptionAll',\n 'Analyze deal pipeline health and propose stage moves:',\n )\n }\n />\n ) : null}\n </div>\n </>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AA4FgB,SA0HZ,UA1HY,KA2HV,YA3HU;AAtFhB,YAAY,WAAW;AACvB,SAAS,WAAW,gBAAgB,oBAAoB;AACxD,SAAS,cAA6D;AACtE,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAC1B,SAAS,yBAAyB;AAClC,SAAS,oBAAoB;AAC7B,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,YAAY;AACrB,SAAS,UAAU;AAEZ,MAAM,yBAAyB;AAqBtC,SAAS,WAAW,OAAwB;AAC1C,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO;AAChE,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,SAAS,OAAO,SAAS,OAAO,EAAE;AACxC,QAAI,OAAO,SAAS,MAAM,EAAG,QAAO;AAAA,EACtC;AACA,SAAO;AACT;AAEA,SAAS,WAAW,OAAwB;AAC1C,SAAO,OAAO,UAAU,WAAW,QAAQ;AAC7C;AAEO,SAAS,6BAA6B,SAAoE;AAC/G,QAAM,iBAAiB,MAAM,QAAQ,SAAS,cAAc,IAAI,SAAS,kBAAkB,CAAC,IAAI,CAAC;AACjG,QAAM,cAAc,eAAe,IAAI,UAAU,EAAE,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;AAC/E,QAAM,gBAAgB,YAAY,SAAS,IACvC,YAAY,SACZ,WAAW,SAAS,aAAa;AACrC,QAAM,gBAAgB,WAAW,SAAS,iBAAiB,SAAS,SAAS,SAAS,QAAQ;AAC9F,QAAM,WAAW,YAAY,SAAS,IAAI,YAAY,KAAK,GAAG,IAAI;AAClE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,YAAY;AAAA,IACZ;AAAA,IACA,OAAO;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,2BACP,cACA,eACoB;AACpB,QAAM,IAAI,KAAK;AACf,SAAO,MAAM,QAAQ,MAAM;AACzB,QAAI,cAAc;AAChB,aAAO;AAAA,QACL;AAAA,UACE,OAAO;AAAA,YACL;AAAA,YACA;AAAA,UACF;AAAA,UACA,QAAQ,cAAc,aAAa;AAAA,UACnC,MAAM,oBAAC,gBAAa,WAAU,UAAS;AAAA,QACzC;AAAA,QACA;AAAA,UACE,OAAO;AAAA,YACL;AAAA,YACA;AAAA,UACF;AAAA,UACA,QAAQ,cAAc,aAAa;AAAA,UACnC,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,QACtC;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,QACE,OAAO;AAAA,UACL;AAAA,UACA;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,QACR,MAAM,oBAAC,gBAAa,WAAU,UAAS;AAAA,MACzC;AAAA,MACA;AAAA,QACE,OAAO;AAAA,UACL;AAAA,UACA;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,QACR,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,MACtC;AAAA,MACA;AAAA,QACE,OAAO;AAAA,UACL;AAAA,UACA;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,QACR,MAAM,oBAAC,UAAO,WAAU,UAAS;AAAA,MACnC;AAAA,IACF;AAAA,EACF,GAAG,CAAC,cAAc,eAAe,CAAC,CAAC;AACrC;AAEA,SAAS,4BAA4B,aAA2D;AAC9F,QAAM,IAAI,KAAK;AACf,SAAO,MAAM,QAAQ,MAAM;AACzB,UAAM,QAA6B,CAAC;AACpC,UAAM,EAAE,eAAe,cAAc,IAAI,YAAY;AACrD,QAAI,gBAAgB,GAAG;AACrB,YAAM,KAAK;AAAA,QACT,OAAO;AAAA,UACL;AAAA,UACA;AAAA,QACF,EAAE,QAAQ,WAAW,OAAO,aAAa,CAAC;AAAA,MAC5C,CAAC;AAAA,IACH,WAAW,gBAAgB,GAAG;AAC5B,YAAM,KAAK;AAAA,QACT,OAAO;AAAA,UACL;AAAA,UACA;AAAA,QACF,EAAE,QAAQ,WAAW,OAAO,aAAa,CAAC;AAAA,MAC5C,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT,GAAG,CAAC,aAAa,CAAC,CAAC;AACrB;AAMe,SAAR,0BAA2C,EAAE,QAAQ,GAA6B;AACvF,QAAM,IAAI,KAAK;AACf,QAAM,OAAO,UAAU;AACvB,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAC5C,QAAM,cAAc,MAAM,QAAQ,MAAM,6BAA6B,OAAO,GAAG,CAAC,OAAO,CAAC;AAExF,QAAM,gBAAgB,YAAY,MAAM;AACxC,QAAM,eAAe,gBAAgB;AACrC,QAAM,cAAc,2BAA2B,cAAc,aAAa;AAC1E,QAAM,eAAe,4BAA4B,WAAW;AAE5D,QAAM,qBAAqB,MAAM,YAAY,MAAM;AACjD,QAAI,KAAK,MAAM,WAAW,UAAU,wBAAwB;AAC1D,WAAK,KAAK,KAAK,MAAM,SAAS;AAC9B,cAAQ,KAAK;AACb;AAAA,IACF;AACA,YAAQ,IAAI;AAAA,EACd,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,aAAa,MAAM,YAAY,MAAM;AACzC,SAAK,KAAK;AAAA,MACR,OAAO;AAAA,MACP,OAAO,EAAE,uCAAuC,eAAe;AAAA,MAC/D,aAAa,EAAE,yCAAyC,OAAO;AAAA,MAC/D;AAAA,MACA,aAAa;AAAA,QACX;AAAA,QACA;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc,EAAE,8CAA8C,eAAe;AAAA,MAC7E,oBAAoB,eAChB;AAAA,QACE;AAAA,QACA;AAAA,MACF,EAAE,QAAQ,WAAW,OAAO,aAAa,CAAC,IAC1C;AAAA,QACE;AAAA,QACA;AAAA,MACF;AAAA,IACN,CAAC;AACD,YAAQ,KAAK;AAAA,EACf,GAAG,CAAC,cAAc,MAAM,cAAc,aAAa,eAAe,aAAa,CAAC,CAAC;AAEjF,QAAM,eAAe;AAAA,IACnB;AAAA,IACA;AAAA,EACF;AACA,QAAM,YAAY,EAAE,wCAAwC,IAAI;AAEhE,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAQ;AAAA,QACR,SAAS;AAAA,QACT,iCAA8B;AAAA,QAC9B,cAAY;AAAA,QACZ,OAAO;AAAA,QACP,WAAW,GAAG,YAAY,0BAA0B;AAAA,QAEpD;AAAA,8BAAC,UAAO,WAAU,UAAS;AAAA,UAC3B,oBAAC,UAAM,qBAAU;AAAA,UAChB,eACC;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,wCAAsC;AAAA,cAErC;AAAA;AAAA,UACH,IACE;AAAA;AAAA;AAAA,IACN;AAAA,IACA,oBAAC,UAAO,MAAY,cAAc,SAChC;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA,+BAA4B;AAAA,QAE5B;AAAA,+BAAC,gBACC;AAAA,iCAAC,SAAI,WAAU,gCACb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,cAAY,EAAE,sCAAsC,cAAc;AAAA,kBAClE,OAAO,EAAE,sCAAsC,cAAc;AAAA,kBAC7D,SAAS;AAAA,kBACT,8BAA2B;AAAA,kBAC3B,WAAU;AAAA,kBAEV,8BAAC,kBAAe,WAAU,UAAS,eAAW,MAAC;AAAA;AAAA,cACjD;AAAA,cACA,oBAAC,eAAY,WAAU,2BACpB,YAAE,uCAAuC,eAAe,GAC3D;AAAA,cACC,eACC;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAU;AAAA,kBACV,wCAAqC;AAAA,kBACrC,wCAAsC;AAAA,kBAErC;AAAA,oBACC;AAAA,oBACA;AAAA,kBACF,EAAE,QAAQ,WAAW,OAAO,aAAa,CAAC;AAAA;AAAA,cAC5C,IACE;AAAA,eACN;AAAA,YACA,oBAAC,qBACE,yBACG;AAAA,cACE;AAAA,cACA;AAAA,YACF,EAAE,QAAQ,WAAW,OAAO,aAAa,CAAC,IAC1C;AAAA,cACE;AAAA,cACA;AAAA,YACF,GACN;AAAA,aACF;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA;AAAA,UACF;AAAA;AAAA;AAAA,IACF,GACF;AAAA,KACF;AAEJ;AAUA,SAAS,qBAAqB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA8B;AAC5B,QAAM,IAAI,KAAK;AACf,QAAM,WAAW,kBAAkB;AACnC,QAAM,UAAU,SAAS,iBAAiB,sBAAsB;AAEhE,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,QAAS,UAAS,cAAc,sBAAsB;AAAA,EAC7D,GAAG,CAAC,SAAS,QAAQ,CAAC;AAEtB,SACE,iCACE;AAAA,wBAAC,gBAAa,SAAS,wBAAwB,WAAU,YAAW;AAAA,IACpE,oBAAC,SAAI,WAAU,kBAAiB,wCAAqC,IAClE,oBACC;AAAA,MAAC;AAAA;AAAA,QAEC,OAAO;AAAA,QACP,gBAAgB,QAAQ;AAAA,QACxB;AAAA,QACA,WAAU;AAAA,QACV,aAAa;AAAA,UACX;AAAA,UACA;AAAA,QACF;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAc,EAAE,8CAA8C,eAAe;AAAA,QAC7E,oBACE,eACI;AAAA,UACE;AAAA,UACA;AAAA,QACF,EAAE,QAAQ,WAAW,OAAO,aAAa,CAAC,IAC1C;AAAA,UACE;AAAA,UACA;AAAA,QACF;AAAA;AAAA,MArBD,QAAQ;AAAA,IAuBf,IACE,MACN;AAAA,KACF;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.6.3-develop.
|
|
3
|
+
"version": "0.6.3-develop.3753.1.29e9a20dde",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -242,16 +242,16 @@
|
|
|
242
242
|
"zod": "^4.4.3"
|
|
243
243
|
},
|
|
244
244
|
"peerDependencies": {
|
|
245
|
-
"@open-mercato/ai-assistant": "0.6.3-develop.
|
|
246
|
-
"@open-mercato/shared": "0.6.3-develop.
|
|
247
|
-
"@open-mercato/ui": "0.6.3-develop.
|
|
245
|
+
"@open-mercato/ai-assistant": "0.6.3-develop.3753.1.29e9a20dde",
|
|
246
|
+
"@open-mercato/shared": "0.6.3-develop.3753.1.29e9a20dde",
|
|
247
|
+
"@open-mercato/ui": "0.6.3-develop.3753.1.29e9a20dde",
|
|
248
248
|
"react": "^19.0.0",
|
|
249
249
|
"react-dom": "^19.0.0"
|
|
250
250
|
},
|
|
251
251
|
"devDependencies": {
|
|
252
|
-
"@open-mercato/ai-assistant": "0.6.3-develop.
|
|
253
|
-
"@open-mercato/shared": "0.6.3-develop.
|
|
254
|
-
"@open-mercato/ui": "0.6.3-develop.
|
|
252
|
+
"@open-mercato/ai-assistant": "0.6.3-develop.3753.1.29e9a20dde",
|
|
253
|
+
"@open-mercato/shared": "0.6.3-develop.3753.1.29e9a20dde",
|
|
254
|
+
"@open-mercato/ui": "0.6.3-develop.3753.1.29e9a20dde",
|
|
255
255
|
"@testing-library/dom": "^10.4.1",
|
|
256
256
|
"@testing-library/jest-dom": "^6.9.1",
|
|
257
257
|
"@testing-library/react": "^16.3.1",
|
package/src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.tsx
CHANGED
|
@@ -32,8 +32,8 @@ interface HostInjectionContext {
|
|
|
32
32
|
total?: number | string
|
|
33
33
|
totalMatching?: number | string
|
|
34
34
|
/** Selected row IDs from DataTable (auto-enriched when bulk actions are present). */
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
selectedRowIds?: string[]
|
|
36
|
+
selectedCount?: number
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
interface MerchandisingAssistantTriggerProps {
|
|
@@ -83,8 +83,8 @@ export function computeCatalogMerchandisingPageContext(
|
|
|
83
83
|
context: HostInjectionContext | undefined,
|
|
84
84
|
): MerchandisingPageContext {
|
|
85
85
|
const totalMatching = readNumber(context?.totalMatching ?? context?.total)
|
|
86
|
-
const selectedRowIds = Array.isArray(context?.
|
|
87
|
-
const selectedCount = selectedRowIds.length > 0 ? selectedRowIds.length : readNumber(context?.
|
|
86
|
+
const selectedRowIds = Array.isArray(context?.selectedRowIds) ? context.selectedRowIds : []
|
|
87
|
+
const selectedCount = selectedRowIds.length > 0 ? selectedRowIds.length : readNumber(context?.selectedCount)
|
|
88
88
|
return {
|
|
89
89
|
view: 'catalog.products.list',
|
|
90
90
|
entityType: 'catalog.products.list',
|
|
@@ -57,7 +57,7 @@ function readString(value: unknown): string {
|
|
|
57
57
|
return typeof value === 'string' ? value : ''
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
function buildDealAnalyzerPageContext(context: HostInjectionContext | undefined): DealAnalyzerPageContext {
|
|
60
|
+
export function buildDealAnalyzerPageContext(context: HostInjectionContext | undefined): DealAnalyzerPageContext {
|
|
61
61
|
const selectedIdsRaw = Array.isArray(context?.selectedRowIds) ? context?.selectedRowIds ?? [] : []
|
|
62
62
|
const selectedIds = selectedIdsRaw.map(readString).filter((id) => id.length > 0)
|
|
63
63
|
const selectedCount = selectedIds.length > 0
|