@open-mercato/ui 0.5.1-develop.3036.f02c281f23 → 0.5.1-develop.3045.b4b3320cc2

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 (148) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +2 -1
  3. package/__integration__/TC-AI-UI-003-aichat-registry.spec.tsx +204 -0
  4. package/dist/ai/AiAssistantLauncher.js +596 -0
  5. package/dist/ai/AiAssistantLauncher.js.map +7 -0
  6. package/dist/ai/AiChat.js +1092 -0
  7. package/dist/ai/AiChat.js.map +7 -0
  8. package/dist/ai/AiChatSessions.js +297 -0
  9. package/dist/ai/AiChatSessions.js.map +7 -0
  10. package/dist/ai/AiDock.js +347 -0
  11. package/dist/ai/AiDock.js.map +7 -0
  12. package/dist/ai/AiMessageContent.js +369 -0
  13. package/dist/ai/AiMessageContent.js.map +7 -0
  14. package/dist/ai/ChatPaneTabs.js +251 -0
  15. package/dist/ai/ChatPaneTabs.js.map +7 -0
  16. package/dist/ai/index.js +115 -0
  17. package/dist/ai/index.js.map +7 -0
  18. package/dist/ai/parts/ConfirmationCard.js +211 -0
  19. package/dist/ai/parts/ConfirmationCard.js.map +7 -0
  20. package/dist/ai/parts/FieldDiffCard.js +119 -0
  21. package/dist/ai/parts/FieldDiffCard.js.map +7 -0
  22. package/dist/ai/parts/MutationPreviewCard.js +224 -0
  23. package/dist/ai/parts/MutationPreviewCard.js.map +7 -0
  24. package/dist/ai/parts/MutationResultCard.js +240 -0
  25. package/dist/ai/parts/MutationResultCard.js.map +7 -0
  26. package/dist/ai/parts/approval-cards-map.js +15 -0
  27. package/dist/ai/parts/approval-cards-map.js.map +7 -0
  28. package/dist/ai/parts/index.js +24 -0
  29. package/dist/ai/parts/index.js.map +7 -0
  30. package/dist/ai/parts/pending-action-api.js +60 -0
  31. package/dist/ai/parts/pending-action-api.js.map +7 -0
  32. package/dist/ai/parts/types.js +1 -0
  33. package/dist/ai/parts/types.js.map +7 -0
  34. package/dist/ai/parts/useAiPendingActionPolling.js +126 -0
  35. package/dist/ai/parts/useAiPendingActionPolling.js.map +7 -0
  36. package/dist/ai/records/ActivityCard.js +83 -0
  37. package/dist/ai/records/ActivityCard.js.map +7 -0
  38. package/dist/ai/records/CompanyCard.js +81 -0
  39. package/dist/ai/records/CompanyCard.js.map +7 -0
  40. package/dist/ai/records/DealCard.js +76 -0
  41. package/dist/ai/records/DealCard.js.map +7 -0
  42. package/dist/ai/records/PersonCard.js +68 -0
  43. package/dist/ai/records/PersonCard.js.map +7 -0
  44. package/dist/ai/records/ProductCard.js +68 -0
  45. package/dist/ai/records/ProductCard.js.map +7 -0
  46. package/dist/ai/records/RecordCard.js +29 -0
  47. package/dist/ai/records/RecordCard.js.map +7 -0
  48. package/dist/ai/records/RecordCardShell.js +103 -0
  49. package/dist/ai/records/RecordCardShell.js.map +7 -0
  50. package/dist/ai/records/index.js +31 -0
  51. package/dist/ai/records/index.js.map +7 -0
  52. package/dist/ai/records/registry.js +51 -0
  53. package/dist/ai/records/registry.js.map +7 -0
  54. package/dist/ai/records/types.js +1 -0
  55. package/dist/ai/records/types.js.map +7 -0
  56. package/dist/ai/ui-part-registry.js +112 -0
  57. package/dist/ai/ui-part-registry.js.map +7 -0
  58. package/dist/ai/ui-part-slots.js +14 -0
  59. package/dist/ai/ui-part-slots.js.map +7 -0
  60. package/dist/ai/ui-parts/pending-phase3-placeholder.js +35 -0
  61. package/dist/ai/ui-parts/pending-phase3-placeholder.js.map +7 -0
  62. package/dist/ai/upload-adapter.js +256 -0
  63. package/dist/ai/upload-adapter.js.map +7 -0
  64. package/dist/ai/useAiChat.js +549 -0
  65. package/dist/ai/useAiChat.js.map +7 -0
  66. package/dist/ai/useAiChatUpload.js +127 -0
  67. package/dist/ai/useAiChatUpload.js.map +7 -0
  68. package/dist/ai/useAiShortcuts.js +43 -0
  69. package/dist/ai/useAiShortcuts.js.map +7 -0
  70. package/dist/backend/AppShell.js +8 -4
  71. package/dist/backend/AppShell.js.map +2 -2
  72. package/dist/backend/BackendChromeProvider.js +2 -0
  73. package/dist/backend/BackendChromeProvider.js.map +2 -2
  74. package/dist/backend/DataTable.js +19 -2
  75. package/dist/backend/DataTable.js.map +2 -2
  76. package/dist/backend/FilterBar.js +19 -15
  77. package/dist/backend/FilterBar.js.map +2 -2
  78. package/dist/backend/dashboard/DashboardScreen.js +31 -3
  79. package/dist/backend/dashboard/DashboardScreen.js.map +2 -2
  80. package/dist/backend/injection/spotIds.js +6 -0
  81. package/dist/backend/injection/spotIds.js.map +2 -2
  82. package/dist/backend/notifications/useNotificationEffect.js +38 -2
  83. package/dist/backend/notifications/useNotificationEffect.js.map +2 -2
  84. package/dist/index.js +1 -0
  85. package/dist/index.js.map +2 -2
  86. package/jest.config.cjs +7 -1
  87. package/jest.markdown-mock.tsx +7 -0
  88. package/package.json +10 -4
  89. package/src/ai/AiAssistantLauncher.tsx +805 -0
  90. package/src/ai/AiChat.tsx +1483 -0
  91. package/src/ai/AiChatSessions.tsx +429 -0
  92. package/src/ai/AiDock.tsx +505 -0
  93. package/src/ai/AiMessageContent.tsx +515 -0
  94. package/src/ai/ChatPaneTabs.tsx +310 -0
  95. package/src/ai/__tests__/AiChat.conversation.test.tsx +160 -0
  96. package/src/ai/__tests__/AiChat.debug.test.tsx +152 -0
  97. package/src/ai/__tests__/AiChat.registry.test.tsx +213 -0
  98. package/src/ai/__tests__/AiChat.test.tsx +257 -0
  99. package/src/ai/__tests__/AiDock.test.tsx +124 -0
  100. package/src/ai/__tests__/AiMessageContent.test.ts +111 -0
  101. package/src/ai/__tests__/ui-part-registry.test.ts +199 -0
  102. package/src/ai/__tests__/ui-part-slots.test.ts +43 -0
  103. package/src/ai/__tests__/upload-adapter.test.ts +213 -0
  104. package/src/ai/__tests__/useAiChatUpload.test.tsx +163 -0
  105. package/src/ai/__tests__/useAiShortcuts.test.tsx +100 -0
  106. package/src/ai/index.ts +125 -0
  107. package/src/ai/parts/ConfirmationCard.tsx +310 -0
  108. package/src/ai/parts/FieldDiffCard.tsx +173 -0
  109. package/src/ai/parts/MutationPreviewCard.tsx +302 -0
  110. package/src/ai/parts/MutationResultCard.tsx +360 -0
  111. package/src/ai/parts/__tests__/ConfirmationCard.test.tsx +169 -0
  112. package/src/ai/parts/__tests__/FieldDiffCard.test.tsx +74 -0
  113. package/src/ai/parts/__tests__/MutationPreviewCard.test.tsx +177 -0
  114. package/src/ai/parts/__tests__/MutationResultCard.test.tsx +127 -0
  115. package/src/ai/parts/__tests__/useAiPendingActionPolling.test.tsx +151 -0
  116. package/src/ai/parts/approval-cards-map.ts +24 -0
  117. package/src/ai/parts/index.ts +27 -0
  118. package/src/ai/parts/pending-action-api.ts +123 -0
  119. package/src/ai/parts/types.ts +84 -0
  120. package/src/ai/parts/useAiPendingActionPolling.ts +210 -0
  121. package/src/ai/records/ActivityCard.tsx +102 -0
  122. package/src/ai/records/CompanyCard.tsx +89 -0
  123. package/src/ai/records/DealCard.tsx +85 -0
  124. package/src/ai/records/PersonCard.tsx +77 -0
  125. package/src/ai/records/ProductCard.tsx +83 -0
  126. package/src/ai/records/RecordCard.tsx +37 -0
  127. package/src/ai/records/RecordCardShell.tsx +169 -0
  128. package/src/ai/records/index.ts +30 -0
  129. package/src/ai/records/registry.tsx +80 -0
  130. package/src/ai/records/types.ts +90 -0
  131. package/src/ai/ui-part-registry.ts +233 -0
  132. package/src/ai/ui-part-slots.ts +32 -0
  133. package/src/ai/ui-parts/pending-phase3-placeholder.tsx +50 -0
  134. package/src/ai/upload-adapter.ts +421 -0
  135. package/src/ai/useAiChat.ts +865 -0
  136. package/src/ai/useAiChatUpload.ts +180 -0
  137. package/src/ai/useAiShortcuts.ts +79 -0
  138. package/src/backend/AppShell.tsx +12 -5
  139. package/src/backend/BackendChromeProvider.tsx +2 -0
  140. package/src/backend/DataTable.tsx +20 -1
  141. package/src/backend/FilterBar.tsx +26 -13
  142. package/src/backend/__tests__/BackendChromeProvider.test.tsx +45 -0
  143. package/src/backend/dashboard/DashboardScreen.tsx +38 -3
  144. package/src/backend/dashboard/__tests__/DashboardScreen.test.tsx +24 -1
  145. package/src/backend/injection/spotIds.ts +6 -0
  146. package/src/backend/notifications/__tests__/useNotificationEffect.test.tsx +77 -0
  147. package/src/backend/notifications/useNotificationEffect.ts +47 -2
  148. package/src/index.ts +1 -0
