@open-mercato/core 0.6.2-develop.3372.1.de9a169973 → 0.6.2-develop.3373.1.be847b1058

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.
@@ -1,9 +1,10 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- import { ShoppingCart, ExternalLink, DollarSign, User, Calendar } from "lucide-react";
4
+ import { ShoppingCart, ExternalLink, Loader2, X } from "lucide-react";
5
5
  import { useRouter } from "next/navigation";
6
6
  import { Button } from "@open-mercato/ui/primitives/button";
7
+ import { IconButton } from "@open-mercato/ui/primitives/icon-button";
7
8
  import { cn } from "@open-mercato/shared/lib/utils";
8
9
  import { formatRelativeTime } from "@open-mercato/shared/lib/time";
9
10
  import { useT } from "@open-mercato/shared/lib/i18n/context";
@@ -44,12 +45,13 @@ function SalesOrderCreatedRenderer({
44
45
  setExecuting(false);
45
46
  }
46
47
  };
48
+ const timeAgo = formatRelativeTime(notification.createdAt, { translate: t }) ?? "";
47
49
  return /* @__PURE__ */ jsxs(
48
50
  "div",
49
51
  {
50
52
  className: cn(
51
- "group relative px-4 py-3 hover:bg-muted/50 cursor-pointer transition-colors border-l-4 border-l-status-info-border",
52
- isUnread && "bg-status-info-bg/50"
53
+ "group relative flex gap-4 items-start rounded-xl p-3 transition-colors hover:bg-muted/40 cursor-pointer",
54
+ isUnread && "bg-muted/20"
53
55
  ),
54
56
  onClick: handleView,
55
57
  onKeyDown: (e) => {
@@ -61,66 +63,86 @@ function SalesOrderCreatedRenderer({
61
63
  role: "button",
62
64
  tabIndex: 0,
63
65
  children: [
64
- isUnread && /* @__PURE__ */ jsx("div", { className: "absolute left-1.5 top-1/2 -translate-y-1/2 h-2 w-2 rounded-full bg-primary" }),
65
- /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
66
- /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 mt-0.5", children: /* @__PURE__ */ jsx("div", { className: "h-10 w-10 rounded-lg bg-status-info-bg flex items-center justify-center", children: /* @__PURE__ */ jsx(ShoppingCart, { className: "h-5 w-5 text-status-info-icon" }) }) }),
67
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
68
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
69
- /* @__PURE__ */ jsxs("div", { children: [
70
- /* @__PURE__ */ jsx("h4", { className: cn("text-sm font-medium", isUnread && "font-semibold"), children: notification.title }),
71
- orderNumber && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 mt-0.5", children: /* @__PURE__ */ jsxs("span", { className: "text-xs font-mono text-muted-foreground bg-muted px-1.5 py-0.5 rounded", children: [
72
- "#",
73
- orderNumber
74
- ] }) })
66
+ /* @__PURE__ */ jsxs("div", { className: "relative shrink-0 flex size-10 items-center justify-center rounded-full bg-status-info-bg", children: [
67
+ /* @__PURE__ */ jsx(ShoppingCart, { className: "size-5 text-status-info-icon", "aria-hidden": "true" }),
68
+ isUnread ? /* @__PURE__ */ jsx(
69
+ "span",
70
+ {
71
+ className: "absolute -right-1 -top-1 size-3 rounded-full bg-accent-indigo ring-2 ring-background",
72
+ "aria-hidden": "true"
73
+ }
74
+ ) : null
75
+ ] }),
76
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [
77
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium leading-5 tracking-tight text-foreground", children: notification.title }),
78
+ /* @__PURE__ */ jsxs("div", { className: "text-xs leading-4 text-muted-foreground", children: [
79
+ timeAgo ? /* @__PURE__ */ jsxs(Fragment, { children: [
80
+ /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: timeAgo }),
81
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "mx-1 text-text-disabled", children: "\xB7" })
82
+ ] }) : null,
83
+ orderNumber ? /* @__PURE__ */ jsxs(Fragment, { children: [
84
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center rounded bg-muted px-1.5 py-0.5 font-mono text-overline text-foreground", children: [
85
+ "#",
86
+ orderNumber
75
87
  ] }),
76
- /* @__PURE__ */ jsxs("span", { className: "flex-shrink-0 text-xs text-muted-foreground flex items-center gap-1", children: [
77
- /* @__PURE__ */ jsx(Calendar, { className: "h-3 w-3" }),
78
- formatRelativeTime(notification.createdAt, { translate: t }) ?? ""
79
- ] })
80
- ] }),
81
- /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center gap-4 text-xs text-muted-foreground", children: [
82
- currentTotal && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
83
- /* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }),
84
- /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: currentTotal })
85
- ] }),
86
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
87
- /* @__PURE__ */ jsx(User, { className: "h-3 w-3" }),
88
- /* @__PURE__ */ jsx("span", { children: t("sales.notifications.renderer.assignedToYou", "Assigned to you") })
89
- ] })
90
- ] }),
91
- /* @__PURE__ */ jsxs("div", { className: "mt-3 flex gap-2", children: [
92
- /* @__PURE__ */ jsxs(
93
- Button,
94
- {
95
- variant: "default",
96
- size: "sm",
97
- onClick: (e) => {
98
- e.stopPropagation();
99
- handleView();
100
- },
101
- disabled: executing || !viewAction && !notification.linkHref,
102
- className: "gap-1",
103
- children: [
104
- /* @__PURE__ */ jsx(ExternalLink, { className: "h-3 w-3" }),
105
- t("sales.notifications.renderer.viewOrder", "View Order")
106
- ]
107
- }
108
- ),
109
- /* @__PURE__ */ jsx(
110
- Button,
111
- {
112
- variant: "ghost",
113
- size: "sm",
114
- onClick: (e) => {
115
- e.stopPropagation();
116
- onDismiss();
117
- },
118
- children: t("notifications.actions.dismiss", "Dismiss")
119
- }
120
- )
121
- ] })
88
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "mx-1 text-text-disabled", children: "\xB7" })
89
+ ] }) : null,
90
+ currentTotal ? /* @__PURE__ */ jsxs(Fragment, { children: [
91
+ /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap font-medium text-foreground", children: currentTotal }),
92
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "mx-1 text-text-disabled", children: "\xB7" })
93
+ ] }) : null,
94
+ /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: t("sales.notifications.renderer.assignedToYou", "Assigned to you") })
95
+ ] }),
96
+ /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center gap-2", children: [
97
+ /* @__PURE__ */ jsxs(
98
+ Button,
99
+ {
100
+ type: "button",
101
+ size: "sm",
102
+ className: "h-8 rounded-md px-2.5 bg-accent-indigo text-accent-indigo-foreground hover:bg-accent-indigo/90",
103
+ onClick: (e) => {
104
+ e.stopPropagation();
105
+ handleView();
106
+ },
107
+ disabled: executing || !viewAction && !notification.linkHref,
108
+ children: [
109
+ /* @__PURE__ */ jsx(ExternalLink, { className: "size-3.5", "aria-hidden": "true" }),
110
+ t("sales.notifications.renderer.viewOrder", "View Order"),
111
+ executing ? /* @__PURE__ */ jsx(Loader2, { className: "ml-1 size-3 animate-spin" }) : null
112
+ ]
113
+ }
114
+ ),
115
+ /* @__PURE__ */ jsx(
116
+ Button,
117
+ {
118
+ type: "button",
119
+ variant: "outline",
120
+ size: "sm",
121
+ className: "h-8 rounded-md px-2.5",
122
+ onClick: (e) => {
123
+ e.stopPropagation();
124
+ onDismiss();
125
+ },
126
+ children: t("notifications.actions.dismiss", "Dismiss")
127
+ }
128
+ )
122
129
  ] })
