@open-mercato/core 0.5.1-develop.2996.ce62fd491c → 0.5.1-develop.3032.01699048cb

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 (69) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/customers/api/companies/[id]/route.js +30 -20
  3. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  4. package/dist/modules/customers/api/companies/route.js +12 -7
  5. package/dist/modules/customers/api/companies/route.js.map +2 -2
  6. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +12 -7
  7. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
  8. package/dist/modules/customers/api/people/route.js +12 -7
  9. package/dist/modules/customers/api/people/route.js.map +2 -2
  10. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +21 -0
  11. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  12. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +27 -30
  13. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  14. package/dist/modules/customers/components/detail/ActivitiesAddNewMenu.js +56 -0
  15. package/dist/modules/customers/components/detail/ActivitiesAddNewMenu.js.map +7 -0
  16. package/dist/modules/customers/components/detail/ActivitiesCard.js +175 -0
  17. package/dist/modules/customers/components/detail/ActivitiesCard.js.map +7 -0
  18. package/dist/modules/customers/components/detail/ActivitiesDayStrip.js +324 -0
  19. package/dist/modules/customers/components/detail/ActivitiesDayStrip.js.map +7 -0
  20. package/dist/modules/customers/components/detail/ActivitiesSection.js +62 -13
  21. package/dist/modules/customers/components/detail/ActivitiesSection.js.map +2 -2
  22. package/dist/modules/customers/components/detail/ActivityLogTab.js +14 -23
  23. package/dist/modules/customers/components/detail/ActivityLogTab.js.map +2 -2
  24. package/dist/modules/customers/components/detail/ActivityTimeline.js +13 -13
  25. package/dist/modules/customers/components/detail/ActivityTimeline.js.map +2 -2
  26. package/dist/modules/customers/components/detail/ActivityTimelineFilters.js +35 -22
  27. package/dist/modules/customers/components/detail/ActivityTimelineFilters.js.map +2 -2
  28. package/dist/modules/customers/components/detail/AiActionChips.js +15 -22
  29. package/dist/modules/customers/components/detail/AiActionChips.js.map +2 -2
  30. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +196 -28
  31. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  32. package/dist/modules/customers/components/detail/schedule/DateTimeFields.js +2 -2
  33. package/dist/modules/customers/components/detail/schedule/DateTimeFields.js.map +2 -2
  34. package/dist/modules/customers/components/detail/schedule/FooterFields.js +14 -2
  35. package/dist/modules/customers/components/detail/schedule/FooterFields.js.map +2 -2
  36. package/dist/modules/customers/components/detail/schedule/LinkedEntitiesField.js +9 -2
  37. package/dist/modules/customers/components/detail/schedule/LinkedEntitiesField.js.map +2 -2
  38. package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +9 -2
  39. package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +2 -2
  40. package/dist/modules/customers/components/detail/schedule/fieldConfig.js +25 -4
  41. package/dist/modules/customers/components/detail/schedule/fieldConfig.js.map +2 -2
  42. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +20 -3
  43. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
  44. package/package.json +3 -3
  45. package/src/modules/customers/api/companies/[id]/route.ts +30 -20
  46. package/src/modules/customers/api/companies/route.ts +12 -7
  47. package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +12 -7
  48. package/src/modules/customers/api/people/route.ts +12 -7
  49. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +22 -0
  50. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +28 -21
  51. package/src/modules/customers/components/detail/ActivitiesAddNewMenu.tsx +67 -0
  52. package/src/modules/customers/components/detail/ActivitiesCard.tsx +231 -0
  53. package/src/modules/customers/components/detail/ActivitiesDayStrip.tsx +390 -0
  54. package/src/modules/customers/components/detail/ActivitiesSection.tsx +91 -40
  55. package/src/modules/customers/components/detail/ActivityLogTab.tsx +25 -23
  56. package/src/modules/customers/components/detail/ActivityTimeline.tsx +15 -19
  57. package/src/modules/customers/components/detail/ActivityTimelineFilters.tsx +36 -29
  58. package/src/modules/customers/components/detail/AiActionChips.tsx +17 -23
  59. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +233 -41
  60. package/src/modules/customers/components/detail/schedule/DateTimeFields.tsx +6 -2
  61. package/src/modules/customers/components/detail/schedule/FooterFields.tsx +22 -2
  62. package/src/modules/customers/components/detail/schedule/LinkedEntitiesField.tsx +10 -2
  63. package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +10 -2
  64. package/src/modules/customers/components/detail/schedule/fieldConfig.ts +26 -6
  65. package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +32 -3
  66. package/src/modules/customers/i18n/de.json +69 -2
  67. package/src/modules/customers/i18n/en.json +69 -2
  68. package/src/modules/customers/i18n/es.json +69 -2
  69. package/src/modules/customers/i18n/pl.json +68 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/customers/components/detail/schedule/useScheduleFormState.ts"],
