@open-mercato/core 0.6.6-develop.6313.1.618a49a6b4 → 0.6.6-develop.6317.1.b3be10ab84
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/modules/customers/ai-tools/_shared.js +158 -0
- package/dist/modules/customers/ai-tools/_shared.js.map +7 -0
- package/dist/modules/customers/ai-tools/companies-pack.js +15 -121
- package/dist/modules/customers/ai-tools/companies-pack.js.map +2 -2
- package/dist/modules/customers/ai-tools/people-pack.js +9 -107
- package/dist/modules/customers/ai-tools/people-pack.js.map +2 -2
- package/dist/modules/customers/components/DictionarySettings.js +92 -37
- package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarScreen.js +1 -0
- package/dist/modules/customers/components/calendar/CalendarScreen.js.map +2 -2
- package/dist/modules/customers/components/calendar/EventPeekPopover.js +12 -1
- package/dist/modules/customers/components/calendar/EventPeekPopover.js.map +2 -2
- package/dist/modules/customers/components/calendar/TimeGrid.js +3 -0
- package/dist/modules/customers/components/calendar/TimeGrid.js.map +2 -2
- package/dist/modules/customers/components/calendar/types.js.map +1 -1
- package/dist/modules/customers/components/formConfig.js +7 -1
- package/dist/modules/customers/components/formConfig.js.map +2 -2
- package/dist/modules/customers/lib/dictionaries.js +10 -0
- package/dist/modules/customers/lib/dictionaries.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/customers/ai-tools/_shared.ts +270 -0
- package/src/modules/customers/ai-tools/companies-pack.ts +17 -157
- package/src/modules/customers/ai-tools/people-pack.ts +11 -133
- package/src/modules/customers/components/DictionarySettings.tsx +60 -2
- package/src/modules/customers/components/calendar/CalendarScreen.tsx +1 -0
- package/src/modules/customers/components/calendar/EventPeekPopover.tsx +29 -11
- package/src/modules/customers/components/calendar/TimeGrid.tsx +3 -0
- package/src/modules/customers/components/calendar/types.ts +1 -0
- package/src/modules/customers/components/formConfig.tsx +8 -2
- package/src/modules/customers/i18n/de.json +1 -0
- package/src/modules/customers/i18n/en.json +1 -0
- package/src/modules/customers/i18n/es.json +1 -0
- package/src/modules/customers/i18n/pl.json +1 -0
- package/src/modules/customers/lib/dictionaries.ts +10 -0
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
* documented aggregate detail route). Tool name, schema, requiredFeatures,
|
|
18
18
|
* and output shape are unchanged.
|
|
19
19
|
*/
|
|
20
|
-
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
21
20
|
import { z } from 'zod'
|
|
22
21
|
import { defineApiBackedAiTool } from '@open-mercato/ai-assistant/modules/ai_assistant/lib/api-backed-tool'
|
|
23
22
|
import {
|
|
@@ -30,20 +29,17 @@ import {
|
|
|
30
29
|
CustomerPersonProfile,
|
|
31
30
|
} from '../data/entities'
|
|
32
31
|
import { assertTenantScope, type CustomersAiToolDefinition, type CustomersToolContext } from './types'
|
|
32
|
+
import {
|
|
33
|
+
buildRelatedRecords,
|
|
34
|
+
buildScope,
|
|
35
|
+
resolveEm,
|
|
36
|
+
toCustomerListSummary,
|
|
37
|
+
toIso,
|
|
38
|
+
type CustomerRelatedRecords,
|
|
39
|
+
} from './_shared'
|
|
33
40
|
|
|
34
41
|
const NIL_UUID = '00000000-0000-0000-0000-000000000000'
|
|
35
42
|
|
|
36
|
-
function resolveEm(ctx: CustomersToolContext | AiToolExecutionContext): EntityManager {
|
|
37
|
-
return ctx.container.resolve<EntityManager>('em')
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function buildScope(ctx: CustomersToolContext | AiToolExecutionContext, tenantId: string) {
|
|
41
|
-
return {
|
|
42
|
-
tenantId,
|
|
43
|
-
organizationId: ctx.organizationId,
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
43
|
const listPeopleInput = z
|
|
48
44
|
.object({
|
|
49
45
|
q: z.string().trim().optional().describe('Optional search text matched against display name / email / phone. Omit or leave empty to list all.'),
|
|
@@ -146,23 +142,7 @@ const listPeopleTool = defineApiBackedAiTool<ListPeopleInput, ListPeopleApiRespo
|
|
|
146
142
|
const data = (response.data ?? {}) as ListPeopleApiResponse
|
|
147
143
|
const rawItems: ListPeopleApiItem[] = Array.isArray(data.items) ? data.items : []
|
|
148
144
|
return {
|
|
149
|
-
items: rawItems.map((row) =>
|
|
150
|
-
const createdAtRaw = row.created_at ?? row.createdAt ?? null
|
|
151
|
-
const createdAt = createdAtRaw ? new Date(String(createdAtRaw)).toISOString() : null
|
|
152
|
-
return {
|
|
153
|
-
id: row.id,
|
|
154
|
-
displayName: row.display_name ?? row.displayName ?? null,
|
|
155
|
-
primaryEmail: row.primary_email ?? row.primaryEmail ?? null,
|
|
156
|
-
primaryPhone: row.primary_phone ?? row.primaryPhone ?? null,
|
|
157
|
-
status: row.status ?? null,
|
|
158
|
-
lifecycleStage: row.lifecycle_stage ?? row.lifecycleStage ?? null,
|
|
159
|
-
source: row.source ?? null,
|
|
160
|
-
ownerUserId: row.owner_user_id ?? row.ownerUserId ?? null,
|
|
161
|
-
organizationId: row.organization_id ?? row.organizationId ?? null,
|
|
162
|
-
tenantId: row.tenant_id ?? row.tenantId ?? null,
|
|
163
|
-
createdAt,
|
|
164
|
-
}
|
|
165
|
-
}),
|
|
145
|
+
items: rawItems.map((row) => toCustomerListSummary(row)),
|
|
166
146
|
total: typeof data.total === 'number' ? data.total : 0,
|
|
167
147
|
limit,
|
|
168
148
|
offset,
|
|
@@ -182,13 +162,6 @@ type GetPersonInput = z.infer<typeof getPersonInput>
|
|
|
182
162
|
|
|
183
163
|
type ApiPersonDetailRow = Record<string, unknown> | null | undefined
|
|
184
164
|
|
|
185
|
-
function toIso(value: unknown): string | null {
|
|
186
|
-
if (!value) return null
|
|
187
|
-
const dt = value instanceof Date ? value : new Date(String(value))
|
|
188
|
-
if (Number.isNaN(dt.getTime())) return null
|
|
189
|
-
return dt.toISOString()
|
|
190
|
-
}
|
|
191
|
-
|
|
192
165
|
const getPersonTool: CustomersAiToolDefinition = {
|
|
193
166
|
name: 'customers.get_person',
|
|
194
167
|
displayName: 'Get person',
|
|
@@ -226,104 +199,9 @@ const getPersonTool: CustomersAiToolDefinition = {
|
|
|
226
199
|
const profileRow = (data.profile ?? null) as ApiPersonDetailRow
|
|
227
200
|
const customFields = (data.customFields ?? {}) as Record<string, unknown>
|
|
228
201
|
|
|
229
|
-
let related:
|
|
202
|
+
let related: CustomerRelatedRecords | null = null
|
|
230
203
|
if (includeRelated) {
|
|
231
|
-
|
|
232
|
-
const activities = Array.isArray(data.activities) ? (data.activities as Array<Record<string, unknown>>) : []
|
|
233
|
-
const notes = Array.isArray(data.comments) ? (data.comments as Array<Record<string, unknown>>) : []
|
|
234
|
-
const todos = Array.isArray(data.todos) ? (data.todos as Array<Record<string, unknown>>) : []
|
|
235
|
-
const interactions = Array.isArray(data.interactions) ? (data.interactions as Array<Record<string, unknown>>) : []
|
|
236
|
-
const tagsRows = Array.isArray(data.tags) ? (data.tags as Array<Record<string, unknown>>) : []
|
|
237
|
-
const dealsRows = Array.isArray(data.deals) ? (data.deals as Array<Record<string, unknown>>) : []
|
|
238
|
-
related = {
|
|
239
|
-
addresses: addresses.map((address) => ({
|
|
240
|
-
id: address.id,
|
|
241
|
-
name: address.name ?? null,
|
|
242
|
-
purpose: address.purpose ?? null,
|
|
243
|
-
addressLine1: address.addressLine1 ?? null,
|
|
244
|
-
addressLine2: address.addressLine2 ?? null,
|
|
245
|
-
city: address.city ?? null,
|
|
246
|
-
region: address.region ?? null,
|
|
247
|
-
postalCode: address.postalCode ?? null,
|
|
248
|
-
country: address.country ?? null,
|
|
249
|
-
isPrimary: !!address.isPrimary,
|
|
250
|
-
})),
|
|
251
|
-
activities: activities.map((activity) => ({
|
|
252
|
-
id: activity.id,
|
|
253
|
-
activityType: activity.activityType,
|
|
254
|
-
subject: activity.subject ?? null,
|
|
255
|
-
body: activity.body ?? null,
|
|
256
|
-
occurredAt: toIso(activity.occurredAt),
|
|
257
|
-
createdAt: toIso(activity.createdAt),
|
|
258
|
-
})),
|
|
259
|
-
notes: notes.map((comment) => ({
|
|
260
|
-
id: comment.id,
|
|
261
|
-
body: comment.body,
|
|
262
|
-
authorUserId: comment.authorUserId ?? null,
|
|
263
|
-
createdAt: toIso(comment.createdAt),
|
|
264
|
-
})),
|
|
265
|
-
tasks: todos.map((task) => ({
|
|
266
|
-
id: task.id,
|
|
267
|
-
todoId: task.todoId ?? task.id,
|
|
268
|
-
todoSource: task.todoSource ?? null,
|
|
269
|
-
createdAt: toIso(task.createdAt),
|
|
270
|
-
})),
|
|
271
|
-
interactions: interactions.map((interaction) => ({
|
|
272
|
-
id: interaction.id,
|
|
273
|
-
interactionType: interaction.interactionType,
|
|
274
|
-
title: interaction.title ?? null,
|
|
275
|
-
status: interaction.status,
|
|
276
|
-
scheduledAt: toIso(interaction.scheduledAt),
|
|
277
|
-
occurredAt: toIso(interaction.occurredAt),
|
|
278
|
-
})),
|
|
279
|
-
tags: tagsRows
|
|
280
|
-
.map((tag) => {
|
|
281
|
-
if (!tag || typeof tag !== 'object') return null
|
|
282
|
-
const id = typeof tag.id === 'string' ? tag.id : null
|
|
283
|
-
const label = typeof tag.label === 'string' ? tag.label : null
|
|
284
|
-
if (!id || !label) return null
|
|
285
|
-
const slug = typeof tag.slug === 'string' ? tag.slug : label
|
|
286
|
-
const color = typeof tag.color === 'string' ? tag.color : null
|
|
287
|
-
return { id, slug, label, color }
|
|
288
|
-
})
|
|
289
|
-
.filter(
|
|
290
|
-
(entry): entry is { id: string; slug: string; label: string; color: string | null } =>
|
|
291
|
-
entry !== null,
|
|
292
|
-
),
|
|
293
|
-
deals: dealsRows
|
|
294
|
-
.map((deal) => {
|
|
295
|
-
if (!deal || typeof deal !== 'object') return null
|
|
296
|
-
const id = typeof deal.id === 'string' ? deal.id : null
|
|
297
|
-
if (!id) return null
|
|
298
|
-
return {
|
|
299
|
-
id,
|
|
300
|
-
title: typeof deal.title === 'string' ? deal.title : '',
|
|
301
|
-
status: typeof deal.status === 'string' ? deal.status : null,
|
|
302
|
-
pipelineStageId:
|
|
303
|
-
typeof deal.pipelineStageId === 'string' ? deal.pipelineStageId : null,
|
|
304
|
-
valueAmount:
|
|
305
|
-
typeof deal.valueAmount === 'string'
|
|
306
|
-
? deal.valueAmount
|
|
307
|
-
: deal.valueAmount === null || deal.valueAmount === undefined
|
|
308
|
-
? null
|
|
309
|
-
: String(deal.valueAmount),
|
|
310
|
-
valueCurrency:
|
|
311
|
-
typeof deal.valueCurrency === 'string' ? deal.valueCurrency : null,
|
|
312
|
-
}
|
|
313
|
-
})
|
|
314
|
-
.filter(
|
|
315
|
-
(
|
|
316
|
-
value,
|
|
317
|
-
): value is {
|
|
318
|
-
id: string
|
|
319
|
-
title: string
|
|
320
|
-
status: string | null
|
|
321
|
-
pipelineStageId: string | null
|
|
322
|
-
valueAmount: string | null
|
|
323
|
-
valueCurrency: string | null
|
|
324
|
-
} => value !== null,
|
|
325
|
-
),
|
|
326
|
-
}
|
|
204
|
+
related = buildRelatedRecords(data)
|
|
327
205
|
}
|
|
328
206
|
|
|
329
207
|
return {
|
|
@@ -14,7 +14,10 @@ import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuarde
|
|
|
14
14
|
import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
15
15
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
16
16
|
import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
17
|
-
import
|
|
17
|
+
import {
|
|
18
|
+
getCustomerDictionarySettingsSectionId,
|
|
19
|
+
type CustomerDictionaryKind,
|
|
20
|
+
} from '../lib/dictionaries'
|
|
18
21
|
import { ICON_SUGGESTIONS } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'
|
|
19
22
|
import {
|
|
20
23
|
DictionaryForm,
|
|
@@ -47,6 +50,19 @@ const DEFAULT_FORM_VALUES: DictionaryFormValues = {
|
|
|
47
50
|
icon: null,
|
|
48
51
|
}
|
|
49
52
|
|
|
53
|
+
const DICTIONARY_HASH_SCROLL_RETRY_DELAYS_MS = [100, 350, 1000] as const
|
|
54
|
+
|
|
55
|
+
function getCurrentDictionaryHashTarget(): HTMLElement | null {
|
|
56
|
+
if (typeof window === 'undefined') return null
|
|
57
|
+
const hash = window.location.hash
|
|
58
|
+
if (!hash.startsWith('#customer-dictionary-')) return null
|
|
59
|
+
try {
|
|
60
|
+
return document.getElementById(decodeURIComponent(hash.slice(1)))
|
|
61
|
+
} catch {
|
|
62
|
+
return document.getElementById(hash.slice(1))
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
50
66
|
export default function DictionarySettings() {
|
|
51
67
|
const t = useT()
|
|
52
68
|
|
|
@@ -98,6 +114,45 @@ export default function DictionarySettings() {
|
|
|
98
114
|
},
|
|
99
115
|
], [t])
|
|
100
116
|
|
|
117
|
+
React.useEffect(() => {
|
|
118
|
+
const timeoutIds: number[] = []
|
|
119
|
+
let frameId: number | null = null
|
|
120
|
+
|
|
121
|
+
const clearScheduledScrolls = () => {
|
|
122
|
+
if (frameId !== null) {
|
|
123
|
+
window.cancelAnimationFrame(frameId)
|
|
124
|
+
frameId = null
|
|
125
|
+
}
|
|
126
|
+
while (timeoutIds.length > 0) {
|
|
127
|
+
const timeoutId = timeoutIds.pop()
|
|
128
|
+
if (timeoutId !== undefined) window.clearTimeout(timeoutId)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const scrollToHashTarget = () => {
|
|
133
|
+
getCurrentDictionaryHashTarget()?.scrollIntoView({ block: 'start' })
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const scheduleHashScroll = () => {
|
|
137
|
+
clearScheduledScrolls()
|
|
138
|
+
if (!window.location.hash.startsWith('#customer-dictionary-')) return
|
|
139
|
+
scrollToHashTarget()
|
|
140
|
+
if (typeof window.requestAnimationFrame === 'function') {
|
|
141
|
+
frameId = window.requestAnimationFrame(scrollToHashTarget)
|
|
142
|
+
}
|
|
143
|
+
DICTIONARY_HASH_SCROLL_RETRY_DELAYS_MS.forEach((delay) => {
|
|
144
|
+
timeoutIds.push(window.setTimeout(scrollToHashTarget, delay))
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
scheduleHashScroll()
|
|
149
|
+
window.addEventListener('hashchange', scheduleHashScroll)
|
|
150
|
+
return () => {
|
|
151
|
+
window.removeEventListener('hashchange', scheduleHashScroll)
|
|
152
|
+
clearScheduledScrolls()
|
|
153
|
+
}
|
|
154
|
+
}, [])
|
|
155
|
+
|
|
101
156
|
return (
|
|
102
157
|
<div className="space-y-8">
|
|
103
158
|
<header className="space-y-2">
|
|
@@ -393,7 +448,10 @@ function CustomerDictionarySection({ kind, title, description }: CustomerDiction
|
|
|
393
448
|
}), [dialog, t])
|
|
394
449
|
|
|
395
450
|
return (
|
|
396
|
-
<section
|
|
451
|
+
<section
|
|
452
|
+
id={getCustomerDictionarySettingsSectionId(kind)}
|
|
453
|
+
className="scroll-mt-24 rounded border bg-card text-card-foreground shadow-sm"
|
|
454
|
+
>
|
|
397
455
|
<div className="border-b px-6 py-4 space-y-1">
|
|
398
456
|
<h2 className="text-lg font-medium">{title}</h2>
|
|
399
457
|
<p className="text-sm text-muted-foreground">{description}</p>
|
|
@@ -499,6 +499,7 @@ export function CalendarScreen() {
|
|
|
499
499
|
showWeekends={preferences.showWeekends}
|
|
500
500
|
showConflicts={preferences.conflictWarnings}
|
|
501
501
|
aiSummaries={preferences.aiSummaries}
|
|
502
|
+
canManage={canManage}
|
|
502
503
|
highlightItemId={highlightItemId}
|
|
503
504
|
onItemClick={openEditEditor}
|
|
504
505
|
onJoin={handleJoin}
|
|
@@ -5,6 +5,7 @@ import { Sparkles } from 'lucide-react'
|
|
|
5
5
|
import { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'
|
|
6
6
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
7
7
|
import { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'
|
|
8
|
+
import { SimpleTooltip } from '@open-mercato/ui/primitives/tooltip'
|
|
8
9
|
import { formatTimeRange } from './EventBlock'
|
|
9
10
|
import type { CalendarItem, CalendarPlatform } from './types'
|
|
10
11
|
|
|
@@ -20,6 +21,7 @@ export type EventPeekPopoverProps = {
|
|
|
20
21
|
open: boolean
|
|
21
22
|
joinUrl: string | null
|
|
22
23
|
aiSummaries: boolean
|
|
24
|
+
canManage?: boolean
|
|
23
25
|
onOpenChange(open: boolean): void
|
|
24
26
|
onJoin(item: CalendarItem): void
|
|
25
27
|
onEdit(item: CalendarItem): void
|
|
@@ -31,6 +33,7 @@ export function EventPeekPopover({
|
|
|
31
33
|
open,
|
|
32
34
|
joinUrl,
|
|
33
35
|
aiSummaries,
|
|
36
|
+
canManage = true,
|
|
34
37
|
onOpenChange,
|
|
35
38
|
onJoin,
|
|
36
39
|
onEdit,
|
|
@@ -81,17 +84,32 @@ export function EventPeekPopover({
|
|
|
81
84
|
{t('customers.calendar.peek.join', 'Join')}
|
|
82
85
|
</Button>
|
|
83
86
|
) : null}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
87
|
+
{canManage ? (
|
|
88
|
+
<Button
|
|
89
|
+
type="button"
|
|
90
|
+
size="sm"
|
|
91
|
+
variant="secondary"
|
|
92
|
+
onClick={() => {
|
|
93
|
+
onOpenChange(false)
|
|
94
|
+
onEdit(item)
|
|
95
|
+
}}
|
|
96
|
+
>
|
|
97
|
+
{t('customers.calendar.peek.edit', 'Edit')}
|
|
98
|
+
</Button>
|
|
99
|
+
) : (
|
|
100
|
+
<SimpleTooltip
|
|
101
|
+
content={t(
|
|
102
|
+
'customers.calendar.peek.editForbidden',
|
|
103
|
+
"You don't have permission to edit events",
|
|
104
|
+
)}
|
|
105
|
+
>
|
|
106
|
+
<span className="inline-flex">
|
|
107
|
+
<Button type="button" size="sm" variant="secondary" disabled className="pointer-events-none">
|
|
108
|
+
{t('customers.calendar.peek.edit', 'Edit')}
|
|
109
|
+
</Button>
|
|
110
|
+
</span>
|
|
111
|
+
</SimpleTooltip>
|
|
112
|
+
)}
|
|
95
113
|
</div>
|
|
96
114
|
</div>
|
|
97
115
|
</PopoverContent>
|
|
@@ -160,6 +160,7 @@ export function TimeGrid({
|
|
|
160
160
|
showWeekends,
|
|
161
161
|
showConflicts,
|
|
162
162
|
aiSummaries,
|
|
163
|
+
canManage = true,
|
|
163
164
|
highlightItemId,
|
|
164
165
|
onItemClick,
|
|
165
166
|
onJoin,
|
|
@@ -351,6 +352,7 @@ export function TimeGrid({
|
|
|
351
352
|
open={selectedId === item.id}
|
|
352
353
|
joinUrl={resolveJoinUrl(item.location)}
|
|
353
354
|
aiSummaries={aiSummaries}
|
|
355
|
+
canManage={canManage}
|
|
354
356
|
onOpenChange={(open) => setSelectedId(open ? item.id : null)}
|
|
355
357
|
onJoin={onJoin}
|
|
356
358
|
onEdit={onItemClick}
|
|
@@ -462,6 +464,7 @@ export function TimeGrid({
|
|
|
462
464
|
open={selectedId === block.item.id}
|
|
463
465
|
joinUrl={resolveJoinUrl(block.item.location)}
|
|
464
466
|
aiSummaries={aiSummaries}
|
|
467
|
+
canManage={canManage}
|
|
465
468
|
onOpenChange={(open) => setSelectedId(open ? block.item.id : null)}
|
|
466
469
|
onJoin={onJoin}
|
|
467
470
|
onEdit={onItemClick}
|
|
@@ -51,7 +51,11 @@ import {
|
|
|
51
51
|
ensureCustomerDictionary,
|
|
52
52
|
invalidateCustomerDictionary,
|
|
53
53
|
} from './detail/hooks/useCustomerDictionary'
|
|
54
|
-
import
|
|
54
|
+
import {
|
|
55
|
+
CUSTOMER_DICTIONARIES_MANAGE_HREF,
|
|
56
|
+
getCustomerDictionaryManageHref,
|
|
57
|
+
type CustomerDictionaryKind,
|
|
58
|
+
} from '../lib/dictionaries'
|
|
55
59
|
import { normalizeCustomFieldSubmitValue } from './detail/customFieldUtils'
|
|
56
60
|
import { CUSTOMER_PHONE_INVALID_MESSAGE_KEY } from '../data/validators'
|
|
57
61
|
|
|
@@ -116,6 +120,8 @@ type DictionarySelectFieldProps = {
|
|
|
116
120
|
showActiveAppearance?: boolean
|
|
117
121
|
}
|
|
118
122
|
|
|
123
|
+
export { CUSTOMER_DICTIONARIES_MANAGE_HREF, getCustomerDictionaryManageHref }
|
|
124
|
+
|
|
119
125
|
const emailValidationSchema = z.string().email()
|
|
120
126
|
const EMAIL_CHECK_DEBOUNCE_MS = 350
|
|
121
127
|
|
|
@@ -233,7 +239,7 @@ export function DictionarySelectField({
|
|
|
233
239
|
fetchOptions={fetchOptions}
|
|
234
240
|
createOption={createOption}
|
|
235
241
|
labels={labels}
|
|
236
|
-
manageHref={manageHref}
|
|
242
|
+
manageHref={manageHref ?? getCustomerDictionaryManageHref(kind)}
|
|
237
243
|
selectClassName={selectClassName}
|
|
238
244
|
allowInlineCreate={allowInlineCreate}
|
|
239
245
|
allowAppearance={allowAppearance}
|
|
@@ -312,6 +312,7 @@
|
|
|
312
312
|
"customers.calendar.peek.attendee": "1 Teilnehmer",
|
|
313
313
|
"customers.calendar.peek.attendees": "{count} Teilnehmer",
|
|
314
314
|
"customers.calendar.peek.edit": "Bearbeiten",
|
|
315
|
+
"customers.calendar.peek.editForbidden": "Sie haben keine Berechtigung, Termine zu bearbeiten",
|
|
315
316
|
"customers.calendar.peek.join": "Beitreten",
|
|
316
317
|
"customers.calendar.platform.meet": "Meet",
|
|
317
318
|
"customers.calendar.platform.slack": "Slack",
|
|
@@ -312,6 +312,7 @@
|
|
|
312
312
|
"customers.calendar.peek.attendee": "1 attendee",
|
|
313
313
|
"customers.calendar.peek.attendees": "{count} attendees",
|
|
314
314
|
"customers.calendar.peek.edit": "Edit",
|
|
315
|
+
"customers.calendar.peek.editForbidden": "You don't have permission to edit events",
|
|
315
316
|
"customers.calendar.peek.join": "Join",
|
|
316
317
|
"customers.calendar.platform.meet": "Meet",
|
|
317
318
|
"customers.calendar.platform.slack": "Slack",
|
|
@@ -312,6 +312,7 @@
|
|
|
312
312
|
"customers.calendar.peek.attendee": "1 asistente",
|
|
313
313
|
"customers.calendar.peek.attendees": "{count} asistentes",
|
|
314
314
|
"customers.calendar.peek.edit": "Editar",
|
|
315
|
+
"customers.calendar.peek.editForbidden": "No tienes permiso para editar eventos",
|
|
315
316
|
"customers.calendar.peek.join": "Unirse",
|
|
316
317
|
"customers.calendar.platform.meet": "Meet",
|
|
317
318
|
"customers.calendar.platform.slack": "Slack",
|
|
@@ -312,6 +312,7 @@
|
|
|
312
312
|
"customers.calendar.peek.attendee": "1 uczestnik",
|
|
313
313
|
"customers.calendar.peek.attendees": "{count} uczestników",
|
|
314
314
|
"customers.calendar.peek.edit": "Edytuj",
|
|
315
|
+
"customers.calendar.peek.editForbidden": "Nie masz uprawnień do edycji wydarzeń",
|
|
315
316
|
"customers.calendar.peek.join": "Dołącz",
|
|
316
317
|
"customers.calendar.platform.meet": "Meet",
|
|
317
318
|
"customers.calendar.platform.slack": "Slack",
|
|
@@ -30,6 +30,16 @@ export type CustomerDictionaryKind = typeof CUSTOMER_DICTIONARY_KINDS[number]
|
|
|
30
30
|
export type CustomerDictionaryDisplayEntry = DictionaryDisplayEntry
|
|
31
31
|
export type CustomerDictionaryMap = DictionaryMap
|
|
32
32
|
|
|
33
|
+
export const CUSTOMER_DICTIONARIES_MANAGE_HREF = '/backend/config/customers'
|
|
34
|
+
|
|
35
|
+
export function getCustomerDictionarySettingsSectionId(kind: CustomerDictionaryKind) {
|
|
36
|
+
return `customer-dictionary-${kind}`
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getCustomerDictionaryManageHref(kind: CustomerDictionaryKind) {
|
|
40
|
+
return `${CUSTOMER_DICTIONARIES_MANAGE_HREF}#${getCustomerDictionarySettingsSectionId(kind)}`
|
|
41
|
+
}
|
|
42
|
+
|
|
33
43
|
export function createEmptyCustomerDictionaryMaps(): Record<CustomerDictionaryKind, CustomerDictionaryMap> {
|
|
34
44
|
return CUSTOMER_DICTIONARY_KINDS.reduce<Record<CustomerDictionaryKind, CustomerDictionaryMap>>(
|
|
35
45
|
(acc, kind) => {
|