123
- ] })
130
+ ] }),
131
+ /* @__PURE__ */ jsx(
132
+ IconButton,
133
+ {
134
+ type: "button",
135
+ variant: "ghost",
136
+ size: "xs",
137
+ className: "absolute right-2 top-2 opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100",
138
+ onClick: (e) => {
139
+ e.stopPropagation();
140
+ onDismiss();
141
+ },
142
+ "aria-label": t("notifications.actions.dismiss", "Dismiss"),
143
+ children: /* @__PURE__ */ jsx(X, { className: "size-3.5" })
144
+ }
145
+ )
124
146
  ]
125
147
  }
126
148
  );
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { ShoppingCart, ExternalLink, DollarSign, User, Calendar } from 'lucide-react'\nimport { useRouter } from 'next/navigation'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { formatRelativeTime } from '@open-mercato/shared/lib/time'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { NotificationRendererProps } from '@open-mercato/shared/modules/notifications/types'\nimport { formatMoney } from '../../components/documents/lineItemUtils'\nimport { useSalesDocumentTotals } from './useSalesDocumentTotals'\n\nfunction normalizeTotal(value?: string | null): string | null {\n if (!value) return null\n let trimmed = value.trim()\n if (trimmed.startsWith('(') && trimmed.endsWith(')')) {\n trimmed = trimmed.slice(1, -1).trim()\n }\n return trimmed.length ? trimmed : null\n}\n\nexport function SalesOrderCreatedRenderer({\n notification,\n onAction,\n onDismiss,\n actions = [],\n}: NotificationRendererProps) {\n const t = useT()\n const router = useRouter()\n const [executing, setExecuting] = React.useState(false)\n const isUnread = notification.status === 'unread'\n const orderNumber = notification.bodyVariables?.orderNumber ?? notification.titleVariables?.orderNumber\n const fallbackTotal =\n normalizeTotal(notification.bodyVariables?.totalAmount ?? null) ??\n normalizeTotal(notification.bodyVariables?.total ?? null)\n const { totals } = useSalesDocumentTotals('order', notification.sourceEntityId)\n\n const currentTotal =\n totals && typeof totals.grandTotalGrossAmount === 'number'\n ? formatMoney(totals.grandTotalGrossAmount, totals.currencyCode)\n : fallbackTotal\n\n const viewAction = actions.find((action) => action.id === 'view') ?? actions[0] ?? null\n\n const handleView = async () => {\n if (!viewAction) {\n if (notification.linkHref) router.push(notification.linkHref)\n return\n }\n setExecuting(true)\n try {\n await onAction(viewAction.id)\n } finally {\n setExecuting(false)\n }\n }\n\n return (\n <div\n className={cn(\n 'group relative px-4 py-3 hover:bg-muted/50 cursor-pointer transition-colors border-l-4 border-l-status-info-border',\n isUnread && 'bg-status-info-bg/50'\n )}\n onClick={handleView}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleView()\n }\n }}\n role=\"button\"\n tabIndex={0}\n >\n {isUnread && (\n <div className=\"absolute left-1.5 top-1/2 -translate-y-1/2 h-2 w-2 rounded-full bg-primary\" />\n )}\n\n <div className=\"flex gap-3\">\n <div className=\"flex-shrink-0 mt-0.5\">\n <div className=\"h-10 w-10 rounded-lg bg-status-info-bg flex items-center justify-center\">\n <ShoppingCart className=\"h-5 w-5 text-status-info-icon\" />\n </div>\n </div>\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-start justify-between gap-2\">\n <div>\n <h4 className={cn('text-sm font-medium', isUnread && 'font-semibold')}>\n {notification.title}\n </h4>\n {orderNumber && (\n <div className=\"flex items-center gap-1 mt-0.5\">\n <span className=\"text-xs font-mono text-muted-foreground bg-muted px-1.5 py-0.5 rounded\">\n #{orderNumber}\n </span>\n </div>\n )}\n </div>\n <span className=\"flex-shrink-0 text-xs text-muted-foreground flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n {formatRelativeTime(notification.createdAt, { translate: t }) ?? ''}\n </span>\n </div>\n\n <div className=\"mt-2 flex items-center gap-4 text-xs text-muted-foreground\">\n {currentTotal && (\n <div className=\"flex items-center gap-1\">\n <DollarSign className=\"h-3 w-3\" />\n <span className=\"font-medium text-foreground\">{currentTotal}</span>\n </div>\n )}\n <div className=\"flex items-center gap-1\">\n <User className=\"h-3 w-3\" />\n <span>{t('sales.notifications.renderer.assignedToYou', 'Assigned to you')}</span>\n </div>\n </div>\n\n <div className=\"mt-3 flex gap-2\">\n <Button\n variant=\"default\"\n size=\"sm\"\n onClick={(e) => {\n e.stopPropagation()\n handleView()\n }}\n disabled={executing || (!viewAction && !notification.linkHref)}\n className=\"gap-1\"\n >\n <ExternalLink className=\"h-3 w-3\" />\n {t('sales.notifications.renderer.viewOrder', 'View Order')}\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => {\n e.stopPropagation()\n onDismiss()\n }}\n >\n {t('notifications.actions.dismiss', 'Dismiss')}\n </Button>\n </div>\n </div>\n </div>\n </div>\n )\n}\n\nexport default SalesOrderCreatedRenderer\n"],
5
- "mappings": ";AA2EQ,cAkBU,YAlBV;AAzER,YAAY,WAAW;AACvB,SAAS,cAAc,cAAc,YAAY,MAAM,gBAAgB;AACvE,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,UAAU;AACnB,SAAS,0BAA0B;AACnC,SAAS,YAAY;AAErB,SAAS,mBAAmB;AAC5B,SAAS,8BAA8B;AAEvC,SAAS,eAAe,OAAsC;AAC5D,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,UAAU,MAAM,KAAK;AACzB,MAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG,GAAG;AACpD,cAAU,QAAQ,MAAM,GAAG,EAAE,EAAE,KAAK;AAAA,EACtC;AACA,SAAO,QAAQ,SAAS,UAAU;AACpC;AAEO,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU,CAAC;AACb,GAA8B;AAC5B,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAS,KAAK;AACtD,QAAM,WAAW,aAAa,WAAW;AACzC,QAAM,cAAc,aAAa,eAAe,eAAe,aAAa,gBAAgB;AAC5F,QAAM,gBACJ,eAAe,aAAa,eAAe,eAAe,IAAI,KAC9D,eAAe,aAAa,eAAe,SAAS,IAAI;AAC1D,QAAM,EAAE,OAAO,IAAI,uBAAuB,SAAS,aAAa,cAAc;AAE9E,QAAM,eACJ,UAAU,OAAO,OAAO,0BAA0B,WAC9C,YAAY,OAAO,uBAAuB,OAAO,YAAY,IAC7D;AAEN,QAAM,aAAa,QAAQ,KAAK,CAAC,WAAW,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,KAAK;AAEnF,QAAM,aAAa,YAAY;AAC7B,QAAI,CAAC,YAAY;AACf,UAAI,aAAa,SAAU,QAAO,KAAK,aAAa,QAAQ;AAC5D;AAAA,IACF;AACA,iBAAa,IAAI;AACjB,QAAI;AACF,YAAM,SAAS,WAAW,EAAE;AAAA,IAC9B,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,YAAY;AAAA,MACd;AAAA,MACA,SAAS;AAAA,MACT,WAAW,CAAC,MAAM;AAChB,YAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AACtC,YAAE,eAAe;AACjB,qBAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,MAAK;AAAA,MACL,UAAU;AAAA,MAET;AAAA,oBACC,oBAAC,SAAI,WAAU,8EAA6E;AAAA,QAG9F,qBAAC,SAAI,WAAU,cACb;AAAA,8BAAC,SAAI,WAAU,wBACb,8BAAC,SAAI,WAAU,2EACb,8BAAC,gBAAa,WAAU,iCAAgC,GAC1D,GACF;AAAA,UAEA,qBAAC,SAAI,WAAU,kBACb;AAAA,iCAAC,SAAI,WAAU,0CACb;AAAA,mCAAC,SACC;AAAA,oCAAC,QAAG,WAAW,GAAG,uBAAuB,YAAY,eAAe,GACjE,uBAAa,OAChB;AAAA,gBACC,eACC,oBAAC,SAAI,WAAU,kCACb,+BAAC,UAAK,WAAU,0EAAyE;AAAA;AAAA,kBACrF;AAAA,mBACJ,GACF;AAAA,iBAEJ;AAAA,cACA,qBAAC,UAAK,WAAU,uEACd;AAAA,oCAAC,YAAS,WAAU,WAAU;AAAA,gBAC7B,mBAAmB,aAAa,WAAW,EAAE,WAAW,EAAE,CAAC,KAAK;AAAA,iBACnE;AAAA,eACF;AAAA,YAEA,qBAAC,SAAI,WAAU,8DACZ;AAAA,8BACC,qBAAC,SAAI,WAAU,2BACb;AAAA,oCAAC,cAAW,WAAU,WAAU;AAAA,gBAChC,oBAAC,UAAK,WAAU,+BAA+B,wBAAa;AAAA,iBAC9D;AAAA,cAEF,qBAAC,SAAI,WAAU,2BACb;AAAA,oCAAC,QAAK,WAAU,WAAU;AAAA,gBAC1B,oBAAC,UAAM,YAAE,8CAA8C,iBAAiB,GAAE;AAAA,iBAC5E;AAAA,eACF;AAAA,YAEA,qBAAC,SAAI,WAAU,mBACb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,SAAS,CAAC,MAAM;AACd,sBAAE,gBAAgB;AAClB,+BAAW;AAAA,kBACb;AAAA,kBACA,UAAU,aAAc,CAAC,cAAc,CAAC,aAAa;AAAA,kBACrD,WAAU;AAAA,kBAEV;AAAA,wCAAC,gBAAa,WAAU,WAAU;AAAA,oBACjC,EAAE,0CAA0C,YAAY;AAAA;AAAA;AAAA,cAC3D;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,SAAS,CAAC,MAAM;AACd,sBAAE,gBAAgB;AAClB,8BAAU;AAAA,kBACZ;AAAA,kBAEC,YAAE,iCAAiC,SAAS;AAAA;AAAA,cAC/C;AAAA,eACF;AAAA,aACF;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,oCAAQ;",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { ShoppingCart, ExternalLink, Loader2, X } from 'lucide-react'\nimport { useRouter } from 'next/navigation'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { formatRelativeTime } from '@open-mercato/shared/lib/time'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { NotificationRendererProps } from '@open-mercato/shared/modules/notifications/types'\nimport { formatMoney } from '../../components/documents/lineItemUtils'\nimport { useSalesDocumentTotals } from './useSalesDocumentTotals'\n\nfunction normalizeTotal(value?: string | null): string | null {\n if (!value) return null\n let trimmed = value.trim()\n if (trimmed.startsWith('(') && trimmed.endsWith(')')) {\n trimmed = trimmed.slice(1, -1).trim()\n }\n return trimmed.length ? trimmed : null\n}\n\nexport function SalesOrderCreatedRenderer({\n notification,\n onAction,\n onDismiss,\n actions = [],\n}: NotificationRendererProps) {\n const t = useT()\n const router = useRouter()\n const [executing, setExecuting] = React.useState(false)\n const isUnread = notification.status === 'unread'\n const orderNumber = notification.bodyVariables?.orderNumber ?? notification.titleVariables?.orderNumber\n const fallbackTotal =\n normalizeTotal(notification.bodyVariables?.totalAmount ?? null) ??\n normalizeTotal(notification.bodyVariables?.total ?? null)\n const { totals } = useSalesDocumentTotals('order', notification.sourceEntityId)\n\n const currentTotal =\n totals && typeof totals.grandTotalGrossAmount === 'number'\n ? formatMoney(totals.grandTotalGrossAmount, totals.currencyCode)\n : fallbackTotal\n\n const viewAction = actions.find((action) => action.id === 'view') ?? actions[0] ?? null\n\n const handleView = async () => {\n if (!viewAction) {\n if (notification.linkHref) router.push(notification.linkHref)\n return\n }\n setExecuting(true)\n try {\n await onAction(viewAction.id)\n } finally {\n setExecuting(false)\n }\n }\n\n const timeAgo = formatRelativeTime(notification.createdAt, { translate: t }) ?? ''\n\n return (\n <div\n className={cn(\n 'group relative flex gap-4 items-start rounded-xl p-3 transition-colors hover:bg-muted/40 cursor-pointer',\n isUnread && 'bg-muted/20',\n )}\n onClick={handleView}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleView()\n }\n }}\n role=\"button\"\n tabIndex={0}\n >\n {/* Avatar: indigo-tinted circle with shopping-cart icon */}\n <div className=\"relative shrink-0 flex size-10 items-center justify-center rounded-full bg-status-info-bg\">\n <ShoppingCart className=\"size-5 text-status-info-icon\" aria-hidden=\"true\" />\n {isUnread ? (\n <span\n className=\"absolute -right-1 -top-1 size-3 rounded-full bg-accent-indigo ring-2 ring-background\"\n aria-hidden=\"true\"\n />\n ) : null}\n </div>\n\n <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n {/* Title row */}\n <p className=\"truncate text-sm font-medium leading-5 tracking-tight text-foreground\">\n {notification.title}\n </p>\n\n {/* Description row: time \u00B7 order# \u00B7 total \u00B7 assigned \u2014 inline so it wraps naturally per line */}\n <div className=\"text-xs leading-4 text-muted-foreground\">\n {timeAgo ? (\n <>\n <span className=\"whitespace-nowrap\">{timeAgo}</span>\n <span aria-hidden=\"true\" className=\"mx-1 text-text-disabled\">\u00B7</span>\n </>\n ) : null}\n {orderNumber ? (\n <>\n <span className=\"inline-flex items-center rounded bg-muted px-1.5 py-0.5 font-mono text-overline text-foreground\">\n #{orderNumber}\n </span>\n <span aria-hidden=\"true\" className=\"mx-1 text-text-disabled\">\u00B7</span>\n </>\n ) : null}\n {currentTotal ? (\n <>\n <span className=\"whitespace-nowrap font-medium text-foreground\">{currentTotal}</span>\n <span aria-hidden=\"true\" className=\"mx-1 text-text-disabled\">\u00B7</span>\n </>\n ) : null}\n <span className=\"whitespace-nowrap\">{t('sales.notifications.renderer.assignedToYou', 'Assigned to you')}</span>\n </div>\n\n {/* Actions row */}\n <div className=\"mt-2 flex items-center gap-2\">\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"h-8 rounded-md px-2.5 bg-accent-indigo text-accent-indigo-foreground hover:bg-accent-indigo/90\"\n onClick={(e) => {\n e.stopPropagation()\n handleView()\n }}\n disabled={executing || (!viewAction && !notification.linkHref)}\n >\n <ExternalLink className=\"size-3.5\" aria-hidden=\"true\" />\n {t('sales.notifications.renderer.viewOrder', 'View Order')}\n {executing ? <Loader2 className=\"ml-1 size-3 animate-spin\" /> : null}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"h-8 rounded-md px-2.5\"\n onClick={(e) => {\n e.stopPropagation()\n onDismiss()\n }}\n >\n {t('notifications.actions.dismiss', 'Dismiss')}\n </Button>\n </div>\n </div>\n\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n className=\"absolute right-2 top-2 opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100\"\n onClick={(e) => {\n e.stopPropagation()\n onDismiss()\n }}\n aria-label={t('notifications.actions.dismiss', 'Dismiss')}\n >\n <X className=\"size-3.5\" />\n </IconButton>\n </div>\n )\n}\n\nexport default SalesOrderCreatedRenderer\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.6.2-develop.3372.1.de9a169973",
3
+ "version": "0.6.2-develop.3373.1.be847b1058",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -238,16 +238,16 @@
238
238
  "zod": "^4.3.6"
