@open-mercato/core 0.6.1-develop.3291.1.6fad645fd0 → 0.6.1-develop.3306.1.9ad9ff2526

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.
Files changed (122) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +14 -0
  3. package/dist/modules/auth/backend/roles/[id]/edit/page.js +2 -1
  4. package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
  5. package/dist/modules/auth/backend/users/[id]/edit/page.js +2 -5
  6. package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
  7. package/dist/modules/catalog/ai-agents.js +47 -5
  8. package/dist/modules/catalog/ai-agents.js.map +2 -2
  9. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js +2 -5
  10. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js.map +2 -2
  11. package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js +4 -3
  12. package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js.map +2 -2
  13. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +2 -9
  14. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  15. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +2 -9
  16. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
  17. package/dist/modules/customers/ai-agents.js +174 -1
  18. package/dist/modules/customers/ai-agents.js.map +2 -2
  19. package/dist/modules/customers/ai-tools/deal-analyzer-pack.js +196 -0
  20. package/dist/modules/customers/ai-tools/deal-analyzer-pack.js.map +7 -0
  21. package/dist/modules/customers/ai-tools.js +3 -1
  22. package/dist/modules/customers/ai-tools.js.map +2 -2
  23. package/dist/modules/customers/backend/customers/companies/page.js +11 -1
  24. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  25. package/dist/modules/customers/backend/customers/deals/page.js +11 -1
  26. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  27. package/dist/modules/customers/backend/customers/people/page.js +11 -1
  28. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  29. package/dist/modules/customers/commands/companies.js +37 -15
  30. package/dist/modules/customers/commands/companies.js.map +2 -2
  31. package/dist/modules/customers/components/detail/AiActionChips.js +2 -2
  32. package/dist/modules/customers/components/detail/AiActionChips.js.map +2 -2
  33. package/dist/modules/customers/components/detail/ChangelogTab.js +3 -9
  34. package/dist/modules/customers/components/detail/ChangelogTab.js.map +2 -2
  35. package/dist/modules/customers/components/detail/changelogValues.js +35 -0
  36. package/dist/modules/customers/components/detail/changelogValues.js.map +7 -0
  37. package/dist/modules/customers/components/detail/customFieldUtils.js +1 -1
  38. package/dist/modules/customers/components/detail/customFieldUtils.js.map +1 -1
  39. package/dist/modules/customers/components/formConfig.js +3 -2
  40. package/dist/modules/customers/components/formConfig.js.map +2 -2
  41. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js +4 -9
  42. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js.map +2 -2
  43. package/dist/modules/customers/widgets/injection/ai-deal-analyzer-trigger/widget.client.js +302 -0
  44. package/dist/modules/customers/widgets/injection/ai-deal-analyzer-trigger/widget.client.js.map +7 -0
  45. package/dist/modules/customers/widgets/injection/ai-deal-analyzer-trigger/widget.js +17 -0
  46. package/dist/modules/customers/widgets/injection/ai-deal-analyzer-trigger/widget.js.map +7 -0
  47. package/dist/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.client.js +2 -2
  48. package/dist/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.client.js.map +2 -2
  49. package/dist/modules/customers/widgets/injection-table.js +2 -2
  50. package/dist/modules/customers/widgets/injection-table.js.map +2 -2
  51. package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js +2 -5
  52. package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js.map +2 -2
  53. package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js +2 -5
  54. package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js.map +2 -2
  55. package/dist/modules/resources/backend/resources/resources/[id]/page.js +2 -5
  56. package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
  57. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +2 -25
  58. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
  59. package/dist/modules/sales/components/channels/ChannelOfferForm.js +2 -43
  60. package/dist/modules/sales/components/channels/ChannelOfferForm.js.map +2 -2
  61. package/dist/modules/sales/components/documents/LineItemDialog.js +162 -42
  62. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  63. package/dist/modules/sales/components/documents/customFieldHelpers.js +4 -31
  64. package/dist/modules/sales/components/documents/customFieldHelpers.js.map +2 -2
  65. package/package.json +15 -6
  66. package/src/modules/auth/backend/roles/[id]/edit/page.tsx +2 -1
  67. package/src/modules/auth/backend/users/[id]/edit/page.tsx +2 -5
  68. package/src/modules/business_rules/migrations/.snapshot-open-mercato.json +746 -394
  69. package/src/modules/catalog/ai-agents.ts +62 -4
  70. package/src/modules/catalog/backend/catalog/categories/[id]/edit/page.tsx +2 -5
  71. package/src/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.tsx +4 -3
  72. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +2 -12
  73. package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +2 -10
  74. package/src/modules/catalog/migrations/.snapshot-open-mercato.json +2519 -1307
  75. package/src/modules/customer_accounts/migrations/.snapshot-open-mercato.json +828 -834
  76. package/src/modules/customers/AGENTS.md +6 -1
  77. package/src/modules/customers/ai-agents.ts +213 -6
  78. package/src/modules/customers/ai-tools/deal-analyzer-pack.ts +286 -0
  79. package/src/modules/customers/ai-tools.ts +2 -0
  80. package/src/modules/customers/backend/customers/companies/page.tsx +10 -0
  81. package/src/modules/customers/backend/customers/deals/page.tsx +10 -0
  82. package/src/modules/customers/backend/customers/people/page.tsx +10 -0
  83. package/src/modules/customers/commands/companies.ts +38 -14
  84. package/src/modules/customers/components/detail/AiActionChips.tsx +2 -2
  85. package/src/modules/customers/components/detail/ChangelogTab.tsx +3 -10
  86. package/src/modules/customers/components/detail/changelogValues.ts +42 -0
  87. package/src/modules/customers/components/detail/customFieldUtils.ts +1 -1
  88. package/src/modules/customers/components/formConfig.tsx +3 -2
  89. package/src/modules/customers/i18n/de.json +7 -0
  90. package/src/modules/customers/i18n/en.json +7 -0
  91. package/src/modules/customers/i18n/es.json +7 -0
  92. package/src/modules/customers/i18n/pl.json +7 -0
  93. package/src/modules/customers/migrations/.snapshot-open-mercato.json +2313 -2336
  94. package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.tsx +4 -6
  95. package/src/modules/customers/widgets/injection/ai-deal-analyzer-trigger/widget.client.tsx +358 -0
  96. package/src/modules/customers/widgets/injection/ai-deal-analyzer-trigger/widget.ts +34 -0
  97. package/src/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.client.tsx +2 -2
  98. package/src/modules/customers/widgets/injection-table.ts +12 -9
  99. package/src/modules/data_sync/migrations/.snapshot-open-mercato.json +554 -210
  100. package/src/modules/dictionaries/migrations/.snapshot-open-mercato.json +146 -146
  101. package/src/modules/directory/backend/directory/organizations/[id]/edit/page.tsx +2 -5
  102. package/src/modules/directory/backend/directory/tenants/[id]/edit/page.tsx +2 -5
  103. package/src/modules/directory/migrations/.snapshot-open-mercato.json +273 -140
  104. package/src/modules/feature_toggles/migrations/.snapshot-open-mercato.json +242 -137
  105. package/src/modules/integrations/migrations/.snapshot-open-mercato.json +498 -195
  106. package/src/modules/payment_gateways/migrations/.snapshot-open-mercato.json +158 -157
  107. package/src/modules/perspectives/migrations/.snapshot-open-mercato.json +274 -129
  108. package/src/modules/planner/migrations/.snapshot-open-mercato.json +327 -157
  109. package/src/modules/progress/AGENTS.md +72 -0
  110. package/src/modules/resources/backend/resources/resources/[id]/page.tsx +2 -5
  111. package/src/modules/resources/migrations/.snapshot-open-mercato.json +920 -470
  112. package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +2 -27
  113. package/src/modules/sales/components/channels/ChannelOfferForm.tsx +2 -49
  114. package/src/modules/sales/components/documents/LineItemDialog.tsx +214 -78
  115. package/src/modules/sales/components/documents/customFieldHelpers.ts +4 -43
  116. package/src/modules/sales/i18n/de.json +3 -0
  117. package/src/modules/sales/i18n/en.json +3 -0
  118. package/src/modules/sales/i18n/es.json +3 -0
  119. package/src/modules/sales/i18n/pl.json +3 -0
  120. package/src/modules/sales/migrations/.snapshot-open-mercato.json +4740 -4778
  121. package/src/modules/shipping_carriers/migrations/.snapshot-open-mercato.json +340 -81
  122. package/src/modules/staff/migrations/.snapshot-open-mercato.json +1439 -729
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.tsx"],
4
- "sourcesContent": ["\"use client\"\n\n/**\n * Step 4.10 \u2014 Backend AiChat injection widget (client).\n *\n * Renders a compact, round, icon-only trigger in the DataTable\n * `:search-trailing` injection slot (right next to the list search input).\n * Clicking the trigger opens a popover listing the AI agents this widget\n * exposes \u2014 currently `customers.account_assistant`, but the popover is\n * the agreed extension point for additional customers-domain agents\n * (selection digesters, deal-shapers, etc.). Picking an agent opens a\n * right-side sheet embedding `<AiChat>` for that agent.\n *\n * `pageContext` shape matches spec \u00A710.1 (view / recordType / recordId\n * / extra). The host DataTable provides selection + total information\n * through the `context` prop injected by `<InjectionSpot>`.\n */\n\nimport * as React from 'react'\nimport { Building2, ChevronDown, Handshake, PanelRightOpen, Search, Sparkles, Users } from 'lucide-react'\nimport { AiChat, type AiChatSuggestion, type AiChatContextItem } from '@open-mercato/ui/ai/AiChat'\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 { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport const CUSTOMERS_AI_INJECT_AGENT_ID = 'customers.account_assistant'\n\nexport type CustomersAiInjectView =\n | 'customers.people.list'\n | 'customers.companies.list'\n | 'customers.deals.list'\n\nexport interface CustomersAiInjectPageContext {\n view: CustomersAiInjectView\n recordType: null\n recordId: string | null\n extra: {\n selectedCount: number\n totalMatching: number\n }\n}\n\nexport function computeCustomersAiInjectPageContext(\n context: HostInjectionContext | undefined,\n): CustomersAiInjectPageContext {\n return buildPageContext(context)\n}\n\ninterface HostInjectionContext {\n tableId?: string | null\n title?: string\n selectedRowIds?: string[]\n selectedCount?: number\n total?: number\n totalMatching?: number\n rowCount?: number\n}\n\ninterface AiAssistantTriggerProps {\n context?: HostInjectionContext\n}\n\nfunction readString(value: unknown): string {\n return typeof value === 'string' ? value : ''\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 resolveView(tableId: string | null | undefined): CustomersAiInjectView {\n if (typeof tableId === 'string') {\n if (tableId.includes('deals')) return 'customers.deals.list'\n if (tableId.includes('companies')) return 'customers.companies.list'\n }\n return 'customers.people.list'\n}\n\nfunction buildPageContext(context: HostInjectionContext | undefined): CustomersAiInjectPageContext {\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: resolveView(context?.tableId),\n recordType: null,\n recordId,\n extra: {\n selectedCount,\n totalMatching,\n },\n }\n}\n\nfunction useCustomerSuggestions(\n view: CustomersAiInjectView,\n hasSelection: boolean,\n selectedCount: number,\n): AiChatSuggestion[] {\n const t = useT()\n return React.useMemo(() => {\n if (view === 'customers.deals.list') {\n if (hasSelection) {\n return [\n {\n label: t(\n 'customers.ai_assistant.suggestions.summarizeSelectedDeals',\n 'Summarize selected deals',\n ),\n prompt: `Give me a summary of my ${selectedCount} selected deals \u2014 stage, value, and close date`,\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.atRiskSelectedDeals',\n 'Selected deals at risk',\n ),\n prompt: `Of my ${selectedCount} selected deals, which look at risk of slipping their close date?`,\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.companiesForSelectedDeals',\n 'Companies on selected deals',\n ),\n prompt: `List the companies associated with my ${selectedCount} selected deals`,\n icon: <Building2 className=\"size-4\" />,\n },\n ]\n }\n return [\n {\n label: t(\n 'customers.ai_assistant.suggestions.dealsClosingThisMonth',\n 'Deals closing this month',\n ),\n prompt: 'Which deals are closing this month?',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.dealsAtRisk',\n 'Deals at risk of slipping',\n ),\n prompt: 'Show me deals at risk of slipping their close date',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.totalOpenDealValue',\n 'Total open deal value',\n ),\n prompt: \"What's the total value of open deals across the pipeline?\",\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.topDealsByValue',\n 'Top deals by value',\n ),\n prompt: 'Show me the top 5 deals by value',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.searchDeals',\n 'Search for a deal',\n ),\n prompt: 'Search for deals by title, company, or stage',\n icon: <Search className=\"size-4\" />,\n },\n ]\n }\n if (view === 'customers.companies.list') {\n if (hasSelection) {\n return [\n {\n label: t(\n 'customers.ai_assistant.suggestions.summarizeSelectedCompanies',\n 'Summarize selected companies',\n ),\n prompt: `Give me a summary of my ${selectedCount} selected companies \u2014 size, industry, and recent activity`,\n icon: <Building2 className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.dealsForSelectedCompanies',\n 'Show deals for selected companies',\n ),\n prompt: `Show me all deals associated with my ${selectedCount} selected companies`,\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.peopleAtSelectedCompanies',\n 'List people at selected companies',\n ),\n prompt: `List the contacts (people) associated with my ${selectedCount} selected companies`,\n icon: <Users className=\"size-4\" />,\n },\n ]\n }\n return [\n {\n label: t(\n 'customers.ai_assistant.suggestions.searchCompanies',\n 'Search for a company',\n ),\n prompt: 'Search for companies by name, industry, or tax ID',\n icon: <Search className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.topCompaniesByDeals',\n 'Top companies by deal value',\n ),\n prompt: 'Show me the companies with the highest open deal value',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.companiesWithoutContacts',\n 'Companies missing contacts',\n ),\n prompt: 'Find companies that have no associated people yet',\n icon: <Users className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.companiesActivityOverview',\n 'Activity overview',\n ),\n prompt: 'Give me an overview of recent company-level activities and interactions',\n icon: <Building2 className=\"size-4\" />,\n },\n ]\n }\n if (hasSelection) {\n return [\n {\n label: t('customers.ai_assistant.suggestions.summarizeSelected', 'Summarize selected contacts'),\n prompt: `Give me a summary of my ${selectedCount} selected contacts \u2014 key details and recent activity`,\n icon: <Users className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.findDeals', 'Show deals for selected people'),\n prompt: `Show me all deals associated with my ${selectedCount} selected contacts`,\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.findCompanies', 'Find related companies'),\n prompt: `Find companies related to my ${selectedCount} selected contacts`,\n icon: <Building2 className=\"size-4\" />,\n },\n ]\n }\n return [\n {\n label: t('customers.ai_assistant.suggestions.searchPeople', 'Search for a contact'),\n prompt: 'Search for contacts by name, email, or company',\n icon: <Search className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.recentDeals', 'Show recent deals'),\n prompt: 'Show me the most recent deals and their current stages',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.topCompanies', 'List top companies'),\n prompt: 'List companies with the most associated contacts and deals',\n icon: <Building2 className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.activityOverview', 'Activity overview'),\n prompt: 'Give me an overview of recent customer activities and interactions',\n icon: <Users className=\"size-4\" />,\n },\n ]\n }, [view, hasSelection, selectedCount, t])\n}\n\nfunction useCustomerContextItems(pageContext: CustomersAiInjectPageContext): AiChatContextItem[] {\n const t = useT()\n return React.useMemo(() => {\n const items: AiChatContextItem[] = []\n const { selectedCount, totalMatching } = pageContext.extra\n const view = pageContext.view\n let selectedKey = 'customers.ai_assistant.context.selectedPeople'\n let selectedFallback = '{count} contacts selected'\n let matchingKey = 'customers.ai_assistant.context.matchingPeople'\n let matchingFallback = '{count} contacts in view'\n if (view === 'customers.companies.list') {\n selectedKey = 'customers.ai_assistant.context.selectedCompanies'\n selectedFallback = '{count} companies selected'\n matchingKey = 'customers.ai_assistant.context.matchingCompanies'\n matchingFallback = '{count} companies in view'\n } else if (view === 'customers.deals.list') {\n selectedKey = 'customers.ai_assistant.context.selectedDeals'\n selectedFallback = '{count} deals selected'\n matchingKey = 'customers.ai_assistant.context.matchingDeals'\n matchingFallback = '{count} deals in view'\n }\n if (selectedCount > 0) {\n items.push({ label: t(selectedKey, selectedFallback).replace('{count}', String(selectedCount)) })\n } else if (totalMatching > 0) {\n items.push({ label: t(matchingKey, matchingFallback).replace('{count}', String(totalMatching)) })\n }\n return items\n }, [pageContext, t])\n}\n\ninterface ViewCopy {\n welcomeDescriptionAll: string\n welcomeDescriptionSelection: string\n descriptionWithSelection: string\n triggerAriaLabel: string\n}\n\nfunction useViewCopy(view: CustomersAiInjectView, selectedCount: number): ViewCopy {\n const t = useT()\n return React.useMemo(() => {\n const replaceCount = (s: string) => s.replace('{count}', String(selectedCount))\n if (view === 'customers.deals.list') {\n return {\n welcomeDescriptionAll: t(\n 'customers.ai_assistant.sheet.welcomeDescriptionAllDeals',\n 'Ask about deals, pipeline, or close dates:',\n ),\n welcomeDescriptionSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.welcomeDescriptionSelectionDeals',\n 'Ready to explore your {count} selected deals:',\n ),\n ),\n descriptionWithSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.descriptionWithSelectionDeals',\n 'Working with {count} selected deals. Ask about their stage, value, and close dates.',\n ),\n ),\n triggerAriaLabel: t(\n 'customers.ai_assistant.trigger.ariaLabelDeals',\n 'Open AI assistant for deals',\n ),\n }\n }\n if (view === 'customers.companies.list') {\n return {\n welcomeDescriptionAll: t(\n 'customers.ai_assistant.sheet.welcomeDescriptionAllCompanies',\n 'Ask me anything about your companies, deals, and activities:',\n ),\n welcomeDescriptionSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.welcomeDescriptionSelectionCompanies',\n 'Ready to explore your {count} selected companies:',\n ),\n ),\n descriptionWithSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.descriptionWithSelectionCompanies',\n 'Working with {count} selected companies. Ask about their deals, contacts, and activities.',\n ),\n ),\n triggerAriaLabel: t(\n 'customers.ai_assistant.trigger.ariaLabelCompanies',\n 'Open AI assistant for companies',\n ),\n }\n }\n return {\n welcomeDescriptionAll: t(\n 'customers.ai_assistant.sheet.welcomeDescriptionAll',\n 'Ask me anything about your customers, companies, and deals:',\n ),\n welcomeDescriptionSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.welcomeDescriptionSelection',\n 'Ready to explore your {count} selected contacts:',\n ),\n ),\n descriptionWithSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.descriptionWithSelection',\n 'Working with {count} selected contacts. Ask about their details, deals, companies, and activities.',\n ),\n ),\n triggerAriaLabel: t(\n 'customers.ai_assistant.trigger.ariaLabel',\n 'Open AI assistant for people',\n ),\n }\n }, [view, selectedCount, t])\n}\n\ninterface CustomerAgentDescriptor {\n id: string\n label: string\n description: string\n icon: React.ReactNode\n}\n\nfunction useCustomerAgents(): CustomerAgentDescriptor[] {\n const t = useT()\n return React.useMemo(\n () => [\n {\n id: CUSTOMERS_AI_INJECT_AGENT_ID,\n label: t('customers.ai_assistant.agents.account.label', 'CRM Assistant'),\n description: t(\n 'customers.ai_assistant.agents.account.description',\n 'Explore people, companies, deals, and activities.',\n ),\n icon: <Sparkles className=\"size-4\" />,\n },\n ],\n [t],\n )\n}\n\nexport default function AiAssistantTriggerWidget({ context }: AiAssistantTriggerProps) {\n const t = useT()\n const dock = useAiDock()\n const [open, setOpen] = React.useState(false)\n const [popoverOpen, setPopoverOpen] = React.useState(false)\n const [activeAgent, setActiveAgent] = React.useState<string>(CUSTOMERS_AI_INJECT_AGENT_ID)\n const [lastAgent, setLastAgent] = React.useState<string | null>(null)\n const pageContext = React.useMemo(() => buildPageContext(context), [context])\n const agents = useCustomerAgents()\n\n const selectedCount = pageContext.extra.selectedCount\n const hasSelection = selectedCount > 0\n const suggestions = useCustomerSuggestions(pageContext.view, hasSelection, selectedCount)\n const contextItems = useCustomerContextItems(pageContext)\n const viewCopy = useViewCopy(pageContext.view, selectedCount)\n\n const openAgent = React.useCallback((agentId: string) => {\n setActiveAgent(agentId)\n setLastAgent(agentId)\n setPopoverOpen(false)\n if (dock.state.assistant?.agent === agentId) {\n dock.dock(dock.state.assistant)\n setOpen(false)\n return\n }\n setOpen(true)\n }, [dock])\n\n const handleSelectAgent = React.useCallback((agentId: string) => {\n openAgent(agentId)\n }, [openAgent])\n\n const handleMainTriggerClick = React.useCallback(() => {\n if (agents.length === 1) {\n openAgent(agents[0].id)\n return\n }\n if (lastAgent && agents.some((a) => a.id === lastAgent)) {\n openAgent(lastAgent)\n return\n }\n setPopoverOpen(true)\n }, [agents, lastAgent, openAgent])\n\n const handleDock = React.useCallback(() => {\n const agent = agents.find((a) => a.id === activeAgent) ?? agents[0]\n if (!agent) return\n dock.dock({\n agent: agent.id,\n label: agent.label,\n description: t('customers.ai_assistant.dock.subtitle', 'Customers'),\n pageContext: pageContext as unknown as Record<string, unknown>,\n placeholder: t(\n 'customers.ai_assistant.sheet.composerPlaceholder',\n 'Ask about people, companies, deals...',\n ),\n suggestions,\n contextItems,\n welcomeTitle: t('customers.ai_assistant.sheet.welcomeTitle', 'CRM Assistant'),\n welcomeDescription: hasSelection\n ? viewCopy.welcomeDescriptionSelection\n : viewCopy.welcomeDescriptionAll,\n })\n setOpen(false)\n }, [\n activeAgent,\n agents,\n contextItems,\n dock,\n hasSelection,\n pageContext,\n suggestions,\n t,\n viewCopy,\n ])\n\n const triggerLabel = viewCopy.triggerAriaLabel\n\n const labelText = t('customers.ai_assistant.trigger.label', 'AI')\n const moreAgentsLabel = t(\n 'customers.ai_assistant.trigger.moreAgentsAriaLabel',\n 'Choose an AI assistant',\n )\n\n return (\n <>\n <div className=\"inline-flex items-center\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleMainTriggerClick}\n data-ai-customers-inject-trigger=\"\"\n aria-label={triggerLabel}\n title={triggerLabel}\n className={cn(\n 'relative',\n 'hover:bg-brand-violet/10',\n agents.length > 1 && 'rounded-r-none border-r-0',\n )}\n >\n <Sparkles\n className=\"size-4 text-brand-violet\"\n aria-hidden\n />\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-customers-inject-selected-count={selectedCount}\n >\n {selectedCount}\n </span>\n ) : null}\n </Button>\n {agents.length > 1 ? (\n <Popover open={popoverOpen} onOpenChange={setPopoverOpen}>\n <PopoverTrigger asChild>\n <IconButton\n type=\"button\"\n variant=\"outline\"\n size=\"lg\"\n aria-label={moreAgentsLabel}\n title={moreAgentsLabel}\n className=\"rounded-l-none\"\n data-ai-customers-inject-picker=\"\"\n >\n <ChevronDown className=\"size-4\" aria-hidden />\n </IconButton>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-72 p-1\">\n <div className=\"px-3 pt-2 pb-1 text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('customers.ai_assistant.popover.heading', 'AI assistants')}\n </div>\n <div className=\"flex flex-col gap-0.5\">\n {agents.map((agent) => (\n <button\n key={agent.id}\n type=\"button\"\n onClick={() => handleSelectAgent(agent.id)}\n data-ai-customers-inject-agent-option={agent.id}\n className=\"flex items-start gap-2 rounded-sm px-2 py-2 text-left text-sm hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground focus-visible:outline-none\"\n >\n <span className=\"mt-0.5 inline-flex size-6 items-center justify-center rounded-full bg-secondary text-secondary-foreground\">\n {agent.icon}\n </span>\n <span className=\"flex-1 min-w-0\">\n <span className=\"block font-medium leading-tight\">{agent.label}</span>\n <span className=\"block text-xs text-muted-foreground leading-snug\">\n {agent.description}\n </span>\n </span>\n </button>\n ))}\n </div>\n </PopoverContent>\n </Popover>\n ) : null}\n </div>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent\n className={cn(\n // Mobile: full-screen sheet (no rounded corners, fills the\n // viewport). Desktop (\u2265sm): right-anchored side sheet.\n // The Dialog primitive ships a centering transform at the sm\n // breakpoint (`sm:top-1/2 sm:left-1/2 sm:-translate-x-1/2\n // sm:-translate-y-1/2 sm:inset-auto`); each must be overridden\n // at the same `sm:` breakpoint or the panel renders half off\n // the viewport.\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 z-[70]',\n )}\n data-ai-customers-inject-sheet=\"\"\n >\n <DialogHeader>\n <div className=\"flex items-center gap-3 pr-8\">\n {/* Dock button lives on the LEFT \u2014 the Dialog primitive\n auto-renders an X close button absolutely positioned in\n the top-right corner, so anything we drop in the header's\n right side visually collides with it. Mobile hides the\n dock entirely (the side panel is desktop-only). */}\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('customers.ai_assistant.sheet.dock', 'Dock to side')}\n title={t('customers.ai_assistant.sheet.dock', 'Dock to side')}\n onClick={handleDock}\n data-ai-customers-inject-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.ai_assistant.sheet.title', 'Customers AI assistant')}\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-customers-inject-selection-pill=\"\"\n data-ai-customers-inject-selected-count={selectedCount}\n >\n {t(\n 'customers.ai_assistant.sheet.selectionPill',\n 'Acting on {count} selected',\n ).replace('{count}', String(selectedCount))}\n </span>\n ) : null}\n </div>\n <DialogDescription>\n {hasSelection\n ? viewCopy.descriptionWithSelection\n : t(\n 'customers.ai_assistant.sheet.description',\n 'Your CRM assistant. Ask about people, companies, deals, and activities.',\n )}\n </DialogDescription>\n </DialogHeader>\n <CustomersChatBody\n activeAgent={activeAgent}\n pageContext={pageContext}\n suggestions={suggestions}\n contextItems={contextItems}\n hasSelection={hasSelection}\n selectedCount={selectedCount}\n viewCopy={viewCopy}\n />\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\ninterface CustomersChatBodyProps {\n activeAgent: string\n pageContext: CustomersAiInjectPageContext\n suggestions: AiChatSuggestion[]\n contextItems: AiChatContextItem[]\n hasSelection: boolean\n selectedCount: number\n viewCopy: ViewCopy\n}\n\nfunction CustomersChatBody({\n activeAgent,\n pageContext,\n suggestions,\n contextItems,\n hasSelection,\n selectedCount,\n viewCopy,\n}: CustomersChatBodyProps) {\n const t = useT()\n const sessions = useAiChatSessions()\n const session = sessions.getActiveSession(activeAgent)\n\n // Lazily ensure an open session exists. Running `ensureSession` inside an\n // effect (not inline during render) keeps the provider's setState calls\n // outside of the render phase. The first frame may render without a\n // session \u2014 that's fine, we render the tab strip alone until the next\n // tick when the new session is committed and `getActiveSession` returns it.\n React.useEffect(() => {\n if (!session) sessions.ensureSession(activeAgent)\n }, [activeAgent, session, sessions])\n\n return (\n <>\n <ChatPaneTabs agentId={activeAgent} className=\"border-b\" />\n <div className=\"min-h-0 flex-1\" data-ai-customers-inject-chat-container=\"\">\n {session ? (\n <AiChat\n // `key` forces a fresh mount when the active tab changes so the\n // AI SDK's status doesn't leak across sessions.\n key={session.id}\n agent={activeAgent}\n conversationId={session.conversationId}\n pageContext={pageContext as unknown as Record<string, unknown>}\n className=\"h-full\"\n placeholder={t(\n 'customers.ai_assistant.sheet.composerPlaceholder',\n 'Ask about people, companies, deals...',\n )}\n suggestions={suggestions}\n contextItems={contextItems}\n welcomeTitle={t('customers.ai_assistant.sheet.welcomeTitle', 'CRM Assistant')}\n welcomeDescription={\n hasSelection\n ? viewCopy.welcomeDescriptionSelection\n : viewCopy.welcomeDescriptionAll\n }\n />\n ) : null}\n </div>\n </>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\n/**\n * Step 4.10 \u2014 Backend AiChat injection widget (client).\n *\n * Renders a compact, round, icon-only trigger in the DataTable\n * `:search-trailing` injection slot (right next to the list search input).\n * Clicking the trigger opens a popover listing the AI agents this widget\n * exposes \u2014 currently `customers.account_assistant`, but the popover is\n * the agreed extension point for additional customers-domain agents\n * (selection digesters, deal-shapers, etc.). Picking an agent opens a\n * right-side sheet embedding `<AiChat>` for that agent.\n *\n * `pageContext` shape matches spec \u00A710.1 (view / recordType / recordId\n * / extra). The host DataTable provides selection + total information\n * through the `context` prop injected by `<InjectionSpot>`.\n */\n\nimport * as React from 'react'\nimport { Building2, ChevronDown, Handshake, PanelRightOpen, Search, Users } 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 { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport const CUSTOMERS_AI_INJECT_AGENT_ID = 'customers.account_assistant'\n\nexport type CustomersAiInjectView =\n | 'customers.people.list'\n | 'customers.companies.list'\n | 'customers.deals.list'\n\nexport interface CustomersAiInjectPageContext {\n view: CustomersAiInjectView\n recordType: null\n recordId: string | null\n extra: {\n selectedCount: number\n totalMatching: number\n }\n}\n\nexport function computeCustomersAiInjectPageContext(\n context: HostInjectionContext | undefined,\n): CustomersAiInjectPageContext {\n return buildPageContext(context)\n}\n\ninterface HostInjectionContext {\n tableId?: string | null\n title?: string\n selectedRowIds?: string[]\n selectedCount?: number\n total?: number\n totalMatching?: number\n rowCount?: number\n}\n\ninterface AiAssistantTriggerProps {\n context?: HostInjectionContext\n}\n\nfunction readString(value: unknown): string {\n return typeof value === 'string' ? value : ''\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 resolveView(tableId: string | null | undefined): CustomersAiInjectView {\n if (typeof tableId === 'string') {\n if (tableId.includes('deals')) return 'customers.deals.list'\n if (tableId.includes('companies')) return 'customers.companies.list'\n }\n return 'customers.people.list'\n}\n\nfunction buildPageContext(context: HostInjectionContext | undefined): CustomersAiInjectPageContext {\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: resolveView(context?.tableId),\n recordType: null,\n recordId,\n extra: {\n selectedCount,\n totalMatching,\n },\n }\n}\n\nfunction useCustomerSuggestions(\n view: CustomersAiInjectView,\n hasSelection: boolean,\n selectedCount: number,\n): AiChatSuggestion[] {\n const t = useT()\n return React.useMemo(() => {\n if (view === 'customers.deals.list') {\n if (hasSelection) {\n return [\n {\n label: t(\n 'customers.ai_assistant.suggestions.summarizeSelectedDeals',\n 'Summarize selected deals',\n ),\n prompt: `Give me a summary of my ${selectedCount} selected deals \u2014 stage, value, and close date`,\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.atRiskSelectedDeals',\n 'Selected deals at risk',\n ),\n prompt: `Of my ${selectedCount} selected deals, which look at risk of slipping their close date?`,\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.companiesForSelectedDeals',\n 'Companies on selected deals',\n ),\n prompt: `List the companies associated with my ${selectedCount} selected deals`,\n icon: <Building2 className=\"size-4\" />,\n },\n ]\n }\n return [\n {\n label: t(\n 'customers.ai_assistant.suggestions.dealsClosingThisMonth',\n 'Deals closing this month',\n ),\n prompt: 'Which deals are closing this month?',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.dealsAtRisk',\n 'Deals at risk of slipping',\n ),\n prompt: 'Show me deals at risk of slipping their close date',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.totalOpenDealValue',\n 'Total open deal value',\n ),\n prompt: \"What's the total value of open deals across the pipeline?\",\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.topDealsByValue',\n 'Top deals by value',\n ),\n prompt: 'Show me the top 5 deals by value',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.searchDeals',\n 'Search for a deal',\n ),\n prompt: 'Search for deals by title, company, or stage',\n icon: <Search className=\"size-4\" />,\n },\n ]\n }\n if (view === 'customers.companies.list') {\n if (hasSelection) {\n return [\n {\n label: t(\n 'customers.ai_assistant.suggestions.summarizeSelectedCompanies',\n 'Summarize selected companies',\n ),\n prompt: `Give me a summary of my ${selectedCount} selected companies \u2014 size, industry, and recent activity`,\n icon: <Building2 className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.dealsForSelectedCompanies',\n 'Show deals for selected companies',\n ),\n prompt: `Show me all deals associated with my ${selectedCount} selected companies`,\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.peopleAtSelectedCompanies',\n 'List people at selected companies',\n ),\n prompt: `List the contacts (people) associated with my ${selectedCount} selected companies`,\n icon: <Users className=\"size-4\" />,\n },\n ]\n }\n return [\n {\n label: t(\n 'customers.ai_assistant.suggestions.searchCompanies',\n 'Search for a company',\n ),\n prompt: 'Search for companies by name, industry, or tax ID',\n icon: <Search className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.topCompaniesByDeals',\n 'Top companies by deal value',\n ),\n prompt: 'Show me the companies with the highest open deal value',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.companiesWithoutContacts',\n 'Companies missing contacts',\n ),\n prompt: 'Find companies that have no associated people yet',\n icon: <Users className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.companiesActivityOverview',\n 'Activity overview',\n ),\n prompt: 'Give me an overview of recent company-level activities and interactions',\n icon: <Building2 className=\"size-4\" />,\n },\n ]\n }\n if (hasSelection) {\n return [\n {\n label: t('customers.ai_assistant.suggestions.summarizeSelected', 'Summarize selected contacts'),\n prompt: `Give me a summary of my ${selectedCount} selected contacts \u2014 key details and recent activity`,\n icon: <Users className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.findDeals', 'Show deals for selected people'),\n prompt: `Show me all deals associated with my ${selectedCount} selected contacts`,\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.findCompanies', 'Find related companies'),\n prompt: `Find companies related to my ${selectedCount} selected contacts`,\n icon: <Building2 className=\"size-4\" />,\n },\n ]\n }\n return [\n {\n label: t('customers.ai_assistant.suggestions.searchPeople', 'Search for a contact'),\n prompt: 'Search for contacts by name, email, or company',\n icon: <Search className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.recentDeals', 'Show recent deals'),\n prompt: 'Show me the most recent deals and their current stages',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.topCompanies', 'List top companies'),\n prompt: 'List companies with the most associated contacts and deals',\n icon: <Building2 className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.activityOverview', 'Activity overview'),\n prompt: 'Give me an overview of recent customer activities and interactions',\n icon: <Users className=\"size-4\" />,\n },\n ]\n }, [view, hasSelection, selectedCount, t])\n}\n\nfunction useCustomerContextItems(pageContext: CustomersAiInjectPageContext): AiChatContextItem[] {\n const t = useT()\n return React.useMemo(() => {\n const items: AiChatContextItem[] = []\n const { selectedCount, totalMatching } = pageContext.extra\n const view = pageContext.view\n let selectedKey = 'customers.ai_assistant.context.selectedPeople'\n let selectedFallback = '{count} contacts selected'\n let matchingKey = 'customers.ai_assistant.context.matchingPeople'\n let matchingFallback = '{count} contacts in view'\n if (view === 'customers.companies.list') {\n selectedKey = 'customers.ai_assistant.context.selectedCompanies'\n selectedFallback = '{count} companies selected'\n matchingKey = 'customers.ai_assistant.context.matchingCompanies'\n matchingFallback = '{count} companies in view'\n } else if (view === 'customers.deals.list') {\n selectedKey = 'customers.ai_assistant.context.selectedDeals'\n selectedFallback = '{count} deals selected'\n matchingKey = 'customers.ai_assistant.context.matchingDeals'\n matchingFallback = '{count} deals in view'\n }\n if (selectedCount > 0) {\n items.push({ label: t(selectedKey, selectedFallback).replace('{count}', String(selectedCount)) })\n } else if (totalMatching > 0) {\n items.push({ label: t(matchingKey, matchingFallback).replace('{count}', String(totalMatching)) })\n }\n return items\n }, [pageContext, t])\n}\n\ninterface ViewCopy {\n welcomeDescriptionAll: string\n welcomeDescriptionSelection: string\n descriptionWithSelection: string\n triggerAriaLabel: string\n}\n\nfunction useViewCopy(view: CustomersAiInjectView, selectedCount: number): ViewCopy {\n const t = useT()\n return React.useMemo(() => {\n const replaceCount = (s: string) => s.replace('{count}', String(selectedCount))\n if (view === 'customers.deals.list') {\n return {\n welcomeDescriptionAll: t(\n 'customers.ai_assistant.sheet.welcomeDescriptionAllDeals',\n 'Ask about deals, pipeline, or close dates:',\n ),\n welcomeDescriptionSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.welcomeDescriptionSelectionDeals',\n 'Ready to explore your {count} selected deals:',\n ),\n ),\n descriptionWithSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.descriptionWithSelectionDeals',\n 'Working with {count} selected deals. Ask about their stage, value, and close dates.',\n ),\n ),\n triggerAriaLabel: t(\n 'customers.ai_assistant.trigger.ariaLabelDeals',\n 'Open AI assistant for deals',\n ),\n }\n }\n if (view === 'customers.companies.list') {\n return {\n welcomeDescriptionAll: t(\n 'customers.ai_assistant.sheet.welcomeDescriptionAllCompanies',\n 'Ask me anything about your companies, deals, and activities:',\n ),\n welcomeDescriptionSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.welcomeDescriptionSelectionCompanies',\n 'Ready to explore your {count} selected companies:',\n ),\n ),\n descriptionWithSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.descriptionWithSelectionCompanies',\n 'Working with {count} selected companies. Ask about their deals, contacts, and activities.',\n ),\n ),\n triggerAriaLabel: t(\n 'customers.ai_assistant.trigger.ariaLabelCompanies',\n 'Open AI assistant for companies',\n ),\n }\n }\n return {\n welcomeDescriptionAll: t(\n 'customers.ai_assistant.sheet.welcomeDescriptionAll',\n 'Ask me anything about your customers, companies, and deals:',\n ),\n welcomeDescriptionSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.welcomeDescriptionSelection',\n 'Ready to explore your {count} selected contacts:',\n ),\n ),\n descriptionWithSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.descriptionWithSelection',\n 'Working with {count} selected contacts. Ask about their details, deals, companies, and activities.',\n ),\n ),\n triggerAriaLabel: t(\n 'customers.ai_assistant.trigger.ariaLabel',\n 'Open AI assistant for people',\n ),\n }\n }, [view, selectedCount, t])\n}\n\ninterface CustomerAgentDescriptor {\n id: string\n label: string\n description: string\n icon: React.ReactNode\n}\n\nfunction useCustomerAgents(): CustomerAgentDescriptor[] {\n const t = useT()\n return React.useMemo(\n () => [\n {\n id: CUSTOMERS_AI_INJECT_AGENT_ID,\n label: t('customers.ai_assistant.agents.account.label', 'CRM Assistant'),\n description: t(\n 'customers.ai_assistant.agents.account.description',\n 'Explore people, companies, deals, and activities.',\n ),\n icon: <AiIcon className=\"size-4\" />,\n },\n ],\n [t],\n )\n}\n\nexport default function AiAssistantTriggerWidget({ context }: AiAssistantTriggerProps) {\n const t = useT()\n const dock = useAiDock()\n const [open, setOpen] = React.useState(false)\n const [popoverOpen, setPopoverOpen] = React.useState(false)\n const [activeAgent, setActiveAgent] = React.useState<string>(CUSTOMERS_AI_INJECT_AGENT_ID)\n const [lastAgent, setLastAgent] = React.useState<string | null>(null)\n const pageContext = React.useMemo(() => buildPageContext(context), [context])\n const agents = useCustomerAgents()\n\n const selectedCount = pageContext.extra.selectedCount\n const hasSelection = selectedCount > 0\n const suggestions = useCustomerSuggestions(pageContext.view, hasSelection, selectedCount)\n const contextItems = useCustomerContextItems(pageContext)\n const viewCopy = useViewCopy(pageContext.view, selectedCount)\n\n const openAgent = React.useCallback((agentId: string) => {\n setActiveAgent(agentId)\n setLastAgent(agentId)\n setPopoverOpen(false)\n if (dock.state.assistant?.agent === agentId) {\n dock.dock(dock.state.assistant)\n setOpen(false)\n return\n }\n setOpen(true)\n }, [dock])\n\n const handleSelectAgent = React.useCallback((agentId: string) => {\n openAgent(agentId)\n }, [openAgent])\n\n const handleMainTriggerClick = React.useCallback(() => {\n if (agents.length === 1) {\n openAgent(agents[0].id)\n return\n }\n if (lastAgent && agents.some((a) => a.id === lastAgent)) {\n openAgent(lastAgent)\n return\n }\n setPopoverOpen(true)\n }, [agents, lastAgent, openAgent])\n\n const handleDock = React.useCallback(() => {\n const agent = agents.find((a) => a.id === activeAgent) ?? agents[0]\n if (!agent) return\n dock.dock({\n agent: agent.id,\n label: agent.label,\n description: t('customers.ai_assistant.dock.subtitle', 'Customers'),\n pageContext: pageContext as unknown as Record<string, unknown>,\n placeholder: t(\n 'customers.ai_assistant.sheet.composerPlaceholder',\n 'Ask about people, companies, deals...',\n ),\n suggestions,\n contextItems,\n welcomeTitle: t('customers.ai_assistant.sheet.welcomeTitle', 'CRM Assistant'),\n welcomeDescription: hasSelection\n ? viewCopy.welcomeDescriptionSelection\n : viewCopy.welcomeDescriptionAll,\n })\n setOpen(false)\n }, [\n activeAgent,\n agents,\n contextItems,\n dock,\n hasSelection,\n pageContext,\n suggestions,\n t,\n viewCopy,\n ])\n\n const triggerLabel = viewCopy.triggerAriaLabel\n\n const labelText = t('customers.ai_assistant.trigger.label', 'AI')\n const moreAgentsLabel = t(\n 'customers.ai_assistant.trigger.moreAgentsAriaLabel',\n 'Choose an AI assistant',\n )\n\n return (\n <>\n <div className=\"inline-flex items-center\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleMainTriggerClick}\n data-ai-customers-inject-trigger=\"\"\n aria-label={triggerLabel}\n title={triggerLabel}\n className={cn(\n 'relative',\n 'hover:bg-brand-violet/10',\n agents.length > 1 && 'rounded-r-none border-r-0',\n )}\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-customers-inject-selected-count={selectedCount}\n >\n {selectedCount}\n </span>\n ) : null}\n </Button>\n {agents.length > 1 ? (\n <Popover open={popoverOpen} onOpenChange={setPopoverOpen}>\n <PopoverTrigger asChild>\n <IconButton\n type=\"button\"\n variant=\"outline\"\n size=\"lg\"\n aria-label={moreAgentsLabel}\n title={moreAgentsLabel}\n className=\"rounded-l-none\"\n data-ai-customers-inject-picker=\"\"\n >\n <ChevronDown className=\"size-4\" aria-hidden />\n </IconButton>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-72 p-1\">\n <div className=\"px-3 pt-2 pb-1 text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('customers.ai_assistant.popover.heading', 'AI assistants')}\n </div>\n <div className=\"flex flex-col gap-0.5\">\n {agents.map((agent) => (\n <button\n key={agent.id}\n type=\"button\"\n onClick={() => handleSelectAgent(agent.id)}\n data-ai-customers-inject-agent-option={agent.id}\n className=\"flex items-start gap-2 rounded-sm px-2 py-2 text-left text-sm hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground focus-visible:outline-none\"\n >\n <span className=\"mt-0.5 inline-flex size-6 items-center justify-center rounded-full bg-secondary text-secondary-foreground\">\n {agent.icon}\n </span>\n <span className=\"flex-1 min-w-0\">\n <span className=\"block font-medium leading-tight\">{agent.label}</span>\n <span className=\"block text-xs text-muted-foreground leading-snug\">\n {agent.description}\n </span>\n </span>\n </button>\n ))}\n </div>\n </PopoverContent>\n </Popover>\n ) : null}\n </div>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent\n className={cn(\n // Mobile: full-screen sheet (no rounded corners, fills the\n // viewport). Desktop (\u2265sm): right-anchored side sheet.\n // The Dialog primitive ships a centering transform at the sm\n // breakpoint (`sm:top-1/2 sm:left-1/2 sm:-translate-x-1/2\n // sm:-translate-y-1/2 sm:inset-auto`); each must be overridden\n // at the same `sm:` breakpoint or the panel renders half off\n // the viewport.\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 z-[70]',\n )}\n data-ai-customers-inject-sheet=\"\"\n >\n <DialogHeader>\n <div className=\"flex items-center gap-3 pr-8\">\n {/* Dock button lives on the LEFT \u2014 the Dialog primitive\n auto-renders an X close button absolutely positioned in\n the top-right corner, so anything we drop in the header's\n right side visually collides with it. Mobile hides the\n dock entirely (the side panel is desktop-only). */}\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('customers.ai_assistant.sheet.dock', 'Dock to side')}\n title={t('customers.ai_assistant.sheet.dock', 'Dock to side')}\n onClick={handleDock}\n data-ai-customers-inject-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.ai_assistant.sheet.title', 'Customers AI assistant')}\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-customers-inject-selection-pill=\"\"\n data-ai-customers-inject-selected-count={selectedCount}\n >\n {t(\n 'customers.ai_assistant.sheet.selectionPill',\n 'Acting on {count} selected',\n ).replace('{count}', String(selectedCount))}\n </span>\n ) : null}\n </div>\n <DialogDescription>\n {hasSelection\n ? viewCopy.descriptionWithSelection\n : t(\n 'customers.ai_assistant.sheet.description',\n 'Your CRM assistant. Ask about people, companies, deals, and activities.',\n )}\n </DialogDescription>\n </DialogHeader>\n <CustomersChatBody\n activeAgent={activeAgent}\n pageContext={pageContext}\n suggestions={suggestions}\n contextItems={contextItems}\n hasSelection={hasSelection}\n selectedCount={selectedCount}\n viewCopy={viewCopy}\n />\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\ninterface CustomersChatBodyProps {\n activeAgent: string\n pageContext: CustomersAiInjectPageContext\n suggestions: AiChatSuggestion[]\n contextItems: AiChatContextItem[]\n hasSelection: boolean\n selectedCount: number\n viewCopy: ViewCopy\n}\n\nfunction CustomersChatBody({\n activeAgent,\n pageContext,\n suggestions,\n contextItems,\n hasSelection,\n selectedCount,\n viewCopy,\n}: CustomersChatBodyProps) {\n const t = useT()\n const sessions = useAiChatSessions()\n const session = sessions.getActiveSession(activeAgent)\n\n // Lazily ensure an open session exists. Running `ensureSession` inside an\n // effect (not inline during render) keeps the provider's setState calls\n // outside of the render phase. The first frame may render without a\n // session \u2014 that's fine, we render the tab strip alone until the next\n // tick when the new session is committed and `getActiveSession` returns it.\n React.useEffect(() => {\n if (!session) sessions.ensureSession(activeAgent)\n }, [activeAgent, session, sessions])\n\n return (\n <>\n <ChatPaneTabs agentId={activeAgent} className=\"border-b\" />\n <div className=\"min-h-0 flex-1\" data-ai-customers-inject-chat-container=\"\">\n {session ? (\n <AiChat\n // `key` forces a fresh mount when the active tab changes so the\n // AI SDK's status doesn't leak across sessions.\n key={session.id}\n agent={activeAgent}\n conversationId={session.conversationId}\n pageContext={pageContext as unknown as Record<string, unknown>}\n className=\"h-full\"\n placeholder={t(\n 'customers.ai_assistant.sheet.composerPlaceholder',\n 'Ask about people, companies, deals...',\n )}\n suggestions={suggestions}\n contextItems={contextItems}\n welcomeTitle={t('customers.ai_assistant.sheet.welcomeTitle', 'CRM Assistant')}\n welcomeDescription={\n hasSelection\n ? viewCopy.welcomeDescriptionSelection\n : viewCopy.welcomeDescriptionAll\n }\n />\n ) : null}\n </div>\n </>\n )\n}\n"],
5
+ "mappings": ";AAmIkB,SA2Yd,UA3Yc,KA6YV,YA7YU;AAjHlB,YAAY,WAAW;AACvB,SAAS,WAAW,aAAa,WAAW,gBAAgB,QAAQ,aAAa;AACjF,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,SAAS,gBAAgB,sBAAsB;AACxD,SAAS,YAAY;AACrB,SAAS,UAAU;AAEZ,MAAM,+BAA+B;AAiBrC,SAAS,oCACd,SAC8B;AAC9B,SAAO,iBAAiB,OAAO;AACjC;AAgBA,SAAS,WAAW,OAAwB;AAC1C,SAAO,OAAO,UAAU,WAAW,QAAQ;AAC7C;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,YAAY,SAA2D;AAC9E,MAAI,OAAO,YAAY,UAAU;AAC/B,QAAI,QAAQ,SAAS,OAAO,EAAG,QAAO;AACtC,QAAI,QAAQ,SAAS,WAAW,EAAG,QAAO;AAAA,EAC5C;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,SAAyE;AACjG,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,YAAY,SAAS,OAAO;AAAA,IAClC,YAAY;AAAA,IACZ;AAAA,IACA,OAAO;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,uBACP,MACA,cACA,eACoB;AACpB,QAAM,IAAI,KAAK;AACf,SAAO,MAAM,QAAQ,MAAM;AACzB,QAAI,SAAS,wBAAwB;AACnC,UAAI,cAAc;AAChB,eAAO;AAAA,UACL;AAAA,YACE,OAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,YACA,QAAQ,2BAA2B,aAAa;AAAA,YAChD,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,UACtC;AAAA,UACA;AAAA,YACE,OAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,YACA,QAAQ,SAAS,aAAa;AAAA,YAC9B,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,UACtC;AAAA,UACA;AAAA,YACE,OAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,YACA,QAAQ,yCAAyC,aAAa;AAAA,YAC9D,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,UACtC;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,QACL;AAAA,UACE,OAAO;AAAA,YACL;AAAA,YACA;AAAA,UACF;AAAA,UACA,QAAQ;AAAA,UACR,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,QACtC;AAAA,QACA;AAAA,UACE,OAAO;AAAA,YACL;AAAA,YACA;AAAA,UACF;AAAA,UACA,QAAQ;AAAA,UACR,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,QACtC;AAAA,QACA;AAAA,UACE,OAAO;AAAA,YACL;AAAA,YACA;AAAA,UACF;AAAA,UACA,QAAQ;AAAA,UACR,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,QACtC;AAAA,QACA;AAAA,UACE,OAAO;AAAA,YACL;AAAA,YACA;AAAA,UACF;AAAA,UACA,QAAQ;AAAA,UACR,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,QACtC;AAAA,QACA;AAAA,UACE,OAAO;AAAA,YACL;AAAA,YACA;AAAA,UACF;AAAA,UACA,QAAQ;AAAA,UACR,MAAM,oBAAC,UAAO,WAAU,UAAS;AAAA,QACnC;AAAA,MACF;AAAA,IACF;AACA,QAAI,SAAS,4BAA4B;AACvC,UAAI,cAAc;AAChB,eAAO;AAAA,UACL;AAAA,YACE,OAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,YACA,QAAQ,2BAA2B,aAAa;AAAA,YAChD,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,UACtC;AAAA,UACA;AAAA,YACE,OAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,YACA,QAAQ,wCAAwC,aAAa;AAAA,YAC7D,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,UACtC;AAAA,UACA;AAAA,YACE,OAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,YACA,QAAQ,iDAAiD,aAAa;AAAA,YACtE,MAAM,oBAAC,SAAM,WAAU,UAAS;AAAA,UAClC;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,QACL;AAAA,UACE,OAAO;AAAA,YACL;AAAA,YACA;AAAA,UACF;AAAA,UACA,QAAQ;AAAA,UACR,MAAM,oBAAC,UAAO,WAAU,UAAS;AAAA,QACnC;AAAA,QACA;AAAA,UACE,OAAO;AAAA,YACL;AAAA,YACA;AAAA,UACF;AAAA,UACA,QAAQ;AAAA,UACR,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,QACtC;AAAA,QACA;AAAA,UACE,OAAO;AAAA,YACL;AAAA,YACA;AAAA,UACF;AAAA,UACA,QAAQ;AAAA,UACR,MAAM,oBAAC,SAAM,WAAU,UAAS;AAAA,QAClC;AAAA,QACA;AAAA,UACE,OAAO;AAAA,YACL;AAAA,YACA;AAAA,UACF;AAAA,UACA,QAAQ;AAAA,UACR,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,QACtC;AAAA,MACF;AAAA,IACF;AACA,QAAI,cAAc;AAChB,aAAO;AAAA,QACL;AAAA,UACE,OAAO,EAAE,wDAAwD,6BAA6B;AAAA,UAC9F,QAAQ,2BAA2B,aAAa;AAAA,UAChD,MAAM,oBAAC,SAAM,WAAU,UAAS;AAAA,QAClC;AAAA,QACA;AAAA,UACE,OAAO,EAAE,gDAAgD,gCAAgC;AAAA,UACzF,QAAQ,wCAAwC,aAAa;AAAA,UAC7D,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,QACtC;AAAA,QACA;AAAA,UACE,OAAO,EAAE,oDAAoD,wBAAwB;AAAA,UACrF,QAAQ,gCAAgC,aAAa;AAAA,UACrD,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,QACtC;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,QACE,OAAO,EAAE,mDAAmD,sBAAsB;AAAA,QAClF,QAAQ;AAAA,QACR,MAAM,oBAAC,UAAO,WAAU,UAAS;AAAA,MACnC;AAAA,MACA;AAAA,QACE,OAAO,EAAE,kDAAkD,mBAAmB;AAAA,QAC9E,QAAQ;AAAA,QACR,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,MACtC;AAAA,MACA;AAAA,QACE,OAAO,EAAE,mDAAmD,oBAAoB;AAAA,QAChF,QAAQ;AAAA,QACR,MAAM,oBAAC,aAAU,WAAU,UAAS;AAAA,MACtC;AAAA,MACA;AAAA,QACE,OAAO,EAAE,uDAAuD,mBAAmB;AAAA,QACnF,QAAQ;AAAA,QACR,MAAM,oBAAC,SAAM,WAAU,UAAS;AAAA,MAClC;AAAA,IACF;AAAA,EACF,GAAG,CAAC,MAAM,cAAc,eAAe,CAAC,CAAC;AAC3C;AAEA,SAAS,wBAAwB,aAAgE;AAC/F,QAAM,IAAI,KAAK;AACf,SAAO,MAAM,QAAQ,MAAM;AACzB,UAAM,QAA6B,CAAC;AACpC,UAAM,EAAE,eAAe,cAAc,IAAI,YAAY;AACrD,UAAM,OAAO,YAAY;AACzB,QAAI,cAAc;AAClB,QAAI,mBAAmB;AACvB,QAAI,cAAc;AAClB,QAAI,mBAAmB;AACvB,QAAI,SAAS,4BAA4B;AACvC,oBAAc;AACd,yBAAmB;AACnB,oBAAc;AACd,yBAAmB;AAAA,IACrB,WAAW,SAAS,wBAAwB;AAC1C,oBAAc;AACd,yBAAmB;AACnB,oBAAc;AACd,yBAAmB;AAAA,IACrB;AACA,QAAI,gBAAgB,GAAG;AACrB,YAAM,KAAK,EAAE,OAAO,EAAE,aAAa,gBAAgB,EAAE,QAAQ,WAAW,OAAO,aAAa,CAAC,EAAE,CAAC;AAAA,IAClG,WAAW,gBAAgB,GAAG;AAC5B,YAAM,KAAK,EAAE,OAAO,EAAE,aAAa,gBAAgB,EAAE,QAAQ,WAAW,OAAO,aAAa,CAAC,EAAE,CAAC;AAAA,IAClG;AACA,WAAO;AAAA,EACT,GAAG,CAAC,aAAa,CAAC,CAAC;AACrB;AASA,SAAS,YAAY,MAA6B,eAAiC;AACjF,QAAM,IAAI,KAAK;AACf,SAAO,MAAM,QAAQ,MAAM;AACzB,UAAM,eAAe,CAAC,MAAc,EAAE,QAAQ,WAAW,OAAO,aAAa,CAAC;AAC9E,QAAI,SAAS,wBAAwB;AACnC,aAAO;AAAA,QACL,uBAAuB;AAAA,UACrB;AAAA,UACA;AAAA,QACF;AAAA,QACA,6BAA6B;AAAA,UAC3B;AAAA,YACE;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,QACA,0BAA0B;AAAA,UACxB;AAAA,YACE;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,QACA,kBAAkB;AAAA,UAChB;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,QAAI,SAAS,4BAA4B;AACvC,aAAO;AAAA,QACL,uBAAuB;AAAA,UACrB;AAAA,UACA;AAAA,QACF;AAAA,QACA,6BAA6B;AAAA,UAC3B;AAAA,YACE;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,QACA,0BAA0B;AAAA,UACxB;AAAA,YACE;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,QACA,kBAAkB;AAAA,UAChB;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,MACL,uBAAuB;AAAA,QACrB;AAAA,QACA;AAAA,MACF;AAAA,MACA,6BAA6B;AAAA,QAC3B;AAAA,UACE;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,MACA,0BAA0B;AAAA,QACxB;AAAA,UACE;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,MACA,kBAAkB;AAAA,QAChB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,MAAM,eAAe,CAAC,CAAC;AAC7B;AASA,SAAS,oBAA+C;AACtD,QAAM,IAAI,KAAK;AACf,SAAO,MAAM;AAAA,IACX,MAAM;AAAA,MACJ;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,+CAA+C,eAAe;AAAA,QACvE,aAAa;AAAA,UACX;AAAA,UACA;AAAA,QACF;AAAA,QACA,MAAM,oBAAC,UAAO,WAAU,UAAS;AAAA,MACnC;AAAA,IACF;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AACF;AAEe,SAAR,yBAA0C,EAAE,QAAQ,GAA4B;AACrF,QAAM,IAAI,KAAK;AACf,QAAM,OAAO,UAAU;AACvB,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAC5C,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,KAAK;AAC1D,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAiB,4BAA4B;AACzF,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAwB,IAAI;AACpE,QAAM,cAAc,MAAM,QAAQ,MAAM,iBAAiB,OAAO,GAAG,CAAC,OAAO,CAAC;AAC5E,QAAM,SAAS,kBAAkB;AAEjC,QAAM,gBAAgB,YAAY,MAAM;AACxC,QAAM,eAAe,gBAAgB;AACrC,QAAM,cAAc,uBAAuB,YAAY,MAAM,cAAc,aAAa;AACxF,QAAM,eAAe,wBAAwB,WAAW;AACxD,QAAM,WAAW,YAAY,YAAY,MAAM,aAAa;AAE5D,QAAM,YAAY,MAAM,YAAY,CAAC,YAAoB;AACvD,mBAAe,OAAO;AACtB,iBAAa,OAAO;AACpB,mBAAe,KAAK;AACpB,QAAI,KAAK,MAAM,WAAW,UAAU,SAAS;AAC3C,WAAK,KAAK,KAAK,MAAM,SAAS;AAC9B,cAAQ,KAAK;AACb;AAAA,IACF;AACA,YAAQ,IAAI;AAAA,EACd,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,oBAAoB,MAAM,YAAY,CAAC,YAAoB;AAC/D,cAAU,OAAO;AAAA,EACnB,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,yBAAyB,MAAM,YAAY,MAAM;AACrD,QAAI,OAAO,WAAW,GAAG;AACvB,gBAAU,OAAO,CAAC,EAAE,EAAE;AACtB;AAAA,IACF;AACA,QAAI,aAAa,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,SAAS,GAAG;AACvD,gBAAU,SAAS;AACnB;AAAA,IACF;AACA,mBAAe,IAAI;AAAA,EACrB,GAAG,CAAC,QAAQ,WAAW,SAAS,CAAC;AAEjC,QAAM,aAAa,MAAM,YAAY,MAAM;AACzC,UAAM,QAAQ,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,WAAW,KAAK,OAAO,CAAC;AAClE,QAAI,CAAC,MAAO;AACZ,SAAK,KAAK;AAAA,MACR,OAAO,MAAM;AAAA,MACb,OAAO,MAAM;AAAA,MACb,aAAa,EAAE,wCAAwC,WAAW;AAAA,MAClE;AAAA,MACA,aAAa;AAAA,QACX;AAAA,QACA;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc,EAAE,6CAA6C,eAAe;AAAA,MAC5E,oBAAoB,eAChB,SAAS,8BACT,SAAS;AAAA,IACf,CAAC;AACD,YAAQ,KAAK;AAAA,EACf,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,eAAe,SAAS;AAE9B,QAAM,YAAY,EAAE,wCAAwC,IAAI;AAChE,QAAM,kBAAkB;AAAA,IACtB;AAAA,IACA;AAAA,EACF;AAEA,SACE,iCACE;AAAA,yBAAC,SAAI,WAAU,4BACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAQ;AAAA,UACR,SAAS;AAAA,UACT,oCAAiC;AAAA,UACjC,cAAY;AAAA,UACZ,OAAO;AAAA,UACP,WAAW;AAAA,YACT;AAAA,YACA;AAAA,YACA,OAAO,SAAS,KAAK;AAAA,UACvB;AAAA,UAEA;AAAA,gCAAC,UAAO,WAAU,UAAS;AAAA,YAC3B,oBAAC,UAAM,qBAAU;AAAA,YAChB,eACC;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,2CAAyC;AAAA,gBAExC;AAAA;AAAA,YACH,IACE;AAAA;AAAA;AAAA,MACN;AAAA,MACC,OAAO,SAAS,IACf,qBAAC,WAAQ,MAAM,aAAa,cAAc,gBACxC;AAAA,4BAAC,kBAAe,SAAO,MACrB;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,cAAY;AAAA,YACZ,OAAO;AAAA,YACP,WAAU;AAAA,YACV,mCAAgC;AAAA,YAEhC,8BAAC,eAAY,WAAU,UAAS,eAAW,MAAC;AAAA;AAAA,QAC9C,GACF;AAAA,QACA,qBAAC,kBAAe,OAAM,OAAM,WAAU,YACpC;AAAA,8BAAC,SAAI,WAAU,oFACZ,YAAE,0CAA0C,eAAe,GAC9D;AAAA,UACA,oBAAC,SAAI,WAAU,yBACZ,iBAAO,IAAI,CAAC,UACX;AAAA,YAAC;AAAA;AAAA,cAEC,MAAK;AAAA,cACL,SAAS,MAAM,kBAAkB,MAAM,EAAE;AAAA,cACzC,yCAAuC,MAAM;AAAA,cAC7C,WAAU;AAAA,cAEV;AAAA,oCAAC,UAAK,WAAU,6GACb,gBAAM,MACT;AAAA,gBACA,qBAAC,UAAK,WAAU,kBACd;AAAA,sCAAC,UAAK,WAAU,mCAAmC,gBAAM,OAAM;AAAA,kBAC/D,oBAAC,UAAK,WAAU,oDACb,gBAAM,aACT;AAAA,mBACF;AAAA;AAAA;AAAA,YAdK,MAAM;AAAA,UAeb,CACD,GACH;AAAA,WACF;AAAA,SACF,IACE;AAAA,OACN;AAAA,IACA,oBAAC,UAAO,MAAY,cAAc,SAChC;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAQT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA,kCAA+B;AAAA,QAE/B;AAAA,+BAAC,gBACC;AAAA,iCAAC,SAAI,WAAU,gCAMb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,cAAY,EAAE,qCAAqC,cAAc;AAAA,kBACjE,OAAO,EAAE,qCAAqC,cAAc;AAAA,kBAC5D,SAAS;AAAA,kBACT,iCAA8B;AAAA,kBAC9B,WAAU;AAAA,kBAEV,8BAAC,kBAAe,WAAU,UAAS,eAAW,MAAC;AAAA;AAAA,cACjD;AAAA,cACA,oBAAC,eAAY,WAAU,2BACpB,YAAE,sCAAsC,wBAAwB,GACnE;AAAA,cACC,eACC;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAU;AAAA,kBACV,2CAAwC;AAAA,kBACxC,2CAAyC;AAAA,kBAExC;AAAA,oBACC;AAAA,oBACA;AAAA,kBACF,EAAE,QAAQ,WAAW,OAAO,aAAa,CAAC;AAAA;AAAA,cAC5C,IACE;AAAA,eACN;AAAA,YACA,oBAAC,qBACE,yBACG,SAAS,2BACT;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,cACA;AAAA,cACA;AAAA;AAAA,UACF;AAAA;AAAA;AAAA,IACF,GACF;AAAA,KACF;AAEJ;AAYA,SAAS,kBAAkB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA2B;AACzB,QAAM,IAAI,KAAK;AACf,QAAM,WAAW,kBAAkB;AACnC,QAAM,UAAU,SAAS,iBAAiB,WAAW;AAOrD,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,QAAS,UAAS,cAAc,WAAW;AAAA,EAClD,GAAG,CAAC,aAAa,SAAS,QAAQ,CAAC;AAEnC,SACE,iCACE;AAAA,wBAAC,gBAAa,SAAS,aAAa,WAAU,YAAW;AAAA,IACzD,oBAAC,SAAI,WAAU,kBAAiB,2CAAwC,IACrE,oBACC;AAAA,MAAC;AAAA;AAAA,QAIC,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,6CAA6C,eAAe;AAAA,QAC5E,oBACE,eACI,SAAS,8BACT,SAAS;AAAA;AAAA,MAfV,QAAQ;AAAA,IAiBf,IACE,MACN;AAAA,KACF;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,302 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Handshake, PanelRightOpen, TrendingDown } from "lucide-react";
5
+ import { AiChat } from "@open-mercato/ui/ai/AiChat";
6
+ import { AiIcon } from "@open-mercato/ui/ai/AiIcon";
7
+ import { useAiDock } from "@open-mercato/ui/ai/AiDock";
8
+ import { useAiChatSessions } from "@open-mercato/ui/ai/AiChatSessions";
9
+ import { ChatPaneTabs } from "@open-mercato/ui/ai/ChatPaneTabs";
10
+ import { Button } from "@open-mercato/ui/primitives/button";
11
+ import { IconButton } from "@open-mercato/ui/primitives/icon-button";
12
+ import {
13
+ Dialog,
14
+ DialogContent,
15
+ DialogDescription,
16
+ DialogHeader,
17
+ DialogTitle
18
+ } from "@open-mercato/ui/primitives/dialog";
19
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
20
+ import { cn } from "@open-mercato/shared/lib/utils";
21
+ const DEAL_ANALYZER_AGENT_ID = "customers.deal_analyzer";
22
+ function readNumber(value) {
23
+ if (typeof value === "number" && Number.isFinite(value)) return value;
24
+ if (typeof value === "string") {
25
+ const parsed = Number.parseInt(value, 10);
26
+ if (Number.isFinite(parsed)) return parsed;
27
+ }
28
+ return 0;
29
+ }
30
+ function readString(value) {
31
+ return typeof value === "string" ? value : "";
32
+ }
33
+ function buildDealAnalyzerPageContext(context) {
34
+ const selectedIdsRaw = Array.isArray(context?.selectedRowIds) ? context?.selectedRowIds ?? [] : [];
35
+ const selectedIds = selectedIdsRaw.map(readString).filter((id) => id.length > 0);
36
+ const selectedCount = selectedIds.length > 0 ? selectedIds.length : readNumber(context?.selectedCount);
37
+ const totalMatching = readNumber(context?.totalMatching ?? context?.total ?? context?.rowCount);
38
+ const recordId = selectedIds.length > 0 ? selectedIds.join(",") : null;
39
+ return {
40
+ view: "customers.deals.list",
41
+ recordType: null,
42
+ recordId,
43
+ extra: {
44
+ selectedCount,
45
+ totalMatching
46
+ }
47
+ };
48
+ }
49
+ function useDealAnalyzerSuggestions(hasSelection, selectedCount) {
50
+ const t = useT();
51
+ return React.useMemo(() => {
52
+ if (hasSelection) {
53
+ return [
54
+ {
55
+ label: t(
56
+ "customers.deal_analyzer.suggestions.analyzeSelected",
57
+ "Analyze selected deals"
58
+ ),
59
+ prompt: `Analyze my ${selectedCount} selected deals and surface any that are stalled`,
60
+ icon: /* @__PURE__ */ jsx(TrendingDown, { className: "size-4" })
61
+ },
62
+ {
63
+ label: t(
64
+ "customers.deal_analyzer.suggestions.proposeStageMove",
65
+ "Propose stage moves for selected deals"
66
+ ),
67
+ prompt: `Analyze my ${selectedCount} selected deals and propose stage moves for stalled high-value ones`,
68
+ icon: /* @__PURE__ */ jsx(Handshake, { className: "size-4" })
69
+ }
70
+ ];
71
+ }
72
+ return [
73
+ {
74
+ label: t(
75
+ "customers.deal_analyzer.suggestions.analyzeStalledDeals",
76
+ "Analyze stalled deals"
77
+ ),
78
+ prompt: "Analyze stalled deals from the last 30 days and propose a stage move for the highest value one",
79
+ icon: /* @__PURE__ */ jsx(TrendingDown, { className: "size-4" })
80
+ },
81
+ {
82
+ label: t(
83
+ "customers.deal_analyzer.suggestions.showAtRiskPipeline",
84
+ "Show at-risk pipeline"
85
+ ),
86
+ prompt: "Show me deals with no activity in the last 14 days worth more than $5,000",
87
+ icon: /* @__PURE__ */ jsx(Handshake, { className: "size-4" })
88
+ },
89
+ {
90
+ label: t(
91
+ "customers.deal_analyzer.suggestions.overviewByStage",
92
+ "Deal health overview"
93
+ ),
94
+ prompt: "Give me a health overview of all open deals ranked by activity recency",
95
+ icon: /* @__PURE__ */ jsx(AiIcon, { className: "size-4" })
96
+ }
97
+ ];
98
+ }, [hasSelection, selectedCount, t]);
99
+ }
100
+ function useDealAnalyzerContextItems(pageContext) {
101
+ const t = useT();
102
+ return React.useMemo(() => {
103
+ const items = [];
104
+ const { selectedCount, totalMatching } = pageContext.extra;
105
+ if (selectedCount > 0) {
106
+ items.push({
107
+ label: t(
108
+ "customers.deal_analyzer.context.selectedDeals",
109
+ "{count} deals selected"
110
+ ).replace("{count}", String(selectedCount))
111
+ });
112
+ } else if (totalMatching > 0) {
113
+ items.push({
114
+ label: t(
115
+ "customers.deal_analyzer.context.dealsInView",
116
+ "{count} deals in view"
117
+ ).replace("{count}", String(totalMatching))
118
+ });
119
+ }
120
+ return items;
121
+ }, [pageContext, t]);
122
+ }
123
+ function DealAnalyzerTriggerWidget({ context }) {
124
+ const t = useT();
125
+ const dock = useAiDock();
126
+ const [open, setOpen] = React.useState(false);
127
+ const pageContext = React.useMemo(() => buildDealAnalyzerPageContext(context), [context]);
128
+ const selectedCount = pageContext.extra.selectedCount;
129
+ const hasSelection = selectedCount > 0;
130
+ const suggestions = useDealAnalyzerSuggestions(hasSelection, selectedCount);
131
+ const contextItems = useDealAnalyzerContextItems(pageContext);
132
+ const handleTriggerClick = React.useCallback(() => {
133
+ if (dock.state.assistant?.agent === DEAL_ANALYZER_AGENT_ID) {
134
+ dock.dock(dock.state.assistant);
135
+ setOpen(false);
136
+ return;
137
+ }
138
+ setOpen(true);
139
+ }, [dock]);
140
+ const handleDock = React.useCallback(() => {
141
+ dock.dock({
142
+ agent: DEAL_ANALYZER_AGENT_ID,
143
+ label: t("customers.deal_analyzer.sheet.title", "Deal Analyzer"),
144
+ description: t("customers.deal_analyzer.dock.subtitle", "Deals"),
145
+ pageContext,
146
+ placeholder: t(
147
+ "customers.deal_analyzer.sheet.composerPlaceholder",
148
+ "Analyze stalled deals, propose stage moves..."
149
+ ),
150
+ suggestions,
151
+ contextItems,
152
+ welcomeTitle: t("customers.deal_analyzer.sheet.welcomeTitle", "Deal Analyzer"),
153
+ welcomeDescription: hasSelection ? t(
154
+ "customers.deal_analyzer.sheet.welcomeDescriptionSelection",
155
+ "Ready to analyze your {count} selected deals:"
156
+ ).replace("{count}", String(selectedCount)) : t(
157
+ "customers.deal_analyzer.sheet.welcomeDescriptionAll",
158
+ "Analyze deal pipeline health and propose stage moves:"
159
+ )
160
+ });
161
+ setOpen(false);
162
+ }, [contextItems, dock, hasSelection, pageContext, selectedCount, suggestions, t]);
163
+ const triggerLabel = t(
164
+ "customers.deal_analyzer.trigger.ariaLabel",
165
+ "Open Deal Analyzer AI agent"
166
+ );
167
+ const labelText = t("customers.ai_assistant.trigger.label", "AI");
168
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
169
+ /* @__PURE__ */ jsxs(
170
+ Button,
171
+ {
172
+ type: "button",
173
+ variant: "outline",
174
+ onClick: handleTriggerClick,
175
+ "data-ai-deal-analyzer-trigger": "",
176
+ "aria-label": triggerLabel,
177
+ title: triggerLabel,
178
+ className: cn("relative", "hover:bg-brand-violet/10"),
179
+ children: [
180
+ /* @__PURE__ */ jsx(AiIcon, { className: "size-4" }),
181
+ /* @__PURE__ */ jsx("span", { children: labelText }),
182
+ hasSelection ? /* @__PURE__ */ jsx(
183
+ "span",
184
+ {
185
+ 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",
186
+ "data-ai-deal-analyzer-selected-count": selectedCount,
187
+ children: selectedCount
188
+ }
189
+ ) : null
190
+ ]
191
+ }
192
+ ),
193
+ /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(
194
+ DialogContent,
195
+ {
196
+ className: cn(
197
+ "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",
198
+ "sm:top-0 sm:bottom-0 sm:right-0 sm:left-auto sm:translate-x-0 sm:translate-y-0",
199
+ "sm:max-w-xl sm:w-[36rem] sm:rounded-l-2xl sm:h-screen sm:max-h-screen",
200
+ "flex flex-col gap-3 p-4"
201
+ ),
202
+ "data-ai-deal-analyzer-sheet": "",
203
+ children: [
204
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
205
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 pr-8", children: [
206
+ /* @__PURE__ */ jsx(
207
+ IconButton,
208
+ {
209
+ type: "button",
210
+ variant: "ghost",
211
+ size: "sm",
212
+ "aria-label": t("customers.deal_analyzer.sheet.dock", "Dock to side"),
213
+ title: t("customers.deal_analyzer.sheet.dock", "Dock to side"),
214
+ onClick: handleDock,
215
+ "data-ai-deal-analyzer-dock": "",
216
+ className: "hidden lg:inline-flex shrink-0",
217
+ children: /* @__PURE__ */ jsx(PanelRightOpen, { className: "size-4", "aria-hidden": true })
218
+ }
219
+ ),
220
+ /* @__PURE__ */ jsx(DialogTitle, { className: "flex-1 min-w-0 truncate", children: t("customers.deal_analyzer.sheet.title", "Deal Analyzer") }),
221
+ hasSelection ? /* @__PURE__ */ jsx(
222
+ "span",
223
+ {
224
+ className: "shrink-0 inline-flex items-center rounded-full border border-border bg-secondary px-2 py-0.5 text-xs text-secondary-foreground",
225
+ "data-ai-deal-analyzer-selection-pill": "",
226
+ "data-ai-deal-analyzer-selected-count": selectedCount,
227
+ children: t(
228
+ "customers.deal_analyzer.sheet.selectionPill",
229
+ "Acting on {count} deals"
230
+ ).replace("{count}", String(selectedCount))
231
+ }
232
+ ) : null
233
+ ] }),
234
+ /* @__PURE__ */ jsx(DialogDescription, { children: hasSelection ? t(
235
+ "customers.deal_analyzer.sheet.descriptionWithSelection",
236
+ "Analyzing {count} selected deals for pipeline health and stage move proposals."
237
+ ).replace("{count}", String(selectedCount)) : t(
238
+ "customers.deal_analyzer.sheet.description",
239
+ "Multi-step deal health analyzer. Surfaces stalled deals and proposes stage transitions for approval."
240
+ ) })
241
+ ] }),
242
+ /* @__PURE__ */ jsx(
243
+ DealAnalyzerChatBody,
244
+ {
245
+ pageContext,
246
+ suggestions,
247
+ contextItems,
248
+ hasSelection,
249
+ selectedCount
250
+ }
251
+ )
252
+ ]
253
+ }
254
+ ) })
255
+ ] });
256
+ }
257
+ function DealAnalyzerChatBody({
258
+ pageContext,
259
+ suggestions,
260
+ contextItems,
261
+ hasSelection,
262
+ selectedCount
263
+ }) {
264
+ const t = useT();
265
+ const sessions = useAiChatSessions();
266
+ const session = sessions.getActiveSession(DEAL_ANALYZER_AGENT_ID);
267
+ React.useEffect(() => {
268
+ if (!session) sessions.ensureSession(DEAL_ANALYZER_AGENT_ID);
269
+ }, [session, sessions]);
270
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
271
+ /* @__PURE__ */ jsx(ChatPaneTabs, { agentId: DEAL_ANALYZER_AGENT_ID, className: "border-b" }),
272
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1", "data-ai-deal-analyzer-chat-container": "", children: session ? /* @__PURE__ */ jsx(
273
+ AiChat,
274
+ {
275
+ agent: DEAL_ANALYZER_AGENT_ID,
276
+ conversationId: session.conversationId,
277
+ pageContext,
278
+ className: "h-full",
279
+ placeholder: t(
280
+ "customers.deal_analyzer.sheet.composerPlaceholder",
281
+ "Analyze stalled deals, propose stage moves..."
282
+ ),
283
+ suggestions,
284
+ contextItems,
285
+ welcomeTitle: t("customers.deal_analyzer.sheet.welcomeTitle", "Deal Analyzer"),
286
+ welcomeDescription: hasSelection ? t(
287
+ "customers.deal_analyzer.sheet.welcomeDescriptionSelection",
288
+ "Ready to analyze your {count} selected deals:"
289
+ ).replace("{count}", String(selectedCount)) : t(
290
+ "customers.deal_analyzer.sheet.welcomeDescriptionAll",
291
+ "Analyze deal pipeline health and propose stage moves:"
292
+ )
293
+ },
294
+ session.id
295
+ ) : null })
296
+ ] });
297
+ }
298
+ export {
299
+ DEAL_ANALYZER_AGENT_ID,
300
+ DealAnalyzerTriggerWidget as default
301
+ };
302
+ //# sourceMappingURL=widget.client.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 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": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,17 @@
1
+ import DealAnalyzerTriggerWidget from "./widget.client.js";
2
+ const widget = {
3
+ metadata: {
4
+ id: "customers.injection.ai-deal-analyzer-trigger",
5
+ title: "Deal Analyzer Trigger",
6
+ description: "Renders an AI button in the deals list search-trailing slot that opens a sheet embedding the deal analyzer agent.",
7
+ features: ["customers.deals.view", "ai_assistant.view"],
8
+ priority: 90,
9
+ enabled: true
10
+ },
11
+ Widget: DealAnalyzerTriggerWidget
12
+ };
13
+ var widget_default = widget;
14
+ export {
15
+ widget_default as default
16
+ };
17
+ //# sourceMappingURL=widget.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/widgets/injection/ai-deal-analyzer-trigger/widget.ts"],
4
+ "sourcesContent": ["import type { InjectionWidgetModule } from '@open-mercato/shared/modules/widgets/injection'\nimport DealAnalyzerTriggerWidget from './widget.client'\n\n/**\n * Step d4 \u2014 Deal Analyzer AiChat injection widget.\n *\n * Drops the `ai-deal-analyzer-trigger` widget into the Deals list DataTable\n * `:search-trailing` injection slot, rendering adjacent to the search input.\n *\n * The trigger opens a right-side sheet embedding\n * `<AiChat agent=\"customers.deal_analyzer\" pageContext={...} />`.\n *\n * `pageContext` follows spec \u00A710.1 shape:\n * { view: 'customers.deals.list',\n * recordType: null,\n * recordId: string, // \"\" or comma-separated selected deal UUIDs\n * extra: { selectedCount, totalMatching } }\n *\n * Feature-gated behind `customers.deals.view` + `ai_assistant.view`.\n */\nconst widget: InjectionWidgetModule<Record<string, unknown>, Record<string, unknown>> = {\n metadata: {\n id: 'customers.injection.ai-deal-analyzer-trigger',\n title: 'Deal Analyzer Trigger',\n description:\n 'Renders an AI button in the deals list search-trailing slot that opens a sheet embedding the deal analyzer agent.',\n features: ['customers.deals.view', 'ai_assistant.view'],\n priority: 90,\n enabled: true,\n },\n Widget: DealAnalyzerTriggerWidget,\n}\n\nexport default widget\n"],
5
+ "mappings": "AACA,OAAO,+BAA+B;AAmBtC,MAAM,SAAkF;AAAA,EACtF,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,aACE;AAAA,IACF,UAAU,CAAC,wBAAwB,mBAAmB;AAAA,IACtD,UAAU;AAAA,IACV,SAAS;AAAA,EACX;AAAA,EACA,QAAQ;AACV;AAEA,IAAO,iBAAQ;",
6
+ "names": []
7
+ }
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- import { Sparkles } from "lucide-react";
4
+ import { AiIcon } from "@open-mercato/ui/ai/AiIcon";
5
5
  import { AiChat } from "@open-mercato/ui/ai/AiChat";
6
6
  import { Button } from "@open-mercato/ui/primitives/button";
7
7
  import {
@@ -56,7 +56,7 @@ function AiDealDetailTriggerWidget({ context, data }) {
56
56
  "Open AI assistant for this deal"
57
57
  ),
58
58
  children: [
59
- /* @__PURE__ */ jsx(Sparkles, { className: "size-4", "aria-hidden": true }),
59
+ /* @__PURE__ */ jsx(AiIcon, { className: "size-4" }),
60
60
  /* @__PURE__ */ jsx("span", { children: t("customers.ai_assistant.dealDetail.trigger.label", "Ask AI") })
61
61
  ]
62
62
  }