4
- "sourcesContent": ["import * as React from 'react'\nimport type { ActivityType } from './fieldConfig'\n\nexport type RsvpStatus = 'pending' | 'accepted' | 'declined' | 'tentative'\n\nexport type Participant = {\n userId: string\n name: string\n email?: string\n color?: string\n status?: RsvpStatus\n}\n\nexport type LinkedEntity = {\n id: string\n type: 'company' | 'deal' | 'offer'\n label: string\n}\n\nexport type ScheduleActivityEditData = {\n id: string\n interactionType?: string\n title?: string | null\n body?: string | null\n scheduledAt?: string | null\n durationMinutes?: number | null\n location?: string | null\n allDay?: boolean | null\n recurrenceRule?: string | null\n recurrenceEnd?: string | 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\nexport const PARTICIPANT_COLORS = [\n 'bg-chart-emerald',\n 'bg-chart-blue',\n 'bg-chart-orange',\n 'bg-chart-violet',\n 'bg-chart-pink',\n 'bg-chart-teal',\n]\n\ninterface UseScheduleFormStateParams {\n open: boolean\n editData: ScheduleActivityEditData | null | undefined\n}\n\nexport function useScheduleFormState({ open, editData }: UseScheduleFormStateParams) {\n const [activityType, setActivityType] = React.useState<ActivityType>('meeting')\n const [title, setTitle] = React.useState('')\n const [date, setDate] = React.useState(() => new Date().toISOString().slice(0, 10))\n const [startTime, setStartTime] = React.useState('10:00')\n const [duration, setDuration] = React.useState(30)\n const [allDay, setAllDay] = React.useState(false)\n const [description, setDescription] = React.useState('')\n const [markdownEnabled, setMarkdownEnabled] = React.useState(true)\n const [location, setLocation] = React.useState('')\n const [reminderMinutes, setReminderMinutes] = React.useState(15)\n const [visibility, setVisibility] = React.useState('team')\n const [participants, setParticipants] = React.useState<Participant[]>([])\n const [linkedEntities, setLinkedEntities] = React.useState<LinkedEntity[]>([])\n const [recurrenceEnabled, setRecurrenceEnabled] = React.useState(false)\n const [recurrenceDays, setRecurrenceDays] = React.useState<boolean[]>([true, false, true, false, false, false, false])\n const [recurrenceEndType, setRecurrenceEndType] = React.useState<'never' | 'count' | 'date'>('never')\n const [recurrenceCount, setRecurrenceCount] = React.useState(8)\n const [recurrenceEndDate, setRecurrenceEndDate] = React.useState('')\n const [conflict, setConflict] = React.useState<string | null>(null)\n const [saving, setSaving] = React.useState(false)\n const [guestPermissions, setGuestPermissions] = React.useState({ canInviteOthers: true, canModify: false, canSeeList: true })\n\n React.useEffect(() => {\n if (open) {\n if (editData) {\n // Edit mode: populate from existing interaction\n setActivityType((editData.interactionType as ActivityType) ?? 'meeting')\n setTitle(editData.title ?? '')\n const scheduledDate = editData.scheduledAt ? new Date(editData.scheduledAt) : new Date()\n setDate(scheduledDate.toISOString().slice(0, 10))\n setStartTime(scheduledDate.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' }))\n setDuration(editData.durationMinutes ?? 30)\n setAllDay(editData.allDay ?? false)\n setDescription(editData.body ?? '')\n setLocation(editData.location ?? '')\n setReminderMinutes(editData.reminderMinutes ?? 15)\n setVisibility(editData.visibility ?? 'team')\n setParticipants(\n Array.isArray(editData.participants)\n ? editData.participants.map((p, i) => ({\n userId: p.userId,\n name: p.name ?? p.userId,\n email: p.email,\n color: PARTICIPANT_COLORS[i % PARTICIPANT_COLORS.length],\n status: (p.status ?? 'pending') as RsvpStatus,\n }))\n : [],\n )\n setLinkedEntities(\n Array.isArray(editData.linkedEntities)\n ? editData.linkedEntities.map((e) => ({ id: e.id, type: e.type as LinkedEntity['type'], label: e.label }))\n : [],\n )\n if (editData.recurrenceRule) {\n setRecurrenceEnabled(true)\n // Parse RRULE to set days and end type\n const rule = editData.recurrenceRule\n const byDayMatch = rule.match(/BYDAY=([A-Z,]+)/)\n if (byDayMatch) {\n const dayNames = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU']\n const activeDays = byDayMatch[1].split(',')\n setRecurrenceDays(dayNames.map((d) => activeDays.includes(d)))\n }\n const countMatch = rule.match(/COUNT=(\\d+)/)\n const untilMatch = rule.match(/UNTIL=(\\d{8})/)\n if (countMatch) {\n setRecurrenceEndType('count')\n setRecurrenceCount(Number(countMatch[1]))\n } else if (untilMatch) {\n setRecurrenceEndType('date')\n const raw = untilMatch[1]\n setRecurrenceEndDate(`${raw.slice(0, 4)}-${raw.slice(4, 6)}-${raw.slice(6, 8)}`)\n } else {\n setRecurrenceEndType('never')\n }\n } else {\n setRecurrenceEnabled(false)\n }\n if (editData.guestPermissions) {\n setGuestPermissions({\n canInviteOthers: editData.guestPermissions.canInviteOthers ?? true,\n canModify: editData.guestPermissions.canModify ?? false,\n canSeeList: editData.guestPermissions.canSeeList ?? true,\n })\n }\n } else {\n // Create mode: reset all fields\n setActivityType('meeting')\n setTitle('')\n setDate(new Date().toISOString().slice(0, 10))\n setStartTime('10:00')\n setDuration(30)\n setAllDay(false)\n setDescription('')\n setLocation('')\n setReminderMinutes(15)\n setVisibility('team')\n setParticipants([])\n setLinkedEntities([])\n setRecurrenceEnabled(false)\n }\n setConflict(null)\n }\n return () => {\n // Safety net: restore body scroll if Radix Dialog fails to clean up\n document.body.style.removeProperty('overflow')\n document.body.style.removeProperty('pointer-events')\n }\n }, [open, editData])\n\n const removeParticipant = React.useCallback((userId: string) => {\n setParticipants((prev) => prev.filter((p) => p.userId !== userId))\n }, [])\n\n const toggleRecurrenceDay = React.useCallback((index: number) => {\n setRecurrenceDays((prev) => {\n const next = [...prev]\n next[index] = !next[index]\n return next\n })\n }, [])\n\n return {\n activityType,\n setActivityType,\n title,\n setTitle,\n date,\n setDate,\n startTime,\n setStartTime,\n duration,\n setDuration,\n allDay,\n setAllDay,\n description,\n setDescription,\n markdownEnabled,\n setMarkdownEnabled,\n location,\n setLocation,\n reminderMinutes,\n setReminderMinutes,\n visibility,\n setVisibility,\n participants,\n setParticipants,\n linkedEntities,\n setLinkedEntities,\n recurrenceEnabled,\n setRecurrenceEnabled,\n recurrenceDays,\n setRecurrenceDays,\n recurrenceEndType,\n setRecurrenceEndType,\n recurrenceCount,\n setRecurrenceCount,\n recurrenceEndDate,\n setRecurrenceEndDate,\n conflict,\n setConflict,\n saving,\n setSaving,\n guestPermissions,\n setGuestPermissions,\n removeParticipant,\n toggleRecurrenceDay,\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as React from 'react'\nimport type { ActivityType } from './fieldConfig'\n\nexport type RsvpStatus = 'pending' | 'accepted' | 'declined' | 'tentative'\n\nexport type Participant = {\n userId: string\n name: string\n email?: string\n color?: string\n status?: RsvpStatus\n}\n\nexport type LinkedEntity = {\n id: string\n type: 'company' | 'deal' | 'offer'\n label: string\n}\n\nexport type ScheduleActivityEditData = {\n id: string\n interactionType?: string\n title?: string | null\n body?: string | null\n scheduledAt?: string | null\n durationMinutes?: number | null\n location?: string | null\n allDay?: boolean | null\n recurrenceRule?: string | null\n recurrenceEnd?: string | 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\nexport const PARTICIPANT_COLORS = [\n 'bg-chart-emerald',\n 'bg-chart-blue',\n 'bg-chart-orange',\n 'bg-chart-violet',\n 'bg-chart-pink',\n 'bg-chart-teal',\n]\n\n// Per-Figma defaults for the Reminder dropdown when the user picks an activity\n// type. Meeting/email keep the standard 15 min; tasks default to 1 day (1440 min)\n// because they're plan-ahead artefacts; calls default to 5 min as a stand-in for\n// the Figma \"After call ends\" treatment (which would need a non-numeric sentinel\n// in the API contract \u2014 tracked as a follow-up).\nconst DEFAULT_REMINDER_MINUTES: Record<ActivityType, number> = {\n meeting: 15,\n call: 5,\n task: 1440,\n email: 15,\n}\n\ninterface UseScheduleFormStateParams {\n open: boolean\n editData: ScheduleActivityEditData | null | undefined\n}\n\nexport function useScheduleFormState({ open, editData }: UseScheduleFormStateParams) {\n const [activityType, setActivityType] = React.useState<ActivityType>('meeting')\n const [title, setTitle] = React.useState('')\n const [date, setDate] = React.useState(() => new Date().toISOString().slice(0, 10))\n const [startTime, setStartTime] = React.useState('10:00')\n const [duration, setDuration] = React.useState(30)\n const [allDay, setAllDay] = React.useState(false)\n const [description, setDescription] = React.useState('')\n const [markdownEnabled, setMarkdownEnabled] = React.useState(true)\n const [location, setLocation] = React.useState('')\n const [reminderMinutes, setReminderMinutes] = React.useState(15)\n const [visibility, setVisibility] = React.useState('team')\n const [participants, setParticipants] = React.useState<Participant[]>([])\n const [linkedEntities, setLinkedEntities] = React.useState<LinkedEntity[]>([])\n const [recurrenceEnabled, setRecurrenceEnabled] = React.useState(false)\n const [recurrenceDays, setRecurrenceDays] = React.useState<boolean[]>([true, false, true, false, false, false, false])\n const [recurrenceEndType, setRecurrenceEndType] = React.useState<'never' | 'count' | 'date'>('never')\n const [recurrenceCount, setRecurrenceCount] = React.useState(8)\n const [recurrenceEndDate, setRecurrenceEndDate] = React.useState('')\n const [conflict, setConflict] = React.useState<string | null>(null)\n const [saving, setSaving] = React.useState(false)\n const [guestPermissions, setGuestPermissions] = React.useState({ canInviteOthers: true, canModify: false, canSeeList: true })\n\n React.useEffect(() => {\n if (open) {\n if (editData) {\n // Edit mode: populate from existing interaction\n const resolvedType = (editData.interactionType as ActivityType) ?? 'meeting'\n setActivityType(resolvedType)\n setTitle(editData.title ?? '')\n const scheduledDate = editData.scheduledAt ? new Date(editData.scheduledAt) : new Date()\n setDate(scheduledDate.toISOString().slice(0, 10))\n setStartTime(scheduledDate.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' }))\n setDuration(editData.durationMinutes ?? 30)\n setAllDay(editData.allDay ?? false)\n setDescription(editData.body ?? '')\n setLocation(editData.location ?? '')\n // Use per-type default when the editData omits an explicit reminder\n // (the menu-driven \"New X\" flow opens the dialog with `reminderMinutes: null`).\n setReminderMinutes(editData.reminderMinutes ?? DEFAULT_REMINDER_MINUTES[resolvedType])\n setVisibility(editData.visibility ?? 'team')\n setParticipants(\n Array.isArray(editData.participants)\n ? editData.participants.map((p, i) => ({\n userId: p.userId,\n name: p.name ?? p.userId,\n email: p.email,\n color: PARTICIPANT_COLORS[i % PARTICIPANT_COLORS.length],\n status: (p.status ?? 'pending') as RsvpStatus,\n }))\n : [],\n )\n setLinkedEntities(\n Array.isArray(editData.linkedEntities)\n ? editData.linkedEntities.map((e) => ({ id: e.id, type: e.type as LinkedEntity['type'], label: e.label }))\n : [],\n )\n if (editData.recurrenceRule) {\n setRecurrenceEnabled(true)\n // Parse RRULE to set days and end type\n const rule = editData.recurrenceRule\n const byDayMatch = rule.match(/BYDAY=([A-Z,]+)/)\n if (byDayMatch) {\n const dayNames = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU']\n const activeDays = byDayMatch[1].split(',')\n setRecurrenceDays(dayNames.map((d) => activeDays.includes(d)))\n }\n const countMatch = rule.match(/COUNT=(\\d+)/)\n const untilMatch = rule.match(/UNTIL=(\\d{8})/)\n if (countMatch) {\n setRecurrenceEndType('count')\n setRecurrenceCount(Number(countMatch[1]))\n } else if (untilMatch) {\n setRecurrenceEndType('date')\n const raw = untilMatch[1]\n setRecurrenceEndDate(`${raw.slice(0, 4)}-${raw.slice(4, 6)}-${raw.slice(6, 8)}`)\n } else {\n setRecurrenceEndType('never')\n }\n } else {\n setRecurrenceEnabled(false)\n }\n if (editData.guestPermissions) {\n setGuestPermissions({\n canInviteOthers: editData.guestPermissions.canInviteOthers ?? true,\n canModify: editData.guestPermissions.canModify ?? false,\n canSeeList: editData.guestPermissions.canSeeList ?? true,\n })\n }\n } else {\n // Create mode: reset all fields\n setActivityType('meeting')\n setTitle('')\n setDate(new Date().toISOString().slice(0, 10))\n setStartTime('10:00')\n setDuration(30)\n setAllDay(false)\n setDescription('')\n setLocation('')\n setReminderMinutes(DEFAULT_REMINDER_MINUTES.meeting)\n setVisibility('team')\n setParticipants([])\n setLinkedEntities([])\n setRecurrenceEnabled(false)\n }\n setConflict(null)\n }\n return () => {\n // Safety net: restore body scroll if Radix Dialog fails to clean up\n document.body.style.removeProperty('overflow')\n document.body.style.removeProperty('pointer-events')\n }\n }, [open, editData])\n\n // Update the Reminder default when the activity type changes in create mode.\n // Skipped in edit mode (the persisted value wins), and gated by `open` to\n // avoid flipping the default in a closed-but-mounted dialog.\n const lastReminderTypeRef = React.useRef<ActivityType>('meeting')\n React.useEffect(() => {\n if (!open || editData) {\n lastReminderTypeRef.current = activityType\n return\n }\n if (lastReminderTypeRef.current === activityType) return\n lastReminderTypeRef.current = activityType\n setReminderMinutes(DEFAULT_REMINDER_MINUTES[activityType])\n }, [activityType, editData, open])\n\n const removeParticipant = React.useCallback((userId: string) => {\n setParticipants((prev) => prev.filter((p) => p.userId !== userId))\n }, [])\n\n const toggleRecurrenceDay = React.useCallback((index: number) => {\n setRecurrenceDays((prev) => {\n const next = [...prev]\n next[index] = !next[index]\n return next\n })\n }, [])\n\n return {\n activityType,\n setActivityType,\n title,\n setTitle,\n date,\n setDate,\n startTime,\n setStartTime,\n duration,\n setDuration,\n allDay,\n setAllDay,\n description,\n setDescription,\n markdownEnabled,\n setMarkdownEnabled,\n location,\n setLocation,\n reminderMinutes,\n setReminderMinutes,\n visibility,\n setVisibility,\n participants,\n setParticipants,\n linkedEntities,\n setLinkedEntities,\n recurrenceEnabled,\n setRecurrenceEnabled,\n recurrenceDays,\n setRecurrenceDays,\n recurrenceEndType,\n setRecurrenceEndType,\n recurrenceCount,\n setRecurrenceCount,\n recurrenceEndDate,\n setRecurrenceEndDate,\n conflict,\n setConflict,\n saving,\n setSaving,\n guestPermissions,\n setGuestPermissions,\n removeParticipant,\n toggleRecurrenceDay,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.5.1-develop.2996.ce62fd491c",
3
+ "version": "0.5.1-develop.3032.01699048cb",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -237,10 +237,10 @@
237
237
  "ts-pattern": "^5.0.0"
238
238
  },