239
239
  },
240
240
  "peerDependencies": {
241
- "@open-mercato/ai-assistant": "0.6.2-develop.3372.1.de9a169973",
242
- "@open-mercato/shared": "0.6.2-develop.3372.1.de9a169973",
243
- "@open-mercato/ui": "0.6.2-develop.3372.1.de9a169973",
241
+ "@open-mercato/ai-assistant": "0.6.2-develop.3373.1.be847b1058",
242
+ "@open-mercato/shared": "0.6.2-develop.3373.1.be847b1058",
243
+ "@open-mercato/ui": "0.6.2-develop.3373.1.be847b1058",
244
244
  "react": "^19.0.0",
245
245
  "react-dom": "^19.0.0"
246
246
  },
247
247
  "devDependencies": {
248
- "@open-mercato/ai-assistant": "0.6.2-develop.3372.1.de9a169973",
249
- "@open-mercato/shared": "0.6.2-develop.3372.1.de9a169973",
250
- "@open-mercato/ui": "0.6.2-develop.3372.1.de9a169973",
248
+ "@open-mercato/ai-assistant": "0.6.2-develop.3373.1.be847b1058",
249
+ "@open-mercato/shared": "0.6.2-develop.3373.1.be847b1058",
250
+ "@open-mercato/ui": "0.6.2-develop.3373.1.be847b1058",
251
251
  "@testing-library/dom": "^10.4.1",
252
252
  "@testing-library/jest-dom": "^6.9.1",
253
253
  "@testing-library/react": "^16.3.1",
@@ -4,6 +4,7 @@
4
4
  "notifications.actions.markRead": "Als gelesen markieren",
5
5
  "notifications.actions.undo": "Rückgängig",
6
6
  "notifications.badge.unread": "{count} ungelesene Benachrichtigungen",
7
+ "notifications.close": "Benachrichtigungen schließen",
7
8
  "notifications.delivery.email.actionNotice": "Aktionen sind in Open Mercato verfügbar und in dieser E-Mail schreibgeschützt.",
8
9
  "notifications.delivery.email.bodyIntro": "Prüfen Sie die Benachrichtigung und führen Sie bei Bedarf Aktionen aus.",
9
10
  "notifications.delivery.email.footer": "Open Mercato Benachrichtigungen",
@@ -17,6 +18,9 @@
17
18
  "notifications.filters.actionRequired": "Aktion erforderlich",
18
19
  "notifications.filters.all": "Alle",
19
20
  "notifications.filters.unread": "Ungelesen",
21
+ "notifications.footer.manage": "Benachrichtigungen verwalten",
22
+ "notifications.footer.toNavigate": "zum Navigieren",
23
+ "notifications.footer.useHint": "Verwende",
20
24
  "notifications.markAllRead": "Alle als gelesen markieren",
21
25
  "notifications.nav.title": "Benachrichtigungen",
22
26
  "notifications.settings.core.appUrl": "Anwendungs-URL",
@@ -46,5 +50,6 @@
46
50
  "notifications.toast.actionError": "Aktion konnte nicht ausgeführt werden",
47
51
  "notifications.toast.actionSuccess": "Aktion erfolgreich ausgeführt",
48
52
  "notifications.toast.dismissed": "Benachrichtigung verworfen",
49
- "notifications.toast.new": "Neue Benachrichtigung"
53
+ "notifications.toast.new": "Neue Benachrichtigung",
54
+ "notifications.unreadSummary": "{count} ungelesen"
50
55
  }
