@open-mercato/core 0.6.4-develop.4199.1.86677441c2 → 0.6.4-develop.4210.1.d412061cfe

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 (99) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/api_docs/frontend/docs/api/Explorer.js +14 -14
  3. package/dist/modules/api_docs/frontend/docs/api/Explorer.js.map +2 -2
  4. package/dist/modules/attachments/components/AttachmentContentPreview.js +2 -2
  5. package/dist/modules/attachments/components/AttachmentContentPreview.js.map +2 -2
  6. package/dist/modules/audit_logs/backend/audit-logs/page.js +3 -3
  7. package/dist/modules/audit_logs/backend/audit-logs/page.js.map +2 -2
  8. package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js +3 -7
  9. package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js.map +2 -2
  10. package/dist/modules/customers/components/detail/ActivityCard.js +2 -2
  11. package/dist/modules/customers/components/detail/ActivityCard.js.map +2 -2
  12. package/dist/modules/customers/components/detail/AssignRoleDialog.js +34 -49
  13. package/dist/modules/customers/components/detail/AssignRoleDialog.js.map +2 -2
  14. package/dist/modules/customers/components/detail/ChangelogEntryRow.js +2 -2
  15. package/dist/modules/customers/components/detail/ChangelogEntryRow.js.map +2 -2
  16. package/dist/modules/customers/components/detail/CompanyDetailHeader.js +10 -1
  17. package/dist/modules/customers/components/detail/CompanyDetailHeader.js.map +2 -2
  18. package/dist/modules/customers/components/detail/DealLinkedEntitiesTab.js +7 -51
  19. package/dist/modules/customers/components/detail/DealLinkedEntitiesTab.js.map +2 -2
  20. package/dist/modules/customers/components/detail/DetailTabsLayout.js +1 -1
  21. package/dist/modules/customers/components/detail/DetailTabsLayout.js.map +2 -2
  22. package/dist/modules/customers/components/detail/ManageTagsDialog.js +25 -33
  23. package/dist/modules/customers/components/detail/ManageTagsDialog.js.map +2 -2
  24. package/dist/modules/customers/components/detail/PersonCard.js +3 -2
  25. package/dist/modules/customers/components/detail/PersonCard.js.map +2 -2
  26. package/dist/modules/customers/components/detail/PersonDetailHeader.js +3 -2
  27. package/dist/modules/customers/components/detail/PersonDetailHeader.js.map +2 -2
  28. package/dist/modules/customers/components/detail/RoleAssignmentRow.js +4 -5
  29. package/dist/modules/customers/components/detail/RoleAssignmentRow.js.map +2 -2
  30. package/dist/modules/customers/components/detail/utils.js +0 -7
  31. package/dist/modules/customers/components/detail/utils.js.map +2 -2
  32. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js +3 -8
  33. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js.map +2 -2
  34. package/dist/modules/dictionaries/components/AppearanceSelector.js +3 -4
  35. package/dist/modules/dictionaries/components/AppearanceSelector.js.map +2 -2
  36. package/dist/modules/integrations/backend/integrations/[id]/page.js +17 -17
  37. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
  38. package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js +1 -1
  39. package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js.map +2 -2
  40. package/dist/modules/planner/components/AvailabilityRulesEditor.js +65 -1
  41. package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
  42. package/dist/modules/planner/lib/deleteAvailabilityRuleSet.js +20 -0
  43. package/dist/modules/planner/lib/deleteAvailabilityRuleSet.js.map +7 -0
  44. package/dist/modules/resources/backend/resources/resources/[id]/page.js +2 -2
  45. package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
  46. package/dist/modules/sales/api/quotes/accept/route.js +14 -37
  47. package/dist/modules/sales/api/quotes/accept/route.js.map +3 -3
  48. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +1 -1
  49. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
  50. package/dist/modules/sales/backend/sales/documents/[id]/page.js +1 -1
  51. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  52. package/dist/modules/sales/commands/documents.js +6 -2
  53. package/dist/modules/sales/commands/documents.js.map +2 -2
  54. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +3 -2
  55. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  56. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +1 -1
  57. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  58. package/dist/modules/translations/components/TranslationDrawerAction.js +27 -65
  59. package/dist/modules/translations/components/TranslationDrawerAction.js.map +2 -2
  60. package/dist/modules/translations/components/TranslationManager.js +2 -2
  61. package/dist/modules/translations/components/TranslationManager.js.map +2 -2
  62. package/dist/modules/translations/widgets/injection/translation-manager/widget.client.js +54 -92
  63. package/dist/modules/translations/widgets/injection/translation-manager/widget.client.js.map +2 -2
  64. package/package.json +7 -7
  65. package/src/modules/api_docs/frontend/docs/api/Explorer.tsx +14 -14
  66. package/src/modules/attachments/components/AttachmentContentPreview.tsx +2 -2
  67. package/src/modules/audit_logs/backend/audit-logs/page.tsx +3 -3
  68. package/src/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.tsx +4 -8
  69. package/src/modules/customers/components/detail/ActivityCard.tsx +2 -4
  70. package/src/modules/customers/components/detail/AssignRoleDialog.tsx +28 -55
  71. package/src/modules/customers/components/detail/ChangelogEntryRow.tsx +6 -4
  72. package/src/modules/customers/components/detail/CompanyDetailHeader.tsx +7 -3
  73. package/src/modules/customers/components/detail/DealLinkedEntitiesTab.tsx +11 -49
  74. package/src/modules/customers/components/detail/DetailTabsLayout.tsx +1 -1
  75. package/src/modules/customers/components/detail/ManageTagsDialog.tsx +27 -36
  76. package/src/modules/customers/components/detail/PersonCard.tsx +3 -4
  77. package/src/modules/customers/components/detail/PersonDetailHeader.tsx +3 -4
  78. package/src/modules/customers/components/detail/RoleAssignmentRow.tsx +4 -7
  79. package/src/modules/customers/components/detail/utils.ts +0 -7
  80. package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.tsx +4 -9
  81. package/src/modules/dictionaries/components/AppearanceSelector.tsx +3 -4
  82. package/src/modules/integrations/backend/integrations/[id]/page.tsx +21 -21
  83. package/src/modules/planner/backend/planner/availability-rulesets/[id]/page.tsx +1 -1
  84. package/src/modules/planner/components/AvailabilityRulesEditor.tsx +62 -0
  85. package/src/modules/planner/lib/deleteAvailabilityRuleSet.ts +35 -0
  86. package/src/modules/resources/backend/resources/resources/[id]/page.tsx +2 -2
  87. package/src/modules/sales/api/quotes/accept/route.ts +22 -38
  88. package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +1 -1
  89. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +1 -1
  90. package/src/modules/sales/commands/documents.ts +16 -2
  91. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +3 -2
  92. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +1 -1
  93. package/src/modules/staff/i18n/de.json +5 -0
  94. package/src/modules/staff/i18n/en.json +5 -0
  95. package/src/modules/staff/i18n/es.json +5 -0
  96. package/src/modules/staff/i18n/pl.json +5 -0
  97. package/src/modules/translations/components/TranslationDrawerAction.tsx +31 -66
  98. package/src/modules/translations/components/TranslationManager.tsx +2 -2
  99. package/src/modules/translations/widgets/injection/translation-manager/widget.client.tsx +53 -84
