@open-mercato/core 0.4.2-canary-07dbc98202 → 0.4.2-canary-1000cb714f

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.
Files changed (235) hide show
  1. package/dist/generated/entities.ids.generated.js +59 -63
  2. package/dist/generated/entities.ids.generated.js.map +2 -2
  3. package/dist/generated/entity-fields-registry.js +0 -2
  4. package/dist/generated/entity-fields-registry.js.map +2 -2
  5. package/dist/modules/api_docs/frontend/docs/api/page.js +2 -3
  6. package/dist/modules/api_docs/frontend/docs/api/page.js.map +2 -2
  7. package/dist/modules/auth/api/admin/nav.js +3 -4
  8. package/dist/modules/auth/api/admin/nav.js.map +2 -2
  9. package/dist/modules/auth/api/reset/confirm.js +2 -25
  10. package/dist/modules/auth/api/reset/confirm.js.map +2 -2
  11. package/dist/modules/auth/api/reset.js +0 -23
  12. package/dist/modules/auth/api/reset.js.map +2 -2
  13. package/dist/modules/auth/api/sidebar/preferences/route.js +9 -14
  14. package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
  15. package/dist/modules/auth/commands/users.js +0 -55
  16. package/dist/modules/auth/commands/users.js.map +2 -2
  17. package/dist/modules/auth/lib/setup-app.js +0 -1
  18. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  19. package/dist/modules/auth/services/authService.js +3 -3
  20. package/dist/modules/auth/services/authService.js.map +2 -2
  21. package/dist/modules/configs/cli.js +0 -6
  22. package/dist/modules/configs/cli.js.map +2 -2
  23. package/dist/modules/customers/commands/deals.js +0 -31
  24. package/dist/modules/customers/commands/deals.js.map +2 -2
  25. package/dist/modules/sales/commands/documents.js +0 -53
  26. package/dist/modules/sales/commands/documents.js.map +2 -2
  27. package/dist/modules/sales/commands/payments.js +0 -26
  28. package/dist/modules/sales/commands/payments.js.map +2 -2
  29. package/dist/modules/staff/commands/leave-requests.js +0 -79
  30. package/dist/modules/staff/commands/leave-requests.js.map +2 -2
  31. package/generated/entities.ids.generated.ts +59 -63
  32. package/generated/entity-fields-registry.ts +0 -2
  33. package/package.json +2 -2
  34. package/src/modules/api_docs/frontend/docs/api/page.tsx +2 -3
  35. package/src/modules/auth/api/admin/nav.ts +6 -10
  36. package/src/modules/auth/api/reset/confirm.ts +2 -25
  37. package/src/modules/auth/api/reset.ts +0 -23
  38. package/src/modules/auth/api/sidebar/preferences/route.ts +12 -21
  39. package/src/modules/auth/commands/users.ts +0 -68
  40. package/src/modules/auth/i18n/de.json +1 -29
  41. package/src/modules/auth/i18n/en.json +1 -29
  42. package/src/modules/auth/i18n/es.json +1 -29
  43. package/src/modules/auth/i18n/pl.json +1 -29
  44. package/src/modules/auth/lib/setup-app.ts +0 -1
  45. package/src/modules/auth/services/authService.ts +4 -4
  46. package/src/modules/business_rules/i18n/en.json +1 -3
  47. package/src/modules/catalog/i18n/en.json +1 -3
  48. package/src/modules/configs/cli.ts +0 -6
  49. package/src/modules/customers/commands/deals.ts +0 -39
  50. package/src/modules/customers/i18n/en.json +1 -5
  51. package/src/modules/sales/commands/documents.ts +0 -65
  52. package/src/modules/sales/commands/payments.ts +0 -33
  53. package/src/modules/sales/i18n/de.json +0 -20
  54. package/src/modules/sales/i18n/en.json +1 -25
  55. package/src/modules/sales/i18n/es.json +0 -20
  56. package/src/modules/sales/i18n/pl.json +0 -20
  57. package/src/modules/staff/commands/leave-requests.ts +0 -94
  58. package/src/modules/staff/i18n/de.json +0 -4
  59. package/src/modules/staff/i18n/en.json +1 -9
  60. package/src/modules/staff/i18n/es.json +0 -4
  61. package/src/modules/staff/i18n/pl.json +0 -4
  62. package/src/modules/workflows/i18n/en.json +1 -3
  63. package/dist/generated/entities/notification/index.js +0 -57
  64. package/dist/generated/entities/notification/index.js.map +0 -7
  65. package/dist/modules/auth/api/profile/route.js +0 -155
  66. package/dist/modules/auth/api/profile/route.js.map +0 -7
  67. package/dist/modules/auth/backend/auth/profile/page.js +0 -99
  68. package/dist/modules/auth/backend/auth/profile/page.js.map +0 -7
  69. package/dist/modules/auth/backend/auth/profile/page.meta.js +0 -12
  70. package/dist/modules/auth/backend/auth/profile/page.meta.js.map +0 -7
  71. package/dist/modules/auth/notifications.js +0 -112
  72. package/dist/modules/auth/notifications.js.map +0 -7
  73. package/dist/modules/business_rules/notifications.js +0 -28
  74. package/dist/modules/business_rules/notifications.js.map +0 -7
  75. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js +0 -37
  76. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js.map +0 -7
  77. package/dist/modules/catalog/notifications.js +0 -28
  78. package/dist/modules/catalog/notifications.js.map +0 -7
  79. package/dist/modules/catalog/subscribers/low-stock-notification.js +0 -38
  80. package/dist/modules/catalog/subscribers/low-stock-notification.js.map +0 -7
  81. package/dist/modules/customers/notifications.js +0 -48
  82. package/dist/modules/customers/notifications.js.map +0 -7
  83. package/dist/modules/notifications/acl.js +0 -11
  84. package/dist/modules/notifications/acl.js.map +0 -7
  85. package/dist/modules/notifications/api/[id]/action/route.js +0 -74
  86. package/dist/modules/notifications/api/[id]/action/route.js.map +0 -7
  87. package/dist/modules/notifications/api/[id]/dismiss/route.js +0 -15
  88. package/dist/modules/notifications/api/[id]/dismiss/route.js.map +0 -7
  89. package/dist/modules/notifications/api/[id]/read/route.js +0 -15
  90. package/dist/modules/notifications/api/[id]/read/route.js.map +0 -7
  91. package/dist/modules/notifications/api/[id]/restore/route.js +0 -53
  92. package/dist/modules/notifications/api/[id]/restore/route.js.map +0 -7
  93. package/dist/modules/notifications/api/batch/route.js +0 -17
  94. package/dist/modules/notifications/api/batch/route.js.map +0 -7
  95. package/dist/modules/notifications/api/feature/route.js +0 -17
  96. package/dist/modules/notifications/api/feature/route.js.map +0 -7
  97. package/dist/modules/notifications/api/mark-all-read/route.js +0 -35
  98. package/dist/modules/notifications/api/mark-all-read/route.js.map +0 -7
  99. package/dist/modules/notifications/api/openapi.js +0 -76
  100. package/dist/modules/notifications/api/openapi.js.map +0 -7
  101. package/dist/modules/notifications/api/role/route.js +0 -17
  102. package/dist/modules/notifications/api/role/route.js.map +0 -7
  103. package/dist/modules/notifications/api/route.js +0 -85
  104. package/dist/modules/notifications/api/route.js.map +0 -7
  105. package/dist/modules/notifications/api/settings/route.js +0 -155
  106. package/dist/modules/notifications/api/settings/route.js.map +0 -7
  107. package/dist/modules/notifications/api/unread-count/route.js +0 -38
  108. package/dist/modules/notifications/api/unread-count/route.js.map +0 -7
  109. package/dist/modules/notifications/backend/config/notifications/page.js +0 -10
  110. package/dist/modules/notifications/backend/config/notifications/page.js.map +0 -7
  111. package/dist/modules/notifications/backend/config/notifications/page.meta.js +0 -24
  112. package/dist/modules/notifications/backend/config/notifications/page.meta.js.map +0 -7
  113. package/dist/modules/notifications/cli.js +0 -16
  114. package/dist/modules/notifications/cli.js.map +0 -7
  115. package/dist/modules/notifications/data/entities.js +0 -112
  116. package/dist/modules/notifications/data/entities.js.map +0 -7
  117. package/dist/modules/notifications/data/validators.js +0 -94
  118. package/dist/modules/notifications/data/validators.js.map +0 -7
  119. package/dist/modules/notifications/di.js +0 -13
  120. package/dist/modules/notifications/di.js.map +0 -7
  121. package/dist/modules/notifications/emails/NotificationEmail.js +0 -58
  122. package/dist/modules/notifications/emails/NotificationEmail.js.map +0 -7
  123. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js +0 -44
  124. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js.map +0 -7
  125. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +0 -219
  126. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +0 -7
  127. package/dist/modules/notifications/index.js +0 -14
  128. package/dist/modules/notifications/index.js.map +0 -7
  129. package/dist/modules/notifications/lib/deliveryConfig.js +0 -105
  130. package/dist/modules/notifications/lib/deliveryConfig.js.map +0 -7
  131. package/dist/modules/notifications/lib/events.js +0 -12
  132. package/dist/modules/notifications/lib/events.js.map +0 -7
  133. package/dist/modules/notifications/lib/notificationBuilder.js +0 -66
  134. package/dist/modules/notifications/lib/notificationBuilder.js.map +0 -7
  135. package/dist/modules/notifications/lib/notificationFactory.js +0 -54
  136. package/dist/modules/notifications/lib/notificationFactory.js.map +0 -7
  137. package/dist/modules/notifications/lib/notificationMapper.js +0 -34
  138. package/dist/modules/notifications/lib/notificationMapper.js.map +0 -7
  139. package/dist/modules/notifications/lib/notificationRecipients.js +0 -35
  140. package/dist/modules/notifications/lib/notificationRecipients.js.map +0 -7
  141. package/dist/modules/notifications/lib/notificationService.js +0 -279
  142. package/dist/modules/notifications/lib/notificationService.js.map +0 -7
  143. package/dist/modules/notifications/lib/routeHelpers.js +0 -101
  144. package/dist/modules/notifications/lib/routeHelpers.js.map +0 -7
  145. package/dist/modules/notifications/lib/safeHref.js +0 -24
  146. package/dist/modules/notifications/lib/safeHref.js.map +0 -7
  147. package/dist/modules/notifications/migrations/Migration20260123000001.js +0 -70
  148. package/dist/modules/notifications/migrations/Migration20260123000001.js.map +0 -7
  149. package/dist/modules/notifications/migrations/Migration20260126150000.js +0 -37
  150. package/dist/modules/notifications/migrations/Migration20260126150000.js.map +0 -7
  151. package/dist/modules/notifications/subscribers/deliver-notification.js +0 -139
  152. package/dist/modules/notifications/subscribers/deliver-notification.js.map +0 -7
  153. package/dist/modules/notifications/workers/create-notification.worker.js +0 -70
  154. package/dist/modules/notifications/workers/create-notification.worker.js.map +0 -7
  155. package/dist/modules/sales/notifications.client.js +0 -51
  156. package/dist/modules/sales/notifications.client.js.map +0 -7
  157. package/dist/modules/sales/notifications.js +0 -88
  158. package/dist/modules/sales/notifications.js.map +0 -7
  159. package/dist/modules/sales/subscribers/quote-expiring-notification.js +0 -38
  160. package/dist/modules/sales/subscribers/quote-expiring-notification.js.map +0 -7
  161. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +0 -137
  162. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +0 -7
  163. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +0 -137
  164. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +0 -7
  165. package/dist/modules/sales/widgets/notifications/index.js +0 -7
  166. package/dist/modules/sales/widgets/notifications/index.js.map +0 -7
  167. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js +0 -60
  168. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js.map +0 -7
  169. package/dist/modules/staff/notifications.js +0 -75
  170. package/dist/modules/staff/notifications.js.map +0 -7
  171. package/dist/modules/workflows/notifications.js +0 -28
  172. package/dist/modules/workflows/notifications.js.map +0 -7
  173. package/dist/modules/workflows/subscribers/task-assigned-notification.js +0 -38
  174. package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +0 -7
  175. package/generated/entities/notification/index.ts +0 -27
  176. package/src/modules/auth/api/profile/route.ts +0 -160
  177. package/src/modules/auth/backend/auth/profile/page.meta.ts +0 -8
  178. package/src/modules/auth/backend/auth/profile/page.tsx +0 -127
  179. package/src/modules/auth/notifications.ts +0 -109
  180. package/src/modules/business_rules/notifications.ts +0 -25
  181. package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +0 -50
  182. package/src/modules/catalog/notifications.ts +0 -25
  183. package/src/modules/catalog/subscribers/low-stock-notification.ts +0 -52
  184. package/src/modules/customers/notifications.ts +0 -44
  185. package/src/modules/notifications/acl.ts +0 -7
  186. package/src/modules/notifications/api/[id]/action/route.ts +0 -75
  187. package/src/modules/notifications/api/[id]/dismiss/route.ts +0 -12
  188. package/src/modules/notifications/api/[id]/read/route.ts +0 -12
  189. package/src/modules/notifications/api/[id]/restore/route.ts +0 -53
  190. package/src/modules/notifications/api/batch/route.ts +0 -14
  191. package/src/modules/notifications/api/feature/route.ts +0 -14
  192. package/src/modules/notifications/api/mark-all-read/route.ts +0 -34
  193. package/src/modules/notifications/api/openapi.ts +0 -76
  194. package/src/modules/notifications/api/role/route.ts +0 -14
  195. package/src/modules/notifications/api/route.ts +0 -92
  196. package/src/modules/notifications/api/settings/route.ts +0 -157
  197. package/src/modules/notifications/api/unread-count/route.ts +0 -38
  198. package/src/modules/notifications/backend/config/notifications/page.meta.ts +0 -22
  199. package/src/modules/notifications/backend/config/notifications/page.tsx +0 -12
  200. package/src/modules/notifications/cli.ts +0 -18
  201. package/src/modules/notifications/data/entities.ts +0 -99
  202. package/src/modules/notifications/data/validators.ts +0 -110
  203. package/src/modules/notifications/di.ts +0 -11
  204. package/src/modules/notifications/emails/NotificationEmail.tsx +0 -98
  205. package/src/modules/notifications/frontend/NotificationInboxPageClient.tsx +0 -42
  206. package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +0 -231
  207. package/src/modules/notifications/i18n/de.json +0 -50
  208. package/src/modules/notifications/i18n/en.json +0 -50
  209. package/src/modules/notifications/i18n/es.json +0 -50
  210. package/src/modules/notifications/i18n/pl.json +0 -50
  211. package/src/modules/notifications/index.ts +0 -12
  212. package/src/modules/notifications/lib/deliveryConfig.ts +0 -145
  213. package/src/modules/notifications/lib/events.ts +0 -48
  214. package/src/modules/notifications/lib/notificationBuilder.ts +0 -121
  215. package/src/modules/notifications/lib/notificationFactory.ts +0 -76
  216. package/src/modules/notifications/lib/notificationMapper.ts +0 -33
  217. package/src/modules/notifications/lib/notificationRecipients.ts +0 -83
  218. package/src/modules/notifications/lib/notificationService.ts +0 -414
  219. package/src/modules/notifications/lib/routeHelpers.ts +0 -151
  220. package/src/modules/notifications/lib/safeHref.ts +0 -29
  221. package/src/modules/notifications/migrations/.snapshot-open-mercato.json +0 -300
  222. package/src/modules/notifications/migrations/Migration20260123000001.ts +0 -73
  223. package/src/modules/notifications/migrations/Migration20260126150000.ts +0 -39
  224. package/src/modules/notifications/subscribers/deliver-notification.ts +0 -175
  225. package/src/modules/notifications/workers/create-notification.worker.ts +0 -122
  226. package/src/modules/sales/notifications.client.ts +0 -65
  227. package/src/modules/sales/notifications.ts +0 -82
  228. package/src/modules/sales/subscribers/quote-expiring-notification.ts +0 -53
  229. package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +0 -156
  230. package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +0 -156
  231. package/src/modules/sales/widgets/notifications/index.ts +0 -2
  232. package/src/modules/sales/widgets/notifications/useSalesDocumentTotals.ts +0 -81
  233. package/src/modules/staff/notifications.ts +0 -71
  234. package/src/modules/workflows/notifications.ts +0 -25
  235. package/src/modules/workflows/subscribers/task-assigned-notification.ts +0 -53