@@ -4,6 +4,7 @@
4
4
  "notifications.actions.markRead": "Mark as read",
5
5
  "notifications.actions.undo": "Undo",
6
6
  "notifications.badge.unread": "{count} unread notifications",
7
+ "notifications.close": "Close notifications",
7
8
  "notifications.delivery.email.actionNotice": "Actions are available in Open Mercato and are read-only in this email.",
8
9
  "notifications.delivery.email.bodyIntro": "Review the notification details and take any required actions.",
9
10
  "notifications.delivery.email.footer": "Open Mercato notifications",
@@ -17,6 +18,9 @@
17
18
  "notifications.filters.actionRequired": "Action Required",
18
19
  "notifications.filters.all": "All",
19
20
  "notifications.filters.unread": "Unread",
21
+ "notifications.footer.manage": "Manage notifications",
22
+ "notifications.footer.toNavigate": "to navigate",
23
+ "notifications.footer.useHint": "Use",
20
24
  "notifications.markAllRead": "Mark all read",
21
25
  "notifications.nav.title": "Notifications",
22
26
  "notifications.settings.core.appUrl": "Application URL",
@@ -46,5 +50,6 @@
46
50
  "notifications.toast.actionError": "Failed to execute action",
47
51
  "notifications.toast.actionSuccess": "Action completed successfully",
