@open-mercato/ui 0.6.8-develop.7082.1.f2a845b020 → 0.6.8-develop.7090.1.4dbca7b350
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/dist/ai/records/ActivityCard.js +4 -3
- package/dist/ai/records/ActivityCard.js.map +2 -2
- package/dist/ai/records/DealCard.js +4 -3
- package/dist/ai/records/DealCard.js.map +2 -2
- package/dist/ai/records/ProductCard.js +3 -2
- package/dist/ai/records/ProductCard.js.map +2 -2
- package/dist/backend/icons/lucideRegistry.generated.js +2 -0
- package/dist/backend/icons/lucideRegistry.generated.js.map +2 -2
- package/dist/primitives/alert.js +1 -1
- package/dist/primitives/alert.js.map +2 -2
- package/dist/utils/format.js +5 -5
- package/dist/utils/format.js.map +2 -2
- package/package.json +3 -3
- package/src/ai/parts/__tests__/ConfirmationCard.test.tsx +2 -0
- package/src/ai/records/ActivityCard.tsx +4 -3
- package/src/ai/records/DealCard.tsx +4 -3
- package/src/ai/records/ProductCard.tsx +3 -2
- package/src/backend/icons/lucideRegistry.generated.tsx +2 -0
- package/src/primitives/__tests__/alert.test.tsx +41 -1
- package/src/primitives/__tests__/notification.test.tsx +1 -1
- package/src/primitives/alert.tsx +5 -2
- package/src/utils/__tests__/format.test.ts +27 -14
- package/src/utils/format.ts +10 -5
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
Generated lucide registry with
|
|
1
|
+
Generated lucide registry with 161 icons -> /home/runner/work/open-mercato/open-mercato/packages/ui/src/backend/icons/lucideRegistry.generated.tsx
|
|
2
2
|
[build:ui] found 400 entry points
|
|
3
3
|
[build:ui] built successfully
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
Phone,
|
|
11
11
|
StickyNote
|
|
12
12
|
} from "lucide-react";
|
|
13
|
-
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
13
|
+
import { useT, useLocale } from "@open-mercato/shared/lib/i18n/context";
|
|
14
14
|
import { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from "./RecordCardShell.js";
|
|
15
15
|
import { formatDate } from "../../utils/format.js";
|
|
16
16
|
function pickActivityIcon(type) {
|
|
@@ -26,10 +26,11 @@ function pickActivityIcon(type) {
|
|
|
26
26
|
}
|
|
27
27
|
function ActivityCard(props) {
|
|
28
28
|
const t = useT();
|
|
29
|
+
const locale = useLocale();
|
|
29
30
|
const Icon = pickActivityIcon(props.type);
|
|
30
31
|
const status = props.status ? { label: props.status, variant: statusToTagVariant(props.status) } : null;
|
|
31
|
-
const dueDate = formatDate(props.dueDate);
|
|
32
|
-
const completedAt = formatDate(props.completedAt);
|
|
32
|
+
const dueDate = formatDate(props.dueDate, locale);
|
|
33
|
+
const completedAt = formatDate(props.completedAt, locale);
|
|
33
34
|
const items = [
|
|
34
35
|
props.type ? { label: t("ai_assistant.chat.records.fields.type", "Type"), value: props.type } : null,
|
|
35
36
|
dueDate ? {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
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'\nimport { formatDate } from '../../utils/format'\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": ";
|
|
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, useLocale } from '@open-mercato/shared/lib/i18n/context'\nimport { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from './RecordCardShell'\nimport type { ActivityRecordPayload } from './types'\nimport { formatDate } from '../../utils/format'\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 locale = useLocale()\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, locale)\n const completedAt = formatDate(props.completedAt, locale)\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": ";AAiDY,SACE,KADF;AA9CZ;AAAA,EACE,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,MAAM,iBAAiB;AAChC,SAAS,cAAc,iBAAiB,QAAQ,0BAA0B;AAE1E,SAAS,kBAAkB;AAE3B,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,SAAS,UAAU;AACzB,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,SAAS,MAAM;AAChD,QAAM,cAAc,WAAW,MAAM,aAAa,MAAM;AAExD,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
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Briefcase, Building2, CalendarDays, CircleDollarSign, User } from "lucide-react";
|
|
4
|
-
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
4
|
+
import { useT, useLocale } from "@open-mercato/shared/lib/i18n/context";
|
|
5
5
|
import { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from "./RecordCardShell.js";
|
|
6
6
|
import { formatCurrency, formatDate } from "../../utils/format.js";
|
|
7
7
|
function DealCard(props) {
|
|
8
8
|
const t = useT();
|
|
9
|
+
const locale = useLocale();
|
|
9
10
|
const status = props.status ? { label: props.status, variant: statusToTagVariant(props.status) } : null;
|
|
10
11
|
const stage = props.stage && props.stage !== props.status ? props.stage : null;
|
|
11
|
-
const amount = formatCurrency(props.amount, props.currency);
|
|
12
|
-
const closeDate = formatDate(props.closeDate);
|
|
12
|
+
const amount = formatCurrency(props.amount, props.currency, locale);
|
|
13
|
+
const closeDate = formatDate(props.closeDate, locale);
|
|
13
14
|
const items = [
|
|
14
15
|
stage ? { label: t("ai_assistant.chat.records.fields.stage", "Stage"), value: stage } : null,
|
|
15
16
|
amount ? { label: t("ai_assistant.chat.records.fields.amount", "Amount"), value: /* @__PURE__ */ jsx("span", { className: "font-medium", children: amount }) } : null,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
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'\nimport { formatCurrency, formatDate } from '../../utils/format'\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 = formatCurrency(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": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Briefcase, Building2, CalendarDays, CircleDollarSign, User } from 'lucide-react'\nimport { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'\nimport { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from './RecordCardShell'\nimport type { DealRecordPayload } from './types'\nimport { formatCurrency, formatDate } from '../../utils/format'\n\nexport interface DealCardProps extends DealRecordPayload {}\n\nexport function DealCard(props: DealCardProps) {\n const t = useT()\n const locale = useLocale()\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 = formatCurrency(props.amount, props.currency, locale)\n const closeDate = formatDate(props.closeDate, locale)\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": ";AAuBqF,cAwB/E,YAxB+E;AApBrF,SAAS,WAAW,WAAW,cAAc,kBAAkB,YAAY;AAC3E,SAAS,MAAM,iBAAiB;AAChC,SAAS,cAAc,iBAAiB,QAAQ,0BAA0B;AAE1E,SAAS,gBAAgB,kBAAkB;AAIpC,SAAS,SAAS,OAAsB;AAC7C,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,SAAS,MAAM,SACjB,EAAE,OAAO,MAAM,QAAQ,SAAS,mBAAmB,MAAM,MAAM,EAAE,IACjE;AACJ,QAAM,QAAQ,MAAM,SAAS,MAAM,UAAU,MAAM,SAAS,MAAM,QAAQ;AAC1E,QAAM,SAAS,eAAe,MAAM,QAAQ,MAAM,UAAU,MAAM;AAClE,QAAM,YAAY,WAAW,MAAM,WAAW,MAAM;AAEpD,QAAM,QAAQ;AAAA,IACZ,QAAQ,EAAE,OAAO,EAAE,0CAA0C,OAAO,GAAG,OAAO,MAAM,IAAI;AAAA,IACxF,SAAS,EAAE,OAAO,EAAE,2CAA2C,QAAQ,GAAG,OAAO,oBAAC,UAAK,WAAU,eAAe,kBAAO,EAAQ,IAAI;AAAA,IACnI,YAAY,EAAE,OAAO,EAAE,0CAA0C,OAAO,GAAG,OAAO,UAAU,IAAI;AAAA,IAChG,MAAM,cAAc,EAAE,OAAO,EAAE,4CAA4C,SAAS,GAAG,OAAO,MAAM,YAAY,IAAI;AAAA,IACpH,MAAM,aAAa,EAAE,OAAO,EAAE,4CAA4C,SAAS,GAAG,OAAO,MAAM,WAAW,IAAI;AAAA,IAClH,MAAM,YAAY,EAAE,OAAO,EAAE,0CAA0C,OAAO,GAAG,OAAO,MAAM,UAAU,IAAI;AAAA,EAC9G,EAAE,OAAO,OAAO;AAEhB,QAAM,gBAA0B,CAAC;AACjC,MAAI,MAAM,YAAa,eAAc,KAAK,MAAM,WAAW;AAC3D,MAAI,MAAM,cAAc,CAAC,MAAM,YAAa,eAAc,KAAK,MAAM,UAAU;AAC/E,MAAI,OAAQ,eAAc,KAAK,MAAM;AAErC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,EAAE,wCAAwC,MAAM;AAAA,MAC3D,UAAU,oBAAC,aAAU,WAAU,UAAS,eAAW,MAAC;AAAA,MACpD,OAAO,MAAM;AAAA,MACb,UAAU,cAAc,KAAK,UAAK;AAAA,MAClC;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,mBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Package, Tag as TagIcon } from "lucide-react";
|
|
4
|
-
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
4
|
+
import { useT, useLocale } from "@open-mercato/shared/lib/i18n/context";
|
|
5
5
|
import { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from "./RecordCardShell.js";
|
|
6
6
|
import { formatCurrency } from "../../utils/format.js";
|
|
7
7
|
function ProductCard(props) {
|
|
8
8
|
const t = useT();
|
|
9
|
+
const locale = useLocale();
|
|
9
10
|
const status = props.status ? { label: props.status, variant: statusToTagVariant(props.status) } : null;
|
|
10
|
-
const price = formatCurrency(props.price, props.currency);
|
|
11
|
+
const price = formatCurrency(props.price, props.currency, locale);
|
|
11
12
|
const leading = props.imageUrl ? /* @__PURE__ */ jsx("div", { className: "relative size-12 overflow-hidden rounded-md border border-border bg-muted", children: /* @__PURE__ */ jsx(
|
|
12
13
|
"img",
|
|
13
14
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
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'\nimport { formatCurrency } from '../../utils/format'\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 = formatCurrency(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": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Package, Tag as TagIcon } from 'lucide-react'\nimport { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'\nimport { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from './RecordCardShell'\nimport type { ProductRecordPayload } from './types'\nimport { formatCurrency } from '../../utils/format'\n\nexport interface ProductCardProps extends ProductRecordPayload {}\n\nexport function ProductCard(props: ProductCardProps) {\n const t = useT()\n const locale = useLocale()\n const status = props.status\n ? { label: props.status, variant: statusToTagVariant(props.status) }\n : null\n const price = formatCurrency(props.price, props.currency, locale)\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": ";AAqBM,cAgCA,YAhCA;AAlBN,SAAS,SAAS,OAAO,eAAe;AACxC,SAAS,MAAM,iBAAiB;AAChC,SAAS,cAAc,iBAAiB,QAAQ,0BAA0B;AAE1E,SAAS,sBAAsB;AAIxB,SAAS,YAAY,OAAyB;AACnD,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,SAAS,MAAM,SACjB,EAAE,OAAO,MAAM,QAAQ,SAAS,mBAAmB,MAAM,MAAM,EAAE,IACjE;AACJ,QAAM,QAAQ,eAAe,MAAM,OAAO,MAAM,UAAU,MAAM;AAEhE,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
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
Bolt,
|
|
18
18
|
Book,
|
|
19
19
|
Bookmark,
|
|
20
|
+
Bot,
|
|
20
21
|
Box,
|
|
21
22
|
Boxes,
|
|
22
23
|
Briefcase,
|
|
@@ -179,6 +180,7 @@ const LUCIDE_ICON_REGISTRY = {
|
|
|
179
180
|
"bolt": Bolt,
|
|
180
181
|
"book": Book,
|
|
181
182
|
"bookmark": Bookmark,
|
|
183
|
+
"bot": Bot,
|
|
182
184
|
"box": Box,
|
|
183
185
|
"boxes": Boxes,
|
|
184
186
|
"briefcase": Briefcase,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/backend/icons/lucideRegistry.generated.tsx"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable */\n// AUTO-GENERATED by @open-mercato/ui build. Do not edit by hand.\n// Generated from discovered icon string usages across the repo.\n\nimport type { LucideIcon } from 'lucide-react';\nimport {\n Activity,\n AlarmClock,\n AlertCircle,\n AlertOctagon,\n AlertTriangle,\n Apple,\n Archive,\n AtSign,\n Award,\n BadgeCheck,\n Ban,\n Banknote,\n BarChart2,\n BarChart3,\n Bell,\n Bolt,\n Book,\n Bookmark,\n Box,\n Boxes,\n Briefcase,\n BriefcaseBusiness,\n Building,\n Building2,\n Calendar,\n CalendarCheck,\n CalendarClock,\n CalendarCog,\n CalendarMinus,\n CalendarOff,\n CalendarX,\n Check,\n CheckCircle,\n CheckCircle2,\n CheckSquare,\n Circle,\n ClipboardCheck,\n ClipboardList,\n Clock,\n Clock3,\n Coins,\n Copy,\n CopyX,\n CreditCard,\n Crown,\n Database,\n DollarSign,\n Download,\n ExternalLink,\n Eye,\n FileCheck,\n FileMinus,\n FilePenLine,\n Files,\n FileSpreadsheet,\n FileText,\n FilterX,\n Flag,\n Flame,\n FolderTree,\n Gauge,\n GitBranch,\n GitCompareArrows,\n GitPullRequestArrow,\n Globe,\n GraduationCap,\n Hammer,\n Hand,\n Handshake,\n Headphones,\n Heart,\n HelpCircle,\n Hourglass,\n Inbox,\n Key,\n KeyRound,\n Layers,\n Leaf,\n Lightbulb,\n LineChart,\n Link,\n List,\n ListX,\n Loader,\n Loader2,\n Lock,\n Mail,\n MailOpen,\n Map,\n MapPin,\n MapPinned,\n Megaphone,\n MessageCircle,\n MessageSquareReply,\n MinusCircle,\n Notebook,\n Package,\n PackageCheck,\n PackagePlus,\n PackageSearch,\n PackageX,\n PauseCircle,\n Percent,\n Phone,\n PhoneCall,\n PieChart,\n PlusSquare,\n Receipt,\n ReceiptText,\n RefreshCcw,\n RefreshCw,\n Repeat2,\n Reply,\n RotateCcw,\n Ruler,\n Send,\n Settings,\n Shapes,\n Share2,\n Shield,\n ShieldAlert,\n ShieldCheck,\n ShoppingBag,\n ShoppingCart,\n Shuffle,\n Sliders,\n Smartphone,\n Snowflake,\n Sparkles,\n Star,\n StickyNote,\n Store,\n Tag,\n Target,\n ThumbsUp,\n Ticket,\n Timer,\n Trash2,\n TrendingUp,\n TriangleAlert,\n Trophy,\n Truck,\n Undo,\n Undo2,\n Unlock,\n User,\n UserCheck,\n UserMinus,\n UserPlus,\n UserRound,\n Users,\n UserX,\n Wallet,\n Warehouse,\n Webhook,\n Workflow,\n Wrench,\n X,\n XCircle,\n ZapOff,\n} from 'lucide-react';\n\nexport const LUCIDE_ICON_REGISTRY: Record<string, LucideIcon> = {\n 'activity': Activity,\n 'alarm-clock': AlarmClock,\n 'alert-circle': AlertCircle,\n 'alert-octagon': AlertOctagon,\n 'alert-triangle': AlertTriangle,\n 'apple': Apple,\n 'archive': Archive,\n 'at-sign': AtSign,\n 'award': Award,\n 'badge-check': BadgeCheck,\n 'ban': Ban,\n 'banknote': Banknote,\n 'bar-chart-2': BarChart2,\n 'bar-chart-3': BarChart3,\n 'bell': Bell,\n 'bolt': Bolt,\n 'book': Book,\n 'bookmark': Bookmark,\n 'box': Box,\n 'boxes': Boxes,\n 'briefcase': Briefcase,\n 'briefcase-business': BriefcaseBusiness,\n 'building': Building,\n 'building2': Building2,\n 'calendar': Calendar,\n 'calendar-check': CalendarCheck,\n 'calendar-clock': CalendarClock,\n 'calendar-cog': CalendarCog,\n 'calendar-minus': CalendarMinus,\n 'calendar-off': CalendarOff,\n 'calendar-x': CalendarX,\n 'check': Check,\n 'check-circle': CheckCircle,\n 'check-circle-2': CheckCircle2,\n 'check-square': CheckSquare,\n 'circle': Circle,\n 'clipboard-check': ClipboardCheck,\n 'clipboard-list': ClipboardList,\n 'clock': Clock,\n 'clock-3': Clock3,\n 'coins': Coins,\n 'copy': Copy,\n 'copy-x': CopyX,\n 'credit-card': CreditCard,\n 'crown': Crown,\n 'database': Database,\n 'dollar-sign': DollarSign,\n 'download': Download,\n 'external-link': ExternalLink,\n 'eye': Eye,\n 'file-check': FileCheck,\n 'file-minus': FileMinus,\n 'file-pen-line': FilePenLine,\n 'file-spreadsheet': FileSpreadsheet,\n 'file-text': FileText,\n 'files': Files,\n 'filter-x': FilterX,\n 'flag': Flag,\n 'flame': Flame,\n 'folder-tree': FolderTree,\n 'gauge': Gauge,\n 'git-branch': GitBranch,\n 'git-compare-arrows': GitCompareArrows,\n 'git-pull-request-arrow': GitPullRequestArrow,\n 'globe': Globe,\n 'graduation-cap': GraduationCap,\n 'hammer': Hammer,\n 'hand': Hand,\n 'handshake': Handshake,\n 'headphones': Headphones,\n 'heart': Heart,\n 'help-circle': HelpCircle,\n 'hourglass': Hourglass,\n 'inbox': Inbox,\n 'key': Key,\n 'key-round': KeyRound,\n 'layers': Layers,\n 'leaf': Leaf,\n 'lightbulb': Lightbulb,\n 'line-chart': LineChart,\n 'link': Link,\n 'list': List,\n 'list-x': ListX,\n 'loader': Loader,\n 'loader-2': Loader2,\n 'lock': Lock,\n 'mail': Mail,\n 'mail-open': MailOpen,\n 'map': Map,\n 'map-pin': MapPin,\n 'map-pinned': MapPinned,\n 'megaphone': Megaphone,\n 'message-circle': MessageCircle,\n 'message-square-reply': MessageSquareReply,\n 'minus-circle': MinusCircle,\n 'notebook': Notebook,\n 'package': Package,\n 'package-check': PackageCheck,\n 'package-plus': PackagePlus,\n 'package-search': PackageSearch,\n 'package-x': PackageX,\n 'pause-circle': PauseCircle,\n 'percent': Percent,\n 'phone': Phone,\n 'phone-call': PhoneCall,\n 'pie-chart': PieChart,\n 'plus-square': PlusSquare,\n 'receipt': Receipt,\n 'receipt-text': ReceiptText,\n 'refresh-ccw': RefreshCcw,\n 'refresh-cw': RefreshCw,\n 'repeat-2': Repeat2,\n 'reply': Reply,\n 'rotate-ccw': RotateCcw,\n 'ruler': Ruler,\n 'send': Send,\n 'settings': Settings,\n 'shapes': Shapes,\n 'share-2': Share2,\n 'shield': Shield,\n 'shield-alert': ShieldAlert,\n 'shield-check': ShieldCheck,\n 'shopping-bag': ShoppingBag,\n 'shopping-cart': ShoppingCart,\n 'shuffle': Shuffle,\n 'sliders': Sliders,\n 'smartphone': Smartphone,\n 'snowflake': Snowflake,\n 'sparkles': Sparkles,\n 'star': Star,\n 'sticky-note': StickyNote,\n 'store': Store,\n 'tag': Tag,\n 'target': Target,\n 'thumbs-up': ThumbsUp,\n 'ticket': Ticket,\n 'timer': Timer,\n 'trash-2': Trash2,\n 'trending-up': TrendingUp,\n 'triangle-alert': TriangleAlert,\n 'trophy': Trophy,\n 'truck': Truck,\n 'undo': Undo,\n 'undo-2': Undo2,\n 'unlock': Unlock,\n 'user': User,\n 'user-check': UserCheck,\n 'user-minus': UserMinus,\n 'user-plus': UserPlus,\n 'user-round': UserRound,\n 'user-x': UserX,\n 'users': Users,\n 'wallet': Wallet,\n 'warehouse': Warehouse,\n 'webhook': Webhook,\n 'workflow': Workflow,\n 'wrench': Wrench,\n 'x': X,\n 'x-circle': XCircle,\n 'zap-off': ZapOff,\n};\n"],
|
|
5
|
-
"mappings": "AAKA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,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",
|
|
4
|
+
"sourcesContent": ["/* eslint-disable */\n// AUTO-GENERATED by @open-mercato/ui build. Do not edit by hand.\n// Generated from discovered icon string usages across the repo.\n\nimport type { LucideIcon } from 'lucide-react';\nimport {\n Activity,\n AlarmClock,\n AlertCircle,\n AlertOctagon,\n AlertTriangle,\n Apple,\n Archive,\n AtSign,\n Award,\n BadgeCheck,\n Ban,\n Banknote,\n BarChart2,\n BarChart3,\n Bell,\n Bolt,\n Book,\n Bookmark,\n Bot,\n Box,\n Boxes,\n Briefcase,\n BriefcaseBusiness,\n Building,\n Building2,\n Calendar,\n CalendarCheck,\n CalendarClock,\n CalendarCog,\n CalendarMinus,\n CalendarOff,\n CalendarX,\n Check,\n CheckCircle,\n CheckCircle2,\n CheckSquare,\n Circle,\n ClipboardCheck,\n ClipboardList,\n Clock,\n Clock3,\n Coins,\n Copy,\n CopyX,\n CreditCard,\n Crown,\n Database,\n DollarSign,\n Download,\n ExternalLink,\n Eye,\n FileCheck,\n FileMinus,\n FilePenLine,\n Files,\n FileSpreadsheet,\n FileText,\n FilterX,\n Flag,\n Flame,\n FolderTree,\n Gauge,\n GitBranch,\n GitCompareArrows,\n GitPullRequestArrow,\n Globe,\n GraduationCap,\n Hammer,\n Hand,\n Handshake,\n Headphones,\n Heart,\n HelpCircle,\n Hourglass,\n Inbox,\n Key,\n KeyRound,\n Layers,\n Leaf,\n Lightbulb,\n LineChart,\n Link,\n List,\n ListX,\n Loader,\n Loader2,\n Lock,\n Mail,\n MailOpen,\n Map,\n MapPin,\n MapPinned,\n Megaphone,\n MessageCircle,\n MessageSquareReply,\n MinusCircle,\n Notebook,\n Package,\n PackageCheck,\n PackagePlus,\n PackageSearch,\n PackageX,\n PauseCircle,\n Percent,\n Phone,\n PhoneCall,\n PieChart,\n PlusSquare,\n Receipt,\n ReceiptText,\n RefreshCcw,\n RefreshCw,\n Repeat2,\n Reply,\n RotateCcw,\n Ruler,\n Send,\n Settings,\n Shapes,\n Share2,\n Shield,\n ShieldAlert,\n ShieldCheck,\n ShoppingBag,\n ShoppingCart,\n Shuffle,\n Sliders,\n Smartphone,\n Snowflake,\n Sparkles,\n Star,\n StickyNote,\n Store,\n Tag,\n Target,\n ThumbsUp,\n Ticket,\n Timer,\n Trash2,\n TrendingUp,\n TriangleAlert,\n Trophy,\n Truck,\n Undo,\n Undo2,\n Unlock,\n User,\n UserCheck,\n UserMinus,\n UserPlus,\n UserRound,\n Users,\n UserX,\n Wallet,\n Warehouse,\n Webhook,\n Workflow,\n Wrench,\n X,\n XCircle,\n ZapOff,\n} from 'lucide-react';\n\nexport const LUCIDE_ICON_REGISTRY: Record<string, LucideIcon> = {\n 'activity': Activity,\n 'alarm-clock': AlarmClock,\n 'alert-circle': AlertCircle,\n 'alert-octagon': AlertOctagon,\n 'alert-triangle': AlertTriangle,\n 'apple': Apple,\n 'archive': Archive,\n 'at-sign': AtSign,\n 'award': Award,\n 'badge-check': BadgeCheck,\n 'ban': Ban,\n 'banknote': Banknote,\n 'bar-chart-2': BarChart2,\n 'bar-chart-3': BarChart3,\n 'bell': Bell,\n 'bolt': Bolt,\n 'book': Book,\n 'bookmark': Bookmark,\n 'bot': Bot,\n 'box': Box,\n 'boxes': Boxes,\n 'briefcase': Briefcase,\n 'briefcase-business': BriefcaseBusiness,\n 'building': Building,\n 'building2': Building2,\n 'calendar': Calendar,\n 'calendar-check': CalendarCheck,\n 'calendar-clock': CalendarClock,\n 'calendar-cog': CalendarCog,\n 'calendar-minus': CalendarMinus,\n 'calendar-off': CalendarOff,\n 'calendar-x': CalendarX,\n 'check': Check,\n 'check-circle': CheckCircle,\n 'check-circle-2': CheckCircle2,\n 'check-square': CheckSquare,\n 'circle': Circle,\n 'clipboard-check': ClipboardCheck,\n 'clipboard-list': ClipboardList,\n 'clock': Clock,\n 'clock-3': Clock3,\n 'coins': Coins,\n 'copy': Copy,\n 'copy-x': CopyX,\n 'credit-card': CreditCard,\n 'crown': Crown,\n 'database': Database,\n 'dollar-sign': DollarSign,\n 'download': Download,\n 'external-link': ExternalLink,\n 'eye': Eye,\n 'file-check': FileCheck,\n 'file-minus': FileMinus,\n 'file-pen-line': FilePenLine,\n 'file-spreadsheet': FileSpreadsheet,\n 'file-text': FileText,\n 'files': Files,\n 'filter-x': FilterX,\n 'flag': Flag,\n 'flame': Flame,\n 'folder-tree': FolderTree,\n 'gauge': Gauge,\n 'git-branch': GitBranch,\n 'git-compare-arrows': GitCompareArrows,\n 'git-pull-request-arrow': GitPullRequestArrow,\n 'globe': Globe,\n 'graduation-cap': GraduationCap,\n 'hammer': Hammer,\n 'hand': Hand,\n 'handshake': Handshake,\n 'headphones': Headphones,\n 'heart': Heart,\n 'help-circle': HelpCircle,\n 'hourglass': Hourglass,\n 'inbox': Inbox,\n 'key': Key,\n 'key-round': KeyRound,\n 'layers': Layers,\n 'leaf': Leaf,\n 'lightbulb': Lightbulb,\n 'line-chart': LineChart,\n 'link': Link,\n 'list': List,\n 'list-x': ListX,\n 'loader': Loader,\n 'loader-2': Loader2,\n 'lock': Lock,\n 'mail': Mail,\n 'mail-open': MailOpen,\n 'map': Map,\n 'map-pin': MapPin,\n 'map-pinned': MapPinned,\n 'megaphone': Megaphone,\n 'message-circle': MessageCircle,\n 'message-square-reply': MessageSquareReply,\n 'minus-circle': MinusCircle,\n 'notebook': Notebook,\n 'package': Package,\n 'package-check': PackageCheck,\n 'package-plus': PackagePlus,\n 'package-search': PackageSearch,\n 'package-x': PackageX,\n 'pause-circle': PauseCircle,\n 'percent': Percent,\n 'phone': Phone,\n 'phone-call': PhoneCall,\n 'pie-chart': PieChart,\n 'plus-square': PlusSquare,\n 'receipt': Receipt,\n 'receipt-text': ReceiptText,\n 'refresh-ccw': RefreshCcw,\n 'refresh-cw': RefreshCw,\n 'repeat-2': Repeat2,\n 'reply': Reply,\n 'rotate-ccw': RotateCcw,\n 'ruler': Ruler,\n 'send': Send,\n 'settings': Settings,\n 'shapes': Shapes,\n 'share-2': Share2,\n 'shield': Shield,\n 'shield-alert': ShieldAlert,\n 'shield-check': ShieldCheck,\n 'shopping-bag': ShoppingBag,\n 'shopping-cart': ShoppingCart,\n 'shuffle': Shuffle,\n 'sliders': Sliders,\n 'smartphone': Smartphone,\n 'snowflake': Snowflake,\n 'sparkles': Sparkles,\n 'star': Star,\n 'sticky-note': StickyNote,\n 'store': Store,\n 'tag': Tag,\n 'target': Target,\n 'thumbs-up': ThumbsUp,\n 'ticket': Ticket,\n 'timer': Timer,\n 'trash-2': Trash2,\n 'trending-up': TrendingUp,\n 'triangle-alert': TriangleAlert,\n 'trophy': Trophy,\n 'truck': Truck,\n 'undo': Undo,\n 'undo-2': Undo2,\n 'unlock': Unlock,\n 'user': User,\n 'user-check': UserCheck,\n 'user-minus': UserMinus,\n 'user-plus': UserPlus,\n 'user-round': UserRound,\n 'user-x': UserX,\n 'users': Users,\n 'wallet': Wallet,\n 'warehouse': Warehouse,\n 'webhook': Webhook,\n 'workflow': Workflow,\n 'wrench': Wrench,\n 'x': X,\n 'x-circle': XCircle,\n 'zap-off': ZapOff,\n};\n"],
|
|
5
|
+
"mappings": "AAKA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/primitives/alert.js
CHANGED
|
@@ -193,7 +193,7 @@ const AlertTitle = React.forwardRef(
|
|
|
193
193
|
);
|
|
194
194
|
AlertTitle.displayName = "AlertTitle";
|
|
195
195
|
const AlertDescription = React.forwardRef(
|
|
196
|
-
({ className, ...props }, ref) => /* @__PURE__ */ jsx("
|
|
196
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("text-sm leading-5", className), ...props })
|
|
197
197
|
);
|
|
198
198
|
AlertDescription.displayName = "AlertDescription";
|
|
199
199
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/primitives/alert.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { AlertCircle, CheckCircle2, Info, Rocket, X, AlertTriangle } from 'lucide-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\n\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport type AlertStatus = 'error' | 'warning' | 'success' | 'information' | 'feature'\nexport type AlertStyle = 'filled' | 'light' | 'lighter' | 'stroke'\nexport type AlertSize = 'xs' | 'sm' | 'default'\n\n/**\n * Legacy `variant` values from the pre-Figma-`169:2358` API. Kept for BC \u2014\n * mapped to `status` inside the Alert component so existing call sites do\n * not need migration. The Figma `light` look (`#fecaca` saturated tint\n * for error \u2192 `state/{x}/light` token family) is heavier than the\n * pre-Figma look; legacy call sites that want the softer pre-Figma\n * appearance should switch explicitly to `style=\"lighter\"`.\n */\ntype LegacyVariant = 'default' | 'destructive' | 'success' | 'warning' | 'info'\n\nconst legacyVariantToStatus: Record<LegacyVariant, AlertStatus> = {\n default: 'information',\n destructive: 'error',\n success: 'success',\n warning: 'warning',\n info: 'information',\n}\n\nconst alertVariants = cva(\n 'relative flex w-full gap-2 transition-colors',\n {\n variants: {\n status: {\n error: '',\n warning: '',\n success: '',\n information: '',\n feature: '',\n },\n style: {\n filled: '',\n light: '',\n lighter: '',\n stroke: '',\n },\n size: {\n // Figma X-Small: px-[8px] py-[6px], radius-8, Paragraph/X Small (12/16)\n xs: 'min-h-8 items-center rounded-lg px-2 py-1.5 text-xs leading-4 tracking-tight',\n // Figma Small: px-[10px] py-[8px], radius-8, Paragraph/Small (14/20)\n sm: 'min-h-9 items-center rounded-lg px-2.5 py-2 text-sm leading-5 tracking-tight',\n // Figma Large: px-[12px] py-[10px], radius-12, Paragraph/Small (14/20) multi-line\n default: 'items-start rounded-xl px-3 py-2.5 text-sm leading-5 tracking-tight',\n },\n },\n compoundVariants: [\n // === filled (Figma: bg = state/{x}/base, text = static-white) ===\n { status: 'error', style: 'filled', className: 'border border-transparent bg-status-error-icon text-white' },\n { status: 'warning', style: 'filled', className: 'border border-transparent bg-status-warning-icon text-white' },\n { status: 'success', style: 'filled', className: 'border border-transparent bg-status-success-icon text-white' },\n { status: 'information', style: 'filled', className: 'border border-transparent bg-status-info-icon text-white' },\n { status: 'feature', style: 'filled', className: 'border border-transparent bg-status-neutral-icon text-white' },\n // === light (Figma: bg = state/{x}/light, text = text/strong-950 = #171717 black) ===\n { status: 'error', style: 'light', className: 'border border-transparent bg-status-error-border text-foreground' },\n { status: 'warning', style: 'light', className: 'border border-transparent bg-status-warning-border text-foreground' },\n { status: 'success', style: 'light', className: 'border border-transparent bg-status-success-border text-foreground' },\n { status: 'information', style: 'light', className: 'border border-transparent bg-status-info-border text-foreground' },\n { status: 'feature', style: 'light', className: 'border border-transparent bg-status-neutral-border text-foreground' },\n // === lighter (Figma: bg = state/{x}/lighter, text = text/strong-950 = #171717 black) ===\n { status: 'error', style: 'lighter', className: 'border border-transparent bg-status-error-bg text-foreground' },\n { status: 'warning', style: 'lighter', className: 'border border-transparent bg-status-warning-bg text-foreground' },\n { status: 'success', style: 'lighter', className: 'border border-transparent bg-status-success-bg text-foreground' },\n { status: 'information', style: 'lighter', className: 'border border-transparent bg-status-info-bg text-foreground' },\n { status: 'feature', style: 'lighter', className: 'border border-transparent bg-status-neutral-bg text-foreground' },\n // === stroke (Figma: bg = white, border = stroke/soft-200, text = strong-950, shadow = regular-shadow/medium) ===\n { style: 'stroke', className: 'border border-border bg-background text-foreground shadow-lg' },\n ],\n defaultVariants: {\n status: 'information',\n style: 'light',\n size: 'sm',\n },\n },\n)\n\n// Dismiss button (X) color per Figma: text/strong-950 (#171717 black) at\n// `opacity-40` for light/lighter/stroke \u2014 matches the Figma `close-line`\n// icon which is the dark icon-strong-950 fill rendered at 40% alpha\n// (visually \u2248 #5c5c5c gray). White at full opacity for filled so it\n// stays legible over the saturated bg.\nconst alertDismissVariants = cva(\n 'shrink-0 rounded-md p-0.5 transition-opacity focus:outline-none focus-visible:ring-2 focus-visible:ring-ring/40',\n {\n variants: {\n style: {\n filled: 'text-white opacity-90 hover:opacity-100',\n light: 'text-foreground opacity-40 hover:opacity-100',\n lighter: 'text-foreground opacity-40 hover:opacity-100',\n stroke: 'text-foreground opacity-40 hover:opacity-100',\n },\n },\n defaultVariants: { style: 'light' },\n },\n)\n\nconst alertIconBadgeVariants = cva(\n 'inline-flex shrink-0 items-center justify-center rounded-full text-white',\n {\n variants: {\n status: {\n error: 'bg-status-error-icon',\n warning: 'bg-status-warning-icon',\n success: 'bg-status-success-icon',\n information: 'bg-status-info-icon',\n feature: 'bg-status-neutral-icon',\n },\n size: {\n xs: 'size-4 [&>svg]:size-2.5',\n sm: 'size-5 [&>svg]:size-3',\n default: 'size-6 [&>svg]:size-3.5',\n },\n },\n defaultVariants: { status: 'information', size: 'sm' },\n },\n)\n\nconst defaultStatusIcons: Record<AlertStatus, React.ComponentType<{ className?: string; 'aria-hidden'?: 'true' }>> = {\n error: AlertCircle,\n warning: AlertTriangle,\n success: CheckCircle2,\n information: Info,\n feature: Rocket,\n}\n\nexport type AlertProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'role' | 'style'> &\n Omit<VariantProps<typeof alertVariants>, 'status' | 'style' | 'size'> & {\n status?: AlertStatus\n style?: AlertStyle\n size?: AlertSize\n /** Show the leading icon. Default `true`. */\n showIcon?: boolean\n /** Override the default per-status Lucide icon. */\n icon?: React.ReactNode\n /** Render a trailing close (X) button. Pair with `onDismiss`. */\n dismissible?: boolean\n onDismiss?: () => void\n dismissAriaLabel?: string\n /** Inline action slot (link buttons) rendered to the right of the body. */\n action?: React.ReactNode\n /**\n * @deprecated Use `status` + `style` instead. Kept for BC. Maps the\n * legacy single-prop variants onto `status` and picks up the new\n * `light` + `sm` defaults.\n */\n variant?: LegacyVariant\n }\n\nconst Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n (\n {\n className,\n status,\n style,\n size,\n showIcon = true,\n icon,\n dismissible = false,\n onDismiss,\n dismissAriaLabel = 'Dismiss',\n action,\n variant,\n children,\n ...props\n },\n ref,\n ) => {\n const resolvedStatus: AlertStatus = status ?? (variant ? legacyVariantToStatus[variant] : 'information')\n const resolvedStyle: AlertStyle = style ?? 'light'\n const resolvedSize: AlertSize = size ?? 'sm'\n const IconComponent = defaultStatusIcons[resolvedStatus]\n const rawIcon = icon ?? <IconComponent aria-hidden=\"true\" />\n\n let iconSlot: React.ReactNode = null\n if (showIcon) {\n if (resolvedStyle === 'filled') {\n // Filled: plain icon over saturated bg \u2014 no badge wrap, inherit text-white.\n iconSlot = (\n <span\n className={cn(\n 'inline-flex shrink-0 items-center justify-center',\n resolvedSize === 'default' ? '[&>svg]:size-5' : '[&>svg]:size-4',\n )}\n data-slot=\"alert-icon\"\n >\n {rawIcon}\n </span>\n )\n } else {\n // light / lighter / stroke: badge wrap with status color bg + white icon.\n iconSlot = (\n <span\n className={alertIconBadgeVariants({ status: resolvedStatus, size: resolvedSize })}\n data-slot=\"alert-icon-badge\"\n data-status={resolvedStatus}\n >\n {rawIcon}\n </span>\n )\n }\n }\n\n return (\n <div\n ref={ref}\n role=\"alert\"\n className={cn(\n alertVariants({ status: resolvedStatus, style: resolvedStyle, size: resolvedSize }),\n className,\n )}\n data-slot=\"alert\"\n data-status={resolvedStatus}\n data-style={resolvedStyle}\n {...props}\n >\n {iconSlot}\n <div className=\"min-w-0 flex-1\">{children}</div>\n {action ? <div className=\"shrink-0\">{action}</div> : null}\n {dismissible ? (\n <button\n type=\"button\"\n onClick={onDismiss}\n aria-label={dismissAriaLabel}\n className={alertDismissVariants({ style: resolvedStyle })}\n data-slot=\"alert-dismiss\"\n >\n <X aria-hidden=\"true\" className=\"size-4\" />\n </button>\n ) : null}\n </div>\n )\n },\n)\nAlert.displayName = 'Alert'\n\n// Figma \"Label/Small\": Inter Medium 14 / 20.\nconst AlertTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(\n ({ className, ...props }, ref) => (\n <h5 ref={ref} className={cn('mb-1 text-sm font-medium leading-5', className)} {...props} />\n ),\n)\nAlertTitle.displayName = 'AlertTitle'\n\n// Figma \"Paragraph/Small\": Inter Regular 14 / 20.\nconst AlertDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(\n ({ className, ...props }, ref) => (\n <p ref={ref} className={cn('text-sm leading-5', className)} {...props} />\n ),\n)\nAlertDescription.displayName = 'AlertDescription'\n\nexport { Alert, AlertDescription, AlertTitle, alertVariants, alertIconBadgeVariants }\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { AlertCircle, CheckCircle2, Info, Rocket, X, AlertTriangle } from 'lucide-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\n\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport type AlertStatus = 'error' | 'warning' | 'success' | 'information' | 'feature'\nexport type AlertStyle = 'filled' | 'light' | 'lighter' | 'stroke'\nexport type AlertSize = 'xs' | 'sm' | 'default'\n\n/**\n * Legacy `variant` values from the pre-Figma-`169:2358` API. Kept for BC \u2014\n * mapped to `status` inside the Alert component so existing call sites do\n * not need migration. The Figma `light` look (`#fecaca` saturated tint\n * for error \u2192 `state/{x}/light` token family) is heavier than the\n * pre-Figma look; legacy call sites that want the softer pre-Figma\n * appearance should switch explicitly to `style=\"lighter\"`.\n */\ntype LegacyVariant = 'default' | 'destructive' | 'success' | 'warning' | 'info'\n\nconst legacyVariantToStatus: Record<LegacyVariant, AlertStatus> = {\n default: 'information',\n destructive: 'error',\n success: 'success',\n warning: 'warning',\n info: 'information',\n}\n\nconst alertVariants = cva(\n 'relative flex w-full gap-2 transition-colors',\n {\n variants: {\n status: {\n error: '',\n warning: '',\n success: '',\n information: '',\n feature: '',\n },\n style: {\n filled: '',\n light: '',\n lighter: '',\n stroke: '',\n },\n size: {\n // Figma X-Small: px-[8px] py-[6px], radius-8, Paragraph/X Small (12/16)\n xs: 'min-h-8 items-center rounded-lg px-2 py-1.5 text-xs leading-4 tracking-tight',\n // Figma Small: px-[10px] py-[8px], radius-8, Paragraph/Small (14/20)\n sm: 'min-h-9 items-center rounded-lg px-2.5 py-2 text-sm leading-5 tracking-tight',\n // Figma Large: px-[12px] py-[10px], radius-12, Paragraph/Small (14/20) multi-line\n default: 'items-start rounded-xl px-3 py-2.5 text-sm leading-5 tracking-tight',\n },\n },\n compoundVariants: [\n // === filled (Figma: bg = state/{x}/base, text = static-white) ===\n { status: 'error', style: 'filled', className: 'border border-transparent bg-status-error-icon text-white' },\n { status: 'warning', style: 'filled', className: 'border border-transparent bg-status-warning-icon text-white' },\n { status: 'success', style: 'filled', className: 'border border-transparent bg-status-success-icon text-white' },\n { status: 'information', style: 'filled', className: 'border border-transparent bg-status-info-icon text-white' },\n { status: 'feature', style: 'filled', className: 'border border-transparent bg-status-neutral-icon text-white' },\n // === light (Figma: bg = state/{x}/light, text = text/strong-950 = #171717 black) ===\n { status: 'error', style: 'light', className: 'border border-transparent bg-status-error-border text-foreground' },\n { status: 'warning', style: 'light', className: 'border border-transparent bg-status-warning-border text-foreground' },\n { status: 'success', style: 'light', className: 'border border-transparent bg-status-success-border text-foreground' },\n { status: 'information', style: 'light', className: 'border border-transparent bg-status-info-border text-foreground' },\n { status: 'feature', style: 'light', className: 'border border-transparent bg-status-neutral-border text-foreground' },\n // === lighter (Figma: bg = state/{x}/lighter, text = text/strong-950 = #171717 black) ===\n { status: 'error', style: 'lighter', className: 'border border-transparent bg-status-error-bg text-foreground' },\n { status: 'warning', style: 'lighter', className: 'border border-transparent bg-status-warning-bg text-foreground' },\n { status: 'success', style: 'lighter', className: 'border border-transparent bg-status-success-bg text-foreground' },\n { status: 'information', style: 'lighter', className: 'border border-transparent bg-status-info-bg text-foreground' },\n { status: 'feature', style: 'lighter', className: 'border border-transparent bg-status-neutral-bg text-foreground' },\n // === stroke (Figma: bg = white, border = stroke/soft-200, text = strong-950, shadow = regular-shadow/medium) ===\n { style: 'stroke', className: 'border border-border bg-background text-foreground shadow-lg' },\n ],\n defaultVariants: {\n status: 'information',\n style: 'light',\n size: 'sm',\n },\n },\n)\n\n// Dismiss button (X) color per Figma: text/strong-950 (#171717 black) at\n// `opacity-40` for light/lighter/stroke \u2014 matches the Figma `close-line`\n// icon which is the dark icon-strong-950 fill rendered at 40% alpha\n// (visually \u2248 #5c5c5c gray). White at full opacity for filled so it\n// stays legible over the saturated bg.\nconst alertDismissVariants = cva(\n 'shrink-0 rounded-md p-0.5 transition-opacity focus:outline-none focus-visible:ring-2 focus-visible:ring-ring/40',\n {\n variants: {\n style: {\n filled: 'text-white opacity-90 hover:opacity-100',\n light: 'text-foreground opacity-40 hover:opacity-100',\n lighter: 'text-foreground opacity-40 hover:opacity-100',\n stroke: 'text-foreground opacity-40 hover:opacity-100',\n },\n },\n defaultVariants: { style: 'light' },\n },\n)\n\nconst alertIconBadgeVariants = cva(\n 'inline-flex shrink-0 items-center justify-center rounded-full text-white',\n {\n variants: {\n status: {\n error: 'bg-status-error-icon',\n warning: 'bg-status-warning-icon',\n success: 'bg-status-success-icon',\n information: 'bg-status-info-icon',\n feature: 'bg-status-neutral-icon',\n },\n size: {\n xs: 'size-4 [&>svg]:size-2.5',\n sm: 'size-5 [&>svg]:size-3',\n default: 'size-6 [&>svg]:size-3.5',\n },\n },\n defaultVariants: { status: 'information', size: 'sm' },\n },\n)\n\nconst defaultStatusIcons: Record<AlertStatus, React.ComponentType<{ className?: string; 'aria-hidden'?: 'true' }>> = {\n error: AlertCircle,\n warning: AlertTriangle,\n success: CheckCircle2,\n information: Info,\n feature: Rocket,\n}\n\nexport type AlertProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'role' | 'style'> &\n Omit<VariantProps<typeof alertVariants>, 'status' | 'style' | 'size'> & {\n status?: AlertStatus\n style?: AlertStyle\n size?: AlertSize\n /** Show the leading icon. Default `true`. */\n showIcon?: boolean\n /** Override the default per-status Lucide icon. */\n icon?: React.ReactNode\n /** Render a trailing close (X) button. Pair with `onDismiss`. */\n dismissible?: boolean\n onDismiss?: () => void\n dismissAriaLabel?: string\n /** Inline action slot (link buttons) rendered to the right of the body. */\n action?: React.ReactNode\n /**\n * @deprecated Use `status` + `style` instead. Kept for BC. Maps the\n * legacy single-prop variants onto `status` and picks up the new\n * `light` + `sm` defaults.\n */\n variant?: LegacyVariant\n }\n\nconst Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n (\n {\n className,\n status,\n style,\n size,\n showIcon = true,\n icon,\n dismissible = false,\n onDismiss,\n dismissAriaLabel = 'Dismiss',\n action,\n variant,\n children,\n ...props\n },\n ref,\n ) => {\n const resolvedStatus: AlertStatus = status ?? (variant ? legacyVariantToStatus[variant] : 'information')\n const resolvedStyle: AlertStyle = style ?? 'light'\n const resolvedSize: AlertSize = size ?? 'sm'\n const IconComponent = defaultStatusIcons[resolvedStatus]\n const rawIcon = icon ?? <IconComponent aria-hidden=\"true\" />\n\n let iconSlot: React.ReactNode = null\n if (showIcon) {\n if (resolvedStyle === 'filled') {\n // Filled: plain icon over saturated bg \u2014 no badge wrap, inherit text-white.\n iconSlot = (\n <span\n className={cn(\n 'inline-flex shrink-0 items-center justify-center',\n resolvedSize === 'default' ? '[&>svg]:size-5' : '[&>svg]:size-4',\n )}\n data-slot=\"alert-icon\"\n >\n {rawIcon}\n </span>\n )\n } else {\n // light / lighter / stroke: badge wrap with status color bg + white icon.\n iconSlot = (\n <span\n className={alertIconBadgeVariants({ status: resolvedStatus, size: resolvedSize })}\n data-slot=\"alert-icon-badge\"\n data-status={resolvedStatus}\n >\n {rawIcon}\n </span>\n )\n }\n }\n\n return (\n <div\n ref={ref}\n role=\"alert\"\n className={cn(\n alertVariants({ status: resolvedStatus, style: resolvedStyle, size: resolvedSize }),\n className,\n )}\n data-slot=\"alert\"\n data-status={resolvedStatus}\n data-style={resolvedStyle}\n {...props}\n >\n {iconSlot}\n <div className=\"min-w-0 flex-1\">{children}</div>\n {action ? <div className=\"shrink-0\">{action}</div> : null}\n {dismissible ? (\n <button\n type=\"button\"\n onClick={onDismiss}\n aria-label={dismissAriaLabel}\n className={alertDismissVariants({ style: resolvedStyle })}\n data-slot=\"alert-dismiss\"\n >\n <X aria-hidden=\"true\" className=\"size-4\" />\n </button>\n ) : null}\n </div>\n )\n },\n)\nAlert.displayName = 'Alert'\n\n// Figma \"Label/Small\": Inter Medium 14 / 20.\nconst AlertTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(\n ({ className, ...props }, ref) => (\n <h5 ref={ref} className={cn('mb-1 text-sm font-medium leading-5', className)} {...props} />\n ),\n)\nAlertTitle.displayName = 'AlertTitle'\n\n// Figma \"Paragraph/Small\": Inter Regular 14 / 20.\n// Renders a <div>, not a <p>: callers routinely nest paragraphs, lists and other\n// block elements inside the description, which is invalid inside <p> and makes\n// React fail hydration with \"In HTML, <p> cannot be a descendant of <p>\".\nconst AlertDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={cn('text-sm leading-5', className)} {...props} />\n ),\n)\nAlertDescription.displayName = 'AlertDescription'\n\nexport { Alert, AlertDescription, AlertTitle, alertVariants, alertIconBadgeVariants }\n"],
|
|
5
|
+
"mappings": ";AAqL4B,cAgCtB,YAhCsB;AAnL5B,YAAY,WAAW;AACvB,SAAS,aAAa,cAAc,MAAM,QAAQ,GAAG,qBAAqB;AAC1E,SAAS,WAA8B;AAEvC,SAAS,UAAU;AAgBnB,MAAM,wBAA4D;AAAA,EAChE,SAAS;AAAA,EACT,aAAa;AAAA,EACb,SAAS;AAAA,EACT,SAAS;AAAA,EACT,MAAM;AACR;AAEA,MAAM,gBAAgB;AAAA,EACpB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,QAAQ;AAAA,QACN,OAAO;AAAA,QACP,SAAS;AAAA,QACT,SAAS;AAAA,QACT,aAAa;AAAA,QACb,SAAS;AAAA,MACX;AAAA,MACA,OAAO;AAAA,QACL,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,SAAS;AAAA,QACT,QAAQ;AAAA,MACV;AAAA,MACA,MAAM;AAAA;AAAA,QAEJ,IAAI;AAAA;AAAA,QAEJ,IAAI;AAAA;AAAA,QAEJ,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,kBAAkB;AAAA;AAAA,MAEhB,EAAE,QAAQ,SAAe,OAAO,UAAU,WAAW,4DAA4D;AAAA,MACjH,EAAE,QAAQ,WAAe,OAAO,UAAU,WAAW,8DAA8D;AAAA,MACnH,EAAE,QAAQ,WAAe,OAAO,UAAU,WAAW,8DAA8D;AAAA,MACnH,EAAE,QAAQ,eAAe,OAAO,UAAU,WAAW,2DAA2D;AAAA,MAChH,EAAE,QAAQ,WAAe,OAAO,UAAU,WAAW,8DAA8D;AAAA;AAAA,MAEnH,EAAE,QAAQ,SAAe,OAAO,SAAS,WAAW,mEAAmE;AAAA,MACvH,EAAE,QAAQ,WAAe,OAAO,SAAS,WAAW,qEAAqE;AAAA,MACzH,EAAE,QAAQ,WAAe,OAAO,SAAS,WAAW,qEAAqE;AAAA,MACzH,EAAE,QAAQ,eAAe,OAAO,SAAS,WAAW,kEAAkE;AAAA,MACtH,EAAE,QAAQ,WAAe,OAAO,SAAS,WAAW,qEAAqE;AAAA;AAAA,MAEzH,EAAE,QAAQ,SAAe,OAAO,WAAW,WAAW,+DAA+D;AAAA,MACrH,EAAE,QAAQ,WAAe,OAAO,WAAW,WAAW,iEAAiE;AAAA,MACvH,EAAE,QAAQ,WAAe,OAAO,WAAW,WAAW,iEAAiE;AAAA,MACvH,EAAE,QAAQ,eAAe,OAAO,WAAW,WAAW,8DAA8D;AAAA,MACpH,EAAE,QAAQ,WAAe,OAAO,WAAW,WAAW,iEAAiE;AAAA;AAAA,MAEvH,EAAE,OAAO,UAAU,WAAW,+DAA+D;AAAA,IAC/F;AAAA,IACA,iBAAiB;AAAA,MACf,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,MAAM;AAAA,IACR;AAAA,EACF;AACF;AAOA,MAAM,uBAAuB;AAAA,EAC3B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,OAAO;AAAA,QACL,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,SAAS;AAAA,QACT,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,IACA,iBAAiB,EAAE,OAAO,QAAQ;AAAA,EACpC;AACF;AAEA,MAAM,yBAAyB;AAAA,EAC7B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,QAAQ;AAAA,QACN,OAAO;AAAA,QACP,SAAS;AAAA,QACT,SAAS;AAAA,QACT,aAAa;AAAA,QACb,SAAS;AAAA,MACX;AAAA,MACA,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,iBAAiB,EAAE,QAAQ,eAAe,MAAM,KAAK;AAAA,EACvD;AACF;AAEA,MAAM,qBAA+G;AAAA,EACnH,OAAO;AAAA,EACP,SAAS;AAAA,EACT,SAAS;AAAA,EACT,aAAa;AAAA,EACb,SAAS;AACX;AAyBA,MAAM,QAAQ,MAAM;AAAA,EAClB,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA,mBAAmB;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,GACA,QACG;AACH,UAAM,iBAA8B,WAAW,UAAU,sBAAsB,OAAO,IAAI;AAC1F,UAAM,gBAA4B,SAAS;AAC3C,UAAM,eAA0B,QAAQ;AACxC,UAAM,gBAAgB,mBAAmB,cAAc;AACvD,UAAM,UAAU,QAAQ,oBAAC,iBAAc,eAAY,QAAO;AAE1D,QAAI,WAA4B;AAChC,QAAI,UAAU;AACZ,UAAI,kBAAkB,UAAU;AAE9B,mBACE;AAAA,UAAC;AAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA,iBAAiB,YAAY,mBAAmB;AAAA,YAClD;AAAA,YACA,aAAU;AAAA,YAET;AAAA;AAAA,QACH;AAAA,MAEJ,OAAO;AAEL,mBACE;AAAA,UAAC;AAAA;AAAA,YACC,WAAW,uBAAuB,EAAE,QAAQ,gBAAgB,MAAM,aAAa,CAAC;AAAA,YAChF,aAAU;AAAA,YACV,eAAa;AAAA,YAEZ;AAAA;AAAA,QACH;AAAA,MAEJ;AAAA,IACF;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,WAAW;AAAA,UACT,cAAc,EAAE,QAAQ,gBAAgB,OAAO,eAAe,MAAM,aAAa,CAAC;AAAA,UAClF;AAAA,QACF;AAAA,QACA,aAAU;AAAA,QACV,eAAa;AAAA,QACb,cAAY;AAAA,QACX,GAAG;AAAA,QAEH;AAAA;AAAA,UACD,oBAAC,SAAI,WAAU,kBAAkB,UAAS;AAAA,UACzC,SAAS,oBAAC,SAAI,WAAU,YAAY,kBAAO,IAAS;AAAA,UACpD,cACC;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS;AAAA,cACT,cAAY;AAAA,cACZ,WAAW,qBAAqB,EAAE,OAAO,cAAc,CAAC;AAAA,cACxD,aAAU;AAAA,cAEV,8BAAC,KAAE,eAAY,QAAO,WAAU,UAAS;AAAA;AAAA,UAC3C,IACE;AAAA;AAAA;AAAA,IACN;AAAA,EAEJ;AACF;AACA,MAAM,cAAc;AAGpB,MAAM,aAAa,MAAM;AAAA,EACvB,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QACxB,oBAAC,QAAG,KAAU,WAAW,GAAG,sCAAsC,SAAS,GAAI,GAAG,OAAO;AAE7F;AACA,WAAW,cAAc;AAMzB,MAAM,mBAAmB,MAAM;AAAA,EAC7B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QACxB,oBAAC,SAAI,KAAU,WAAW,GAAG,qBAAqB,SAAS,GAAI,GAAG,OAAO;AAE7E;AACA,iBAAiB,cAAc;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/utils/format.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function formatCurrency(value, currency) {
|
|
1
|
+
function formatCurrency(value, currency, locale) {
|
|
2
2
|
if (value === null || value === void 0 || value === "") return null;
|
|
3
3
|
const numeric = typeof value === "number" ? value : Number(value);
|
|
4
4
|
if (!Number.isFinite(numeric)) {
|
|
@@ -7,18 +7,18 @@ function formatCurrency(value, currency) {
|
|
|
7
7
|
const code = currency && currency.length === 3 ? currency.toUpperCase() : void 0;
|
|
8
8
|
try {
|
|
9
9
|
if (code) {
|
|
10
|
-
return new Intl.NumberFormat(
|
|
10
|
+
return new Intl.NumberFormat(locale, { style: "currency", currency: code }).format(numeric);
|
|
11
11
|
}
|
|
12
12
|
} catch {
|
|
13
13
|
}
|
|
14
|
-
const formatted = new Intl.NumberFormat().format(numeric);
|
|
14
|
+
const formatted = new Intl.NumberFormat(locale).format(numeric);
|
|
15
15
|
return code ? `${formatted} ${code}` : formatted;
|
|
16
16
|
}
|
|
17
|
-
function formatDate(value) {
|
|
17
|
+
function formatDate(value, locale) {
|
|
18
18
|
if (!value) return null;
|
|
19
19
|
const date = new Date(value);
|
|
20
20
|
if (Number.isNaN(date.getTime())) return value;
|
|
21
|
-
return date.toLocaleDateString(
|
|
21
|
+
return date.toLocaleDateString(locale, { year: "numeric", month: "short", day: "numeric" });
|
|
22
22
|
}
|
|
23
23
|
export {
|
|
24
24
|
formatCurrency,
|
package/dist/utils/format.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/format.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Format a monetary value with an optional ISO-4217 currency code.\n *\n * Returns `null` for empty input, the original string for non-numeric input,\n * and falls back to a plain number format (optionally suffixed with the code)\n * when the currency code is missing or rejected by `Intl.NumberFormat`.\n */\nexport function formatCurrency(\n value: string | number | null | undefined,\n currency?: string | null,\n): string | null {\n if (value === null || value === undefined || value === '') return null\n const numeric = typeof value === 'number' ? value : Number(value)\n if (!Number.isFinite(numeric)) {\n return typeof value === 'string' ? value : null\n }\n const code = currency && currency.length === 3 ? currency.toUpperCase() : undefined\n try {\n if (code) {\n return new Intl.NumberFormat(
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/**\n * Format a monetary value with an optional ISO-4217 currency code.\n *\n * Returns `null` for empty input, the original string for non-numeric input,\n * and falls back to a plain number format (optionally suffixed with the code)\n * when the currency code is missing or rejected by `Intl.NumberFormat`.\n *\n * Pass `locale` to format in the application locale (`useLocale()` in client\n * components). Omitting it keeps the runtime default, which varies per machine\n * and is therefore not assertable in tests.\n */\nexport function formatCurrency(\n value: string | number | null | undefined,\n currency?: string | null,\n locale?: string,\n): string | null {\n if (value === null || value === undefined || value === '') return null\n const numeric = typeof value === 'number' ? value : Number(value)\n if (!Number.isFinite(numeric)) {\n return typeof value === 'string' ? value : null\n }\n const code = currency && currency.length === 3 ? currency.toUpperCase() : undefined\n try {\n if (code) {\n return new Intl.NumberFormat(locale, { style: 'currency', currency: code }).format(numeric)\n }\n } catch {\n // fall through to plain number formatting\n }\n const formatted = new Intl.NumberFormat(locale).format(numeric)\n return code ? `${formatted} ${code}` : formatted\n}\n\n/**\n * Format an ISO date string as a localized short date (e.g. `Jun 9, 2026`).\n *\n * Returns `null` for empty input and echoes the original value back when it is\n * not a parseable date. Pass `locale` to format in the application locale.\n */\nexport function formatDate(value: string | null | undefined, locale?: string): string | null {\n if (!value) return null\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return value\n return date.toLocaleDateString(locale, { year: 'numeric', month: 'short', day: 'numeric' })\n}\n"],
|
|
5
|
+
"mappings": "AAWO,SAAS,eACd,OACA,UACA,QACe;AACf,MAAI,UAAU,QAAQ,UAAU,UAAa,UAAU,GAAI,QAAO;AAClE,QAAM,UAAU,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;AAChE,MAAI,CAAC,OAAO,SAAS,OAAO,GAAG;AAC7B,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,QAAQ,EAAE,OAAO,YAAY,UAAU,KAAK,CAAC,EAAE,OAAO,OAAO;AAAA,IAC5F;AAAA,EACF,QAAQ;AAAA,EAER;AACA,QAAM,YAAY,IAAI,KAAK,aAAa,MAAM,EAAE,OAAO,OAAO;AAC9D,SAAO,OAAO,GAAG,SAAS,IAAI,IAAI,KAAK;AACzC;AAQO,SAAS,WAAW,OAAkC,QAAgC;AAC3F,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,MAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,EAAG,QAAO;AACzC,SAAO,KAAK,mBAAmB,QAAQ,EAAE,MAAM,WAAW,OAAO,SAAS,KAAK,UAAU,CAAC;AAC5F;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/ui",
|
|
3
|
-
"version": "0.6.8-develop.
|
|
3
|
+
"version": "0.6.8-develop.7090.1.4dbca7b350",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -155,14 +155,14 @@
|
|
|
155
155
|
"remark-gfm": "^4.0.1"
|
|
156
156
|
},
|
|
157
157
|
"peerDependencies": {
|
|
158
|
-
"@open-mercato/shared": "0.6.8-develop.
|
|
158
|
+
"@open-mercato/shared": "0.6.8-develop.7090.1.4dbca7b350",
|
|
159
159
|
"react": ">=18.0.0",
|
|
160
160
|
"react-dom": ">=18.0.0",
|
|
161
161
|
"react-is": ">=18.0.0"
|
|
162
162
|
},
|
|
163
163
|
"devDependencies": {
|
|
164
164
|
"@figma/code-connect": "^1.3.4",
|
|
165
|
-
"@open-mercato/shared": "0.6.8-develop.
|
|
165
|
+
"@open-mercato/shared": "0.6.8-develop.7090.1.4dbca7b350",
|
|
166
166
|
"@testing-library/dom": "^10.4.1",
|
|
167
167
|
"@testing-library/jest-dom": "^7.0.0",
|
|
168
168
|
"@testing-library/react": "^16.3.1",
|
|
@@ -133,6 +133,8 @@ describe('ConfirmationCard', () => {
|
|
|
133
133
|
expect(
|
|
134
134
|
document.querySelectorAll('[data-ai-confirmation-stale-record]').length,
|
|
135
135
|
).toBe(2)
|
|
136
|
+
expect(document.querySelectorAll('p p').length).toBe(0)
|
|
137
|
+
expect(document.querySelectorAll('p ul').length).toBe(0)
|
|
136
138
|
})
|
|
137
139
|
|
|
138
140
|
it('renders the schema_drift "Schema changed" alert', () => {
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
StickyNote,
|
|
13
13
|
} from 'lucide-react'
|
|
14
14
|
import type { LucideIcon } from 'lucide-react'
|
|
15
|
-
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
15
|
+
import { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'
|
|
16
16
|
import { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from './RecordCardShell'
|
|
17
17
|
import type { ActivityRecordPayload } from './types'
|
|
18
18
|
import { formatDate } from '../../utils/format'
|
|
@@ -33,12 +33,13 @@ export interface ActivityCardProps extends ActivityRecordPayload {}
|
|
|
33
33
|
|
|
34
34
|
export function ActivityCard(props: ActivityCardProps) {
|
|
35
35
|
const t = useT()
|
|
36
|
+
const locale = useLocale()
|
|
36
37
|
const Icon = pickActivityIcon(props.type)
|
|
37
38
|
const status = props.status
|
|
38
39
|
? { label: props.status, variant: statusToTagVariant(props.status) }
|
|
39
40
|
: null
|
|
40
|
-
const dueDate = formatDate(props.dueDate)
|
|
41
|
-
const completedAt = formatDate(props.completedAt)
|
|
41
|
+
const dueDate = formatDate(props.dueDate, locale)
|
|
42
|
+
const completedAt = formatDate(props.completedAt, locale)
|
|
42
43
|
|
|
43
44
|
const items = [
|
|
44
45
|
props.type ? { label: t('ai_assistant.chat.records.fields.type', 'Type'), value: props.type } : null,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import { Briefcase, Building2, CalendarDays, CircleDollarSign, User } from 'lucide-react'
|
|
5
|
-
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
5
|
+
import { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'
|
|
6
6
|
import { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from './RecordCardShell'
|
|
7
7
|
import type { DealRecordPayload } from './types'
|
|
8
8
|
import { formatCurrency, formatDate } from '../../utils/format'
|
|
@@ -11,12 +11,13 @@ export interface DealCardProps extends DealRecordPayload {}
|
|
|
11
11
|
|
|
12
12
|
export function DealCard(props: DealCardProps) {
|
|
13
13
|
const t = useT()
|
|
14
|
+
const locale = useLocale()
|
|
14
15
|
const status = props.status
|
|
15
16
|
? { label: props.status, variant: statusToTagVariant(props.status) }
|
|
16
17
|
: null
|
|
17
18
|
const stage = props.stage && props.stage !== props.status ? props.stage : null
|
|
18
|
-
const amount = formatCurrency(props.amount, props.currency)
|
|
19
|
-
const closeDate = formatDate(props.closeDate)
|
|
19
|
+
const amount = formatCurrency(props.amount, props.currency, locale)
|
|
20
|
+
const closeDate = formatDate(props.closeDate, locale)
|
|
20
21
|
|
|
21
22
|
const items = [
|
|
22
23
|
stage ? { label: t('ai_assistant.chat.records.fields.stage', 'Stage'), value: stage } : null,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import { Package, Tag as TagIcon } from 'lucide-react'
|
|
5
|
-
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
5
|
+
import { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'
|
|
6
6
|
import { KeyValueList, RecordCardShell, TagRow, statusToTagVariant } from './RecordCardShell'
|
|
7
7
|
import type { ProductRecordPayload } from './types'
|
|
8
8
|
import { formatCurrency } from '../../utils/format'
|
|
@@ -11,10 +11,11 @@ export interface ProductCardProps extends ProductRecordPayload {}
|
|
|
11
11
|
|
|
12
12
|
export function ProductCard(props: ProductCardProps) {
|
|
13
13
|
const t = useT()
|
|
14
|
+
const locale = useLocale()
|
|
14
15
|
const status = props.status
|
|
15
16
|
? { label: props.status, variant: statusToTagVariant(props.status) }
|
|
16
17
|
: null
|
|
17
|
-
const price = formatCurrency(props.price, props.currency)
|
|
18
|
+
const price = formatCurrency(props.price, props.currency, locale)
|
|
18
19
|
|
|
19
20
|
const leading = props.imageUrl ? (
|
|
20
21
|
<div className="relative size-12 overflow-hidden rounded-md border border-border bg-muted">
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
Bolt,
|
|
23
23
|
Book,
|
|
24
24
|
Bookmark,
|
|
25
|
+
Bot,
|
|
25
26
|
Box,
|
|
26
27
|
Boxes,
|
|
27
28
|
Briefcase,
|
|
@@ -185,6 +186,7 @@ export const LUCIDE_ICON_REGISTRY: Record<string, LucideIcon> = {
|
|
|
185
186
|
'bolt': Bolt,
|
|
186
187
|
'book': Book,
|
|
187
188
|
'bookmark': Bookmark,
|
|
189
|
+
'bot': Bot,
|
|
188
190
|
'box': Box,
|
|
189
191
|
'boxes': Boxes,
|
|
190
192
|
'briefcase': Briefcase,
|
|
@@ -189,7 +189,47 @@ describe('Alert primitive', () => {
|
|
|
189
189
|
</Alert>,
|
|
190
190
|
)
|
|
191
191
|
expect(screen.getByText('Saved').tagName).toBe('H5')
|
|
192
|
-
expect(screen.getByText('Your changes were saved.').tagName).toBe('
|
|
192
|
+
expect(screen.getByText('Your changes were saved.').tagName).toBe('DIV')
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
describe('AlertDescription accepts block-level children', () => {
|
|
196
|
+
it('renders a div so nested paragraphs stay valid HTML', () => {
|
|
197
|
+
const { container } = render(
|
|
198
|
+
<Alert status="information">
|
|
199
|
+
<AlertDescription>
|
|
200
|
+
<p>First paragraph.</p>
|
|
201
|
+
<p>Second paragraph.</p>
|
|
202
|
+
</AlertDescription>
|
|
203
|
+
</Alert>,
|
|
204
|
+
)
|
|
205
|
+
expect(screen.getByText('First paragraph.').parentElement?.tagName).toBe('DIV')
|
|
206
|
+
expect(container.querySelectorAll('p p')).toHaveLength(0)
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
it('keeps a nested list out of a paragraph', () => {
|
|
210
|
+
const { container } = render(
|
|
211
|
+
<Alert status="warning">
|
|
212
|
+
<AlertDescription>
|
|
213
|
+
<p>These records changed:</p>
|
|
214
|
+
<ul>
|
|
215
|
+
<li>r-1</li>
|
|
216
|
+
</ul>
|
|
217
|
+
</AlertDescription>
|
|
218
|
+
</Alert>,
|
|
219
|
+
)
|
|
220
|
+
expect(container.querySelectorAll('p ul')).toHaveLength(0)
|
|
221
|
+
expect(container.querySelectorAll('p p')).toHaveLength(0)
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it('forwards a ref to the rendered div', () => {
|
|
225
|
+
const ref = React.createRef<HTMLDivElement>()
|
|
226
|
+
render(
|
|
227
|
+
<Alert>
|
|
228
|
+
<AlertDescription ref={ref}>Body</AlertDescription>
|
|
229
|
+
</Alert>,
|
|
230
|
+
)
|
|
231
|
+
expect(ref.current).toBeInstanceOf(HTMLDivElement)
|
|
232
|
+
})
|
|
193
233
|
})
|
|
194
234
|
|
|
195
235
|
describe('legacy variant prop (BC)', () => {
|
|
@@ -26,7 +26,7 @@ describe('Notification primitive', () => {
|
|
|
26
26
|
it('renders title as AlertTitle and description below', () => {
|
|
27
27
|
render(<Notification title="John commented" description="On the latest deal" />)
|
|
28
28
|
expect(screen.getByText('John commented').tagName).toBe('H5')
|
|
29
|
-
expect(screen.getByText('On the latest deal').tagName).toBe('
|
|
29
|
+
expect(screen.getByText('On the latest deal').tagName).toBe('DIV')
|
|
30
30
|
})
|
|
31
31
|
|
|
32
32
|
it('description gets opacity-70 (DS opacity scale; was opacity-72 in early v5, snapped to scale per .ai/ds-rules.md)', () => {
|
package/src/primitives/alert.tsx
CHANGED
|
@@ -252,9 +252,12 @@ const AlertTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTM
|
|
|
252
252
|
AlertTitle.displayName = 'AlertTitle'
|
|
253
253
|
|
|
254
254
|
// Figma "Paragraph/Small": Inter Regular 14 / 20.
|
|
255
|
-
|
|
255
|
+
// Renders a <div>, not a <p>: callers routinely nest paragraphs, lists and other
|
|
256
|
+
// block elements inside the description, which is invalid inside <p> and makes
|
|
257
|
+
// React fail hydration with "In HTML, <p> cannot be a descendant of <p>".
|
|
258
|
+
const AlertDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
256
259
|
({ className, ...props }, ref) => (
|
|
257
|
-
<
|
|
260
|
+
<div ref={ref} className={cn('text-sm leading-5', className)} {...props} />
|
|
258
261
|
),
|
|
259
262
|
)
|
|
260
263
|
AlertDescription.displayName = 'AlertDescription'
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { formatCurrency, formatDate } from '../format'
|
|
2
2
|
|
|
3
|
+
// Every assertion below pins the locale explicitly (#5105). Without it these tests read the
|
|
4
|
+
// runtime's default locale, so they pass in en-US and fail on any contributor machine whose
|
|
5
|
+
// default is something else.
|
|
6
|
+
const EN = 'en-US'
|
|
7
|
+
const PL = 'pl-PL'
|
|
8
|
+
|
|
9
|
+
// A date-time literal without an offset is parsed as *local* time, so it lands on the same
|
|
10
|
+
// calendar day in every timezone and both the month and the day can be asserted exactly. The
|
|
11
|
+
// instant form cannot: midday UTC keeps the month stable but still shifts the day at the
|
|
12
|
+
// extremes (UTC+14 / UTC-12), which is why the assertions below no longer use one.
|
|
13
|
+
const LOCAL_MIDDAY = '2026-06-09T12:00:00'
|
|
14
|
+
|
|
3
15
|
describe('formatCurrency', () => {
|
|
4
16
|
it('returns null for empty input', () => {
|
|
5
17
|
expect(formatCurrency(null)).toBeNull()
|
|
@@ -16,21 +28,25 @@ describe('formatCurrency', () => {
|
|
|
16
28
|
})
|
|
17
29
|
|
|
18
30
|
it('formats a numeric value with an ISO currency code', () => {
|
|
19
|
-
|
|
20
|
-
expect(result).toMatch(/1,234\.5/)
|
|
21
|
-
expect(result).toMatch(/\$|USD/)
|
|
31
|
+
expect(formatCurrency(1234.5, 'usd', EN)).toBe('$1,234.50')
|
|
22
32
|
})
|
|
23
33
|
|
|
24
34
|
it('formats a numeric string the same as a number', () => {
|
|
25
|
-
expect(formatCurrency('1234.5', 'USD')).toBe(formatCurrency(1234.5, 'USD'))
|
|
35
|
+
expect(formatCurrency('1234.5', 'USD', EN)).toBe(formatCurrency(1234.5, 'USD', EN))
|
|
26
36
|
})
|
|
27
37
|
|
|
28
38
|
it('falls back to a plain number without a currency code', () => {
|
|
29
|
-
expect(formatCurrency(1000)).toBe(
|
|
39
|
+
expect(formatCurrency(1000, null, EN)).toBe('1,000')
|
|
30
40
|
})
|
|
31
41
|
|
|
32
42
|
it('ignores currency codes that are not three characters', () => {
|
|
33
|
-
expect(formatCurrency(1000, 'US')).toBe(
|
|
43
|
+
expect(formatCurrency(1000, 'US', EN)).toBe('1,000')
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('formats in the requested locale rather than the runtime default', () => {
|
|
47
|
+
expect(formatCurrency(1234.5, 'USD', PL)).toMatch(/^1234,50/)
|
|
48
|
+
expect(formatCurrency(1234.5, 'USD', PL)).toMatch(/USD/)
|
|
49
|
+
expect(formatCurrency(1000, null, PL)).toBe('1000')
|
|
34
50
|
})
|
|
35
51
|
})
|
|
36
52
|
|
|
@@ -46,13 +62,10 @@ describe('formatDate', () => {
|
|
|
46
62
|
})
|
|
47
63
|
|
|
48
64
|
it('formats a valid ISO date as a localized short date', () => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
day: 'numeric',
|
|
55
|
-
}),
|
|
56
|
-
)
|
|
65
|
+
expect(formatDate(LOCAL_MIDDAY, EN)).toBe('Jun 9, 2026')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('formats in the requested locale rather than the runtime default', () => {
|
|
69
|
+
expect(formatDate(LOCAL_MIDDAY, PL)).toBe('9 cze 2026')
|
|
57
70
|
})
|
|
58
71
|
})
|
package/src/utils/format.ts
CHANGED
|
@@ -4,10 +4,15 @@
|
|
|
4
4
|
* Returns `null` for empty input, the original string for non-numeric input,
|
|
5
5
|
* and falls back to a plain number format (optionally suffixed with the code)
|
|
6
6
|
* when the currency code is missing or rejected by `Intl.NumberFormat`.
|
|
7
|
+
*
|
|
8
|
+
* Pass `locale` to format in the application locale (`useLocale()` in client
|
|
9
|
+
* components). Omitting it keeps the runtime default, which varies per machine
|
|
10
|
+
* and is therefore not assertable in tests.
|
|
7
11
|
*/
|
|
8
12
|
export function formatCurrency(
|
|
9
13
|
value: string | number | null | undefined,
|
|
10
14
|
currency?: string | null,
|
|
15
|
+
locale?: string,
|
|
11
16
|
): string | null {
|
|
12
17
|
if (value === null || value === undefined || value === '') return null
|
|
13
18
|
const numeric = typeof value === 'number' ? value : Number(value)
|
|
@@ -17,12 +22,12 @@ export function formatCurrency(
|
|
|
17
22
|
const code = currency && currency.length === 3 ? currency.toUpperCase() : undefined
|
|
18
23
|
try {
|
|
19
24
|
if (code) {
|
|
20
|
-
return new Intl.NumberFormat(
|
|
25
|
+
return new Intl.NumberFormat(locale, { style: 'currency', currency: code }).format(numeric)
|
|
21
26
|
}
|
|
22
27
|
} catch {
|
|
23
28
|
// fall through to plain number formatting
|
|
24
29
|
}
|
|
25
|
-
const formatted = new Intl.NumberFormat().format(numeric)
|
|
30
|
+
const formatted = new Intl.NumberFormat(locale).format(numeric)
|
|
26
31
|
return code ? `${formatted} ${code}` : formatted
|
|
27
32
|
}
|
|
28
33
|
|
|
@@ -30,11 +35,11 @@ export function formatCurrency(
|
|
|
30
35
|
* Format an ISO date string as a localized short date (e.g. `Jun 9, 2026`).
|
|
31
36
|
*
|
|
32
37
|
* Returns `null` for empty input and echoes the original value back when it is
|
|
33
|
-
* not a parseable date.
|
|
38
|
+
* not a parseable date. Pass `locale` to format in the application locale.
|
|
34
39
|
*/
|
|
35
|
-
export function formatDate(value: string | null | undefined): string | null {
|
|
40
|
+
export function formatDate(value: string | null | undefined, locale?: string): string | null {
|
|
36
41
|
if (!value) return null
|
|
37
42
|
const date = new Date(value)
|
|
38
43
|
if (Number.isNaN(date.getTime())) return value
|
|
39
|
-
return date.toLocaleDateString(
|
|
44
|
+
return date.toLocaleDateString(locale, { year: 'numeric', month: 'short', day: 'numeric' })
|
|
40
45
|
}
|