@open-mercato/core 0.6.8-develop.7064.1.d9a7131615 → 0.6.8-develop.7066.1.6bfa9ff69f

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/customers/api/interactions/route.js +3 -1
  3. package/dist/modules/customers/api/interactions/route.js.map +2 -2
  4. package/dist/modules/customers/commands/interactions.js.map +1 -1
  5. package/dist/modules/customers/components/calendar/AgendaList.js +2 -2
  6. package/dist/modules/customers/components/calendar/AgendaList.js.map +2 -2
  7. package/dist/modules/customers/components/calendar/CalendarEventEditor.js +1 -1
  8. package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +2 -2
  9. package/dist/modules/customers/components/calendar/CalendarScreen.js +1 -1
  10. package/dist/modules/customers/components/calendar/CalendarScreen.js.map +2 -2
  11. package/dist/modules/customers/components/calendar/EventBlock.js +1 -1
  12. package/dist/modules/customers/components/calendar/EventBlock.js.map +2 -2
  13. package/dist/modules/customers/components/calendar/editor/PeopleField.js +40 -5
  14. package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
  15. package/dist/modules/customers/components/calendar/editor/hooks.js +2 -1
  16. package/dist/modules/customers/components/calendar/editor/hooks.js.map +2 -2
  17. package/dist/modules/customers/components/calendar/types.js +1 -1
  18. package/dist/modules/customers/components/calendar/types.js.map +2 -2
  19. package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +4 -4
  20. package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +2 -2
  21. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +3 -3
  22. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
  23. package/dist/modules/customers/data/entities.js.map +1 -1
  24. package/dist/modules/customers/data/validators.js +22 -1
  25. package/dist/modules/customers/data/validators.js.map +2 -2
  26. package/dist/modules/customers/lib/calendar/conflicts.js +8 -2
  27. package/dist/modules/customers/lib/calendar/conflicts.js.map +2 -2
  28. package/dist/modules/customers/lib/calendar/editorPayload.js +3 -2
  29. package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
  30. package/dist/modules/customers/lib/calendar/mapItem.js +8 -3
  31. package/dist/modules/customers/lib/calendar/mapItem.js.map +2 -2
  32. package/dist/modules/customers/lib/calendar/participantIdentity.js +9 -0
  33. package/dist/modules/customers/lib/calendar/participantIdentity.js.map +7 -0
  34. package/package.json +7 -7
  35. package/src/modules/customers/api/interactions/route.ts +8 -2
  36. package/src/modules/customers/commands/interactions.ts +1 -1
  37. package/src/modules/customers/components/calendar/AgendaList.tsx +3 -3
  38. package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +1 -1
  39. package/src/modules/customers/components/calendar/CalendarScreen.tsx +1 -1
  40. package/src/modules/customers/components/calendar/EventBlock.tsx +2 -2
  41. package/src/modules/customers/components/calendar/editor/PeopleField.tsx +61 -5
  42. package/src/modules/customers/components/calendar/editor/hooks.ts +7 -1
  43. package/src/modules/customers/components/calendar/types.ts +2 -2
  44. package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +5 -5
  45. package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +5 -5
  46. package/src/modules/customers/components/detail/types.ts +1 -1
  47. package/src/modules/customers/data/entities.ts +1 -1
  48. package/src/modules/customers/data/validators.ts +32 -6
  49. package/src/modules/customers/i18n/de.json +4 -0
  50. package/src/modules/customers/i18n/en.json +4 -0
  51. package/src/modules/customers/i18n/es.json +4 -0
  52. package/src/modules/customers/i18n/ko.json +4 -0
  53. package/src/modules/customers/i18n/pl.json +4 -0
  54. package/src/modules/customers/lib/calendar/conflicts.ts +10 -2
  55. package/src/modules/customers/lib/calendar/editorPayload.ts +4 -3
  56. package/src/modules/customers/lib/calendar/mapItem.ts +8 -3
  57. package/src/modules/customers/lib/calendar/participantIdentity.ts +17 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/customers/commands/interactions.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport {\n parseWithCustomFields,\n setCustomFieldsIfAny,\n emitCrudSideEffects,\n emitCrudUndoSideEffects,\n requireId,\n normalizeAuthorUserId,\n} from '@open-mercato/shared/lib/commands/helpers'\nimport { DefaultDataEngine, type DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { CustomerInteraction, CustomerEntity } from '../data/entities'\nimport {\n interactionCreateSchema,\n interactionUpdateSchema,\n interactionCompleteSchema,\n interactionCancelSchema,\n type InteractionCreateInput,\n type InteractionUpdateInput,\n type InteractionCompleteInput,\n type InteractionCancelInput,\n} from '../data/validators'\nimport {\n ensureOrganizationScope,\n ensureTenantScope,\n requireTimelineParentEntity,\n extractUndoPayload,\n emitQueryIndexUpsertEvents,\n requireDealInScope,\n resolveParentResourceKind,\n} from './shared'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport {\n loadCustomFieldSnapshot,\n buildCustomFieldResetMap,\n} from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { CrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceRecordGoneIsConflict, enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { CrudIndexerConfig, CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport { recomputeNextInteraction } from '../lib/interactionProjection'\nimport {\n INTERACTION_STATUS_CANCELED,\n INTERACTION_STATUS_COMPLETED,\n INTERACTION_STATUS_PLANNED,\n} from '../lib/interactionStatus'\nimport { canChangeEmailVisibility } from '../lib/visibilityFilter'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\nconst INTERACTION_ENTITY_ID = 'customers:customer_interaction'\nconst interactionCrudIndexer: CrudIndexerConfig<CustomerInteraction> = {\n entityType: 'customers:customer_interaction' as const,\n}\n\nconst interactionCrudEvents: CrudEventsConfig = {\n module: 'customers',\n entity: 'interaction',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n entityId:\n ctx.entity && typeof ctx.entity === 'object' && 'entity' in (ctx.entity as Record<string, unknown>)\n ? (() => {\n const entityRef = (ctx.entity as CustomerInteraction).entity\n return typeof entityRef === 'string' ? entityRef : entityRef?.id ?? null\n })()\n : null,\n interactionType:\n ctx.entity && typeof ctx.entity === 'object' && 'interactionType' in (ctx.entity as Record<string, unknown>)\n ? (ctx.entity as CustomerInteraction).interactionType\n : null,\n status:\n ctx.entity && typeof ctx.entity === 'object' && 'status' in (ctx.entity as Record<string, unknown>)\n ? (ctx.entity as CustomerInteraction).status\n : null,\n source:\n ctx.entity && typeof ctx.entity === 'object' && 'source' in (ctx.entity as Record<string, unknown>)\n ? (ctx.entity as CustomerInteraction).source ?? null\n : null,\n ...(ctx.syncOrigin ? { syncOrigin: ctx.syncOrigin } : {}),\n }),\n}\n\ntype InteractionSnapshot = {\n interaction: {\n id: string\n organizationId: string\n tenantId: string\n entityId: string\n entityKind: string | null\n dealId: string | null\n interactionType: string\n title: string | null\n body: string | null\n status: string\n scheduledAt: Date | null\n occurredAt: Date | null\n priority: number | null\n authorUserId: string | null\n ownerUserId: string | null\n appearanceIcon: string | null\n appearanceColor: string | null\n source: string | null\n durationMinutes: number | null\n location: string | null\n allDay: boolean | null\n recurrenceRule: string | null\n recurrenceEnd: Date | null\n participants: Array<{ userId: string; name?: string; email?: string; status?: string }> | null\n reminderMinutes: number | null\n visibility: string | null\n linkedEntities: Array<{ id: string; type: string; label: string }> | null\n guestPermissions: { canInviteOthers?: boolean; canModify?: boolean; canSeeList?: boolean } | null\n }\n custom?: Record<string, unknown>\n}\n\ntype InteractionUndoPayload = {\n before?: InteractionSnapshot | null\n after?: InteractionSnapshot | null\n}\n\nasync function loadInteractionSnapshot(em: EntityManager, id: string): Promise<InteractionSnapshot | null> {\n const interaction = await findOneWithDecryption(em, CustomerInteraction, { id }, { populate: ['entity'] })\n if (!interaction) return null\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: INTERACTION_ENTITY_ID,\n recordId: interaction.id,\n tenantId: interaction.tenantId,\n organizationId: interaction.organizationId,\n })\n const entityRef = interaction.entity\n const entityKind = (typeof entityRef === 'object' && entityRef !== null && 'kind' in entityRef)\n ? (entityRef as { kind: string }).kind\n : null\n return {\n interaction: {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n entityId: typeof entityRef === 'string' ? entityRef : entityRef.id,\n entityKind,\n dealId: interaction.dealId ?? null,\n interactionType: interaction.interactionType,\n title: interaction.title ?? null,\n body: interaction.body ?? null,\n status: interaction.status,\n scheduledAt: interaction.scheduledAt ?? null,\n occurredAt: interaction.occurredAt ?? null,\n priority: interaction.priority ?? null,\n authorUserId: interaction.authorUserId ?? null,\n ownerUserId: interaction.ownerUserId ?? null,\n appearanceIcon: interaction.appearanceIcon ?? null,\n appearanceColor: interaction.appearanceColor ?? null,\n source: interaction.source ?? null,\n durationMinutes: interaction.durationMinutes ?? null,\n location: interaction.location ?? null,\n allDay: interaction.allDay ?? null,\n recurrenceRule: interaction.recurrenceRule ?? null,\n recurrenceEnd: interaction.recurrenceEnd ?? null,\n participants: interaction.participants ?? null,\n reminderMinutes: interaction.reminderMinutes ?? null,\n visibility: interaction.visibility ?? null,\n linkedEntities: interaction.linkedEntities ?? null,\n guestPermissions: interaction.guestPermissions ?? null,\n },\n custom,\n }\n}\n\nasync function setInteractionCustomFields(\n dataEngine: DataEngine,\n interactionId: string,\n organizationId: string,\n tenantId: string,\n values: Record<string, unknown>\n) {\n if (!values || !Object.keys(values).length) return\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: INTERACTION_ENTITY_ID,\n recordId: interactionId,\n organizationId,\n tenantId,\n values,\n notify: false,\n })\n}\n\nasync function emitLifecycleEvent(\n ctx: CommandRuntimeContext,\n eventId: string,\n payload: Record<string, unknown>\n): Promise<void> {\n let bus: { emitEvent(event: string, payload: unknown, options?: unknown): Promise<void> } | null = null\n try {\n bus = ctx.container.resolve('eventBus')\n } catch (err) {\n logger.warn('eventBus resolve failed; skipping emit', { component: 'commands.interactions', eventId, err })\n bus = null\n }\n if (!bus) return\n await bus\n .emitEvent(eventId, payload, { persistent: true })\n .catch((err) => {\n logger.warn('Event emit failed', { component: 'commands.interactions', eventId, err })\n return undefined\n })\n}\n\nasync function emitInteractionRevertedEvent(\n ctx: CommandRuntimeContext,\n interaction: InteractionSnapshot['interaction'],\n): Promise<void> {\n await emitLifecycleEvent(ctx, 'customers.interaction.reverted', {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n entityId: interaction.entityId,\n interactionType: interaction.interactionType,\n source: interaction.source ?? null,\n status: interaction.status,\n occurredAt: interaction.occurredAt?.toISOString() ?? null,\n ...(ctx.syncOrigin ? { syncOrigin: ctx.syncOrigin } : {}),\n })\n}\n\ntype InteractionIdentifiers = {\n id: string\n organizationId: string\n tenantId: string\n}\n\ntype InteractionProjectionMutation = {\n entityId: string\n nextInteractionId: string | null\n}\n\nfunction createTransactionalDataEngine(ctx: CommandRuntimeContext, em: EntityManager): DataEngine {\n const emWithConnection = em as EntityManager & { getConnection?: () => unknown }\n if (typeof emWithConnection.getConnection !== 'function') {\n return ctx.container.resolve('dataEngine') as DataEngine\n }\n return new DefaultDataEngine(em, ctx.container)\n}\n\nasync function runInTransaction<TResult>(\n em: EntityManager,\n operation: (trx: EntityManager) => Promise<TResult>,\n): Promise<TResult> {\n // Mirrors the SPEC-018 fix applied to withAtomicFlush: use explicit begin/commit/rollback\n // so the outer EntityManager stays bound to the transaction, and closures over `em` inside\n // `operation` participate in the same transaction. This avoids the em.transactional(cb)\n // hazard where the callback receives a child EM whose flushes can silently race against\n // subsequent queries on the original `em`.\n const supportsBegin =\n typeof (em as unknown as { begin?: () => Promise<void> }).begin === 'function' &&\n typeof (em as unknown as { commit?: () => Promise<void> }).commit === 'function' &&\n typeof (em as unknown as { rollback?: () => Promise<void> }).rollback === 'function'\n if (!supportsBegin) {\n return operation(em)\n }\n await em.begin()\n try {\n const result = await operation(em)\n await em.commit()\n return result\n } catch (err) {\n try {\n await em.rollback()\n } catch {\n // rollback failure should not mask the original error; intentionally swallowed\n }\n throw err\n }\n}\n\nasync function emitNextInteractionUpdatedEvent(\n ctx: CommandRuntimeContext,\n projection: InteractionProjectionMutation,\n identifiers: InteractionIdentifiers,\n): Promise<void> {\n await emitQueryIndexUpsertEvents(ctx, [{\n entityType: 'customers:customer_entity',\n recordId: projection.entityId,\n organizationId: identifiers.organizationId,\n tenantId: identifiers.tenantId,\n }])\n await emitLifecycleEvent(ctx, 'customers.next_interaction.updated', {\n id: projection.entityId,\n entityId: projection.entityId,\n nextInteractionId: projection.nextInteractionId,\n organizationId: identifiers.organizationId,\n tenantId: identifiers.tenantId,\n })\n}\n\n// \u2500\u2500\u2500 Create \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype InteractionGraphValues = {\n id?: string\n organizationId: string\n tenantId: string\n entity: CustomerEntity\n interactionType: string\n title: string | null\n body: string | null\n status: string\n scheduledAt: Date | null\n occurredAt: Date | null\n priority: number | null\n authorUserId: string | null\n ownerUserId: string | null\n dealId: string | null\n source: string | null\n appearanceIcon: string | null\n appearanceColor: string | null\n durationMinutes: number | null\n location: string | null\n allDay: boolean | null\n recurrenceRule: string | null\n recurrenceEnd: Date | null\n participants: InteractionSnapshot['interaction']['participants']\n reminderMinutes: number | null\n visibility: string | null\n linkedEntities: InteractionSnapshot['interaction']['linkedEntities']\n guestPermissions: InteractionSnapshot['interaction']['guestPermissions']\n}\n\n// Single source of truth for the interaction row mapping, shared by `execute`\n// (values from validated input) and `redo` (values from the after-snapshot,\n// carrying the original id). Keeps create and id-preserving redo from drifting\n// field-by-field. Caller owns the surrounding transaction, persist, projection\n// recompute, and custom-field handling.\nfunction buildInteractionGraph(em: EntityManager, values: InteractionGraphValues): CustomerInteraction {\n return em.create(CustomerInteraction, {\n ...(values.id ? { id: values.id } : {}),\n organizationId: values.organizationId,\n tenantId: values.tenantId,\n entity: values.entity,\n interactionType: values.interactionType,\n title: values.title,\n body: values.body,\n status: values.status,\n scheduledAt: values.scheduledAt,\n occurredAt: values.occurredAt,\n priority: values.priority,\n authorUserId: values.authorUserId,\n ownerUserId: values.ownerUserId,\n dealId: values.dealId,\n source: values.source,\n appearanceIcon: values.appearanceIcon,\n appearanceColor: values.appearanceColor,\n durationMinutes: values.durationMinutes,\n location: values.location,\n allDay: values.allDay,\n recurrenceRule: values.recurrenceRule,\n recurrenceEnd: values.recurrenceEnd,\n participants: values.participants,\n reminderMinutes: values.reminderMinutes,\n visibility: values.visibility,\n linkedEntities: values.linkedEntities,\n guestPermissions: values.guestPermissions,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n}\n\nconst createInteractionCommand: CommandHandler<InteractionCreateInput, { interactionId: string; entityId: string }> = {\n id: 'customers.interactions.create',\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(interactionCreateSchema, rawInput)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const normalizedAuthor = normalizeAuthorUserId(parsed.authorUserId ?? null, ctx.auth)\n const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {\n const entity = await requireTimelineParentEntity(trx, parsed.entityId, { tenantId: parsed.tenantId, organizationId: parsed.organizationId })\n ensureTenantScope(ctx, entity.tenantId)\n ensureOrganizationScope(ctx, entity.organizationId)\n\n if (parsed.dealId) {\n await requireDealInScope(trx, parsed.dealId, entity.tenantId, entity.organizationId)\n }\n\n const interaction = buildInteractionGraph(trx, {\n ...(parsed.id ? { id: parsed.id } : {}),\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n entity,\n interactionType: parsed.interactionType,\n title: parsed.title ?? null,\n body: parsed.body ?? null,\n status: parsed.status ?? INTERACTION_STATUS_PLANNED,\n scheduledAt: parsed.scheduledAt ?? null,\n occurredAt: parsed.occurredAt ?? null,\n priority: parsed.priority ?? null,\n authorUserId: normalizedAuthor,\n ownerUserId: parsed.ownerUserId ?? null,\n dealId: parsed.dealId ?? null,\n source: parsed.source ?? null,\n appearanceIcon: parsed.appearanceIcon ?? null,\n appearanceColor: parsed.appearanceColor ?? null,\n durationMinutes: parsed.durationMinutes ?? null,\n location: parsed.location ?? null,\n allDay: parsed.allDay ?? null,\n recurrenceRule: parsed.recurrenceRule ?? null,\n recurrenceEnd: parsed.recurrenceEnd ?? null,\n participants: parsed.participants ?? null,\n reminderMinutes: parsed.reminderMinutes ?? null,\n visibility: parsed.visibility ?? null,\n linkedEntities: parsed.linkedEntities ?? null,\n guestPermissions: parsed.guestPermissions ?? null,\n })\n trx.persist(interaction)\n await trx.flush()\n\n await setInteractionCustomFields(\n createTransactionalDataEngine(ctx, trx),\n interaction.id,\n entity.organizationId,\n entity.tenantId,\n custom,\n )\n\n const projection = await recomputeNextInteraction(trx, entity.id)\n\n return {\n interaction,\n entityId: entity.id,\n nextInteractionId: projection.nextInteractionId,\n }\n })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: interaction,\n identifiers: {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n },\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitNextInteractionUpdatedEvent(ctx, { entityId, nextInteractionId }, {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n })\n\n return { interactionId: interaction.id, entityId }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return await loadInteractionSnapshot(em, result.interactionId)\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const snapshot = snapshots.after as InteractionSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.interactions.create', 'Create interaction'),\n resourceKind: 'customers.interaction',\n resourceId: result.interactionId,\n parentResourceKind: resolveParentResourceKind(snapshot?.interaction?.entityKind),\n parentResourceId: snapshot?.interaction?.entityId ?? null,\n tenantId: snapshot?.interaction.tenantId ?? null,\n organizationId: snapshot?.interaction.organizationId ?? null,\n snapshotAfter: snapshot ?? null,\n payload: {\n undo: {\n after: snapshot,\n } satisfies InteractionUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const interactionId = logEntry?.resourceId\n if (!interactionId) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const result = await runInTransaction(em, async (trx) => {\n const record = await findOneWithDecryption(trx, CustomerInteraction, { id: interactionId })\n if (!record) return null\n const entityId = typeof record.entity === 'string' ? record.entity : record.entity.id\n trx.remove(record)\n await trx.flush()\n const projection = await recomputeNextInteraction(trx, entityId)\n return {\n entityId,\n nextInteractionId: projection.nextInteractionId,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n }\n })\n if (!result) return\n await emitNextInteractionUpdatedEvent(ctx, {\n entityId: result.entityId,\n nextInteractionId: result.nextInteractionId,\n }, result.identifiers)\n },\n redo: async ({ logEntry, ctx }) => {\n const after = resolveRedoSnapshot<InteractionSnapshot>(logEntry)\n if (!after) {\n throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for interaction create' })\n }\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const { interaction, nextInteractionId } = await runInTransaction(em, async (trx) => {\n const entity = await requireTimelineParentEntity(trx, after.interaction.entityId, { tenantId: after.interaction.tenantId, organizationId: after.interaction.organizationId })\n let interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: after.interaction.id })\n if (!interaction) {\n interaction = buildInteractionGraph(trx, {\n id: after.interaction.id,\n organizationId: after.interaction.organizationId,\n tenantId: after.interaction.tenantId,\n entity,\n interactionType: after.interaction.interactionType,\n title: after.interaction.title,\n body: after.interaction.body,\n status: after.interaction.status,\n scheduledAt: after.interaction.scheduledAt,\n occurredAt: after.interaction.occurredAt,\n priority: after.interaction.priority,\n authorUserId: after.interaction.authorUserId,\n ownerUserId: after.interaction.ownerUserId,\n dealId: after.interaction.dealId,\n source: after.interaction.source,\n appearanceIcon: after.interaction.appearanceIcon,\n appearanceColor: after.interaction.appearanceColor,\n durationMinutes: after.interaction.durationMinutes,\n location: after.interaction.location,\n allDay: after.interaction.allDay,\n recurrenceRule: after.interaction.recurrenceRule,\n recurrenceEnd: after.interaction.recurrenceEnd,\n participants: after.interaction.participants,\n reminderMinutes: after.interaction.reminderMinutes,\n visibility: after.interaction.visibility,\n linkedEntities: after.interaction.linkedEntities,\n guestPermissions: after.interaction.guestPermissions,\n })\n trx.persist(interaction)\n } else {\n interaction.deletedAt = null\n interaction.entity = entity\n interaction.interactionType = after.interaction.interactionType\n interaction.title = after.interaction.title\n interaction.body = after.interaction.body\n interaction.status = after.interaction.status\n interaction.scheduledAt = after.interaction.scheduledAt\n interaction.occurredAt = after.interaction.occurredAt\n interaction.priority = after.interaction.priority\n interaction.authorUserId = after.interaction.authorUserId\n interaction.ownerUserId = after.interaction.ownerUserId\n interaction.dealId = after.interaction.dealId\n interaction.source = after.interaction.source\n interaction.appearanceIcon = after.interaction.appearanceIcon\n interaction.appearanceColor = after.interaction.appearanceColor\n interaction.durationMinutes = after.interaction.durationMinutes\n interaction.location = after.interaction.location\n interaction.allDay = after.interaction.allDay\n interaction.recurrenceRule = after.interaction.recurrenceRule\n interaction.recurrenceEnd = after.interaction.recurrenceEnd\n interaction.participants = after.interaction.participants\n interaction.reminderMinutes = after.interaction.reminderMinutes\n interaction.visibility = after.interaction.visibility\n interaction.linkedEntities = after.interaction.linkedEntities\n interaction.guestPermissions = after.interaction.guestPermissions\n }\n await trx.flush()\n\n const projection = await recomputeNextInteraction(trx, after.interaction.entityId)\n\n const restoreValues = buildCustomFieldResetMap(after.custom, undefined)\n if (Object.keys(restoreValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: createTransactionalDataEngine(ctx, trx),\n entityId: INTERACTION_ENTITY_ID,\n recordId: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n values: restoreValues,\n notify: false,\n })\n }\n\n return { interaction, nextInteractionId: projection.nextInteractionId }\n })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: interaction,\n identifiers: {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n },\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitNextInteractionUpdatedEvent(ctx, {\n entityId: after.interaction.entityId,\n nextInteractionId,\n }, {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n })\n\n return { interactionId: interaction.id, entityId: after.interaction.entityId }\n },\n}\n\n// \u2500\u2500\u2500 Update \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst updateInteractionCommand: CommandHandler<InteractionUpdateInput, { interactionId: string }> = {\n id: 'customers.interactions.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseWithCustomFields(interactionUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadInteractionSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(interactionUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {\n const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: parsed.id, deletedAt: null })\n if (!interaction) {\n enforceRecordGoneIsConflict({ resourceKind: 'customers.interaction', resourceId: parsed.id, request: ctx.request ?? null })\n throw notFound('Interaction not found')\n }\n ensureTenantScope(ctx, interaction.tenantId)\n ensureOrganizationScope(ctx, interaction.organizationId)\n\n // Concurrent-edit guard for command-driven callers (e.g. the legacy\n // /api/customers/todos route, which bypasses the makeCrudRoute lock guard):\n // when the client opted into optimistic locking, a stale edit fails with the\n // unified 409 instead of silently overwriting (#2055). Strictly additive \u2014\n // no-op when no expected-version header is present.\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: 'customers.interaction',\n resourceId: interaction.id,\n current: interaction.updatedAt,\n request: ctx.request ?? null,\n })\n\n // Email visibility is an access-controlled field: only the interaction's\n // author may change a\n // private email's visibility (mirrors the dedicated PATCH .../visibility\n // route). Enforce it here \u2014 the single persistence path \u2014 so the generic\n // update route (PUT /api/interactions) cannot bypass the gate. Evaluated\n // against the row's pre-mutation author/type. 404 (not 403) keeps the\n // existence-masking consistent with the dedicated route.\n if (\n parsed.visibility !== undefined &&\n interaction.interactionType === 'email' &&\n (parsed.visibility ?? null) !== (interaction.visibility ?? null)\n ) {\n const actorUserId = (ctx.auth as { sub?: string | null } | null)?.sub ?? null\n if (\n !canChangeEmailVisibility({\n interactionType: interaction.interactionType,\n currentVisibility: interaction.visibility,\n nextVisibility: parsed.visibility,\n authorUserId: interaction.authorUserId,\n actorUserId,\n // v1 strict owner-only: only the author may flip visibility; no admin\n // bypass (canChangeEmailVisibility ignores caller features in v1).\n userFeatures: undefined,\n })\n ) {\n throw notFound('Email not found')\n }\n }\n\n if (parsed.dealId !== undefined) {\n if (parsed.dealId) {\n await requireDealInScope(trx, parsed.dealId, interaction.tenantId, interaction.organizationId)\n }\n interaction.dealId = parsed.dealId ?? null\n }\n if (parsed.interactionType !== undefined) interaction.interactionType = parsed.interactionType\n if (parsed.title !== undefined) interaction.title = parsed.title ?? null\n if (parsed.body !== undefined) interaction.body = parsed.body ?? null\n if (parsed.status !== undefined) interaction.status = parsed.status\n if (parsed.scheduledAt !== undefined) interaction.scheduledAt = parsed.scheduledAt ?? null\n if (parsed.occurredAt !== undefined) interaction.occurredAt = parsed.occurredAt ?? null\n if (parsed.priority !== undefined) interaction.priority = parsed.priority ?? null\n if (parsed.authorUserId !== undefined) interaction.authorUserId = parsed.authorUserId ?? null\n if (parsed.ownerUserId !== undefined) interaction.ownerUserId = parsed.ownerUserId ?? null\n if (parsed.appearanceIcon !== undefined) interaction.appearanceIcon = parsed.appearanceIcon ?? null\n if (parsed.appearanceColor !== undefined) interaction.appearanceColor = parsed.appearanceColor ?? null\n if (parsed.pinned !== undefined) interaction.pinned = parsed.pinned\n if (parsed.durationMinutes !== undefined) interaction.durationMinutes = parsed.durationMinutes ?? null\n if (parsed.location !== undefined) interaction.location = parsed.location ?? null\n if (parsed.allDay !== undefined) interaction.allDay = parsed.allDay ?? null\n if (parsed.recurrenceRule !== undefined) interaction.recurrenceRule = parsed.recurrenceRule ?? null\n if (parsed.recurrenceEnd !== undefined) interaction.recurrenceEnd = parsed.recurrenceEnd ?? null\n if (parsed.participants !== undefined) interaction.participants = parsed.participants ?? null\n if (parsed.reminderMinutes !== undefined) interaction.reminderMinutes = parsed.reminderMinutes ?? null\n if (parsed.visibility !== undefined) interaction.visibility = parsed.visibility ?? null\n if (parsed.linkedEntities !== undefined) interaction.linkedEntities = parsed.linkedEntities ?? null\n if (parsed.guestPermissions !== undefined) interaction.guestPermissions = parsed.guestPermissions ?? null\n\n await trx.flush()\n\n const entityId = typeof interaction.entity === 'string' ? interaction.entity : interaction.entity.id\n await setInteractionCustomFields(\n createTransactionalDataEngine(ctx, trx),\n interaction.id,\n interaction.organizationId,\n interaction.tenantId,\n custom,\n )\n\n const projection = await recomputeNextInteraction(trx, entityId)\n\n return { interaction, entityId, nextInteractionId: projection.nextInteractionId }\n })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: interaction,\n identifiers: {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n },\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitNextInteractionUpdatedEvent(ctx, { entityId, nextInteractionId }, {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n })\n\n return { interactionId: interaction.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return await loadInteractionSnapshot(em, result.interactionId)\n },\n buildLog: async ({ snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as InteractionSnapshot | undefined\n if (!before) return null\n const afterSnapshot = snapshots.after as InteractionSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.interactions.update', 'Update interaction'),\n resourceKind: 'customers.interaction',\n resourceId: before.interaction.id,\n parentResourceKind: resolveParentResourceKind(before.interaction.entityKind),\n parentResourceId: before.interaction.entityId ?? null,\n tenantId: before.interaction.tenantId,\n organizationId: before.interaction.organizationId,\n snapshotBefore: before,\n snapshotAfter: afterSnapshot ?? null,\n payload: {\n undo: {\n before,\n after: afterSnapshot ?? null,\n } satisfies InteractionUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<InteractionUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const { interaction, nextInteractionId } = await runInTransaction(em, async (trx) => {\n let interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: before.interaction.id })\n const entity = await requireTimelineParentEntity(trx, before.interaction.entityId, { tenantId: before.interaction.tenantId, organizationId: before.interaction.organizationId })\n\n if (!interaction) {\n interaction = trx.create(CustomerInteraction, {\n id: before.interaction.id,\n organizationId: before.interaction.organizationId,\n tenantId: before.interaction.tenantId,\n entity,\n interactionType: before.interaction.interactionType,\n title: before.interaction.title,\n body: before.interaction.body,\n status: before.interaction.status,\n scheduledAt: before.interaction.scheduledAt,\n occurredAt: before.interaction.occurredAt,\n priority: before.interaction.priority,\n authorUserId: before.interaction.authorUserId,\n ownerUserId: before.interaction.ownerUserId,\n dealId: before.interaction.dealId,\n source: before.interaction.source,\n appearanceIcon: before.interaction.appearanceIcon,\n appearanceColor: before.interaction.appearanceColor,\n durationMinutes: before.interaction.durationMinutes,\n location: before.interaction.location,\n allDay: before.interaction.allDay,\n recurrenceRule: before.interaction.recurrenceRule,\n recurrenceEnd: before.interaction.recurrenceEnd,\n participants: before.interaction.participants,\n reminderMinutes: before.interaction.reminderMinutes,\n visibility: before.interaction.visibility,\n linkedEntities: before.interaction.linkedEntities,\n guestPermissions: before.interaction.guestPermissions,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n trx.persist(interaction)\n } else {\n interaction.entity = entity\n interaction.interactionType = before.interaction.interactionType\n interaction.title = before.interaction.title\n interaction.body = before.interaction.body\n interaction.status = before.interaction.status\n interaction.scheduledAt = before.interaction.scheduledAt\n interaction.occurredAt = before.interaction.occurredAt\n interaction.priority = before.interaction.priority\n interaction.authorUserId = before.interaction.authorUserId\n interaction.ownerUserId = before.interaction.ownerUserId\n interaction.dealId = before.interaction.dealId\n interaction.source = before.interaction.source\n interaction.appearanceIcon = before.interaction.appearanceIcon\n interaction.appearanceColor = before.interaction.appearanceColor\n interaction.durationMinutes = before.interaction.durationMinutes\n interaction.location = before.interaction.location\n interaction.allDay = before.interaction.allDay\n interaction.recurrenceRule = before.interaction.recurrenceRule\n interaction.recurrenceEnd = before.interaction.recurrenceEnd\n interaction.participants = before.interaction.participants\n interaction.reminderMinutes = before.interaction.reminderMinutes\n interaction.visibility = before.interaction.visibility\n interaction.linkedEntities = before.interaction.linkedEntities\n interaction.guestPermissions = before.interaction.guestPermissions\n }\n\n await trx.flush()\n const projection = await recomputeNextInteraction(trx, before.interaction.entityId)\n\n const resetValues = buildCustomFieldResetMap(before.custom, payload?.after?.custom)\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: createTransactionalDataEngine(ctx, trx),\n entityId: INTERACTION_ENTITY_ID,\n recordId: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n values: resetValues,\n notify: false,\n })\n }\n\n return {\n interaction,\n nextInteractionId: projection.nextInteractionId,\n }\n })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: interaction,\n identifiers: {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n },\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitNextInteractionUpdatedEvent(ctx, {\n entityId: before.interaction.entityId,\n nextInteractionId,\n }, {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n })\n },\n}\n\n// \u2500\u2500\u2500 Complete \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst completeInteractionCommand: CommandHandler<InteractionCompleteInput, { interactionId: string }> = {\n id: 'customers.interactions.complete',\n async prepare(rawInput, ctx) {\n const parsed = interactionCompleteSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadInteractionSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const parsed = interactionCompleteSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {\n const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: parsed.id, deletedAt: null })\n if (!interaction) {\n enforceRecordGoneIsConflict({ resourceKind: 'customers.interaction', resourceId: parsed.id, request: ctx.request ?? null })\n throw notFound('Interaction not found')\n }\n ensureTenantScope(ctx, interaction.tenantId)\n ensureOrganizationScope(ctx, interaction.organizationId)\n\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: 'customers.interaction',\n resourceId: interaction.id,\n current: interaction.updatedAt,\n request: ctx.request ?? null,\n })\n\n interaction.status = INTERACTION_STATUS_COMPLETED\n interaction.occurredAt = parsed.occurredAt ?? new Date()\n await trx.flush()\n\n const entityId = typeof interaction.entity === 'string' ? interaction.entity : interaction.entity.id\n const projection = await recomputeNextInteraction(trx, entityId)\n return { interaction, entityId, nextInteractionId: projection.nextInteractionId }\n })\n\n const identifiers = {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n }\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: interaction,\n identifiers,\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitLifecycleEvent(ctx, 'customers.interaction.completed', {\n ...identifiers,\n entityId,\n interactionType: interaction.interactionType,\n status: interaction.status,\n source: interaction.source ?? null,\n occurredAt: interaction.occurredAt?.toISOString() ?? null,\n ...(ctx.syncOrigin ? { syncOrigin: ctx.syncOrigin } : {}),\n })\n await emitNextInteractionUpdatedEvent(ctx, { entityId, nextInteractionId }, identifiers)\n\n return { interactionId: interaction.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return await loadInteractionSnapshot(em, result.interactionId)\n },\n buildLog: async ({ snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as InteractionSnapshot | undefined\n if (!before) return null\n const afterSnapshot = snapshots.after as InteractionSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.interactions.complete', 'Complete interaction'),\n resourceKind: 'customers.interaction',\n resourceId: before.interaction.id,\n parentResourceKind: resolveParentResourceKind(before.interaction.entityKind),\n parentResourceId: before.interaction.entityId ?? null,\n tenantId: before.interaction.tenantId,\n organizationId: before.interaction.organizationId,\n snapshotBefore: before,\n snapshotAfter: afterSnapshot ?? null,\n payload: {\n undo: {\n before,\n after: afterSnapshot ?? null,\n } satisfies InteractionUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<InteractionUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const result = await runInTransaction(em, async (trx) => {\n const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: before.interaction.id })\n if (!interaction) return null\n\n interaction.status = before.interaction.status\n interaction.occurredAt = before.interaction.occurredAt\n await trx.flush()\n\n const projection = await recomputeNextInteraction(trx, before.interaction.entityId)\n return {\n interaction,\n nextInteractionId: projection.nextInteractionId,\n }\n })\n if (!result) return\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: result.interaction,\n identifiers: {\n id: result.interaction.id,\n organizationId: result.interaction.organizationId,\n tenantId: result.interaction.tenantId,\n },\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitInteractionRevertedEvent(ctx, before.interaction)\n await emitNextInteractionUpdatedEvent(ctx, {\n entityId: before.interaction.entityId,\n nextInteractionId: result.nextInteractionId,\n }, {\n id: result.interaction.id,\n organizationId: result.interaction.organizationId,\n tenantId: result.interaction.tenantId,\n })\n },\n}\n\n// \u2500\u2500\u2500 Cancel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst cancelInteractionCommand: CommandHandler<InteractionCancelInput, { interactionId: string }> = {\n id: 'customers.interactions.cancel',\n async prepare(rawInput, ctx) {\n const parsed = interactionCancelSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadInteractionSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const parsed = interactionCancelSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {\n const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: parsed.id, deletedAt: null })\n if (!interaction) {\n enforceRecordGoneIsConflict({ resourceKind: 'customers.interaction', resourceId: parsed.id, request: ctx.request ?? null })\n throw notFound('Interaction not found')\n }\n ensureTenantScope(ctx, interaction.tenantId)\n ensureOrganizationScope(ctx, interaction.organizationId)\n\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: 'customers.interaction',\n resourceId: interaction.id,\n current: interaction.updatedAt,\n request: ctx.request ?? null,\n })\n\n interaction.status = INTERACTION_STATUS_CANCELED\n await trx.flush()\n\n const entityId = typeof interaction.entity === 'string' ? interaction.entity : interaction.entity.id\n const projection = await recomputeNextInteraction(trx, entityId)\n return { interaction, entityId, nextInteractionId: projection.nextInteractionId }\n })\n\n const identifiers = {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n }\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: interaction,\n identifiers,\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitLifecycleEvent(ctx, 'customers.interaction.canceled', {\n ...identifiers,\n entityId,\n interactionType: interaction.interactionType,\n status: interaction.status,\n source: interaction.source ?? null,\n ...(ctx.syncOrigin ? { syncOrigin: ctx.syncOrigin } : {}),\n })\n await emitNextInteractionUpdatedEvent(ctx, { entityId, nextInteractionId }, identifiers)\n\n return { interactionId: interaction.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return await loadInteractionSnapshot(em, result.interactionId)\n },\n buildLog: async ({ snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as InteractionSnapshot | undefined\n if (!before) return null\n const afterSnapshot = snapshots.after as InteractionSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.interactions.cancel', 'Cancel interaction'),\n resourceKind: 'customers.interaction',\n resourceId: before.interaction.id,\n parentResourceKind: resolveParentResourceKind(before.interaction.entityKind),\n parentResourceId: before.interaction.entityId ?? null,\n tenantId: before.interaction.tenantId,\n organizationId: before.interaction.organizationId,\n snapshotBefore: before,\n snapshotAfter: afterSnapshot ?? null,\n payload: {\n undo: {\n before,\n after: afterSnapshot ?? null,\n } satisfies InteractionUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<InteractionUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const result = await runInTransaction(em, async (trx) => {\n const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: before.interaction.id })\n if (!interaction) return null\n\n interaction.status = before.interaction.status\n await trx.flush()\n\n const projection = await recomputeNextInteraction(trx, before.interaction.entityId)\n return {\n interaction,\n nextInteractionId: projection.nextInteractionId,\n }\n })\n if (!result) return\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: result.interaction,\n identifiers: {\n id: result.interaction.id,\n organizationId: result.interaction.organizationId,\n tenantId: result.interaction.tenantId,\n },\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitInteractionRevertedEvent(ctx, before.interaction)\n await emitNextInteractionUpdatedEvent(ctx, {\n entityId: before.interaction.entityId,\n nextInteractionId: result.nextInteractionId,\n }, {\n id: result.interaction.id,\n organizationId: result.interaction.organizationId,\n tenantId: result.interaction.tenantId,\n })\n },\n}\n\n// \u2500\u2500\u2500 Delete \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst deleteInteractionCommand: CommandHandler<{ body?: Record<string, unknown>; query?: Record<string, unknown> }, { interactionId: string }> =\n {\n id: 'customers.interactions.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Interaction id required')\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadInteractionSnapshot(em, id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Interaction id required')\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {\n const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id, deletedAt: null })\n if (!interaction) {\n enforceRecordGoneIsConflict({ resourceKind: 'customers.interaction', resourceId: id, request: ctx.request ?? null })\n throw notFound('Interaction not found')\n }\n ensureTenantScope(ctx, interaction.tenantId)\n ensureOrganizationScope(ctx, interaction.organizationId)\n\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: 'customers.interaction',\n resourceId: interaction.id,\n current: interaction.updatedAt,\n request: ctx.request ?? null,\n })\n\n const entityId = typeof interaction.entity === 'string' ? interaction.entity : interaction.entity.id\n interaction.deletedAt = new Date()\n await trx.flush()\n\n const projection = await recomputeNextInteraction(trx, entityId)\n return { interaction, entityId, nextInteractionId: projection.nextInteractionId }\n })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: interaction,\n identifiers: {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n },\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitNextInteractionUpdatedEvent(ctx, { entityId, nextInteractionId }, {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n })\n return { interactionId: interaction.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as InteractionSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('customers.audit.interactions.delete', 'Delete interaction'),\n resourceKind: 'customers.interaction',\n resourceId: before.interaction.id,\n parentResourceKind: resolveParentResourceKind(before.interaction.entityKind),\n parentResourceId: before.interaction.entityId ?? null,\n tenantId: before.interaction.tenantId,\n organizationId: before.interaction.organizationId,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n } satisfies InteractionUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<InteractionUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const { interaction, nextInteractionId } = await runInTransaction(em, async (trx) => {\n const entity = await requireTimelineParentEntity(trx, before.interaction.entityId, { tenantId: before.interaction.tenantId, organizationId: before.interaction.organizationId })\n let interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: before.interaction.id })\n if (!interaction) {\n interaction = trx.create(CustomerInteraction, {\n id: before.interaction.id,\n organizationId: before.interaction.organizationId,\n tenantId: before.interaction.tenantId,\n entity,\n interactionType: before.interaction.interactionType,\n title: before.interaction.title,\n body: before.interaction.body,\n status: before.interaction.status,\n scheduledAt: before.interaction.scheduledAt,\n occurredAt: before.interaction.occurredAt,\n priority: before.interaction.priority,\n authorUserId: before.interaction.authorUserId,\n ownerUserId: before.interaction.ownerUserId,\n dealId: before.interaction.dealId,\n source: before.interaction.source,\n appearanceIcon: before.interaction.appearanceIcon,\n appearanceColor: before.interaction.appearanceColor,\n durationMinutes: before.interaction.durationMinutes,\n location: before.interaction.location,\n allDay: before.interaction.allDay,\n recurrenceRule: before.interaction.recurrenceRule,\n recurrenceEnd: before.interaction.recurrenceEnd,\n participants: before.interaction.participants,\n reminderMinutes: before.interaction.reminderMinutes,\n visibility: before.interaction.visibility,\n linkedEntities: before.interaction.linkedEntities,\n guestPermissions: before.interaction.guestPermissions,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n trx.persist(interaction)\n } else {\n interaction.deletedAt = null\n interaction.entity = entity\n interaction.interactionType = before.interaction.interactionType\n interaction.title = before.interaction.title\n interaction.body = before.interaction.body\n interaction.status = before.interaction.status\n interaction.scheduledAt = before.interaction.scheduledAt\n interaction.occurredAt = before.interaction.occurredAt\n interaction.priority = before.interaction.priority\n interaction.authorUserId = before.interaction.authorUserId\n interaction.ownerUserId = before.interaction.ownerUserId\n interaction.dealId = before.interaction.dealId\n interaction.source = before.interaction.source\n interaction.appearanceIcon = before.interaction.appearanceIcon\n interaction.appearanceColor = before.interaction.appearanceColor\n interaction.durationMinutes = before.interaction.durationMinutes\n interaction.location = before.interaction.location\n interaction.allDay = before.interaction.allDay\n interaction.recurrenceRule = before.interaction.recurrenceRule\n interaction.recurrenceEnd = before.interaction.recurrenceEnd\n interaction.participants = before.interaction.participants\n interaction.reminderMinutes = before.interaction.reminderMinutes\n interaction.visibility = before.interaction.visibility\n interaction.linkedEntities = before.interaction.linkedEntities\n interaction.guestPermissions = before.interaction.guestPermissions\n }\n await trx.flush()\n\n const projection = await recomputeNextInteraction(trx, before.interaction.entityId)\n\n const resetValues = buildCustomFieldResetMap(before.custom, undefined)\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: createTransactionalDataEngine(ctx, trx),\n entityId: INTERACTION_ENTITY_ID,\n recordId: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n values: resetValues,\n notify: false,\n })\n }\n\n return {\n interaction,\n nextInteractionId: projection.nextInteractionId,\n }\n })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'created',\n entity: interaction,\n identifiers: {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n },\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitNextInteractionUpdatedEvent(ctx, {\n entityId: before.interaction.entityId,\n nextInteractionId,\n }, {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n })\n },\n }\n\n// \u2500\u2500\u2500 Recompute Next (internal repair) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst recomputeNextSchema = z.object({ entityId: z.string().min(1) })\n\nconst recomputeNextCommand: CommandHandler<{ entityId: string }, { entityId: string }> = {\n id: 'customers.interaction.recompute_next',\n async execute(rawInput, ctx) {\n const parsed = recomputeNextSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const projection = await recomputeNextInteraction(em, parsed.entityId)\n const entity = await requireTimelineParentEntity(em, parsed.entityId, {\n tenantId: ctx.auth?.tenantId ?? '',\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? '',\n })\n await emitNextInteractionUpdatedEvent(ctx, {\n entityId: parsed.entityId,\n nextInteractionId: projection.nextInteractionId,\n }, {\n id: parsed.entityId,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n })\n return { entityId: parsed.entityId }\n },\n}\n\nregisterCommand(createInteractionCommand)\nregisterCommand(updateInteractionCommand)\nregisterCommand(completeInteractionCommand)\nregisterCommand(cancelInteractionCommand)\nregisterCommand(deleteInteractionCommand)\nregisterCommand(recomputeNextCommand)\n"],
4
+ "sourcesContent": ["import { z } from 'zod'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport {\n parseWithCustomFields,\n setCustomFieldsIfAny,\n emitCrudSideEffects,\n emitCrudUndoSideEffects,\n requireId,\n normalizeAuthorUserId,\n} from '@open-mercato/shared/lib/commands/helpers'\nimport { DefaultDataEngine, type DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { CustomerInteraction, CustomerEntity } from '../data/entities'\nimport {\n interactionCreateSchema,\n interactionUpdateSchema,\n interactionCompleteSchema,\n interactionCancelSchema,\n type InteractionCreateInput,\n type InteractionUpdateInput,\n type InteractionCompleteInput,\n type InteractionCancelInput,\n} from '../data/validators'\nimport {\n ensureOrganizationScope,\n ensureTenantScope,\n requireTimelineParentEntity,\n extractUndoPayload,\n emitQueryIndexUpsertEvents,\n requireDealInScope,\n resolveParentResourceKind,\n} from './shared'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport {\n loadCustomFieldSnapshot,\n buildCustomFieldResetMap,\n} from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { CrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceRecordGoneIsConflict, enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { CrudIndexerConfig, CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport { recomputeNextInteraction } from '../lib/interactionProjection'\nimport {\n INTERACTION_STATUS_CANCELED,\n INTERACTION_STATUS_COMPLETED,\n INTERACTION_STATUS_PLANNED,\n} from '../lib/interactionStatus'\nimport { canChangeEmailVisibility } from '../lib/visibilityFilter'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\nconst INTERACTION_ENTITY_ID = 'customers:customer_interaction'\nconst interactionCrudIndexer: CrudIndexerConfig<CustomerInteraction> = {\n entityType: 'customers:customer_interaction' as const,\n}\n\nconst interactionCrudEvents: CrudEventsConfig = {\n module: 'customers',\n entity: 'interaction',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n entityId:\n ctx.entity && typeof ctx.entity === 'object' && 'entity' in (ctx.entity as Record<string, unknown>)\n ? (() => {\n const entityRef = (ctx.entity as CustomerInteraction).entity\n return typeof entityRef === 'string' ? entityRef : entityRef?.id ?? null\n })()\n : null,\n interactionType:\n ctx.entity && typeof ctx.entity === 'object' && 'interactionType' in (ctx.entity as Record<string, unknown>)\n ? (ctx.entity as CustomerInteraction).interactionType\n : null,\n status:\n ctx.entity && typeof ctx.entity === 'object' && 'status' in (ctx.entity as Record<string, unknown>)\n ? (ctx.entity as CustomerInteraction).status\n : null,\n source:\n ctx.entity && typeof ctx.entity === 'object' && 'source' in (ctx.entity as Record<string, unknown>)\n ? (ctx.entity as CustomerInteraction).source ?? null\n : null,\n ...(ctx.syncOrigin ? { syncOrigin: ctx.syncOrigin } : {}),\n }),\n}\n\ntype InteractionSnapshot = {\n interaction: {\n id: string\n organizationId: string\n tenantId: string\n entityId: string\n entityKind: string | null\n dealId: string | null\n interactionType: string\n title: string | null\n body: string | null\n status: string\n scheduledAt: Date | null\n occurredAt: Date | null\n priority: number | null\n authorUserId: string | null\n ownerUserId: string | null\n appearanceIcon: string | null\n appearanceColor: string | null\n source: string | null\n durationMinutes: number | null\n location: string | null\n allDay: boolean | null\n recurrenceRule: string | null\n recurrenceEnd: Date | null\n participants: Array<{ userId?: string; name?: string; email?: string; status?: string }> | null\n reminderMinutes: number | null\n visibility: string | null\n linkedEntities: Array<{ id: string; type: string; label: string }> | null\n guestPermissions: { canInviteOthers?: boolean; canModify?: boolean; canSeeList?: boolean } | null\n }\n custom?: Record<string, unknown>\n}\n\ntype InteractionUndoPayload = {\n before?: InteractionSnapshot | null\n after?: InteractionSnapshot | null\n}\n\nasync function loadInteractionSnapshot(em: EntityManager, id: string): Promise<InteractionSnapshot | null> {\n const interaction = await findOneWithDecryption(em, CustomerInteraction, { id }, { populate: ['entity'] })\n if (!interaction) return null\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: INTERACTION_ENTITY_ID,\n recordId: interaction.id,\n tenantId: interaction.tenantId,\n organizationId: interaction.organizationId,\n })\n const entityRef = interaction.entity\n const entityKind = (typeof entityRef === 'object' && entityRef !== null && 'kind' in entityRef)\n ? (entityRef as { kind: string }).kind\n : null\n return {\n interaction: {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n entityId: typeof entityRef === 'string' ? entityRef : entityRef.id,\n entityKind,\n dealId: interaction.dealId ?? null,\n interactionType: interaction.interactionType,\n title: interaction.title ?? null,\n body: interaction.body ?? null,\n status: interaction.status,\n scheduledAt: interaction.scheduledAt ?? null,\n occurredAt: interaction.occurredAt ?? null,\n priority: interaction.priority ?? null,\n authorUserId: interaction.authorUserId ?? null,\n ownerUserId: interaction.ownerUserId ?? null,\n appearanceIcon: interaction.appearanceIcon ?? null,\n appearanceColor: interaction.appearanceColor ?? null,\n source: interaction.source ?? null,\n durationMinutes: interaction.durationMinutes ?? null,\n location: interaction.location ?? null,\n allDay: interaction.allDay ?? null,\n recurrenceRule: interaction.recurrenceRule ?? null,\n recurrenceEnd: interaction.recurrenceEnd ?? null,\n participants: interaction.participants ?? null,\n reminderMinutes: interaction.reminderMinutes ?? null,\n visibility: interaction.visibility ?? null,\n linkedEntities: interaction.linkedEntities ?? null,\n guestPermissions: interaction.guestPermissions ?? null,\n },\n custom,\n }\n}\n\nasync function setInteractionCustomFields(\n dataEngine: DataEngine,\n interactionId: string,\n organizationId: string,\n tenantId: string,\n values: Record<string, unknown>\n) {\n if (!values || !Object.keys(values).length) return\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: INTERACTION_ENTITY_ID,\n recordId: interactionId,\n organizationId,\n tenantId,\n values,\n notify: false,\n })\n}\n\nasync function emitLifecycleEvent(\n ctx: CommandRuntimeContext,\n eventId: string,\n payload: Record<string, unknown>\n): Promise<void> {\n let bus: { emitEvent(event: string, payload: unknown, options?: unknown): Promise<void> } | null = null\n try {\n bus = ctx.container.resolve('eventBus')\n } catch (err) {\n logger.warn('eventBus resolve failed; skipping emit', { component: 'commands.interactions', eventId, err })\n bus = null\n }\n if (!bus) return\n await bus\n .emitEvent(eventId, payload, { persistent: true })\n .catch((err) => {\n logger.warn('Event emit failed', { component: 'commands.interactions', eventId, err })\n return undefined\n })\n}\n\nasync function emitInteractionRevertedEvent(\n ctx: CommandRuntimeContext,\n interaction: InteractionSnapshot['interaction'],\n): Promise<void> {\n await emitLifecycleEvent(ctx, 'customers.interaction.reverted', {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n entityId: interaction.entityId,\n interactionType: interaction.interactionType,\n source: interaction.source ?? null,\n status: interaction.status,\n occurredAt: interaction.occurredAt?.toISOString() ?? null,\n ...(ctx.syncOrigin ? { syncOrigin: ctx.syncOrigin } : {}),\n })\n}\n\ntype InteractionIdentifiers = {\n id: string\n organizationId: string\n tenantId: string\n}\n\ntype InteractionProjectionMutation = {\n entityId: string\n nextInteractionId: string | null\n}\n\nfunction createTransactionalDataEngine(ctx: CommandRuntimeContext, em: EntityManager): DataEngine {\n const emWithConnection = em as EntityManager & { getConnection?: () => unknown }\n if (typeof emWithConnection.getConnection !== 'function') {\n return ctx.container.resolve('dataEngine') as DataEngine\n }\n return new DefaultDataEngine(em, ctx.container)\n}\n\nasync function runInTransaction<TResult>(\n em: EntityManager,\n operation: (trx: EntityManager) => Promise<TResult>,\n): Promise<TResult> {\n // Mirrors the SPEC-018 fix applied to withAtomicFlush: use explicit begin/commit/rollback\n // so the outer EntityManager stays bound to the transaction, and closures over `em` inside\n // `operation` participate in the same transaction. This avoids the em.transactional(cb)\n // hazard where the callback receives a child EM whose flushes can silently race against\n // subsequent queries on the original `em`.\n const supportsBegin =\n typeof (em as unknown as { begin?: () => Promise<void> }).begin === 'function' &&\n typeof (em as unknown as { commit?: () => Promise<void> }).commit === 'function' &&\n typeof (em as unknown as { rollback?: () => Promise<void> }).rollback === 'function'\n if (!supportsBegin) {\n return operation(em)\n }\n await em.begin()\n try {\n const result = await operation(em)\n await em.commit()\n return result\n } catch (err) {\n try {\n await em.rollback()\n } catch {\n // rollback failure should not mask the original error; intentionally swallowed\n }\n throw err\n }\n}\n\nasync function emitNextInteractionUpdatedEvent(\n ctx: CommandRuntimeContext,\n projection: InteractionProjectionMutation,\n identifiers: InteractionIdentifiers,\n): Promise<void> {\n await emitQueryIndexUpsertEvents(ctx, [{\n entityType: 'customers:customer_entity',\n recordId: projection.entityId,\n organizationId: identifiers.organizationId,\n tenantId: identifiers.tenantId,\n }])\n await emitLifecycleEvent(ctx, 'customers.next_interaction.updated', {\n id: projection.entityId,\n entityId: projection.entityId,\n nextInteractionId: projection.nextInteractionId,\n organizationId: identifiers.organizationId,\n tenantId: identifiers.tenantId,\n })\n}\n\n// \u2500\u2500\u2500 Create \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype InteractionGraphValues = {\n id?: string\n organizationId: string\n tenantId: string\n entity: CustomerEntity\n interactionType: string\n title: string | null\n body: string | null\n status: string\n scheduledAt: Date | null\n occurredAt: Date | null\n priority: number | null\n authorUserId: string | null\n ownerUserId: string | null\n dealId: string | null\n source: string | null\n appearanceIcon: string | null\n appearanceColor: string | null\n durationMinutes: number | null\n location: string | null\n allDay: boolean | null\n recurrenceRule: string | null\n recurrenceEnd: Date | null\n participants: InteractionSnapshot['interaction']['participants']\n reminderMinutes: number | null\n visibility: string | null\n linkedEntities: InteractionSnapshot['interaction']['linkedEntities']\n guestPermissions: InteractionSnapshot['interaction']['guestPermissions']\n}\n\n// Single source of truth for the interaction row mapping, shared by `execute`\n// (values from validated input) and `redo` (values from the after-snapshot,\n// carrying the original id). Keeps create and id-preserving redo from drifting\n// field-by-field. Caller owns the surrounding transaction, persist, projection\n// recompute, and custom-field handling.\nfunction buildInteractionGraph(em: EntityManager, values: InteractionGraphValues): CustomerInteraction {\n return em.create(CustomerInteraction, {\n ...(values.id ? { id: values.id } : {}),\n organizationId: values.organizationId,\n tenantId: values.tenantId,\n entity: values.entity,\n interactionType: values.interactionType,\n title: values.title,\n body: values.body,\n status: values.status,\n scheduledAt: values.scheduledAt,\n occurredAt: values.occurredAt,\n priority: values.priority,\n authorUserId: values.authorUserId,\n ownerUserId: values.ownerUserId,\n dealId: values.dealId,\n source: values.source,\n appearanceIcon: values.appearanceIcon,\n appearanceColor: values.appearanceColor,\n durationMinutes: values.durationMinutes,\n location: values.location,\n allDay: values.allDay,\n recurrenceRule: values.recurrenceRule,\n recurrenceEnd: values.recurrenceEnd,\n participants: values.participants,\n reminderMinutes: values.reminderMinutes,\n visibility: values.visibility,\n linkedEntities: values.linkedEntities,\n guestPermissions: values.guestPermissions,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n}\n\nconst createInteractionCommand: CommandHandler<InteractionCreateInput, { interactionId: string; entityId: string }> = {\n id: 'customers.interactions.create',\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(interactionCreateSchema, rawInput)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const normalizedAuthor = normalizeAuthorUserId(parsed.authorUserId ?? null, ctx.auth)\n const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {\n const entity = await requireTimelineParentEntity(trx, parsed.entityId, { tenantId: parsed.tenantId, organizationId: parsed.organizationId })\n ensureTenantScope(ctx, entity.tenantId)\n ensureOrganizationScope(ctx, entity.organizationId)\n\n if (parsed.dealId) {\n await requireDealInScope(trx, parsed.dealId, entity.tenantId, entity.organizationId)\n }\n\n const interaction = buildInteractionGraph(trx, {\n ...(parsed.id ? { id: parsed.id } : {}),\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n entity,\n interactionType: parsed.interactionType,\n title: parsed.title ?? null,\n body: parsed.body ?? null,\n status: parsed.status ?? INTERACTION_STATUS_PLANNED,\n scheduledAt: parsed.scheduledAt ?? null,\n occurredAt: parsed.occurredAt ?? null,\n priority: parsed.priority ?? null,\n authorUserId: normalizedAuthor,\n ownerUserId: parsed.ownerUserId ?? null,\n dealId: parsed.dealId ?? null,\n source: parsed.source ?? null,\n appearanceIcon: parsed.appearanceIcon ?? null,\n appearanceColor: parsed.appearanceColor ?? null,\n durationMinutes: parsed.durationMinutes ?? null,\n location: parsed.location ?? null,\n allDay: parsed.allDay ?? null,\n recurrenceRule: parsed.recurrenceRule ?? null,\n recurrenceEnd: parsed.recurrenceEnd ?? null,\n participants: parsed.participants ?? null,\n reminderMinutes: parsed.reminderMinutes ?? null,\n visibility: parsed.visibility ?? null,\n linkedEntities: parsed.linkedEntities ?? null,\n guestPermissions: parsed.guestPermissions ?? null,\n })\n trx.persist(interaction)\n await trx.flush()\n\n await setInteractionCustomFields(\n createTransactionalDataEngine(ctx, trx),\n interaction.id,\n entity.organizationId,\n entity.tenantId,\n custom,\n )\n\n const projection = await recomputeNextInteraction(trx, entity.id)\n\n return {\n interaction,\n entityId: entity.id,\n nextInteractionId: projection.nextInteractionId,\n }\n })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: interaction,\n identifiers: {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n },\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitNextInteractionUpdatedEvent(ctx, { entityId, nextInteractionId }, {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n })\n\n return { interactionId: interaction.id, entityId }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return await loadInteractionSnapshot(em, result.interactionId)\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const snapshot = snapshots.after as InteractionSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.interactions.create', 'Create interaction'),\n resourceKind: 'customers.interaction',\n resourceId: result.interactionId,\n parentResourceKind: resolveParentResourceKind(snapshot?.interaction?.entityKind),\n parentResourceId: snapshot?.interaction?.entityId ?? null,\n tenantId: snapshot?.interaction.tenantId ?? null,\n organizationId: snapshot?.interaction.organizationId ?? null,\n snapshotAfter: snapshot ?? null,\n payload: {\n undo: {\n after: snapshot,\n } satisfies InteractionUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const interactionId = logEntry?.resourceId\n if (!interactionId) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const result = await runInTransaction(em, async (trx) => {\n const record = await findOneWithDecryption(trx, CustomerInteraction, { id: interactionId })\n if (!record) return null\n const entityId = typeof record.entity === 'string' ? record.entity : record.entity.id\n trx.remove(record)\n await trx.flush()\n const projection = await recomputeNextInteraction(trx, entityId)\n return {\n entityId,\n nextInteractionId: projection.nextInteractionId,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n }\n })\n if (!result) return\n await emitNextInteractionUpdatedEvent(ctx, {\n entityId: result.entityId,\n nextInteractionId: result.nextInteractionId,\n }, result.identifiers)\n },\n redo: async ({ logEntry, ctx }) => {\n const after = resolveRedoSnapshot<InteractionSnapshot>(logEntry)\n if (!after) {\n throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for interaction create' })\n }\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const { interaction, nextInteractionId } = await runInTransaction(em, async (trx) => {\n const entity = await requireTimelineParentEntity(trx, after.interaction.entityId, { tenantId: after.interaction.tenantId, organizationId: after.interaction.organizationId })\n let interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: after.interaction.id })\n if (!interaction) {\n interaction = buildInteractionGraph(trx, {\n id: after.interaction.id,\n organizationId: after.interaction.organizationId,\n tenantId: after.interaction.tenantId,\n entity,\n interactionType: after.interaction.interactionType,\n title: after.interaction.title,\n body: after.interaction.body,\n status: after.interaction.status,\n scheduledAt: after.interaction.scheduledAt,\n occurredAt: after.interaction.occurredAt,\n priority: after.interaction.priority,\n authorUserId: after.interaction.authorUserId,\n ownerUserId: after.interaction.ownerUserId,\n dealId: after.interaction.dealId,\n source: after.interaction.source,\n appearanceIcon: after.interaction.appearanceIcon,\n appearanceColor: after.interaction.appearanceColor,\n durationMinutes: after.interaction.durationMinutes,\n location: after.interaction.location,\n allDay: after.interaction.allDay,\n recurrenceRule: after.interaction.recurrenceRule,\n recurrenceEnd: after.interaction.recurrenceEnd,\n participants: after.interaction.participants,\n reminderMinutes: after.interaction.reminderMinutes,\n visibility: after.interaction.visibility,\n linkedEntities: after.interaction.linkedEntities,\n guestPermissions: after.interaction.guestPermissions,\n })\n trx.persist(interaction)\n } else {\n interaction.deletedAt = null\n interaction.entity = entity\n interaction.interactionType = after.interaction.interactionType\n interaction.title = after.interaction.title\n interaction.body = after.interaction.body\n interaction.status = after.interaction.status\n interaction.scheduledAt = after.interaction.scheduledAt\n interaction.occurredAt = after.interaction.occurredAt\n interaction.priority = after.interaction.priority\n interaction.authorUserId = after.interaction.authorUserId\n interaction.ownerUserId = after.interaction.ownerUserId\n interaction.dealId = after.interaction.dealId\n interaction.source = after.interaction.source\n interaction.appearanceIcon = after.interaction.appearanceIcon\n interaction.appearanceColor = after.interaction.appearanceColor\n interaction.durationMinutes = after.interaction.durationMinutes\n interaction.location = after.interaction.location\n interaction.allDay = after.interaction.allDay\n interaction.recurrenceRule = after.interaction.recurrenceRule\n interaction.recurrenceEnd = after.interaction.recurrenceEnd\n interaction.participants = after.interaction.participants\n interaction.reminderMinutes = after.interaction.reminderMinutes\n interaction.visibility = after.interaction.visibility\n interaction.linkedEntities = after.interaction.linkedEntities\n interaction.guestPermissions = after.interaction.guestPermissions\n }\n await trx.flush()\n\n const projection = await recomputeNextInteraction(trx, after.interaction.entityId)\n\n const restoreValues = buildCustomFieldResetMap(after.custom, undefined)\n if (Object.keys(restoreValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: createTransactionalDataEngine(ctx, trx),\n entityId: INTERACTION_ENTITY_ID,\n recordId: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n values: restoreValues,\n notify: false,\n })\n }\n\n return { interaction, nextInteractionId: projection.nextInteractionId }\n })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: interaction,\n identifiers: {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n },\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitNextInteractionUpdatedEvent(ctx, {\n entityId: after.interaction.entityId,\n nextInteractionId,\n }, {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n })\n\n return { interactionId: interaction.id, entityId: after.interaction.entityId }\n },\n}\n\n// \u2500\u2500\u2500 Update \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst updateInteractionCommand: CommandHandler<InteractionUpdateInput, { interactionId: string }> = {\n id: 'customers.interactions.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseWithCustomFields(interactionUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadInteractionSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(interactionUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {\n const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: parsed.id, deletedAt: null })\n if (!interaction) {\n enforceRecordGoneIsConflict({ resourceKind: 'customers.interaction', resourceId: parsed.id, request: ctx.request ?? null })\n throw notFound('Interaction not found')\n }\n ensureTenantScope(ctx, interaction.tenantId)\n ensureOrganizationScope(ctx, interaction.organizationId)\n\n // Concurrent-edit guard for command-driven callers (e.g. the legacy\n // /api/customers/todos route, which bypasses the makeCrudRoute lock guard):\n // when the client opted into optimistic locking, a stale edit fails with the\n // unified 409 instead of silently overwriting (#2055). Strictly additive \u2014\n // no-op when no expected-version header is present.\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: 'customers.interaction',\n resourceId: interaction.id,\n current: interaction.updatedAt,\n request: ctx.request ?? null,\n })\n\n // Email visibility is an access-controlled field: only the interaction's\n // author may change a\n // private email's visibility (mirrors the dedicated PATCH .../visibility\n // route). Enforce it here \u2014 the single persistence path \u2014 so the generic\n // update route (PUT /api/interactions) cannot bypass the gate. Evaluated\n // against the row's pre-mutation author/type. 404 (not 403) keeps the\n // existence-masking consistent with the dedicated route.\n if (\n parsed.visibility !== undefined &&\n interaction.interactionType === 'email' &&\n (parsed.visibility ?? null) !== (interaction.visibility ?? null)\n ) {\n const actorUserId = (ctx.auth as { sub?: string | null } | null)?.sub ?? null\n if (\n !canChangeEmailVisibility({\n interactionType: interaction.interactionType,\n currentVisibility: interaction.visibility,\n nextVisibility: parsed.visibility,\n authorUserId: interaction.authorUserId,\n actorUserId,\n // v1 strict owner-only: only the author may flip visibility; no admin\n // bypass (canChangeEmailVisibility ignores caller features in v1).\n userFeatures: undefined,\n })\n ) {\n throw notFound('Email not found')\n }\n }\n\n if (parsed.dealId !== undefined) {\n if (parsed.dealId) {\n await requireDealInScope(trx, parsed.dealId, interaction.tenantId, interaction.organizationId)\n }\n interaction.dealId = parsed.dealId ?? null\n }\n if (parsed.interactionType !== undefined) interaction.interactionType = parsed.interactionType\n if (parsed.title !== undefined) interaction.title = parsed.title ?? null\n if (parsed.body !== undefined) interaction.body = parsed.body ?? null\n if (parsed.status !== undefined) interaction.status = parsed.status\n if (parsed.scheduledAt !== undefined) interaction.scheduledAt = parsed.scheduledAt ?? null\n if (parsed.occurredAt !== undefined) interaction.occurredAt = parsed.occurredAt ?? null\n if (parsed.priority !== undefined) interaction.priority = parsed.priority ?? null\n if (parsed.authorUserId !== undefined) interaction.authorUserId = parsed.authorUserId ?? null\n if (parsed.ownerUserId !== undefined) interaction.ownerUserId = parsed.ownerUserId ?? null\n if (parsed.appearanceIcon !== undefined) interaction.appearanceIcon = parsed.appearanceIcon ?? null\n if (parsed.appearanceColor !== undefined) interaction.appearanceColor = parsed.appearanceColor ?? null\n if (parsed.pinned !== undefined) interaction.pinned = parsed.pinned\n if (parsed.durationMinutes !== undefined) interaction.durationMinutes = parsed.durationMinutes ?? null\n if (parsed.location !== undefined) interaction.location = parsed.location ?? null\n if (parsed.allDay !== undefined) interaction.allDay = parsed.allDay ?? null\n if (parsed.recurrenceRule !== undefined) interaction.recurrenceRule = parsed.recurrenceRule ?? null\n if (parsed.recurrenceEnd !== undefined) interaction.recurrenceEnd = parsed.recurrenceEnd ?? null\n if (parsed.participants !== undefined) interaction.participants = parsed.participants ?? null\n if (parsed.reminderMinutes !== undefined) interaction.reminderMinutes = parsed.reminderMinutes ?? null\n if (parsed.visibility !== undefined) interaction.visibility = parsed.visibility ?? null\n if (parsed.linkedEntities !== undefined) interaction.linkedEntities = parsed.linkedEntities ?? null\n if (parsed.guestPermissions !== undefined) interaction.guestPermissions = parsed.guestPermissions ?? null\n\n await trx.flush()\n\n const entityId = typeof interaction.entity === 'string' ? interaction.entity : interaction.entity.id\n await setInteractionCustomFields(\n createTransactionalDataEngine(ctx, trx),\n interaction.id,\n interaction.organizationId,\n interaction.tenantId,\n custom,\n )\n\n const projection = await recomputeNextInteraction(trx, entityId)\n\n return { interaction, entityId, nextInteractionId: projection.nextInteractionId }\n })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: interaction,\n identifiers: {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n },\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitNextInteractionUpdatedEvent(ctx, { entityId, nextInteractionId }, {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n })\n\n return { interactionId: interaction.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return await loadInteractionSnapshot(em, result.interactionId)\n },\n buildLog: async ({ snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as InteractionSnapshot | undefined\n if (!before) return null\n const afterSnapshot = snapshots.after as InteractionSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.interactions.update', 'Update interaction'),\n resourceKind: 'customers.interaction',\n resourceId: before.interaction.id,\n parentResourceKind: resolveParentResourceKind(before.interaction.entityKind),\n parentResourceId: before.interaction.entityId ?? null,\n tenantId: before.interaction.tenantId,\n organizationId: before.interaction.organizationId,\n snapshotBefore: before,\n snapshotAfter: afterSnapshot ?? null,\n payload: {\n undo: {\n before,\n after: afterSnapshot ?? null,\n } satisfies InteractionUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<InteractionUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const { interaction, nextInteractionId } = await runInTransaction(em, async (trx) => {\n let interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: before.interaction.id })\n const entity = await requireTimelineParentEntity(trx, before.interaction.entityId, { tenantId: before.interaction.tenantId, organizationId: before.interaction.organizationId })\n\n if (!interaction) {\n interaction = trx.create(CustomerInteraction, {\n id: before.interaction.id,\n organizationId: before.interaction.organizationId,\n tenantId: before.interaction.tenantId,\n entity,\n interactionType: before.interaction.interactionType,\n title: before.interaction.title,\n body: before.interaction.body,\n status: before.interaction.status,\n scheduledAt: before.interaction.scheduledAt,\n occurredAt: before.interaction.occurredAt,\n priority: before.interaction.priority,\n authorUserId: before.interaction.authorUserId,\n ownerUserId: before.interaction.ownerUserId,\n dealId: before.interaction.dealId,\n source: before.interaction.source,\n appearanceIcon: before.interaction.appearanceIcon,\n appearanceColor: before.interaction.appearanceColor,\n durationMinutes: before.interaction.durationMinutes,\n location: before.interaction.location,\n allDay: before.interaction.allDay,\n recurrenceRule: before.interaction.recurrenceRule,\n recurrenceEnd: before.interaction.recurrenceEnd,\n participants: before.interaction.participants,\n reminderMinutes: before.interaction.reminderMinutes,\n visibility: before.interaction.visibility,\n linkedEntities: before.interaction.linkedEntities,\n guestPermissions: before.interaction.guestPermissions,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n trx.persist(interaction)\n } else {\n interaction.entity = entity\n interaction.interactionType = before.interaction.interactionType\n interaction.title = before.interaction.title\n interaction.body = before.interaction.body\n interaction.status = before.interaction.status\n interaction.scheduledAt = before.interaction.scheduledAt\n interaction.occurredAt = before.interaction.occurredAt\n interaction.priority = before.interaction.priority\n interaction.authorUserId = before.interaction.authorUserId\n interaction.ownerUserId = before.interaction.ownerUserId\n interaction.dealId = before.interaction.dealId\n interaction.source = before.interaction.source\n interaction.appearanceIcon = before.interaction.appearanceIcon\n interaction.appearanceColor = before.interaction.appearanceColor\n interaction.durationMinutes = before.interaction.durationMinutes\n interaction.location = before.interaction.location\n interaction.allDay = before.interaction.allDay\n interaction.recurrenceRule = before.interaction.recurrenceRule\n interaction.recurrenceEnd = before.interaction.recurrenceEnd\n interaction.participants = before.interaction.participants\n interaction.reminderMinutes = before.interaction.reminderMinutes\n interaction.visibility = before.interaction.visibility\n interaction.linkedEntities = before.interaction.linkedEntities\n interaction.guestPermissions = before.interaction.guestPermissions\n }\n\n await trx.flush()\n const projection = await recomputeNextInteraction(trx, before.interaction.entityId)\n\n const resetValues = buildCustomFieldResetMap(before.custom, payload?.after?.custom)\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: createTransactionalDataEngine(ctx, trx),\n entityId: INTERACTION_ENTITY_ID,\n recordId: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n values: resetValues,\n notify: false,\n })\n }\n\n return {\n interaction,\n nextInteractionId: projection.nextInteractionId,\n }\n })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: interaction,\n identifiers: {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n },\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitNextInteractionUpdatedEvent(ctx, {\n entityId: before.interaction.entityId,\n nextInteractionId,\n }, {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n })\n },\n}\n\n// \u2500\u2500\u2500 Complete \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst completeInteractionCommand: CommandHandler<InteractionCompleteInput, { interactionId: string }> = {\n id: 'customers.interactions.complete',\n async prepare(rawInput, ctx) {\n const parsed = interactionCompleteSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadInteractionSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const parsed = interactionCompleteSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {\n const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: parsed.id, deletedAt: null })\n if (!interaction) {\n enforceRecordGoneIsConflict({ resourceKind: 'customers.interaction', resourceId: parsed.id, request: ctx.request ?? null })\n throw notFound('Interaction not found')\n }\n ensureTenantScope(ctx, interaction.tenantId)\n ensureOrganizationScope(ctx, interaction.organizationId)\n\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: 'customers.interaction',\n resourceId: interaction.id,\n current: interaction.updatedAt,\n request: ctx.request ?? null,\n })\n\n interaction.status = INTERACTION_STATUS_COMPLETED\n interaction.occurredAt = parsed.occurredAt ?? new Date()\n await trx.flush()\n\n const entityId = typeof interaction.entity === 'string' ? interaction.entity : interaction.entity.id\n const projection = await recomputeNextInteraction(trx, entityId)\n return { interaction, entityId, nextInteractionId: projection.nextInteractionId }\n })\n\n const identifiers = {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n }\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: interaction,\n identifiers,\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitLifecycleEvent(ctx, 'customers.interaction.completed', {\n ...identifiers,\n entityId,\n interactionType: interaction.interactionType,\n status: interaction.status,\n source: interaction.source ?? null,\n occurredAt: interaction.occurredAt?.toISOString() ?? null,\n ...(ctx.syncOrigin ? { syncOrigin: ctx.syncOrigin } : {}),\n })\n await emitNextInteractionUpdatedEvent(ctx, { entityId, nextInteractionId }, identifiers)\n\n return { interactionId: interaction.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return await loadInteractionSnapshot(em, result.interactionId)\n },\n buildLog: async ({ snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as InteractionSnapshot | undefined\n if (!before) return null\n const afterSnapshot = snapshots.after as InteractionSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.interactions.complete', 'Complete interaction'),\n resourceKind: 'customers.interaction',\n resourceId: before.interaction.id,\n parentResourceKind: resolveParentResourceKind(before.interaction.entityKind),\n parentResourceId: before.interaction.entityId ?? null,\n tenantId: before.interaction.tenantId,\n organizationId: before.interaction.organizationId,\n snapshotBefore: before,\n snapshotAfter: afterSnapshot ?? null,\n payload: {\n undo: {\n before,\n after: afterSnapshot ?? null,\n } satisfies InteractionUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<InteractionUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const result = await runInTransaction(em, async (trx) => {\n const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: before.interaction.id })\n if (!interaction) return null\n\n interaction.status = before.interaction.status\n interaction.occurredAt = before.interaction.occurredAt\n await trx.flush()\n\n const projection = await recomputeNextInteraction(trx, before.interaction.entityId)\n return {\n interaction,\n nextInteractionId: projection.nextInteractionId,\n }\n })\n if (!result) return\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: result.interaction,\n identifiers: {\n id: result.interaction.id,\n organizationId: result.interaction.organizationId,\n tenantId: result.interaction.tenantId,\n },\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitInteractionRevertedEvent(ctx, before.interaction)\n await emitNextInteractionUpdatedEvent(ctx, {\n entityId: before.interaction.entityId,\n nextInteractionId: result.nextInteractionId,\n }, {\n id: result.interaction.id,\n organizationId: result.interaction.organizationId,\n tenantId: result.interaction.tenantId,\n })\n },\n}\n\n// \u2500\u2500\u2500 Cancel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst cancelInteractionCommand: CommandHandler<InteractionCancelInput, { interactionId: string }> = {\n id: 'customers.interactions.cancel',\n async prepare(rawInput, ctx) {\n const parsed = interactionCancelSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadInteractionSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const parsed = interactionCancelSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {\n const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: parsed.id, deletedAt: null })\n if (!interaction) {\n enforceRecordGoneIsConflict({ resourceKind: 'customers.interaction', resourceId: parsed.id, request: ctx.request ?? null })\n throw notFound('Interaction not found')\n }\n ensureTenantScope(ctx, interaction.tenantId)\n ensureOrganizationScope(ctx, interaction.organizationId)\n\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: 'customers.interaction',\n resourceId: interaction.id,\n current: interaction.updatedAt,\n request: ctx.request ?? null,\n })\n\n interaction.status = INTERACTION_STATUS_CANCELED\n await trx.flush()\n\n const entityId = typeof interaction.entity === 'string' ? interaction.entity : interaction.entity.id\n const projection = await recomputeNextInteraction(trx, entityId)\n return { interaction, entityId, nextInteractionId: projection.nextInteractionId }\n })\n\n const identifiers = {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n }\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: interaction,\n identifiers,\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitLifecycleEvent(ctx, 'customers.interaction.canceled', {\n ...identifiers,\n entityId,\n interactionType: interaction.interactionType,\n status: interaction.status,\n source: interaction.source ?? null,\n ...(ctx.syncOrigin ? { syncOrigin: ctx.syncOrigin } : {}),\n })\n await emitNextInteractionUpdatedEvent(ctx, { entityId, nextInteractionId }, identifiers)\n\n return { interactionId: interaction.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return await loadInteractionSnapshot(em, result.interactionId)\n },\n buildLog: async ({ snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as InteractionSnapshot | undefined\n if (!before) return null\n const afterSnapshot = snapshots.after as InteractionSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.interactions.cancel', 'Cancel interaction'),\n resourceKind: 'customers.interaction',\n resourceId: before.interaction.id,\n parentResourceKind: resolveParentResourceKind(before.interaction.entityKind),\n parentResourceId: before.interaction.entityId ?? null,\n tenantId: before.interaction.tenantId,\n organizationId: before.interaction.organizationId,\n snapshotBefore: before,\n snapshotAfter: afterSnapshot ?? null,\n payload: {\n undo: {\n before,\n after: afterSnapshot ?? null,\n } satisfies InteractionUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<InteractionUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const result = await runInTransaction(em, async (trx) => {\n const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: before.interaction.id })\n if (!interaction) return null\n\n interaction.status = before.interaction.status\n await trx.flush()\n\n const projection = await recomputeNextInteraction(trx, before.interaction.entityId)\n return {\n interaction,\n nextInteractionId: projection.nextInteractionId,\n }\n })\n if (!result) return\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: result.interaction,\n identifiers: {\n id: result.interaction.id,\n organizationId: result.interaction.organizationId,\n tenantId: result.interaction.tenantId,\n },\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitInteractionRevertedEvent(ctx, before.interaction)\n await emitNextInteractionUpdatedEvent(ctx, {\n entityId: before.interaction.entityId,\n nextInteractionId: result.nextInteractionId,\n }, {\n id: result.interaction.id,\n organizationId: result.interaction.organizationId,\n tenantId: result.interaction.tenantId,\n })\n },\n}\n\n// \u2500\u2500\u2500 Delete \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst deleteInteractionCommand: CommandHandler<{ body?: Record<string, unknown>; query?: Record<string, unknown> }, { interactionId: string }> =\n {\n id: 'customers.interactions.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Interaction id required')\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadInteractionSnapshot(em, id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Interaction id required')\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {\n const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id, deletedAt: null })\n if (!interaction) {\n enforceRecordGoneIsConflict({ resourceKind: 'customers.interaction', resourceId: id, request: ctx.request ?? null })\n throw notFound('Interaction not found')\n }\n ensureTenantScope(ctx, interaction.tenantId)\n ensureOrganizationScope(ctx, interaction.organizationId)\n\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: 'customers.interaction',\n resourceId: interaction.id,\n current: interaction.updatedAt,\n request: ctx.request ?? null,\n })\n\n const entityId = typeof interaction.entity === 'string' ? interaction.entity : interaction.entity.id\n interaction.deletedAt = new Date()\n await trx.flush()\n\n const projection = await recomputeNextInteraction(trx, entityId)\n return { interaction, entityId, nextInteractionId: projection.nextInteractionId }\n })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: interaction,\n identifiers: {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n },\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitNextInteractionUpdatedEvent(ctx, { entityId, nextInteractionId }, {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n })\n return { interactionId: interaction.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as InteractionSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('customers.audit.interactions.delete', 'Delete interaction'),\n resourceKind: 'customers.interaction',\n resourceId: before.interaction.id,\n parentResourceKind: resolveParentResourceKind(before.interaction.entityKind),\n parentResourceId: before.interaction.entityId ?? null,\n tenantId: before.interaction.tenantId,\n organizationId: before.interaction.organizationId,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n } satisfies InteractionUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<InteractionUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const { interaction, nextInteractionId } = await runInTransaction(em, async (trx) => {\n const entity = await requireTimelineParentEntity(trx, before.interaction.entityId, { tenantId: before.interaction.tenantId, organizationId: before.interaction.organizationId })\n let interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: before.interaction.id })\n if (!interaction) {\n interaction = trx.create(CustomerInteraction, {\n id: before.interaction.id,\n organizationId: before.interaction.organizationId,\n tenantId: before.interaction.tenantId,\n entity,\n interactionType: before.interaction.interactionType,\n title: before.interaction.title,\n body: before.interaction.body,\n status: before.interaction.status,\n scheduledAt: before.interaction.scheduledAt,\n occurredAt: before.interaction.occurredAt,\n priority: before.interaction.priority,\n authorUserId: before.interaction.authorUserId,\n ownerUserId: before.interaction.ownerUserId,\n dealId: before.interaction.dealId,\n source: before.interaction.source,\n appearanceIcon: before.interaction.appearanceIcon,\n appearanceColor: before.interaction.appearanceColor,\n durationMinutes: before.interaction.durationMinutes,\n location: before.interaction.location,\n allDay: before.interaction.allDay,\n recurrenceRule: before.interaction.recurrenceRule,\n recurrenceEnd: before.interaction.recurrenceEnd,\n participants: before.interaction.participants,\n reminderMinutes: before.interaction.reminderMinutes,\n visibility: before.interaction.visibility,\n linkedEntities: before.interaction.linkedEntities,\n guestPermissions: before.interaction.guestPermissions,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n trx.persist(interaction)\n } else {\n interaction.deletedAt = null\n interaction.entity = entity\n interaction.interactionType = before.interaction.interactionType\n interaction.title = before.interaction.title\n interaction.body = before.interaction.body\n interaction.status = before.interaction.status\n interaction.scheduledAt = before.interaction.scheduledAt\n interaction.occurredAt = before.interaction.occurredAt\n interaction.priority = before.interaction.priority\n interaction.authorUserId = before.interaction.authorUserId\n interaction.ownerUserId = before.interaction.ownerUserId\n interaction.dealId = before.interaction.dealId\n interaction.source = before.interaction.source\n interaction.appearanceIcon = before.interaction.appearanceIcon\n interaction.appearanceColor = before.interaction.appearanceColor\n interaction.durationMinutes = before.interaction.durationMinutes\n interaction.location = before.interaction.location\n interaction.allDay = before.interaction.allDay\n interaction.recurrenceRule = before.interaction.recurrenceRule\n interaction.recurrenceEnd = before.interaction.recurrenceEnd\n interaction.participants = before.interaction.participants\n interaction.reminderMinutes = before.interaction.reminderMinutes\n interaction.visibility = before.interaction.visibility\n interaction.linkedEntities = before.interaction.linkedEntities\n interaction.guestPermissions = before.interaction.guestPermissions\n }\n await trx.flush()\n\n const projection = await recomputeNextInteraction(trx, before.interaction.entityId)\n\n const resetValues = buildCustomFieldResetMap(before.custom, undefined)\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: createTransactionalDataEngine(ctx, trx),\n entityId: INTERACTION_ENTITY_ID,\n recordId: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n values: resetValues,\n notify: false,\n })\n }\n\n return {\n interaction,\n nextInteractionId: projection.nextInteractionId,\n }\n })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'created',\n entity: interaction,\n identifiers: {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n },\n syncOrigin: ctx.syncOrigin,\n indexer: interactionCrudIndexer,\n events: interactionCrudEvents,\n })\n await emitNextInteractionUpdatedEvent(ctx, {\n entityId: before.interaction.entityId,\n nextInteractionId,\n }, {\n id: interaction.id,\n organizationId: interaction.organizationId,\n tenantId: interaction.tenantId,\n })\n },\n }\n\n// \u2500\u2500\u2500 Recompute Next (internal repair) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst recomputeNextSchema = z.object({ entityId: z.string().min(1) })\n\nconst recomputeNextCommand: CommandHandler<{ entityId: string }, { entityId: string }> = {\n id: 'customers.interaction.recompute_next',\n async execute(rawInput, ctx) {\n const parsed = recomputeNextSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const projection = await recomputeNextInteraction(em, parsed.entityId)\n const entity = await requireTimelineParentEntity(em, parsed.entityId, {\n tenantId: ctx.auth?.tenantId ?? '',\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? '',\n })\n await emitNextInteractionUpdatedEvent(ctx, {\n entityId: parsed.entityId,\n nextInteractionId: projection.nextInteractionId,\n }, {\n id: parsed.entityId,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n })\n return { entityId: parsed.entityId }\n },\n}\n\nregisterCommand(createInteractionCommand)\nregisterCommand(updateInteractionCommand)\nregisterCommand(completeInteractionCommand)\nregisterCommand(cancelInteractionCommand)\nregisterCommand(deleteInteractionCommand)\nregisterCommand(recomputeNextCommand)\n"],
5
5
  "mappings": "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",
6
6
  "names": ["interaction", "entityId"]
7
7
  }