48
52
  "notifications.toast.dismissed": "Notification dismissed",
49
- "notifications.toast.new": "New notification"
53
+ "notifications.toast.new": "New notification",
54
+ "notifications.unreadSummary": "{count} unread"
50
55
  }
@@ -4,6 +4,7 @@
4
4
  "notifications.actions.markRead": "Marcar como leída",
5
5
  "notifications.actions.undo": "Deshacer",
6
6
  "notifications.badge.unread": "{count} notificaciones no leídas",
7
+ "notifications.close": "Cerrar notificaciones",
7
8
  "notifications.delivery.email.actionNotice": "Las acciones están disponibles en Open Mercato y son de solo lectura en este correo.",
8
9
  "notifications.delivery.email.bodyIntro": "Revisa los detalles de la notificación y realiza las acciones necesarias.",
9
10
  "notifications.delivery.email.footer": "Notificaciones de Open Mercato",
@@ -17,6 +18,9 @@
17
18
  "notifications.filters.actionRequired": "Acción requerida",
18
19
  "notifications.filters.all": "Todas",
19
20
  "notifications.filters.unread": "No leídas",
21
+ "notifications.footer.manage": "Gestionar notificaciones",
22
+ "notifications.footer.toNavigate": "para navegar",
23
+ "notifications.footer.useHint": "Usa",
20
24
  "notifications.markAllRead": "Marcar todo como leído",
