@open-mercato/core 0.9.0 → 0.9.1-develop.7462.1.94c09f158f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/generated/entities/customer_dictionary_entry/index.js +2 -0
- package/dist/generated/entities/customer_dictionary_entry/index.js.map +2 -2
- package/dist/generated/entities/customer_interaction/index.js +2 -0
- package/dist/generated/entities/customer_interaction/index.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +2 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/attachments/api/route.js +29 -3
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/attachments/lib/ocrLimits.js +49 -0
- package/dist/modules/attachments/lib/ocrLimits.js.map +7 -0
- package/dist/modules/attachments/lib/ocrQueue.js +76 -2
- package/dist/modules/attachments/lib/ocrQueue.js.map +2 -2
- package/dist/modules/attachments/lib/ocrService.js +4 -0
- package/dist/modules/attachments/lib/ocrService.js.map +2 -2
- package/dist/modules/attachments/lib/pdfProcessing.js +14 -2
- package/dist/modules/attachments/lib/pdfProcessing.js.map +2 -2
- package/dist/modules/attachments/lib/scoped-upload-service.js +4 -2
- package/dist/modules/attachments/lib/scoped-upload-service.js.map +2 -2
- package/dist/modules/attachments/lib/textExtraction.js +13 -1
- package/dist/modules/attachments/lib/textExtraction.js.map +2 -2
- package/dist/modules/catalog/commands/categories.js +1 -1
- package/dist/modules/catalog/commands/categories.js.map +2 -2
- package/dist/modules/catalog/commands/productDeleteChildren.js +389 -0
- package/dist/modules/catalog/commands/productDeleteChildren.js.map +7 -0
- package/dist/modules/catalog/commands/products.js +52 -17
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/catalog/search.js +3 -1
- package/dist/modules/catalog/search.js.map +2 -2
- package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js +2 -1
- package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +4 -6
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js +4 -6
- package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js +47 -11
- package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js.map +3 -3
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +10 -7
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +2 -2
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js +5 -1
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +2 -2
- package/dist/modules/communication_channels/lib/inbound-message-origin.js +1 -1
- package/dist/modules/communication_channels/lib/inbound-message-origin.js.map +2 -2
- package/dist/modules/communication_channels/lib/oauth-result.js +12 -0
- package/dist/modules/communication_channels/lib/oauth-result.js.map +7 -0
- package/dist/modules/communication_channels/lib/oauth-state.js +4 -1
- package/dist/modules/communication_channels/lib/oauth-state.js.map +2 -2
- package/dist/modules/communication_channels/lib/use-connect-channel.js +10 -0
- package/dist/modules/communication_channels/lib/use-connect-channel.js.map +2 -2
- package/dist/modules/customers/api/activity-types/route.js +151 -0
- package/dist/modules/customers/api/activity-types/route.js.map +7 -0
- package/dist/modules/customers/api/deals/[id]/people/route.js +58 -5
- package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +13 -5
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/route.js +20 -5
- package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/route.js +3 -0
- package/dist/modules/customers/api/interactions/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/tasks/route.js +1 -0
- package/dist/modules/customers/api/interactions/tasks/route.js.map +2 -2
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js +18 -0
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js +0 -23
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +15 -17
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/calendar-event-types.js +548 -0
- package/dist/modules/customers/calendar-event-types.js.map +7 -0
- package/dist/modules/customers/commands/dictionaries.js +70 -10
- package/dist/modules/customers/commands/dictionaries.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js +187 -22
- package/dist/modules/customers/commands/interactions.js.map +3 -3
- package/dist/modules/customers/components/ActivityTypeEditor.js +288 -0
- package/dist/modules/customers/components/ActivityTypeEditor.js.map +7 -0
- package/dist/modules/customers/components/DictionarySettings.js +3 -1
- package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js +391 -297
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +3 -3
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +9 -74
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
- package/dist/modules/customers/components/calendar/TimeGrid.js +20 -16
- package/dist/modules/customers/components/calendar/TimeGrid.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/EventTypePanel.js +218 -0
- package/dist/modules/customers/components/calendar/editor/EventTypePanel.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/LocationField.js +7 -3
- package/dist/modules/customers/components/calendar/editor/LocationField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js +5 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PriorityField.js +3 -1
- package/dist/modules/customers/components/calendar/editor/PriorityField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/RepeatField.js +9 -1
- package/dist/modules/customers/components/calendar/editor/RepeatField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/ResourcesField.js +4 -1
- package/dist/modules/customers/components/calendar/editor/ResourcesField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js +15 -7
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/SegmentGroup.js +3 -1
- package/dist/modules/customers/components/calendar/editor/SegmentGroup.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/hooks.js +28 -13
- package/dist/modules/customers/components/calendar/editor/hooks.js.map +3 -3
- package/dist/modules/customers/components/calendar/editor/inputs.js +14 -6
- package/dist/modules/customers/components/calendar/editor/inputs.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/useCalendarCustomFields.js +54 -0
- package/dist/modules/customers/components/calendar/editor/useCalendarCustomFields.js.map +7 -0
- package/dist/modules/customers/components/calendar/editor/useEventTypeCatalog.js +82 -0
- package/dist/modules/customers/components/calendar/editor/useEventTypeCatalog.js.map +7 -0
- package/dist/modules/customers/components/calendar/types.js +1 -0
- package/dist/modules/customers/components/calendar/types.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js +78 -343
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
- package/dist/modules/customers/components/detail/CreatePersonDialog.js +12 -11
- package/dist/modules/customers/components/detail/CreatePersonDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/DealForm.js +7 -3
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/DealPeopleSection.js +206 -0
- package/dist/modules/customers/components/detail/DealPeopleSection.js.map +7 -0
- package/dist/modules/customers/components/detail/LinkedPeopleSection.js +342 -0
- package/dist/modules/customers/components/detail/LinkedPeopleSection.js.map +7 -0
- package/dist/modules/customers/components/detail/PersonCard.js +5 -2
- package/dist/modules/customers/components/detail/PersonCard.js.map +2 -2
- package/dist/modules/customers/components/linking/adapters/personAdapter.js +3 -0
- package/dist/modules/customers/components/linking/adapters/personAdapter.js.map +2 -2
- package/dist/modules/customers/data/entities.js +6 -0
- package/dist/modules/customers/data/entities.js.map +2 -2
- package/dist/modules/customers/data/validators.js +39 -10
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/di.js +20 -1
- package/dist/modules/customers/di.js.map +2 -2
- package/dist/modules/customers/extension-points.js +13 -0
- package/dist/modules/customers/extension-points.js.map +2 -2
- package/dist/modules/customers/lib/calendar/conflicts.js +59 -16
- package/dist/modules/customers/lib/calendar/conflicts.js.map +2 -2
- package/dist/modules/customers/lib/calendar/editorPayload.js +34 -16
- package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
- package/dist/modules/customers/lib/calendar/eventTypeResolver.js +197 -0
- package/dist/modules/customers/lib/calendar/eventTypeResolver.js.map +7 -0
- package/dist/modules/customers/lib/calendar/interactionApplicability.js +67 -0
- package/dist/modules/customers/lib/calendar/interactionApplicability.js.map +7 -0
- package/dist/modules/customers/lib/calendar/mapItem.js +5 -2
- package/dist/modules/customers/lib/calendar/mapItem.js.map +2 -2
- package/dist/modules/customers/lib/calendar/recurrence.js +62 -1
- package/dist/modules/customers/lib/calendar/recurrence.js.map +2 -2
- package/dist/modules/customers/lib/calendar/timezone.js +111 -0
- package/dist/modules/customers/lib/calendar/timezone.js.map +7 -0
- package/dist/modules/customers/lib/interactionReadModel.js +1 -0
- package/dist/modules/customers/lib/interactionReadModel.js.map +2 -2
- package/dist/modules/customers/lib/todoCompatibility.js +1 -0
- package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
- package/dist/modules/customers/migrations/Migration20260928175058_customers.js +17 -0
- package/dist/modules/customers/migrations/Migration20260928175058_customers.js.map +7 -0
- package/dist/modules/customers/migrations/Migration20260930110235_customers.js +17 -0
- package/dist/modules/customers/migrations/Migration20260930110235_customers.js.map +7 -0
- package/dist/modules/dictionaries/components/DictionaryTable.js +60 -51
- package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
- package/dist/modules/messages/subscribers/message-notification.js +13 -1
- package/dist/modules/messages/subscribers/message-notification.js.map +2 -2
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +201 -53
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/payment_gateways/lib/gateway-service.js +37 -11
- package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
- package/dist/modules/planner/lib/availabilityMerge.js +101 -38
- package/dist/modules/planner/lib/availabilityMerge.js.map +3 -3
- package/dist/modules/planner/services/plannerAvailabilityService.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +159 -81
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/lineItemUtils.js +7 -0
- package/dist/modules/sales/components/documents/lineItemUtils.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +12 -7
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +2 -2
- package/dist/modules/sync_excel/lib/adapters/customers.js +28 -25
- package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
- package/generated/entities/customer_dictionary_entry/index.ts +1 -0
- package/generated/entities/customer_interaction/index.ts +1 -0
- package/generated/entity-fields-registry.ts +2 -0
- package/package.json +9 -8
- package/src/modules/attachments/api/route.ts +36 -3
- package/src/modules/attachments/lib/ocrLimits.ts +62 -0
- package/src/modules/attachments/lib/ocrQueue.ts +95 -2
- package/src/modules/attachments/lib/ocrService.ts +4 -0
- package/src/modules/attachments/lib/pdfProcessing.ts +14 -2
- package/src/modules/attachments/lib/scoped-upload-service.ts +4 -2
- package/src/modules/attachments/lib/textExtraction.ts +14 -1
- package/src/modules/auth/i18n/de.json +3 -0
- package/src/modules/auth/i18n/en.json +3 -0
- package/src/modules/auth/i18n/es.json +3 -0
- package/src/modules/auth/i18n/ko.json +3 -0
- package/src/modules/auth/i18n/pl.json +3 -0
- package/src/modules/catalog/commands/categories.ts +1 -1
- package/src/modules/catalog/commands/productDeleteChildren.ts +557 -0
- package/src/modules/catalog/commands/products.ts +58 -17
- package/src/modules/catalog/search.ts +5 -1
- package/src/modules/communication_channels/api/get/oauth/[provider]/callback/route.ts +2 -1
- package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +4 -6
- package/src/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.ts +4 -6
- package/src/modules/communication_channels/api/post/oauth/[provider]/initiate/route.ts +46 -10
- package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +12 -9
- package/src/modules/communication_channels/commands/deliver-outbound-message.ts +6 -1
- package/src/modules/communication_channels/i18n/de.json +4 -1
- package/src/modules/communication_channels/i18n/en.json +4 -1
- package/src/modules/communication_channels/i18n/es.json +4 -1
- package/src/modules/communication_channels/i18n/ko.json +4 -1
- package/src/modules/communication_channels/i18n/pl.json +4 -1
- package/src/modules/communication_channels/lib/inbound-message-origin.ts +2 -6
- package/src/modules/communication_channels/lib/oauth-result.ts +13 -0
- package/src/modules/communication_channels/lib/oauth-state.ts +4 -1
- package/src/modules/communication_channels/lib/use-connect-channel.ts +10 -0
- package/src/modules/customers/api/activity-types/route.ts +154 -0
- package/src/modules/customers/api/deals/[id]/people/route.ts +82 -3
- package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +9 -1
- package/src/modules/customers/api/dictionaries/[kind]/route.ts +18 -2
- package/src/modules/customers/api/interactions/route.ts +4 -0
- package/src/modules/customers/api/interactions/tasks/route.ts +1 -0
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.meta.ts +20 -0
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.ts +0 -29
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +15 -17
- package/src/modules/customers/calendar-event-types.ts +674 -0
- package/src/modules/customers/commands/dictionaries.ts +84 -8
- package/src/modules/customers/commands/interactions.ts +244 -21
- package/src/modules/customers/components/ActivityTypeEditor.tsx +239 -0
- package/src/modules/customers/components/DictionarySettings.tsx +5 -3
- package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +347 -218
- package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +18 -96
- package/src/modules/customers/components/calendar/TimeGrid.tsx +20 -18
- package/src/modules/customers/components/calendar/editor/EventTypePanel.tsx +230 -0
- package/src/modules/customers/components/calendar/editor/LocationField.tsx +7 -1
- package/src/modules/customers/components/calendar/editor/PeopleField.tsx +4 -0
- package/src/modules/customers/components/calendar/editor/PriorityField.tsx +3 -0
- package/src/modules/customers/components/calendar/editor/RepeatField.tsx +10 -0
- package/src/modules/customers/components/calendar/editor/ResourcesField.tsx +4 -0
- package/src/modules/customers/components/calendar/editor/ScheduleSection.tsx +16 -4
- package/src/modules/customers/components/calendar/editor/SegmentGroup.tsx +3 -0
- package/src/modules/customers/components/calendar/editor/hooks.ts +29 -13
- package/src/modules/customers/components/calendar/editor/inputs.tsx +17 -5
- package/src/modules/customers/components/calendar/editor/useCalendarCustomFields.ts +49 -0
- package/src/modules/customers/components/calendar/editor/useEventTypeCatalog.ts +100 -0
- package/src/modules/customers/components/calendar/types.ts +1 -0
- package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +94 -388
- package/src/modules/customers/components/detail/CreatePersonDialog.tsx +39 -19
- package/src/modules/customers/components/detail/DealForm.tsx +7 -3
- package/src/modules/customers/components/detail/DealPeopleSection.tsx +256 -0
- package/src/modules/customers/components/detail/LinkedPeopleSection.tsx +474 -0
- package/src/modules/customers/components/detail/PersonCard.tsx +12 -4
- package/src/modules/customers/components/detail/types.ts +1 -0
- package/src/modules/customers/components/linking/adapters/personAdapter.tsx +4 -0
- package/src/modules/customers/data/entities.ts +8 -1
- package/src/modules/customers/data/validators.ts +42 -7
- package/src/modules/customers/di.ts +21 -0
- package/src/modules/customers/extension-points.ts +13 -0
- package/src/modules/customers/i18n/de.json +104 -8
- package/src/modules/customers/i18n/en.json +104 -8
- package/src/modules/customers/i18n/es.json +104 -8
- package/src/modules/customers/i18n/ko.json +104 -8
- package/src/modules/customers/i18n/pl.json +104 -8
- package/src/modules/customers/lib/calendar/conflicts.ts +106 -27
- package/src/modules/customers/lib/calendar/editorPayload.ts +39 -19
- package/src/modules/customers/lib/calendar/eventTypeResolver.ts +258 -0
- package/src/modules/customers/lib/calendar/interactionApplicability.ts +99 -0
- package/src/modules/customers/lib/calendar/mapItem.ts +6 -2
- package/src/modules/customers/lib/calendar/recurrence.ts +65 -1
- package/src/modules/customers/lib/calendar/timezone.ts +117 -0
- package/src/modules/customers/lib/interactionReadModel.ts +1 -0
- package/src/modules/customers/lib/todoCompatibility.ts +2 -0
- package/src/modules/customers/migrations/.snapshot-open-mercato.json +34 -0
- package/src/modules/customers/migrations/Migration20260928175058_customers.ts +15 -0
- package/src/modules/customers/migrations/Migration20260930110235_customers.ts +15 -0
- package/src/modules/dictionaries/components/DictionaryTable.tsx +63 -56
- package/src/modules/messages/subscribers/message-notification.ts +13 -3
- package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +243 -62
- package/src/modules/payment_gateways/lib/gateway-service.ts +39 -12
- package/src/modules/planner/lib/availabilityMerge.ts +106 -41
- package/src/modules/planner/services/plannerAvailabilityService.ts +4 -0
- package/src/modules/sales/components/documents/LineItemDialog.tsx +194 -111
- package/src/modules/sales/components/documents/lineItemUtils.ts +18 -0
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +25 -13
- package/src/modules/sync_excel/lib/adapters/customers.ts +33 -27
|
@@ -10,7 +10,12 @@ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
|
10
10
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
11
11
|
import { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
12
12
|
import { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
CustomerDeal,
|
|
15
|
+
CustomerDealPersonLink,
|
|
16
|
+
CustomerEntity,
|
|
17
|
+
CustomerPersonProfile,
|
|
18
|
+
} from '../../../../data/entities'
|
|
14
19
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
15
20
|
|
|
16
21
|
const logger = createLogger('customers')
|
|
@@ -41,12 +46,36 @@ type DealPersonItem = {
|
|
|
41
46
|
kind: 'person'
|
|
42
47
|
linkedAt: string
|
|
43
48
|
isPrimary: boolean
|
|
49
|
+
// Added for the shared linked-people card, which renders the same shape the company
|
|
50
|
+
// endpoint already returns. Purely additive: every key above keeps its meaning, so
|
|
51
|
+
// `DealLinkedEntitiesTab` and any third-party consumer are unaffected.
|
|
52
|
+
displayName: string
|
|
53
|
+
primaryEmail: string | null
|
|
54
|
+
primaryPhone: string | null
|
|
55
|
+
status: string | null
|
|
56
|
+
lifecycleStage: string | null
|
|
57
|
+
jobTitle: string | null
|
|
58
|
+
department: string | null
|
|
59
|
+
createdAt: string
|
|
60
|
+
organizationId: string
|
|
61
|
+
temperature: string | null
|
|
62
|
+
source: string | null
|
|
44
63
|
}
|
|
45
64
|
|
|
46
65
|
function matchesSearch(item: DealPersonItem, query: string): boolean {
|
|
47
66
|
const normalized = query.trim().toLowerCase()
|
|
48
67
|
if (!normalized.length) return true
|
|
49
|
-
return [
|
|
68
|
+
return [
|
|
69
|
+
item.label,
|
|
70
|
+
item.subtitle,
|
|
71
|
+
item.primaryEmail,
|
|
72
|
+
item.primaryPhone,
|
|
73
|
+
item.jobTitle,
|
|
74
|
+
item.department,
|
|
75
|
+
item.status,
|
|
76
|
+
item.lifecycleStage,
|
|
77
|
+
item.source,
|
|
78
|
+
]
|
|
50
79
|
.filter((value): value is string => typeof value === 'string' && value.length > 0)
|
|
51
80
|
.some((value) => value.toLowerCase().includes(normalized))
|
|
52
81
|
}
|
|
@@ -119,17 +148,56 @@ export async function GET(req: Request, ctx: { params?: { id?: string } }) {
|
|
|
119
148
|
entityScope,
|
|
120
149
|
)
|
|
121
150
|
|
|
151
|
+
// One batched read for the profile-only columns, keyed by every linked person — not one
|
|
152
|
+
// query per row. The handler filters, sorts and paginates in memory below, so the batch
|
|
153
|
+
// covers the whole link set rather than the page; that is fine at the expected scale of
|
|
154
|
+
// tens of contacts per deal, and the in-memory pipeline is what to revisit first if it
|
|
155
|
+
// ever is not.
|
|
156
|
+
const personIds = links
|
|
157
|
+
.map((link) => (link.person as CustomerEntity | null)?.id)
|
|
158
|
+
.filter((personId): personId is string => typeof personId === 'string' && personId.length > 0)
|
|
159
|
+
|
|
160
|
+
const profiles = personIds.length > 0
|
|
161
|
+
? await findWithDecryption(
|
|
162
|
+
em,
|
|
163
|
+
CustomerPersonProfile,
|
|
164
|
+
{
|
|
165
|
+
entity: { $in: personIds },
|
|
166
|
+
tenantId: deal.tenantId,
|
|
167
|
+
organizationId: deal.organizationId,
|
|
168
|
+
},
|
|
169
|
+
{},
|
|
170
|
+
entityScope,
|
|
171
|
+
)
|
|
172
|
+
: []
|
|
173
|
+
const profileByPersonId = new Map(
|
|
174
|
+
profiles.map((profile) => [(profile.entity as { id: string }).id, profile]),
|
|
175
|
+
)
|
|
176
|
+
|
|
122
177
|
const items = links
|
|
123
178
|
.map((link) => {
|
|
124
179
|
const person = link.person as CustomerEntity | null
|
|
125
180
|
if (!person?.id) return null
|
|
181
|
+
const profile = profileByPersonId.get(person.id) ?? null
|
|
182
|
+
const displayName = person.displayName ?? person.primaryEmail ?? person.id
|
|
126
183
|
return {
|
|
127
184
|
id: person.id,
|
|
128
|
-
label:
|
|
185
|
+
label: displayName,
|
|
129
186
|
subtitle: person.primaryEmail ?? person.primaryPhone ?? null,
|
|
130
187
|
kind: 'person',
|
|
131
188
|
linkedAt: link.createdAt.toISOString(),
|
|
132
189
|
isPrimary: link.isPrimary === true,
|
|
190
|
+
displayName,
|
|
191
|
+
primaryEmail: person.primaryEmail ?? null,
|
|
192
|
+
primaryPhone: person.primaryPhone ?? null,
|
|
193
|
+
status: person.status ?? null,
|
|
194
|
+
lifecycleStage: person.lifecycleStage ?? null,
|
|
195
|
+
jobTitle: profile?.jobTitle ?? null,
|
|
196
|
+
department: profile?.department ?? null,
|
|
197
|
+
createdAt: person.createdAt.toISOString(),
|
|
198
|
+
organizationId: person.organizationId,
|
|
199
|
+
temperature: person.temperature ?? null,
|
|
200
|
+
source: person.source ?? null,
|
|
133
201
|
} satisfies DealPersonItem
|
|
134
202
|
})
|
|
135
203
|
.filter((item): item is DealPersonItem => item !== null)
|
|
@@ -176,6 +244,17 @@ export const openApi: OpenApiRouteDoc = {
|
|
|
176
244
|
kind: z.literal('person'),
|
|
177
245
|
linkedAt: z.string(),
|
|
178
246
|
isPrimary: z.boolean(),
|
|
247
|
+
displayName: z.string(),
|
|
248
|
+
primaryEmail: z.string().nullable(),
|
|
249
|
+
primaryPhone: z.string().nullable(),
|
|
250
|
+
status: z.string().nullable(),
|
|
251
|
+
lifecycleStage: z.string().nullable(),
|
|
252
|
+
jobTitle: z.string().nullable(),
|
|
253
|
+
department: z.string().nullable(),
|
|
254
|
+
createdAt: z.string(),
|
|
255
|
+
organizationId: z.string().uuid().nullable(),
|
|
256
|
+
temperature: z.string().nullable(),
|
|
257
|
+
source: z.string().nullable(),
|
|
179
258
|
}),
|
|
180
259
|
),
|
|
181
260
|
total: z.number().int().nonnegative(),
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from '@open-mercato/shared/lib/crud/mutation-guard'
|
|
18
18
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
19
19
|
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
20
|
+
import { calendarEventTypeBehaviorSchema } from '../../../../calendar-event-types'
|
|
20
21
|
|
|
21
22
|
const logger = createLogger('customers')
|
|
22
23
|
|
|
@@ -30,8 +31,9 @@ const patchSchema = z
|
|
|
30
31
|
label: z.string().trim().max(150).optional(),
|
|
31
32
|
color: z.union([z.string().trim(), z.null()]).optional(),
|
|
32
33
|
icon: z.union([z.string().trim(), z.null()]).optional(),
|
|
34
|
+
behavior: calendarEventTypeBehaviorSchema.nullable().optional(),
|
|
33
35
|
})
|
|
34
|
-
.refine((input) => input.value !== undefined || input.label !== undefined || input.color !== undefined || input.icon !== undefined, {
|
|
36
|
+
.refine((input) => input.value !== undefined || input.label !== undefined || input.color !== undefined || input.icon !== undefined || input.behavior !== undefined, {
|
|
35
37
|
message: 'No changes provided',
|
|
36
38
|
})
|
|
37
39
|
|
|
@@ -81,6 +83,7 @@ export async function PATCH(req: Request, ctx: { params?: { kind?: string; id?:
|
|
|
81
83
|
label: payload.label,
|
|
82
84
|
color: payload.color,
|
|
83
85
|
icon: payload.icon,
|
|
86
|
+
behavior: payload.behavior,
|
|
84
87
|
},
|
|
85
88
|
ctx: routeContext.ctx,
|
|
86
89
|
})) as CommandExecuteResult<{ entryId: string; changed: boolean }>
|
|
@@ -103,6 +106,7 @@ export async function PATCH(req: Request, ctx: { params?: { kind?: string; id?:
|
|
|
103
106
|
),
|
|
104
107
|
})
|
|
105
108
|
}
|
|
109
|
+
if (typeof err.body.code === 'string') throw err
|
|
106
110
|
throw new CrudHttpError(409, { error: routeContext.translate('customers.config.dictionaries.errors.duplicate', 'An entry with this value already exists') })
|
|
107
111
|
}
|
|
108
112
|
if (err.status === 400) {
|
|
@@ -141,8 +145,10 @@ export async function PATCH(req: Request, ctx: { params?: { kind?: string; id?:
|
|
|
141
145
|
label: entry.label,
|
|
142
146
|
color: entry.color,
|
|
143
147
|
icon: entry.icon,
|
|
148
|
+
...(mappedKind === 'activity_type' ? { behavior: entry.activityTypeBehavior ?? null } : {}),
|
|
144
149
|
organizationId: entry.organizationId,
|
|
145
150
|
isInherited: false,
|
|
151
|
+
updatedAt: entry.updatedAt,
|
|
146
152
|
})
|
|
147
153
|
if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {
|
|
148
154
|
response.headers.set(
|
|
@@ -297,8 +303,10 @@ const dictionaryEntrySchema = z.object({
|
|
|
297
303
|
label: z.string().nullable().optional(),
|
|
298
304
|
color: z.string().nullable().optional(),
|
|
299
305
|
icon: z.string().nullable().optional(),
|
|
306
|
+
behavior: calendarEventTypeBehaviorSchema.nullable().optional(),
|
|
300
307
|
organizationId: z.string().uuid().nullable().optional(),
|
|
301
308
|
isInherited: z.boolean().optional(),
|
|
309
|
+
updatedAt: z.date().or(z.string()).optional(),
|
|
302
310
|
})
|
|
303
311
|
|
|
304
312
|
const dictionaryDeleteResponseSchema = z.object({
|
|
@@ -24,6 +24,8 @@ import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
|
24
24
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
25
25
|
import { CUSTOMER_DICTIONARY_ORGANIZATION_REQUIRED_CODE } from '../../../lib/dictionaries'
|
|
26
26
|
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
27
|
+
import { calendarEventTypeBehaviorSchema } from '../../../calendar-event-types'
|
|
28
|
+
import type { OrganizationHierarchyService } from '@open-mercato/shared/lib/auth/principal-service'
|
|
27
29
|
|
|
28
30
|
const logger = createLogger('customers')
|
|
29
31
|
|
|
@@ -35,6 +37,7 @@ const postSchema = z.object({
|
|
|
35
37
|
label: z.string().trim().max(150).optional(),
|
|
36
38
|
color: colorSchema.or(z.null()).optional(),
|
|
37
39
|
icon: iconSchema.or(z.null()).optional(),
|
|
40
|
+
behavior: calendarEventTypeBehaviorSchema.nullable().optional(),
|
|
38
41
|
})
|
|
39
42
|
|
|
40
43
|
const querySchema = z.object({
|
|
@@ -52,7 +55,7 @@ export async function GET(req: Request, ctx: { params?: { kind?: string } }) {
|
|
|
52
55
|
const query = querySchema.parse({
|
|
53
56
|
organizationId: url.searchParams.get('organizationId') ?? undefined,
|
|
54
57
|
})
|
|
55
|
-
const { translate, em, organizationId, readableOrganizationIds, tenantId, cache } = await resolveDictionaryRouteContext(req, {
|
|
58
|
+
const { translate, em, organizationId, readableOrganizationIds, tenantId, cache, container } = await resolveDictionaryRouteContext(req, {
|
|
56
59
|
selectedId: query.organizationId ?? undefined,
|
|
57
60
|
})
|
|
58
61
|
const { kind, mappedKind } = mapDictionaryKind(ctx.params?.kind)
|
|
@@ -64,7 +67,15 @@ export async function GET(req: Request, ctx: { params?: { kind?: string } }) {
|
|
|
64
67
|
}
|
|
65
68
|
const settings = await loadCustomerSettings(em, { tenantId, organizationId })
|
|
66
69
|
const sortMode = resolveDictionaryEntrySortMode(settings?.dictionarySortModes?.[kind])
|
|
67
|
-
|
|
70
|
+
let scopedOrganizationIds = readableOrganizationIds.length > 0 ? readableOrganizationIds : [organizationId]
|
|
71
|
+
if (mappedKind === 'activity_type') {
|
|
72
|
+
const hierarchy = container.resolve('organizationHierarchyService') as OrganizationHierarchyService
|
|
73
|
+
const ancestors = await hierarchy.resolveAncestorIds({ tenantId, organizationId })
|
|
74
|
+
if (ancestors === null) {
|
|
75
|
+
throw new CrudHttpError(403, { error: translate('customers.errors.organization_forbidden', 'Organization not accessible') })
|
|
76
|
+
}
|
|
77
|
+
scopedOrganizationIds = Array.from(new Set([organizationId, ...ancestors.slice().reverse()]))
|
|
78
|
+
}
|
|
68
79
|
const canUseCache = Boolean(cache) && mappedKind !== 'person_company_role'
|
|
69
80
|
|
|
70
81
|
let cacheKey: string | null = null
|
|
@@ -133,6 +144,7 @@ export async function GET(req: Request, ctx: { params?: { kind?: string } }) {
|
|
|
133
144
|
label: entry.label,
|
|
134
145
|
color: entry.color,
|
|
135
146
|
icon: entry.icon,
|
|
147
|
+
...(mappedKind === 'activity_type' ? { behavior: entry.activityTypeBehavior ?? null } : {}),
|
|
136
148
|
organizationId: entry.organizationId,
|
|
137
149
|
isInherited: entry.organizationId !== organizationId,
|
|
138
150
|
createdAt: entry.createdAt,
|
|
@@ -213,6 +225,7 @@ export async function POST(req: Request, ctx: { params?: { kind?: string } }) {
|
|
|
213
225
|
label: body.label,
|
|
214
226
|
color: body.color,
|
|
215
227
|
icon: body.icon,
|
|
228
|
+
behavior: body.behavior,
|
|
216
229
|
},
|
|
217
230
|
ctx: context.ctx,
|
|
218
231
|
})) as CommandExecuteResult<{ entryId: string; mode: 'created' | 'updated' | 'unchanged' }>
|
|
@@ -248,8 +261,10 @@ export async function POST(req: Request, ctx: { params?: { kind?: string } }) {
|
|
|
248
261
|
label: entry.label,
|
|
249
262
|
color: entry.color,
|
|
250
263
|
icon: entry.icon,
|
|
264
|
+
...(mappedKind === 'activity_type' ? { behavior: entry.activityTypeBehavior ?? null } : {}),
|
|
251
265
|
organizationId: entry.organizationId,
|
|
252
266
|
isInherited: false,
|
|
267
|
+
updatedAt: entry.updatedAt,
|
|
253
268
|
},
|
|
254
269
|
{ status: result.mode === 'created' ? 201 : 200 }
|
|
255
270
|
)
|
|
@@ -288,6 +303,7 @@ const dictionaryEntrySchema = z.object({
|
|
|
288
303
|
label: z.string().nullable().optional(),
|
|
289
304
|
color: z.string().nullable().optional(),
|
|
290
305
|
icon: z.string().nullable().optional(),
|
|
306
|
+
behavior: calendarEventTypeBehaviorSchema.nullable().optional(),
|
|
291
307
|
organizationId: z.string().uuid().nullable().optional(),
|
|
292
308
|
isInherited: z.boolean().optional(),
|
|
293
309
|
createdAt: z.date().or(z.string()).optional(),
|
|
@@ -179,6 +179,7 @@ type InteractionListRow = {
|
|
|
179
179
|
appearance_icon: string | null
|
|
180
180
|
appearance_color: string | null
|
|
181
181
|
source: string | null
|
|
182
|
+
timezone: string | null
|
|
182
183
|
duration_minutes: number | null
|
|
183
184
|
location: string | null
|
|
184
185
|
all_day: boolean | null
|
|
@@ -314,6 +315,7 @@ const INTERACTION_LIST_COLUMNS = [
|
|
|
314
315
|
'appearance_icon',
|
|
315
316
|
'appearance_color',
|
|
316
317
|
'source',
|
|
318
|
+
'timezone',
|
|
317
319
|
'duration_minutes',
|
|
318
320
|
'location',
|
|
319
321
|
'all_day',
|
|
@@ -713,6 +715,7 @@ export async function GET(req: Request) {
|
|
|
713
715
|
source: row.source ?? null,
|
|
714
716
|
duration: row.duration_minutes ?? null,
|
|
715
717
|
durationMinutes: row.duration_minutes ?? null,
|
|
718
|
+
timezone: row.timezone ?? null,
|
|
716
719
|
location: row.location ?? null,
|
|
717
720
|
allDay: row.all_day ?? null,
|
|
718
721
|
recurrenceRule: row.recurrence_rule ?? null,
|
|
@@ -794,6 +797,7 @@ const interactionListItemSchema = z
|
|
|
794
797
|
source: z.string().nullable().optional(),
|
|
795
798
|
duration: z.number().nullable().optional(),
|
|
796
799
|
durationMinutes: z.number().nullable().optional(),
|
|
800
|
+
timezone: z.string().nullable().optional(),
|
|
797
801
|
location: z.string().nullable().optional(),
|
|
798
802
|
allDay: z.boolean().nullable().optional(),
|
|
799
803
|
recurrenceRule: z.string().nullable().optional(),
|
|
@@ -153,6 +153,7 @@ const todoItemSchema = z.object({
|
|
|
153
153
|
todoSeverity: z.string().nullable().optional(),
|
|
154
154
|
todoDescription: z.string().nullable().optional(),
|
|
155
155
|
todoDueAt: z.string().nullable().optional(),
|
|
156
|
+
todoTimezone: z.string().nullable().optional(),
|
|
156
157
|
todoCustomValues: z.record(z.string(), z.unknown()).nullable().optional(),
|
|
157
158
|
todoOrganizationId: z.string().nullable(),
|
|
158
159
|
organizationId: z.string(),
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
const pipelineIcon = React.createElement(
|
|
4
|
+
'svg',
|
|
5
|
+
{
|
|
6
|
+
width: 16,
|
|
7
|
+
height: 16,
|
|
8
|
+
viewBox: '0 0 24 24',
|
|
9
|
+
fill: 'none',
|
|
10
|
+
stroke: 'currentColor',
|
|
11
|
+
strokeWidth: 2,
|
|
12
|
+
strokeLinecap: 'round',
|
|
13
|
+
strokeLinejoin: 'round',
|
|
14
|
+
},
|
|
15
|
+
React.createElement('rect', { x: 3, y: 4, width: 5, height: 16, rx: 1 }),
|
|
16
|
+
React.createElement('rect', { x: 10, y: 4, width: 5, height: 16, rx: 1 }),
|
|
17
|
+
React.createElement('rect', { x: 17, y: 4, width: 4, height: 16, rx: 1 })
|
|
18
|
+
)
|
|
19
|
+
|
|
1
20
|
export const metadata = {
|
|
2
21
|
requireAuth: true,
|
|
3
22
|
requireFeatures: ['customers.pipelines.manage'],
|
|
@@ -6,6 +25,7 @@ export const metadata = {
|
|
|
6
25
|
pageGroup: 'Module Configs',
|
|
7
26
|
pageGroupKey: 'settings.sections.moduleConfigs',
|
|
8
27
|
pageOrder: 4,
|
|
28
|
+
icon: pipelineIcon,
|
|
9
29
|
pageContext: 'settings' as const,
|
|
10
30
|
breadcrumb: [
|
|
11
31
|
{ label: 'Pipeline stages', labelKey: 'customers.config.nav.pipelineStages' },
|
|
@@ -42,7 +42,6 @@ type UseDealAssociationsResult = {
|
|
|
42
42
|
companiesSaving: boolean
|
|
43
43
|
handlePeopleAssociationsChange: (nextIds: string[]) => Promise<void>
|
|
44
44
|
handleCompaniesAssociationsChange: (nextIds: string[]) => Promise<void>
|
|
45
|
-
loadLinkedPeoplePage: (page: number, query: string) => Promise<LinkedPageResult>
|
|
46
45
|
loadLinkedCompaniesPage: (page: number, query: string) => Promise<LinkedPageResult>
|
|
47
46
|
}
|
|
48
47
|
|
|
@@ -64,33 +63,6 @@ export function useDealAssociations({
|
|
|
64
63
|
setCompaniesEditorIds(data?.linkedCompanyIds ?? [])
|
|
65
64
|
}, [data?.linkedCompanyIds, data?.linkedPersonIds])
|
|
66
65
|
|
|
67
|
-
const loadLinkedPeoplePage = React.useCallback(
|
|
68
|
-
async (page: number, query: string): Promise<LinkedPageResult> => {
|
|
69
|
-
if (!currentDealId) {
|
|
70
|
-
return { items: [], totalPages: 1, total: 0 }
|
|
71
|
-
}
|
|
72
|
-
const params = new URLSearchParams({
|
|
73
|
-
page: String(page),
|
|
74
|
-
pageSize: '20',
|
|
75
|
-
sort: 'name-asc',
|
|
76
|
-
})
|
|
77
|
-
if (query.trim().length > 0) {
|
|
78
|
-
params.set('search', query.trim())
|
|
79
|
-
}
|
|
80
|
-
const payload = await readApiResultOrThrow<{
|
|
81
|
-
items?: DealAssociation[]
|
|
82
|
-
total?: number
|
|
83
|
-
totalPages?: number
|
|
84
|
-
}>(`/api/customers/deals/${encodeURIComponent(currentDealId)}/people?${params.toString()}`)
|
|
85
|
-
return {
|
|
86
|
-
items: Array.isArray(payload.items) ? payload.items : [],
|
|
87
|
-
totalPages: typeof payload.totalPages === 'number' ? payload.totalPages : 1,
|
|
88
|
-
total: typeof payload.total === 'number' ? payload.total : 0,
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
[currentDealId],
|
|
92
|
-
)
|
|
93
|
-
|
|
94
66
|
const loadLinkedCompaniesPage = React.useCallback(
|
|
95
67
|
async (page: number, query: string): Promise<LinkedPageResult> => {
|
|
96
68
|
if (!currentDealId) {
|
|
@@ -220,7 +192,6 @@ export function useDealAssociations({
|
|
|
220
192
|
companiesSaving,
|
|
221
193
|
handlePeopleAssociationsChange,
|
|
222
194
|
handleCompaniesAssociationsChange,
|
|
223
|
-
loadLinkedPeoplePage,
|
|
224
195
|
loadLinkedCompaniesPage,
|
|
225
196
|
}
|
|
226
197
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import { extensionPoints } from '@open-mercato/core/modules/customers/extension-points'
|
|
5
5
|
import Link from 'next/link'
|
|
6
|
-
import { Building2, FileText, Hash, UserSearch
|
|
6
|
+
import { Building2, FileText, Hash, UserSearch } from 'lucide-react'
|
|
7
7
|
import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
|
|
8
8
|
import { useRouter, useSearchParams, usePathname } from 'next/navigation'
|
|
9
9
|
import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
@@ -24,6 +24,7 @@ import { DealDetailHeader } from '../../../../components/detail/DealDetailHeader
|
|
|
24
24
|
import { DealDetailTabs, resolveLegacyTab, type DealTabId } from '../../../../components/detail/DealDetailTabs'
|
|
25
25
|
import { DealForm, useDealAssociationLookups } from '../../../../components/detail/DealForm'
|
|
26
26
|
import { DealLinkedEntitiesTab } from '../../../../components/detail/DealLinkedEntitiesTab'
|
|
27
|
+
import { DealPeopleSection } from '../../../../components/detail/DealPeopleSection'
|
|
27
28
|
import { ConfirmDealLostDialog } from '../../../../components/detail/ConfirmDealLostDialog'
|
|
28
29
|
import { DealLostSummaryDialog } from '../../../../components/detail/DealLostSummaryDialog'
|
|
29
30
|
import { DealWonPopup } from '../../../../components/detail/DealWonPopup'
|
|
@@ -116,7 +117,7 @@ export default function DealDetailPage({ params }: { params?: { id?: string } })
|
|
|
116
117
|
setActiveTab(initialTab)
|
|
117
118
|
}, [initialTab])
|
|
118
119
|
|
|
119
|
-
const {
|
|
120
|
+
const { searchCompaniesPage, fetchCompaniesByIds } = useDealAssociationLookups({
|
|
120
121
|
excludeLinkedDealId: data?.deal.id ?? null,
|
|
121
122
|
})
|
|
122
123
|
|
|
@@ -191,7 +192,6 @@ export default function DealDetailPage({ params }: { params?: { id?: string } })
|
|
|
191
192
|
companiesSaving,
|
|
192
193
|
handlePeopleAssociationsChange,
|
|
193
194
|
handleCompaniesAssociationsChange,
|
|
194
|
-
loadLinkedPeoplePage,
|
|
195
195
|
loadLinkedCompaniesPage,
|
|
196
196
|
} = useDealAssociations({
|
|
197
197
|
currentDealId,
|
|
@@ -534,22 +534,20 @@ export default function DealDetailPage({ params }: { params?: { id?: string } })
|
|
|
534
534
|
|
|
535
535
|
if (activeTab === 'people') {
|
|
536
536
|
return (
|
|
537
|
-
<
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
manageLabel={t('customers.deals.detail.peopleEditorTitle', 'Manage linked people')}
|
|
541
|
-
searchPlaceholder={t('customers.deals.detail.peopleSearch', 'Search linked people…')}
|
|
542
|
-
linkedItems={data.people}
|
|
543
|
-
linkedCount={data.counts.people}
|
|
537
|
+
<DealPeopleSection
|
|
538
|
+
dealId={data.deal.id}
|
|
539
|
+
dealName={dealName}
|
|
544
540
|
selectedIds={peopleEditorIds}
|
|
545
|
-
disabled={peopleSaving || isSaving}
|
|
546
|
-
savePending={peopleSaving}
|
|
547
|
-
hrefBuilder={(personId) => `/backend/customers/people-v2/${encodeURIComponent(personId)}`}
|
|
548
541
|
onSaveSelection={(next) => handlePeopleAssociationsChange(next)}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
542
|
+
addActionLabel={t('customers.deals.detail.peopleAdd', 'Add person')}
|
|
543
|
+
disabled={peopleSaving || isSaving}
|
|
544
|
+
emptyLabel={t('customers.deals.detail.peopleEmpty', 'No people linked to this deal yet.')}
|
|
545
|
+
emptyState={{
|
|
546
|
+
title: t('customers.deals.detail.peopleEmptyTitle', 'Link the people involved'),
|
|
547
|
+
actionLabel: t('customers.deals.detail.peopleEmptyAction', 'Add person'),
|
|
548
|
+
}}
|
|
549
|
+
translator={detailTranslator}
|
|
550
|
+
runGuardedMutation={runMutationWithContext}
|
|
553
551
|
/>
|
|
554
552
|
)
|
|
555
553
|
}
|