@open-mercato/ui 0.7.0 → 0.7.1-develop.7103.1.41ff100d93
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/AppShell.js +129 -106
- package/dist/backend/AppShell.js.map +2 -2
- package/dist/backend/DataTable.js +20 -7
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/detail/NotesSection.js +26 -20
- package/dist/backend/detail/NotesSection.js.map +2 -2
- package/dist/backend/icons/lucideRegistry.generated.js +4 -0
- package/dist/backend/icons/lucideRegistry.generated.js.map +2 -2
- package/dist/backend/injection/useRegisteredComponent.js +24 -6
- package/dist/backend/injection/useRegisteredComponent.js.map +2 -2
- package/dist/backend/inputs/ComboboxInput.js +8 -4
- package/dist/backend/inputs/ComboboxInput.js.map +2 -2
- package/dist/backend/inputs/LookupSelect.js +8 -8
- package/dist/backend/inputs/LookupSelect.js.map +2 -2
- package/dist/backend/sidebar/SidebarCustomizationEditor.js +36 -30
- package/dist/backend/sidebar/SidebarCustomizationEditor.js.map +2 -2
- package/dist/backend/utils/crud.js.map +2 -2
- package/dist/primitives/alert.js +1 -1
- package/dist/primitives/alert.js.map +2 -2
- package/dist/primitives/icon-button.js +20 -1
- package/dist/primitives/icon-button.js.map +2 -2
- package/dist/primitives/pagination.js +14 -9
- package/dist/primitives/pagination.js.map +2 -2
- package/dist/primitives/rich-editor.js +14 -7
- package/dist/primitives/rich-editor.js.map +2 -2
- package/dist/utils/format.js +5 -5
- package/dist/utils/format.js.map +2 -2
- package/package.json +5 -4
- 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/AppShell.tsx +89 -58
- package/src/backend/DataTable.tsx +44 -10
- package/src/backend/__tests__/AppShell.test.tsx +162 -1
- package/src/backend/__tests__/DataTable.cappedPagination.test.tsx +122 -0
- package/src/backend/__tests__/DataTable.headerWrap.test.tsx +111 -0
- package/src/backend/__tests__/DataTable.render.test.tsx +37 -0
- package/src/backend/__tests__/NotesSection.test.tsx +89 -0
- package/src/backend/__tests__/useRegisteredComponent.identity.test.tsx +305 -0
- package/src/backend/detail/NotesSection.tsx +92 -67
- package/src/backend/icons/lucideRegistry.generated.tsx +4 -0
- package/src/backend/injection/useRegisteredComponent.tsx +49 -8
- package/src/backend/inputs/ComboboxInput.tsx +21 -5
- package/src/backend/inputs/LookupSelect.tsx +8 -8
- package/src/backend/inputs/__tests__/ComboboxInput.test.tsx +142 -0
- package/src/backend/inputs/__tests__/LookupSelect.test.tsx +75 -0
- package/src/backend/sidebar/SidebarCustomizationEditor.tsx +14 -11
- package/src/backend/utils/crud.ts +2 -0
- package/src/primitives/__tests__/alert.test.tsx +41 -1
- package/src/primitives/__tests__/icon-button.test.tsx +70 -0
- package/src/primitives/__tests__/notification.test.tsx +1 -1
- package/src/primitives/__tests__/pagination.test.tsx +54 -0
- package/src/primitives/__tests__/rich-editor.test.tsx +43 -0
- package/src/primitives/alert.tsx +5 -2
- package/src/primitives/icon-button.tsx +20 -1
- package/src/primitives/pagination.tsx +40 -9
- package/src/primitives/rich-editor.tsx +9 -4
- 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
|
}
|
package/dist/backend/AppShell.js
CHANGED
|
@@ -267,6 +267,12 @@ function resolveItemKey(item) {
|
|
|
267
267
|
if (candidate && candidate.length > 0) return candidate;
|
|
268
268
|
return item.href;
|
|
269
269
|
}
|
|
270
|
+
function sidebarBlockPadding(compact) {
|
|
271
|
+
return `${compact ? "px-2" : "px-3"} transition-[padding] duration-200 ease-out`;
|
|
272
|
+
}
|
|
273
|
+
function sidebarInjectionWrapper(compact) {
|
|
274
|
+
return `${sidebarBlockPadding(compact)} empty:hidden`;
|
|
275
|
+
}
|
|
270
276
|
function SerializedIcon({ markup }) {
|
|
271
277
|
return /* @__PURE__ */ jsx("span", { "aria-hidden": "true", dangerouslySetInnerHTML: { __html: markup } });
|
|
272
278
|
}
|
|
@@ -528,10 +534,6 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
528
534
|
const key = resolveGroupKey(activeGroup);
|
|
529
535
|
setOpenGroups((prev) => prev[key] === false ? { ...prev, [key]: true } : prev);
|
|
530
536
|
}, [pathname, navGroups]);
|
|
531
|
-
React.useEffect(() => {
|
|
532
|
-
setHeaderTitle(currentTitle);
|
|
533
|
-
setHeaderBreadcrumb(breadcrumb);
|
|
534
|
-
}, [currentTitle, breadcrumb]);
|
|
535
537
|
const prevPathname = React.useRef(pathname);
|
|
536
538
|
useIsomorphicLayoutEffect(() => {
|
|
537
539
|
if (pathname !== prevPathname.current) {
|
|
@@ -540,14 +542,19 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
540
542
|
setHeaderBreadcrumb(void 0);
|
|
541
543
|
}
|
|
542
544
|
}, [pathname]);
|
|
545
|
+
useIsomorphicLayoutEffect(() => {
|
|
546
|
+
setHeaderTitle(currentTitle);
|
|
547
|
+
setHeaderBreadcrumb(breadcrumb);
|
|
548
|
+
}, [currentTitle, breadcrumb]);
|
|
543
549
|
React.useEffect(() => {
|
|
544
550
|
setNavGroups(cloneSidebarGroups(resolvedGroups));
|
|
545
551
|
}, [resolvedGroups]);
|
|
546
552
|
function renderSectionSidebar(sections, title, compact, hideHeader, hideSearch) {
|
|
547
553
|
const sortedSections = [...sections].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
548
554
|
const lastVisibleIndex = sortedSections.length - 1;
|
|
555
|
+
const blockPadding = sidebarBlockPadding(compact);
|
|
549
556
|
return /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col gap-3", children: [
|
|
550
|
-
!hideHeader && /* @__PURE__ */ jsx("div", { className:
|
|
557
|
+
!hideHeader && /* @__PURE__ */ jsx("div", { className: `mb-2 ${blockPadding}`, children: /* @__PURE__ */ jsxs(
|
|
551
558
|
Link,
|
|
552
559
|
{
|
|
553
560
|
href: "/backend",
|
|
@@ -559,7 +566,7 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
559
566
|
]
|
|
560
567
|
}
|
|
561
568
|
) }),
|
|
562
|
-
!compact && !hideSearch && /* @__PURE__ */ jsx(
|
|
569
|
+
!compact && !hideSearch && /* @__PURE__ */ jsx("div", { className: blockPadding, children: /* @__PURE__ */ jsx(
|
|
563
570
|
SearchInput,
|
|
564
571
|
{
|
|
565
572
|
value: navQuery,
|
|
@@ -569,8 +576,8 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
569
576
|
clearLabel: t("appShell.searchNavClear", "Clear search"),
|
|
570
577
|
className: "mb-2"
|
|
571
578
|
}
|
|
572
|
-
),
|
|
573
|
-
/* @__PURE__ */ jsx("div", { "data-sidebar-scroll": "true", className:
|
|
579
|
+
) }),
|
|
580
|
+
/* @__PURE__ */ jsx("div", { "data-sidebar-scroll": "true", className: "flex flex-1 flex-col gap-3 overflow-x-hidden overflow-y-auto scrollbar-hide", children: /* @__PURE__ */ jsx("nav", { className: "flex flex-col gap-2", children: sortedSections.map((section, sectionIndex) => {
|
|
574
581
|
const sectionNavQueryActive = hideSearch ? false : navQueryActive;
|
|
575
582
|
const matchesItemQuery = (item) => {
|
|
576
583
|
if (!sectionNavQueryActive) return true;
|
|
@@ -627,29 +634,38 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
627
634
|
showChildren ? childItems.map((child) => renderSectionItem(child, depth + 1)) : null
|
|
628
635
|
] }, item.id);
|
|
629
636
|
};
|
|
630
|
-
return /* @__PURE__ */ jsxs(
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
{
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
637
|
+
return /* @__PURE__ */ jsxs(
|
|
638
|
+
"div",
|
|
639
|
+
{
|
|
640
|
+
className: `${blockPadding} ${sectionIndex !== lastVisibleIndex ? "border-b pb-2" : ""}`,
|
|
641
|
+
"data-sidebar-group": "true",
|
|
642
|
+
children: [
|
|
643
|
+
!compact && /* @__PURE__ */ jsxs(
|
|
644
|
+
Button,
|
|
645
|
+
{
|
|
646
|
+
variant: "muted",
|
|
647
|
+
onClick: () => toggleGroup(sectionKey),
|
|
648
|
+
className: "w-full px-1 justify-between flex text-xs font-medium uppercase tracking-wider text-muted-foreground/70 py-1",
|
|
649
|
+
"aria-expanded": open,
|
|
650
|
+
children: [
|
|
651
|
+
/* @__PURE__ */ jsx("span", { children: sectionLabel }),
|
|
652
|
+
/* @__PURE__ */ jsx(Chevron, { open })
|
|
653
|
+
]
|
|
654
|
+
}
|
|
655
|
+
),
|
|
656
|
+
(open || compact) && /* @__PURE__ */ jsx("div", { className: `flex flex-col ${compact ? "items-center" : ""} gap-1`, children: sortedItems.map((item) => renderSectionItem(item)) })
|
|
657
|
+
]
|
|
658
|
+
},
|
|
659
|
+
section.id
|
|
660
|
+
);
|
|
647
661
|
}) }) })
|
|
648
662
|
] });
|
|
649
663
|
}
|
|
650
664
|
function renderSidebar(compact, hideHeader, forceMainOnly) {
|
|
665
|
+
const blockPadding = sidebarBlockPadding(compact);
|
|
666
|
+
const injectionWrapper = sidebarInjectionWrapper(compact);
|
|
651
667
|
if (!isChromeReady && isChromeLoading) {
|
|
652
|
-
return /* @__PURE__ */ jsxs("div", { className:
|
|
668
|
+
return /* @__PURE__ */ jsxs("div", { className: `flex min-h-full flex-col gap-3 ${blockPadding}`, "data-testid": "backend-chrome-loading", children: [
|
|
653
669
|
!hideHeader ? /* @__PURE__ */ jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxs(
|
|
654
670
|
Link,
|
|
655
671
|
{
|
|
@@ -662,7 +678,7 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
662
678
|
]
|
|
663
679
|
}
|
|
664
680
|
) }) : null,
|
|
665
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-3
|
|
681
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-3", children: [
|
|
666
682
|
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
667
683
|
/* @__PURE__ */ jsx("div", { className: "h-8 rounded bg-muted/50" }),
|
|
668
684
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2 pl-1", children: [
|
|
@@ -710,7 +726,7 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
710
726
|
const isMobileVariant = !!hideHeader;
|
|
711
727
|
const shouldRenderSidebarInjectionSpots = !isMobileVariant;
|
|
712
728
|
return /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col gap-3", children: [
|
|
713
|
-
!hideHeader && /* @__PURE__ */ jsx("div", { className:
|
|
729
|
+
!hideHeader && /* @__PURE__ */ jsx("div", { className: `mb-2 ${blockPadding}`, children: /* @__PURE__ */ jsxs(
|
|
714
730
|
Link,
|
|
715
731
|
{
|
|
716
732
|
href: "/backend",
|
|
@@ -722,14 +738,14 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
722
738
|
]
|
|
723
739
|
}
|
|
724
740
|
) }),
|
|
725
|
-
shouldRenderSidebarInjectionSpots ? /* @__PURE__ */ jsx(
|
|
741
|
+
shouldRenderSidebarInjectionSpots ? /* @__PURE__ */ jsx("div", { className: injectionWrapper, "data-sidebar-injection-wrapper": "true", children: /* @__PURE__ */ jsx(
|
|
726
742
|
InjectionSpot,
|
|
727
743
|
{
|
|
728
744
|
spotId: BACKEND_SIDEBAR_TOP_INJECTION_SPOT_ID,
|
|
729
745
|
context: injectionContext
|
|
730
746
|
}
|
|
731
|
-
) : null,
|
|
732
|
-
!compact && /* @__PURE__ */ jsx(
|
|
747
|
+
) }) : null,
|
|
748
|
+
!compact && /* @__PURE__ */ jsx("div", { className: blockPadding, children: /* @__PURE__ */ jsx(
|
|
733
749
|
SearchInput,
|
|
734
750
|
{
|
|
735
751
|
value: navQuery,
|
|
@@ -739,8 +755,8 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
739
755
|
clearLabel: t("appShell.searchNavClear", "Clear search"),
|
|
740
756
|
className: "mb-2"
|
|
741
757
|
}
|
|
742
|
-
),
|
|
743
|
-
/* @__PURE__ */ jsx("div", { "data-sidebar-scroll": "true", className:
|
|
758
|
+
) }),
|
|
759
|
+
/* @__PURE__ */ jsx("div", { "data-sidebar-scroll": "true", className: "flex flex-1 flex-col gap-3 overflow-x-hidden overflow-y-auto scrollbar-hide", children: (() => {
|
|
744
760
|
const isSettingsPath = (href) => {
|
|
745
761
|
if (href === "/backend/settings") return true;
|
|
746
762
|
return resolvedSettingsPathPrefixes.some((prefix) => href.startsWith(prefix));
|
|
@@ -761,13 +777,13 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
761
777
|
return -1;
|
|
762
778
|
})();
|
|
763
779
|
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("nav", { className: "flex flex-col gap-2", "data-testid": "sidebar", children: [
|
|
764
|
-
shouldRenderSidebarInjectionSpots ? /* @__PURE__ */ jsx(
|
|
780
|
+
shouldRenderSidebarInjectionSpots ? /* @__PURE__ */ jsx("div", { className: injectionWrapper, "data-sidebar-injection-wrapper": "true", children: /* @__PURE__ */ jsx(
|
|
765
781
|
InjectionSpot,
|
|
766
782
|
{
|
|
767
783
|
spotId: BACKEND_SIDEBAR_NAV_INJECTION_SPOT_ID,
|
|
768
784
|
context: injectionContext
|
|
769
785
|
}
|
|
770
|
-
) : null,
|
|
786
|
+
) }) : null,
|
|
771
787
|
mainGroups.map((g, gi) => {
|
|
772
788
|
const groupId = resolveGroupKey(g);
|
|
773
789
|
const open = navQueryActive ? true : openGroups[groupId] !== false;
|
|
@@ -779,87 +795,94 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
779
795
|
return itemChildren.some((c) => matchesQuery(c.title));
|
|
780
796
|
});
|
|
781
797
|
if (visibleItems.length === 0) return null;
|
|
782
|
-
return /* @__PURE__ */ jsxs(
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
{
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
children: [
|
|
791
|
-
/* @__PURE__ */ jsx("span", { children: g.name }),
|
|
792
|
-
/* @__PURE__ */ jsx(Chevron, { open })
|
|
793
|
-
]
|
|
794
|
-
}
|
|
795
|
-
),
|
|
796
|
-
(open || compact) && /* @__PURE__ */ jsx("div", { className: `flex flex-col ${compact ? "items-center" : ""} gap-1`, children: visibleItems.map((i) => {
|
|
797
|
-
const allChildItems = (i.children ?? []).filter((child) => child.hidden !== true);
|
|
798
|
-
const matchingChildItems = navQueryActive ? allChildItems.filter((c) => matchesQuery(c.title)) : allChildItems;
|
|
799
|
-
const childItems = navQueryActive ? matchingChildItems : allChildItems;
|
|
800
|
-
const showChildren = navQueryActive ? matchingChildItems.length > 0 : !!pathname && allChildItems.length > 0 && pathname.startsWith(i.href);
|
|
801
|
-
const hasActiveChild = !!(pathname && allChildItems.some((c) => pathname.startsWith(c.href)));
|
|
802
|
-
const isParentActive = pathname === i.href || !navQueryActive && showChildren && !hasActiveChild;
|
|
803
|
-
const base = compact ? "w-10 h-10 justify-center" : "w-full px-3 py-2 gap-2";
|
|
804
|
-
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
805
|
-
/* @__PURE__ */ jsxs(
|
|
806
|
-
Link,
|
|
798
|
+
return /* @__PURE__ */ jsxs(
|
|
799
|
+
"div",
|
|
800
|
+
{
|
|
801
|
+
className: `${blockPadding} ${gi !== mainLastVisibleGroupIndex ? "border-b pb-2" : ""}`,
|
|
802
|
+
"data-sidebar-group": "true",
|
|
803
|
+
children: [
|
|
804
|
+
!compact && /* @__PURE__ */ jsxs(
|
|
805
|
+
Button,
|
|
807
806
|
{
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
"
|
|
811
|
-
|
|
812
|
-
"data-menu-item-id": i.id ?? i.href,
|
|
813
|
-
onClick: () => setMobileOpen(false),
|
|
807
|
+
variant: "muted",
|
|
808
|
+
onClick: () => toggleGroup(groupId),
|
|
809
|
+
className: "w-full px-1 justify-between flex text-xs font-medium uppercase tracking-wider text-muted-foreground/70 py-1",
|
|
810
|
+
"aria-expanded": open,
|
|
814
811
|
children: [
|
|
815
|
-
|
|
816
|
-
/* @__PURE__ */ jsx(
|
|
817
|
-
i.icon,
|
|
818
|
-
i.iconName,
|
|
819
|
-
i.iconMarkup,
|
|
820
|
-
DefaultIcon
|
|
821
|
-
) }),
|
|
822
|
-
!compact && /* @__PURE__ */ jsx("span", { children: i.title })
|
|
812
|
+
/* @__PURE__ */ jsx("span", { children: g.name }),
|
|
813
|
+
/* @__PURE__ */ jsx(Chevron, { open })
|
|
823
814
|
]
|
|
824
815
|
}
|
|
825
816
|
),
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
817
|
+
(open || compact) && /* @__PURE__ */ jsx("div", { className: `flex flex-col ${compact ? "items-center" : ""} gap-1`, children: visibleItems.map((i) => {
|
|
818
|
+
const allChildItems = (i.children ?? []).filter((child) => child.hidden !== true);
|
|
819
|
+
const matchingChildItems = navQueryActive ? allChildItems.filter((c) => matchesQuery(c.title)) : allChildItems;
|
|
820
|
+
const childItems = navQueryActive ? matchingChildItems : allChildItems;
|
|
821
|
+
const showChildren = navQueryActive ? matchingChildItems.length > 0 : !!pathname && allChildItems.length > 0 && pathname.startsWith(i.href);
|
|
822
|
+
const hasActiveChild = !!(pathname && allChildItems.some((c) => pathname.startsWith(c.href)));
|
|
823
|
+
const isParentActive = pathname === i.href || !navQueryActive && showChildren && !hasActiveChild;
|
|
824
|
+
const base = compact ? "w-10 h-10 justify-center" : "w-full px-3 py-2 gap-2";
|
|
825
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
826
|
+
/* @__PURE__ */ jsxs(
|
|
832
827
|
Link,
|
|
833
828
|
{
|
|
834
|
-
href:
|
|
835
|
-
className: `relative text-sm font-medium rounded-lg inline-flex items-center ${
|
|
836
|
-
"aria-disabled":
|
|
837
|
-
title: compact ?
|
|
838
|
-
"data-menu-item-id":
|
|
829
|
+
href: i.href,
|
|
830
|
+
className: `relative text-sm font-medium rounded-lg inline-flex items-center ${base} ${isParentActive ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted"} ${i.enabled === false ? "pointer-events-none opacity-50" : ""}`,
|
|
831
|
+
"aria-disabled": i.enabled === false,
|
|
832
|
+
title: compact ? i.title : void 0,
|
|
833
|
+
"data-menu-item-id": i.id ?? i.href,
|
|
839
834
|
onClick: () => setMobileOpen(false),
|
|
840
835
|
children: [
|
|
841
|
-
|
|
836
|
+
isParentActive ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: `absolute ${compact ? "left-[-20px]" : "left-[-12px]"} top-2 w-1 h-5 rounded-r bg-foreground` }) : null,
|
|
842
837
|
/* @__PURE__ */ jsx("span", { className: "flex items-center justify-center shrink-0", children: renderIcon(
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
838
|
+
i.icon,
|
|
839
|
+
i.iconName,
|
|
840
|
+
i.iconMarkup,
|
|
841
|
+
DefaultIcon
|
|
847
842
|
) }),
|
|
848
|
-
!compact && /* @__PURE__ */ jsx("span", { children:
|
|
843
|
+
!compact && /* @__PURE__ */ jsx("span", { children: i.title })
|
|
849
844
|
]
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
845
|
+
}
|
|
846
|
+
),
|
|
847
|
+
showChildren ? /* @__PURE__ */ jsxs("div", { className: `relative flex flex-col ${compact ? "items-center" : ""} gap-1`, children: [
|
|
848
|
+
!compact && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "pointer-events-none absolute left-1.5 top-1 bottom-1 w-px bg-border" }),
|
|
849
|
+
childItems.map((c) => {
|
|
850
|
+
const childActive = pathname?.startsWith(c.href);
|
|
851
|
+
const childBase = compact ? "w-10 h-8 justify-center" : "w-full pl-5 pr-3 py-2 gap-2";
|
|
852
|
+
return /* @__PURE__ */ jsxs(
|
|
853
|
+
Link,
|
|
854
|
+
{
|
|
855
|
+
href: c.href,
|
|
856
|
+
className: `relative text-sm font-medium rounded-lg inline-flex items-center ${childBase} ${childActive ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted"} ${c.enabled === false ? "pointer-events-none opacity-50" : ""}`,
|
|
857
|
+
"aria-disabled": c.enabled === false,
|
|
858
|
+
title: compact ? c.title : void 0,
|
|
859
|
+
"data-menu-item-id": c.id ?? c.href,
|
|
860
|
+
onClick: () => setMobileOpen(false),
|
|
861
|
+
children: [
|
|
862
|
+
childActive ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: `absolute ${compact ? "left-[-20px]" : "left-[-12px]"} top-2 w-1 h-5 rounded-r bg-foreground` }) : null,
|
|
863
|
+
/* @__PURE__ */ jsx("span", { className: "flex items-center justify-center shrink-0", children: renderIcon(
|
|
864
|
+
c.icon,
|
|
865
|
+
c.iconName,
|
|
866
|
+
c.iconMarkup,
|
|
867
|
+
c.href.includes("/backend/entities/user/") && c.href.endsWith("/records") ? DataTableIcon : DefaultIcon
|
|
868
|
+
) }),
|
|
869
|
+
!compact && /* @__PURE__ */ jsx("span", { children: c.title })
|
|
870
|
+
]
|
|
871
|
+
},
|
|
872
|
+
c.href
|
|
873
|
+
);
|
|
874
|
+
})
|
|
875
|
+
] }) : null
|
|
876
|
+
] }, i.href);
|
|
877
|
+
}) })
|
|
878
|
+
]
|
|
879
|
+
},
|
|
880
|
+
groupId
|
|
881
|
+
);
|
|
859
882
|
})
|
|
860
883
|
] }) });
|
|
861
884
|
})() }),
|
|
862
|
-
/* @__PURE__ */ jsxs("div", { className:
|
|
885
|
+
/* @__PURE__ */ jsxs("div", { className: `sticky bottom-0 bg-background pb-1 ${injectionWrapper}`, "data-sidebar-injection-wrapper": "true", children: [
|
|
863
886
|
shouldRenderSidebarInjectionSpots ? /* @__PURE__ */ jsx(
|
|
864
887
|
InjectionSpot,
|
|
865
888
|
{
|
|
@@ -904,7 +927,7 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
904
927
|
}
|
|
905
928
|
if (!sections) return null;
|
|
906
929
|
return /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col gap-2", children: [
|
|
907
|
-
/* @__PURE__ */ jsxs(
|
|
930
|
+
/* @__PURE__ */ jsx("div", { className: sidebarBlockPadding(false), children: /* @__PURE__ */ jsxs(
|
|
908
931
|
Link,
|
|
909
932
|
{
|
|
910
933
|
href: "/backend",
|
|
@@ -916,7 +939,7 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
916
939
|
/* @__PURE__ */ jsx("span", { className: "truncate", children: title })
|
|
917
940
|
]
|
|
918
941
|
}
|
|
919
|
-
),
|
|
942
|
+
) }),
|
|
920
943
|
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1", children: renderSectionSidebar(sections, title, false, true, true) })
|
|
921
944
|
] });
|
|
922
945
|
}
|
|
@@ -974,7 +997,7 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
974
997
|
children: effectiveCollapsed ? /* @__PURE__ */ jsx(PanelLeftOpen, { className: "size-4" }) : /* @__PURE__ */ jsx(PanelLeftClose, { className: "size-4" })
|
|
975
998
|
}
|
|
976
999
|
),
|
|
977
|
-
/* @__PURE__ */ jsxs("aside", { ref: sidebarAsideRef, className: `${asideClassesBase}
|
|
1000
|
+
/* @__PURE__ */ jsxs("aside", { ref: sidebarAsideRef, className: `${asideClassesBase} px-0 hidden lg:block lg:sticky lg:top-0 lg:h-svh lg:self-start lg:overflow-hidden lg:relative transition-[width] duration-200 ease-out`, style: { width: asideWidth }, children: [
|
|
978
1001
|
renderSidebar(effectiveCollapsed, false, isSectionView),
|
|
979
1002
|
sidebarScrollState !== "none" ? /* @__PURE__ */ jsx(
|
|
980
1003
|
"div",
|
|
@@ -1006,7 +1029,7 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
1006
1029
|
isSectionView ? /* @__PURE__ */ jsxs(
|
|
1007
1030
|
"aside",
|
|
1008
1031
|
{
|
|
1009
|
-
className: `${asideClassesBase} px-
|
|
1032
|
+
className: `${asideClassesBase} px-0 hidden lg:block lg:sticky lg:top-0 lg:h-svh lg:self-start lg:overflow-hidden lg:relative`,
|
|
1010
1033
|
style: { width: "240px" },
|
|
1011
1034
|
"data-testid": "appshell-section-sidebar",
|
|
1012
1035
|
children: [
|
|
@@ -1166,7 +1189,7 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
1166
1189
|
id: "mobile-drawer-tabpanel",
|
|
1167
1190
|
role: sidebarMode !== "main" ? "tabpanel" : void 0,
|
|
1168
1191
|
"aria-labelledby": sidebarMode !== "main" ? `mobile-drawer-tab-${mobileDrawerView === "main" ? "main" : "section"}` : void 0,
|
|
1169
|
-
className: "min-h-0 flex-1 overflow-y-auto overflow-x-hidden
|
|
1192
|
+
className: "min-h-0 flex-1 overflow-y-auto overflow-x-hidden py-3",
|
|
1170
1193
|
children: renderSidebar(false, true, mobileDrawerView === "main")
|
|
1171
1194
|
}
|
|
1172
1195
|
)
|