21
25
  "notifications.nav.title": "Notificaciones",
22
26
  "notifications.settings.core.appUrl": "URL de la aplicación",
@@ -46,5 +50,6 @@
46
50
  "notifications.toast.actionError": "No se pudo ejecutar la acción",
47
51
  "notifications.toast.actionSuccess": "Acción completada correctamente",
48
52
  "notifications.toast.dismissed": "Notificación descartada",
49
- "notifications.toast.new": "Nueva notificación"
53
+ "notifications.toast.new": "Nueva notificación",
54
+ "notifications.unreadSummary": "{count} sin leer"
50
55
  }
@@ -4,6 +4,7 @@
4
4
  "notifications.actions.markRead": "Oznacz jako przeczytane",
5
5
  "notifications.actions.undo": "Cofnij",
6
6
  "notifications.badge.unread": "{count} nieprzeczytanych powiadomień",
7
+ "notifications.close": "Zamknij powiadomienia",
7
8
  "notifications.delivery.email.actionNotice": "Akcje są dostępne w Open Mercato i w tym e-mailu są tylko do odczytu.",
8
9
  "notifications.delivery.email.bodyIntro": "Sprawdź szczegóły powiadomienia i wykonaj wymagane działania.",
9
10
  "notifications.delivery.email.footer": "Powiadomienia Open Mercato",