@@ -0,0 +1,83 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ Activity as ActivityIcon,
5
+ CalendarClock,
6
+ CheckCircle2,
7
+ ListChecks,
8
+ Mail,
9
+ MessageSquare,
10
+ Phone,
11
+ StickyNote
12
+ } from "lucide-react";
13
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
14
+ import { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from "./RecordCardShell.js";
15
+ function formatDate(value) {
16
+ if (!value) return null;
17
+ const d = new Date(value);
18
+ if (Number.isNaN(d.getTime())) return value;
19
+ return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
20
+ }
21
+ function pickActivityIcon(type) {
22
+ if (!type) return ActivityIcon;
23
+ const t = type.toLowerCase();
24
+ if (t.includes("call")) return Phone;
25
+ if (t.includes("mail") || t.includes("email")) return Mail;
26
+ if (t.includes("meeting") || t.includes("chat")) return MessageSquare;
27
+ if (t.includes("task") || t.includes("todo")) return ListChecks;
28
+ if (t.includes("note")) return StickyNote;
29
+ if (t.includes("done") || t.includes("complete")) return CheckCircle2;
30
+ return ActivityIcon;
31
+ }
32
+ function ActivityCard(props) {
33
+ const t = useT();
34
+ const Icon = pickActivityIcon(props.type);
35
+ const status = props.status ? { label: props.status, variant: statusToTagVariant(props.status) } : null;
36
+ const dueDate = formatDate(props.dueDate);
37
+ const completedAt = formatDate(props.completedAt);
38
+ const items = [
39
+ props.type ? { label: t("ai_assistant.chat.records.fields.type", "Type"), value: props.type } : null,
40
+ dueDate ? {
41
+ label: t("ai_assistant.chat.records.fields.due", "Due"),
42
+ value: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
43
+ /* @__PURE__ */ jsx(CalendarClock, { className: "size-3 text-muted-foreground", "aria-hidden": true }),
44
+ dueDate
45
+ ] })
46
+ } : null,
47
+ completedAt ? {
48
+ label: t("ai_assistant.chat.records.fields.completed", "Completed"),
49
+ value: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
50
+ /* @__PURE__ */ jsx(CheckCircle2, { className: "size-3 text-status-success-icon", "aria-hidden": true }),
51
+ completedAt
52
+ ] })
53
+ } : null,
54
+ props.relatedTo ? { label: t("ai_assistant.chat.records.fields.related", "Related"), value: props.relatedTo } : null,
55
+ props.ownerName ? { label: t("ai_assistant.chat.records.fields.owner", "Owner"), value: props.ownerName } : null
56
+ ].filter(Boolean);
57
+ const subtitle = [props.type, props.relatedTo].filter(Boolean).join(" \u2022 ");
58
+ return /* @__PURE__ */ jsx(
59
+ RecordCardShell,
60
+ {
61
+ kindLabel: t("ai_assistant.chat.records.kinds.activity", "Activity"),
62
+ kindIcon: /* @__PURE__ */ jsx(Icon, { className: "size-4", "aria-hidden": true }),
63
+ title: props.title,
64
+ subtitle: subtitle || void 0,
65
+ status,
66
+ href: props.href,
67
+ id: props.id,
68
+ className: props.className,
69
+ dataKind: "activity",
70
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
71
+ /* @__PURE__ */ jsx(KeyValueList, { items }),
72
+ props.description ? /* @__PURE__ */ jsx("p", { className: "line-clamp-3 text-muted-foreground", children: props.description }) : null,
73
+ props.tags && props.tags.length > 0 ? /* @__PURE__ */ jsx(TagRow, { tags: props.tags }) : null
74
+ ] })
75
+ }
76
+ );
77
+ }
78
+ var ActivityCard_default = ActivityCard;
79
+ export {
80
+ ActivityCard,
81
+ ActivityCard_default as default
82
+ };
83
+ //# sourceMappingURL=ActivityCard.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/ai/records/ActivityCard.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport {\n Activity as ActivityIcon,\n CalendarClock,\n CheckCircle2,\n ListChecks,\n Mail,\n MessageSquare,\n Phone,\n StickyNote,\n} from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from './RecordCardShell'\nimport type { ActivityRecordPayload } from './types'\n\nfunction formatDate(value: string | null | undefined): string | null {\n if (!value) return null\n const d = new Date(value)\n if (Number.isNaN(d.getTime())) return value\n return d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' })\n}\n\nfunction pickActivityIcon(type: string | null | undefined): LucideIcon {\n if (!type) return ActivityIcon\n const t = type.toLowerCase()\n if (t.includes('call')) return Phone\n if (t.includes('mail') || t.includes('email')) return Mail\n if (t.includes('meeting') || t.includes('chat')) return MessageSquare\n if (t.includes('task') || t.includes('todo')) return ListChecks\n if (t.includes('note')) return StickyNote\n if (t.includes('done') || t.includes('complete')) return CheckCircle2\n return ActivityIcon\n}\n\nexport interface ActivityCardProps extends ActivityRecordPayload {}\n\nexport function ActivityCard(props: ActivityCardProps) {\n const t = useT()\n const Icon = pickActivityIcon(props.type)\n const status = props.status\n ? { label: props.status, variant: statusToTagVariant(props.status) }\n : null\n const dueDate = formatDate(props.dueDate)\n const completedAt = formatDate(props.completedAt)\n\n const items = [\n props.type ? { label: t('ai_assistant.chat.records.fields.type', 'Type'), value: props.type } : null,\n dueDate\n ? {\n label: t('ai_assistant.chat.records.fields.due', 'Due'),\n value: (\n <span className=\"inline-flex items-center gap-1\">\n <CalendarClock className=\"size-3 text-muted-foreground\" aria-hidden />\n {dueDate}\n </span>\n ),\n }\n : null,\n completedAt\n ? {\n label: t('ai_assistant.chat.records.fields.completed', 'Completed'),\n value: (\n <span className=\"inline-flex items-center gap-1\">\n <CheckCircle2 className=\"size-3 text-status-success-icon\" aria-hidden />\n {completedAt}\n </span>\n ),\n }\n : null,\n props.relatedTo ? { label: t('ai_assistant.chat.records.fields.related', 'Related'), value: props.relatedTo } : null,\n props.ownerName ? { label: t('ai_assistant.chat.records.fields.owner', 'Owner'), value: props.ownerName } : null,\n ].filter(Boolean) as { label: string; value: React.ReactNode }[]\n\n const subtitle = [props.type, props.relatedTo].filter(Boolean).join(' \u2022 ')\n\n return (\n <RecordCardShell\n kindLabel={t('ai_assistant.chat.records.kinds.activity', 'Activity')}\n kindIcon={<Icon className=\"size-4\" aria-hidden />}\n title={props.title}\n subtitle={subtitle || undefined}\n status={status}\n href={props.href}\n id={props.id}\n className={props.className}\n dataKind=\"activity\"\n >\n <div className=\"space-y-2\">\n <KeyValueList items={items} />\n {props.description ? (\n <p className=\"line-clamp-3 text-muted-foreground\">{props.description}</p>\n ) : null}\n {props.tags && props.tags.length > 0 ? <TagRow tags={props.tags} /> : null}\n </div>\n </RecordCardShell>\n )\n}\n\nexport default ActivityCard\n"],
5
+ "mappings": ";AAsDY,SACE,KADF;AAnDZ;AAAA,EACE,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,YAAY;AACrB,SAAS,cAAc,iBAAiB,QAAQ,0BAA0B;AAG1E,SAAS,WAAW,OAAiD;AACnE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,IAAI,IAAI,KAAK,KAAK;AACxB,MAAI,OAAO,MAAM,EAAE,QAAQ,CAAC,EAAG,QAAO;AACtC,SAAO,EAAE,mBAAmB,QAAW,EAAE,MAAM,WAAW,OAAO,SAAS,KAAK,UAAU,CAAC;AAC5F;AAEA,SAAS,iBAAiB,MAA6C;AACrE,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,IAAI,KAAK,YAAY;AAC3B,MAAI,EAAE,SAAS,MAAM,EAAG,QAAO;AAC/B,MAAI,EAAE,SAAS,MAAM,KAAK,EAAE,SAAS,OAAO,EAAG,QAAO;AACtD,MAAI,EAAE,SAAS,SAAS,KAAK,EAAE,SAAS,MAAM,EAAG,QAAO;AACxD,MAAI,EAAE,SAAS,MAAM,KAAK,EAAE,SAAS,MAAM,EAAG,QAAO;AACrD,MAAI,EAAE,SAAS,MAAM,EAAG,QAAO;AAC/B,MAAI,EAAE,SAAS,MAAM,KAAK,EAAE,SAAS,UAAU,EAAG,QAAO;AACzD,SAAO;AACT;AAIO,SAAS,aAAa,OAA0B;AACrD,QAAM,IAAI,KAAK;AACf,QAAM,OAAO,iBAAiB,MAAM,IAAI;AACxC,QAAM,SAAS,MAAM,SACjB,EAAE,OAAO,MAAM,QAAQ,SAAS,mBAAmB,MAAM,MAAM,EAAE,IACjE;AACJ,QAAM,UAAU,WAAW,MAAM,OAAO;AACxC,QAAM,cAAc,WAAW,MAAM,WAAW;AAEhD,QAAM,QAAQ;AAAA,IACZ,MAAM,OAAO,EAAE,OAAO,EAAE,yCAAyC,MAAM,GAAG,OAAO,MAAM,KAAK,IAAI;AAAA,IAChG,UACI;AAAA,MACE,OAAO,EAAE,wCAAwC,KAAK;AAAA,MACtD,OACE,qBAAC,UAAK,WAAU,kCACd;AAAA,4BAAC,iBAAc,WAAU,gCAA+B,eAAW,MAAC;AAAA,QACnE;AAAA,SACH;AAAA,IAEJ,IACA;AAAA,IACJ,cACI;AAAA,MACE,OAAO,EAAE,8CAA8C,WAAW;AAAA,MAClE,OACE,qBAAC,UAAK,WAAU,kCACd;AAAA,4BAAC,gBAAa,WAAU,mCAAkC,eAAW,MAAC;AAAA,QACrE;AAAA,SACH;AAAA,IAEJ,IACA;AAAA,IACJ,MAAM,YAAY,EAAE,OAAO,EAAE,4CAA4C,SAAS,GAAG,OAAO,MAAM,UAAU,IAAI;AAAA,IAChH,MAAM,YAAY,EAAE,OAAO,EAAE,0CAA0C,OAAO,GAAG,OAAO,MAAM,UAAU,IAAI;AAAA,EAC9G,EAAE,OAAO,OAAO;AAEhB,QAAM,WAAW,CAAC,MAAM,MAAM,MAAM,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,UAAK;AAEzE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,EAAE,4CAA4C,UAAU;AAAA,MACnE,UAAU,oBAAC,QAAK,WAAU,UAAS,eAAW,MAAC;AAAA,MAC/C,OAAO,MAAM;AAAA,MACb,UAAU,YAAY;AAAA,MACtB;AAAA,MACA,MAAM,MAAM;AAAA,MACZ,IAAI,MAAM;AAAA,MACV,WAAW,MAAM;AAAA,MACjB,UAAS;AAAA,MAET,+BAAC,SAAI,WAAU,aACb;AAAA,4BAAC,gBAAa,OAAc;AAAA,QAC3B,MAAM,cACL,oBAAC,OAAE,WAAU,sCAAsC,gBAAM,aAAY,IACnE;AAAA,QACH,MAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,oBAAC,UAAO,MAAM,MAAM,MAAM,IAAK;AAAA,SACxE;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,uBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,81 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Building2, Globe, Mail, MapPin, Phone } from "lucide-react";
4
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
5
+ import { Avatar } from "../../primitives/avatar.js";
6
+ import { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from "./RecordCardShell.js";
7
+ function normalizeWebsite(value) {
8
+ const trimmed = value.trim();
9
+ if (/^https?:\/\//i.test(trimmed)) {
10
+ return { href: trimmed, label: trimmed.replace(/^https?:\/\//i, "") };
11
+ }
12
+ return { href: `https://${trimmed}`, label: trimmed };
13
+ }
14
+ function CompanyCard(props) {
15
+ const t = useT();
16
+ const status = props.status ? { label: props.status, variant: statusToTagVariant(props.status) } : null;
17
+ const websiteEntry = props.website ? normalizeWebsite(props.website) : null;
18
+ const location = [props.city, props.country].filter(Boolean).join(", ");
19
+ const items = [
20
+ props.industry ? { label: t("ai_assistant.chat.records.fields.industry", "Industry"), value: props.industry } : null,
21
+ websiteEntry ? {
22
+ label: t("ai_assistant.chat.records.fields.website", "Website"),
23
+ value: /* @__PURE__ */ jsx(
24
+ "a",
25
+ {
26
+ href: websiteEntry.href,
27
+ target: "_blank",
28
+ rel: "noopener noreferrer",
29
+ className: "text-primary hover:underline",
30
+ onClick: (event) => event.stopPropagation(),
31
+ children: websiteEntry.label
32
+ }
33
+ )
34
+ } : null,
35
+ props.email ? {
36
+ label: t("ai_assistant.chat.records.fields.email", "Email"),
37
+ value: /* @__PURE__ */ jsx(
38
+ "a",
39
+ {
40
+ href: `mailto:${props.email}`,
41
+ className: "text-primary hover:underline",
42
+ onClick: (event) => event.stopPropagation(),
43
+ children: props.email
44
+ }
45
+ )
46
+ } : null,
47
+ props.phone ? { label: t("ai_assistant.chat.records.fields.phone", "Phone"), value: props.phone } : null,
48
+ location ? { label: t("ai_assistant.chat.records.fields.location", "Location"), value: location } : null,
49
+ props.ownerName ? { label: t("ai_assistant.chat.records.fields.owner", "Owner"), value: props.ownerName } : null
50
+ ].filter(Boolean);
51
+ return /* @__PURE__ */ jsx(
52
+ RecordCardShell,
53
+ {
54
+ kindLabel: t("ai_assistant.chat.records.kinds.company", "Company"),
55
+ kindIcon: /* @__PURE__ */ jsx(Building2, { className: "size-4", "aria-hidden": true }),
56
+ leading: /* @__PURE__ */ jsx(Avatar, { label: props.name, src: props.logoUrl ?? void 0, size: "md", variant: "monochrome" }),
57
+ title: props.name,
58
+ subtitle: [props.industry, location].filter(Boolean).join(" \u2022 ") || void 0,
59
+ status,
60
+ href: props.href,
61
+ id: props.id,
62
+ className: props.className,
63
+ dataKind: "company",
64
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
65
+ /* @__PURE__ */ jsx(KeyValueList, { items }),
66
+ props.tags && props.tags.length > 0 ? /* @__PURE__ */ jsx(TagRow, { tags: props.tags }) : null
67
+ ] })
68
+ }
69
+ );
70
+ }
71
+ var CompanyCard_default = CompanyCard;
72
+ export {
73
+ Building2,
74
+ CompanyCard,
75
+ Globe,
76
+ Mail,
77
+ MapPin,
78
+ Phone,
79
+ CompanyCard_default as default
80
+ };
81
+ //# sourceMappingURL=CompanyCard.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/ai/records/CompanyCard.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Building2, Globe, Mail, MapPin, Phone } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Avatar } from '../../primitives/avatar'\nimport { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from './RecordCardShell'\nimport type { CompanyRecordPayload } from './types'\n\nexport interface CompanyCardProps extends CompanyRecordPayload {}\n\nfunction normalizeWebsite(value: string): { href: string; label: string } {\n const trimmed = value.trim()\n if (/^https?:\\/\\//i.test(trimmed)) {\n return { href: trimmed, label: trimmed.replace(/^https?:\\/\\//i, '') }\n }\n return { href: `https://${trimmed}`, label: trimmed }\n}\n\nexport function CompanyCard(props: CompanyCardProps) {\n const t = useT()\n const status = props.status\n ? { label: props.status, variant: statusToTagVariant(props.status) }\n : null\n\n const websiteEntry = props.website ? normalizeWebsite(props.website) : null\n const location = [props.city, props.country].filter(Boolean).join(', ')\n\n const items = [\n props.industry ? { label: t('ai_assistant.chat.records.fields.industry', 'Industry'), value: props.industry } : null,\n websiteEntry\n ? {\n label: t('ai_assistant.chat.records.fields.website', 'Website'),\n value: (\n <a\n href={websiteEntry.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-primary hover:underline\"\n onClick={(event) => event.stopPropagation()}\n >\n {websiteEntry.label}\n </a>\n ),\n }\n : null,\n props.email\n ? {\n label: t('ai_assistant.chat.records.fields.email', 'Email'),\n value: (\n <a\n href={`mailto:${props.email}`}\n className=\"text-primary hover:underline\"\n onClick={(event) => event.stopPropagation()}\n >\n {props.email}\n </a>\n ),\n }\n : null,\n props.phone ? { label: t('ai_assistant.chat.records.fields.phone', 'Phone'), value: props.phone } : null,\n location ? { label: t('ai_assistant.chat.records.fields.location', 'Location'), value: location } : null,\n props.ownerName ? { label: t('ai_assistant.chat.records.fields.owner', 'Owner'), value: props.ownerName } : null,\n ].filter(Boolean) as { label: string; value: React.ReactNode }[]\n\n return (\n <RecordCardShell\n kindLabel={t('ai_assistant.chat.records.kinds.company', 'Company')}\n kindIcon={<Building2 className=\"size-4\" aria-hidden />}\n leading={<Avatar label={props.name} src={props.logoUrl ?? undefined} size=\"md\" variant=\"monochrome\" />}\n title={props.name}\n subtitle={[props.industry, location].filter(Boolean).join(' \u2022 ') || undefined}\n status={status}\n href={props.href}\n id={props.id}\n className={props.className}\n dataKind=\"company\"\n >\n <div className=\"space-y-2\">\n <KeyValueList items={items} />\n {props.tags && props.tags.length > 0 ? <TagRow tags={props.tags} /> : null}\n </div>\n </RecordCardShell>\n )\n}\n\nexport default CompanyCard\n\nexport { Building2, Globe, Mail, MapPin, Phone }\n"],
5
+ "mappings": ";AAkCY,cA4CN,YA5CM;AA/BZ,SAAS,WAAW,OAAO,MAAM,QAAQ,aAAa;AACtD,SAAS,YAAY;AACrB,SAAS,cAAc;AACvB,SAAS,cAAc,iBAAiB,QAAQ,0BAA0B;AAK1E,SAAS,iBAAiB,OAAgD;AACxE,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,gBAAgB,KAAK,OAAO,GAAG;AACjC,WAAO,EAAE,MAAM,SAAS,OAAO,QAAQ,QAAQ,iBAAiB,EAAE,EAAE;AAAA,EACtE;AACA,SAAO,EAAE,MAAM,WAAW,OAAO,IAAI,OAAO,QAAQ;AACtD;AAEO,SAAS,YAAY,OAAyB;AACnD,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,MAAM,SACjB,EAAE,OAAO,MAAM,QAAQ,SAAS,mBAAmB,MAAM,MAAM,EAAE,IACjE;AAEJ,QAAM,eAAe,MAAM,UAAU,iBAAiB,MAAM,OAAO,IAAI;AACvE,QAAM,WAAW,CAAC,MAAM,MAAM,MAAM,OAAO,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAEtE,QAAM,QAAQ;AAAA,IACZ,MAAM,WAAW,EAAE,OAAO,EAAE,6CAA6C,UAAU,GAAG,OAAO,MAAM,SAAS,IAAI;AAAA,IAChH,eACI;AAAA,MACE,OAAO,EAAE,4CAA4C,SAAS;AAAA,MAC9D,OACE;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,aAAa;AAAA,UACnB,QAAO;AAAA,UACP,KAAI;AAAA,UACJ,WAAU;AAAA,UACV,SAAS,CAAC,UAAU,MAAM,gBAAgB;AAAA,UAEzC,uBAAa;AAAA;AAAA,MAChB;AAAA,IAEJ,IACA;AAAA,IACJ,MAAM,QACF;AAAA,MACE,OAAO,EAAE,0CAA0C,OAAO;AAAA,MAC1D,OACE;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,UAAU,MAAM,KAAK;AAAA,UAC3B,WAAU;AAAA,UACV,SAAS,CAAC,UAAU,MAAM,gBAAgB;AAAA,UAEzC,gBAAM;AAAA;AAAA,MACT;AAAA,IAEJ,IACA;AAAA,IACJ,MAAM,QAAQ,EAAE,OAAO,EAAE,0CAA0C,OAAO,GAAG,OAAO,MAAM,MAAM,IAAI;AAAA,IACpG,WAAW,EAAE,OAAO,EAAE,6CAA6C,UAAU,GAAG,OAAO,SAAS,IAAI;AAAA,IACpG,MAAM,YAAY,EAAE,OAAO,EAAE,0CAA0C,OAAO,GAAG,OAAO,MAAM,UAAU,IAAI;AAAA,EAC9G,EAAE,OAAO,OAAO;AAEhB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,EAAE,2CAA2C,SAAS;AAAA,MACjE,UAAU,oBAAC,aAAU,WAAU,UAAS,eAAW,MAAC;AAAA,MACpD,SAAS,oBAAC,UAAO,OAAO,MAAM,MAAM,KAAK,MAAM,WAAW,QAAW,MAAK,MAAK,SAAQ,cAAa;AAAA,MACpG,OAAO,MAAM;AAAA,MACb,UAAU,CAAC,MAAM,UAAU,QAAQ,EAAE,OAAO,OAAO,EAAE,KAAK,UAAK,KAAK;AAAA,MACpE;AAAA,MACA,MAAM,MAAM;AAAA,MACZ,IAAI,MAAM;AAAA,MACV,WAAW,MAAM;AAAA,MACjB,UAAS;AAAA,MAET,+BAAC,SAAI,WAAU,aACb;AAAA,4BAAC,gBAAa,OAAc;AAAA,QAC3B,MAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,oBAAC,UAAO,MAAM,MAAM,MAAM,IAAK;AAAA,SACxE;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,sBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,76 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Briefcase, Building2, CalendarDays, CircleDollarSign, User } from "lucide-react";
4
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
5
+ import { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from "./RecordCardShell.js";
6
+ function formatAmount(amount, currency) {
7
+ if (amount === null || amount === void 0 || amount === "") return null;
8
+ const value = typeof amount === "number" ? amount : Number(amount);
9
+ if (!Number.isFinite(value)) {
10
+ return typeof amount === "string" ? amount : null;
11
+ }
12
+ const code = currency && currency.length === 3 ? currency.toUpperCase() : void 0;
13
+ try {
14
+ if (code) {
15
+ return new Intl.NumberFormat(void 0, { style: "currency", currency: code }).format(value);
16
+ }
17
+ } catch {
18
+ }
19
+ const formatted = new Intl.NumberFormat().format(value);
20
+ return code ? `${formatted} ${code}` : formatted;
21
+ }
22
+ function formatDate(value) {
23
+ if (!value) return null;
24
+ const d = new Date(value);
25
+ if (Number.isNaN(d.getTime())) return value;
26
+ return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
27
+ }
28
+ function DealCard(props) {
29
+ const t = useT();
30
+ const status = props.status ? { label: props.status, variant: statusToTagVariant(props.status) } : null;
31
+ const stage = props.stage && props.stage !== props.status ? props.stage : null;
32
+ const amount = formatAmount(props.amount, props.currency);
33
+ const closeDate = formatDate(props.closeDate);
34
+ const items = [
35
+ stage ? { label: t("ai_assistant.chat.records.fields.stage", "Stage"), value: stage } : null,
36
+ amount ? { label: t("ai_assistant.chat.records.fields.amount", "Amount"), value: /* @__PURE__ */ jsx("span", { className: "font-medium", children: amount }) } : null,
37
+ closeDate ? { label: t("ai_assistant.chat.records.fields.close", "Close"), value: closeDate } : null,
38
+ props.companyName ? { label: t("ai_assistant.chat.records.fields.company", "Company"), value: props.companyName } : null,
39
+ props.personName ? { label: t("ai_assistant.chat.records.fields.contact", "Contact"), value: props.personName } : null,
40
+ props.ownerName ? { label: t("ai_assistant.chat.records.fields.owner", "Owner"), value: props.ownerName } : null
41
+ ].filter(Boolean);
42
+ const subtitleParts = [];
43
+ if (props.companyName) subtitleParts.push(props.companyName);
44
+ if (props.personName && !props.companyName) subtitleParts.push(props.personName);
45
+ if (amount) subtitleParts.push(amount);
46
+ return /* @__PURE__ */ jsx(
47
+ RecordCardShell,
48
+ {
49
+ kindLabel: t("ai_assistant.chat.records.kinds.deal", "Deal"),
50
+ kindIcon: /* @__PURE__ */ jsx(Briefcase, { className: "size-4", "aria-hidden": true }),
51
+ title: props.title,
52
+ subtitle: subtitleParts.join(" \u2022 "),
53
+ status,
54
+ href: props.href,
55
+ id: props.id,
56
+ className: props.className,
57
+ dataKind: "deal",
58
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
59
+ /* @__PURE__ */ jsx(KeyValueList, { items }),
60
+ props.description ? /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-muted-foreground", children: props.description }) : null,
61
+ props.tags && props.tags.length > 0 ? /* @__PURE__ */ jsx(TagRow, { tags: props.tags }) : null
62
+ ] })
63
+ }
64
+ );
65
+ }
66
+ var DealCard_default = DealCard;
67
+ export {
68
+ Briefcase,
69
+ Building2,
70
+ CalendarDays,
71
+ CircleDollarSign,
72
+ DealCard,
73
+ User,
74
+ DealCard_default as default
75
+ };
76
+ //# sourceMappingURL=DealCard.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/ai/records/DealCard.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Briefcase, Building2, CalendarDays, CircleDollarSign, User } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from './RecordCardShell'\nimport type { DealRecordPayload } from './types'\n\nfunction formatAmount(amount: string | number | null | undefined, currency?: string | null): string | null {\n if (amount === null || amount === undefined || amount === '') return null\n const value = typeof amount === 'number' ? amount : Number(amount)\n if (!Number.isFinite(value)) {\n return typeof amount === 'string' ? amount : null\n }\n const code = currency && currency.length === 3 ? currency.toUpperCase() : undefined\n try {\n if (code) {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency: code }).format(value)\n }\n } catch {\n // fall through to fallback\n }\n const formatted = new Intl.NumberFormat().format(value)\n return code ? `${formatted} ${code}` : formatted\n}\n\nfunction formatDate(value: string | null | undefined): string | null {\n if (!value) return null\n const d = new Date(value)\n if (Number.isNaN(d.getTime())) return value\n return d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' })\n}\n\nexport interface DealCardProps extends DealRecordPayload {}\n\nexport function DealCard(props: DealCardProps) {\n const t = useT()\n const status = props.status\n ? { label: props.status, variant: statusToTagVariant(props.status) }\n : null\n const stage = props.stage && props.stage !== props.status ? props.stage : null\n const amount = formatAmount(props.amount, props.currency)\n const closeDate = formatDate(props.closeDate)\n\n const items = [\n stage ? { label: t('ai_assistant.chat.records.fields.stage', 'Stage'), value: stage } : null,\n amount ? { label: t('ai_assistant.chat.records.fields.amount', 'Amount'), value: <span className=\"font-medium\">{amount}</span> } : null,\n closeDate ? { label: t('ai_assistant.chat.records.fields.close', 'Close'), value: closeDate } : null,\n props.companyName ? { label: t('ai_assistant.chat.records.fields.company', 'Company'), value: props.companyName } : null,\n props.personName ? { label: t('ai_assistant.chat.records.fields.contact', 'Contact'), value: props.personName } : null,\n props.ownerName ? { label: t('ai_assistant.chat.records.fields.owner', 'Owner'), value: props.ownerName } : null,\n ].filter(Boolean) as { label: string; value: React.ReactNode }[]\n\n const subtitleParts: string[] = []\n if (props.companyName) subtitleParts.push(props.companyName)\n if (props.personName && !props.companyName) subtitleParts.push(props.personName)\n if (amount) subtitleParts.push(amount)\n\n return (\n <RecordCardShell\n kindLabel={t('ai_assistant.chat.records.kinds.deal', 'Deal')}\n kindIcon={<Briefcase className=\"size-4\" aria-hidden />}\n title={props.title}\n subtitle={subtitleParts.join(' \u2022 ')}\n status={status}\n href={props.href}\n id={props.id}\n className={props.className}\n dataKind=\"deal\"\n >\n <div className=\"space-y-2\">\n <KeyValueList items={items} />\n {props.description ? (\n <p className=\"line-clamp-2 text-muted-foreground\">{props.description}</p>\n ) : null}\n {props.tags && props.tags.length > 0 ? <TagRow tags={props.tags} /> : null}\n </div>\n </RecordCardShell>\n )\n}\n\nexport default DealCard\n\n// Re-export icons consumers may want when extending the layout\nexport { Briefcase, Building2, CalendarDays, CircleDollarSign, User }\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,68 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Mail, Phone, User as UserIcon } from "lucide-react";
4
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
5
+ import { Avatar } from "../../primitives/avatar.js";
6
+ import { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from "./RecordCardShell.js";
7
+ function PersonCard(props) {
8
+ const t = useT();
9
+ const status = props.status ? { label: props.status, variant: statusToTagVariant(props.status) } : null;
10
+ const items = [
11
+ props.title ? { label: t("ai_assistant.chat.records.fields.title", "Title"), value: props.title } : null,
12
+ props.companyName ? { label: t("ai_assistant.chat.records.fields.company", "Company"), value: props.companyName } : null,
13
+ props.email ? {
14
+ label: t("ai_assistant.chat.records.fields.email", "Email"),
15
+ value: /* @__PURE__ */ jsx(
16
+ "a",
17
+ {
18
+ href: `mailto:${props.email}`,
19
+ className: "text-primary hover:underline",
20
+ onClick: (event) => event.stopPropagation(),
21
+ children: props.email
22
+ }
23
+ )
24
+ } : null,
25
+ props.phone ? {
26
+ label: t("ai_assistant.chat.records.fields.phone", "Phone"),
27
+ value: /* @__PURE__ */ jsx(
28
+ "a",
29
+ {
30
+ href: `tel:${props.phone.replace(/\s+/g, "")}`,
31
+ className: "text-primary hover:underline",
32
+ onClick: (event) => event.stopPropagation(),
33
+ children: props.phone
34
+ }
35
+ )
36
+ } : null,
37
+ props.ownerName ? { label: t("ai_assistant.chat.records.fields.owner", "Owner"), value: props.ownerName } : null
38
+ ].filter(Boolean);
39
+ const subtitle = [props.title, props.companyName].filter(Boolean).join(" \u2022 ");
40
+ return /* @__PURE__ */ jsx(
41
+ RecordCardShell,
42
+ {
43
+ kindLabel: t("ai_assistant.chat.records.kinds.person", "Person"),
44
+ kindIcon: /* @__PURE__ */ jsx(UserIcon, { className: "size-4", "aria-hidden": true }),
45
+ leading: /* @__PURE__ */ jsx(Avatar, { label: props.name, src: props.avatarUrl ?? void 0, size: "md" }),
46
+ title: props.name,
47
+ subtitle: subtitle || void 0,
48
+ status,
49
+ href: props.href,
50
+ id: props.id,
51
+ className: props.className,
52
+ dataKind: "person",
53
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
54
+ /* @__PURE__ */ jsx(KeyValueList, { items }),
55
+ props.tags && props.tags.length > 0 ? /* @__PURE__ */ jsx(TagRow, { tags: props.tags }) : null
56
+ ] })
57
+ }
58
+ );
59
+ }
60
+ var PersonCard_default = PersonCard;
61
+ export {
62
+ Mail,
63
+ PersonCard,
64
+ Phone,
65
+ UserIcon,
66
+ PersonCard_default as default
67
+ };
68
+ //# sourceMappingURL=PersonCard.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/ai/records/PersonCard.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Mail, Phone, User as UserIcon } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Avatar } from '../../primitives/avatar'\nimport { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from './RecordCardShell'\nimport type { PersonRecordPayload } from './types'\n\nexport interface PersonCardProps extends PersonRecordPayload {}\n\nexport function PersonCard(props: PersonCardProps) {\n const t = useT()\n const status = props.status\n ? { label: props.status, variant: statusToTagVariant(props.status) }\n : null\n\n const items = [\n props.title ? { label: t('ai_assistant.chat.records.fields.title', 'Title'), value: props.title } : null,\n props.companyName ? { label: t('ai_assistant.chat.records.fields.company', 'Company'), value: props.companyName } : null,\n props.email\n ? {\n label: t('ai_assistant.chat.records.fields.email', 'Email'),\n value: (\n <a\n href={`mailto:${props.email}`}\n className=\"text-primary hover:underline\"\n onClick={(event) => event.stopPropagation()}\n >\n {props.email}\n </a>\n ),\n }\n : null,\n props.phone\n ? {\n label: t('ai_assistant.chat.records.fields.phone', 'Phone'),\n value: (\n <a\n href={`tel:${props.phone.replace(/\\s+/g, '')}`}\n className=\"text-primary hover:underline\"\n onClick={(event) => event.stopPropagation()}\n >\n {props.phone}\n </a>\n ),\n }\n : null,\n props.ownerName ? { label: t('ai_assistant.chat.records.fields.owner', 'Owner'), value: props.ownerName } : null,\n ].filter(Boolean) as { label: string; value: React.ReactNode }[]\n\n const subtitle = [props.title, props.companyName].filter(Boolean).join(' \u2022 ')\n\n return (\n <RecordCardShell\n kindLabel={t('ai_assistant.chat.records.kinds.person', 'Person')}\n kindIcon={<UserIcon className=\"size-4\" aria-hidden />}\n leading={<Avatar label={props.name} src={props.avatarUrl ?? undefined} size=\"md\" />}\n title={props.name}\n subtitle={subtitle || undefined}\n status={status}\n href={props.href}\n id={props.id}\n className={props.className}\n dataKind=\"person\"\n >\n <div className=\"space-y-2\">\n <KeyValueList items={items} />\n {props.tags && props.tags.length > 0 ? <TagRow tags={props.tags} /> : null}\n </div>\n </RecordCardShell>\n )\n}\n\nexport default PersonCard\n\nexport { Mail, Phone, UserIcon }\n"],
5
+ "mappings": ";AAwBY,cA0CN,YA1CM;AArBZ,SAAS,MAAM,OAAO,QAAQ,gBAAgB;AAC9C,SAAS,YAAY;AACrB,SAAS,cAAc;AACvB,SAAS,cAAc,iBAAiB,QAAQ,0BAA0B;AAKnE,SAAS,WAAW,OAAwB;AACjD,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,MAAM,SACjB,EAAE,OAAO,MAAM,QAAQ,SAAS,mBAAmB,MAAM,MAAM,EAAE,IACjE;AAEJ,QAAM,QAAQ;AAAA,IACZ,MAAM,QAAQ,EAAE,OAAO,EAAE,0CAA0C,OAAO,GAAG,OAAO,MAAM,MAAM,IAAI;AAAA,IACpG,MAAM,cAAc,EAAE,OAAO,EAAE,4CAA4C,SAAS,GAAG,OAAO,MAAM,YAAY,IAAI;AAAA,IACpH,MAAM,QACF;AAAA,MACE,OAAO,EAAE,0CAA0C,OAAO;AAAA,MAC1D,OACE;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,UAAU,MAAM,KAAK;AAAA,UAC3B,WAAU;AAAA,UACV,SAAS,CAAC,UAAU,MAAM,gBAAgB;AAAA,UAEzC,gBAAM;AAAA;AAAA,MACT;AAAA,IAEJ,IACA;AAAA,IACJ,MAAM,QACF;AAAA,MACE,OAAO,EAAE,0CAA0C,OAAO;AAAA,MAC1D,OACE;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,OAAO,MAAM,MAAM,QAAQ,QAAQ,EAAE,CAAC;AAAA,UAC5C,WAAU;AAAA,UACV,SAAS,CAAC,UAAU,MAAM,gBAAgB;AAAA,UAEzC,gBAAM;AAAA;AAAA,MACT;AAAA,IAEJ,IACA;AAAA,IACJ,MAAM,YAAY,EAAE,OAAO,EAAE,0CAA0C,OAAO,GAAG,OAAO,MAAM,UAAU,IAAI;AAAA,EAC9G,EAAE,OAAO,OAAO;AAEhB,QAAM,WAAW,CAAC,MAAM,OAAO,MAAM,WAAW,EAAE,OAAO,OAAO,EAAE,KAAK,UAAK;AAE5E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,EAAE,0CAA0C,QAAQ;AAAA,MAC/D,UAAU,oBAAC,YAAS,WAAU,UAAS,eAAW,MAAC;AAAA,MACnD,SAAS,oBAAC,UAAO,OAAO,MAAM,MAAM,KAAK,MAAM,aAAa,QAAW,MAAK,MAAK;AAAA,MACjF,OAAO,MAAM;AAAA,MACb,UAAU,YAAY;AAAA,MACtB;AAAA,MACA,MAAM,MAAM;AAAA,MACZ,IAAI,MAAM;AAAA,MACV,WAAW,MAAM;AAAA,MACjB,UAAS;AAAA,MAET,+BAAC,SAAI,WAAU,aACb;AAAA,4BAAC,gBAAa,OAAc;AAAA,QAC3B,MAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,oBAAC,UAAO,MAAM,MAAM,MAAM,IAAK;AAAA,SACxE;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,qBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,68 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Package, Tag as TagIcon } from "lucide-react";
4
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
5
+ import { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from "./RecordCardShell.js";
6
+ function formatPrice(price, currency) {
7
+ if (price === null || price === void 0 || price === "") return null;
8
+ const value = typeof price === "number" ? price : Number(price);
9
+ if (!Number.isFinite(value)) {
10
+ return typeof price === "string" ? price : null;
11
+ }
12
+ const code = currency && currency.length === 3 ? currency.toUpperCase() : void 0;
13
+ try {
14
+ if (code) {
15
+ return new Intl.NumberFormat(void 0, { style: "currency", currency: code }).format(value);
16
+ }
17
+ } catch {
18
+ }
19
+ const formatted = new Intl.NumberFormat().format(value);
20
+ return code ? `${formatted} ${code}` : formatted;
21
+ }
22
+ function ProductCard(props) {
23
+ const t = useT();
24
+ const status = props.status ? { label: props.status, variant: statusToTagVariant(props.status) } : null;
25
+ const price = formatPrice(props.price, props.currency);
26
+ const leading = props.imageUrl ? /* @__PURE__ */ jsx("div", { className: "relative size-12 overflow-hidden rounded-md border border-border bg-muted", children: /* @__PURE__ */ jsx(
27
+ "img",
28
+ {
29
+ src: props.imageUrl,
30
+ alt: props.name,
31
+ className: "size-full object-cover",
32
+ loading: "lazy"
33
+ }
34
+ ) }) : /* @__PURE__ */ jsx("div", { className: "flex size-12 items-center justify-center rounded-md border border-border bg-muted text-muted-foreground", "aria-hidden": true, children: /* @__PURE__ */ jsx(Package, { className: "size-5" }) });
35
+ const items = [
36
+ props.sku ? { label: t("ai_assistant.chat.records.fields.sku", "SKU"), value: /* @__PURE__ */ jsx("span", { className: "font-mono text-[11px]", children: props.sku }) } : null,
37
+ price ? { label: t("ai_assistant.chat.records.fields.price", "Price"), value: /* @__PURE__ */ jsx("span", { className: "font-medium", children: price }) } : null,
38
+ props.category ? { label: t("ai_assistant.chat.records.fields.category", "Category"), value: props.category } : null
39
+ ].filter(Boolean);
40
+ return /* @__PURE__ */ jsx(
41
+ RecordCardShell,
42
+ {
43
+ kindLabel: t("ai_assistant.chat.records.kinds.product", "Product"),
44
+ kindIcon: /* @__PURE__ */ jsx(Package, { className: "size-4", "aria-hidden": true }),
45
+ leading,
46
+ title: props.name,
47
+ subtitle: [props.sku, price].filter(Boolean).join(" \u2022 ") || void 0,
48
+ status,
49
+ href: props.href,
50
+ id: props.id,
51
+ className: props.className,
52
+ dataKind: "product",
53
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
54
+ /* @__PURE__ */ jsx(KeyValueList, { items }),
55
+ props.description ? /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-muted-foreground", children: props.description }) : null,
56
+ props.tags && props.tags.length > 0 ? /* @__PURE__ */ jsx(TagRow, { tags: props.tags }) : null
57
+ ] })
58
+ }
59
+ );
60
+ }
61
+ var ProductCard_default = ProductCard;
62
+ export {
63
+ Package,
64
+ ProductCard,
65
+ TagIcon,
66
+ ProductCard_default as default
67
+ };
68
+ //# sourceMappingURL=ProductCard.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/ai/records/ProductCard.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Package, Tag as TagIcon } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from './RecordCardShell'\nimport type { ProductRecordPayload } from './types'\n\nfunction formatPrice(price: string | number | null | undefined, currency?: string | null): string | null {\n if (price === null || price === undefined || price === '') return null\n const value = typeof price === 'number' ? price : Number(price)\n if (!Number.isFinite(value)) {\n return typeof price === 'string' ? price : null\n }\n const code = currency && currency.length === 3 ? currency.toUpperCase() : undefined\n try {\n if (code) {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency: code }).format(value)\n }\n } catch {\n // fall through\n }\n const formatted = new Intl.NumberFormat().format(value)\n return code ? `${formatted} ${code}` : formatted\n}\n\nexport interface ProductCardProps extends ProductRecordPayload {}\n\nexport function ProductCard(props: ProductCardProps) {\n const t = useT()\n const status = props.status\n ? { label: props.status, variant: statusToTagVariant(props.status) }\n : null\n const price = formatPrice(props.price, props.currency)\n\n const leading = props.imageUrl ? (\n <div className=\"relative size-12 overflow-hidden rounded-md border border-border bg-muted\">\n <img\n src={props.imageUrl}\n alt={props.name}\n className=\"size-full object-cover\"\n loading=\"lazy\"\n />\n </div>\n ) : (\n <div className=\"flex size-12 items-center justify-center rounded-md border border-border bg-muted text-muted-foreground\" aria-hidden>\n <Package className=\"size-5\" />\n </div>\n )\n\n const items = [\n props.sku ? { label: t('ai_assistant.chat.records.fields.sku', 'SKU'), value: <span className=\"font-mono text-[11px]\">{props.sku}</span> } : null,\n price ? { label: t('ai_assistant.chat.records.fields.price', 'Price'), value: <span className=\"font-medium\">{price}</span> } : null,\n props.category ? { label: t('ai_assistant.chat.records.fields.category', 'Category'), value: props.category } : null,\n ].filter(Boolean) as { label: string; value: React.ReactNode }[]\n\n return (\n <RecordCardShell\n kindLabel={t('ai_assistant.chat.records.kinds.product', 'Product')}\n kindIcon={<Package className=\"size-4\" aria-hidden />}\n leading={leading}\n title={props.name}\n subtitle={[props.sku, price].filter(Boolean).join(' \u2022 ') || undefined}\n status={status}\n href={props.href}\n id={props.id}\n className={props.className}\n dataKind=\"product\"\n >\n <div className=\"space-y-2\">\n <KeyValueList items={items} />\n {props.description ? (\n <p className=\"line-clamp-2 text-muted-foreground\">{props.description}</p>\n ) : null}\n {props.tags && props.tags.length > 0 ? <TagRow tags={props.tags} /> : null}\n </div>\n </RecordCardShell>\n )\n}\n\nexport default ProductCard\n\nexport { Package, TagIcon }\n"],
5
+ "mappings": ";AAqCM,cAgCA,YAhCA;AAlCN,SAAS,SAAS,OAAO,eAAe;AACxC,SAAS,YAAY;AACrB,SAAS,cAAc,iBAAiB,QAAQ,0BAA0B;AAG1E,SAAS,YAAY,OAA2C,UAAyC;AACvG,MAAI,UAAU,QAAQ,UAAU,UAAa,UAAU,GAAI,QAAO;AAClE,QAAM,QAAQ,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;AAC9D,MAAI,CAAC,OAAO,SAAS,KAAK,GAAG;AAC3B,WAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,EAC7C;AACA,QAAM,OAAO,YAAY,SAAS,WAAW,IAAI,SAAS,YAAY,IAAI;AAC1E,MAAI;AACF,QAAI,MAAM;AACR,aAAO,IAAI,KAAK,aAAa,QAAW,EAAE,OAAO,YAAY,UAAU,KAAK,CAAC,EAAE,OAAO,KAAK;AAAA,IAC7F;AAAA,EACF,QAAQ;AAAA,EAER;AACA,QAAM,YAAY,IAAI,KAAK,aAAa,EAAE,OAAO,KAAK;AACtD,SAAO,OAAO,GAAG,SAAS,IAAI,IAAI,KAAK;AACzC;AAIO,SAAS,YAAY,OAAyB;AACnD,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,MAAM,SACjB,EAAE,OAAO,MAAM,QAAQ,SAAS,mBAAmB,MAAM,MAAM,EAAE,IACjE;AACJ,QAAM,QAAQ,YAAY,MAAM,OAAO,MAAM,QAAQ;AAErD,QAAM,UAAU,MAAM,WACpB,oBAAC,SAAI,WAAU,6EACb;AAAA,IAAC;AAAA;AAAA,MACC,KAAK,MAAM;AAAA,MACX,KAAK,MAAM;AAAA,MACX,WAAU;AAAA,MACV,SAAQ;AAAA;AAAA,EACV,GACF,IAEA,oBAAC,SAAI,WAAU,2GAA0G,eAAW,MAClI,8BAAC,WAAQ,WAAU,UAAS,GAC9B;AAGF,QAAM,QAAQ;AAAA,IACZ,MAAM,MAAM,EAAE,OAAO,EAAE,wCAAwC,KAAK,GAAG,OAAO,oBAAC,UAAK,WAAU,yBAAyB,gBAAM,KAAI,EAAQ,IAAI;AAAA,IAC7I,QAAQ,EAAE,OAAO,EAAE,0CAA0C,OAAO,GAAG,OAAO,oBAAC,UAAK,WAAU,eAAe,iBAAM,EAAQ,IAAI;AAAA,IAC/H,MAAM,WAAW,EAAE,OAAO,EAAE,6CAA6C,UAAU,GAAG,OAAO,MAAM,SAAS,IAAI;AAAA,EAClH,EAAE,OAAO,OAAO;AAEhB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,EAAE,2CAA2C,SAAS;AAAA,MACjE,UAAU,oBAAC,WAAQ,WAAU,UAAS,eAAW,MAAC;AAAA,MAClD;AAAA,MACA,OAAO,MAAM;AAAA,MACb,UAAU,CAAC,MAAM,KAAK,KAAK,EAAE,OAAO,OAAO,EAAE,KAAK,UAAK,KAAK;AAAA,MAC5D;AAAA,MACA,MAAM,MAAM;AAAA,MACZ,IAAI,MAAM;AAAA,MACV,WAAW,MAAM;AAAA,MACjB,UAAS;AAAA,MAET,+BAAC,SAAI,WAAU,aACb;AAAA,4BAAC,gBAAa,OAAc;AAAA,QAC3B,MAAM,cACL,oBAAC,OAAE,WAAU,sCAAsC,gBAAM,aAAY,IACnE;AAAA,QACH,MAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,oBAAC,UAAO,MAAM,MAAM,MAAM,IAAK;AAAA,SACxE;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,sBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,29 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { ActivityCard } from "./ActivityCard.js";
4
+ import { CompanyCard } from "./CompanyCard.js";
5
+ import { DealCard } from "./DealCard.js";
6
+ import { PersonCard } from "./PersonCard.js";
7
+ import { ProductCard } from "./ProductCard.js";
8
+ function RecordCard({ data }) {
9
+ switch (data.kind) {
10
+ case "deal":
11
+ return /* @__PURE__ */ jsx(DealCard, { ...data });
12
+ case "person":
13
+ return /* @__PURE__ */ jsx(PersonCard, { ...data });
14
+ case "company":
15
+ return /* @__PURE__ */ jsx(CompanyCard, { ...data });
16
+ case "product":
17
+ return /* @__PURE__ */ jsx(ProductCard, { ...data });
18
+ case "activity":
19
+ return /* @__PURE__ */ jsx(ActivityCard, { ...data });
20
+ default:
21
+ return null;
22
+ }
23
+ }
24
+ var RecordCard_default = RecordCard;
25
+ export {
26
+ RecordCard,
27
+ RecordCard_default as default
28
+ };
29
+ //# sourceMappingURL=RecordCard.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/ai/records/RecordCard.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { ActivityCard } from './ActivityCard'\nimport { CompanyCard } from './CompanyCard'\nimport { DealCard } from './DealCard'\nimport { PersonCard } from './PersonCard'\nimport { ProductCard } from './ProductCard'\nimport type { RecordCardPayload } from './types'\n\nexport interface RecordCardProps {\n data: RecordCardPayload\n}\n\n/**\n * Renders an Open Mercato record card based on the `kind` discriminator.\n * Used by the AI chat transcript to upgrade fenced ```open-mercato:<kind>```\n * blocks into rich, interactive widgets.\n */\nexport function RecordCard({ data }: RecordCardProps) {\n switch (data.kind) {\n case 'deal':\n return <DealCard {...data} />\n case 'person':\n return <PersonCard {...data} />\n case 'company':\n return <CompanyCard {...data} />\n case 'product':\n return <ProductCard {...data} />\n case 'activity':\n return <ActivityCard {...data} />\n default:\n return null\n }\n}\n\nexport default RecordCard\n"],
5
+ "mappings": ";AAsBa;AAnBb,SAAS,oBAAoB;AAC7B,SAAS,mBAAmB;AAC5B,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAYrB,SAAS,WAAW,EAAE,KAAK,GAAoB;AACpD,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aAAO,oBAAC,YAAU,GAAG,MAAM;AAAA,IAC7B,KAAK;AACH,aAAO,oBAAC,cAAY,GAAG,MAAM;AAAA,IAC/B,KAAK;AACH,aAAO,oBAAC,eAAa,GAAG,MAAM;AAAA,IAChC,KAAK;AACH,aAAO,oBAAC,eAAa,GAAG,MAAM;AAAA,IAChC,KAAK;AACH,aAAO,oBAAC,gBAAc,GAAG,MAAM;AAAA,IACjC;AACE,aAAO;AAAA,EACX;AACF;AAEA,IAAO,qBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,103 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { ExternalLink } from "lucide-react";
5
+ import { cn } from "@open-mercato/shared/lib/utils";
6
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
7
+ import { Tag } from "../../primitives/tag.js";
8
+ function RecordCardShell({
9
+ kindLabel,
10
+ kindIcon,
11
+ title,
12
+ subtitle,
13
+ status,
14
+ href,
15
+ id,
16
+ leading,
17
+ children,
18
+ className,
19
+ dataKind
20
+ }) {
21
+ const t = useT();
22
+ return /* @__PURE__ */ jsxs(
23
+ "div",
24
+ {
25
+ "data-ai-record-card": dataKind ?? kindLabel.toLowerCase(),
26
+ "data-record-id": id,
27
+ className: cn(
28
+ "group/record relative my-2 flex flex-col gap-3 rounded-lg border border-border bg-card p-3 text-card-foreground shadow-sm transition-colors",
29
+ href ? "hover:border-primary/40 hover:bg-accent/40" : "",
30
+ className
31
+ ),
32
+ children: [
33
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
34
+ leading ? /* @__PURE__ */ jsx("div", { className: "shrink-0", children: leading }) : /* @__PURE__ */ jsx("div", { className: "flex size-9 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary", "aria-hidden": true, children: kindIcon }),
35
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
36
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
37
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: kindLabel }),
38
+ status ? /* @__PURE__ */ jsx(Tag, { variant: status.variant, dot: true, children: status.label }) : null
39
+ ] }),
40
+ /* @__PURE__ */ jsxs("div", { className: "mt-0.5 flex items-start gap-2", children: [
41
+ /* @__PURE__ */ jsx("h4", { className: "min-w-0 flex-1 truncate text-sm font-semibold leading-snug text-foreground", children: href ? /* @__PURE__ */ jsx(
42
+ "a",
43
+ {
44
+ href,
45
+ target: "_blank",
46
+ rel: "noopener noreferrer",
47
+ className: "outline-none hover:underline focus-visible:underline",
48
+ children: title
49
+ }
50
+ ) : title }),
51
+ href ? /* @__PURE__ */ jsx(
52
+ "a",
53
+ {
54
+ href,
55
+ target: "_blank",
56
+ rel: "noopener noreferrer",
57
+ className: "shrink-0 rounded-md p-1 text-muted-foreground hover:text-foreground",
58
+ "aria-label": t("ai_assistant.chat.records.openRecord", "Open record"),
59
+ children: /* @__PURE__ */ jsx(ExternalLink, { className: "size-3.5", "aria-hidden": true })
60
+ }
61
+ ) : null
62
+ ] }),
63
+ subtitle ? /* @__PURE__ */ jsx("div", { className: "mt-0.5 truncate text-xs text-muted-foreground", children: subtitle }) : null
64
+ ] })
65
+ ] }),
66
+ children ? /* @__PURE__ */ jsx("div", { className: "text-xs text-foreground", children }) : null
67
+ ]
68
+ }
69
+ );
70
+ }
71
+ function KeyValueList({ items }) {
72
+ if (items.length === 0) return null;
73
+ return /* @__PURE__ */ jsx("dl", { className: "grid grid-cols-[minmax(80px,auto)_1fr] gap-x-3 gap-y-1", children: items.map((item, idx) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
74
+ /* @__PURE__ */ jsx("dt", { className: "truncate text-[11px] uppercase tracking-wide text-muted-foreground", children: item.label }),
75
+ /* @__PURE__ */ jsx("dd", { className: "min-w-0 truncate text-foreground", children: item.value })
76
+ ] }, `${item.label}-${idx}`)) });
77
+ }
78
+ function TagRow({ tags }) {
79
+ if (tags.length === 0) return null;
80
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: tags.map((tag, idx) => /* @__PURE__ */ jsx(Tag, { variant: "brand", children: tag }, `${tag}-${idx}`)) });
81
+ }
82
+ function statusToTagVariant(status) {
83
+ if (!status) return "neutral";
84
+ const s = String(status).toLowerCase().trim();
85
+ if (s === "won" || s === "win" || s === "active" || s === "completed" || s === "done" || s === "paid" || s === "success" || s === "closed_won" || s === "closed-won")
86
+ return "success";
87
+ if (s === "lost" || s === "failed" || s === "cancelled" || s === "canceled" || s === "overdue" || s === "closed_lost" || s === "closed-lost")
88
+ return "error";
89
+ if (s === "pending" || s === "in_progress" || s === "in progress" || s === "open" || s === "qualified")
90
+ return "info";
91
+ if (s === "at_risk" || s === "at risk" || s === "review" || s === "follow_up")
92
+ return "warning";
93
+ if (s === "draft" || s === "archived" || s === "inactive")
94
+ return "neutral";
95
+ return "info";
96
+ }
97
+ export {
98
+ KeyValueList,
99
+ RecordCardShell,
100
+ TagRow,
101
+ statusToTagVariant
102
+ };
103
+ //# sourceMappingURL=RecordCardShell.js.map