@open-mercato/core 0.4.5-develop-636d33c995 → 0.4.5-develop-811deeb983

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 (136) hide show
  1. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js +17 -2
  2. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js.map +2 -2
  3. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +15 -0
  4. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  5. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +30 -0
  6. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
  7. package/dist/modules/catalog/lib/messageObjectPreviews.js +146 -0
  8. package/dist/modules/catalog/lib/messageObjectPreviews.js.map +7 -0
  9. package/dist/modules/catalog/message-objects.js +95 -0
  10. package/dist/modules/catalog/message-objects.js.map +7 -0
  11. package/dist/modules/currencies/backend/currencies/[id]/page.js +21 -0
  12. package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
  13. package/dist/modules/currencies/lib/messageObjectPreviews.js +51 -0
  14. package/dist/modules/currencies/lib/messageObjectPreviews.js.map +7 -0
  15. package/dist/modules/currencies/message-objects.js +41 -0
  16. package/dist/modules/currencies/message-objects.js.map +7 -0
  17. package/dist/modules/customers/backend/customers/companies/[id]/page.js +20 -0
  18. package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
  19. package/dist/modules/customers/backend/customers/deals/[id]/page.js +12 -1
  20. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  21. package/dist/modules/customers/backend/customers/people/[id]/page.js +20 -0
  22. package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
  23. package/dist/modules/customers/components/detail/CompanyHighlights.js +18 -14
  24. package/dist/modules/customers/components/detail/CompanyHighlights.js.map +2 -2
  25. package/dist/modules/customers/components/detail/PersonHighlights.js +18 -14
  26. package/dist/modules/customers/components/detail/PersonHighlights.js.map +2 -2
  27. package/dist/modules/customers/lib/messageObjectPreviews.js +41 -5
  28. package/dist/modules/customers/lib/messageObjectPreviews.js.map +2 -2
  29. package/dist/modules/customers/message-objects.js +31 -11
  30. package/dist/modules/customers/message-objects.js.map +2 -2
  31. package/dist/modules/messages/commands/messages.js +3 -0
  32. package/dist/modules/messages/commands/messages.js.map +2 -2
  33. package/dist/modules/messages/components/message-detail/panels/objects-panel.js +6 -1
  34. package/dist/modules/messages/components/message-detail/panels/objects-panel.js.map +2 -2
  35. package/dist/modules/messages/components/message-detail/panels/thread-panel.js +4 -1
  36. package/dist/modules/messages/components/message-detail/panels/thread-panel.js.map +2 -2
  37. package/dist/modules/messages/frontend/messages/view/[token]/page.js +1 -0
  38. package/dist/modules/messages/frontend/messages/view/[token]/page.js.map +2 -2
  39. package/dist/modules/resources/backend/resources/resources/[id]/page.js +24 -7
  40. package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
  41. package/dist/modules/resources/lib/messageObjectPreviews.js +43 -0
  42. package/dist/modules/resources/lib/messageObjectPreviews.js.map +7 -0
  43. package/dist/modules/resources/message-objects.js +37 -0
  44. package/dist/modules/resources/message-objects.js.map +7 -0
  45. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +19 -0
  46. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
  47. package/dist/modules/sales/backend/sales/documents/[id]/page.js +23 -2
  48. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  49. package/dist/modules/sales/backend/sales/quotes/[id]/page.js +1 -1
  50. package/dist/modules/sales/backend/sales/quotes/[id]/page.js.map +2 -2
  51. package/dist/modules/sales/lib/messageObjectPreviews.js +49 -4
  52. package/dist/modules/sales/lib/messageObjectPreviews.js.map +2 -2
  53. package/dist/modules/sales/message-objects.js +44 -2
  54. package/dist/modules/sales/message-objects.js.map +2 -2
  55. package/dist/modules/sales/widgets/messages/SalesDocumentMessageDetail.js +59 -30
  56. package/dist/modules/sales/widgets/messages/SalesDocumentMessageDetail.js.map +2 -2
  57. package/dist/modules/sales/widgets/messages/SalesDocumentMessagePreview.js +1 -1
  58. package/dist/modules/sales/widgets/messages/SalesDocumentMessagePreview.js.map +1 -1
  59. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +8 -30
  60. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
  61. package/dist/modules/staff/backend/staff/my-availability/page.js +13 -0
  62. package/dist/modules/staff/backend/staff/my-availability/page.js.map +2 -2
  63. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +8 -31
  64. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
  65. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +32 -10
  66. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  67. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js +14 -1
  68. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js.map +2 -2
  69. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +14 -1
  70. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  71. package/dist/modules/staff/components/TeamForm.js +4 -2
  72. package/dist/modules/staff/components/TeamForm.js.map +2 -2
  73. package/dist/modules/staff/components/TeamRoleForm.js +4 -2
  74. package/dist/modules/staff/components/TeamRoleForm.js.map +2 -2
  75. package/dist/modules/staff/lib/messageObjectPreviews.js +111 -2
  76. package/dist/modules/staff/lib/messageObjectPreviews.js.map +2 -2
  77. package/dist/modules/staff/message-objects.js +79 -8
  78. package/dist/modules/staff/message-objects.js.map +2 -2
  79. package/package.json +2 -2
  80. package/src/modules/catalog/backend/catalog/categories/[id]/edit/page.tsx +19 -5
  81. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +14 -0
  82. package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +40 -0
  83. package/src/modules/catalog/lib/messageObjectPreviews.ts +176 -0
  84. package/src/modules/catalog/message-objects.ts +102 -0
  85. package/src/modules/currencies/backend/currencies/[id]/page.tsx +20 -0
  86. package/src/modules/currencies/lib/messageObjectPreviews.ts +65 -0
  87. package/src/modules/currencies/message-objects.ts +40 -0
  88. package/src/modules/customers/backend/customers/companies/[id]/page.tsx +19 -0
  89. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +13 -0
  90. package/src/modules/customers/backend/customers/people/[id]/page.tsx +19 -0
  91. package/src/modules/customers/components/detail/CompanyHighlights.tsx +14 -9
  92. package/src/modules/customers/components/detail/PersonHighlights.tsx +14 -9
  93. package/src/modules/customers/lib/messageObjectPreviews.ts +43 -3
  94. package/src/modules/customers/message-objects.ts +31 -11
  95. package/src/modules/messages/commands/messages.ts +4 -0
  96. package/src/modules/messages/components/message-detail/panels/objects-panel.tsx +8 -1
  97. package/src/modules/messages/components/message-detail/panels/thread-panel.tsx +3 -0
  98. package/src/modules/messages/frontend/messages/view/[token]/page.tsx +1 -0
  99. package/src/modules/resources/backend/resources/resources/[id]/page.tsx +20 -4
  100. package/src/modules/resources/lib/messageObjectPreviews.ts +55 -0
  101. package/src/modules/resources/message-objects.ts +36 -0
  102. package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +18 -0
  103. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +23 -0
  104. package/src/modules/sales/backend/sales/quotes/[id]/page.tsx +1 -1
  105. package/src/modules/sales/lib/messageObjectPreviews.ts +54 -4
  106. package/src/modules/sales/message-objects.ts +44 -2
  107. package/src/modules/sales/widgets/messages/SalesDocumentMessageDetail.tsx +72 -34
  108. package/src/modules/sales/widgets/messages/SalesDocumentMessagePreview.tsx +1 -1
  109. package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +7 -29
  110. package/src/modules/staff/backend/staff/my-availability/page.tsx +14 -0
  111. package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +8 -30
  112. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +28 -7
  113. package/src/modules/staff/backend/staff/team-roles/[id]/edit/page.tsx +12 -0
  114. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +12 -0
  115. package/src/modules/staff/components/TeamForm.tsx +3 -0
  116. package/src/modules/staff/components/TeamRoleForm.tsx +3 -0
  117. package/src/modules/staff/lib/messageObjectPreviews.ts +133 -2
  118. package/src/modules/staff/message-objects.ts +79 -8
  119. package/dist/modules/customers/widgets/messages/CustomerMessageObjectDetail.js +0 -51
  120. package/dist/modules/customers/widgets/messages/CustomerMessageObjectDetail.js.map +0 -7
  121. package/dist/modules/customers/widgets/messages/CustomerMessageObjectPreview.js +0 -35
  122. package/dist/modules/customers/widgets/messages/CustomerMessageObjectPreview.js.map +0 -7
  123. package/dist/modules/customers/widgets/messages/index.js +0 -7
  124. package/dist/modules/customers/widgets/messages/index.js.map +0 -7
  125. package/dist/modules/staff/widgets/messages/StaffMessageObjectDetail.js +0 -51
  126. package/dist/modules/staff/widgets/messages/StaffMessageObjectDetail.js.map +0 -7
  127. package/dist/modules/staff/widgets/messages/StaffMessageObjectPreview.js +0 -34
  128. package/dist/modules/staff/widgets/messages/StaffMessageObjectPreview.js.map +0 -7
  129. package/dist/modules/staff/widgets/messages/index.js +0 -7
  130. package/dist/modules/staff/widgets/messages/index.js.map +0 -7
  131. package/src/modules/customers/widgets/messages/CustomerMessageObjectDetail.tsx +0 -57
  132. package/src/modules/customers/widgets/messages/CustomerMessageObjectPreview.tsx +0 -49
  133. package/src/modules/customers/widgets/messages/index.ts +0 -2
  134. package/src/modules/staff/widgets/messages/StaffMessageObjectDetail.tsx +0 -57
  135. package/src/modules/staff/widgets/messages/StaffMessageObjectPreview.tsx +0 -44
  136. package/src/modules/staff/widgets/messages/index.ts +0 -2
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/staff/message-objects.ts"],
4
- "sourcesContent": ["import type { MessageObjectTypeDefinition } from '@open-mercato/shared/modules/messages/types'\nimport { LeaveRequestDetail } from './components/LeaveRequestDetail'\nimport { LeaveRequestPreview } from './components/LeaveRequestPreview'\nimport { StaffMessageObjectDetail } from './widgets/messages/StaffMessageObjectDetail'\nimport { StaffMessageObjectPreview } from './widgets/messages/StaffMessageObjectPreview'\n\nexport const messageObjectTypes: MessageObjectTypeDefinition[] = [\n {\n module: 'staff',\n entityType: 'leave_request',\n messageTypes: ['default', 'messages.defaultWithObjects', 'staff.leave_request_approval', 'staff.leave_request_status'],\n entityId: 'staff:staff_leave_request',\n optionLabelField: 'id',\n optionSubtitleField: 'status',\n labelKey: 'staff.leaveRequests.page.title',\n icon: 'calendar-clock',\n PreviewComponent: LeaveRequestPreview,\n DetailComponent: LeaveRequestDetail,\n actions: [\n {\n id: 'approve',\n labelKey: 'staff.notifications.leaveRequest.actions.approve',\n variant: 'default',\n commandId: 'staff.leave-requests.accept',\n icon: 'check',\n },\n {\n id: 'reject',\n labelKey: 'staff.notifications.leaveRequest.actions.reject',\n variant: 'destructive',\n commandId: 'staff.leave-requests.reject',\n icon: 'x',\n },\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend/staff/leave-requests/{entityId}',\n icon: 'external-link',\n isTerminal: false,\n },\n ],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return {\n title: 'Leave request',\n subtitle: entityId,\n }\n }\n const { loadLeaveRequestPreview } = await import('./lib/messageObjectPreviews')\n return loadLeaveRequestPreview(entityId, ctx)\n },\n },\n {\n module: 'staff',\n entityType: 'team',\n messageTypes: ['default', 'messages.defaultWithObjects'],\n entityId: 'staff:staff_team',\n optionLabelField: 'name',\n optionSubtitleField: 'description',\n labelKey: 'staff.teams.page.title',\n icon: 'users',\n PreviewComponent: StaffMessageObjectPreview,\n DetailComponent: StaffMessageObjectDetail,\n actions: [],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return {\n title: 'Team',\n subtitle: entityId,\n }\n }\n const { loadTeamPreview } = await import('./lib/messageObjectPreviews')\n return loadTeamPreview(entityId, ctx)\n },\n },\n {\n module: 'staff',\n entityType: 'team_member',\n messageTypes: ['default', 'messages.defaultWithObjects'],\n entityId: 'staff:staff_team_member',\n optionLabelField: 'displayName',\n optionSubtitleField: 'email',\n labelKey: 'staff.teamMembers.page.title',\n icon: 'user-round',\n PreviewComponent: StaffMessageObjectPreview,\n DetailComponent: StaffMessageObjectDetail,\n actions: [],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return {\n title: 'Team member',\n subtitle: entityId,\n }\n }\n const { loadTeamMemberPreview } = await import('./lib/messageObjectPreviews')\n return loadTeamMemberPreview(entityId, ctx)\n },\n },\n]\n\nexport default messageObjectTypes\n"],
5
- "mappings": "AACA,SAAS,0BAA0B;AACnC,SAAS,2BAA2B;AACpC,SAAS,gCAAgC;AACzC,SAAS,iCAAiC;AAEnC,MAAM,qBAAoD;AAAA,EAC/D;AAAA,IACE,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,cAAc,CAAC,WAAW,+BAA+B,gCAAgC,4BAA4B;AAAA,IACrH,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,UAAU;AAAA,IACV,MAAM;AAAA,IACN,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,SAAS;AAAA,MACP;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,WAAW;AAAA,QACX,MAAM;AAAA,MACR;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,WAAW;AAAA,QACX,MAAM;AAAA,MACR;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,QACN,MAAM;AAAA,QACN,YAAY;AAAA,MACd;AAAA,IACF;AAAA,IACA,aAAa,OAAO,UAAU,QAAQ;AACpC,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO;AAAA,UACL,OAAO;AAAA,UACP,UAAU;AAAA,QACZ;AAAA,MACF;AACA,YAAM,EAAE,wBAAwB,IAAI,MAAM,OAAO,6BAA6B;AAC9E,aAAO,wBAAwB,UAAU,GAAG;AAAA,IAC9C;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,cAAc,CAAC,WAAW,6BAA6B;AAAA,IACvD,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,UAAU;AAAA,IACV,MAAM;AAAA,IACN,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,SAAS,CAAC;AAAA,IACV,aAAa,OAAO,UAAU,QAAQ;AACpC,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO;AAAA,UACL,OAAO;AAAA,UACP,UAAU;AAAA,QACZ;AAAA,MACF;AACA,YAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,6BAA6B;AACtE,aAAO,gBAAgB,UAAU,GAAG;AAAA,IACtC;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,cAAc,CAAC,WAAW,6BAA6B;AAAA,IACvD,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,UAAU;AAAA,IACV,MAAM;AAAA,IACN,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,SAAS,CAAC;AAAA,IACV,aAAa,OAAO,UAAU,QAAQ;AACpC,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO;AAAA,UACL,OAAO;AAAA,UACP,UAAU;AAAA,QACZ;AAAA,MACF;AACA,YAAM,EAAE,sBAAsB,IAAI,MAAM,OAAO,6BAA6B;AAC5E,aAAO,sBAAsB,UAAU,GAAG;AAAA,IAC5C;AAAA,EACF;AACF;AAEA,IAAO,0BAAQ;",
4
+ "sourcesContent": ["import type { MessageObjectTypeDefinition } from '@open-mercato/shared/modules/messages/types'\nimport { MessageObjectDetail, MessageObjectPreview } from '@open-mercato/ui/backend/messages'\nimport { LeaveRequestDetail } from './components/LeaveRequestDetail'\nimport { LeaveRequestPreview } from './components/LeaveRequestPreview'\n\nexport const messageObjectTypes: MessageObjectTypeDefinition[] = [\n {\n module: 'staff',\n entityType: 'leave_request',\n messageTypes: ['default', 'messages.defaultWithObjects', 'staff.leave_request_approval', 'staff.leave_request_status'],\n entityId: 'staff:staff_leave_request',\n optionLabelField: 'id',\n optionSubtitleField: 'status',\n labelKey: 'staff.leaveRequests.page.title',\n icon: 'calendar-clock',\n PreviewComponent: LeaveRequestPreview,\n DetailComponent: LeaveRequestDetail,\n actions: [\n {\n id: 'approve',\n labelKey: 'staff.notifications.leaveRequest.actions.approve',\n variant: 'default',\n commandId: 'staff.leave-requests.accept',\n icon: 'check',\n },\n {\n id: 'reject',\n labelKey: 'staff.notifications.leaveRequest.actions.reject',\n variant: 'destructive',\n commandId: 'staff.leave-requests.reject',\n icon: 'x',\n },\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend/staff/leave-requests/{entityId}',\n icon: 'external-link',\n isTerminal: false,\n },\n ],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return {\n title: 'Leave request',\n subtitle: entityId,\n }\n }\n const { loadLeaveRequestPreview } = await import('./lib/messageObjectPreviews')\n return loadLeaveRequestPreview(entityId, ctx)\n },\n },\n {\n module: 'staff',\n entityType: 'team',\n messageTypes: ['default', 'messages.defaultWithObjects'],\n entityId: 'staff:staff_team',\n optionLabelField: 'name',\n optionSubtitleField: 'description',\n labelKey: 'staff.teams.page.title',\n icon: 'users',\n PreviewComponent: MessageObjectPreview,\n DetailComponent: MessageObjectDetail,\n actions: [\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend/staff/teams/{entityId}/edit',\n icon: 'external-link',\n isTerminal: false,\n },\n ],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return {\n title: 'Team',\n subtitle: entityId,\n }\n }\n const { loadTeamPreview } = await import('./lib/messageObjectPreviews')\n return loadTeamPreview(entityId, ctx)\n },\n },\n {\n module: 'staff',\n entityType: 'team_member',\n messageTypes: ['default', 'messages.defaultWithObjects'],\n entityId: 'staff:staff_team_member',\n optionLabelField: 'displayName',\n optionSubtitleField: 'email',\n labelKey: 'staff.teamMembers.page.title',\n icon: 'user-round',\n PreviewComponent: MessageObjectPreview,\n DetailComponent: MessageObjectDetail,\n actions: [\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend/staff/team-members/{entityId}',\n icon: 'external-link',\n isTerminal: false,\n },\n ],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return {\n title: 'Team member',\n subtitle: entityId,\n }\n }\n const { loadTeamMemberPreview } = await import('./lib/messageObjectPreviews')\n return loadTeamMemberPreview(entityId, ctx)\n },\n },\n {\n module: 'staff',\n entityType: 'team_role',\n messageTypes: ['default', 'messages.defaultWithObjects'],\n entityId: 'staff:staff_team_role',\n optionLabelField: 'name',\n optionSubtitleField: 'description',\n labelKey: 'staff.messageObjects.teamRole.title',\n icon: 'shield',\n PreviewComponent: MessageObjectPreview,\n DetailComponent: MessageObjectDetail,\n actions: [\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend/staff/team-roles/{entityId}/edit',\n },\n ],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return { title: 'Team role', subtitle: entityId }\n }\n const { loadStaffTeamRolePreview } = await import('./lib/messageObjectPreviews')\n return loadStaffTeamRolePreview(entityId, ctx)\n },\n },\n {\n module: 'staff',\n entityType: 'my_availability',\n messageTypes: ['default', 'messages.defaultWithObjects'],\n entityId: 'planner:planner_availability_rule_set',\n optionLabelField: 'name',\n optionSubtitleField: 'description',\n labelKey: 'staff.messageObjects.myAvailability.title',\n icon: 'calendar-clock',\n PreviewComponent: MessageObjectPreview,\n DetailComponent: MessageObjectDetail,\n actions: [\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend/staff/my-availability',\n },\n ],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return { title: 'My availability', subtitle: entityId }\n }\n const { loadStaffAvailabilityPreview } = await import('./lib/messageObjectPreviews')\n return loadStaffAvailabilityPreview(entityId, ctx)\n },\n },\n]\n\nexport default messageObjectTypes\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.4.5-develop-636d33c995",
3
+ "version": "0.4.5-develop-811deeb983",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -207,7 +207,7 @@
207
207
  }