@@ -17,6 +18,9 @@
17
18
  "notifications.filters.actionRequired": "Wymagana akcja",
18
19
  "notifications.filters.all": "Wszystkie",
19
20
  "notifications.filters.unread": "Nieprzeczytane",
21
+ "notifications.footer.manage": "Zarządzaj powiadomieniami",
22
+ "notifications.footer.toNavigate": "do nawigacji",
23
+ "notifications.footer.useHint": "Użyj",
20
24
  "notifications.markAllRead": "Oznacz wszystkie jako przeczytane",
21
25
  "notifications.nav.title": "Powiadomienia",
22
26
  "notifications.settings.core.appUrl": "URL aplikacji",
@@ -46,5 +50,6 @@
46
50
  "notifications.toast.actionError": "Nie udało się wykonać akcji",
47
51
  "notifications.toast.actionSuccess": "Akcja wykonana pomyślnie",
48
52
  "notifications.toast.dismissed": "Powiadomienie odrzucone",
49
- "notifications.toast.new": "Nowe powiadomienie"
53
+ "notifications.toast.new": "Nowe powiadomienie",
54
+ "notifications.unreadSummary": "{count} nieprzeczytanych"
50
55
  }
@@ -1,9 +1,10 @@
1
1
  'use client'
2
2
 
3
3
  import * as React from 'react'
4
- import { ShoppingCart, ExternalLink, DollarSign, User, Calendar } from 'lucide-react'
4
+ import { ShoppingCart, ExternalLink, Loader2, X } from 'lucide-react'
5
5
  import { useRouter } from 'next/navigation'
6
6
  import { Button } from '@open-mercato/ui/primitives/button'
7
+ import { IconButton } from '@open-mercato/ui/primitives/icon-button'
7
8
  import { cn } from '@open-mercato/shared/lib/utils'
8
9
  import { formatRelativeTime } from '@open-mercato/shared/lib/time'
9
10
  import { useT } from '@open-mercato/shared/lib/i18n/context'
@@ -56,11 +57,13 @@ export function SalesOrderCreatedRenderer({
56
57
  }
57
58
  }
58
59
 