@@ -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, 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": "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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 { ButtonGroup } from '@open-mercato/ui/primitives/button-group'\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 <ButtonGroup>\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('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-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 aria-label={moreAgentsLabel}\n title={moreAgentsLabel}\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 </ButtonGroup>\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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",
6
6
  "names": []
7
7
  }
@@ -3,6 +3,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { Ellipsis } from "lucide-react";
5
5
  import { Button } from "@open-mercato/ui/primitives/button";
6
+ import { ColorPicker } from "@open-mercato/ui/primitives/color-picker";
6
7
  import { Input } from "@open-mercato/ui/primitives/input";
7
8
  import { ICON_LIBRARY, ICON_SUGGESTIONS, renderDictionaryColor, renderDictionaryIcon } from "./dictionaryAppearance.js";
8
9
  const ICON_PICKER_LIMIT = 240;
@@ -84,13 +85,11 @@ function AppearanceSelector({
84
85
  ] }),
85
86
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
86
87
  /* @__PURE__ */ jsx(
87
- "input",
88
+ ColorPicker,
88
89
  {
89
- type: "color",
90
90
  value: normalizedColor,
91
- onChange: (event) => onColorChange(event.target.value),
91
+ onChange: (next) => onColorChange(next),
92
92
  disabled,
93
- className: "h-10 w-12 cursor-pointer rounded border border-border bg-background",
94
93
  "aria-label": labels.colorLabel
95
94
  }
96
95
  ),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/dictionaries/components/AppearanceSelector.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Ellipsis } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { ICON_LIBRARY, ICON_SUGGESTIONS, type IconOption, renderDictionaryColor, renderDictionaryIcon } from './dictionaryAppearance'\n\nexport type AppearanceSelectorLabels = {\n colorLabel: string\n colorHelp?: string\n colorClearLabel: string\n iconLabel: string\n iconPlaceholder: string\n iconPickerTriggerLabel: string\n iconSearchPlaceholder: string\n iconSearchEmptyLabel: string\n iconSuggestionsLabel: string\n iconClearLabel: string\n previewEmptyLabel: string\n}\n\ntype AppearanceSelectorProps = {\n icon: string | null | undefined\n color: string | null | undefined\n onIconChange: (next: string | null) => void\n onColorChange: (next: string | null) => void\n labels: AppearanceSelectorLabels\n disabled?: boolean\n iconSuggestions?: IconOption[]\n iconLibrary?: IconOption[]\n className?: string\n}\n\nconst ICON_PICKER_LIMIT = 240\n\nexport function AppearanceSelector({\n icon,\n color,\n onIconChange,\n onColorChange,\n labels,\n disabled = false,\n iconSuggestions = ICON_SUGGESTIONS,\n iconLibrary,\n className,\n}: AppearanceSelectorProps) {\n const normalizedIcon = icon ?? ''\n const normalizedColor = color ?? '#000000'\n const hasAppearance = Boolean(icon) || Boolean(color)\n const iconOptions = React.useMemo(() => (iconLibrary && iconLibrary.length ? iconLibrary : ICON_LIBRARY), [iconLibrary])\n const [pickerOpen, setPickerOpen] = React.useState(false)\n const [iconSearch, setIconSearch] = React.useState('')\n const pickerContainerRef = React.useRef<HTMLDivElement | null>(null)\n const searchInputRef = React.useRef<HTMLInputElement | null>(null)\n\n const closePicker = React.useCallback(() => {\n setPickerOpen(false)\n setIconSearch('')\n }, [])\n\n const handleIconSelection = React.useCallback(\n (next: string) => {\n onIconChange(next)\n closePicker()\n },\n [closePicker, onIconChange],\n )\n\n React.useEffect(() => {\n if (!pickerOpen) return\n const handlePointerDown = (event: PointerEvent) => {\n const target = event.target as Node\n if (pickerContainerRef.current?.contains(target)) return\n closePicker()\n }\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') closePicker()\n }\n document.addEventListener('pointerdown', handlePointerDown)\n document.addEventListener('keydown', handleKeyDown)\n return () => {\n document.removeEventListener('pointerdown', handlePointerDown)\n document.removeEventListener('keydown', handleKeyDown)\n }\n }, [closePicker, pickerOpen])\n\n React.useEffect(() => {\n if (!pickerOpen) return\n const frame = window.requestAnimationFrame(() => {\n searchInputRef.current?.focus()\n searchInputRef.current?.select()\n })\n return () => window.cancelAnimationFrame(frame)\n }, [pickerOpen])\n\n React.useEffect(() => {\n if (!disabled) return\n closePicker()\n }, [closePicker, disabled])\n\n const filteredIcons = React.useMemo(() => {\n const term = iconSearch.trim().toLowerCase()\n if (!term) {\n return iconOptions.slice(0, ICON_PICKER_LIMIT)\n }\n const matches = iconOptions.filter((option) => {\n const haystack = [option.label, option.value, ...(option.keywords ?? [])].join(' ').toLowerCase()\n return haystack.includes(term)\n })\n return matches.slice(0, ICON_PICKER_LIMIT)\n }, [iconOptions, iconSearch])\n\n return (\n <div className={['space-y-4', className].filter(Boolean).join(' ')}>\n <div className=\"space-y-2\">\n <label className=\"flex items-center gap-2 text-sm font-medium\">\n {labels.colorLabel}\n {labels.colorHelp ? <span className=\"text-xs font-normal text-muted-foreground\">{labels.colorHelp}</span> : null}\n </label>\n <div className=\"flex flex-wrap items-center gap-2\">\n <input\n type=\"color\"\n value={normalizedColor}\n onChange={(event) => onColorChange(event.target.value)}\n disabled={disabled}\n className=\"h-10 w-12 cursor-pointer rounded border border-border bg-background\"\n aria-label={labels.colorLabel}\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => onColorChange(null)}\n disabled={disabled || !color}\n >\n {labels.colorClearLabel}\n </Button>\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{labels.iconLabel}</label>\n <div ref={pickerContainerRef} className=\"relative\">\n <div className=\"flex gap-2\">\n <Input\n type=\"text\"\n value={normalizedIcon}\n onChange={(event) => onIconChange(event.target.value)}\n placeholder={labels.iconPlaceholder}\n className=\"flex-1\"\n disabled={disabled}\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n onClick={() => setPickerOpen((prev) => !prev)}\n aria-label={labels.iconPickerTriggerLabel}\n aria-expanded={pickerOpen}\n aria-haspopup=\"dialog\"\n disabled={disabled}\n >\n <Ellipsis className=\"h-4 w-4\" />\n </Button>\n </div>\n {pickerOpen ? (\n <div className=\"absolute left-0 right-0 top-full z-dropdown mt-2 rounded-md border border-border bg-popover p-3 shadow-lg\">\n <div className=\"space-y-3\">\n <Input\n ref={searchInputRef}\n type=\"search\"\n value={iconSearch}\n onChange={(event) => setIconSearch(event.target.value)}\n placeholder={labels.iconSearchPlaceholder}\n aria-label={labels.iconSearchPlaceholder}\n autoComplete=\"off\"\n />\n <div className=\"max-h-64 overflow-y-auto pr-1\">\n {filteredIcons.length ? (\n <div className=\"grid grid-cols-6 gap-2 sm:grid-cols-8\">\n {filteredIcons.map((option) => {\n const isSelected = normalizedIcon === option.value\n return (\n <Button\n key={option.value}\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className={isSelected ? 'bg-primary/10 text-primary ring-1 ring-primary/60' : undefined}\n onClick={() => handleIconSelection(option.value)}\n title={option.label}\n aria-label={option.label}\n aria-pressed={isSelected}\n >\n {renderDictionaryIcon(option.value, 'h-4 w-4')}\n </Button>\n )\n })}\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">{labels.iconSearchEmptyLabel}</p>\n )}\n </div>\n {iconSuggestions.length ? (\n <div className=\"space-y-2\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {labels.iconSuggestionsLabel}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {iconSuggestions.map((suggestion) => {\n const isSelected = normalizedIcon === suggestion.value\n return (\n <Button\n key={suggestion.value}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className={`text-xs ${isSelected ? 'border-primary bg-primary/10 text-primary' : ''}`}\n onClick={() => handleIconSelection(suggestion.value)}\n >\n {renderDictionaryIcon(suggestion.value, 'h-3 w-3')}\n {suggestion.label}\n </Button>\n )\n })}\n </div>\n </div>\n ) : null}\n <div className=\"flex justify-end\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => onIconChange(null)}>\n {labels.iconClearLabel}\n </Button>\n </div>\n </div>\n </div>\n ) : null}\n </div>\n </div>\n\n <div>\n <label className=\"text-sm font-medium\">Preview</label>\n <div className=\"flex items-center gap-3 rounded border border-dashed border-border px-3 py-2\">\n {hasAppearance ? (\n <>\n {renderDictionaryIcon(icon, 'h-5 w-5')}\n {renderDictionaryColor(color, 'h-4 w-4 rounded-full')}\n </>\n ) : (\n <span className=\"text-sm text-muted-foreground\">{labels.previewEmptyLabel}</span>\n )}\n </div>\n </div>\n </div>\n )\n}\n\nexport function useAppearanceState(initialIcon: string | null, initialColor: string | null) {\n const [icon, setIcon] = React.useState<string | null>(initialIcon)\n const [color, setColor] = React.useState<string | null>(initialColor)\n return React.useMemo(\n () => ({\n icon,\n color,\n setIcon,\n setColor,\n }),\n [icon, color],\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Ellipsis } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { ColorPicker } from '@open-mercato/ui/primitives/color-picker'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { ICON_LIBRARY, ICON_SUGGESTIONS, type IconOption, renderDictionaryColor, renderDictionaryIcon } from './dictionaryAppearance'\n\nexport type AppearanceSelectorLabels = {\n colorLabel: string\n colorHelp?: string\n colorClearLabel: string\n iconLabel: string\n iconPlaceholder: string\n iconPickerTriggerLabel: string\n iconSearchPlaceholder: string\n iconSearchEmptyLabel: string\n iconSuggestionsLabel: string\n iconClearLabel: string\n previewEmptyLabel: string\n}\n\ntype AppearanceSelectorProps = {\n icon: string | null | undefined\n color: string | null | undefined\n onIconChange: (next: string | null) => void\n onColorChange: (next: string | null) => void\n labels: AppearanceSelectorLabels\n disabled?: boolean\n iconSuggestions?: IconOption[]\n iconLibrary?: IconOption[]\n className?: string\n}\n\nconst ICON_PICKER_LIMIT = 240\n\nexport function AppearanceSelector({\n icon,\n color,\n onIconChange,\n onColorChange,\n labels,\n disabled = false,\n iconSuggestions = ICON_SUGGESTIONS,\n iconLibrary,\n className,\n}: AppearanceSelectorProps) {\n const normalizedIcon = icon ?? ''\n const normalizedColor = color ?? '#000000'\n const hasAppearance = Boolean(icon) || Boolean(color)\n const iconOptions = React.useMemo(() => (iconLibrary && iconLibrary.length ? iconLibrary : ICON_LIBRARY), [iconLibrary])\n const [pickerOpen, setPickerOpen] = React.useState(false)\n const [iconSearch, setIconSearch] = React.useState('')\n const pickerContainerRef = React.useRef<HTMLDivElement | null>(null)\n const searchInputRef = React.useRef<HTMLInputElement | null>(null)\n\n const closePicker = React.useCallback(() => {\n setPickerOpen(false)\n setIconSearch('')\n }, [])\n\n const handleIconSelection = React.useCallback(\n (next: string) => {\n onIconChange(next)\n closePicker()\n },\n [closePicker, onIconChange],\n )\n\n React.useEffect(() => {\n if (!pickerOpen) return\n const handlePointerDown = (event: PointerEvent) => {\n const target = event.target as Node\n if (pickerContainerRef.current?.contains(target)) return\n closePicker()\n }\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') closePicker()\n }\n document.addEventListener('pointerdown', handlePointerDown)\n document.addEventListener('keydown', handleKeyDown)\n return () => {\n document.removeEventListener('pointerdown', handlePointerDown)\n document.removeEventListener('keydown', handleKeyDown)\n }\n }, [closePicker, pickerOpen])\n\n React.useEffect(() => {\n if (!pickerOpen) return\n const frame = window.requestAnimationFrame(() => {\n searchInputRef.current?.focus()\n searchInputRef.current?.select()\n })\n return () => window.cancelAnimationFrame(frame)\n }, [pickerOpen])\n\n React.useEffect(() => {\n if (!disabled) return\n closePicker()\n }, [closePicker, disabled])\n\n const filteredIcons = React.useMemo(() => {\n const term = iconSearch.trim().toLowerCase()\n if (!term) {\n return iconOptions.slice(0, ICON_PICKER_LIMIT)\n }\n const matches = iconOptions.filter((option) => {\n const haystack = [option.label, option.value, ...(option.keywords ?? [])].join(' ').toLowerCase()\n return haystack.includes(term)\n })\n return matches.slice(0, ICON_PICKER_LIMIT)\n }, [iconOptions, iconSearch])\n\n return (\n <div className={['space-y-4', className].filter(Boolean).join(' ')}>\n <div className=\"space-y-2\">\n <label className=\"flex items-center gap-2 text-sm font-medium\">\n {labels.colorLabel}\n {labels.colorHelp ? <span className=\"text-xs font-normal text-muted-foreground\">{labels.colorHelp}</span> : null}\n </label>\n <div className=\"flex flex-wrap items-center gap-2\">\n <ColorPicker\n value={normalizedColor}\n onChange={(next) => onColorChange(next)}\n disabled={disabled}\n aria-label={labels.colorLabel}\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => onColorChange(null)}\n disabled={disabled || !color}\n >\n {labels.colorClearLabel}\n </Button>\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">{labels.iconLabel}</label>\n <div ref={pickerContainerRef} className=\"relative\">\n <div className=\"flex gap-2\">\n <Input\n type=\"text\"\n value={normalizedIcon}\n onChange={(event) => onIconChange(event.target.value)}\n placeholder={labels.iconPlaceholder}\n className=\"flex-1\"\n disabled={disabled}\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n onClick={() => setPickerOpen((prev) => !prev)}\n aria-label={labels.iconPickerTriggerLabel}\n aria-expanded={pickerOpen}\n aria-haspopup=\"dialog\"\n disabled={disabled}\n >\n <Ellipsis className=\"h-4 w-4\" />\n </Button>\n </div>\n {pickerOpen ? (\n <div className=\"absolute left-0 right-0 top-full z-dropdown mt-2 rounded-md border border-border bg-popover p-3 shadow-lg\">\n <div className=\"space-y-3\">\n <Input\n ref={searchInputRef}\n type=\"search\"\n value={iconSearch}\n onChange={(event) => setIconSearch(event.target.value)}\n placeholder={labels.iconSearchPlaceholder}\n aria-label={labels.iconSearchPlaceholder}\n autoComplete=\"off\"\n />\n <div className=\"max-h-64 overflow-y-auto pr-1\">\n {filteredIcons.length ? (\n <div className=\"grid grid-cols-6 gap-2 sm:grid-cols-8\">\n {filteredIcons.map((option) => {\n const isSelected = normalizedIcon === option.value\n return (\n <Button\n key={option.value}\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className={isSelected ? 'bg-primary/10 text-primary ring-1 ring-primary/60' : undefined}\n onClick={() => handleIconSelection(option.value)}\n title={option.label}\n aria-label={option.label}\n aria-pressed={isSelected}\n >\n {renderDictionaryIcon(option.value, 'h-4 w-4')}\n </Button>\n )\n })}\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">{labels.iconSearchEmptyLabel}</p>\n )}\n </div>\n {iconSuggestions.length ? (\n <div className=\"space-y-2\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {labels.iconSuggestionsLabel}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {iconSuggestions.map((suggestion) => {\n const isSelected = normalizedIcon === suggestion.value\n return (\n <Button\n key={suggestion.value}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className={`text-xs ${isSelected ? 'border-primary bg-primary/10 text-primary' : ''}`}\n onClick={() => handleIconSelection(suggestion.value)}\n >\n {renderDictionaryIcon(suggestion.value, 'h-3 w-3')}\n {suggestion.label}\n </Button>\n )\n })}\n </div>\n </div>\n ) : null}\n <div className=\"flex justify-end\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => onIconChange(null)}>\n {labels.iconClearLabel}\n </Button>\n </div>\n </div>\n </div>\n ) : null}\n </div>\n </div>\n\n <div>\n <label className=\"text-sm font-medium\">Preview</label>\n <div className=\"flex items-center gap-3 rounded border border-dashed border-border px-3 py-2\">\n {hasAppearance ? (\n <>\n {renderDictionaryIcon(icon, 'h-5 w-5')}\n {renderDictionaryColor(color, 'h-4 w-4 rounded-full')}\n </>\n ) : (\n <span className=\"text-sm text-muted-foreground\">{labels.previewEmptyLabel}</span>\n )}\n </div>\n </div>\n </div>\n )\n}\n\nexport function useAppearanceState(initialIcon: string | null, initialColor: string | null) {\n const [icon, setIcon] = React.useState<string | null>(initialIcon)\n const [color, setColor] = React.useState<string | null>(initialColor)\n return React.useMemo(\n () => ({\n icon,\n color,\n setIcon,\n setColor,\n }),\n [icon, color],\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -48,15 +48,15 @@ function isEditableCredentialField(field) {
48
48
  return !UNSUPPORTED_CREDENTIAL_FIELD_TYPES.has(field.type);
49
49
  }
50
50
  const LOG_LEVEL_STYLES = {
51
- info: "bg-blue-100 text-blue-800",
52
- warn: "bg-yellow-100 text-yellow-800",
53
- error: "bg-red-100 text-red-800"
51
+ info: "bg-status-info-bg text-status-info-text",
52
+ warn: "bg-status-warning-bg text-status-warning-text",
53
+ error: "bg-status-error-bg text-status-error-text"
54
54
  };
55
55
  const HEALTH_STATUS_STYLES = {
56
- healthy: "bg-green-100 text-green-800",
57
- degraded: "bg-yellow-100 text-yellow-800",
58
- unhealthy: "bg-red-100 text-red-800",
59
- unconfigured: "bg-zinc-100 text-zinc-700"
56
+ healthy: "bg-status-success-bg text-status-success-text",
57
+ degraded: "bg-status-warning-bg text-status-warning-text",
58
+ unhealthy: "bg-status-error-bg text-status-error-text",
59
+ unconfigured: "bg-status-neutral-bg text-status-neutral-text"
60
60
  };
61
61
  const HEALTH_STATUS_ICONS = {
62
62
  healthy: CheckCircle2,
@@ -91,7 +91,7 @@ function RunActivityStrip({
91
91
  const progress = typeof run.progressJob?.progressPercent === "number" ? run.progressJob.progressPercent : 0;
92
92
  const processed = typeof run.progressJob?.processedCount === "number" ? run.progressJob.processedCount : 0;
93
93
  const total = typeof run.progressJob?.totalCount === "number" ? run.progressJob.totalCount : null;
94
- const statusClass = run.status === "completed" ? "border-emerald-500/30 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300" : run.status === "failed" ? "border-red-500/30 bg-red-500/10 text-red-700 dark:text-red-300" : run.status === "cancelled" ? "border-zinc-500/30 bg-zinc-500/10 text-zinc-700 dark:text-zinc-300" : "border-blue-500/30 bg-blue-500/10 text-blue-700 dark:text-blue-300";
94
+ const statusClass = run.status === "completed" ? "border-status-success-border bg-status-success-bg text-status-success-text" : run.status === "failed" ? "border-status-error-border bg-status-error-bg text-status-error-text" : run.status === "cancelled" ? "border-status-neutral-border bg-status-neutral-bg text-status-neutral-text" : "border-status-info-border bg-status-info-bg text-status-info-text";
95
95
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3 rounded-lg border bg-muted/20 px-4 py-3 text-sm", children: [
96
96
  /* @__PURE__ */ jsxs(Badge, { variant: "outline", className: cn("gap-1.5", statusClass), children: [
97
97
  /* @__PURE__ */ jsx(RefreshCw, { className: cn("h-3.5 w-3.5", run.status === "running" ? "animate-spin" : "") }),
@@ -742,7 +742,7 @@ function IntegrationDetailPage({ params }) {
742
742
  ...trailingInjectedTabs.map((tab) => tab.id)
743
743
  ];
744
744
  const StateIcon = resolvedState?.isEnabled ? CheckCircle2 : XCircle;
745
- const stateBadgeClass = resolvedState?.isEnabled ? "border-emerald-500/30 bg-emerald-500/10 text-emerald-300" : "border-zinc-500/30 bg-zinc-500/10 text-zinc-300";
745
+ const stateBadgeClass = resolvedState?.isEnabled ? "border-status-success-border bg-status-success-bg text-status-success-text" : "border-status-neutral-border bg-status-neutral-bg text-status-neutral-text";
746
746
  const showCredentialActions = showCredentialsTab && activeTab === "credentials" && credentialFormFields.length > 0;
747
747
  React.useEffect(() => {
748
748
  setActiveTab(resolveRequestedIntegrationDetailTab(searchParams?.get("tab"), visibleTabIds));
@@ -885,7 +885,7 @@ function IntegrationDetailPage({ params }) {
885
885
  TabsTrigger,
886
886
  {
887
887
  value: "credentials",
888
- className: "mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-foreground aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0",
888
+ className: "mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-accent-indigo aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0",
889
889
  children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2", children: [
890
890
  /* @__PURE__ */ jsx(Key, { className: "h-4 w-4" }),
891
891
  /* @__PURE__ */ jsx("span", { children: t("integrations.detail.tabs.credentials") })
@@ -896,7 +896,7 @@ function IntegrationDetailPage({ params }) {
896
896
  TabsTrigger,
897
897
  {
898
898
  value: leadingInjectedTab.id,
899
- className: "mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-foreground aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0",
899
+ className: "mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-accent-indigo aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0",
900
900
  children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2", children: [
901
901
  /* @__PURE__ */ jsx(Settings, { className: "h-4 w-4" }),
902
902
  /* @__PURE__ */ jsx("span", { children: leadingInjectedTab.label })
@@ -907,7 +907,7 @@ function IntegrationDetailPage({ params }) {
907
907
  TabsTrigger,
908
908
  {
909
909
  value: "version",
910
- className: "mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-foreground aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0",
910
+ className: "mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-accent-indigo aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0",
911
911
  children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2", children: [
912
912
  /* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4" }),
913
913
  /* @__PURE__ */ jsx("span", { children: t("integrations.detail.tabs.version") })
@@ -918,7 +918,7 @@ function IntegrationDetailPage({ params }) {
918
918
  TabsTrigger,
919
919
  {
920
920
  value: "data-sync-schedule",
921
- className: "mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-foreground aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0",
921
+ className: "mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-accent-indigo aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0",
922
922
  children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2", children: [
923
923
  /* @__PURE__ */ jsx(Calendar, { className: "h-4 w-4" }),
924
924
  /* @__PURE__ */ jsx("span", { children: t("data_sync.integrationTab.title", "Sync schedules") })
@@ -929,7 +929,7 @@ function IntegrationDetailPage({ params }) {
929
929
  TabsTrigger,
930
930
  {
931
931
  value: "health",
932
- className: "mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-foreground aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0",
932
+ className: "mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-accent-indigo aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0",
933
933
  children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2", children: [
934
934
  /* @__PURE__ */ jsx(Activity, { className: "h-4 w-4" }),
935
935
  /* @__PURE__ */ jsx("span", { children: t("integrations.detail.tabs.health") })
@@ -940,7 +940,7 @@ function IntegrationDetailPage({ params }) {
940
940
  TabsTrigger,
941
941
  {
942
942
  value: "logs",
943
- className: "mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-foreground aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0",
943
+ className: "mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-accent-indigo aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0",
944
944
  children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2", children: [
945
945
  /* @__PURE__ */ jsx(FileText, { className: "h-4 w-4" }),
946
946
  /* @__PURE__ */ jsx("span", { children: t("integrations.detail.tabs.logs") })
@@ -951,7 +951,7 @@ function IntegrationDetailPage({ params }) {
951
951
  TabsTrigger,
952
952
  {
953
953
  value: tab.id,
954
- className: "mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-foreground aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0",
954
+ className: "mr-8 h-auto rounded-none border-b-2 border-transparent bg-transparent px-0 py-2.5 text-sm font-medium text-muted-foreground shadow-none transition-colors hover:bg-transparent hover:text-foreground aria-selected:border-accent-indigo aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none last:mr-0",
955
955
  children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2", children: [
956
956
  /* @__PURE__ */ jsx(Settings, { className: "h-4 w-4" }),
957
957
  /* @__PURE__ */ jsx("span", { children: tab.label })
@@ -961,7 +961,7 @@ function IntegrationDetailPage({ params }) {
961
961
  ))
962
962
  ] }),
963
963
  showCredentialsTab ? /* @__PURE__ */ jsx(TabsContent, { value: "credentials", className: "mt-0", children: /* @__PURE__ */ jsxs("section", { className: "space-y-4 rounded-lg border bg-card p-6", children: [
964
- detail.bundle ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-blue-200 bg-blue-50 p-3 text-sm text-blue-800", children: t("integrations.detail.credentials.bundleShared", { bundle: detail.bundle.title }) }) : null,
964
+ detail.bundle ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-status-info-border bg-status-info-bg p-3 text-sm text-status-info-text", children: t("integrations.detail.credentials.bundleShared", { bundle: detail.bundle.title }) }) : null,
965
965
  credentialFormFields.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("integrations.detail.credentials.notConfigured") }) : /* @__PURE__ */ jsx(
966
966
  CrudForm,
967
967
  {