@@ -1,137 +0,0 @@
1
- "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
3
- import * as React from "react";
4
- import { FileText, ExternalLink, DollarSign, User, Calendar } from "lucide-react";
5
- import { useRouter } from "next/navigation";
6
- import { Button } from "@open-mercato/ui/primitives/button";
7
- import { cn } from "@open-mercato/shared/lib/utils";
8
- import { useT } from "@open-mercato/shared/lib/i18n/context";
9
- import { formatMoney } from "../../components/documents/lineItemUtils.js";
10
- import { useSalesDocumentTotals } from "./useSalesDocumentTotals.js";
11
- function formatTimeAgo(dateString, t) {
12
- const date = new Date(dateString);
13
- const now = /* @__PURE__ */ new Date();
14
- const diffMs = now.getTime() - date.getTime();
15
- const diffMins = Math.floor(diffMs / 6e4);
16
- const diffHours = Math.floor(diffMs / 36e5);
17
- const diffDays = Math.floor(diffMs / 864e5);
18
- if (diffMins < 1) return t("common.time.justNow", "just now");
19
- if (diffMins < 60) return t("common.time.minutesAgo", "{count}m ago").replace("{count}", String(diffMins));
20
- if (diffHours < 24) return t("common.time.hoursAgo", "{count}h ago").replace("{count}", String(diffHours));
21
- if (diffDays < 7) return t("common.time.daysAgo", "{count}d ago").replace("{count}", String(diffDays));
22
- return date.toLocaleDateString();
23
- }
24
- function normalizeTotal(value) {
25
- if (!value) return null;
26
- let trimmed = value.trim();
27
- if (trimmed.startsWith("(") && trimmed.endsWith(")")) {
28
- trimmed = trimmed.slice(1, -1).trim();
29
- }
30
- return trimmed.length ? trimmed : null;
31
- }
32
- function SalesQuoteCreatedRenderer({
33
- notification,
34
- onAction,
35
- onDismiss,
36
- actions = []
37
- }) {
38
- const t = useT();
39
- const router = useRouter();
40
- const [executing, setExecuting] = React.useState(false);
41
- const isUnread = notification.status === "unread";
42
- const quoteNumber = notification.bodyVariables?.quoteNumber ?? notification.titleVariables?.quoteNumber;
43
- const fallbackTotal = normalizeTotal(notification.bodyVariables?.totalAmount ?? null) ?? normalizeTotal(notification.bodyVariables?.total ?? null);
44
- const { totals } = useSalesDocumentTotals("quote", notification.sourceEntityId);
45
- const currentTotal = totals && typeof totals.grandTotalGrossAmount === "number" ? formatMoney(totals.grandTotalGrossAmount, totals.currencyCode) : fallbackTotal;
46
- const viewAction = actions.find((action) => action.id === "view") ?? actions[0] ?? null;
47
- const handleView = async () => {
48
- if (!viewAction) {
49
- if (notification.linkHref) router.push(notification.linkHref);
50
- return;
51
- }
52
- setExecuting(true);
53
- try {
54
- await onAction(viewAction.id);
55
- } finally {
56
- setExecuting(false);
57
- }
58
- };
59
- return /* @__PURE__ */ jsxs(
60
- "div",
61
- {
62
- className: cn(
63
- "group relative px-4 py-3 hover:bg-muted/50 cursor-pointer transition-colors border-l-4 border-l-amber-500",
64
- isUnread && "bg-amber-50/50 dark:bg-amber-950/20"
65
- ),
66
- onClick: handleView,
67
- children: [
68
- isUnread && /* @__PURE__ */ jsx("div", { className: "absolute left-1.5 top-1/2 -translate-y-1/2 h-2 w-2 rounded-full bg-primary" }),
69
- /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
70
- /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 mt-0.5", children: /* @__PURE__ */ jsx("div", { className: "h-10 w-10 rounded-lg bg-amber-100 dark:bg-amber-900/40 flex items-center justify-center", children: /* @__PURE__ */ jsx(FileText, { className: "h-5 w-5 text-amber-600 dark:text-amber-400" }) }) }),
71
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
72
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
73
- /* @__PURE__ */ jsxs("div", { children: [
74
- /* @__PURE__ */ jsx("h4", { className: cn("text-sm font-medium", isUnread && "font-semibold"), children: notification.title }),
75
- quoteNumber && /* @__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: [
76
- "#",
77
- quoteNumber
78
- ] }) })
79
- ] }),
80
- /* @__PURE__ */ jsxs("span", { className: "flex-shrink-0 text-xs text-muted-foreground flex items-center gap-1", children: [
81
- /* @__PURE__ */ jsx(Calendar, { className: "h-3 w-3" }),
82
- formatTimeAgo(notification.createdAt, t)
83
- ] })
84
- ] }),
85
- /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center gap-4 text-xs text-muted-foreground", children: [
86
- currentTotal && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
87
- /* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3" }),
88
- /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: currentTotal })
89
- ] }),
90
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
91
- /* @__PURE__ */ jsx(User, { className: "h-3 w-3" }),
92
- /* @__PURE__ */ jsx("span", { children: t("sales.notifications.renderer.pendingReview", "Pending review") })
93
- ] })
94
- ] }),
95
- /* @__PURE__ */ jsxs("div", { className: "mt-3 flex gap-2", children: [
96
- /* @__PURE__ */ jsxs(
97
- Button,
98
- {
99
- variant: "default",
100
- size: "sm",
101
- onClick: (e) => {
102
- e.stopPropagation();
103
- handleView();
104
- },
105
- disabled: executing || !viewAction && !notification.linkHref,
106
- className: "gap-1",
107
- children: [
108
- /* @__PURE__ */ jsx(ExternalLink, { className: "h-3 w-3" }),
109
- t("sales.notifications.renderer.viewQuote", "View Quote")
110
- ]
111
- }
112
- ),
113
- /* @__PURE__ */ jsx(
114
- Button,
115
- {
116
- variant: "ghost",
117
- size: "sm",
118
- onClick: (e) => {
119
- e.stopPropagation();
120
- onDismiss();
121
- },
122
- children: t("notifications.actions.dismiss", "Dismiss")
123
- }
124
- )
125
- ] })
126
- ] })
127
- ] })
128
- ]
129
- }
130
- );
131
- }
132
- var SalesQuoteCreatedRenderer_default = SalesQuoteCreatedRenderer;
133
- export {
134
- SalesQuoteCreatedRenderer,
135
- SalesQuoteCreatedRenderer_default as default
136
- };
137
- //# sourceMappingURL=SalesQuoteCreatedRenderer.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../../src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { FileText, 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 { 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 formatTimeAgo(dateString: string, t: (key: string, fallback?: string) => string): string {\n const date = new Date(dateString)\n const now = new Date()\n const diffMs = now.getTime() - date.getTime()\n const diffMins = Math.floor(diffMs / 60000)\n const diffHours = Math.floor(diffMs / 3600000)\n const diffDays = Math.floor(diffMs / 86400000)\n\n if (diffMins < 1) return t('common.time.justNow', 'just now')\n if (diffMins < 60) return t('common.time.minutesAgo', '{count}m ago').replace('{count}', String(diffMins))\n if (diffHours < 24) return t('common.time.hoursAgo', '{count}h ago').replace('{count}', String(diffHours))\n if (diffDays < 7) return t('common.time.daysAgo', '{count}d ago').replace('{count}', String(diffDays))\n return date.toLocaleDateString()\n}\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 SalesQuoteCreatedRenderer({\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 quoteNumber = notification.bodyVariables?.quoteNumber ?? notification.titleVariables?.quoteNumber\n const fallbackTotal =\n normalizeTotal(notification.bodyVariables?.totalAmount ?? null) ??\n normalizeTotal(notification.bodyVariables?.total ?? null)\n const { totals } = useSalesDocumentTotals('quote', 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-amber-500',\n isUnread && 'bg-amber-50/50 dark:bg-amber-950/20'\n )}\n onClick={handleView}\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-amber-100 dark:bg-amber-900/40 flex items-center justify-center\">\n <FileText className=\"h-5 w-5 text-amber-600 dark:text-amber-400\" />\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 {quoteNumber && (\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 #{quoteNumber}\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 {formatTimeAgo(notification.createdAt, 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.pendingReview', 'Pending review')}</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.viewQuote', 'View Quote')}\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 SalesQuoteCreatedRenderer\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- import { SalesOrderCreatedRenderer } from "./SalesOrderCreatedRenderer.js";
2
- import { SalesQuoteCreatedRenderer } from "./SalesQuoteCreatedRenderer.js";
3
- export {
4
- SalesOrderCreatedRenderer,
5
- SalesQuoteCreatedRenderer
6
- };
7
- //# sourceMappingURL=index.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../../src/modules/sales/widgets/notifications/index.ts"],
4
- "sourcesContent": ["export { SalesOrderCreatedRenderer } from './SalesOrderCreatedRenderer'\nexport { SalesQuoteCreatedRenderer } from './SalesQuoteCreatedRenderer'\n"],
5
- "mappings": "AAAA,SAAS,iCAAiC;AAC1C,SAAS,iCAAiC;",
6
- "names": []
7
- }
@@ -1,60 +0,0 @@
1
- "use client";
2
- import * as React from "react";
3
- import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
4
- const REFRESH_INTERVAL_MS = 3e4;
5
- function buildDocumentTotalsUrl(kind, documentId) {
6
- const params = new URLSearchParams({ id: documentId, page: "1", pageSize: "1" });
7
- const collection = kind === "order" ? "orders" : "quotes";
8
- return `/api/sales/${collection}?${params.toString()}`;
9
- }
10
- function extractTotals(payload) {
11
- const item = payload?.items?.[0];
12
- if (!item) return null;
13
- const rawAmount = item.grandTotalGrossAmount;
14
- let grandTotalGrossAmount = null;
15
- if (typeof rawAmount === "number") {
16
- grandTotalGrossAmount = Number.isNaN(rawAmount) ? null : rawAmount;
17
- } else if (typeof rawAmount === "string" && rawAmount.trim().length) {
18
- const parsed = Number(rawAmount);
19
- grandTotalGrossAmount = Number.isNaN(parsed) ? null : parsed;
20
- }
21
- return {
22
- grandTotalGrossAmount,
23
- currencyCode: typeof item.currencyCode === "string" ? item.currencyCode : null
24
- };
25
- }
26
- function useSalesDocumentTotals(kind, documentId) {
27
- const [totals, setTotals] = React.useState(null);
28
- React.useEffect(() => {
29
- if (!documentId) {
30
- setTotals(null);
31
- return;
32
- }
33
- let active = true;
34
- const loadTotals = async () => {
35
- try {
36
- const call = await apiCall(buildDocumentTotalsUrl(kind, documentId));
37
- if (!active) return;
38
- if (call.ok) {
39
- const nextTotals = extractTotals(call.result ?? null);
40
- setTotals(nextTotals);
41
- }
42
- } catch {
43
- if (active) {
44
- setTotals(null);
45
- }
46
- }
47
- };
48
- loadTotals();
49
- const interval = setInterval(loadTotals, REFRESH_INTERVAL_MS);
50
- return () => {
51
- active = false;
52
- clearInterval(interval);
53
- };
54
- }, [kind, documentId]);
55
- return { totals };
56
- }
57
- export {
58
- useSalesDocumentTotals
59
- };
60
- //# sourceMappingURL=useSalesDocumentTotals.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../../src/modules/sales/widgets/notifications/useSalesDocumentTotals.ts"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\n\ntype DocumentKind = 'order' | 'quote'\n\ntype DocumentTotals = {\n grandTotalGrossAmount: number | null\n currencyCode: string | null\n}\n\ntype DocumentListResponse = {\n items?: Array<{\n grandTotalGrossAmount?: number | string | null\n currencyCode?: string | null\n }>\n}\n\nconst REFRESH_INTERVAL_MS = 30000\n\nfunction buildDocumentTotalsUrl(kind: DocumentKind, documentId: string) {\n const params = new URLSearchParams({ id: documentId, page: '1', pageSize: '1' })\n const collection = kind === 'order' ? 'orders' : 'quotes'\n return `/api/sales/${collection}?${params.toString()}`\n}\n\nfunction extractTotals(payload: DocumentListResponse | null): DocumentTotals | null {\n const item = payload?.items?.[0]\n if (!item) return null\n const rawAmount = item.grandTotalGrossAmount\n let grandTotalGrossAmount: number | null = null\n if (typeof rawAmount === 'number') {\n grandTotalGrossAmount = Number.isNaN(rawAmount) ? null : rawAmount\n } else if (typeof rawAmount === 'string' && rawAmount.trim().length) {\n const parsed = Number(rawAmount)\n grandTotalGrossAmount = Number.isNaN(parsed) ? null : parsed\n }\n return {\n grandTotalGrossAmount,\n currencyCode: typeof item.currencyCode === 'string' ? item.currencyCode : null,\n }\n}\n\nexport function useSalesDocumentTotals(kind: DocumentKind, documentId?: string | null) {\n const [totals, setTotals] = React.useState<DocumentTotals | null>(null)\n\n React.useEffect(() => {\n if (!documentId) {\n setTotals(null)\n return\n }\n\n let active = true\n\n const loadTotals = async () => {\n try {\n const call = await apiCall<DocumentListResponse>(buildDocumentTotalsUrl(kind, documentId))\n if (!active) return\n if (call.ok) {\n const nextTotals = extractTotals(call.result ?? null)\n setTotals(nextTotals)\n }\n } catch {\n if (active) {\n setTotals(null)\n }\n }\n }\n\n loadTotals()\n const interval = setInterval(loadTotals, REFRESH_INTERVAL_MS)\n\n return () => {\n active = false\n clearInterval(interval)\n }\n }, [kind, documentId])\n\n return { totals }\n}\n"],
5
- "mappings": ";AAEA,YAAY,WAAW;AACvB,SAAS,eAAe;AAgBxB,MAAM,sBAAsB;AAE5B,SAAS,uBAAuB,MAAoB,YAAoB;AACtE,QAAM,SAAS,IAAI,gBAAgB,EAAE,IAAI,YAAY,MAAM,KAAK,UAAU,IAAI,CAAC;AAC/E,QAAM,aAAa,SAAS,UAAU,WAAW;AACjD,SAAO,cAAc,UAAU,IAAI,OAAO,SAAS,CAAC;AACtD;AAEA,SAAS,cAAc,SAA6D;AAClF,QAAM,OAAO,SAAS,QAAQ,CAAC;AAC/B,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,YAAY,KAAK;AACvB,MAAI,wBAAuC;AAC3C,MAAI,OAAO,cAAc,UAAU;AACjC,4BAAwB,OAAO,MAAM,SAAS,IAAI,OAAO;AAAA,EAC3D,WAAW,OAAO,cAAc,YAAY,UAAU,KAAK,EAAE,QAAQ;AACnE,UAAM,SAAS,OAAO,SAAS;AAC/B,4BAAwB,OAAO,MAAM,MAAM,IAAI,OAAO;AAAA,EACxD;AACA,SAAO;AAAA,IACL;AAAA,IACA,cAAc,OAAO,KAAK,iBAAiB,WAAW,KAAK,eAAe;AAAA,EAC5E;AACF;AAEO,SAAS,uBAAuB,MAAoB,YAA4B;AACrF,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAgC,IAAI;AAEtE,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,YAAY;AACf,gBAAU,IAAI;AACd;AAAA,IACF;AAEA,QAAI,SAAS;AAEb,UAAM,aAAa,YAAY;AAC7B,UAAI;AACF,cAAM,OAAO,MAAM,QAA8B,uBAAuB,MAAM,UAAU,CAAC;AACzF,YAAI,CAAC,OAAQ;AACb,YAAI,KAAK,IAAI;AACX,gBAAM,aAAa,cAAc,KAAK,UAAU,IAAI;AACpD,oBAAU,UAAU;AAAA,QACtB;AAAA,MACF,QAAQ;AACN,YAAI,QAAQ;AACV,oBAAU,IAAI;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAEA,eAAW;AACX,UAAM,WAAW,YAAY,YAAY,mBAAmB;AAE5D,WAAO,MAAM;AACX,eAAS;AACT,oBAAc,QAAQ;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,MAAM,UAAU,CAAC;AAErB,SAAO,EAAE,OAAO;AAClB;",
6
- "names": []
7
- }
@@ -1,75 +0,0 @@
1
- const notificationTypes = [
2
- {
3
- type: "staff.leave_request.pending",
4
- module: "staff",
5
- titleKey: "staff.notifications.leaveRequest.pending.title",
6
- bodyKey: "staff.notifications.leaveRequest.pending.body",
7
- icon: "calendar-off",
8
- severity: "warning",
9
- actions: [
10
- {
11
- id: "approve",
12
- labelKey: "staff.notifications.leaveRequest.actions.approve",
13
- variant: "default",
14
- icon: "check",
15
- commandId: "staff.leave-requests.accept"
16
- },
17
- {
18
- id: "reject",
19
- labelKey: "staff.notifications.leaveRequest.actions.reject",
20
- variant: "destructive",
21
- icon: "x",
22
- commandId: "staff.leave-requests.reject"
23
- }
24
- ],
25
- primaryActionId: "approve",
26
- linkHref: "/backend/staff/leave-requests/{sourceEntityId}",
27
- expiresAfterHours: 168
28
- },
29
- {
30
- type: "staff.leave_request.approved",
31
- module: "staff",
32
- titleKey: "staff.notifications.leaveRequest.approved.title",
33
- bodyKey: "staff.notifications.leaveRequest.approved.body",
34
- icon: "calendar-check",
35
- severity: "success",
36
- actions: [
37
- {
38
- id: "view",
39
- labelKey: "common.view",
40
- variant: "outline",
41
- href: "/backend/staff/leave-requests/{sourceEntityId}",
42
- icon: "external-link"
43
- }
44
- ],
45
- linkHref: "/backend/staff/leave-requests/{sourceEntityId}",
46
- expiresAfterHours: 168
47
- // 7 days
48
- },
49
- {
50
- type: "staff.leave_request.rejected",
51
- module: "staff",
52
- titleKey: "staff.notifications.leaveRequest.rejected.title",
53
- bodyKey: "staff.notifications.leaveRequest.rejected.body",
54
- icon: "calendar-x",
55
- severity: "warning",
56
- actions: [
57
- {
58
- id: "view",
59
- labelKey: "common.view",
60
- variant: "outline",
61
- href: "/backend/staff/leave-requests/{sourceEntityId}",
62
- icon: "external-link"
63
- }
64
- ],
65
- linkHref: "/backend/staff/leave-requests/{sourceEntityId}",
66
- expiresAfterHours: 168
67
- // 7 days
68
- }
69
- ];
70
- var notifications_default = notificationTypes;
71
- export {
72
- notifications_default as default,
73
- notificationTypes
74
- };
75
- //# sourceMappingURL=notifications.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/modules/staff/notifications.ts"],
4
- "sourcesContent": ["import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'\n\nexport const notificationTypes: NotificationTypeDefinition[] = [\n {\n type: 'staff.leave_request.pending',\n module: 'staff',\n titleKey: 'staff.notifications.leaveRequest.pending.title',\n bodyKey: 'staff.notifications.leaveRequest.pending.body',\n icon: 'calendar-off',\n severity: 'warning',\n actions: [\n {\n id: 'approve',\n labelKey: 'staff.notifications.leaveRequest.actions.approve',\n variant: 'default',\n icon: 'check',\n commandId: 'staff.leave-requests.accept',\n },\n {\n id: 'reject',\n labelKey: 'staff.notifications.leaveRequest.actions.reject',\n variant: 'destructive',\n icon: 'x',\n commandId: 'staff.leave-requests.reject',\n },\n ],\n primaryActionId: 'approve',\n linkHref: '/backend/staff/leave-requests/{sourceEntityId}',\n expiresAfterHours: 168,\n },\n {\n type: 'staff.leave_request.approved',\n module: 'staff',\n titleKey: 'staff.notifications.leaveRequest.approved.title',\n bodyKey: 'staff.notifications.leaveRequest.approved.body',\n icon: 'calendar-check',\n severity: 'success',\n actions: [\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend/staff/leave-requests/{sourceEntityId}',\n icon: 'external-link',\n },\n ],\n linkHref: '/backend/staff/leave-requests/{sourceEntityId}',\n expiresAfterHours: 168, // 7 days\n },\n {\n type: 'staff.leave_request.rejected',\n module: 'staff',\n titleKey: 'staff.notifications.leaveRequest.rejected.title',\n bodyKey: 'staff.notifications.leaveRequest.rejected.body',\n icon: 'calendar-x',\n severity: 'warning',\n actions: [\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend/staff/leave-requests/{sourceEntityId}',\n icon: 'external-link',\n },\n ],\n linkHref: '/backend/staff/leave-requests/{sourceEntityId}',\n expiresAfterHours: 168, // 7 days\n },\n]\n\nexport default notificationTypes\n"],
5
- "mappings": "AAEO,MAAM,oBAAkD;AAAA,EAC7D;AAAA,IACE,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,MACP;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,QACN,WAAW;AAAA,MACb;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,QACN,WAAW;AAAA,MACb;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,IACjB,UAAU;AAAA,IACV,mBAAmB;AAAA,EACrB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,MACP;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,QACN,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,UAAU;AAAA,IACV,mBAAmB;AAAA;AAAA,EACrB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,MACP;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,QACN,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,UAAU;AAAA,IACV,mBAAmB;AAAA;AAAA,EACrB;AACF;AAEA,IAAO,wBAAQ;",
6
- "names": []
7
- }
@@ -1,28 +0,0 @@
1
- const notificationTypes = [
2
- {
3
- type: "workflows.task.assigned",
4
- module: "workflows",
5
- titleKey: "workflows.notifications.task.assigned.title",
6
- bodyKey: "workflows.notifications.task.assigned.body",
7
- icon: "clipboard-list",
8
- severity: "info",
9
- actions: [
10
- {
11
- id: "view",
12
- labelKey: "common.view",
13
- variant: "outline",
14
- href: "/backend/workflows/tasks/{sourceEntityId}",
15
- icon: "external-link"
16
- }
17
- ],
18
- linkHref: "/backend/workflows/tasks/{sourceEntityId}",
19
- expiresAfterHours: 168
20
- // 7 days
21
- }
22
- ];
23
- var notifications_default = notificationTypes;
24
- export {
25
- notifications_default as default,
26
- notificationTypes
27
- };
28
- //# sourceMappingURL=notifications.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/modules/workflows/notifications.ts"],
4
- "sourcesContent": ["import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'\n\nexport const notificationTypes: NotificationTypeDefinition[] = [\n {\n type: 'workflows.task.assigned',\n module: 'workflows',\n titleKey: 'workflows.notifications.task.assigned.title',\n bodyKey: 'workflows.notifications.task.assigned.body',\n icon: 'clipboard-list',\n severity: 'info',\n actions: [\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend/workflows/tasks/{sourceEntityId}',\n icon: 'external-link',\n },\n ],\n linkHref: '/backend/workflows/tasks/{sourceEntityId}',\n expiresAfterHours: 168, // 7 days\n },\n]\n\nexport default notificationTypes\n"],
5
- "mappings": "AAEO,MAAM,oBAAkD;AAAA,EAC7D;AAAA,IACE,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,MACP;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,QACN,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,UAAU;AAAA,IACV,mBAAmB;AAAA;AAAA,EACrB;AACF;AAEA,IAAO,wBAAQ;",
6
- "names": []
7
- }
@@ -1,38 +0,0 @@
1
- import { resolveNotificationService } from "../../notifications/lib/notificationService.js";
2
- import { buildNotificationFromType } from "../../notifications/lib/notificationBuilder.js";
3
- import { notificationTypes } from "../notifications.js";
4
- const metadata = {
5
- event: "workflows.task.assigned",
6
- persistent: true,
7
- id: "workflows:task-assigned-notification"
8
- };
9
- async function handle(payload, ctx) {
10
- if (!payload.assignedUserId) return;
11
- try {
12
- const notificationService = resolveNotificationService(ctx);
13
- const typeDef = notificationTypes.find((type) => type.type === "workflows.task.assigned");
14
- if (!typeDef) return;
15
- const notificationInput = buildNotificationFromType(typeDef, {
16
- recipientUserId: payload.assignedUserId,
17
- bodyVariables: {
18
- taskName: payload.taskName,
19
- workflowName: payload.workflowName,
20
- dueDate: payload.dueDate ?? ""
21
- },
22
- sourceEntityType: "workflows:user_task",
23
- sourceEntityId: payload.taskId,
24
- linkHref: `/backend/workflows/tasks/${payload.taskId}`
25
- });
26
- await notificationService.create(notificationInput, {
27
- tenantId: payload.tenantId,
28
- organizationId: payload.organizationId ?? null
29
- });
30
- } catch (err) {
31
- console.error("[workflows:task-assigned-notification] Failed to create notification:", err);
32
- }
33
- }
34
- export {
35
- handle as default,
36
- metadata
37
- };
38
- //# sourceMappingURL=task-assigned-notification.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../src/modules/workflows/subscribers/task-assigned-notification.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { resolveNotificationService } from '../../notifications/lib/notificationService'\nimport { buildNotificationFromType } from '../../notifications/lib/notificationBuilder'\nimport { notificationTypes } from '../notifications'\n\nexport const metadata = {\n event: 'workflows.task.assigned',\n persistent: true,\n id: 'workflows:task-assigned-notification',\n}\n\ntype TaskAssignedPayload = {\n taskId: string\n taskName: string\n workflowName: string\n assignedUserId: string\n dueDate?: string | null\n tenantId: string\n organizationId?: string | null\n}\n\ntype ResolverContext = {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport default async function handle(payload: TaskAssignedPayload, ctx: ResolverContext) {\n if (!payload.assignedUserId) return\n\n try {\n const notificationService = resolveNotificationService(ctx)\n const typeDef = notificationTypes.find((type) => type.type === 'workflows.task.assigned')\n if (!typeDef) return\n\n const notificationInput = buildNotificationFromType(typeDef, {\n recipientUserId: payload.assignedUserId,\n bodyVariables: {\n taskName: payload.taskName,\n workflowName: payload.workflowName,\n dueDate: payload.dueDate ?? '',\n },\n sourceEntityType: 'workflows:user_task',\n sourceEntityId: payload.taskId,\n linkHref: `/backend/workflows/tasks/${payload.taskId}`,\n })\n\n await notificationService.create(notificationInput, {\n tenantId: payload.tenantId,\n organizationId: payload.organizationId ?? null,\n })\n } catch (err) {\n console.error('[workflows:task-assigned-notification] Failed to create notification:', err)\n }\n}\n"],
5
- "mappings": "AACA,SAAS,kCAAkC;AAC3C,SAAS,iCAAiC;AAC1C,SAAS,yBAAyB;AAE3B,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAgBA,eAAO,OAA8B,SAA8B,KAAsB;AACvF,MAAI,CAAC,QAAQ,eAAgB;AAE7B,MAAI;AACF,UAAM,sBAAsB,2BAA2B,GAAG;AAC1D,UAAM,UAAU,kBAAkB,KAAK,CAAC,SAAS,KAAK,SAAS,yBAAyB;AACxF,QAAI,CAAC,QAAS;AAEd,UAAM,oBAAoB,0BAA0B,SAAS;AAAA,MAC3D,iBAAiB,QAAQ;AAAA,MACzB,eAAe;AAAA,QACb,UAAU,QAAQ;AAAA,QAClB,cAAc,QAAQ;AAAA,QACtB,SAAS,QAAQ,WAAW;AAAA,MAC9B;AAAA,MACA,kBAAkB;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,UAAU,4BAA4B,QAAQ,MAAM;AAAA,IACtD,CAAC;AAED,UAAM,oBAAoB,OAAO,mBAAmB;AAAA,MAClD,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ,kBAAkB;AAAA,IAC5C,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,YAAQ,MAAM,yEAAyE,GAAG;AAAA,EAC5F;AACF;",
6
- "names": []
7
- }
@@ -1,27 +0,0 @@
1
- export const id = 'id'
2
- export const recipient_user_id = 'recipient_user_id'
3
- export const type = 'type'
4
- export const title_key = 'title_key'
5
- export const body_key = 'body_key'
6
- export const title_variables = 'title_variables'
7
- export const body_variables = 'body_variables'
8
- export const title = 'title'
9
- export const body = 'body'
10
- export const icon = 'icon'
11
- export const severity = 'severity'
12
- export const status = 'status'
13
- export const action_data = 'action_data'
14
- export const action_result = 'action_result'
15
- export const action_taken = 'action_taken'
16
- export const source_module = 'source_module'
17
- export const source_entity_type = 'source_entity_type'
18
- export const source_entity_id = 'source_entity_id'
19
- export const link_href = 'link_href'
20
- export const group_key = 'group_key'
21
- export const created_at = 'created_at'
22
- export const read_at = 'read_at'
23
- export const actioned_at = 'actioned_at'
24
- export const dismissed_at = 'dismissed_at'
25
- export const expires_at = 'expires_at'
26
- export const tenant_id = 'tenant_id'
27
- export const organization_id = 'organization_id'
@@ -1,160 +0,0 @@
1
- import { NextResponse } from 'next/server'
2
- import { z } from 'zod'
3
- import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
4
- import type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
5
- import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
6
- import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
7
- import { signJwt } from '@open-mercato/shared/lib/auth/jwt'
8
- import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
9
- import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
10
- import { AuthService } from '@open-mercato/core/modules/auth/services/authService'
11
- import { User } from '@open-mercato/core/modules/auth/data/entities'
12
- import type { EntityManager } from '@mikro-orm/postgresql'
13
- import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
14
-
15
- const profileResponseSchema = z.object({
16
- email: z.string().email(),
17
- })
18
-
19
- const updateSchema = z.object({
20
- email: z.string().email().optional(),
21
- password: z.string().min(6).optional(),
22
- }).refine((data) => Boolean(data.email || data.password), {
23
- message: 'Provide an email or password.',
24
- path: ['email'],
25
- })
26
-
27
- const profileUpdateResponseSchema = z.object({
28
- ok: z.literal(true),
29
- email: z.string().email(),
30
- })
31
-
32
- export const metadata = {
33
- GET: { requireAuth: true },
34
- PUT: { requireAuth: true },
35
- }
36
-
37
- function buildCommandContext(container: Awaited<ReturnType<typeof createRequestContainer>>, auth: NonNullable<Awaited<ReturnType<typeof getAuthFromRequest>>>, req: Request): CommandRuntimeContext {
38
- return {
39
- container,
40
- auth,
41
- organizationScope: null,
42
- selectedOrganizationId: auth.orgId ?? null,
43
- organizationIds: auth.orgId ? [auth.orgId] : null,
44
- request: req,
45
- }
46
- }
47
-
48
- export async function GET(req: Request) {
49
- const { translate } = await resolveTranslations()
50
- const auth = await getAuthFromRequest(req)
51
- if (!auth?.sub) {
52
- return NextResponse.json({ error: translate('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })
53
- }
54
- try {
55
- const container = await createRequestContainer()
56
- const em = (container.resolve('em') as EntityManager)
57
- const user = await findOneWithDecryption(
58
- em,
59
- User,
60
- { id: auth.sub, deletedAt: null },
61
- undefined,
62
- { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },
63
- )
64
- if (!user) {
65
- return NextResponse.json({ error: translate('auth.users.form.errors.notFound', 'User not found') }, { status: 404 })
66
- }
67
- return NextResponse.json({ email: String(user.email) })
68
- } catch (err) {
69
- console.error('auth.profile.load failed', err)
70
- return NextResponse.json({ error: translate('auth.profile.form.errors.load', 'Failed to load profile.') }, { status: 400 })
71
- }
72
- }
73
-
74
- export async function PUT(req: Request) {
75
- const { translate } = await resolveTranslations()
76
- const auth = await getAuthFromRequest(req)
77
- if (!auth?.sub) {
78
- return NextResponse.json({ error: translate('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })
79
- }
80
- try {
81
- const body = await req.json().catch(() => ({}))
82
- const parsed = updateSchema.safeParse(body)
83
- if (!parsed.success) {
84
- return NextResponse.json(
85
- {
86
- error: translate('auth.profile.form.errors.invalid', 'Invalid profile update.'),
87
- issues: parsed.error.issues,
88
- },
89
- { status: 400 },
90
- )
91
- }
92
- const container = await createRequestContainer()
93
- const commandBus = (container.resolve('commandBus') as CommandBus)
94
- const ctx = buildCommandContext(container, auth, req)
95
- const { result } = await commandBus.execute<{ id: string; email?: string; password?: string }, User>(
96
- 'auth.users.update',
97
- {
98
- input: {
99
- id: auth.sub,
100
- email: parsed.data.email,
101
- password: parsed.data.password,
102
- },
103
- ctx,
104
- },
105
- )
106
- const authService = container.resolve('authService') as AuthService
107
- const roles = await authService.getUserRoles(result, result.tenantId ? String(result.tenantId) : null)
108
- const jwt = signJwt({
109
- sub: String(result.id),
110
- tenantId: result.tenantId ? String(result.tenantId) : null,
111
- orgId: result.organizationId ? String(result.organizationId) : null,
112
- email: result.email,
113
- roles,
114
- })
115
- const res = NextResponse.json({ ok: true, email: String(result.email) })
116
- res.cookies.set('auth_token', jwt, {
117
- httpOnly: true,
118
- path: '/',
119
- sameSite: 'lax',
120
- secure: process.env.NODE_ENV === 'production',
121
- maxAge: 60 * 60 * 8,
122
- })
123
- return res
124
- } catch (err) {
125
- if (err instanceof CrudHttpError) {
126
- return NextResponse.json(err.body, { status: err.status })
127
- }
128
- console.error('auth.profile.update failed', err)
129
- return NextResponse.json({ error: translate('auth.profile.form.errors.save', 'Failed to update profile.') }, { status: 400 })
130
- }
131
- }
132
-
133
- export const openApi: OpenApiRouteDoc = {
134
- tag: 'Authentication & Accounts',
135
- summary: 'Profile settings',
136
- methods: {
137
- GET: {
138
- summary: 'Get current profile',
139
- description: 'Returns the email address for the signed-in user.',
140
- responses: [
141
- { status: 200, description: 'Profile payload', schema: profileResponseSchema },
142
- { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },
143
- { status: 404, description: 'User not found', schema: z.object({ error: z.string() }) },
144
- ],
145
- },
146
- PUT: {
147
- summary: 'Update current profile',
148
- description: 'Updates the email address or password for the signed-in user.',
149
- requestBody: {
150
- contentType: 'application/json',
151
- schema: updateSchema,
152
- },
153
- responses: [
154
- { status: 200, description: 'Profile updated', schema: profileUpdateResponseSchema },
155
- { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },
156
- { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },
157
- ],
158
- },
159
- },
160
- }
@@ -1,8 +0,0 @@
1
- export const metadata = {
2
- requireAuth: true,
3
- pageTitle: 'Profile',
4
- pageTitleKey: 'auth.profile.title',
5
- breadcrumb: [
6
- { label: 'Profile', labelKey: 'auth.profile.title' },
7
- ],
8
- }