@open-mercato/core 0.6.6-develop.6205.1.109e4b6a84 → 0.6.6-develop.6227.1.2695efff30
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/currencies/backend/currencies/[id]/page.js +33 -8
- package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/page.js +63 -27
- package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js +35 -14
- package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
- package/dist/modules/currencies/components/CurrencyFetchingConfig.js +104 -41
- package/dist/modules/currencies/components/CurrencyFetchingConfig.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js +57 -8
- package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +5 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +30 -14
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +29 -19
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +2 -2
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +166 -64
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +2 -2
- package/dist/modules/customers/components/AddressFormatSettings.js +31 -16
- package/dist/modules/customers/components/AddressFormatSettings.js.map +2 -2
- package/dist/modules/customers/components/DictionarySettings.js +57 -31
- package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
- package/dist/modules/customers/components/PipelineSettings.js +156 -76
- package/dist/modules/customers/components/PipelineSettings.js.map +2 -2
- package/dist/modules/customers/components/calendar/AgendaList.js +10 -4
- package/dist/modules/customers/components/calendar/AgendaList.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +18 -2
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
- package/dist/modules/customers/components/calendar/MonthGrid.js +5 -2
- package/dist/modules/customers/components/calendar/MonthGrid.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js +6 -0
- package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/RelatedToField.js +2 -0
- package/dist/modules/customers/components/calendar/editor/RelatedToField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js +4 -1
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js.map +2 -2
- package/dist/modules/customers/lib/calendar/labels.js +33 -0
- package/dist/modules/customers/lib/calendar/labels.js.map +7 -0
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +32 -11
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
- package/dist/modules/data_sync/api/mappings/[id]/route.js +59 -0
- package/dist/modules/data_sync/api/mappings/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/mappings/route.js +44 -0
- package/dist/modules/data_sync/api/mappings/route.js.map +2 -2
- package/dist/modules/data_sync/api/run.js +31 -0
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +31 -0
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/retry.js +31 -0
- package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/[id]/route.js +59 -3
- package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/route.js +33 -4
- package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
- package/dist/modules/directory/api/organization-branding/route.js +54 -2
- package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
- package/dist/modules/directory/backend/directory/branding/page.js +15 -9
- package/dist/modules/directory/backend/directory/branding/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/page.js +28 -9
- package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/tenants/page.js +29 -13
- package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
- package/dist/modules/entities/api/definitions.batch.js +15 -0
- package/dist/modules/entities/api/definitions.batch.js.map +2 -2
- package/dist/modules/entities/api/definitions.js +26 -0
- package/dist/modules/entities/api/definitions.js.map +2 -2
- package/dist/modules/entities/api/definitions.mutation-guard.js +57 -0
- package/dist/modules/entities/api/definitions.mutation-guard.js.map +7 -0
- package/dist/modules/entities/api/definitions.restore.js +15 -0
- package/dist/modules/entities/api/definitions.restore.js.map +2 -2
- package/dist/modules/entities/api/entities.js +31 -3
- package/dist/modules/entities/api/entities.js.map +2 -2
- package/dist/modules/entities/api/records.js +18 -0
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +28 -10
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/feature_toggles/api/overrides/route.js +38 -1
- package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/emails/[id]/route.js +30 -0
- package/dist/modules/inbox_ops/api/emails/[id]/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/[id]/accept-all/route.js +30 -0
- package/dist/modules/inbox_ops/api/proposals/[id]/accept-all/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js +31 -0
- package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/[id]/categorize/route.js +31 -0
- package/dist/modules/inbox_ops/api/proposals/[id]/categorize/route.js.map +2 -2
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js +68 -39
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js.map +2 -2
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +14 -4
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +2 -2
- package/dist/modules/payment_gateways/api/cancel/route.js +37 -0
- package/dist/modules/payment_gateways/api/cancel/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/capture/route.js +37 -0
- package/dist/modules/payment_gateways/api/capture/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/guards.js +31 -0
- package/dist/modules/payment_gateways/api/guards.js.map +7 -0
- package/dist/modules/payment_gateways/api/refund/route.js +37 -0
- package/dist/modules/payment_gateways/api/refund/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/sessions/route.js +37 -0
- package/dist/modules/payment_gateways/api/sessions/route.js.map +2 -2
- package/dist/modules/planner/api/availability-date-specific.js +11 -1
- package/dist/modules/planner/api/availability-date-specific.js.map +2 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js +20 -3
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
- package/dist/modules/planner/commands/availability-date-specific.js +16 -0
- package/dist/modules/planner/commands/availability-date-specific.js.map +2 -2
- package/dist/modules/planner/components/AvailabilityRulesEditor.js +109 -40
- package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
- package/dist/modules/query_index/api/purge.js +35 -3
- package/dist/modules/query_index/api/purge.js.map +2 -2
- package/dist/modules/query_index/api/reindex.js +41 -3
- package/dist/modules/query_index/api/reindex.js.map +2 -2
- package/dist/modules/query_index/components/QueryIndexesTable.js +57 -24
- package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
- package/dist/modules/shipping_carriers/api/shipments/route.js +31 -0
- package/dist/modules/shipping_carriers/api/shipments/route.js.map +2 -2
- package/dist/modules/shipping_carriers/api/tracking/refresh/route.js +55 -0
- package/dist/modules/shipping_carriers/api/tracking/refresh/route.js.map +7 -0
- package/dist/modules/shipping_carriers/data/validators.js +6 -1
- package/dist/modules/shipping_carriers/data/validators.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js +28 -8
- package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/shipping-service.js +37 -7
- package/dist/modules/shipping_carriers/lib/shipping-service.js.map +2 -2
- package/dist/modules/shipping_carriers/workers/status-poller.js +1 -1
- package/dist/modules/shipping_carriers/workers/status-poller.js.map +2 -2
- package/dist/modules/translations/components/TranslationManager.js +49 -20
- package/dist/modules/translations/components/TranslationManager.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/currencies/backend/currencies/[id]/page.tsx +40 -10
- package/src/modules/currencies/backend/currencies/page.tsx +68 -29
- package/src/modules/currencies/backend/exchange-rates/page.tsx +40 -15
- package/src/modules/currencies/components/CurrencyFetchingConfig.tsx +110 -41
- package/src/modules/customer_accounts/api/admin/roles/[id]/acl.ts +69 -7
- package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +16 -8
- package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +32 -14
- package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +32 -20
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +171 -64
- package/src/modules/customers/components/AddressFormatSettings.tsx +39 -19
- package/src/modules/customers/components/DictionarySettings.tsx +63 -29
- package/src/modules/customers/components/PipelineSettings.tsx +165 -80
- package/src/modules/customers/components/calendar/AgendaList.tsx +13 -8
- package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +16 -2
- package/src/modules/customers/components/calendar/MonthGrid.tsx +5 -2
- package/src/modules/customers/components/calendar/editor/PeopleField.tsx +6 -0
- package/src/modules/customers/components/calendar/editor/RelatedToField.tsx +2 -0
- package/src/modules/customers/components/calendar/editor/ScheduleSection.tsx +7 -0
- package/src/modules/customers/i18n/de.json +11 -7
- package/src/modules/customers/i18n/en.json +6 -2
- package/src/modules/customers/i18n/es.json +11 -7
- package/src/modules/customers/i18n/pl.json +11 -7
- package/src/modules/customers/lib/calendar/labels.ts +42 -0
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +39 -11
- package/src/modules/data_sync/api/mappings/[id]/route.ts +63 -0
- package/src/modules/data_sync/api/mappings/route.ts +48 -0
- package/src/modules/data_sync/api/run.ts +33 -0
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +34 -0
- package/src/modules/data_sync/api/runs/[id]/retry.ts +33 -0
- package/src/modules/data_sync/api/schedules/[id]/route.ts +64 -2
- package/src/modules/data_sync/api/schedules/route.ts +36 -4
- package/src/modules/directory/api/organization-branding/route.ts +61 -0
- package/src/modules/directory/backend/directory/branding/page.tsx +16 -10
- package/src/modules/directory/backend/directory/organizations/page.tsx +35 -8
- package/src/modules/directory/backend/directory/tenants/page.tsx +37 -13
- package/src/modules/entities/api/definitions.batch.ts +17 -0
- package/src/modules/entities/api/definitions.mutation-guard.ts +80 -0
- package/src/modules/entities/api/definitions.restore.ts +17 -0
- package/src/modules/entities/api/definitions.ts +30 -0
- package/src/modules/entities/api/entities.ts +35 -3
- package/src/modules/entities/api/records.ts +20 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +33 -10
- package/src/modules/feature_toggles/api/overrides/route.ts +44 -1
- package/src/modules/inbox_ops/api/emails/[id]/route.ts +32 -0
- package/src/modules/inbox_ops/api/proposals/[id]/accept-all/route.ts +32 -0
- package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.ts +33 -0
- package/src/modules/inbox_ops/api/proposals/[id]/categorize/route.ts +33 -0
- package/src/modules/messages/components/message-detail/hooks/useMessageDetailsActions.ts +80 -42
- package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +21 -4
- package/src/modules/payment_gateways/api/cancel/route.ts +39 -0
- package/src/modules/payment_gateways/api/capture/route.ts +39 -0
- package/src/modules/payment_gateways/api/guards.ts +59 -0
- package/src/modules/payment_gateways/api/refund/route.ts +39 -0
- package/src/modules/payment_gateways/api/sessions/route.ts +40 -0
- package/src/modules/planner/api/availability-date-specific.ts +10 -0
- package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +26 -5
- package/src/modules/planner/commands/availability-date-specific.ts +24 -0
- package/src/modules/planner/components/AvailabilityRulesEditor.tsx +122 -41
- package/src/modules/query_index/api/purge.ts +37 -3
- package/src/modules/query_index/api/reindex.ts +43 -3
- package/src/modules/query_index/components/QueryIndexesTable.tsx +66 -24
- package/src/modules/shipping_carriers/api/shipments/route.ts +31 -0
- package/src/modules/shipping_carriers/api/tracking/refresh/route.ts +53 -0
- package/src/modules/shipping_carriers/data/validators.ts +5 -0
- package/src/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.ts +29 -8
- package/src/modules/shipping_carriers/lib/shipping-service.ts +43 -7
- package/src/modules/shipping_carriers/workers/status-poller.ts +1 -1
- package/src/modules/translations/components/TranslationManager.tsx +65 -21
|
@@ -16,6 +16,7 @@ import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
|
16
16
|
import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
|
|
17
17
|
import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
|
|
18
18
|
import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
|
|
19
|
+
import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'
|
|
19
20
|
import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
20
21
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
21
22
|
import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
@@ -103,6 +104,31 @@ export default function PipelineSettings(): React.ReactElement {
|
|
|
103
104
|
const [stageForm, setStageForm] = React.useState({ label: '', color: null as string | null, icon: null as string | null })
|
|
104
105
|
const [submittingStage, setSubmittingStage] = React.useState(false)
|
|
105
106
|
|
|
107
|
+
const { runMutation, retryLastMutation } = useGuardedMutation<{
|
|
108
|
+
formId: string
|
|
109
|
+
resourceKind: string
|
|
110
|
+
retryLastMutation: () => Promise<boolean>
|
|
111
|
+
}>({
|
|
112
|
+
contextId: 'customers-pipeline-settings',
|
|
113
|
+
blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),
|
|
114
|
+
})
|
|
115
|
+
const pipelineMutationContext = React.useMemo(
|
|
116
|
+
() => ({
|
|
117
|
+
formId: 'customers-pipeline-settings',
|
|
118
|
+
resourceKind: 'customers.pipeline',
|
|
119
|
+
retryLastMutation,
|
|
120
|
+
}),
|
|
121
|
+
[retryLastMutation],
|
|
122
|
+
)
|
|
123
|
+
const stageMutationContext = React.useMemo(
|
|
124
|
+
() => ({
|
|
125
|
+
formId: 'customers-pipeline-settings',
|
|
126
|
+
resourceKind: 'customers.pipeline_stage',
|
|
127
|
+
retryLastMutation,
|
|
128
|
+
}),
|
|
129
|
+
[retryLastMutation],
|
|
130
|
+
)
|
|
131
|
+
|
|
106
132
|
const loadPipelines = React.useCallback(async () => {
|
|
107
133
|
setLoadingPipelines(true)
|
|
108
134
|
try {
|
|
@@ -165,38 +191,50 @@ export default function PipelineSettings(): React.ReactElement {
|
|
|
165
191
|
setSubmittingPipeline(true)
|
|
166
192
|
try {
|
|
167
193
|
if (pipelineDialog?.mode === 'create') {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
194
|
+
await runMutation({
|
|
195
|
+
operation: async () => {
|
|
196
|
+
const res = await apiCall('/api/customers/pipelines', {
|
|
197
|
+
method: 'POST',
|
|
198
|
+
headers: { 'content-type': 'application/json' },
|
|
199
|
+
body: JSON.stringify({ name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault }),
|
|
200
|
+
})
|
|
201
|
+
if (!res.ok) {
|
|
202
|
+
await raiseCrudError(res.response, t('customers.pipelines.errors.createFailed', 'Failed to create pipeline'))
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
context: pipelineMutationContext,
|
|
206
|
+
mutationPayload: { action: 'create', name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault },
|
|
172
207
|
})
|
|
173
|
-
if (!res.ok) {
|
|
174
|
-
await raiseCrudError(res.response, t('customers.pipelines.errors.createFailed', 'Failed to create pipeline'))
|
|
175
|
-
return
|
|
176
|
-
}
|
|
177
208
|
flash(t('customers.pipelines.flash.created', 'Pipeline created'), 'success')
|
|
178
209
|
} else if (pipelineDialog?.mode === 'edit') {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
210
|
+
await runMutation({
|
|
211
|
+
operation: async () => {
|
|
212
|
+
const res = await withScopedApiRequestHeaders(
|
|
213
|
+
buildOptimisticLockHeader(pipelineDialog.entry.updatedAt),
|
|
214
|
+
() =>
|
|
215
|
+
apiCall('/api/customers/pipelines', {
|
|
216
|
+
method: 'PUT',
|
|
217
|
+
headers: { 'content-type': 'application/json' },
|
|
218
|
+
body: JSON.stringify({ id: pipelineDialog.entry.id, name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault }),
|
|
219
|
+
}),
|
|
220
|
+
)
|
|
221
|
+
if (!res.ok) {
|
|
222
|
+
await raiseCrudError(res.response, t('customers.pipelines.errors.updateFailed', 'Failed to update pipeline'))
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
context: pipelineMutationContext,
|
|
226
|
+
mutationPayload: { action: 'update', id: pipelineDialog.entry.id, name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault },
|
|
227
|
+
})
|
|
192
228
|
flash(t('customers.pipelines.flash.updated', 'Pipeline updated'), 'success')
|
|
193
229
|
}
|
|
194
230
|
setPipelineDialog(null)
|
|
195
231
|
await loadPipelines()
|
|
232
|
+
} catch {
|
|
233
|
+
return
|
|
196
234
|
} finally {
|
|
197
235
|
setSubmittingPipeline(false)
|
|
198
236
|
}
|
|
199
|
-
}, [pipelineDialog, pipelineForm, loadPipelines, t])
|
|
237
|
+
}, [pipelineDialog, pipelineForm, loadPipelines, pipelineMutationContext, runMutation, t])
|
|
200
238
|
|
|
201
239
|
const handleDeletePipeline = React.useCallback(async (pipeline: Pipeline) => {
|
|
202
240
|
const confirmed = await confirm({
|
|
@@ -206,25 +244,36 @@ export default function PipelineSettings(): React.ReactElement {
|
|
|
206
244
|
variant: 'destructive',
|
|
207
245
|
})
|
|
208
246
|
if (!confirmed) return
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
247
|
+
try {
|
|
248
|
+
await runMutation({
|
|
249
|
+
operation: async () => {
|
|
250
|
+
const res = await withScopedApiRequestHeaders(
|
|
251
|
+
buildOptimisticLockHeader(pipeline.updatedAt),
|
|
252
|
+
() =>
|
|
253
|
+
apiCall('/api/customers/pipelines', {
|
|
254
|
+
method: 'DELETE',
|
|
255
|
+
headers: { 'content-type': 'application/json' },
|
|
256
|
+
body: JSON.stringify({ id: pipeline.id }),
|
|
257
|
+
}),
|
|
258
|
+
)
|
|
259
|
+
if (!res.ok) {
|
|
260
|
+
const body = (res.result ?? {}) as Record<string, unknown>
|
|
261
|
+
const msg = typeof body.error === 'string' ? body.error : t('customers.pipelines.errors.deleteFailed', 'Failed to delete pipeline')
|
|
262
|
+
throw new Error(msg)
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
context: pipelineMutationContext,
|
|
266
|
+
mutationPayload: { action: 'delete', id: pipeline.id },
|
|
267
|
+
})
|
|
268
|
+
} catch (err) {
|
|
269
|
+
const msg = err instanceof Error && err.message ? err.message : t('customers.pipelines.errors.deleteFailed', 'Failed to delete pipeline')
|
|
221
270
|
flash(msg, 'error')
|
|
222
271
|
return
|
|
223
272
|
}
|
|
224
273
|
flash(t('customers.pipelines.flash.deleted', 'Pipeline deleted'), 'success')
|
|
225
274
|
if (expandedPipelineId === pipeline.id) setExpandedPipelineId(null)
|
|
226
275
|
await loadPipelines()
|
|
227
|
-
}, [confirm, expandedPipelineId, loadPipelines, t])
|
|
276
|
+
}, [confirm, expandedPipelineId, loadPipelines, pipelineMutationContext, runMutation, t])
|
|
228
277
|
|
|
229
278
|
const openCreateStage = React.useCallback((pipelineId: string) => {
|
|
230
279
|
setStageForm({ label: '', color: null, icon: null })
|
|
@@ -249,39 +298,53 @@ export default function PipelineSettings(): React.ReactElement {
|
|
|
249
298
|
if (stageForm.icon) appearance.icon = stageForm.icon
|
|
250
299
|
|
|
251
300
|
if (stageDialog?.mode === 'create') {
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
301
|
+
const targetPipelineId = stageDialog.pipelineId
|
|
302
|
+
await runMutation({
|
|
303
|
+
operation: async () => {
|
|
304
|
+
const res = await apiCall('/api/customers/pipeline-stages', {
|
|
305
|
+
method: 'POST',
|
|
306
|
+
headers: { 'content-type': 'application/json' },
|
|
307
|
+
body: JSON.stringify({ pipelineId: targetPipelineId, label: stageForm.label.trim(), ...appearance }),
|
|
308
|
+
})
|
|
309
|
+
if (!res.ok) {
|
|
310
|
+
await raiseCrudError(res.response, t('customers.pipelines.errors.stageCreateFailed', 'Failed to create stage'))
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
context: stageMutationContext,
|
|
314
|
+
mutationPayload: { action: 'create', pipelineId: targetPipelineId, label: stageForm.label.trim(), ...appearance },
|
|
256
315
|
})
|
|
257
|
-
if (!res.ok) {
|
|
258
|
-
await raiseCrudError(res.response, t('customers.pipelines.errors.stageCreateFailed', 'Failed to create stage'))
|
|
259
|
-
return
|
|
260
|
-
}
|
|
261
316
|
flash(t('customers.pipelines.flash.stageCreated', 'Stage created'), 'success')
|
|
262
|
-
await loadStages(
|
|
317
|
+
await loadStages(targetPipelineId)
|
|
263
318
|
} else if (stageDialog?.mode === 'edit') {
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
() =>
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
319
|
+
const targetStage = stageDialog.entry
|
|
320
|
+
await runMutation({
|
|
321
|
+
operation: async () => {
|
|
322
|
+
const res = await withScopedApiRequestHeaders(
|
|
323
|
+
buildOptimisticLockHeader(targetStage.updatedAt),
|
|
324
|
+
() =>
|
|
325
|
+
apiCall('/api/customers/pipeline-stages', {
|
|
326
|
+
method: 'PUT',
|
|
327
|
+
headers: { 'content-type': 'application/json' },
|
|
328
|
+
body: JSON.stringify({ id: targetStage.id, label: stageForm.label.trim(), ...appearance }),
|
|
329
|
+
}),
|
|
330
|
+
)
|
|
331
|
+
if (!res.ok) {
|
|
332
|
+
await raiseCrudError(res.response, t('customers.pipelines.errors.stageUpdateFailed', 'Failed to update stage'))
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
context: stageMutationContext,
|
|
336
|
+
mutationPayload: { action: 'update', id: targetStage.id, label: stageForm.label.trim(), ...appearance },
|
|
337
|
+
})
|
|
277
338
|
flash(t('customers.pipelines.flash.stageUpdated', 'Stage updated'), 'success')
|
|
278
|
-
await loadStages(
|
|
339
|
+
await loadStages(targetStage.pipelineId)
|
|
279
340
|
}
|
|
280
341
|
setStageDialog(null)
|
|
342
|
+
} catch {
|
|
343
|
+
return
|
|
281
344
|
} finally {
|
|
282
345
|
setSubmittingStage(false)
|
|
283
346
|
}
|
|
284
|
-
}, [stageDialog, stageForm, loadStages, t])
|
|
347
|
+
}, [stageDialog, stageForm, loadStages, runMutation, stageMutationContext, t])
|
|
285
348
|
|
|
286
349
|
const handleDeleteStage = React.useCallback(async (stage: PipelineStage) => {
|
|
287
350
|
const confirmed = await confirm({
|
|
@@ -291,24 +354,35 @@ export default function PipelineSettings(): React.ReactElement {
|
|
|
291
354
|
variant: 'destructive',
|
|
292
355
|
})
|
|
293
356
|
if (!confirmed) return
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
357
|
+
try {
|
|
358
|
+
await runMutation({
|
|
359
|
+
operation: async () => {
|
|
360
|
+
const res = await withScopedApiRequestHeaders(
|
|
361
|
+
buildOptimisticLockHeader(stage.updatedAt),
|
|
362
|
+
() =>
|
|
363
|
+
apiCall('/api/customers/pipeline-stages', {
|
|
364
|
+
method: 'DELETE',
|
|
365
|
+
headers: { 'content-type': 'application/json' },
|
|
366
|
+
body: JSON.stringify({ id: stage.id }),
|
|
367
|
+
}),
|
|
368
|
+
)
|
|
369
|
+
if (!res.ok) {
|
|
370
|
+
const body = (res.result ?? {}) as Record<string, unknown>
|
|
371
|
+
const msg = typeof body.error === 'string' ? body.error : t('customers.pipelines.errors.stageDeleteFailed', 'Failed to delete stage')
|
|
372
|
+
throw new Error(msg)
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
context: stageMutationContext,
|
|
376
|
+
mutationPayload: { action: 'delete', id: stage.id },
|
|
377
|
+
})
|
|
378
|
+
} catch (err) {
|
|
379
|
+
const msg = err instanceof Error && err.message ? err.message : t('customers.pipelines.errors.stageDeleteFailed', 'Failed to delete stage')
|
|
306
380
|
flash(msg, 'error')
|
|
307
381
|
return
|
|
308
382
|
}
|
|
309
383
|
flash(t('customers.pipelines.flash.stageDeleted', 'Stage deleted'), 'success')
|
|
310
384
|
await loadStages(stage.pipelineId)
|
|
311
|
-
}, [confirm, loadStages, t])
|
|
385
|
+
}, [confirm, loadStages, runMutation, stageMutationContext, t])
|
|
312
386
|
|
|
313
387
|
const handleMoveStage = React.useCallback(async (stage: PipelineStage, direction: 'up' | 'down') => {
|
|
314
388
|
const pipelineStages = stages[stage.pipelineId] ?? []
|
|
@@ -323,17 +397,28 @@ export default function PipelineSettings(): React.ReactElement {
|
|
|
323
397
|
reordered[swapIdx] = temp
|
|
324
398
|
|
|
325
399
|
const orderedStages = reordered.map((s, i) => ({ id: s.id, order: i }))
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
400
|
+
try {
|
|
401
|
+
await runMutation({
|
|
402
|
+
operation: async () => {
|
|
403
|
+
const res = await apiCall('/api/customers/pipeline-stages/reorder', {
|
|
404
|
+
method: 'POST',
|
|
405
|
+
headers: { 'content-type': 'application/json' },
|
|
406
|
+
body: JSON.stringify({ stages: orderedStages }),
|
|
407
|
+
})
|
|
408
|
+
if (!res.ok) {
|
|
409
|
+
throw new Error(t('customers.pipelines.errors.reorderFailed', 'Failed to reorder stages'))
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
context: stageMutationContext,
|
|
413
|
+
mutationPayload: { action: 'reorder', stages: orderedStages },
|
|
414
|
+
})
|
|
415
|
+
} catch (err) {
|
|
416
|
+
const msg = err instanceof Error && err.message ? err.message : t('customers.pipelines.errors.reorderFailed', 'Failed to reorder stages')
|
|
417
|
+
flash(msg, 'error')
|
|
333
418
|
return
|
|
334
419
|
}
|
|
335
420
|
await loadStages(stage.pipelineId)
|
|
336
|
-
}, [stages, loadStages, t])
|
|
421
|
+
}, [stages, loadStages, runMutation, stageMutationContext, t])
|
|
337
422
|
|
|
338
423
|
const handleKeyDown = React.useCallback(
|
|
339
424
|
(handler: () => void) => (e: React.KeyboardEvent) => {
|
|
@@ -7,11 +7,12 @@ import { isToday } from 'date-fns/isToday'
|
|
|
7
7
|
import { isTomorrow } from 'date-fns/isTomorrow'
|
|
8
8
|
import { startOfDay } from 'date-fns/startOfDay'
|
|
9
9
|
import { CalendarClock } from 'lucide-react'
|
|
10
|
-
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
10
|
+
import { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'
|
|
11
11
|
import { cn } from '@open-mercato/shared/lib/utils'
|
|
12
12
|
import { Avatar } from '@open-mercato/ui/primitives/avatar'
|
|
13
13
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
14
14
|
import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
|
|
15
|
+
import { eventDisplayTitle, pluralCategory } from '../../lib/calendar/labels'
|
|
15
16
|
import type { AgendaListProps, CalendarCategory, CalendarItem } from './types'
|
|
16
17
|
|
|
17
18
|
const MAX_AVATARS_PER_ROW = 2
|
|
@@ -84,8 +85,15 @@ function buildDayGroups(anchor: Date, horizonDays: number, items: CalendarItem[]
|
|
|
84
85
|
|
|
85
86
|
function AgendaDayHeader({ date, count }: { date: Date; count: number }) {
|
|
86
87
|
const t = useT()
|
|
88
|
+
const locale = useLocale()
|
|
87
89
|
const todayMarker = isToday(date)
|
|
88
90
|
const tomorrowMarker = !todayMarker && isTomorrow(date)
|
|
91
|
+
const countKey = `customers.calendar.agenda.eventsCount.${pluralCategory(locale, count)}`
|
|
92
|
+
const resolvedCount = t(countKey, { count })
|
|
93
|
+
const countLabel =
|
|
94
|
+
resolvedCount === countKey
|
|
95
|
+
? t('customers.calendar.agenda.eventsCount.other', '{count} events', { count })
|
|
96
|
+
: resolvedCount
|
|
89
97
|
return (
|
|
90
98
|
<div className="flex w-full items-center gap-2 bg-muted/50 px-3 py-2.5 sm:px-5">
|
|
91
99
|
<span className="text-sm font-semibold text-foreground">{groupLabelOf(date)}</span>
|
|
@@ -95,11 +103,7 @@ function AgendaDayHeader({ date, count }: { date: Date; count: number }) {
|
|
|
95
103
|
</span>
|
|
96
104
|
) : null}
|
|
97
105
|
<span aria-hidden="true" className="w-2.5 shrink-0" />
|
|
98
|
-
<span className="text-xs font-medium text-muted-foreground">
|
|
99
|
-
{count === 1
|
|
100
|
-
? t('customers.calendar.agenda.eventCount', '1 event')
|
|
101
|
-
: t('customers.calendar.agenda.eventsCount', '{count} events', { count })}
|
|
102
|
-
</span>
|
|
106
|
+
<span className="text-xs font-medium text-muted-foreground">{countLabel}</span>
|
|
103
107
|
</div>
|
|
104
108
|
)
|
|
105
109
|
}
|
|
@@ -116,6 +120,7 @@ function AgendaRow({
|
|
|
116
120
|
const t = useT()
|
|
117
121
|
const canceled = item.status === 'canceled'
|
|
118
122
|
const done = item.status === 'done'
|
|
123
|
+
const title = eventDisplayTitle(item.title, t('customers.calendar.grid.untitled', 'Untitled'))
|
|
119
124
|
const typeLabel = typeLabels?.[item.interactionType] ?? deriveTypeLabel(item.interactionType)
|
|
120
125
|
const platformLabels: Record<string, string> = {
|
|
121
126
|
zoom: t('customers.calendar.platformFull.zoom', 'Zoom'),
|
|
@@ -135,7 +140,7 @@ function AgendaRow({
|
|
|
135
140
|
<Button
|
|
136
141
|
type="button"
|
|
137
142
|
variant="ghost"
|
|
138
|
-
aria-label={`${
|
|
143
|
+
aria-label={`${title} · ${ariaTime}`}
|
|
139
144
|
onClick={() => onItemClick(item)}
|
|
140
145
|
className={cn(
|
|
141
146
|
'h-auto w-full justify-start whitespace-normal rounded-none bg-background px-3 py-3 text-left transition-colors hover:bg-muted/30 sm:gap-3.5 sm:px-5',
|
|
@@ -161,7 +166,7 @@ function AgendaRow({
|
|
|
161
166
|
canceled && 'line-through',
|
|
162
167
|
)}
|
|
163
168
|
>
|
|
164
|
-
{
|
|
169
|
+
{title}
|
|
165
170
|
</span>
|
|
166
171
|
<span className="truncate text-xs text-muted-foreground">
|
|
167
172
|
{locationLabel ? `${typeLabel} · ${locationLabel}` : typeLabel}
|
|
@@ -78,6 +78,10 @@ export function CalendarSettingsModal({
|
|
|
78
78
|
]
|
|
79
79
|
|
|
80
80
|
const title = t('customers.calendar.settings.title', 'Customization')
|
|
81
|
+
const removeTagLabel = React.useCallback(
|
|
82
|
+
(tag: string) => t('customers.calendar.settings.removeTag', 'Remove {tag}', { tag }),
|
|
83
|
+
[t],
|
|
84
|
+
)
|
|
81
85
|
|
|
82
86
|
return (
|
|
83
87
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
@@ -119,6 +123,7 @@ export function CalendarSettingsModal({
|
|
|
119
123
|
placeholder={t('customers.calendar.settings.addCategory', 'Add a category…')}
|
|
120
124
|
value={draft.eventCategories}
|
|
121
125
|
maxTags={MAX_EVENT_CATEGORIES}
|
|
126
|
+
removeTagLabel={removeTagLabel}
|
|
122
127
|
onChange={(eventCategories) => setDraft((current) => ({ ...current, eventCategories }))}
|
|
123
128
|
/>
|
|
124
129
|
<SettingsTagInput
|
|
@@ -131,6 +136,7 @@ export function CalendarSettingsModal({
|
|
|
131
136
|
placeholder={t('customers.calendar.settings.addType', 'Add a type…')}
|
|
132
137
|
value={draft.activityTypes}
|
|
133
138
|
maxTags={MAX_ACTIVITY_TYPES}
|
|
139
|
+
removeTagLabel={removeTagLabel}
|
|
134
140
|
onChange={(activityTypes) => setDraft((current) => ({ ...current, activityTypes }))}
|
|
135
141
|
/>
|
|
136
142
|
{toggleRows.map((row) => (
|
|
@@ -186,10 +192,11 @@ type SettingsTagInputProps = {
|
|
|
186
192
|
placeholder: string
|
|
187
193
|
value: string[]
|
|
188
194
|
maxTags: number
|
|
195
|
+
removeTagLabel: (tag: string) => string
|
|
189
196
|
onChange(value: string[]): void
|
|
190
197
|
}
|
|
191
198
|
|
|
192
|
-
function SettingsTagInput({ label, maxLabel, hint, placeholder, value, maxTags, onChange }: SettingsTagInputProps) {
|
|
199
|
+
function SettingsTagInput({ label, maxLabel, hint, placeholder, value, maxTags, removeTagLabel, onChange }: SettingsTagInputProps) {
|
|
193
200
|
return (
|
|
194
201
|
<div className="flex flex-col gap-1">
|
|
195
202
|
<div className="flex items-center gap-1">
|
|
@@ -201,7 +208,14 @@ function SettingsTagInput({ label, maxLabel, hint, placeholder, value, maxTags,
|
|
|
201
208
|
</span>
|
|
202
209
|
</SimpleTooltip>
|
|
203
210
|
</div>
|
|
204
|
-
<TagInput
|
|
211
|
+
<TagInput
|
|
212
|
+
value={value}
|
|
213
|
+
onChange={onChange}
|
|
214
|
+
placeholder={placeholder}
|
|
215
|
+
maxTags={maxTags}
|
|
216
|
+
aria-label={label}
|
|
217
|
+
removeTagLabel={removeTagLabel}
|
|
218
|
+
/>
|
|
205
219
|
</div>
|
|
206
220
|
)
|
|
207
221
|
}
|
|
@@ -11,6 +11,7 @@ import { cn } from '@open-mercato/shared/lib/utils'
|
|
|
11
11
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
12
12
|
import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
|
|
13
13
|
import { getVisibleRange } from '../../lib/calendar/range'
|
|
14
|
+
import { eventDisplayTitle } from '../../lib/calendar/labels'
|
|
14
15
|
import type { CalendarItem, MonthGridProps } from './types'
|
|
15
16
|
|
|
16
17
|
const MAX_PILLS_PER_DAY = 2
|
|
@@ -61,7 +62,9 @@ function groupItemsByDay(items: CalendarItem[]): Map<string, CalendarItem[]> {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
function MonthPill({ item, onItemClick }: { item: CalendarItem; onItemClick: (item: CalendarItem) => void }) {
|
|
65
|
+
const t = useT()
|
|
64
66
|
const canceled = item.status === 'canceled'
|
|
67
|
+
const title = eventDisplayTitle(item.title, t('customers.calendar.grid.untitled', 'Untitled'))
|
|
65
68
|
const timeLabel = item.allDay ? '' : ` · ${formatTime(item.start)} – ${formatTime(item.end)}`
|
|
66
69
|
const tintStyle = item.color
|
|
67
70
|
? { backgroundColor: `${item.color}${SOFT_TINT_ALPHA}`, color: item.color }
|
|
@@ -70,7 +73,7 @@ function MonthPill({ item, onItemClick }: { item: CalendarItem; onItemClick: (it
|
|
|
70
73
|
<Button
|
|
71
74
|
type="button"
|
|
72
75
|
variant="ghost"
|
|
73
|
-
aria-label={`${
|
|
76
|
+
aria-label={`${title}${timeLabel}`}
|
|
74
77
|
onClick={(event) => {
|
|
75
78
|
event.stopPropagation()
|
|
76
79
|
onItemClick(item)
|
|
@@ -94,7 +97,7 @@ function MonthPill({ item, onItemClick }: { item: CalendarItem; onItemClick: (it
|
|
|
94
97
|
canceled && 'line-through',
|
|
95
98
|
)}
|
|
96
99
|
>
|
|
97
|
-
{
|
|
100
|
+
{title}
|
|
98
101
|
</span>
|
|
99
102
|
</Button>
|
|
100
103
|
)
|
|
@@ -7,6 +7,7 @@ import { Avatar } from '@open-mercato/ui/primitives/avatar'
|
|
|
7
7
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
8
8
|
import { Input } from '@open-mercato/ui/primitives/input'
|
|
9
9
|
import type { EditorParticipant } from '../../../lib/calendar/editorPayload'
|
|
10
|
+
import { composeAccessibleName } from '../../../lib/calendar/labels'
|
|
10
11
|
import { searchPeopleOptions, type PersonOption } from './lookups'
|
|
11
12
|
import { CONTROL_BORDER, DROPDOWN_PANEL_CLASS, PersonChip, UppercaseBadge } from './inputs'
|
|
12
13
|
|
|
@@ -119,6 +120,11 @@ export function PeopleField({
|
|
|
119
120
|
variant="ghost"
|
|
120
121
|
role="option"
|
|
121
122
|
aria-selected={false}
|
|
123
|
+
aria-label={composeAccessibleName([
|
|
124
|
+
option.name,
|
|
125
|
+
option.email,
|
|
126
|
+
option.isCustomer ? t('customers.calendar.editor.customerBadge', 'Customer') : null,
|
|
127
|
+
])}
|
|
122
128
|
onClick={() => {
|
|
123
129
|
const participant: EditorParticipant = {
|
|
124
130
|
userId: option.userId,
|
|
@@ -8,6 +8,7 @@ import { Button } from '@open-mercato/ui/primitives/button'
|
|
|
8
8
|
import { IconButton } from '@open-mercato/ui/primitives/icon-button'
|
|
9
9
|
import { Input } from '@open-mercato/ui/primitives/input'
|
|
10
10
|
import type { EditorRelatedTo } from '../../../lib/calendar/editorPayload'
|
|
11
|
+
import { composeAccessibleName } from '../../../lib/calendar/labels'
|
|
11
12
|
import {
|
|
12
13
|
fetchDealsForEntity,
|
|
13
14
|
searchRelatedEntities,
|
|
@@ -178,6 +179,7 @@ export function RelatedToField({
|
|
|
178
179
|
variant="ghost"
|
|
179
180
|
role="option"
|
|
180
181
|
aria-selected={value?.id === option.id}
|
|
182
|
+
aria-label={composeAccessibleName([option.label, option.subtitle, kindLabel(option.kind)])}
|
|
181
183
|
onClick={() => {
|
|
182
184
|
onChange({ id: option.id, kind: option.kind, label: option.label })
|
|
183
185
|
if (value?.id !== option.id) onDealChange(null)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
5
5
|
import type { EditorDateLabel } from '../../../lib/calendar/editorPayload'
|
|
6
|
+
import { multiDayEventSpan } from '../../../lib/calendar/labels'
|
|
6
7
|
import { AllDayToggle, DateControl, LABEL_CLASS, TimeControl } from './inputs'
|
|
7
8
|
|
|
8
9
|
const DATE_LABEL_TEXT: Record<EditorDateLabel, { key: string; fallback: string }> = {
|
|
@@ -76,6 +77,7 @@ export function ScheduleSection({
|
|
|
76
77
|
}) {
|
|
77
78
|
const t = useT()
|
|
78
79
|
const showTime = !(hasAllDay && allDay)
|
|
80
|
+
const multiDaySpan = hasEnd && !endsError ? multiDayEventSpan(date, endDate) : 0
|
|
79
81
|
return (
|
|
80
82
|
<div className="flex w-full flex-col gap-2.5">
|
|
81
83
|
{hasAllDay ? (
|
|
@@ -105,6 +107,11 @@ export function ScheduleSection({
|
|
|
105
107
|
/>
|
|
106
108
|
) : null}
|
|
107
109
|
{endsError ? <p className="text-xs text-status-error-text">{endsError}</p> : null}
|
|
110
|
+
{multiDaySpan > 0 ? (
|
|
111
|
+
<p className="text-xs text-muted-foreground">
|
|
112
|
+
{t('customers.calendar.editor.multiDayHint', 'Multi-day event · {count} days', { count: multiDaySpan })}
|
|
113
|
+
</p>
|
|
114
|
+
) : null}
|
|
108
115
|
</div>
|
|
109
116
|
)
|
|
110
117
|
}
|
|
@@ -151,11 +151,11 @@
|
|
|
151
151
|
"customers.audit.entityRoles.create": "Create entity role",
|
|
152
152
|
"customers.audit.entityRoles.delete": "Delete entity role",
|
|
153
153
|
"customers.audit.entityRoles.update": "Update entity role",
|
|
154
|
-
"customers.audit.interactions.cancel": "
|
|
155
|
-
"customers.audit.interactions.complete": "
|
|
156
|
-
"customers.audit.interactions.create": "
|
|
157
|
-
"customers.audit.interactions.delete": "
|
|
158
|
-
"customers.audit.interactions.update": "
|
|
154
|
+
"customers.audit.interactions.cancel": "Interaktion abbrechen",
|
|
155
|
+
"customers.audit.interactions.complete": "Interaktion abschließen",
|
|
156
|
+
"customers.audit.interactions.create": "Interaktion erstellen",
|
|
157
|
+
"customers.audit.interactions.delete": "Interaktion löschen",
|
|
158
|
+
"customers.audit.interactions.update": "Interaktion aktualisieren",
|
|
159
159
|
"customers.audit.labels.assign": "Label zuweisen",
|
|
160
160
|
"customers.audit.labels.create": "Label erstellen",
|
|
161
161
|
"customers.audit.labels.unassign": "Labelzuweisung entfernen",
|
|
@@ -174,8 +174,10 @@
|
|
|
174
174
|
"customers.audit.todos.link": "To-do verknüpfen",
|
|
175
175
|
"customers.audit.todos.unlink": "To-do-Verknüpfung lösen",
|
|
176
176
|
"customers.calendar.actions.newEvent": "Neuer Termin",
|
|
177
|
-
"customers.calendar.agenda.
|
|
178
|
-
"customers.calendar.agenda.eventsCount": "{count} Termine",
|
|
177
|
+
"customers.calendar.agenda.eventsCount.few": "{count} Termine",
|
|
178
|
+
"customers.calendar.agenda.eventsCount.many": "{count} Termine",
|
|
179
|
+
"customers.calendar.agenda.eventsCount.one": "{count} Termin",
|
|
180
|
+
"customers.calendar.agenda.eventsCount.other": "{count} Termine",
|
|
179
181
|
"customers.calendar.agenda.today": "Heute",
|
|
180
182
|
"customers.calendar.agenda.tomorrow": "Morgen",
|
|
181
183
|
"customers.calendar.cards.allDay": "Ganztägig",
|
|
@@ -230,6 +232,7 @@
|
|
|
230
232
|
"customers.calendar.editor.error": "Termin konnte nicht gespeichert werden",
|
|
231
233
|
"customers.calendar.editor.location": "Ort",
|
|
232
234
|
"customers.calendar.editor.locationPlaceholder": "Ort oder Link hinzufügen…",
|
|
235
|
+
"customers.calendar.editor.multiDayHint": "Mehrtägiger Termin · {count} Tage",
|
|
233
236
|
"customers.calendar.editor.noResults": "Keine Ergebnisse",
|
|
234
237
|
"customers.calendar.editor.participants": "Teilnehmer",
|
|
235
238
|
"customers.calendar.editor.phoneLink": "Telefon / Link",
|
|
@@ -334,6 +337,7 @@
|
|
|
334
337
|
"customers.calendar.settings.eventCategories": "Ereigniskategorien",
|
|
335
338
|
"customers.calendar.settings.eventCategoriesHint": "Eigene Gruppierungslabels (z. B. Teammeeting, Verkaufsanruf). Werden beim Erstellen eines Ereignisses angeboten.",
|
|
336
339
|
"customers.calendar.settings.max": "(max. {count})",
|
|
340
|
+
"customers.calendar.settings.removeTag": "{tag} entfernen",
|
|
337
341
|
"customers.calendar.settings.save": "Änderungen speichern",
|
|
338
342
|
"customers.calendar.settings.saved": "Kalendereinstellungen gespeichert",
|
|
339
343
|
"customers.calendar.settings.showCrmActivities": "CRM-Aktivitäten im Kalender anzeigen",
|
|
@@ -174,8 +174,10 @@
|
|
|
174
174
|
"customers.audit.todos.link": "Link todo",
|
|
175
175
|
"customers.audit.todos.unlink": "Unlink todo",
|
|
176
176
|
"customers.calendar.actions.newEvent": "New event",
|
|
177
|
-
"customers.calendar.agenda.
|
|
178
|
-
"customers.calendar.agenda.eventsCount": "{count} events",
|
|
177
|
+
"customers.calendar.agenda.eventsCount.few": "{count} events",
|
|
178
|
+
"customers.calendar.agenda.eventsCount.many": "{count} events",
|
|
179
|
+
"customers.calendar.agenda.eventsCount.one": "{count} event",
|
|
180
|
+
"customers.calendar.agenda.eventsCount.other": "{count} events",
|
|
179
181
|
"customers.calendar.agenda.today": "Today",
|
|
180
182
|
"customers.calendar.agenda.tomorrow": "Tomorrow",
|
|
181
183
|
"customers.calendar.cards.allDay": "All day",
|
|
@@ -230,6 +232,7 @@
|
|
|
230
232
|
"customers.calendar.editor.error": "Failed to save event",
|
|
231
233
|
"customers.calendar.editor.location": "Location",
|
|
232
234
|
"customers.calendar.editor.locationPlaceholder": "Add a location or link…",
|
|
235
|
+
"customers.calendar.editor.multiDayHint": "Multi-day event · {count} days",
|
|
233
236
|
"customers.calendar.editor.noResults": "No results",
|
|
234
237
|
"customers.calendar.editor.participants": "Participants",
|
|
235
238
|
"customers.calendar.editor.phoneLink": "Phone / link",
|
|
@@ -334,6 +337,7 @@
|
|
|
334
337
|
"customers.calendar.settings.eventCategories": "Event Categories",
|
|
335
338
|
"customers.calendar.settings.eventCategoriesHint": "Your own grouping labels (e.g. Team Meeting, Sales Call). Offered when creating an event.",
|
|
336
339
|
"customers.calendar.settings.max": "(max. {count})",
|
|
340
|
+
"customers.calendar.settings.removeTag": "Remove {tag}",
|
|
337
341
|
"customers.calendar.settings.save": "Save Changes",
|
|
338
342
|
"customers.calendar.settings.saved": "Calendar settings saved",
|
|
339
343
|
"customers.calendar.settings.showCrmActivities": "Show CRM activities on calendar",
|