@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.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +2 -1
- package/__integration__/TC-AI-UI-003-aichat-registry.spec.tsx +204 -0
- package/dist/ai/AiAssistantLauncher.js +596 -0
- package/dist/ai/AiAssistantLauncher.js.map +7 -0
- package/dist/ai/AiChat.js +1092 -0
- package/dist/ai/AiChat.js.map +7 -0
- package/dist/ai/AiChatSessions.js +297 -0
- package/dist/ai/AiChatSessions.js.map +7 -0
- package/dist/ai/AiDock.js +347 -0
- package/dist/ai/AiDock.js.map +7 -0
- package/dist/ai/AiMessageContent.js +369 -0
- package/dist/ai/AiMessageContent.js.map +7 -0
- package/dist/ai/ChatPaneTabs.js +251 -0
- package/dist/ai/ChatPaneTabs.js.map +7 -0
- package/dist/ai/index.js +115 -0
- package/dist/ai/index.js.map +7 -0
- package/dist/ai/parts/ConfirmationCard.js +211 -0
- package/dist/ai/parts/ConfirmationCard.js.map +7 -0
- package/dist/ai/parts/FieldDiffCard.js +119 -0
- package/dist/ai/parts/FieldDiffCard.js.map +7 -0
- package/dist/ai/parts/MutationPreviewCard.js +224 -0
- package/dist/ai/parts/MutationPreviewCard.js.map +7 -0
- package/dist/ai/parts/MutationResultCard.js +240 -0
- package/dist/ai/parts/MutationResultCard.js.map +7 -0
- package/dist/ai/parts/approval-cards-map.js +15 -0
- package/dist/ai/parts/approval-cards-map.js.map +7 -0
- package/dist/ai/parts/index.js +24 -0
- package/dist/ai/parts/index.js.map +7 -0
- package/dist/ai/parts/pending-action-api.js +60 -0
- package/dist/ai/parts/pending-action-api.js.map +7 -0
- package/dist/ai/parts/types.js +1 -0
- package/dist/ai/parts/types.js.map +7 -0
- package/dist/ai/parts/useAiPendingActionPolling.js +126 -0
- package/dist/ai/parts/useAiPendingActionPolling.js.map +7 -0
- package/dist/ai/records/ActivityCard.js +83 -0
- package/dist/ai/records/ActivityCard.js.map +7 -0
- package/dist/ai/records/CompanyCard.js +81 -0
- package/dist/ai/records/CompanyCard.js.map +7 -0
- package/dist/ai/records/DealCard.js +76 -0
- package/dist/ai/records/DealCard.js.map +7 -0
- package/dist/ai/records/PersonCard.js +68 -0
- package/dist/ai/records/PersonCard.js.map +7 -0
- package/dist/ai/records/ProductCard.js +68 -0
- package/dist/ai/records/ProductCard.js.map +7 -0
- package/dist/ai/records/RecordCard.js +29 -0
- package/dist/ai/records/RecordCard.js.map +7 -0
- package/dist/ai/records/RecordCardShell.js +103 -0
- package/dist/ai/records/RecordCardShell.js.map +7 -0
- package/dist/ai/records/index.js +31 -0
- package/dist/ai/records/index.js.map +7 -0
- package/dist/ai/records/registry.js +51 -0
- package/dist/ai/records/registry.js.map +7 -0
- package/dist/ai/records/types.js +1 -0
- package/dist/ai/records/types.js.map +7 -0
- package/dist/ai/ui-part-registry.js +112 -0
- package/dist/ai/ui-part-registry.js.map +7 -0
- package/dist/ai/ui-part-slots.js +14 -0
- package/dist/ai/ui-part-slots.js.map +7 -0
- package/dist/ai/ui-parts/pending-phase3-placeholder.js +35 -0
- package/dist/ai/ui-parts/pending-phase3-placeholder.js.map +7 -0
- package/dist/ai/upload-adapter.js +256 -0
- package/dist/ai/upload-adapter.js.map +7 -0
- package/dist/ai/useAiChat.js +549 -0
- package/dist/ai/useAiChat.js.map +7 -0
- package/dist/ai/useAiChatUpload.js +127 -0
- package/dist/ai/useAiChatUpload.js.map +7 -0
- package/dist/ai/useAiShortcuts.js +43 -0
- package/dist/ai/useAiShortcuts.js.map +7 -0
- package/dist/backend/AppShell.js +8 -4
- package/dist/backend/AppShell.js.map +2 -2
- package/dist/backend/BackendChromeProvider.js +2 -0
- package/dist/backend/BackendChromeProvider.js.map +2 -2
- package/dist/backend/DataTable.js +19 -2
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/FilterBar.js +19 -15
- package/dist/backend/FilterBar.js.map +2 -2
- package/dist/backend/dashboard/DashboardScreen.js +31 -3
- package/dist/backend/dashboard/DashboardScreen.js.map +2 -2
- package/dist/backend/injection/spotIds.js +6 -0
- package/dist/backend/injection/spotIds.js.map +2 -2
- package/dist/backend/notifications/useNotificationEffect.js +38 -2
- package/dist/backend/notifications/useNotificationEffect.js.map +2 -2
- package/dist/index.js +1 -0
- package/dist/index.js.map +2 -2
- package/jest.config.cjs +7 -1
- package/jest.markdown-mock.tsx +7 -0
- package/package.json +10 -4
- package/src/ai/AiAssistantLauncher.tsx +805 -0
- package/src/ai/AiChat.tsx +1483 -0
- package/src/ai/AiChatSessions.tsx +429 -0
- package/src/ai/AiDock.tsx +505 -0
- package/src/ai/AiMessageContent.tsx +515 -0
- package/src/ai/ChatPaneTabs.tsx +310 -0
- package/src/ai/__tests__/AiChat.conversation.test.tsx +160 -0
- package/src/ai/__tests__/AiChat.debug.test.tsx +152 -0
- package/src/ai/__tests__/AiChat.registry.test.tsx +213 -0
- package/src/ai/__tests__/AiChat.test.tsx +257 -0
- package/src/ai/__tests__/AiDock.test.tsx +124 -0
- package/src/ai/__tests__/AiMessageContent.test.ts +111 -0
- package/src/ai/__tests__/ui-part-registry.test.ts +199 -0
- package/src/ai/__tests__/ui-part-slots.test.ts +43 -0
- package/src/ai/__tests__/upload-adapter.test.ts +213 -0
- package/src/ai/__tests__/useAiChatUpload.test.tsx +163 -0
- package/src/ai/__tests__/useAiShortcuts.test.tsx +100 -0
- package/src/ai/index.ts +125 -0
- package/src/ai/parts/ConfirmationCard.tsx +310 -0
- package/src/ai/parts/FieldDiffCard.tsx +173 -0
- package/src/ai/parts/MutationPreviewCard.tsx +302 -0
- package/src/ai/parts/MutationResultCard.tsx +360 -0
- package/src/ai/parts/__tests__/ConfirmationCard.test.tsx +169 -0
- package/src/ai/parts/__tests__/FieldDiffCard.test.tsx +74 -0
- package/src/ai/parts/__tests__/MutationPreviewCard.test.tsx +177 -0
- package/src/ai/parts/__tests__/MutationResultCard.test.tsx +127 -0
- package/src/ai/parts/__tests__/useAiPendingActionPolling.test.tsx +151 -0
- package/src/ai/parts/approval-cards-map.ts +24 -0
- package/src/ai/parts/index.ts +27 -0
- package/src/ai/parts/pending-action-api.ts +123 -0
- package/src/ai/parts/types.ts +84 -0
- package/src/ai/parts/useAiPendingActionPolling.ts +210 -0
- package/src/ai/records/ActivityCard.tsx +102 -0
- package/src/ai/records/CompanyCard.tsx +89 -0
- package/src/ai/records/DealCard.tsx +85 -0
- package/src/ai/records/PersonCard.tsx +77 -0
- package/src/ai/records/ProductCard.tsx +83 -0
- package/src/ai/records/RecordCard.tsx +37 -0
- package/src/ai/records/RecordCardShell.tsx +169 -0
- package/src/ai/records/index.ts +30 -0
- package/src/ai/records/registry.tsx +80 -0
- package/src/ai/records/types.ts +90 -0
- package/src/ai/ui-part-registry.ts +233 -0
- package/src/ai/ui-part-slots.ts +32 -0
- package/src/ai/ui-parts/pending-phase3-placeholder.tsx +50 -0
- package/src/ai/upload-adapter.ts +421 -0
- package/src/ai/useAiChat.ts +865 -0
- package/src/ai/useAiChatUpload.ts +180 -0
- package/src/ai/useAiShortcuts.ts +79 -0
- package/src/backend/AppShell.tsx +12 -5
- package/src/backend/BackendChromeProvider.tsx +2 -0
- package/src/backend/DataTable.tsx +20 -1
- package/src/backend/FilterBar.tsx +26 -13
- package/src/backend/__tests__/BackendChromeProvider.test.tsx +45 -0
- package/src/backend/dashboard/DashboardScreen.tsx +38 -3
- package/src/backend/dashboard/__tests__/DashboardScreen.test.tsx +24 -1
- package/src/backend/injection/spotIds.ts +6 -0
- package/src/backend/notifications/__tests__/useNotificationEffect.test.tsx +77 -0
- package/src/backend/notifications/useNotificationEffect.ts +47 -2
- 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": "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",
|
|
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
|