208
208
  },
209
209
  "dependencies": {
210
- "@open-mercato/shared": "0.4.5-develop-636d33c995",
210
+ "@open-mercato/shared": "0.4.5-develop-811deeb983",
211
211
  "@types/html-to-text": "^9.0.4",
212
212
  "@types/semver": "^7.5.8",
213
213
  "@xyflow/react": "^12.6.0",
@@ -11,6 +11,7 @@ import { useT } from '@open-mercato/shared/lib/i18n/context'
11
11
  import { E } from '#generated/entities.ids.generated'
12
12
  import { CategorySelect } from '../../../../../components/categories/CategorySelect'
13
13
  import { CategorySlugFieldSync } from '../../../../../components/categories/CategorySlugFieldSync'
14
+ import { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'
14
15
 
15
16
  type CategoryRow = {
16
17
  id: string
@@ -214,11 +215,24 @@ export default function EditCatalogCategoryPage({ params }: { params?: { id?: st
214
215
  submitLabel={t('catalog.categories.form.action.save', 'Save')}
215
216
  cancelHref="/backend/catalog/categories"
216
217
  successRedirect={`/backend/catalog/categories?flash=${encodeURIComponent(t('catalog.categories.flash.updated', 'Category updated'))}&type=success`}
217
- extraActions={pathLabel ? (
218
- <span className="text-xs text-muted-foreground">
219
- {t('catalog.categories.form.pathLabel', { path: pathLabel })}
220
- </span>
221
- ) : null}
218
+ extraActions={(
219
+ <>
220
+ <SendObjectMessageDialog
221
+ object={{
222
+ entityModule: 'catalog',
223
+ entityType: 'category',
224
+ entityId: categoryId,
225
+ previewData: { title: initialValues?.name ?? categoryId },
226
+ }}
227
+ viewHref={`/backend/catalog/categories/${categoryId}/edit`}
228
+ />
229
+ {pathLabel ? (
230
+ <span className="text-xs text-muted-foreground">
231
+ {t('catalog.categories.form.pathLabel', { path: pathLabel })}
232
+ </span>
233
+ ) : null}
234
+ </>
235
+ )}
222
236
  onSubmit={async (values) => {
223
237
  await submitCategoryUpdate(categoryId, values, t)
224
238
  }}
@@ -106,6 +106,7 @@ import {
106
106
  DialogHeader,
107
107
  DialogTitle,
108
108
  } from "@open-mercato/ui/primitives/dialog";
109
+ import { SendObjectMessageDialog } from "@open-mercato/ui/backend/messages/SendObjectMessageDialog.tsx";
109
110
 
110
111
  const MarkdownEditor = dynamic(() => import("@uiw/react-md-editor"), {
111
112
  ssr: false,
@@ -1204,6 +1205,19 @@ export default function EditCatalogProductPage({
1204
1205
  resourceKind: "catalog.product",
1205
1206
  resourceId: productId ? String(productId) : "",
1206
1207
  }}
1208
+ extraActions={productId ? (
1209
+ <SendObjectMessageDialog
1210
+ object={{
1211
+ entityModule: "catalog",
1212
+ entityType: "product",
1213
+ entityId: productId,
1214
+ previewData: {
1215
+ title: initialValues?.title ?? productId,
1216
+ }
1217
+ }}
1218
+ viewHref={`/backend/catalog/products/${productId}/edit`}
1219
+ />
1220
+ ) : undefined}
1207
1221
  fields={[]}
1208
1222
  groups={groups}
1209
1223
  injectionSpotId="crud-form:catalog.product"
@@ -11,6 +11,7 @@ import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/ap
11
11
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
12
12
  import { useT } from '@open-mercato/shared/lib/i18n/context'
13
13
  import { E } from '#generated/entities.ids.generated'
14
+ import { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'
14
15
  import {
15
16
  type VariantFormValues,
16
17
  type VariantPriceDraft,
@@ -61,6 +62,21 @@ type AttachmentListResponse = {
61
62
  items?: ProductMediaItem[]
62
63
  }
63
64
 
65
+ function resolveVariantPriceLabel(prices: Record<string, VariantPriceDraft> | undefined): string | null {
66
+ if (!prices || typeof prices !== 'object') return null
67
+ const entries = Object.values(prices)
68
+ for (const entry of entries) {
69
+ const amount = typeof entry?.amount === 'string' ? entry.amount.trim() : ''
70
+ if (!amount) continue
71
+ const currencyCode =
72
+ typeof entry.currencyCode === 'string' && entry.currencyCode.trim().length
73
+ ? entry.currencyCode.trim().toUpperCase()
74
+ : null
75
+ return currencyCode ? `${currencyCode} ${amount}` : amount
76
+ }
77
+ return null
78
+ }
79
+
64
80
  export default function EditVariantPage({ params }: { params?: { productId?: string; variantId?: string } }) {
65
81
  const router = useRouter()
66
82
  const t = useT()
@@ -407,6 +423,30 @@ export default function EditVariantPage({ params }: { params?: { productId?: str
407
423
  title={formTitle}
408
424
  backHref={productVariantsHref}
409
425
  versionHistory={{ resourceKind: 'catalog.variant', resourceId: variantId ? String(variantId) : '' }}
426
+ extraActions={(
427
+ <SendObjectMessageDialog
428
+ object={{
429
+ entityModule: 'catalog',
430
+ entityType: 'variant',
431
+ entityId: variantId,
432
+ previewData: {
433
+ title:
434
+ (typeof initialValues?.name === 'string' && initialValues.name.trim().length
435
+ ? initialValues.name
436
+ : variantId),
437
+ metadata: {
438
+ [t('catalog.variants.form.skuLabel')]:
439
+ (typeof initialValues?.sku === 'string' && initialValues.sku.trim().length
440
+ ? initialValues.sku
441
+ : '-'),
442
+ [t('catalog.variants.form.pricesLabel')]:
443
+ resolveVariantPriceLabel(initialValues?.prices) ?? '-',
444
+ },
445
+ },
446
+ }}
447
+ viewHref={`/backend/catalog/products/${currentProductId}/variants/${variantId}`}
448
+ />
449
+ )}
410
450
  fields={[]}
411
451
  groups={groups}
412
452
  entityId={E.catalog.catalog_product_variant}
@@ -0,0 +1,176 @@
1
+ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
2
+ import { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
3
+ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
4
+ import type { ObjectPreviewData } from '@open-mercato/shared/modules/messages/types'
5
+ import type { EntityManager } from '@mikro-orm/postgresql'
6
+ import { CatalogProduct, CatalogProductCategory, CatalogProductPrice, CatalogProductVariant } from '../data/entities'
7
+
8
+ type PreviewContext = {
9
+ tenantId: string
10
+ organizationId?: string | null
11
+ }
12
+
13
+ async function resolveEm() {
14
+ const { resolve } = await createRequestContainer()
15
+ return resolve('em') as EntityManager
16
+ }
17
+
18
+ function formatVariantPrice(amount: string | null | undefined, currencyCode: string | null | undefined): string | null {
19
+ if (!amount) return null
20
+ const value = Number(amount)
21
+ if (!Number.isFinite(value)) return currencyCode ? `${currencyCode.toUpperCase()} ${amount}` : amount
22
+ if (!currencyCode) return value.toLocaleString()
23
+ try {
24
+ return new Intl.NumberFormat(undefined, {
25
+ style: 'currency',
26
+ currency: currencyCode.toUpperCase(),
27
+ }).format(value)
28
+ } catch {
29
+ return `${currencyCode.toUpperCase()} ${value.toLocaleString()}`
30
+ }
31
+ }
32
+
33
+ export async function loadCatalogProductPreview(
34
+ entityId: string,
35
+ ctx: PreviewContext,
36
+ ): Promise<ObjectPreviewData> {
37
+ const { t } = await resolveTranslations()
38
+ const defaultTitle = t('catalog.messageObjects.product.title')
39
+
40
+ if (!ctx.organizationId) {
41
+ return { title: defaultTitle, subtitle: entityId }
42
+ }
43
+
44
+ const em = await resolveEm()
45
+ const entity = await findOneWithDecryption(
46
+ em,
47
+ CatalogProduct,
48
+ {
49
+ id: entityId,
50
+ tenantId: ctx.tenantId,
51
+ organizationId: ctx.organizationId,
52
+ deletedAt: null,
53
+ },
54
+ undefined,
55
+ { tenantId: ctx.tenantId, organizationId: ctx.organizationId },
56
+ )
57
+
58
+ if (!entity) {
59
+ return {
60
+ title: defaultTitle,
61
+ subtitle: entityId,
62
+ status: t('customers.messageObjects.notFound'),
63
+ statusColor: 'gray',
64
+ }
65
+ }
66
+
67
+ return {
68
+ title: entity.title,
69
+ subtitle: entity.subtitle ?? undefined,
70
+ }
71
+ }
72
+
73
+ export async function loadCatalogVariantPreview(
74
+ entityId: string,
75
+ ctx: PreviewContext,
76
+ ): Promise<ObjectPreviewData> {
77
+ const { t } = await resolveTranslations()
78
+ const defaultTitle = t('catalog.variants.form.editTitle')
79
+
80
+ if (!ctx.organizationId) {
81
+ return { title: defaultTitle, subtitle: entityId }
82
+ }
83
+
84
+ const em = await resolveEm()
85
+ const variant = await findOneWithDecryption(
86
+ em,
87
+ CatalogProductVariant,
88
+ {
89
+ id: entityId,
90
+ tenantId: ctx.tenantId,
91
+ organizationId: ctx.organizationId,
92
+ deletedAt: null,
93
+ },
94
+ undefined,
95
+ { tenantId: ctx.tenantId, organizationId: ctx.organizationId },
96
+ )
97
+
98
+ if (!variant) {
99
+ return {
100
+ title: defaultTitle,
101
+ subtitle: entityId,
102
+ status: t('catalog.variants.form.errors.notFound'),
103
+ statusColor: 'gray',
104
+ }
105
+ }
106
+
107
+ const prices = await findWithDecryption(
108
+ em,
109
+ CatalogProductPrice,
110
+ {
111
+ variant,
112
+ tenantId: ctx.tenantId,
113
+ organizationId: ctx.organizationId,
114
+ },
115
+ { orderBy: { createdAt: 'ASC' }, limit: 10 },
116
+ { tenantId: ctx.tenantId, organizationId: ctx.organizationId },
117
+ )
118
+
119
+ const firstPrice = prices.find((entry) => Boolean(entry.unitPriceGross || entry.unitPriceNet))
120
+ const priceLabel = formatVariantPrice(
121
+ firstPrice?.unitPriceGross ?? firstPrice?.unitPriceNet ?? null,
122
+ firstPrice?.currencyCode ?? null,
123
+ )
124
+
125
+ const metadata: Record<string, string> = {}
126
+ const skuLabel = t('catalog.variants.form.skuLabel')
127
+ const pricesLabel = t('catalog.variants.form.pricesLabel')
128
+ if (variant.sku && variant.sku.trim().length > 0) metadata[skuLabel] = variant.sku
129
+ if (priceLabel) metadata[pricesLabel] = priceLabel
130
+
131
+ return {
132
+ title: variant.name ?? '',
133
+ subtitle: variant.sku ?? undefined,
134
+ metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
135
+ }
136
+ }
137
+
138
+ export async function loadCatalogCategoryPreview(
139
+ entityId: string,
140
+ ctx: PreviewContext,
141
+ ): Promise<ObjectPreviewData> {
142
+ const { t } = await resolveTranslations()
143
+ const defaultTitle = t('catalog.messageObjects.category.title')
144
+
145
+ if (!ctx.organizationId) {
146
+ return { title: defaultTitle, subtitle: entityId }
147
+ }
148
+
149
+ const em = await resolveEm()
150
+ const entity = await findOneWithDecryption(
151
+ em,
152
+ CatalogProductCategory,
153
+ {
154
+ id: entityId,
155
+ tenantId: ctx.tenantId,
156
+ organizationId: ctx.organizationId,
157
+ deletedAt: null,
158
+ },
159
+ undefined,
160
+ { tenantId: ctx.tenantId, organizationId: ctx.organizationId },
161
+ )
162
+
163
+ if (!entity) {
164
+ return {
165
+ title: defaultTitle,
166
+ subtitle: entityId,
167
+ status: t('customers.messageObjects.notFound'),
168
+ statusColor: 'gray',
169
+ }
170
+ }
171
+
172
+ return {
173
+ title: entity.name,
174
+ subtitle: entity.description ?? undefined,
175
+ }
176
+ }
@@ -0,0 +1,102 @@
1
+ import type { MessageObjectTypeDefinition } from '@open-mercato/shared/modules/messages/types'
2
+ import { MessageObjectDetail, MessageObjectPreview } from '@open-mercato/ui/backend/messages'
3
+
4
+ const objectMessageTypes = ['default', 'messages.defaultWithObjects']
5
+
6
+ export const messageObjectTypes: MessageObjectTypeDefinition[] = [
7
+ {
8
+ module: 'catalog',
9
+ entityType: 'product',
10
+ messageTypes: objectMessageTypes,
11
+ entityId: 'catalog:catalog_product',
12
+ optionLabelField: 'title',
13
+ optionSubtitleField: 'subtitle',
14
+ labelKey: 'catalog.messageObjects.product.title',
15
+ icon: 'package',
16
+ PreviewComponent: MessageObjectPreview,
17
+ DetailComponent: MessageObjectDetail,
18
+ actions: [
19
+ {
20
+ id: 'view',
21
+ labelKey: 'common.view',
22
+ variant: 'outline',
23
+ href: '/backend/catalog/products/{entityId}',
24
+ },
25
+ ],
26
+ loadPreview: async (entityId, ctx) => {
27
+ if (typeof window !== 'undefined') {
28
+ return { title: 'Product', subtitle: entityId }
29
+ }
30
+ const previews = await import('./lib/messageObjectPreviews')
31
+ const productLoader = (
32
+ previews as typeof previews & {
33
+ loadCatalogProductPreview?: (id: string, previewCtx: typeof ctx) => Promise<{ title: string; subtitle?: string }>
34
+ }
35
+ ).loadCatalogProductPreview
36
+ if (productLoader) return productLoader(entityId, ctx)
37
+ return previews.loadCatalogCategoryPreview(entityId, ctx)
38
+ },
39
+ },
40
+ {
41
+ module: 'catalog',
42
+ entityType: 'variant',
43
+ messageTypes: objectMessageTypes,
44
+ entityId: 'catalog:catalog_product_variant',
45
+ optionLabelField: 'name',
46
+ optionSubtitleField: 'sku',
47
+ labelKey: 'catalog.variants.form.editTitle',
48
+ icon: 'package-plus',
49
+ PreviewComponent: MessageObjectPreview,
50
+ DetailComponent: MessageObjectDetail,
51
+ actions: [
52
+ {
53
+ id: 'view',
54
+ labelKey: 'common.view',
55
+ variant: 'outline',
56
+ href: '/backend/catalog/products',
57
+ },
58
+ ],
59
+ loadPreview: async (entityId, ctx) => {
60
+ if (typeof window !== 'undefined') {
61
+ return { title: 'Variant', subtitle: entityId }
62
+ }
63
+ const previews = await import('./lib/messageObjectPreviews')
64
+ const variantLoader = (
65
+ previews as typeof previews & {
66
+ loadCatalogVariantPreview?: (id: string, previewCtx: typeof ctx) => Promise<{ title: string; subtitle?: string }>
67
+ }
68
+ ).loadCatalogVariantPreview
69
+ if (variantLoader) return variantLoader(entityId, ctx)
70
+ return previews.loadCatalogProductPreview(entityId, ctx)
71
+ },
72
+ },
73
+ {
74
+ module: 'catalog',
75
+ entityType: 'category',
76
+ messageTypes: objectMessageTypes,
77
+ entityId: 'catalog:catalog_product_category',
78
+ optionLabelField: 'name',
79
+ optionSubtitleField: 'description',
80
+ labelKey: 'catalog.messageObjects.category.title',
81
+ icon: 'tag',
82
+ PreviewComponent: MessageObjectPreview,
83
+ DetailComponent: MessageObjectDetail,
84
+ actions: [
85
+ {
86
+ id: 'view',
87
+ labelKey: 'common.view',
88
+ variant: 'outline',
89
+ href: '/backend/catalog/categories/{entityId}/edit',
90
+ },
91
+ ],
92
+ loadPreview: async (entityId, ctx) => {
93
+ if (typeof window !== 'undefined') {
94
+ return { title: 'Category', subtitle: entityId }
95
+ }
96
+ const { loadCatalogCategoryPreview } = await import('./lib/messageObjectPreviews')
97
+ return loadCatalogCategoryPreview(entityId, ctx)
98
+ },
99
+ },
100
+ ]
101
+
102
+ export default messageObjectTypes
@@ -9,6 +9,7 @@ import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors
9
9
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
10
10
  import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
11
11
  import { useT } from '@open-mercato/shared/lib/i18n/context'
12
+ import { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'
12
13
  import { DataLoader } from '@open-mercato/ui/primitives/DataLoader'
13
14
  import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
14
15
 
@@ -180,6 +181,25 @@ export default function EditCurrencyPage({ params }: { params?: { id?: string }
180
181
  title={t('currencies.edit.title')}
181
182
  backHref="/backend/currencies"
182
183
  versionHistory={{ resourceKind: 'currencies.currency', resourceId: currency.id }}
184
+ extraActions={(
185
+ <SendObjectMessageDialog
186
+ object={{
187
+ entityModule: 'currencies',
188
+ entityType: 'currency',
189
+ entityId: currency.id,
190
+ previewData: {
191
+ title: currency.name,
192
+ subtitle: currency.code,
193
+ metadata: {
194
+ [t('currencies.form.field.code')]: currency.code,
195
+ [t('currencies.form.field.name')]: currency.name,
196
+ [t('currencies.form.field.symbol')]: currency.symbol || '-',
197
+ },
198
+ },
199
+ }}
200
+ viewHref={`/backend/currencies/${currency.id}`}
201
+ />
202
+ )}
183
203
  fields={[]}
184
204
  groups={groups}
185
205
  initialValues={{
@@ -0,0 +1,65 @@
1
+ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
2
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
3
+ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
4
+ import type { ObjectPreviewData } from '@open-mercato/shared/modules/messages/types'
5
+ import type { EntityManager } from '@mikro-orm/postgresql'
6
+ import { Currency } from '../data/entities'
7
+
8
+ type PreviewContext = {
9
+ tenantId: string
10
+ organizationId?: string | null
11
+ }
12
+
13
+ async function resolveEm() {
14
+ const { resolve } = await createRequestContainer()
15
+ return resolve('em') as EntityManager
16
+ }
17
+
18
+ export async function loadCurrencyPreview(
19
+ entityId: string,
20
+ ctx: PreviewContext,
21
+ ): Promise<ObjectPreviewData> {
22
+ const { t } = await resolveTranslations()
23
+ const defaultTitle = t('currencies.messageObjects.currency.title')
24
+
25
+ if (!ctx.organizationId) {
26
+ return { title: defaultTitle, subtitle: entityId }
27
+ }
28
+
29
+ const em = await resolveEm()
30
+ const entity = await findOneWithDecryption(
31
+ em,
32
+ Currency,
33
+ {
34
+ id: entityId,
35
+ tenantId: ctx.tenantId,
36
+ organizationId: ctx.organizationId,
37
+ deletedAt: null,
38
+ },
39
+ undefined,
40
+ { tenantId: ctx.tenantId, organizationId: ctx.organizationId },
41
+ )
42
+
43
+ if (!entity) {
44
+ return {
45
+ title: defaultTitle,
46
+ subtitle: entityId,
47
+ status: t('customers.messageObjects.notFound'),
48
+ statusColor: 'gray',
49
+ }
50
+ }
51
+
52
+ const subtitleParts = [entity.code, entity.symbol]
53
+ .filter((part): part is string => Boolean(part && part.trim().length > 0))
54
+ const metadata: Record<string, string> = {}
55
+ const codeLabel = t('currencies.form.field.code')
56
+ const symbolLabel = t('currencies.form.field.symbol')
57
+ if (entity.code) metadata[codeLabel] = entity.code
58
+ if (entity.symbol) metadata[symbolLabel] = entity.symbol
59
+
60
+ return {
61
+ title: entity.name,
62
+ subtitle: subtitleParts.join(' · ') || entityId,
63
+ metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
64
+ }
65
+ }
@@ -0,0 +1,40 @@
1
+ import type { MessageObjectTypeDefinition } from '@open-mercato/shared/modules/messages/types'
2
+ import { MessageObjectDetail, MessageObjectPreview } from '@open-mercato/ui/backend/messages'
3
+
4
+ const objectMessageTypes = ['default', 'messages.defaultWithObjects']
5
+
6
+ export const messageObjectTypes: MessageObjectTypeDefinition[] = [
7
+ {
8
+ module: 'currencies',
9
+ entityType: 'currency',
10
+ messageTypes: objectMessageTypes,
11
+ entityId: 'currencies:currency',
12
+ optionLabelField: 'name',
13
+ optionSubtitleField: 'code',
14
+ labelKey: 'currencies.messageObjects.currency.title',
15
+ icon: 'coins',
16
+ PreviewComponent: MessageObjectPreview,
17
+ DetailComponent: MessageObjectDetail,
18
+ actions: [
19
+ {
20
+ id: 'view',
21
+ labelKey: 'common.view',
22
+ variant: 'outline',
23
+ href: '/backend/currencies/{entityId}',
24
+ },
25
+ ],
26
+ loadPreview: async (entityId, ctx) => {
27
+ if (typeof window !== 'undefined') {
28
+ return {
29
+ title: 'Currency',
30
+ subtitle: entityId,
31
+ metadata: { id: entityId },
32
+ }
33
+ }
34
+ const { loadCurrencyPreview } = await import('./lib/messageObjectPreviews')
35
+ return loadCurrencyPreview(entityId, ctx)
36
+ },
37
+ },
38
+ ]
39
+
40
+ export default messageObjectTypes
@@ -49,6 +49,7 @@ import { readMarkdownPreferenceCookie, writeMarkdownPreferenceCookie } from '../
49
49
  import { InjectionSpot, useInjectionWidgets } from '@open-mercato/ui/backend/injection/InjectionSpot'
50
50
  import { DetailTabsLayout } from '../../../../components/detail/DetailTabsLayout'
51
51
  import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'
52
+ import { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'
52
53
 
53
54
  type CompanyOverview = {
54
55
  company: {
@@ -730,6 +731,24 @@ export default function CustomerCompanyDetailPage({ params }: { params?: { id?:
730
731
  profile={profile ?? null}
731
732
  validators={validators}
732
733
  onDisplayNameSave={updateDisplayName}
734
+ utilityActions={(
735
+ <SendObjectMessageDialog
736
+ object={{
737
+ entityModule: 'customers',
738
+ entityType: 'company',
739
+ entityId: companyId,
740
+ previewData: {
741
+ title: company.displayName,
742
+ subtitle: company.primaryEmail ?? undefined,
743
+ metadata: {
744
+ [t('customers.companies.detail.highlights.primaryPhone')]: company.primaryPhone ?? '-',
745
+ [t('customers.companies.detail.fields.industry')]: profile?.industry ?? '-',
746
+ },
747
+ },
748
+ }}
749
+ viewHref={`/backend/customers/companies/${companyId}`}
750
+ />
751
+ )}
733
752
  onPrimaryEmailSave={(value) => updateCompanyField('primaryEmail', value)}
734
753
  onPrimaryPhoneSave={(value) => updateCompanyField('primaryPhone', value)}
735
754
  onStatusSave={(value) => updateCompanyField('status', value)}
@@ -380,6 +380,13 @@ export default function DealDetailPage({ params }: { params?: { id?: string } })
380
380
  const statusDictEntry = data.deal.status ? statusDictionaryMap?.[data.deal.status] ?? null : null
381
381
  const pipelineLabel = resolveDictionaryLabel(data.deal.pipelineStage, pipelineDictionaryMap)
382
382
  const pipelineDictEntry = data.deal.pipelineStage ? pipelineDictionaryMap?.[data.deal.pipelineStage] ?? null : null
383
+ const previewValueAmount = formatCurrency(data.deal.valueAmount, data.deal.valueCurrency)
384
+ const previewProbability = data.deal.probability !== null && data.deal.probability !== undefined
385
+ ? `${data.deal.probability}%`
386
+ : null
387
+ const dealPreviewMetadata: Record<string, string> = {}
388
+ if (previewValueAmount) dealPreviewMetadata[t('customers.deals.detail.fields.value')] = previewValueAmount
389
+ if (previewProbability) dealPreviewMetadata[t('customers.deals.detail.fields.probability')] = previewProbability
383
390
 
384
391
  const peopleSummaryLabel =
385
392
  data.people.length === 1
@@ -409,7 +416,13 @@ export default function DealDetailPage({ params }: { params?: { id?: string } })
409
416
  entityId: data.deal.id,
410
417
  sourceEntityType: 'customers.deal',
411
418
  sourceEntityId: data.deal.id,
419
+ previewData: {
420
+ title: data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal'),
421
+ status: data.deal.status ? statusLabel : undefined,
422
+ metadata: Object.keys(dealPreviewMetadata).length > 0 ? dealPreviewMetadata : undefined,
423
+ },
412
424
  }}
425
+ viewHref={`/backend/customers/deals/${data.deal.id}`}
413
426
  defaultValues={{
414
427
  sourceEntityType: 'customers.deal',
415
428
  sourceEntityId: data.deal.id,