@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.
- package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +84 -62
- package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/notifications/i18n/de.json +6 -1
- package/src/modules/notifications/i18n/en.json +6 -1
- package/src/modules/notifications/i18n/es.json +6 -1
- package/src/modules/notifications/i18n/pl.json +6 -1
- package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +87 -69
|
@@ -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,
|
|
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
|
|
52
|
-
isUnread && "bg-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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__ */
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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,
|
|
5
|
-
"mappings": ";
|
|
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": ";AA8EM,SAmBM,UAlBJ,KADF;AA5EN,YAAY,WAAW;AACvB,SAAS,cAAc,cAAc,SAAS,SAAS;AACvD,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAC3B,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,QAAM,UAAU,mBAAmB,aAAa,WAAW,EAAE,WAAW,EAAE,CAAC,KAAK;AAEhF,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,MAGV;AAAA,6BAAC,SAAI,WAAU,6FACb;AAAA,8BAAC,gBAAa,WAAU,gCAA+B,eAAY,QAAO;AAAA,UACzE,WACC;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,eAAY;AAAA;AAAA,UACd,IACE;AAAA,WACN;AAAA,QAEA,qBAAC,SAAI,WAAU,sCAEb;AAAA,8BAAC,OAAE,WAAU,yEACV,uBAAa,OAChB;AAAA,UAGA,qBAAC,SAAI,WAAU,2CACZ;AAAA,sBACC,iCACE;AAAA,kCAAC,UAAK,WAAU,qBAAqB,mBAAQ;AAAA,cAC7C,oBAAC,UAAK,eAAY,QAAO,WAAU,2BAA0B,kBAAC;AAAA,eAChE,IACE;AAAA,YACH,cACC,iCACE;AAAA,mCAAC,UAAK,WAAU,mGAAkG;AAAA;AAAA,gBAC9G;AAAA,iBACJ;AAAA,cACA,oBAAC,UAAK,eAAY,QAAO,WAAU,2BAA0B,kBAAC;AAAA,eAChE,IACE;AAAA,YACH,eACC,iCACE;AAAA,kCAAC,UAAK,WAAU,iDAAiD,wBAAa;AAAA,cAC9E,oBAAC,UAAK,eAAY,QAAO,WAAU,2BAA0B,kBAAC;AAAA,eAChE,IACE;AAAA,YACJ,oBAAC,UAAK,WAAU,qBAAqB,YAAE,8CAA8C,iBAAiB,GAAE;AAAA,aAC1G;AAAA,UAGA,qBAAC,SAAI,WAAU,gCACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,SAAS,CAAC,MAAM;AACd,oBAAE,gBAAgB;AAClB,6BAAW;AAAA,gBACb;AAAA,gBACA,UAAU,aAAc,CAAC,cAAc,CAAC,aAAa;AAAA,gBAErD;AAAA,sCAAC,gBAAa,WAAU,YAAW,eAAY,QAAO;AAAA,kBACrD,EAAE,0CAA0C,YAAY;AAAA,kBACxD,YAAY,oBAAC,WAAQ,WAAU,4BAA2B,IAAK;AAAA;AAAA;AAAA,YAClE;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,SAAS,CAAC,MAAM;AACd,oBAAE,gBAAgB;AAClB,4BAAU;AAAA,gBACZ;AAAA,gBAEC,YAAE,iCAAiC,SAAS;AAAA;AAAA,YAC/C;AAAA,aACF;AAAA,WACF;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,WAAU;AAAA,YACV,SAAS,CAAC,MAAM;AACd,gBAAE,gBAAgB;AAClB,wBAAU;AAAA,YACZ;AAAA,YACA,cAAY,EAAE,iCAAiC,SAAS;AAAA,YAExD,8BAAC,KAAE,WAAU,YAAW;AAAA;AAAA,QAC1B;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,oCAAQ;",
|
|
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.
|
|
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.
|
|
242
|
-
"@open-mercato/shared": "0.6.2-develop.
|
|
243
|
-
"@open-mercato/ui": "0.6.2-develop.
|
|
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.
|
|
249
|
-
"@open-mercato/shared": "0.6.2-develop.
|
|
250
|
-
"@open-mercato/ui": "0.6.2-develop.
|
|
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,
|
|
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
|
|
63
|
-
isUnread && 'bg-
|
|
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
|
-
{
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
}
|