@open-mercato/core 0.8.1-develop.7275.1.f772b944d4 → 0.8.1-develop.7295.1.d0e0e33014
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/auth/api/users/acl/route.js.map +2 -2
- package/dist/modules/auth/components/AclEditor.js +21 -3
- package/dist/modules/auth/components/AclEditor.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/page.js +6 -4
- package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/map/components/DealsMapView.js +6 -4
- package/dist/modules/customers/backend/customers/deals/map/components/DealsMapView.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +6 -4
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.js +5 -3
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +6 -4
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/page.js +6 -4
- package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js +9 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/hooks.js +4 -2
- package/dist/modules/customers/components/calendar/editor/hooks.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/lookups.js +7 -3
- package/dist/modules/customers/components/calendar/editor/lookups.js.map +2 -2
- package/dist/modules/customers/components/detail/AssignRoleDialog.js +5 -2
- package/dist/modules/customers/components/detail/AssignRoleDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/RoleAssignmentRow.js +4 -2
- package/dist/modules/customers/components/detail/RoleAssignmentRow.js.map +2 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +8 -5
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/DateTimeFields.js +10 -7
- package/dist/modules/customers/components/detail/schedule/DateTimeFields.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +4 -2
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/fieldConfig.js +9 -0
- package/dist/modules/customers/components/detail/schedule/fieldConfig.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/index.js +3 -1
- package/dist/modules/customers/components/detail/schedule/index.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +11 -4
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
- package/dist/modules/customers/components/formConfig.js +17 -9
- package/dist/modules/customers/components/formConfig.js.map +2 -2
- package/dist/modules/customers/data/validators.js +12 -7
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/lib/assignableStaff.js +103 -22
- package/dist/modules/customers/lib/assignableStaff.js.map +2 -2
- package/dist/modules/customers/setup.js +10 -2
- package/dist/modules/customers/setup.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/[id]/route.js +5 -0
- package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/route.js +5 -0
- package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +29 -5
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js +48 -11
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
- package/dist/modules/data_sync/data/validators.js +13 -2
- package/dist/modules/data_sync/data/validators.js.map +2 -2
- package/dist/modules/data_sync/lib/schedule-value.js +30 -0
- package/dist/modules/data_sync/lib/schedule-value.js.map +7 -0
- package/dist/modules/eudr/data/validators.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js +3 -4
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.js +3 -1
- package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +2 -1
- package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/page.js +3 -4
- package/dist/modules/portal/frontend/[orgSlug]/portal/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js +3 -4
- package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js.map +2 -2
- package/dist/modules/portal/lib/navigation.js +3 -4
- package/dist/modules/portal/lib/navigation.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +155 -15
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/page.js +122 -86
- package/dist/modules/staff/backend/staff/time-tracking/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +38 -17
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js +45 -18
- package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js.map +2 -2
- package/dist/modules/staff/extension-points.js +16 -0
- package/dist/modules/staff/extension-points.js.map +2 -2
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js +3 -4
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js.map +2 -2
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js +3 -4
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js +14 -13
- package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js +60 -41
- package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js.map +2 -2
- package/dist/modules/warranty_claims/api/ai/assess/route.js +2 -2
- package/dist/modules/warranty_claims/api/ai/assess/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/ai/draft-reply/route.js +3 -4
- package/dist/modules/warranty_claims/api/ai/draft-reply/route.js.map +3 -3
- package/dist/modules/warranty_claims/api/ai/suggest/route.js +3 -3
- package/dist/modules/warranty_claims/api/ai/suggest/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/assign/route.js +3 -3
- package/dist/modules/warranty_claims/api/assign/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/credit-memo/route.js +2 -2
- package/dist/modules/warranty_claims/api/credit-memo/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/entitlement/route.js +2 -2
- package/dist/modules/warranty_claims/api/entitlement/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/events/route.js +4 -4
- package/dist/modules/warranty_claims/api/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/external/claims/route.js +3 -3
- package/dist/modules/warranty_claims/api/external/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/route.js +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/events/route.js +2 -2
- package/dist/modules/warranty_claims/api/portal/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/receiving/route.js +2 -2
- package/dist/modules/warranty_claims/api/receiving/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/replacement-order/route.js +2 -2
- package/dist/modules/warranty_claims/api/replacement-order/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/return-label/route.js +2 -2
- package/dist/modules/warranty_claims/api/return-label/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/risk/route.js +2 -2
- package/dist/modules/warranty_claims/api/risk/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/sales-return/route.js +2 -2
- package/dist/modules/warranty_claims/api/sales-return/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/settings-general/route.js +4 -4
- package/dist/modules/warranty_claims/api/settings-general/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/stats/route.js +2 -2
- package/dist/modules/warranty_claims/api/stats/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/submit/route.js +3 -3
- package/dist/modules/warranty_claims/api/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/transition/route.js +3 -3
- package/dist/modules/warranty_claims/api/transition/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js +3 -3
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js.map +2 -2
- package/dist/modules/warranty_claims/backend/page.js +4 -2
- package/dist/modules/warranty_claims/backend/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/[id]/page.js +9 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/[id]/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/settings/page.js +4 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/settings/page.js.map +2 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/[id]/page.js +3 -4
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/[id]/page.js.map +2 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/new/page.js +3 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/new/page.js.map +2 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js +3 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js.map +2 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -1
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
- package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.js +2 -2
- package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.js.map +2 -2
- package/dist/modules/workflows/lib/event-trigger-service.js +56 -1
- package/dist/modules/workflows/lib/event-trigger-service.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/auth/api/users/acl/route.ts +4 -3
- package/src/modules/auth/components/AclEditor.tsx +38 -2
- package/src/modules/auth/i18n/de.json +5 -1
- package/src/modules/auth/i18n/en.json +5 -1
- package/src/modules/auth/i18n/es.json +5 -1
- package/src/modules/auth/i18n/ko.json +5 -1
- package/src/modules/auth/i18n/pl.json +5 -1
- package/src/modules/customers/backend/customers/companies/page.tsx +6 -4
- package/src/modules/customers/backend/customers/deals/map/components/DealsMapView.tsx +6 -4
- package/src/modules/customers/backend/customers/deals/page.tsx +6 -4
- package/src/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.tsx +5 -3
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +6 -4
- package/src/modules/customers/backend/customers/people/page.tsx +6 -4
- package/src/modules/customers/components/calendar/editor/PeopleField.tsx +9 -2
- package/src/modules/customers/components/calendar/editor/hooks.ts +4 -2
- package/src/modules/customers/components/calendar/editor/lookups.ts +12 -4
- package/src/modules/customers/components/detail/AssignRoleDialog.tsx +4 -1
- package/src/modules/customers/components/detail/RoleAssignmentRow.tsx +4 -2
- package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +15 -7
- package/src/modules/customers/components/detail/schedule/DateTimeFields.tsx +16 -7
- package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +4 -2
- package/src/modules/customers/components/detail/schedule/fieldConfig.ts +15 -0
- package/src/modules/customers/components/detail/schedule/index.ts +1 -1
- package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +16 -9
- package/src/modules/customers/components/formConfig.tsx +35 -12
- package/src/modules/customers/data/validators.ts +14 -8
- package/src/modules/customers/i18n/de.json +1 -0
- package/src/modules/customers/i18n/en.json +1 -0
- package/src/modules/customers/i18n/es.json +1 -0
- package/src/modules/customers/i18n/ko.json +1 -0
- package/src/modules/customers/i18n/pl.json +1 -0
- package/src/modules/customers/lib/assignableStaff.ts +172 -34
- package/src/modules/customers/setup.ts +8 -0
- package/src/modules/data_sync/api/schedules/[id]/route.ts +5 -0
- package/src/modules/data_sync/api/schedules/route.ts +5 -0
- package/src/modules/data_sync/backend/data-sync/page.tsx +48 -4
- package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +61 -2
- package/src/modules/data_sync/data/validators.ts +26 -2
- package/src/modules/data_sync/i18n/de.json +2 -0
- package/src/modules/data_sync/i18n/en.json +2 -0
- package/src/modules/data_sync/i18n/es.json +2 -0
- package/src/modules/data_sync/i18n/ko.json +2 -0
- package/src/modules/data_sync/i18n/pl.json +2 -0
- package/src/modules/data_sync/lib/schedule-value.ts +45 -0
- package/src/modules/eudr/data/validators.ts +3 -4
- package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/page.tsx +5 -4
- package/src/modules/portal/frontend/[orgSlug]/portal/invite/page.tsx +3 -1
- package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +2 -1
- package/src/modules/portal/frontend/[orgSlug]/portal/page.tsx +5 -5
- package/src/modules/portal/frontend/[orgSlug]/portal/profile/page.tsx +5 -4
- package/src/modules/portal/i18n/de.json +1 -0
- package/src/modules/portal/i18n/en.json +1 -0
- package/src/modules/portal/i18n/es.json +1 -0
- package/src/modules/portal/i18n/ko.json +1 -0
- package/src/modules/portal/i18n/pl.json +1 -0
- package/src/modules/portal/lib/navigation.ts +8 -3
- package/src/modules/query_index/lib/engine.ts +238 -17
- package/src/modules/staff/backend/staff/time-tracking/page.tsx +161 -130
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +55 -43
- package/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.tsx +61 -38
- package/src/modules/staff/extension-points.ts +16 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.tsx +3 -4
- package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/page.tsx +3 -4
- package/src/modules/staff/i18n/de.json +3 -0
- package/src/modules/staff/i18n/en.json +3 -0
- package/src/modules/staff/i18n/es.json +3 -0
- package/src/modules/staff/i18n/ko.json +3 -0
- package/src/modules/staff/i18n/pl.json +3 -0
- package/src/modules/staff/lib/time-tracking-ui/ReportSheet.tsx +32 -29
- package/src/modules/staff/lib/time-tracking-ui/TaskListView.tsx +89 -79
- package/src/modules/warranty_claims/api/ai/assess/route.ts +2 -2
- package/src/modules/warranty_claims/api/ai/draft-reply/route.ts +2 -3
- package/src/modules/warranty_claims/api/ai/suggest/route.ts +3 -3
- package/src/modules/warranty_claims/api/assign/route.ts +3 -3
- package/src/modules/warranty_claims/api/credit-memo/route.ts +2 -2
- package/src/modules/warranty_claims/api/entitlement/route.ts +2 -2
- package/src/modules/warranty_claims/api/events/route.ts +4 -4
- package/src/modules/warranty_claims/api/external/claims/route.ts +3 -3
- package/src/modules/warranty_claims/api/portal/claims/[id]/submit/route.ts +2 -2
- package/src/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.ts +2 -2
- package/src/modules/warranty_claims/api/portal/claims/route.ts +2 -2
- package/src/modules/warranty_claims/api/portal/events/route.ts +2 -2
- package/src/modules/warranty_claims/api/receiving/route.ts +2 -2
- package/src/modules/warranty_claims/api/replacement-order/route.ts +2 -2
- package/src/modules/warranty_claims/api/return-label/route.ts +2 -2
- package/src/modules/warranty_claims/api/risk/route.ts +2 -2
- package/src/modules/warranty_claims/api/sales-return/route.ts +2 -2
- package/src/modules/warranty_claims/api/settings-general/route.ts +4 -4
- package/src/modules/warranty_claims/api/stats/route.ts +2 -2
- package/src/modules/warranty_claims/api/submit/route.ts +3 -3
- package/src/modules/warranty_claims/api/transition/route.ts +3 -3
- package/src/modules/warranty_claims/api/vendor-recovery/route.ts +3 -3
- package/src/modules/warranty_claims/api/vendor-recovery-suggestions/route.ts +2 -2
- package/src/modules/warranty_claims/backend/page.tsx +4 -2
- package/src/modules/warranty_claims/backend/warranty_claims/[id]/page.tsx +9 -2
- package/src/modules/warranty_claims/backend/warranty_claims/settings/page.tsx +4 -2
- package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/[id]/page.tsx +5 -4
- package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/new/page.tsx +5 -2
- package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.tsx +5 -2
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -1
- package/src/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.tsx +2 -2
- package/src/modules/workflows/i18n/de.json +1 -1
- package/src/modules/workflows/i18n/en.json +1 -1
- package/src/modules/workflows/i18n/es.json +1 -1
- package/src/modules/workflows/i18n/ko.json +1 -1
- package/src/modules/workflows/i18n/pl.json +1 -1
- package/src/modules/workflows/lib/event-trigger-service.ts +139 -0
|
@@ -11,6 +11,7 @@ import { Avatar } from '@open-mercato/ui/primitives/avatar'
|
|
|
11
11
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
12
12
|
import { Badge } from '@open-mercato/ui/primitives/badge'
|
|
13
13
|
import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
14
|
+
import { useCurrentOrganization } from '@open-mercato/ui/backend/BackendChromeProvider'
|
|
14
15
|
import { fetchAssignableStaffMembers } from './assignableStaff'
|
|
15
16
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
16
17
|
|
|
@@ -61,13 +62,14 @@ export function RoleAssignmentRow({
|
|
|
61
62
|
onUpdated,
|
|
62
63
|
}: RoleAssignmentRowProps) {
|
|
63
64
|
const t = useT()
|
|
65
|
+
const activeOrgId = useCurrentOrganization()?.id ?? null
|
|
64
66
|
const { confirm, ConfirmDialogElement } = useConfirmDialog()
|
|
65
67
|
const [removing, setRemoving] = React.useState(false)
|
|
66
68
|
const [changingUser, setChangingUser] = React.useState(false)
|
|
67
69
|
|
|
68
70
|
const searchUsers = React.useCallback(async (query: string): Promise<LookupSelectItem[]> => {
|
|
69
71
|
try {
|
|
70
|
-
const members = await fetchAssignableStaffMembers(query, { pageSize: 20 })
|
|
72
|
+
const members = await fetchAssignableStaffMembers(query, { pageSize: 20, activeOrgId })
|
|
71
73
|
return members.map((member) => ({
|
|
72
74
|
id: member.userId,
|
|
73
75
|
title: member.displayName,
|
|
@@ -77,7 +79,7 @@ export function RoleAssignmentRow({
|
|
|
77
79
|
logger.error('customers.roles.searchUsers failed', { err: error })
|
|
78
80
|
return []
|
|
79
81
|
}
|
|
80
|
-
}, [])
|
|
82
|
+
}, [activeOrgId])
|
|
81
83
|
|
|
82
84
|
const handleUserChange = React.useCallback(async (userId: string | null) => {
|
|
83
85
|
if (!userId || userId === role.userId) return
|
|
@@ -21,6 +21,8 @@ import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
|
21
21
|
import {
|
|
22
22
|
useScheduleFormState,
|
|
23
23
|
FIELD_VISIBILITY,
|
|
24
|
+
isDateRequired,
|
|
25
|
+
isTimeRequired,
|
|
24
26
|
getFieldLabel,
|
|
25
27
|
DateTimeFields,
|
|
26
28
|
ParticipantsField,
|
|
@@ -365,8 +367,10 @@ export function ScheduleActivityDialog({
|
|
|
365
367
|
const trimmedDate = state.date.trim()
|
|
366
368
|
const trimmedStartTime = state.startTime.trim()
|
|
367
369
|
const trimmedCallPhone = callPhoneNumber.trim()
|
|
368
|
-
|
|
369
|
-
|
|
370
|
+
// Only calendar-bound types must be scheduled; a task may stay an undated
|
|
371
|
+
// backlog item, so blocking its save on an empty date was wrong (#5941).
|
|
372
|
+
const isDateMissing = isDateRequired(state.activityType) && !trimmedDate
|
|
373
|
+
const isTimeMissing = isTimeRequired(state.activityType) && !state.allDay && !trimmedStartTime
|
|
370
374
|
const isSubmitDisabled =
|
|
371
375
|
state.saving ||
|
|
372
376
|
!state.title.trim() ||
|
|
@@ -399,9 +403,13 @@ export function ScheduleActivityDialog({
|
|
|
399
403
|
}
|
|
400
404
|
state.setSaving(true)
|
|
401
405
|
try {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
406
|
+
// An undated activity (a backlog task) has no moment to compute — sending
|
|
407
|
+
// an explicit null clears `scheduled_at` instead of posting an
|
|
408
|
+
// `Invalid Date` built from an empty date string (#5941).
|
|
409
|
+
const timeForPayload = state.allDay ? '00:00' : trimmedStartTime
|
|
410
|
+
const scheduledAt = trimmedDate
|
|
411
|
+
? new Date(`${trimmedDate}T${timeForPayload || '00:00'}:00`).toISOString()
|
|
412
|
+
: null
|
|
405
413
|
|
|
406
414
|
const recurrenceRule = state.recurrenceEnabled
|
|
407
415
|
? buildRecurrenceRule(state.recurrenceDays, state.recurrenceEndType, state.recurrenceCount, state.recurrenceEndDate)
|
|
@@ -422,8 +430,8 @@ export function ScheduleActivityDialog({
|
|
|
422
430
|
title: state.title.trim(),
|
|
423
431
|
body: state.description.trim() || null,
|
|
424
432
|
status: 'planned',
|
|
425
|
-
date: trimmedDate,
|
|
426
|
-
time:
|
|
433
|
+
date: trimmedDate || null,
|
|
434
|
+
time: trimmedDate ? timeForPayload || null : null,
|
|
427
435
|
phoneNumber: state.activityType === 'call' ? phoneNumberForPayload : undefined,
|
|
428
436
|
// Only tasks expose the priority control, so other types leave the column
|
|
429
437
|
// untouched rather than clearing it on a type switch (#5943).
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
SelectValue,
|
|
16
16
|
} from '@open-mercato/ui/primitives/select'
|
|
17
17
|
import type { ActivityType, ScheduleFieldId } from './fieldConfig'
|
|
18
|
-
import { isVisible, getFieldLabel } from './fieldConfig'
|
|
18
|
+
import { isVisible, isDateRequired, isTimeRequired, getFieldLabel } from './fieldConfig'
|
|
19
19
|
import { USER_TIMEZONE } from '../../../lib/localDay'
|
|
20
20
|
|
|
21
21
|
function parseIsoDate(value: string): Date | null {
|
|
@@ -100,8 +100,10 @@ export function DateTimeFields({
|
|
|
100
100
|
const showAllDay = isVisible(activityType, 'allDay')
|
|
101
101
|
const showRecurrence = isVisible(activityType, 'recurrence')
|
|
102
102
|
|
|
103
|
-
const
|
|
104
|
-
const
|
|
103
|
+
const dateRequired = isDateRequired(activityType)
|
|
104
|
+
const timeRequired = isTimeRequired(activityType)
|
|
105
|
+
const dateMissing = dateRequired && !date.trim()
|
|
106
|
+
const timeMissing = timeRequired && showStartTime && !allDay && !startTime.trim()
|
|
105
107
|
const dateErrorId = 'schedule-date-error'
|
|
106
108
|
const timeErrorId = 'schedule-time-error'
|
|
107
109
|
|
|
@@ -112,13 +114,18 @@ export function DateTimeFields({
|
|
|
112
114
|
<div className="flex min-w-0 flex-[1.5] flex-col gap-1.5">
|
|
113
115
|
<label className="text-overline font-semibold text-muted-foreground tracking-wider">
|
|
114
116
|
{getFieldLabel(activityType, 'date', t, 'customers.schedule.date', 'Date')}
|
|
115
|
-
|
|
117
|
+
{dateRequired ? (
|
|
118
|
+
<span aria-hidden="true" className="ml-1 text-status-error-foreground">*</span>
|
|
119
|
+
) : null}
|
|
116
120
|
</label>
|
|
117
121
|
<DatePicker
|
|
118
122
|
value={parseIsoDate(date)}
|
|
119
123
|
onChange={(next) => setDate(formatIsoDate(next))}
|
|
120
124
|
placeholder={t('customers.schedule.date.placeholder', 'Pick a date')}
|
|
121
|
-
required
|
|
125
|
+
required={dateRequired}
|
|
126
|
+
// An optional date seeds to today, so the picker needs a way back to
|
|
127
|
+
// "no due date"; the default apply/cancel footer cannot reach null.
|
|
128
|
+
footer={dateRequired ? undefined : 'today-clear'}
|
|
122
129
|
aria-describedby={dateMissing ? dateErrorId : undefined}
|
|
123
130
|
className={cn(
|
|
124
131
|
'h-10',
|
|
@@ -135,7 +142,9 @@ export function DateTimeFields({
|
|
|
135
142
|
<div className="flex min-w-0 flex-1 flex-col gap-1.5">
|
|
136
143
|
<label className="text-overline font-semibold text-muted-foreground tracking-wider">
|
|
137
144
|
{getFieldLabel(activityType, 'startTime', t, 'customers.schedule.start', 'Start')}
|
|
138
|
-
|
|
145
|
+
{timeRequired ? (
|
|
146
|
+
<span aria-hidden="true" className="ml-1 text-status-error-foreground">*</span>
|
|
147
|
+
) : null}
|
|
139
148
|
</label>
|
|
140
149
|
<TimePicker
|
|
141
150
|
value={startTime || null}
|
|
@@ -147,7 +156,7 @@ export function DateTimeFields({
|
|
|
147
156
|
timeMissing ? 'border-status-error-border' : undefined,
|
|
148
157
|
)}
|
|
149
158
|
showNowButton
|
|
150
|
-
showClearButton={
|
|
159
|
+
showClearButton={!timeRequired}
|
|
151
160
|
/>
|
|
152
161
|
{timeMissing ? (
|
|
153
162
|
<p id={timeErrorId} className="text-xs text-status-error-foreground">
|
|
@@ -10,6 +10,7 @@ import { Checkbox } from '@open-mercato/ui/primitives/checkbox'
|
|
|
10
10
|
import { IconButton } from '@open-mercato/ui/primitives/icon-button'
|
|
11
11
|
import { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'
|
|
12
12
|
import { SearchInput } from '@open-mercato/ui/primitives/search-input'
|
|
13
|
+
import { useCurrentOrganization } from '@open-mercato/ui/backend/BackendChromeProvider'
|
|
13
14
|
import { fetchAssignableStaffMembersPage } from '../assignableStaff'
|
|
14
15
|
import type { ActivityType, ScheduleFieldId } from './fieldConfig'
|
|
15
16
|
import { isVisible, getFieldLabel } from './fieldConfig'
|
|
@@ -39,6 +40,7 @@ function ParticipantSearchPopover({
|
|
|
39
40
|
const [hasMore, setHasMore] = React.useState(false)
|
|
40
41
|
const [loading, setLoading] = React.useState(false)
|
|
41
42
|
const [loadError, setLoadError] = React.useState<string | null>(null)
|
|
43
|
+
const activeOrgId = useCurrentOrganization()?.id ?? null
|
|
42
44
|
const selectableResults = React.useMemo(
|
|
43
45
|
() => results.filter((result) => !existingIds.has(result.userId)),
|
|
44
46
|
[existingIds, results],
|
|
@@ -48,7 +50,7 @@ function ParticipantSearchPopover({
|
|
|
48
50
|
if (!open) return
|
|
49
51
|
const controller = new AbortController()
|
|
50
52
|
setLoading(true)
|
|
51
|
-
fetchAssignableStaffMembersPage(query, { page, pageSize: PAGE_SIZE, signal: controller.signal })
|
|
53
|
+
fetchAssignableStaffMembersPage(query, { page, pageSize: PAGE_SIZE, activeOrgId, signal: controller.signal })
|
|
52
54
|
.then((result) => {
|
|
53
55
|
const members = result.items
|
|
54
56
|
const nextResults = members.map((member) => ({
|
|
@@ -77,7 +79,7 @@ function ParticipantSearchPopover({
|
|
|
77
79
|
})
|
|
78
80
|
.finally(() => setLoading(false))
|
|
79
81
|
return () => controller.abort()
|
|
80
|
-
}, [open, page, query, t])
|
|
82
|
+
}, [activeOrgId, open, page, query, t])
|
|
81
83
|
|
|
82
84
|
React.useEffect(() => {
|
|
83
85
|
if (!open) return
|
|
@@ -84,6 +84,21 @@ export function isVisible(type: ActivityType, fieldId: ScheduleFieldId): boolean
|
|
|
84
84
|
return FIELD_VISIBILITY[type].has(fieldId)
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
// Calendar-bound types must land on a concrete moment, but a task is a backlog
|
|
88
|
+
// item whose due date is optional by design (`interactions.scheduled_at` is
|
|
89
|
+
// nullable and the task lists already render an undated row) — #5941.
|
|
90
|
+
const OPTIONAL_DATE_TYPES: Set<ActivityType> = new Set(['task'])
|
|
91
|
+
|
|
92
|
+
export function isDateRequired(type: ActivityType): boolean {
|
|
93
|
+
return isVisible(type, 'date') && !OPTIONAL_DATE_TYPES.has(type)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// A time without a date is meaningless, so the time follows the date: it is
|
|
97
|
+
// only demanded for the types that must be scheduled in the first place.
|
|
98
|
+
export function isTimeRequired(type: ActivityType): boolean {
|
|
99
|
+
return isVisible(type, 'startTime') && isDateRequired(type)
|
|
100
|
+
}
|
|
101
|
+
|
|
87
102
|
export function getFieldLabel(
|
|
88
103
|
type: ActivityType,
|
|
89
104
|
fieldId: ScheduleFieldId,
|
|
@@ -3,7 +3,7 @@ export { ParticipantsField } from './ParticipantsField'
|
|
|
3
3
|
export { LocationField } from './LocationField'
|
|
4
4
|
export { FooterFields } from './FooterFields'
|
|
5
5
|
export { LinkedEntitiesField } from './LinkedEntitiesField'
|
|
6
|
-
export { isVisible, getFieldLabel, FIELD_VISIBILITY, FIELD_LABEL_OVERRIDES } from './fieldConfig'
|
|
6
|
+
export { isVisible, isDateRequired, isTimeRequired, getFieldLabel, FIELD_VISIBILITY, FIELD_LABEL_OVERRIDES } from './fieldConfig'
|
|
7
7
|
export type { ActivityType, ScheduleFieldId } from './fieldConfig'
|
|
8
8
|
export { useScheduleFormState, PARTICIPANT_COLORS } from './useScheduleFormState'
|
|
9
9
|
export type { Participant, LinkedEntity, RsvpStatus, ScheduleActivityEditData } from './useScheduleFormState'
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import type { ActivityType } from './fieldConfig'
|
|
3
|
+
import { isDateRequired } from './fieldConfig'
|
|
3
4
|
|
|
4
5
|
export type RsvpStatus = 'pending' | 'accepted' | 'declined' | 'tentative'
|
|
5
6
|
|
|
@@ -165,14 +166,20 @@ export function useScheduleFormState({ open, editData }: UseScheduleFormStatePar
|
|
|
165
166
|
// local-day convention.
|
|
166
167
|
const sourceTimestamp = editData.occurredAt ?? editData.scheduledAt ?? null
|
|
167
168
|
const seedDate = sourceTimestamp ? new Date(sourceTimestamp) : null
|
|
168
|
-
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
169
|
+
const seedDateValid = seedDate !== null && !Number.isNaN(seedDate.getTime())
|
|
170
|
+
// A real edit of an undated task must stay undated: falling back to a
|
|
171
|
+
// computed default here would silently give a backlog item a due date on
|
|
172
|
+
// the next save (#5941). Menu-driven "New Task" (no id yet, so `isEditing`
|
|
173
|
+
// is false) still gets the create-mode default so #5940's forward-seeded
|
|
174
|
+
// default keeps working.
|
|
175
|
+
if (isEditing && !seedDateValid && !isDateRequired(resolvedType)) {
|
|
176
|
+
setDate('')
|
|
177
|
+
setStartTime('')
|
|
178
|
+
} else {
|
|
179
|
+
const dateForForm = seedDateValid ? (seedDate as Date) : resolveDefaultActivityStart(resolvedType, new Date())
|
|
180
|
+
setDate(formatLocalDateInput(dateForForm))
|
|
181
|
+
setStartTime(formatLocalTimeInput(dateForForm))
|
|
182
|
+
}
|
|
176
183
|
setDuration(editData.durationMinutes ?? 30)
|
|
177
184
|
setAllDay(editData.allDay ?? false)
|
|
178
185
|
setDescription(editData.body ?? '')
|
|
@@ -253,7 +260,7 @@ export function useScheduleFormState({ open, editData }: UseScheduleFormStatePar
|
|
|
253
260
|
document.body.style.removeProperty('overflow')
|
|
254
261
|
document.body.style.removeProperty('pointer-events')
|
|
255
262
|
}
|
|
256
|
-
}, [open, editData])
|
|
263
|
+
}, [open, editData, isEditing])
|
|
257
264
|
|
|
258
265
|
// Update the Reminder default when the activity type changes in create mode.
|
|
259
266
|
// Skipped in edit mode (the persisted value wins), and gated by `open` to
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
31
31
|
import { apiCall, apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
32
32
|
import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
|
|
33
|
-
import { PhoneNumberField } from '@open-mercato/ui/backend/inputs/PhoneNumberField'
|
|
33
|
+
import { PhoneNumberField, type PhoneCountry } from '@open-mercato/ui/backend/inputs/PhoneNumberField'
|
|
34
34
|
import { isValidPhoneNumber } from '@open-mercato/shared/lib/phone'
|
|
35
35
|
import type {
|
|
36
36
|
CrudCustomFieldRenderProps,
|
|
@@ -59,7 +59,11 @@ import {
|
|
|
59
59
|
type CustomerDictionaryKind,
|
|
60
60
|
} from '../lib/dictionaries'
|
|
61
61
|
import { normalizeCustomFieldSubmitValue } from './detail/customFieldUtils'
|
|
62
|
-
import {
|
|
62
|
+
import {
|
|
63
|
+
CUSTOMER_EMAIL_INVALID_MESSAGE_KEY,
|
|
64
|
+
CUSTOMER_PHONE_INVALID_MESSAGE_KEY,
|
|
65
|
+
CUSTOMER_URL_INVALID_MESSAGE_KEY,
|
|
66
|
+
} from '../data/validators'
|
|
63
67
|
|
|
64
68
|
export const metadata = {
|
|
65
69
|
navHidden: true,
|
|
@@ -75,6 +79,17 @@ export type Translator = (
|
|
|
75
79
|
params?: Record<string, string | number>,
|
|
76
80
|
) => string
|
|
77
81
|
|
|
82
|
+
export type CustomerFormFieldOptions = {
|
|
83
|
+
/** Country pre-selected in the phone field when the value is empty. */
|
|
84
|
+
defaultCountryIso2?: string
|
|
85
|
+
/**
|
|
86
|
+
* Country list for the phone field's selector. Supply it to limit the picker
|
|
87
|
+
* to specific markets or to name the countries yourself; omitted, the field
|
|
88
|
+
* localizes its built-in list for the active locale.
|
|
89
|
+
*/
|
|
90
|
+
phoneCountries?: PhoneCountry[]
|
|
91
|
+
}
|
|
92
|
+
|
|
78
93
|
export type PersonFormValues = {
|
|
79
94
|
displayName: string
|
|
80
95
|
firstName: string
|
|
@@ -124,7 +139,7 @@ type DictionarySelectFieldProps = {
|
|
|
124
139
|
|
|
125
140
|
export { CUSTOMER_DICTIONARIES_MANAGE_HREF, getCustomerDictionaryManageHref }
|
|
126
141
|
|
|
127
|
-
const emailValidationSchema = z.string().email()
|
|
142
|
+
const emailValidationSchema = z.string().email(CUSTOMER_EMAIL_INVALID_MESSAGE_KEY)
|
|
128
143
|
const EMAIL_CHECK_DEBOUNCE_MS = 350
|
|
129
144
|
|
|
130
145
|
const createSectionHeadingField = (id: string, title: string): CrudField => ({
|
|
@@ -418,7 +433,11 @@ const companyDictionaryFieldDefinitions: DictionaryFieldDefinition[] = [
|
|
|
418
433
|
},
|
|
419
434
|
]
|
|
420
435
|
|
|
421
|
-
const createPrimaryPhoneField = (
|
|
436
|
+
const createPrimaryPhoneField = (
|
|
437
|
+
t: Translator,
|
|
438
|
+
defaultCountryIso2?: string,
|
|
439
|
+
phoneCountries?: PhoneCountry[],
|
|
440
|
+
): CrudField => ({
|
|
422
441
|
id: 'primaryPhone',
|
|
423
442
|
label: t('customers.people.form.primaryPhone'),
|
|
424
443
|
type: 'custom',
|
|
@@ -449,6 +468,7 @@ const createPrimaryPhoneField = (t: Translator, defaultCountryIso2?: string): Cr
|
|
|
449
468
|
minDigits={7}
|
|
450
469
|
onDuplicateLookup={!disabled && !error ? duplicateLookup : undefined}
|
|
451
470
|
defaultCountryIso2={defaultCountryIso2}
|
|
471
|
+
countries={phoneCountries}
|
|
452
472
|
/>
|
|
453
473
|
)
|
|
454
474
|
},
|
|
@@ -723,7 +743,7 @@ export const createPersonFormSchema = () =>
|
|
|
723
743
|
primaryEmail: z
|
|
724
744
|
.string()
|
|
725
745
|
.trim()
|
|
726
|
-
.email()
|
|
746
|
+
.email(CUSTOMER_EMAIL_INVALID_MESSAGE_KEY)
|
|
727
747
|
.optional()
|
|
728
748
|
.or(z.literal(''))
|
|
729
749
|
.transform((val) => (val === '' ? undefined : val)),
|
|
@@ -871,8 +891,9 @@ export const createDisplayNameSection = (t: Translator) =>
|
|
|
871
891
|
)
|
|
872
892
|
}
|
|
873
893
|
|
|
874
|
-
export const createPersonFormFields = (t: Translator, options?:
|
|
894
|
+
export const createPersonFormFields = (t: Translator, options?: CustomerFormFieldOptions): CrudField[] => {
|
|
875
895
|
const defaultCountryIso2 = options?.defaultCountryIso2
|
|
896
|
+
const phoneCountries = options?.phoneCountries
|
|
876
897
|
const contactSection = createSectionHeadingField('__contactInformationSection', t('customers.people.form.sections.contactInformation'))
|
|
877
898
|
const companySection = createSectionHeadingField('__companyInformationSection', t('customers.people.form.sections.companyInformation'))
|
|
878
899
|
const dictionaryFields: CrudField[] = dictionaryFieldDefinitions.map((definition) => ({
|
|
@@ -933,7 +954,7 @@ export const createPersonFormFields = (t: Translator, options?: { defaultCountry
|
|
|
933
954
|
},
|
|
934
955
|
contactSection,
|
|
935
956
|
createPrimaryEmailField(t),
|
|
936
|
-
createPrimaryPhoneField(t, defaultCountryIso2),
|
|
957
|
+
createPrimaryPhoneField(t, defaultCountryIso2, phoneCountries),
|
|
937
958
|
companySection,
|
|
938
959
|
{
|
|
939
960
|
id: 'companyEntityId',
|
|
@@ -1141,7 +1162,7 @@ export const createCompanyFormSchema = () =>
|
|
|
1141
1162
|
primaryEmail: z
|
|
1142
1163
|
.string()
|
|
1143
1164
|
.trim()
|
|
1144
|
-
.email()
|
|
1165
|
+
.email(CUSTOMER_EMAIL_INVALID_MESSAGE_KEY)
|
|
1145
1166
|
.optional()
|
|
1146
1167
|
.or(z.literal(''))
|
|
1147
1168
|
.transform((val) => (val === '' ? undefined : val)),
|
|
@@ -1228,8 +1249,9 @@ export const createCompanyFormSchema = () =>
|
|
|
1228
1249
|
})
|
|
1229
1250
|
.passthrough()
|
|
1230
1251
|
|
|
1231
|
-
export const createCompanyFormFields = (t: Translator, options?:
|
|
1252
|
+
export const createCompanyFormFields = (t: Translator, options?: CustomerFormFieldOptions): CrudField[] => {
|
|
1232
1253
|
const defaultCountryIso2 = options?.defaultCountryIso2
|
|
1254
|
+
const phoneCountries = options?.phoneCountries
|
|
1233
1255
|
const dictionaryFields: CrudField[] = companyDictionaryFieldDefinitions.map((definition) => ({
|
|
1234
1256
|
id: definition.id,
|
|
1235
1257
|
label: t(definition.labelKey),
|
|
@@ -1276,6 +1298,7 @@ export const createCompanyFormFields = (t: Translator, options?: { defaultCountr
|
|
|
1276
1298
|
invalidLabel={t('customers.people.form.primaryPhone.invalid', 'Enter a valid phone number with country code (e.g. +1 212 555 1234)')}
|
|
1277
1299
|
minDigits={7}
|
|
1278
1300
|
defaultCountryIso2={defaultCountryIso2}
|
|
1301
|
+
countries={phoneCountries}
|
|
1279
1302
|
/>
|
|
1280
1303
|
),
|
|
1281
1304
|
} as CrudField,
|
|
@@ -1569,7 +1592,7 @@ const clearableEmailField = () =>
|
|
|
1569
1592
|
z
|
|
1570
1593
|
.string()
|
|
1571
1594
|
.trim()
|
|
1572
|
-
.email()
|
|
1595
|
+
.email(CUSTOMER_EMAIL_INVALID_MESSAGE_KEY)
|
|
1573
1596
|
.optional()
|
|
1574
1597
|
.or(z.literal(''))
|
|
1575
1598
|
.transform((val) => (val === '' ? null : val))
|
|
@@ -1657,7 +1680,7 @@ const buildIndustryLabels = (t: Translator): DictionarySelectLabels => ({
|
|
|
1657
1680
|
manageTitle: t('customers.people.form.dictionary.manage'),
|
|
1658
1681
|
})
|
|
1659
1682
|
|
|
1660
|
-
export const createCompanyEditFields = (t: Translator, options?:
|
|
1683
|
+
export const createCompanyEditFields = (t: Translator, options?: CustomerFormFieldOptions): CrudField[] => {
|
|
1661
1684
|
const baseFields = createCompanyFormFields(t, options)
|
|
1662
1685
|
const industryLabels = buildIndustryLabels(t)
|
|
1663
1686
|
|
|
@@ -1682,7 +1705,7 @@ export const createCompanyEditFields = (t: Translator, options?: { defaultCountr
|
|
|
1682
1705
|
})
|
|
1683
1706
|
}
|
|
1684
1707
|
|
|
1685
|
-
export const createPersonEditFields = (t: Translator, options?:
|
|
1708
|
+
export const createPersonEditFields = (t: Translator, options?: CustomerFormFieldOptions): CrudField[] => {
|
|
1686
1709
|
const baseFields = createPersonFormFields(t, options)
|
|
1687
1710
|
return [
|
|
1688
1711
|
...baseFields,
|
|
@@ -6,6 +6,7 @@ import { dictionaryEntrySortModeSchema } from '@open-mercato/core/modules/dictio
|
|
|
6
6
|
const uuid = () => z.string().uuid()
|
|
7
7
|
|
|
8
8
|
export const CUSTOMER_PHONE_INVALID_MESSAGE_KEY = 'customers.people.form.primaryPhone.invalid'
|
|
9
|
+
export const CUSTOMER_EMAIL_INVALID_MESSAGE_KEY = 'customers.people.form.primaryEmail.invalid'
|
|
9
10
|
export const CUSTOMER_URL_INVALID_MESSAGE_KEY = 'customers.people.form.websiteUrl.invalid'
|
|
10
11
|
export const ACTIVITY_DATE_REQUIRED_MESSAGE_KEY = 'customers.activities.errors.dateRequired'
|
|
11
12
|
export const ACTIVITY_TIME_REQUIRED_MESSAGE_KEY = 'customers.activities.errors.timeRequired'
|
|
@@ -37,7 +38,7 @@ const phoneSchema = z.preprocess(
|
|
|
37
38
|
|
|
38
39
|
const clearableEmailSchema = z.preprocess(
|
|
39
40
|
emptyStringToNull,
|
|
40
|
-
z.string().email().max(320).nullable().optional(),
|
|
41
|
+
z.string().email(CUSTOMER_EMAIL_INVALID_MESSAGE_KEY).max(320).nullable().optional(),
|
|
41
42
|
)
|
|
42
43
|
|
|
43
44
|
const clearableUrlSchema = z.preprocess(
|
|
@@ -219,8 +220,8 @@ export const activityCreateSchema = scopedSchema.extend({
|
|
|
219
220
|
activityType: z.string().min(1).max(100),
|
|
220
221
|
subject: z.string().max(200).optional(),
|
|
221
222
|
body: z.string().max(8000).optional(),
|
|
222
|
-
date: z.string().trim().min(1, ACTIVITY_DATE_REQUIRED_MESSAGE_KEY).optional(),
|
|
223
|
-
time: z.string().trim().min(1, ACTIVITY_TIME_REQUIRED_MESSAGE_KEY).optional(),
|
|
223
|
+
date: z.string().trim().min(1, ACTIVITY_DATE_REQUIRED_MESSAGE_KEY).optional().nullable(),
|
|
224
|
+
time: z.string().trim().min(1, ACTIVITY_TIME_REQUIRED_MESSAGE_KEY).optional().nullable(),
|
|
224
225
|
phoneNumber: interactionPhoneNumberSchema,
|
|
225
226
|
occurredAt: z.coerce.date().optional(),
|
|
226
227
|
dealId: uuid().optional(),
|
|
@@ -500,8 +501,10 @@ const interactionCreateBaseSchema = scopedSchema.extend({
|
|
|
500
501
|
// rows, external writers, and the dispatch-crm MCP keep working. Open/terminal semantics
|
|
501
502
|
// live in lib/interactionStatus.ts, not in this validator.
|
|
502
503
|
status: z.string().max(50).optional().default('planned'),
|
|
503
|
-
|
|
504
|
-
|
|
504
|
+
// Nullable like the sibling `scheduledAt` below: an undated activity (a
|
|
505
|
+
// backlog task) says "no date" with an explicit null, not by omission (#5941).
|
|
506
|
+
date: z.string().trim().min(1, ACTIVITY_DATE_REQUIRED_MESSAGE_KEY).optional().nullable(),
|
|
507
|
+
time: z.string().trim().min(1, ACTIVITY_TIME_REQUIRED_MESSAGE_KEY).optional().nullable(),
|
|
505
508
|
phoneNumber: interactionPhoneNumberSchema,
|
|
506
509
|
scheduledAt: z.coerce.date().optional().nullable(),
|
|
507
510
|
occurredAt: z.coerce.date().optional().nullable(),
|
|
@@ -515,7 +518,7 @@ const interactionCreateBaseSchema = scopedSchema.extend({
|
|
|
515
518
|
...interactionExtendedFields,
|
|
516
519
|
})
|
|
517
520
|
|
|
518
|
-
function deriveScheduledAtFromDateTime(date?: string, time?: string): Date | null {
|
|
521
|
+
function deriveScheduledAtFromDateTime(date?: string | null, time?: string | null): Date | null {
|
|
519
522
|
if (!date || typeof date !== 'string') return null
|
|
520
523
|
const trimmedDate = date.trim()
|
|
521
524
|
if (!trimmedDate) return null
|
|
@@ -567,8 +570,8 @@ const interactionUpdateBaseSchema = z
|
|
|
567
570
|
title: z.string().trim().max(500).optional().nullable(),
|
|
568
571
|
body: z.string().trim().max(10000).optional().nullable(),
|
|
569
572
|
status: z.string().max(50).optional(),
|
|
570
|
-
date: z.string().trim().min(1, ACTIVITY_DATE_REQUIRED_MESSAGE_KEY).optional(),
|
|
571
|
-
time: z.string().trim().min(1, ACTIVITY_TIME_REQUIRED_MESSAGE_KEY).optional(),
|
|
573
|
+
date: z.string().trim().min(1, ACTIVITY_DATE_REQUIRED_MESSAGE_KEY).optional().nullable(),
|
|
574
|
+
time: z.string().trim().min(1, ACTIVITY_TIME_REQUIRED_MESSAGE_KEY).optional().nullable(),
|
|
572
575
|
phoneNumber: interactionPhoneNumberSchema,
|
|
573
576
|
scheduledAt: z.coerce.date().optional().nullable(),
|
|
574
577
|
occurredAt: z.coerce.date().optional().nullable(),
|
|
@@ -608,6 +611,9 @@ export const interactionUpdateSchema = interactionUpdateBaseSchema
|
|
|
608
611
|
// the update doesn't silently leave `scheduled_at` stale.
|
|
609
612
|
.transform((value) => {
|
|
610
613
|
if (value.scheduledAt !== undefined) return value
|
|
614
|
+
// An explicit `date: null` is how a caller drops the due date; mirror it
|
|
615
|
+
// onto `scheduledAt` rather than leaving the old timestamp behind (#5941).
|
|
616
|
+
if (value.date === null) return { ...value, scheduledAt: null }
|
|
611
617
|
if (!value.date && !value.time) return value
|
|
612
618
|
const derived = deriveScheduledAtFromDateTime(value.date, value.time)
|
|
613
619
|
return derived ? { ...value, scheduledAt: derived } : value
|
|
@@ -2337,6 +2337,7 @@
|
|
|
2337
2337
|
"customers.people.form.phoneDuplicateNotice": "Diese Telefonnummer ist bereits {{name}} zugeordnet.",
|
|
2338
2338
|
"customers.people.form.preferredName": "Bevorzugter Name",
|
|
2339
2339
|
"customers.people.form.primaryEmail": "E-Mail",
|
|
2340
|
+
"customers.people.form.primaryEmail.invalid": "Geben Sie eine gültige E-Mail-Adresse ein (z. B. name@beispiel.de)",
|
|
2340
2341
|
"customers.people.form.primaryEmailPlaceholder": "name@beispiel.de",
|
|
2341
2342
|
"customers.people.form.primaryPhone": "Telefon",
|
|
2342
2343
|
"customers.people.form.primaryPhone.invalid": "Enter a valid phone number (digits, spaces, dashes, parentheses)",
|
|
@@ -2337,6 +2337,7 @@
|
|
|
2337
2337
|
"customers.people.form.phoneDuplicateNotice": "This phone number is already assigned to {{name}}.",
|
|
2338
2338
|
"customers.people.form.preferredName": "Preferred name",
|
|
2339
2339
|
"customers.people.form.primaryEmail": "Primary email",
|
|
2340
|
+
"customers.people.form.primaryEmail.invalid": "Enter a valid email address (e.g. name@example.com)",
|
|
2340
2341
|
"customers.people.form.primaryEmailPlaceholder": "name@example.com",
|
|
2341
2342
|
"customers.people.form.primaryPhone": "Primary phone",
|
|
2342
2343
|
"customers.people.form.primaryPhone.invalid": "Enter a valid phone number with country code (e.g. +1 212 555 1234)",
|
|
@@ -2337,6 +2337,7 @@
|
|
|
2337
2337
|
"customers.people.form.phoneDuplicateNotice": "Este número de teléfono ya está asignado a {{name}}.",
|
|
2338
2338
|
"customers.people.form.preferredName": "Nombre preferido",
|
|
2339
2339
|
"customers.people.form.primaryEmail": "Correo electrónico",
|
|
2340
|
+
"customers.people.form.primaryEmail.invalid": "Introduce una dirección de correo electrónico válida (p. ej., nombre@ejemplo.com)",
|
|
2340
2341
|
"customers.people.form.primaryEmailPlaceholder": "nombre@ejemplo.com",
|
|
2341
2342
|
"customers.people.form.primaryPhone": "Teléfono",
|
|
2342
2343
|
"customers.people.form.primaryPhone.invalid": "Enter a valid phone number (digits, spaces, dashes, parentheses)",
|
|
@@ -2337,6 +2337,7 @@
|
|
|
2337
2337
|
"customers.people.form.phoneDuplicateNotice": "이 전화번호는 이미 {{name}}에 지정되어 있습니다.",
|
|
2338
2338
|
"customers.people.form.preferredName": "선호하는 이름",
|
|
2339
2339
|
"customers.people.form.primaryEmail": "기본 이메일",
|
|
2340
|
+
"customers.people.form.primaryEmail.invalid": "유효한 이메일 주소를 입력하세요 (예: name@example.com)",
|
|
2340
2341
|
"customers.people.form.primaryEmailPlaceholder": "name@example.com",
|
|
2341
2342
|
"customers.people.form.primaryPhone": "기본 전화번호",
|
|
2342
2343
|
"customers.people.form.primaryPhone.invalid": "국가 코드를 포함한 유효한 전화번호를 입력하세요 (예: +1 212 555 1234)",
|
|
@@ -2337,6 +2337,7 @@
|
|
|
2337
2337
|
"customers.people.form.phoneDuplicateNotice": "Ten numer telefonu jest już przypisany do {{name}}.",
|
|
2338
2338
|
"customers.people.form.preferredName": "Preferowane imię",
|
|
2339
2339
|
"customers.people.form.primaryEmail": "E-mail",
|
|
2340
|
+
"customers.people.form.primaryEmail.invalid": "Wprowadź prawidłowy adres e-mail (np. nazwa@example.com)",
|
|
2340
2341
|
"customers.people.form.primaryEmailPlaceholder": "nazwa@example.com",
|
|
2341
2342
|
"customers.people.form.primaryPhone": "Telefon",
|
|
2342
2343
|
"customers.people.form.primaryPhone.invalid": "Enter a valid phone number (digits, spaces, dashes, parentheses)",
|