@@ -45,7 +45,7 @@ function deriveTypeLabel(interactionType) {
45
45
  return normalized.charAt(0).toUpperCase() + normalized.slice(1);
46
46
  }
47
47
  function participantLabel(participant) {
48
- return participant.name ?? participant.email ?? participant.userId;
48
+ return participant.name ?? participant.email ?? participant.userId ?? "";
49
49
  }
50
50
  function buildDayGroups(anchor, horizonDays, items) {
51
51
  const sorted = [...items].sort((first, second) => first.start.getTime() - second.start.getTime());
@@ -141,7 +141,7 @@ function AgendaRow({
141
141
  ] }),
142
142
  /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
143
143
  item.participants.length > 0 ? /* @__PURE__ */ jsxs("span", { className: "hidden items-center gap-0.5 sm:flex", children: [
144
- item.participants.slice(0, MAX_AVATARS_PER_ROW).map((participant) => /* @__PURE__ */ jsx(Avatar, { size: "xs", label: participantLabel(participant) }, participant.userId)),
144
+ item.participants.slice(0, MAX_AVATARS_PER_ROW).map((participant, index) => /* @__PURE__ */ jsx(Avatar, { size: "xs", label: participantLabel(participant) }, participant.userId ?? participant.email ?? index)),
145
145
  item.participants.length > MAX_AVATARS_PER_ROW ? /* @__PURE__ */ jsxs("span", { className: "ps-0.5 text-xs font-medium text-muted-foreground", children: [
146
146
  "+",
147
147
  item.participants.length - MAX_AVATARS_PER_ROW
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/calendar/AgendaList.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { addDays } from 'date-fns/addDays'\nimport { format } from 'date-fns/format'\nimport { isToday } from 'date-fns/isToday'\nimport { isTomorrow } from 'date-fns/isTomorrow'\nimport { startOfDay } from 'date-fns/startOfDay'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { eventDisplayTitle, pluralCategory } from '../../lib/calendar/labels'\nimport { formatTimeLabel, formatTimeRangeLabel } from '../../lib/calendar/format'\nimport type { AgendaListProps, CalendarCategory, CalendarItem } from './types'\n\nconst MAX_AVATARS_PER_ROW = 2\n\nconst CATEGORY_BADGE_CLASS: Record<CalendarCategory, string> = {\n meeting: 'bg-status-info-bg text-status-info-text',\n event: 'bg-status-warning-bg text-status-warning-text',\n task: 'bg-primary text-primary-foreground',\n other: 'bg-status-neutral-bg text-status-neutral-text',\n}\n\nconst CATEGORY_LABEL: Record<CalendarCategory, { key: string; fallback: string }> = {\n meeting: { key: 'customers.calendar.category.meeting', fallback: 'Meeting' },\n event: { key: 'customers.calendar.category.event', fallback: 'Event' },\n task: { key: 'customers.calendar.category.task', fallback: 'Task' },\n other: { key: 'customers.calendar.category.other', fallback: 'Other' },\n}\n\ntype AgendaDayGroup = { date: Date; items: CalendarItem[] }\n\nfunction dayKeyOf(date: Date): string {\n return format(date, 'yyyy-MM-dd')\n}\n\nfunction formatUrlHost(location: string): string {\n try {\n const url = new URL(location.startsWith('http') ? location : `https://${location}`)\n return url.hostname\n } catch {\n return location\n }\n}\n\nfunction groupLabelOf(locale: string, date: Date): string {\n return date.toLocaleDateString(locale, { weekday: 'long', month: 'short', day: 'numeric' })\n}\n\nfunction deriveTypeLabel(interactionType: string): string {\n const normalized = interactionType.replace(/[-_]+/g, ' ').trim()\n if (!normalized) return interactionType\n return normalized.charAt(0).toUpperCase() + normalized.slice(1)\n}\n\nfunction participantLabel(participant: CalendarItem['participants'][number]): string {\n return participant.name ?? participant.email ?? participant.userId\n}\n\nfunction buildDayGroups(anchor: Date, horizonDays: number, items: CalendarItem[]): AgendaDayGroup[] {\n const sorted = [...items].sort((first, second) => first.start.getTime() - second.start.getTime())\n const byDay = new Map<string, CalendarItem[]>()\n for (const item of sorted) {\n const key = dayKeyOf(item.start)\n const bucket = byDay.get(key)\n if (bucket) bucket.push(item)\n else byDay.set(key, [item])\n }\n const firstDay = startOfDay(anchor)\n const groups: AgendaDayGroup[] = []\n for (let offset = 0; offset <= horizonDays; offset += 1) {\n const day = addDays(firstDay, offset)\n const dayItems = byDay.get(dayKeyOf(day))\n if (dayItems && dayItems.length > 0) groups.push({ date: day, items: dayItems })\n }\n return groups\n}\n\nfunction AgendaDayHeader({ date, count }: { date: Date; count: number }) {\n const t = useT()\n const locale = useLocale()\n const todayMarker = isToday(date)\n const tomorrowMarker = !todayMarker && isTomorrow(date)\n const countKey = `customers.calendar.agenda.eventsCount.${pluralCategory(locale, count)}`\n const resolvedCount = t(countKey, { count })\n const countLabel =\n resolvedCount === countKey\n ? t('customers.calendar.agenda.eventsCount.other', '{count} events', { count })\n : resolvedCount\n return (\n <div className=\"flex w-full items-center gap-2 bg-muted/50 px-3 py-2.5 sm:px-5\">\n <span className=\"text-sm font-semibold text-foreground\">{groupLabelOf(locale, date)}</span>\n {todayMarker || tomorrowMarker ? (\n <span className={cn('text-xs font-medium', todayMarker ? 'text-foreground' : 'text-muted-foreground')}>\n {`\u00B7 ${todayMarker ? t('customers.calendar.agenda.today', 'Today') : t('customers.calendar.agenda.tomorrow', 'Tomorrow')}`}\n </span>\n ) : null}\n <span aria-hidden=\"true\" className=\"w-2.5 shrink-0\" />\n <span className=\"text-xs font-medium text-muted-foreground\">{countLabel}</span>\n </div>\n )\n}\n\nfunction AgendaRow({\n item,\n typeLabels,\n onItemClick,\n}: {\n item: CalendarItem\n typeLabels?: Record<string, string>\n onItemClick: (item: CalendarItem) => void\n}) {\n const t = useT()\n const locale = useLocale()\n const canceled = item.status === 'canceled'\n const done = item.status === 'done'\n const title = eventDisplayTitle(item.title, t('customers.calendar.grid.untitled', 'Untitled'))\n const typeLabel = typeLabels?.[item.interactionType] ?? deriveTypeLabel(item.interactionType)\n const platformLabels: Record<string, string> = {\n zoom: t('customers.calendar.platformFull.zoom', 'Zoom'),\n meet: t('customers.calendar.platformFull.meet', 'Google Meet'),\n slack: t('customers.calendar.platformFull.slack', 'Slack'),\n teams: t('customers.calendar.platformFull.teams', 'Microsoft Teams'),\n }\n const locationLabel = item.platform\n ? platformLabels[item.platform]\n : item.locationKind === 'url' && item.location\n ? formatUrlHost(item.location)\n : item.location\n const startLabel = item.allDay ? t('customers.calendar.grid.allDay', 'All day') : formatTimeLabel(locale, item.start)\n const endLabel = item.allDay ? null : formatTimeLabel(locale, item.end)\n const ariaTime = item.allDay ? startLabel : formatTimeRangeLabel(locale, item.start, item.end)\n return (\n <Button\n type=\"button\"\n variant=\"ghost\"\n aria-label={`${title} \u00B7 ${ariaTime}`}\n onClick={() => onItemClick(item)}\n className={cn(\n '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',\n canceled && 'opacity-60',\n )}\n >\n <div className=\"flex w-16 shrink-0 flex-col gap-0.5 sm:w-[86px]\">\n <span className={cn('truncate text-sm font-semibold leading-4', done || canceled ? 'text-muted-foreground' : 'text-foreground')}>\n {startLabel}\n </span>\n {endLabel ? <span className=\"truncate text-xs text-muted-foreground\">{endLabel}</span> : null}\n </div>\n <span\n aria-hidden=\"true\"\n className={cn('h-9 w-1 shrink-0 rounded-full', !item.color && 'bg-muted-foreground/40')}\n style={item.color ? { backgroundColor: item.color } : undefined}\n />\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <span\n className={cn(\n 'truncate text-sm font-semibold',\n done || canceled ? 'text-muted-foreground' : 'text-foreground',\n canceled && 'line-through',\n )}\n >\n {title}\n </span>\n <span className=\"truncate text-xs text-muted-foreground\">\n {locationLabel ? `${typeLabel} \u00B7 ${locationLabel}` : typeLabel}\n </span>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {item.participants.length > 0 ? (\n <span className=\"hidden items-center gap-0.5 sm:flex\">\n {item.participants.slice(0, MAX_AVATARS_PER_ROW).map((participant) => (\n <Avatar key={participant.userId} size=\"xs\" label={participantLabel(participant)} />\n ))}\n {item.participants.length > MAX_AVATARS_PER_ROW ? (\n <span className=\"ps-0.5 text-xs font-medium text-muted-foreground\">\n +{item.participants.length - MAX_AVATARS_PER_ROW}\n </span>\n ) : null}\n </span>\n ) : null}\n <span\n className={cn(\n 'inline-flex shrink-0 items-center rounded-full px-2 py-0.5 text-overline font-medium uppercase tracking-wide',\n CATEGORY_BADGE_CLASS[item.category],\n )}\n >\n {t(CATEGORY_LABEL[item.category].key, CATEGORY_LABEL[item.category].fallback)}\n </span>\n </div>\n </Button>\n )\n}\n\nexport function AgendaList({ anchor, horizonDays, items, typeLabels, onItemClick }: AgendaListProps) {\n const t = useT()\n const groups = React.useMemo(() => buildDayGroups(anchor, horizonDays, items), [anchor, horizonDays, items])\n return (\n <div className=\"flex w-full flex-col divide-y divide-border overflow-hidden border border-border bg-background\">\n {groups.map((group) => (\n <React.Fragment key={dayKeyOf(group.date)}>\n <AgendaDayHeader date={group.date} count={group.items.length} />\n {group.items.map((item) => (\n <AgendaRow key={item.id} item={item} typeLabels={typeLabels} onItemClick={onItemClick} />\n ))}\n </React.Fragment>\n ))}\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { addDays } from 'date-fns/addDays'\nimport { format } from 'date-fns/format'\nimport { isToday } from 'date-fns/isToday'\nimport { isTomorrow } from 'date-fns/isTomorrow'\nimport { startOfDay } from 'date-fns/startOfDay'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { eventDisplayTitle, pluralCategory } from '../../lib/calendar/labels'\nimport { formatTimeLabel, formatTimeRangeLabel } from '../../lib/calendar/format'\nimport type { AgendaListProps, CalendarCategory, CalendarItem } from './types'\n\nconst MAX_AVATARS_PER_ROW = 2\n\nconst CATEGORY_BADGE_CLASS: Record<CalendarCategory, string> = {\n meeting: 'bg-status-info-bg text-status-info-text',\n event: 'bg-status-warning-bg text-status-warning-text',\n task: 'bg-primary text-primary-foreground',\n other: 'bg-status-neutral-bg text-status-neutral-text',\n}\n\nconst CATEGORY_LABEL: Record<CalendarCategory, { key: string; fallback: string }> = {\n meeting: { key: 'customers.calendar.category.meeting', fallback: 'Meeting' },\n event: { key: 'customers.calendar.category.event', fallback: 'Event' },\n task: { key: 'customers.calendar.category.task', fallback: 'Task' },\n other: { key: 'customers.calendar.category.other', fallback: 'Other' },\n}\n\ntype AgendaDayGroup = { date: Date; items: CalendarItem[] }\n\nfunction dayKeyOf(date: Date): string {\n return format(date, 'yyyy-MM-dd')\n}\n\nfunction formatUrlHost(location: string): string {\n try {\n const url = new URL(location.startsWith('http') ? location : `https://${location}`)\n return url.hostname\n } catch {\n return location\n }\n}\n\nfunction groupLabelOf(locale: string, date: Date): string {\n return date.toLocaleDateString(locale, { weekday: 'long', month: 'short', day: 'numeric' })\n}\n\nfunction deriveTypeLabel(interactionType: string): string {\n const normalized = interactionType.replace(/[-_]+/g, ' ').trim()\n if (!normalized) return interactionType\n return normalized.charAt(0).toUpperCase() + normalized.slice(1)\n}\n\nfunction participantLabel(participant: CalendarItem['participants'][number]): string {\n return participant.name ?? participant.email ?? participant.userId ?? ''\n}\n\nfunction buildDayGroups(anchor: Date, horizonDays: number, items: CalendarItem[]): AgendaDayGroup[] {\n const sorted = [...items].sort((first, second) => first.start.getTime() - second.start.getTime())\n const byDay = new Map<string, CalendarItem[]>()\n for (const item of sorted) {\n const key = dayKeyOf(item.start)\n const bucket = byDay.get(key)\n if (bucket) bucket.push(item)\n else byDay.set(key, [item])\n }\n const firstDay = startOfDay(anchor)\n const groups: AgendaDayGroup[] = []\n for (let offset = 0; offset <= horizonDays; offset += 1) {\n const day = addDays(firstDay, offset)\n const dayItems = byDay.get(dayKeyOf(day))\n if (dayItems && dayItems.length > 0) groups.push({ date: day, items: dayItems })\n }\n return groups\n}\n\nfunction AgendaDayHeader({ date, count }: { date: Date; count: number }) {\n const t = useT()\n const locale = useLocale()\n const todayMarker = isToday(date)\n const tomorrowMarker = !todayMarker && isTomorrow(date)\n const countKey = `customers.calendar.agenda.eventsCount.${pluralCategory(locale, count)}`\n const resolvedCount = t(countKey, { count })\n const countLabel =\n resolvedCount === countKey\n ? t('customers.calendar.agenda.eventsCount.other', '{count} events', { count })\n : resolvedCount\n return (\n <div className=\"flex w-full items-center gap-2 bg-muted/50 px-3 py-2.5 sm:px-5\">\n <span className=\"text-sm font-semibold text-foreground\">{groupLabelOf(locale, date)}</span>\n {todayMarker || tomorrowMarker ? (\n <span className={cn('text-xs font-medium', todayMarker ? 'text-foreground' : 'text-muted-foreground')}>\n {`\u00B7 ${todayMarker ? t('customers.calendar.agenda.today', 'Today') : t('customers.calendar.agenda.tomorrow', 'Tomorrow')}`}\n </span>\n ) : null}\n <span aria-hidden=\"true\" className=\"w-2.5 shrink-0\" />\n <span className=\"text-xs font-medium text-muted-foreground\">{countLabel}</span>\n </div>\n )\n}\n\nfunction AgendaRow({\n item,\n typeLabels,\n onItemClick,\n}: {\n item: CalendarItem\n typeLabels?: Record<string, string>\n onItemClick: (item: CalendarItem) => void\n}) {\n const t = useT()\n const locale = useLocale()\n const canceled = item.status === 'canceled'\n const done = item.status === 'done'\n const title = eventDisplayTitle(item.title, t('customers.calendar.grid.untitled', 'Untitled'))\n const typeLabel = typeLabels?.[item.interactionType] ?? deriveTypeLabel(item.interactionType)\n const platformLabels: Record<string, string> = {\n zoom: t('customers.calendar.platformFull.zoom', 'Zoom'),\n meet: t('customers.calendar.platformFull.meet', 'Google Meet'),\n slack: t('customers.calendar.platformFull.slack', 'Slack'),\n teams: t('customers.calendar.platformFull.teams', 'Microsoft Teams'),\n }\n const locationLabel = item.platform\n ? platformLabels[item.platform]\n : item.locationKind === 'url' && item.location\n ? formatUrlHost(item.location)\n : item.location\n const startLabel = item.allDay ? t('customers.calendar.grid.allDay', 'All day') : formatTimeLabel(locale, item.start)\n const endLabel = item.allDay ? null : formatTimeLabel(locale, item.end)\n const ariaTime = item.allDay ? startLabel : formatTimeRangeLabel(locale, item.start, item.end)\n return (\n <Button\n type=\"button\"\n variant=\"ghost\"\n aria-label={`${title} \u00B7 ${ariaTime}`}\n onClick={() => onItemClick(item)}\n className={cn(\n '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',\n canceled && 'opacity-60',\n )}\n >\n <div className=\"flex w-16 shrink-0 flex-col gap-0.5 sm:w-[86px]\">\n <span className={cn('truncate text-sm font-semibold leading-4', done || canceled ? 'text-muted-foreground' : 'text-foreground')}>\n {startLabel}\n </span>\n {endLabel ? <span className=\"truncate text-xs text-muted-foreground\">{endLabel}</span> : null}\n </div>\n <span\n aria-hidden=\"true\"\n className={cn('h-9 w-1 shrink-0 rounded-full', !item.color && 'bg-muted-foreground/40')}\n style={item.color ? { backgroundColor: item.color } : undefined}\n />\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <span\n className={cn(\n 'truncate text-sm font-semibold',\n done || canceled ? 'text-muted-foreground' : 'text-foreground',\n canceled && 'line-through',\n )}\n >\n {title}\n </span>\n <span className=\"truncate text-xs text-muted-foreground\">\n {locationLabel ? `${typeLabel} \u00B7 ${locationLabel}` : typeLabel}\n </span>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {item.participants.length > 0 ? (\n <span className=\"hidden items-center gap-0.5 sm:flex\">\n {item.participants.slice(0, MAX_AVATARS_PER_ROW).map((participant, index) => (\n <Avatar key={participant.userId ?? participant.email ?? index} size=\"xs\" label={participantLabel(participant)} />\n ))}\n {item.participants.length > MAX_AVATARS_PER_ROW ? (\n <span className=\"ps-0.5 text-xs font-medium text-muted-foreground\">\n +{item.participants.length - MAX_AVATARS_PER_ROW}\n </span>\n ) : null}\n </span>\n ) : null}\n <span\n className={cn(\n 'inline-flex shrink-0 items-center rounded-full px-2 py-0.5 text-overline font-medium uppercase tracking-wide',\n CATEGORY_BADGE_CLASS[item.category],\n )}\n >\n {t(CATEGORY_LABEL[item.category].key, CATEGORY_LABEL[item.category].fallback)}\n </span>\n </div>\n </Button>\n )\n}\n\nexport function AgendaList({ anchor, horizonDays, items, typeLabels, onItemClick }: AgendaListProps) {\n const t = useT()\n const groups = React.useMemo(() => buildDayGroups(anchor, horizonDays, items), [anchor, horizonDays, items])\n return (\n <div className=\"flex w-full flex-col divide-y divide-border overflow-hidden border border-border bg-background\">\n {groups.map((group) => (\n <React.Fragment key={dayKeyOf(group.date)}>\n <AgendaDayHeader date={group.date} count={group.items.length} />\n {group.items.map((item) => (\n <AgendaRow key={item.id} item={item} typeLabels={typeLabels} onItemClick={onItemClick} />\n ))}\n </React.Fragment>\n ))}\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -113,7 +113,7 @@ function EditorBody({
113
113
  const ids = [];
114
114
  if (form.relatedTo && form.relatedTo.kind !== "company") ids.push(form.relatedTo.id);
115
115
  for (const participant of form.participants) {
116
- if (participant.isCustomer) ids.push(participant.userId);
116
+ if (participant.isCustomer && participant.userId) ids.push(participant.userId);
117
117
  }
118
118
  return Array.from(new Set(ids));
119
119
  }, [form.relatedTo, form.participants]);