@open-mercato/core 0.4.5-develop-610fbb24ec → 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 (141) 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/data/validators.js +4 -3
  8. package/dist/modules/catalog/data/validators.js.map +2 -2
  9. package/dist/modules/catalog/lib/messageObjectPreviews.js +146 -0
  10. package/dist/modules/catalog/lib/messageObjectPreviews.js.map +7 -0
  11. package/dist/modules/catalog/message-objects.js +95 -0
  12. package/dist/modules/catalog/message-objects.js.map +7 -0
  13. package/dist/modules/currencies/backend/currencies/[id]/page.js +21 -0
  14. package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
  15. package/dist/modules/currencies/lib/messageObjectPreviews.js +51 -0
  16. package/dist/modules/currencies/lib/messageObjectPreviews.js.map +7 -0
  17. package/dist/modules/currencies/message-objects.js +41 -0
  18. package/dist/modules/currencies/message-objects.js.map +7 -0
  19. package/dist/modules/customers/backend/customers/companies/[id]/page.js +20 -0
  20. package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
  21. package/dist/modules/customers/backend/customers/deals/[id]/page.js +12 -1
  22. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  23. package/dist/modules/customers/backend/customers/people/[id]/page.js +20 -0
  24. package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
  25. package/dist/modules/customers/components/detail/CompanyHighlights.js +18 -14
  26. package/dist/modules/customers/components/detail/CompanyHighlights.js.map +2 -2
  27. package/dist/modules/customers/components/detail/PersonHighlights.js +18 -14
  28. package/dist/modules/customers/components/detail/PersonHighlights.js.map +2 -2
  29. package/dist/modules/customers/lib/messageObjectPreviews.js +41 -5
  30. package/dist/modules/customers/lib/messageObjectPreviews.js.map +2 -2
  31. package/dist/modules/customers/message-objects.js +31 -11
  32. package/dist/modules/customers/message-objects.js.map +2 -2
  33. package/dist/modules/messages/commands/messages.js +3 -0
  34. package/dist/modules/messages/commands/messages.js.map +2 -2
  35. package/dist/modules/messages/components/message-detail/panels/objects-panel.js +6 -1
  36. package/dist/modules/messages/components/message-detail/panels/objects-panel.js.map +2 -2
  37. package/dist/modules/messages/components/message-detail/panels/thread-panel.js +4 -1
  38. package/dist/modules/messages/components/message-detail/panels/thread-panel.js.map +2 -2
  39. package/dist/modules/messages/frontend/messages/view/[token]/page.js +1 -0
  40. package/dist/modules/messages/frontend/messages/view/[token]/page.js.map +2 -2
  41. package/dist/modules/resources/backend/resources/resources/[id]/page.js +24 -7
  42. package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
  43. package/dist/modules/resources/lib/messageObjectPreviews.js +43 -0
  44. package/dist/modules/resources/lib/messageObjectPreviews.js.map +7 -0
  45. package/dist/modules/resources/message-objects.js +37 -0
  46. package/dist/modules/resources/message-objects.js.map +7 -0
  47. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +19 -0
  48. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
  49. package/dist/modules/sales/backend/sales/documents/[id]/page.js +23 -2
  50. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  51. package/dist/modules/sales/backend/sales/quotes/[id]/page.js +1 -1
  52. package/dist/modules/sales/backend/sales/quotes/[id]/page.js.map +2 -2
  53. package/dist/modules/sales/lib/messageObjectPreviews.js +49 -4
  54. package/dist/modules/sales/lib/messageObjectPreviews.js.map +2 -2
  55. package/dist/modules/sales/message-objects.js +44 -2
  56. package/dist/modules/sales/message-objects.js.map +2 -2
  57. package/dist/modules/sales/widgets/messages/SalesDocumentMessageDetail.js +59 -30
  58. package/dist/modules/sales/widgets/messages/SalesDocumentMessageDetail.js.map +2 -2
  59. package/dist/modules/sales/widgets/messages/SalesDocumentMessagePreview.js +1 -1
  60. package/dist/modules/sales/widgets/messages/SalesDocumentMessagePreview.js.map +1 -1
  61. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +8 -30
  62. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
  63. package/dist/modules/staff/backend/staff/my-availability/page.js +13 -0
  64. package/dist/modules/staff/backend/staff/my-availability/page.js.map +2 -2
  65. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +8 -31
  66. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
  67. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +32 -10
  68. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  69. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js +14 -1
  70. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js.map +2 -2
  71. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +14 -1
  72. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  73. package/dist/modules/staff/components/TeamForm.js +4 -2
  74. package/dist/modules/staff/components/TeamForm.js.map +2 -2
  75. package/dist/modules/staff/components/TeamRoleForm.js +4 -2
  76. package/dist/modules/staff/components/TeamRoleForm.js.map +2 -2
  77. package/dist/modules/staff/lib/messageObjectPreviews.js +111 -2
  78. package/dist/modules/staff/lib/messageObjectPreviews.js.map +2 -2
  79. package/dist/modules/staff/message-objects.js +79 -8
  80. package/dist/modules/staff/message-objects.js.map +2 -2
  81. package/package.json +3 -3
  82. package/src/modules/catalog/backend/catalog/categories/[id]/edit/page.tsx +19 -5
  83. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +14 -0
  84. package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +40 -0
  85. package/src/modules/catalog/data/validators.ts +47 -45
  86. package/src/modules/catalog/lib/messageObjectPreviews.ts +176 -0
  87. package/src/modules/catalog/message-objects.ts +102 -0
  88. package/src/modules/currencies/backend/currencies/[id]/page.tsx +20 -0
  89. package/src/modules/currencies/lib/messageObjectPreviews.ts +65 -0
  90. package/src/modules/currencies/message-objects.ts +40 -0
  91. package/src/modules/customers/backend/customers/companies/[id]/page.tsx +19 -0
  92. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +13 -0
  93. package/src/modules/customers/backend/customers/people/[id]/page.tsx +19 -0
  94. package/src/modules/customers/components/detail/CompanyHighlights.tsx +14 -9
  95. package/src/modules/customers/components/detail/PersonHighlights.tsx +14 -9
  96. package/src/modules/customers/lib/messageObjectPreviews.ts +43 -3
  97. package/src/modules/customers/message-objects.ts +31 -11
  98. package/src/modules/customers/migrations/.snapshot-open-mercato.json +236 -0
  99. package/src/modules/customers/migrations/.snapshot-openmercato.json +236 -0
  100. package/src/modules/messages/commands/messages.ts +4 -0
  101. package/src/modules/messages/components/message-detail/panels/objects-panel.tsx +8 -1
  102. package/src/modules/messages/components/message-detail/panels/thread-panel.tsx +3 -0
  103. package/src/modules/messages/frontend/messages/view/[token]/page.tsx +1 -0
  104. package/src/modules/resources/backend/resources/resources/[id]/page.tsx +20 -4
  105. package/src/modules/resources/lib/messageObjectPreviews.ts +55 -0
  106. package/src/modules/resources/message-objects.ts +36 -0
  107. package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +18 -0
  108. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +23 -0
  109. package/src/modules/sales/backend/sales/quotes/[id]/page.tsx +1 -1
  110. package/src/modules/sales/lib/messageObjectPreviews.ts +54 -4
  111. package/src/modules/sales/message-objects.ts +44 -2
  112. package/src/modules/sales/widgets/messages/SalesDocumentMessageDetail.tsx +72 -34
  113. package/src/modules/sales/widgets/messages/SalesDocumentMessagePreview.tsx +1 -1
  114. package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +7 -29
  115. package/src/modules/staff/backend/staff/my-availability/page.tsx +14 -0
  116. package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +8 -30
  117. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +28 -7
  118. package/src/modules/staff/backend/staff/team-roles/[id]/edit/page.tsx +12 -0
  119. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +12 -0
  120. package/src/modules/staff/components/TeamForm.tsx +3 -0
  121. package/src/modules/staff/components/TeamRoleForm.tsx +3 -0
  122. package/src/modules/staff/lib/messageObjectPreviews.ts +133 -2
  123. package/src/modules/staff/message-objects.ts +79 -8
  124. package/dist/modules/customers/widgets/messages/CustomerMessageObjectDetail.js +0 -51
  125. package/dist/modules/customers/widgets/messages/CustomerMessageObjectDetail.js.map +0 -7
  126. package/dist/modules/customers/widgets/messages/CustomerMessageObjectPreview.js +0 -35
  127. package/dist/modules/customers/widgets/messages/CustomerMessageObjectPreview.js.map +0 -7
  128. package/dist/modules/customers/widgets/messages/index.js +0 -7
  129. package/dist/modules/customers/widgets/messages/index.js.map +0 -7
  130. package/dist/modules/staff/widgets/messages/StaffMessageObjectDetail.js +0 -51
  131. package/dist/modules/staff/widgets/messages/StaffMessageObjectDetail.js.map +0 -7
  132. package/dist/modules/staff/widgets/messages/StaffMessageObjectPreview.js +0 -34
  133. package/dist/modules/staff/widgets/messages/StaffMessageObjectPreview.js.map +0 -7
  134. package/dist/modules/staff/widgets/messages/index.js +0 -7
  135. package/dist/modules/staff/widgets/messages/index.js.map +0 -7
  136. package/src/modules/customers/widgets/messages/CustomerMessageObjectDetail.tsx +0 -57
  137. package/src/modules/customers/widgets/messages/CustomerMessageObjectPreview.tsx +0 -49
  138. package/src/modules/customers/widgets/messages/index.ts +0 -2
  139. package/src/modules/staff/widgets/messages/StaffMessageObjectDetail.tsx +0 -57
  140. package/src/modules/staff/widgets/messages/StaffMessageObjectPreview.tsx +0 -44
  141. package/src/modules/staff/widgets/messages/index.ts +0 -2