239
239
  "peerDependencies": {
240
- "@open-mercato/shared": "0.5.1-develop.2996.ce62fd491c"
240
+ "@open-mercato/shared": "0.5.1-develop.3032.01699048cb"
241
241
  },
242
242
  "devDependencies": {
243
- "@open-mercato/shared": "0.5.1-develop.2996.ce62fd491c",
243
+ "@open-mercato/shared": "0.5.1-develop.3032.01699048cb",
244
244
  "@testing-library/dom": "^10.4.1",
245
245
  "@testing-library/jest-dom": "^6.9.1",
246
246
  "@testing-library/react": "^16.3.1",
@@ -42,7 +42,10 @@ import type { EntityId } from '@open-mercato/shared/modules/entities'
42
42
  import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
43
43
  import { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
44
44
  import { parseBooleanFromUnknown } from '@open-mercato/shared/lib/boolean'
45
- import { withActiveCustomerPersonCompanyLinkFilter } from '../../../lib/personCompanyLinkTable'
45
+ import {
46
+ filterActivePersonCompanyLinks,
47
+ withActiveCustomerPersonCompanyLinkFilter,
48
+ } from '../../../lib/personCompanyLinkTable'
46
49
  import { normalizeCustomerDetailCustomFields } from '../../detailCustomFields'
47
50
 
48
51
  export const metadata = {
@@ -703,15 +706,17 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
703
706
  },
704
707
  'customers.companies.GET',
705
708
  )
706
- const companyLinks = await findWithDecryption(
707
- em,
708
- CustomerPersonCompanyLink,
709
- companyLinkWhere,
710
- {
711
- populate: ['person', 'person.personProfile'],
712
- orderBy: { isPrimary: 'desc', createdAt: 'asc' },
713
- },
714
- peopleDecryptionScope,
709
+ const companyLinks = filterActivePersonCompanyLinks(
710
+ await findWithDecryption(
711
+ em,
712
+ CustomerPersonCompanyLink,
713
+ companyLinkWhere,
714
+ {
715
+ populate: ['person', 'person.personProfile'],
716
+ orderBy: { isPrimary: 'desc', createdAt: 'asc' },
717
+ },
718
+ peopleDecryptionScope,
719
+ ),
715
720
  )
716
721
  companyLinks.forEach((link) => {
717
722
  const entity = typeof link.person === 'string' ? null : link.person
@@ -829,18 +834,23 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
829
834
  })
830
835
  const peopleCount = includePeople
831
836
  ? relatedPeople.length
832
- : await em.count(
833
- CustomerPersonCompanyLink,
834
- await withActiveCustomerPersonCompanyLinkFilter(
837
+ : filterActivePersonCompanyLinks(
838
+ await findWithDecryption(
835
839
  em,
836
- {
837
- company: company.id,
838
- organizationId: company.organizationId,
839
- tenantId: company.tenantId,
840
- },
841
- 'customers.companies.GET',
840
+ CustomerPersonCompanyLink,
841
+ await withActiveCustomerPersonCompanyLinkFilter(
842
+ em,
843
+ {
844
+ company: company.id,
845
+ organizationId: company.organizationId,
846
+ tenantId: company.tenantId,
847
+ },
848
+ 'customers.companies.GET',
849
+ ),
850
+ {},
851
+ { tenantId: company.tenantId, organizationId: company.organizationId },
842
852
  ),
843
- )
853
+ ).length
844
854
  const kpiInteractionRows = canonicalActiveInteractions.length
845
855
  ? canonicalActiveInteractions
846
856
  : await findWithDecryption(
@@ -33,7 +33,10 @@ import {
33
33
  createPagedListResponseSchema,
34
34
  defaultOkResponseSchema,
35
35
  } from '../openapi'
36
- import { withActiveCustomerPersonCompanyLinkFilter } from '../../lib/personCompanyLinkTable'
36
+ import {
37
+ filterActivePersonCompanyLinks,
38
+ withActiveCustomerPersonCompanyLinkFilter,
39
+ } from '../../lib/personCompanyLinkTable'
37
40
  import { normalizeCompanyProfilePayload } from './payload'
38
41
 
39
42
  const rawBodySchema = z.object({}).passthrough()
@@ -217,12 +220,14 @@ const crud = makeCrudRoute({
217
220
  { person: query.excludeLinkedPersonId },
218
221
  'customers.companies.GET',
219
222
  )
220
- const links = await findWithDecryption(
221
- em,
222
- CustomerPersonCompanyLink,
223
- linkWhere,
224
- { populate: ['company'] },
225
- decryptionScope,
223
+ const links = filterActivePersonCompanyLinks(
224
+ await findWithDecryption(
225
+ em,
226
+ CustomerPersonCompanyLink,
227
+ linkWhere,
228
+ { populate: ['company'] },
229
+ decryptionScope,
230
+ ),
226
231
  )
227
232
  links.forEach((link) => {
228
233
  const companyId = link.company?.id
@@ -20,7 +20,10 @@ import {
20
20
  CustomerDeal,
21
21
  CustomerInteraction,
22
22
  } from '../../../../../data/entities'
23
- import { withActiveCustomerPersonCompanyLinkFilter } from '../../../../../lib/personCompanyLinkTable'
23
+ import {
24
+ filterActivePersonCompanyLinks,
25
+ withActiveCustomerPersonCompanyLinkFilter,
26
+ } from '../../../../../lib/personCompanyLinkTable'
24
27
 
25
28
  const paramsSchema = z.object({
26
29
  id: z.string().uuid(),
@@ -187,12 +190,14 @@ export async function GET(req: Request, ctx: { params?: { id?: string } }) {
187
190
  },
188
191
  'customers.people.companiesEnriched.GET',
189
192
  )
190
- const links = await findWithDecryption(
191
- em,
192
- CustomerPersonCompanyLink,
193
- linkWhere,
194
- { populate: ['company'] },
195
- entityScope,
193
+ const links = filterActivePersonCompanyLinks(
194
+ await findWithDecryption(
195
+ em,
196
+ CustomerPersonCompanyLink,
197
+ linkWhere,
198
+ { populate: ['company'] },
199
+ entityScope,
200
+ ),
196
201
  )
197
202
 
198
203
  const companyIds = links.map((link) => (link.company as CustomerEntity).id)
@@ -29,7 +29,10 @@ import {
29
29
  createPagedListResponseSchema,
30
30
  defaultOkResponseSchema,
31
31
  } from '../openapi'
32
- import { withActiveCustomerPersonCompanyLinkFilter } from '../../lib/personCompanyLinkTable'
32
+ import {
33
+ filterActivePersonCompanyLinks,
34
+ withActiveCustomerPersonCompanyLinkFilter,
35
+ } from '../../lib/personCompanyLinkTable'
33
36
  import { normalizeProfilePayload } from './payload'
34
37
 
35
38
  const rawBodySchema = z.object({}).passthrough()
@@ -221,12 +224,14 @@ const crud = makeCrudRoute({
221
224
  { company: query.excludeLinkedCompanyId },
222
225
  'customers.people.GET',
223
226
  )
224
- const links = await findWithDecryption(
225
- em,
226
- CustomerPersonCompanyLink,
227
- linkWhere,
228
- { populate: ['person'] },
229
- decryptionScope,
227
+ const links = filterActivePersonCompanyLinks(
228
+ await findWithDecryption(
229
+ em,
230
+ CustomerPersonCompanyLink,
231
+ linkWhere,
232
+ { populate: ['person'] },
233
+ decryptionScope,
234
+ ),
230
235
  )
231
236
  links.forEach((link) => {
232
237
  const personId = link.person?.id
@@ -219,6 +219,27 @@ export default function CompanyDetailV2Page({ params }: { params?: { id?: string
219
219
  setScheduleDialogOpen(true)
220
220
  }, [])
221
221
 
222
+ const handleAddActivity = React.useCallback((kind: 'meeting' | 'call' | 'task' | 'email') => {
223
+ setScheduleEditData({
224
+ id: '',
225
+ interactionType: kind,
226
+ title: null,
227
+ body: null,
228
+ scheduledAt: null,
229
+ durationMinutes: null,
230
+ location: null,
231
+ allDay: null,
232
+ recurrenceRule: null,
233
+ recurrenceEnd: null,
234
+ participants: null,
235
+ reminderMinutes: null,
236
+ visibility: null,
237
+ linkedEntities: null,
238
+ guestPermissions: null,
239
+ })
240
+ setScheduleDialogOpen(true)
241
+ }, [])
242
+
222
243
  // Injected tabs from UMES
223
244
  const { widgets: injectedTabWidgets } = useInjectionWidgets('detail:customers.company:tabs', {
224
245
  context: injectionContext,
@@ -473,6 +494,7 @@ export default function CompanyDetailV2Page({ params }: { params?: { id?: string
473
494
  plannedActivities={plannedActivities}
474
495
  onActivityCreated={handleActivityCreated}
475
496
  onScheduleRequested={openNewScheduleDialog}
497
+ onAddActivity={handleAddActivity}
476
498
  onMarkDone={handleMarkDone}
477
499
  onEditActivity={handleEditActivity}
478
500
  onCancelActivity={handleCancelActivity}
@@ -23,18 +23,17 @@ import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuarde
23
23
  import { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'
24
24
 
25
25
  import { ActivitiesSection } from '../../../../components/detail/ActivitiesSection'
26
+ import { ActivitiesCard } from '../../../../components/detail/ActivitiesCard'
27
+ import type { ActivityKind } from '../../../../components/detail/ActivitiesAddNewMenu'
26
28
  import { DealsSection } from '../../../../components/detail/DealsSection'
27
29
  import { TasksSection } from '../../../../components/detail/TasksSection'
28
30
  import type { TagSummary } from '../../../../components/detail/types'
29
- import { InlineActivityComposer } from '../../../../components/detail/InlineActivityComposer'
30
- import { PlannedActivitiesSection } from '../../../../components/detail/PlannedActivitiesSection'
31
31
  import { ScheduleActivityDialog, type ScheduleActivityEditData } from '../../../../components/detail/ScheduleActivityDialog'
32
32
  import { PersonDetailHeader } from '../../../../components/detail/PersonDetailHeader'
33
33
  import { ChangelogTab } from '../../../../components/detail/ChangelogTab'
34
34
  import { PersonDetailTabs, resolveLegacyTab, type PersonTabId } from '../../../../components/detail/PersonDetailTabs'
35
35
  import { PersonCompaniesSection } from '../../../../components/detail/PersonCompaniesSection'
36
36
  import { MobilePersonDetail } from '../../../../components/detail/MobilePersonDetail'
37
- import { useInteractionMutations } from '../../../../components/detail/hooks/useInteractionMutations'
38
37
  import type { TagsSectionController } from '@open-mercato/ui/backend/detail'
39
38
  import {
40
39
  buildPersonEditPayload,
@@ -190,11 +189,26 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
190
189
  [injectionContext, runMutation],
191
190
  )
192
191
 
193
- const { completeInteraction: handleMarkDone, cancelInteraction: handleCancelActivity } = useInteractionMutations({
194
- runMutationWithContext,
195
- onAfterChange: handleActivityCreated,
196
- logContext: 'customers.people-v2',
197
- })
192
+ const handleAddActivity = React.useCallback((kind: ActivityKind) => {
193
+ setScheduleEditData({
194
+ id: '',
195
+ interactionType: kind,
196
+ title: null,
197
+ body: null,
198
+ scheduledAt: null,
199
+ durationMinutes: null,
200
+ location: null,
201
+ allDay: null,
202
+ recurrenceRule: null,
203
+ recurrenceEnd: null,
204
+ participants: null,
205
+ reminderMinutes: null,
206
+ visibility: null,
207
+ linkedEntities: null,
208
+ guestPermissions: null,
209
+ })
210
+ setScheduleDialogOpen(true)
211
+ }, [])
198
212
 
199
213
  const handleEditActivity = React.useCallback((activity: { id: string; interactionType?: string; title?: string | null; body?: string | null; scheduledAt?: string | null; [key: string]: unknown }) => {
200
214
  const raw = activity as Record<string, unknown>
@@ -448,20 +462,13 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
448
462
  if (activeTab === 'activities') {
449
463
  return (
450
464
  <div className="space-y-4">
451
- <InlineActivityComposer
452
- entityType="person"
465
+ <ActivitiesCard
453
466
  entityId={personId}
454
- onActivityCreated={handleActivityCreated}
455
- runGuardedMutation={runMutationWithContext}
456
- onScheduleRequested={() => { setScheduleEditData(null); setScheduleDialogOpen(true) }}
457
- useCanonicalInteractions={useCanonicalInteractions}
458
- />
459
- <PlannedActivitiesSection
460
- activities={plannedActivities}
461
- onComplete={handleMarkDone}
462
- onSchedule={() => { setScheduleEditData(null); setScheduleDialogOpen(true) }}
463
- onEdit={handleEditActivity}
464
- onCancel={handleCancelActivity}
467
+ plannedActivities={plannedActivities}
468
+ refreshKey={activityRefreshKey}
469
+ onAddNew={handleAddActivity}
470
+ onEditActivity={handleEditActivity}
471
+ entityCompanyName={data.company?.displayName ?? data.companies?.[0]?.displayName ?? null}
465
472
  />
466
473
  <ActivitiesSection
467
474
  entityId={personId}
@@ -0,0 +1,67 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { Check, Phone, Mail, Users, CheckSquare } from 'lucide-react'
5
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
6
+ import { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'
7
+
8
+ export type ActivityKind = 'meeting' | 'call' | 'task' | 'email'
9
+
10
+ interface ActivitiesAddNewMenuProps {
11
+ onSelect: (kind: ActivityKind) => void
12
+ disabled?: boolean
13
+ }
14
+
15
+ const MENU_ITEMS: ReadonlyArray<{ kind: ActivityKind; icon: React.ComponentType<{ className?: string }>; key: string; fallback: string }> = [
16
+ { kind: 'meeting', icon: Users, key: 'customers.activities.add.meeting', fallback: 'New meeting' },
17
+ { kind: 'call', icon: Phone, key: 'customers.activities.add.call', fallback: 'Log call' },
18
+ { kind: 'task', icon: CheckSquare, key: 'customers.activities.add.task', fallback: 'New task' },
19
+ { kind: 'email', icon: Mail, key: 'customers.activities.add.email', fallback: 'Compose email' },
20
+ ]
21
+
22
+ export function ActivitiesAddNewMenu({ onSelect, disabled }: ActivitiesAddNewMenuProps) {
23
+ const t = useT()
24
+ const [open, setOpen] = React.useState(false)
25
+
26
+ const handleSelect = React.useCallback(
27
+ (kind: ActivityKind) => {
28
+ setOpen(false)
29
+ onSelect(kind)
30
+ },
31
+ [onSelect],
32
+ )
33
+
34
+ return (
35
+ <Popover open={open} onOpenChange={setOpen}>
36
+ <PopoverTrigger asChild>
37
+ <button
38
+ type="button"
39
+ disabled={disabled}
40
+ aria-label={t('customers.activities.addNew', 'Add new')}
41
+ className="inline-flex items-center gap-1.5 overflow-hidden rounded-md bg-foreground pl-3 pr-3.5 py-2 text-xs font-semibold text-background transition-colors hover:bg-foreground/90 disabled:opacity-60"
42
+ >
43
+ <Check className="size-3.5" />
44
+ {t('customers.activities.addNew', 'Add new')}
45
+ </button>
46
+ </PopoverTrigger>
47
+ <PopoverContent align="end" className="w-[180px] p-1">
48
+ <ul className="flex flex-col">
49
+ {MENU_ITEMS.map(({ kind, icon: Icon, key, fallback }) => (
50
+ <li key={kind}>
51
+ <button
52
+ type="button"
53
+ onClick={() => handleSelect(kind)}
54
+ className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm text-foreground hover:bg-accent/40"
55
+ >
56
+ <Icon className="size-4 text-muted-foreground" />
57
+ {t(key, fallback)}
58
+ </button>
59
+ </li>
60
+ ))}
61
+ </ul>
62
+ </PopoverContent>
63
+ </Popover>
64
+ )
65
+ }
66
+
67
+ export default ActivitiesAddNewMenu