@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,122 +0,0 @@
1
- import type { EntityManager } from '@mikro-orm/core'
2
- import type { Knex } from 'knex'
3
- import { Notification } from '../data/entities'
4
- import type { CreateNotificationInput, CreateRoleNotificationInput, CreateFeatureNotificationInput } from '../data/validators'
5
- import { buildNotificationEntity, emitNotificationCreated, emitNotificationCreatedBatch } from '../lib/notificationFactory'
6
- import { getRecipientUserIdsForFeature, getRecipientUserIdsForRole } from '../lib/notificationRecipients'
7
-
8
- function getKnex(em: EntityManager): Knex {
9
- return (em.getConnection() as unknown as { getKnex: () => Knex }).getKnex()
10
- }
11
-
12
- export const NOTIFICATIONS_QUEUE_NAME = 'notifications'
13
-
14
- export type CreateNotificationJob = {
15
- type: 'create'
16
- input: CreateNotificationInput
17
- tenantId: string
18
- organizationId?: string | null
19
- }
20
-
21
- export type CreateRoleNotificationJob = {
22
- type: 'create-role'
23
- input: CreateRoleNotificationInput
24
- tenantId: string
25
- organizationId?: string | null
26
- }
27
-
28
- export type CreateFeatureNotificationJob = {
29
- type: 'create-feature'
30
- input: CreateFeatureNotificationInput
31
- tenantId: string
32
- organizationId?: string | null
33
- }
34
-
35
- export type CleanupExpiredJob = {
36
- type: 'cleanup-expired'
37
- }
38
-
39
- export type NotificationJob = CreateNotificationJob | CreateRoleNotificationJob | CreateFeatureNotificationJob | CleanupExpiredJob
40
-
41
- export const metadata = {
42
- queue: NOTIFICATIONS_QUEUE_NAME,
43
- id: 'notifications:create',
44
- concurrency: 5,
45
- }
46
-
47
- type HandlerContext = {
48
- resolve: <T = unknown>(name: string) => T
49
- }
50
-
51
- export default async function handle(
52
- job: { payload: NotificationJob },
53
- ctx: HandlerContext
54
- ): Promise<void> {
55
- const { payload } = job
56
-
57
- if (payload.type === 'create') {
58
- const em = (ctx.resolve('em') as EntityManager).fork()
59
- const eventBus = ctx.resolve('eventBus') as { emit: (event: string, payload: unknown) => Promise<void> }
60
- const { input, tenantId, organizationId } = payload
61
- const { recipientUserId, ...content } = input
62
- const notification = buildNotificationEntity(em, content, recipientUserId, { tenantId, organizationId })
63
-
64
- await em.persistAndFlush(notification)
65
-
66
- await emitNotificationCreated(eventBus, notification, { tenantId, organizationId })
67
- } else if (payload.type === 'create-role') {
68
- const em = (ctx.resolve('em') as EntityManager).fork()
69
- const eventBus = ctx.resolve('eventBus') as { emit: (event: string, payload: unknown) => Promise<void> }
70
- const { input, tenantId, organizationId } = payload
71
-
72
- const knex = getKnex(em)
73
- const recipientUserIds = await getRecipientUserIdsForRole(knex, tenantId, input.roleId)
74
- if (recipientUserIds.length === 0) {
75
- return
76
- }
77
-
78
- const { roleId: _roleId, ...content } = input
79
- const notifications: Notification[] = []
80
- for (const recipientUserId of recipientUserIds) {
81
- const notification = buildNotificationEntity(em, content, recipientUserId, { tenantId, organizationId })
82
- notifications.push(notification)
83
- }
84
-
85
- await em.persistAndFlush(notifications)
86
-
87
- await emitNotificationCreatedBatch(eventBus, notifications, { tenantId, organizationId })
88
- } else if (payload.type === 'create-feature') {
89
- const em = (ctx.resolve('em') as EntityManager).fork()
90
- const eventBus = ctx.resolve('eventBus') as { emit: (event: string, payload: unknown) => Promise<void> }
91
- const { input, tenantId, organizationId } = payload
92
-
93
- const knex = getKnex(em)
94
- const recipientUserIds = await getRecipientUserIdsForFeature(knex, tenantId, input.requiredFeature)
95
-
96
- if (recipientUserIds.length === 0) {
97
- return
98
- }
99
-
100
- const notifications: Notification[] = []
101
- const { requiredFeature: _requiredFeature, ...content } = input
102
- for (const recipientUserId of recipientUserIds) {
103
- const notification = buildNotificationEntity(em, content, recipientUserId, { tenantId, organizationId })
104
- notifications.push(notification)
105
- }
106
-
107
- await em.persistAndFlush(notifications)
108
-
109
- await emitNotificationCreatedBatch(eventBus, notifications, { tenantId, organizationId })
110
- } else if (payload.type === 'cleanup-expired') {
111
- const em = (ctx.resolve('em') as EntityManager).fork()
112
- const knex = getKnex(em)
113
-
114
- await knex('notifications')
115
- .where('expires_at', '<', knex.fn.now())
116
- .whereNotIn('status', ['actioned', 'dismissed'])
117
- .update({
118
- status: 'dismissed',
119
- dismissed_at: knex.fn.now(),
120
- })
121
- }
122
- }
@@ -1,65 +0,0 @@
1
- 'use client'
2
-
3
- import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
4
- import { SalesOrderCreatedRenderer } from './widgets/notifications/SalesOrderCreatedRenderer'
5
- import { SalesQuoteCreatedRenderer } from './widgets/notifications/SalesQuoteCreatedRenderer'
6
-
7
- /**
8
- * Client-side notification type definitions with custom renderers.
9
- * These should be used in client components where custom rendering is needed.
10
- *
11
- * Example usage:
12
- * ```tsx
13
- * import { salesNotificationTypes } from '@open-mercato/core/modules/sales/notifications.client'
14
- *
15
- * // Use in NotificationPanel or NotificationItem
16
- * const renderer = salesNotificationTypes.find(t => t.type === notification.type)?.Renderer
17
- * if (renderer) {
18
- * return <renderer notification={notification} onAction={...} onDismiss={...} actions={...} />
19
- * }
20
- * ```
21
- */
22
- export const salesNotificationTypes: NotificationTypeDefinition[] = [
23
- {
24
- type: 'sales.order.created',
25
- module: 'sales',
26
- titleKey: 'sales.notifications.order.created.title',
27
- bodyKey: 'sales.notifications.order.created.body',
28
- icon: 'shopping-cart',
29
- severity: 'info',
30
- actions: [
31
- {
32
- id: 'view',
33
- labelKey: 'common.view',
34
- variant: 'outline',
35
- href: '/backend/sales/orders/{sourceEntityId}',
36
- icon: 'external-link',
37
- },
38
- ],
39
- linkHref: '/backend/sales/orders/{sourceEntityId}',
40
- Renderer: SalesOrderCreatedRenderer,
41
- expiresAfterHours: 168,
42
- },
43
- {
44
- type: 'sales.quote.created',
45
- module: 'sales',
46
- titleKey: 'sales.notifications.quote.created.title',
47
- bodyKey: 'sales.notifications.quote.created.body',
48
- icon: 'file-text',
49
- severity: 'info',
50
- actions: [
51
- {
52
- id: 'view',
53
- labelKey: 'common.view',
54
- variant: 'outline',
55
- href: '/backend/sales/quotes/{sourceEntityId}',
56
- icon: 'external-link',
57
- },
58
- ],
59
- linkHref: '/backend/sales/quotes/{sourceEntityId}',
60
- Renderer: SalesQuoteCreatedRenderer,
61
- expiresAfterHours: 168,
62
- },
63
- ]
64
-
65
- export default salesNotificationTypes
@@ -1,82 +0,0 @@
1
- import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
2
-
3
- export const notificationTypes: NotificationTypeDefinition[] = [
4
- {
5
- type: 'sales.order.created',
6
- module: 'sales',
7
- titleKey: 'sales.notifications.order.created.title',
8
- bodyKey: 'sales.notifications.order.created.body',
9
- icon: 'shopping-cart',
10
- severity: 'info',
11
- actions: [
12
- {
13
- id: 'view',
14
- labelKey: 'common.view',
15
- variant: 'outline',
16
- href: '/backend/sales/orders/{sourceEntityId}',
17
- icon: 'external-link',
18
- },
19
- ],
20
- linkHref: '/backend/sales/orders/{sourceEntityId}',
21
- expiresAfterHours: 168, // 7 days
22
- },
23
- {
24
- type: 'sales.quote.created',
25
- module: 'sales',
26
- titleKey: 'sales.notifications.quote.created.title',
27
- bodyKey: 'sales.notifications.quote.created.body',
28
- icon: 'file-text',
29
- severity: 'info',
30
- actions: [
31
- {
32
- id: 'view',
33
- labelKey: 'common.view',
34
- variant: 'outline',
35
- href: '/backend/sales/quotes/{sourceEntityId}',
36
- icon: 'external-link',
37
- },
38
- ],
39
- linkHref: '/backend/sales/quotes/{sourceEntityId}',
40
- expiresAfterHours: 168, // 7 days
41
- },
42
- {
43
- type: 'sales.payment.received',
44
- module: 'sales',
45
- titleKey: 'sales.notifications.payment.received.title',
46
- bodyKey: 'sales.notifications.payment.received.body',
47
- icon: 'credit-card',
48
- severity: 'success',
49
- actions: [
50
- {
51
- id: 'view',
52
- labelKey: 'common.view',
53
- variant: 'outline',
54
- href: '/backend/sales/orders/{sourceEntityId}',
55
- icon: 'external-link',
56
- },
57
- ],
58
- linkHref: '/backend/sales/orders/{sourceEntityId}',
59
- expiresAfterHours: 168, // 7 days
60
- },
61
- {
62
- type: 'sales.quote.expiring',
63
- module: 'sales',
64
- titleKey: 'sales.notifications.quote.expiring.title',
65
- bodyKey: 'sales.notifications.quote.expiring.body',
66
- icon: 'clock',
67
- severity: 'warning',
68
- actions: [
69
- {
70
- id: 'view',
71
- labelKey: 'common.view',
72
- variant: 'outline',
73
- href: '/backend/sales/quotes/{sourceEntityId}',
74
- icon: 'external-link',
75
- },
76
- ],
77
- linkHref: '/backend/sales/quotes/{sourceEntityId}',
78
- expiresAfterHours: 72, // 3 days
79
- },
80
- ]
81
-
82
- export default notificationTypes
@@ -1,53 +0,0 @@
1
- import type { EntityManager } from '@mikro-orm/postgresql'
2
- import { resolveNotificationService } from '../../notifications/lib/notificationService'
3
- import { buildFeatureNotificationFromType } from '../../notifications/lib/notificationBuilder'
4
- import { notificationTypes } from '../notifications'
5
-
6
- export const metadata = {
7
- event: 'sales.quote.expiring',
8
- persistent: true,
9
- id: 'sales:quote-expiring-notification',
10
- }
11
-
12
- type QuoteExpiringPayload = {
13
- quoteId: string
14
- quoteNumber: string
15
- expiresAt: string
16
- daysUntilExpiry: number
17
- customerName?: string | null
18
- totalAmount?: string | null
19
- tenantId: string
20
- organizationId?: string | null
21
- }
22
-
23
- type ResolverContext = {
24
- resolve: <T = unknown>(name: string) => T
25
- }
26
-
27
- export default async function handle(payload: QuoteExpiringPayload, ctx: ResolverContext) {
28
- try {
29
- const notificationService = resolveNotificationService(ctx)
30
- const typeDef = notificationTypes.find((type) => type.type === 'sales.quote.expiring')
31
- if (!typeDef) return
32
-
33
- const notificationInput = buildFeatureNotificationFromType(typeDef, {
34
- requiredFeature: 'sales.quotes.manage',
35
- bodyVariables: {
36
- quoteNumber: payload.quoteNumber,
37
- expiresAt: payload.expiresAt,
38
- daysUntilExpiry: String(payload.daysUntilExpiry),
39
- customerName: payload.customerName ?? '',
40
- },
41
- sourceEntityType: 'sales:quote',
42
- sourceEntityId: payload.quoteId,
43
- linkHref: `/backend/sales/quotes/${payload.quoteId}`,
44
- })
45
-
46
- await notificationService.createForFeature(notificationInput, {
47
- tenantId: payload.tenantId,
48
- organizationId: payload.organizationId ?? null,
49
- })
50
- } catch (err) {
51
- console.error('[sales:quote-expiring-notification] Failed to create notification:', err)
52
- }
53
- }
@@ -1,156 +0,0 @@
1
- 'use client'
2
-
3
- import * as React from 'react'
4
- import { ShoppingCart, 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 type { NotificationRendererProps } from '@open-mercato/shared/modules/notifications/types'
10
- import { formatMoney } from '../../components/documents/lineItemUtils'
11
- import { useSalesDocumentTotals } from './useSalesDocumentTotals'
12
-
13
- function formatTimeAgo(dateString: string, t: (key: string, fallback?: string) => string): string {
14
- const date = new Date(dateString)
15
- const now = new Date()
16
- const diffMs = now.getTime() - date.getTime()
17
- const diffMins = Math.floor(diffMs / 60000)
18
- const diffHours = Math.floor(diffMs / 3600000)
19
- const diffDays = Math.floor(diffMs / 86400000)
20
-
21
- if (diffMins < 1) return t('common.time.justNow', 'just now')
22
- if (diffMins < 60) return t('common.time.minutesAgo', '{count}m ago').replace('{count}', String(diffMins))
23
- if (diffHours < 24) return t('common.time.hoursAgo', '{count}h ago').replace('{count}', String(diffHours))
24
- if (diffDays < 7) return t('common.time.daysAgo', '{count}d ago').replace('{count}', String(diffDays))
25
- return date.toLocaleDateString()
26
- }
27
-
28
- function normalizeTotal(value?: string | null): string | null {
29
- if (!value) return null
30
- let trimmed = value.trim()
31
- if (trimmed.startsWith('(') && trimmed.endsWith(')')) {
32
- trimmed = trimmed.slice(1, -1).trim()
33
- }
34
- return trimmed.length ? trimmed : null
35
- }
36
-
37
- export function SalesOrderCreatedRenderer({
38
- notification,
39
- onAction,
40
- onDismiss,
41
- actions = [],
42
- }: NotificationRendererProps) {
43
- const t = useT()
44
- const router = useRouter()
45
- const [executing, setExecuting] = React.useState(false)
46
- const isUnread = notification.status === 'unread'
47
- const orderNumber = notification.bodyVariables?.orderNumber ?? notification.titleVariables?.orderNumber
48
- const fallbackTotal =
49
- normalizeTotal(notification.bodyVariables?.totalAmount ?? null) ??
50
- normalizeTotal(notification.bodyVariables?.total ?? null)
51
- const { totals } = useSalesDocumentTotals('order', notification.sourceEntityId)
52
-
53
- const currentTotal =
54
- totals && typeof totals.grandTotalGrossAmount === 'number'
55
- ? formatMoney(totals.grandTotalGrossAmount, totals.currencyCode)
56
- : fallbackTotal
57
-
58
- const viewAction = actions.find((action) => action.id === 'view') ?? actions[0] ?? null
59
-
60
- const handleView = async () => {
61
- if (!viewAction) {
62
- if (notification.linkHref) router.push(notification.linkHref)
63
- return
64
- }
65
- setExecuting(true)
66
- try {
67
- await onAction(viewAction.id)
68
- } finally {
69
- setExecuting(false)
70
- }
71
- }
72
-
73
- return (
74
- <div
75
- className={cn(
76
- 'group relative px-4 py-3 hover:bg-muted/50 cursor-pointer transition-colors border-l-4 border-l-blue-500',
77
- isUnread && 'bg-blue-50/50 dark:bg-blue-950/20'
78
- )}
79
- onClick={handleView}
80
- >
81
- {isUnread && (
82
- <div className="absolute left-1.5 top-1/2 -translate-y-1/2 h-2 w-2 rounded-full bg-primary" />
83
- )}
84
-
85
- <div className="flex gap-3">
86
- <div className="flex-shrink-0 mt-0.5">
87
- <div className="h-10 w-10 rounded-lg bg-blue-100 dark:bg-blue-900/40 flex items-center justify-center">
88
- <ShoppingCart className="h-5 w-5 text-blue-600 dark:text-blue-400" />
89
- </div>
90
- </div>
91
-
92
- <div className="flex-1 min-w-0">
93
- <div className="flex items-start justify-between gap-2">
94
- <div>
95
- <h4 className={cn('text-sm font-medium', isUnread && 'font-semibold')}>
96
- {notification.title}
97
- </h4>
98
- {orderNumber && (
99
- <div className="flex items-center gap-1 mt-0.5">
100
- <span className="text-xs font-mono text-muted-foreground bg-muted px-1.5 py-0.5 rounded">
101
- #{orderNumber}
102
- </span>
103
- </div>
104
- )}
105
- </div>
106
- <span className="flex-shrink-0 text-xs text-muted-foreground flex items-center gap-1">
107
- <Calendar className="h-3 w-3" />
108
- {formatTimeAgo(notification.createdAt, t)}
109
- </span>
110
- </div>
111
-
112
- <div className="mt-2 flex items-center gap-4 text-xs text-muted-foreground">
113
- {currentTotal && (
114
- <div className="flex items-center gap-1">
115
- <DollarSign className="h-3 w-3" />
116
- <span className="font-medium text-foreground">{currentTotal}</span>
117
- </div>
118
- )}
119
- <div className="flex items-center gap-1">
120
- <User className="h-3 w-3" />
121
- <span>{t('sales.notifications.renderer.assignedToYou', 'Assigned to you')}</span>
122
- </div>
123
- </div>
124
-
125
- <div className="mt-3 flex gap-2">
126
- <Button
127
- variant="default"
128
- size="sm"
129
- onClick={(e) => {
130
- e.stopPropagation()
131
- handleView()
132
- }}
133
- disabled={executing || (!viewAction && !notification.linkHref)}
134
- className="gap-1"
135
- >
136
- <ExternalLink className="h-3 w-3" />
137
- {t('sales.notifications.renderer.viewOrder', 'View Order')}
138
- </Button>
139
- <Button
140
- variant="ghost"
141
- size="sm"
142
- onClick={(e) => {
143
- e.stopPropagation()
144
- onDismiss()
145
- }}
146
- >
147
- {t('notifications.actions.dismiss', 'Dismiss')}
148
- </Button>
149
- </div>
150
- </div>
151
- </div>
152
- </div>
153
- )
154
- }
155
-
156
- export default SalesOrderCreatedRenderer
@@ -1,156 +0,0 @@
1
- 'use client'
2
-
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 type { NotificationRendererProps } from '@open-mercato/shared/modules/notifications/types'
10
- import { formatMoney } from '../../components/documents/lineItemUtils'
11
- import { useSalesDocumentTotals } from './useSalesDocumentTotals'
12
-
13
- function formatTimeAgo(dateString: string, t: (key: string, fallback?: string) => string): string {
14
- const date = new Date(dateString)
15
- const now = new Date()
16
- const diffMs = now.getTime() - date.getTime()
17
- const diffMins = Math.floor(diffMs / 60000)
18
- const diffHours = Math.floor(diffMs / 3600000)
19
- const diffDays = Math.floor(diffMs / 86400000)
20
-
21
- if (diffMins < 1) return t('common.time.justNow', 'just now')
22
- if (diffMins < 60) return t('common.time.minutesAgo', '{count}m ago').replace('{count}', String(diffMins))
23
- if (diffHours < 24) return t('common.time.hoursAgo', '{count}h ago').replace('{count}', String(diffHours))
24
- if (diffDays < 7) return t('common.time.daysAgo', '{count}d ago').replace('{count}', String(diffDays))
25
- return date.toLocaleDateString()
26
- }
27
-
28
- function normalizeTotal(value?: string | null): string | null {
29
- if (!value) return null
30
- let trimmed = value.trim()
31
- if (trimmed.startsWith('(') && trimmed.endsWith(')')) {
32
- trimmed = trimmed.slice(1, -1).trim()
33
- }
34
- return trimmed.length ? trimmed : null
35
- }
36
-
37
- export function SalesQuoteCreatedRenderer({
38
- notification,
39
- onAction,
40
- onDismiss,
41
- actions = [],
42
- }: NotificationRendererProps) {
43
- const t = useT()
44
- const router = useRouter()
45
- const [executing, setExecuting] = React.useState(false)
46
- const isUnread = notification.status === 'unread'
47
- const quoteNumber = notification.bodyVariables?.quoteNumber ?? notification.titleVariables?.quoteNumber
48
- const fallbackTotal =
49
- normalizeTotal(notification.bodyVariables?.totalAmount ?? null) ??
50
- normalizeTotal(notification.bodyVariables?.total ?? null)
51
- const { totals } = useSalesDocumentTotals('quote', notification.sourceEntityId)
52
-
53
- const currentTotal =
54
- totals && typeof totals.grandTotalGrossAmount === 'number'
55
- ? formatMoney(totals.grandTotalGrossAmount, totals.currencyCode)
56
- : fallbackTotal
57
-
58
- const viewAction = actions.find((action) => action.id === 'view') ?? actions[0] ?? null
59
-
60
- const handleView = async () => {
61
- if (!viewAction) {
62
- if (notification.linkHref) router.push(notification.linkHref)
63
- return
64
- }
65
- setExecuting(true)
66
- try {
67
- await onAction(viewAction.id)
68
- } finally {
69
- setExecuting(false)
70
- }
71
- }
72
-
73
- return (
74
- <div
75
- className={cn(
76
- 'group relative px-4 py-3 hover:bg-muted/50 cursor-pointer transition-colors border-l-4 border-l-amber-500',
77
- isUnread && 'bg-amber-50/50 dark:bg-amber-950/20'
78
- )}
79
- onClick={handleView}
80
- >
81
- {isUnread && (
82
- <div className="absolute left-1.5 top-1/2 -translate-y-1/2 h-2 w-2 rounded-full bg-primary" />
83
- )}
84
-
85
- <div className="flex gap-3">
86
- <div className="flex-shrink-0 mt-0.5">
87
- <div className="h-10 w-10 rounded-lg bg-amber-100 dark:bg-amber-900/40 flex items-center justify-center">
88
- <FileText className="h-5 w-5 text-amber-600 dark:text-amber-400" />
89
- </div>
90
- </div>
91
-
92
- <div className="flex-1 min-w-0">
93
- <div className="flex items-start justify-between gap-2">
94
- <div>
95
- <h4 className={cn('text-sm font-medium', isUnread && 'font-semibold')}>
96
- {notification.title}
97
- </h4>
98
- {quoteNumber && (
99
- <div className="flex items-center gap-1 mt-0.5">
100
- <span className="text-xs font-mono text-muted-foreground bg-muted px-1.5 py-0.5 rounded">
101
- #{quoteNumber}
102
- </span>
103
- </div>
104
- )}
105
- </div>
106
- <span className="flex-shrink-0 text-xs text-muted-foreground flex items-center gap-1">
107
- <Calendar className="h-3 w-3" />
108
- {formatTimeAgo(notification.createdAt, t)}
109
- </span>
110
- </div>
111
-
112
- <div className="mt-2 flex items-center gap-4 text-xs text-muted-foreground">
113
- {currentTotal && (
114
- <div className="flex items-center gap-1">
115
- <DollarSign className="h-3 w-3" />
116
- <span className="font-medium text-foreground">{currentTotal}</span>
117
- </div>
118
- )}
119
- <div className="flex items-center gap-1">
120
- <User className="h-3 w-3" />
121
- <span>{t('sales.notifications.renderer.pendingReview', 'Pending review')}</span>
122
- </div>
123
- </div>
124
-
125
- <div className="mt-3 flex gap-2">
126
- <Button
127
- variant="default"
128
- size="sm"
129
- onClick={(e) => {
130
- e.stopPropagation()
131
- handleView()
132
- }}
133
- disabled={executing || (!viewAction && !notification.linkHref)}
134
- className="gap-1"
135
- >
136
- <ExternalLink className="h-3 w-3" />
137
- {t('sales.notifications.renderer.viewQuote', 'View Quote')}
138
- </Button>
139
- <Button
140
- variant="ghost"
141
- size="sm"
142
- onClick={(e) => {
143
- e.stopPropagation()
144
- onDismiss()
145
- }}
146
- >
147
- {t('notifications.actions.dismiss', 'Dismiss')}
148
- </Button>
149
- </div>
150
- </div>
151
- </div>
152
- </div>
153
- )
154
- }
155
-
156
- export default SalesQuoteCreatedRenderer
@@ -1,2 +0,0 @@
1
- export { SalesOrderCreatedRenderer } from './SalesOrderCreatedRenderer'
2
- export { SalesQuoteCreatedRenderer } from './SalesQuoteCreatedRenderer'