@@ -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,
@@ -48,6 +48,7 @@ import { readMarkdownPreferenceCookie, writeMarkdownPreferenceCookie } from '../
48
48
  import { InjectionSpot, useInjectionWidgets } from '@open-mercato/ui/backend/injection/InjectionSpot'
49
49
  import { DetailTabsLayout } from '../../../../components/detail/DetailTabsLayout'
50
50
  import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'
51
+ import { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'
51
52
 
52
53
  type PersonOverview = {
53
54
  person: {
@@ -656,6 +657,24 @@ export default function CustomerPersonDetailPage({ params }: { params?: { id?: s
656
657
  phone: validators.phone,
657
658
  displayName: validators.displayName,
658
659
  }}
660
+ utilityActions={(
661
+ <SendObjectMessageDialog
662
+ object={{
663
+ entityModule: 'customers',
664
+ entityType: 'person',
665
+ entityId: personId,
666
+ previewData: {
667
+ title: person.displayName,
668
+ subtitle: person.primaryEmail ?? undefined,
669
+ metadata: {
670
+ [t('customers.people.detail.highlights.primaryPhone')]: person.primaryPhone ?? '-',
671
+ [t('customers.people.detail.fields.jobTitle')]: profile?.jobTitle ?? '-',
672
+ },
673
+ },
674
+ }}
675
+ viewHref={`/backend/customers/people/${personId}`}
676
+ />
677
+ )}
659
678
  onDisplayNameSave={updateDisplayName}
660
679
  onPrimaryEmailSave={async (next) => {
661
680
  const send = typeof next === 'string' ? next : ''
@@ -52,6 +52,7 @@ export type CompanyHighlightsProps = {
52
52
  onNextInteractionSave: (payload: NextInteractionPayload | null) => Promise<void>
53
53
  onDelete: () => void
54
54
  isDeleting: boolean
55
+ utilityActions?: React.ReactNode
55
56
  }
56
57
 
57
58
  export function CompanyHighlights({
@@ -65,6 +66,7 @@ export function CompanyHighlights({
65
66
  onNextInteractionSave,
66
67
  onDelete,
67
68
  isDeleting,
69
+ utilityActions,
68
70
  }: CompanyHighlightsProps) {
69
71
  const t = useT()
70
72
  const historyFallbackId =
@@ -77,15 +79,18 @@ export function CompanyHighlights({
77
79
  backHref="/backend/customers/companies"
78
80
  backLabel={t('customers.companies.detail.actions.backToList', 'Back to companies')}
79
81
  utilityActions={(
80
- <VersionHistoryAction
81
- config={{
82
- resourceKind: 'customers.company',
83
- resourceId: company.id,
84
- resourceIdFallback: historyFallbackId,
85
- organizationId: company.organizationId ?? undefined,
86
- }}
87
- t={t}
88
- />
82
+ <>
83
+ {utilityActions}
84
+ <VersionHistoryAction
85
+ config={{
86
+ resourceKind: 'customers.company',
87
+ resourceId: company.id,
88
+ resourceIdFallback: historyFallbackId,
89
+ organizationId: company.organizationId ?? undefined,
90
+ }}
91
+ t={t}
92
+ />
93
+ </>
89
94
  )}
90
95
  title={
91
96
  <InlineTextEditor
@@ -55,6 +55,7 @@ export type PersonHighlightsProps = {
55
55
  onDelete: () => void
56
56
  isDeleting: boolean
57
57
  onCompanySave: (companyId: string | null) => Promise<void>
58
+ utilityActions?: React.ReactNode
58
59
  }
59
60
 
60
61
  type CompanyInfo = { id: string; name: string }
@@ -71,6 +72,7 @@ export function PersonHighlights({
71
72
  onDelete,
72
73
  isDeleting,
73
74
  onCompanySave,
75
+ utilityActions,
74
76
  }: PersonHighlightsProps) {
75
77
  const router = useRouter()
76
78
  const t = useT()
@@ -339,15 +341,18 @@ export function PersonHighlights({
339
341
  backHref="/backend/customers/people"
340
342
  backLabel={t('customers.people.detail.actions.backToList')}
341
343
  utilityActions={(
342
- <VersionHistoryAction
343
- config={{
344
- resourceKind: 'customers.person',
345
- resourceId: person.id,
346
- resourceIdFallback: historyFallbackId,
347
- organizationId: person.organizationId ?? undefined,
348
- }}
349
- t={t}
350
- />
344
+ <>
345
+ {utilityActions}
346
+ <VersionHistoryAction
347
+ config={{
348
+ resourceKind: 'customers.person',
349
+ resourceId: person.id,
350
+ resourceIdFallback: historyFallbackId,
351
+ organizationId: person.organizationId ?? undefined,
352
+ }}
353
+ t={t}
354
+ />
355
+ </>
351
356
  )}
352
357
  title={
353
358
  <InlineTextEditor
@@ -3,7 +3,7 @@ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
3
3
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
4
4
  import type { ObjectPreviewData } from '@open-mercato/shared/modules/messages/types'
5
5
  import type { EntityManager } from '@mikro-orm/postgresql'
6
- import { CustomerDeal, CustomerEntity } from '../data/entities'
6
+ import { CustomerCompanyProfile, CustomerDeal, CustomerEntity, CustomerPersonProfile } from '../data/entities'
7
7
 
8
8
  type PreviewContext = {
9
9
  tenantId: string
@@ -63,13 +63,32 @@ export async function loadCustomerPersonPreview(entityId: string, ctx: PreviewCo
63
63
  return { title: defaultTitle, subtitle: entityId, status: t('customers.messageObjects.notFound'), statusColor: 'gray' }
64
64
  }
65
65
 
66
+ const profile = await findOneWithDecryption(
67
+ em,
68
+ CustomerPersonProfile,
69
+ {
70
+ entity,
71
+ tenantId: ctx.tenantId,
72
+ organizationId: ctx.organizationId,
73
+ },
74
+ undefined,
75
+ { tenantId: ctx.tenantId, organizationId: ctx.organizationId },
76
+ )
77
+
66
78
  const subtitleParts = [entity.primaryEmail, entity.primaryPhone]
67
79
  .filter((part): part is string => Boolean(part && part.trim().length > 0))
80
+ const metadata: Record<string, string> = {}
81
+ const phoneLabel = t('customers.people.detail.highlights.primaryPhone')
82
+ const jobTitleLabel = t('customers.people.detail.fields.jobTitle')
83
+ if (entity.primaryPhone && entity.primaryPhone.trim().length > 0) metadata[phoneLabel] = entity.primaryPhone
84
+ if (profile?.jobTitle && profile.jobTitle.trim().length > 0) metadata[jobTitleLabel] = profile.jobTitle
85
+
68
86
  return {
69
87
  title: entity.displayName,
70
88
  subtitle: subtitleParts.join(' • ') || entityId,
71
89
  status: entity.status ?? undefined,
72
90
  statusColor: statusColor(entity.status),
91
+ metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
73
92
  }
74
93
  }
75
94
 
@@ -100,13 +119,32 @@ export async function loadCustomerCompanyPreview(entityId: string, ctx: PreviewC
100
119
  return { title: defaultTitle, subtitle: entityId, status: t('customers.messageObjects.notFound'), statusColor: 'gray' }
101
120
  }
102
121
 
122
+ const profile = await findOneWithDecryption(
123
+ em,
124
+ CustomerCompanyProfile,
125
+ {
126
+ entity,
127
+ tenantId: ctx.tenantId,
128
+ organizationId: ctx.organizationId,
129
+ },
130
+ undefined,
131
+ { tenantId: ctx.tenantId, organizationId: ctx.organizationId },
132
+ )
133
+
103
134
  const subtitleParts = [entity.primaryEmail, entity.primaryPhone]
104
135
  .filter((part): part is string => Boolean(part && part.trim().length > 0))
136
+ const metadata: Record<string, string> = {}
137
+ const phoneLabel = t('customers.companies.detail.highlights.primaryPhone')
138
+ const industryLabel = t('customers.companies.detail.fields.industry')
139
+ if (entity.primaryPhone && entity.primaryPhone.trim().length > 0) metadata[phoneLabel] = entity.primaryPhone
140
+ if (profile?.industry && profile.industry.trim().length > 0) metadata[industryLabel] = profile.industry
141
+
105
142
  return {
106
143
  title: entity.displayName,
107
144
  subtitle: subtitleParts.join(' • ') || entityId,
108
145
  status: entity.status ?? undefined,
109
146
  statusColor: statusColor(entity.status),
147
+ metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
110
148
  }
111
149
  }
112
150
 
@@ -140,8 +178,10 @@ export async function loadCustomerDealPreview(entityId: string, ctx: PreviewCont
140
178
  const probability = typeof deal.probability === 'number' ? `${deal.probability}%` : null
141
179
  const subtitle = [amount, probability].filter((part): part is string => Boolean(part && part.length > 0)).join(' • ')
142
180
  const metadata: Record<string, string> = {}
143
- if (amount) metadata.Value = amount
144
- if (probability) metadata.Probability = probability
181
+ const valueLabel = t('customers.deals.detail.fields.value')
182
+ const probabilityLabel = t('customers.deals.detail.fields.probability')
183
+ if (amount) metadata[valueLabel] = amount
184
+ if (probability) metadata[probabilityLabel] = probability
145
185
 
146
186
  return {
147
187
  title: deal.title,
@@ -1,6 +1,5 @@
1
1
  import type { MessageObjectTypeDefinition } from '@open-mercato/shared/modules/messages/types'
2
- import { CustomerMessageObjectDetail } from './widgets/messages/CustomerMessageObjectDetail'
3
- import { CustomerMessageObjectPreview } from './widgets/messages/CustomerMessageObjectPreview'
2
+ import { MessageObjectDetail, MessageObjectPreview } from '@open-mercato/ui'
4
3
 
5
4
  const objectMessageTypes = ['default', 'messages.defaultWithObjects']
6
5
 
@@ -14,9 +13,16 @@ export const messageObjectTypes: MessageObjectTypeDefinition[] = [
14
13
  optionSubtitleField: 'email',
15
14
  labelKey: 'customers.people.list.title',
16
15
  icon: 'user-round',
17
- PreviewComponent: CustomerMessageObjectPreview,
18
- DetailComponent: CustomerMessageObjectDetail,
19
- actions: [],
16
+ PreviewComponent: MessageObjectPreview,
17
+ DetailComponent: MessageObjectDetail,
18
+ actions: [
19
+ {
20
+ id: 'view',
21
+ labelKey: 'common.view',
22
+ variant: 'outline',
23
+ href: '/backend/customers/people/{entityId}',
24
+ },
25
+ ],
20
26
  loadPreview: async (entityId, ctx) => {
21
27
  if (typeof window !== 'undefined') {
22
28
  return { title: 'Person', subtitle: entityId }
@@ -34,9 +40,16 @@ export const messageObjectTypes: MessageObjectTypeDefinition[] = [
34
40
  optionSubtitleField: 'taxId',
35
41
  labelKey: 'customers.companies.list.title',
36
42
  icon: 'building2',
37
- PreviewComponent: CustomerMessageObjectPreview,
38
- DetailComponent: CustomerMessageObjectDetail,
39
- actions: [],
43
+ PreviewComponent: MessageObjectPreview,
44
+ DetailComponent: MessageObjectDetail,
45
+ actions: [
46
+ {
47
+ id: 'view',
48
+ labelKey: 'common.view',
49
+ variant: 'outline',
50
+ href: '/backend/customers/companies/{entityId}',
51
+ },
52
+ ],
40
53
  loadPreview: async (entityId, ctx) => {
41
54
  if (typeof window !== 'undefined') {
42
55
  return { title: 'Company', subtitle: entityId }
@@ -54,9 +67,16 @@ export const messageObjectTypes: MessageObjectTypeDefinition[] = [
54
67
  optionSubtitleField: 'status',
55
68
  labelKey: 'customers.deals.list.title',
56
69
  icon: 'briefcase-business',
57
- PreviewComponent: CustomerMessageObjectPreview,
58
- DetailComponent: CustomerMessageObjectDetail,
59
- actions: [],
70
+ PreviewComponent: MessageObjectPreview,
71
+ DetailComponent: MessageObjectDetail,
72
+ actions: [
73
+ {
74
+ id: 'view',
75
+ labelKey: 'common.view',
76
+ variant: 'outline',
77
+ href: '/backend/customers/deals/{entityId}',
78
+ },
79
+ ],
60
80
  loadPreview: async (entityId, ctx) => {
61
81
  if (typeof window !== 'undefined') {
62
82
  return { title: 'Deal', subtitle: entityId }