60
+ const timeAgo = formatRelativeTime(notification.createdAt, { translate: t }) ?? ''
61
+
59
62
  return (
60
63
  <div
61
64
  className={cn(
62
- 'group relative px-4 py-3 hover:bg-muted/50 cursor-pointer transition-colors border-l-4 border-l-status-info-border',
63
- isUnread && 'bg-status-info-bg/50'
65
+ 'group relative flex gap-4 items-start rounded-xl p-3 transition-colors hover:bg-muted/40 cursor-pointer',
66
+ isUnread && 'bg-muted/20',
64
67
  )}
65
68
  onClick={handleView}
66
69
  onKeyDown={(e) => {
@@ -72,77 +75,92 @@ export function SalesOrderCreatedRenderer({
72
75
  role="button"
73
76
  tabIndex={0}
74
77
  >
75
- {isUnread && (
76
- <div className="absolute left-1.5 top-1/2 -translate-y-1/2 h-2 w-2 rounded-full bg-primary" />
77
- )}
78
-
79
- <div className="flex gap-3">
80
- <div className="flex-shrink-0 mt-0.5">
81
- <div className="h-10 w-10 rounded-lg bg-status-info-bg flex items-center justify-center">
82
- <ShoppingCart className="h-5 w-5 text-status-info-icon" />
83
- </div>
84
- </div>
78
+ {/* Avatar: indigo-tinted circle with shopping-cart icon */}
79
+ <div className="relative shrink-0 flex size-10 items-center justify-center rounded-full bg-status-info-bg">
80
+ <ShoppingCart className="size-5 text-status-info-icon" aria-hidden="true" />
81
+ {isUnread ? (
82
+ <span
83
+ className="absolute -right-1 -top-1 size-3 rounded-full bg-accent-indigo ring-2 ring-background"
84
+ aria-hidden="true"
85
+ />
86
+ ) : null}
87
+ </div>
85
88
 
86
- <div className="flex-1 min-w-0">
87
- <div className="flex items-start justify-between gap-2">
88
- <div>
89
- <h4 className={cn('text-sm font-medium', isUnread && 'font-semibold')}>
90
- {notification.title}
91
- </h4>
92
- {orderNumber && (
93
- <div className="flex items-center gap-1 mt-0.5">
94
- <span className="text-xs font-mono text-muted-foreground bg-muted px-1.5 py-0.5 rounded">
95
- #{orderNumber}
96
- </span>
97
- </div>
98
- )}
99
- </div>
100
- <span className="flex-shrink-0 text-xs text-muted-foreground flex items-center gap-1">
101
- <Calendar className="h-3 w-3" />
102
- {formatRelativeTime(notification.createdAt, { translate: t }) ?? ''}
103
- </span>
104
- </div>
89
+ <div className="flex min-w-0 flex-1 flex-col gap-1">
90
+ {/* Title row */}
91
+ <p className="truncate text-sm font-medium leading-5 tracking-tight text-foreground">
92
+ {notification.title}
93
+ </p>
105
94
 
106
- <div className="mt-2 flex items-center gap-4 text-xs text-muted-foreground">
107
- {currentTotal && (
108
- <div className="flex items-center gap-1">
109
- <DollarSign className="h-3 w-3" />
110
- <span className="font-medium text-foreground">{currentTotal}</span>
111
- </div>
112
- )}
113
- <div className="flex items-center gap-1">
114
- <User className="h-3 w-3" />
115
- <span>{t('sales.notifications.renderer.assignedToYou', 'Assigned to you')}</span>
116
- </div>
117
- </div>
95
+ {/* Description row: time · order# · total · assigned — inline so it wraps naturally per line */}
96
+ <div className="text-xs leading-4 text-muted-foreground">
97
+ {timeAgo ? (
98
+ <>
99
+ <span className="whitespace-nowrap">{timeAgo}</span>
100
+ <span aria-hidden="true" className="mx-1 text-text-disabled">·</span>
101
+ </>
102
+ ) : null}
103
+ {orderNumber ? (
104
+ <>
105
+ <span className="inline-flex items-center rounded bg-muted px-1.5 py-0.5 font-mono text-overline text-foreground">
106
+ #{orderNumber}
107
+ </span>
108
+ <span aria-hidden="true" className="mx-1 text-text-disabled">·</span>
109
+ </>
110
+ ) : null}
111
+ {currentTotal ? (
112
+ <>
113
+ <span className="whitespace-nowrap font-medium text-foreground">{currentTotal}</span>
114
+ <span aria-hidden="true" className="mx-1 text-text-disabled">·</span>
115
+ </>
116
+ ) : null}
117
+ <span className="whitespace-nowrap">{t('sales.notifications.renderer.assignedToYou', 'Assigned to you')}</span>
118
+ </div>
118
119
 
119
- <div className="mt-3 flex gap-2">
120
- <Button
121
- variant="default"
122
- size="sm"
123
- onClick={(e) => {
124
- e.stopPropagation()
125
- handleView()
126
- }}
127
- disabled={executing || (!viewAction && !notification.linkHref)}
128
- className="gap-1"
129
- >
130
- <ExternalLink className="h-3 w-3" />
131
- {t('sales.notifications.renderer.viewOrder', 'View Order')}
132
- </Button>
133
- <Button
134
- variant="ghost"
135
- size="sm"
136
- onClick={(e) => {
137
- e.stopPropagation()
138
- onDismiss()
139
- }}
140
- >
141
- {t('notifications.actions.dismiss', 'Dismiss')}
142
- </Button>
143
- </div>
120
+ {/* Actions row */}
121
+ <div className="mt-2 flex items-center gap-2">
122
+ <Button
123
+ type="button"
124
+ size="sm"
125
+ className="h-8 rounded-md px-2.5 bg-accent-indigo text-accent-indigo-foreground hover:bg-accent-indigo/90"
126
+ onClick={(e) => {
127
+ e.stopPropagation()
128
+ handleView()
129
+ }}
130
+ disabled={executing || (!viewAction && !notification.linkHref)}
131
+ >
132
+ <ExternalLink className="size-3.5" aria-hidden="true" />
133
+ {t('sales.notifications.renderer.viewOrder', 'View Order')}
134
+ {executing ? <Loader2 className="ml-1 size-3 animate-spin" /> : null}
135
+ </Button>
136
+ <Button
137
+ type="button"
138
+ variant="outline"
139
+ size="sm"
140
+ className="h-8 rounded-md px-2.5"
141
+ onClick={(e) => {
142
+ e.stopPropagation()
143
+ onDismiss()
144
+ }}
145
+ >
146
+ {t('notifications.actions.dismiss', 'Dismiss')}
147
+ </Button>
144
148
  </div>
145
149
  </div>
150
+
151
+ <IconButton
152
+ type="button"
153
+ variant="ghost"
154
+ size="xs"
155
+ className="absolute right-2 top-2 opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100"
156
+ onClick={(e) => {
157
+ e.stopPropagation()
158
+ onDismiss()
159
+ }}
160
+ aria-label={t('notifications.actions.dismiss', 'Dismiss')}
161
+ >
162
+ <X className="size-3.5" />
163
+ </IconButton>
146
164
  </div>
147
165
  )
148
166
  }