@open-mercato/ui 0.6.2-develop.3461.1.605f31c2c9 → 0.6.2

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 (86) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/ai/AiChat.js +130 -22
  3. package/dist/ai/AiChat.js.map +2 -2
  4. package/dist/ai/AiChatSessions.js +73 -3
  5. package/dist/ai/AiChatSessions.js.map +2 -2
  6. package/dist/ai/conversation-store.js +149 -0
  7. package/dist/ai/conversation-store.js.map +7 -0
  8. package/dist/ai/parts/MutationPreviewCard.js +4 -3
  9. package/dist/ai/parts/MutationPreviewCard.js.map +2 -2
  10. package/dist/ai/parts/MutationResultCard.js +9 -16
  11. package/dist/ai/parts/MutationResultCard.js.map +2 -2
  12. package/dist/ai/useAiChat.js +80 -4
  13. package/dist/ai/useAiChat.js.map +2 -2
  14. package/dist/backend/AuthSessionGuard.js +99 -0
  15. package/dist/backend/AuthSessionGuard.js.map +7 -0
  16. package/dist/backend/CrudForm.js +2 -1
  17. package/dist/backend/CrudForm.js.map +2 -2
  18. package/dist/backend/FilterOverlay.js +1 -1
  19. package/dist/backend/FilterOverlay.js.map +1 -1
  20. package/dist/backend/dashboard/widgetRegistry.js +2 -1
  21. package/dist/backend/dashboard/widgetRegistry.js.map +2 -2
  22. package/dist/backend/date-range/dateRanges.js +16 -18
  23. package/dist/backend/date-range/dateRanges.js.map +2 -2
  24. package/dist/backend/detail/AttachmentMetadataDialog.js +3 -1
  25. package/dist/backend/detail/AttachmentMetadataDialog.js.map +2 -2
  26. package/dist/backend/detail/InlineEditors.js +1 -1
  27. package/dist/backend/detail/InlineEditors.js.map +1 -1
  28. package/dist/backend/injection/useAppEvent.js +2 -7
  29. package/dist/backend/injection/useAppEvent.js.map +2 -2
  30. package/dist/backend/injection/widgetRegistry.js +2 -1
  31. package/dist/backend/injection/widgetRegistry.js.map +2 -2
  32. package/dist/backend/messages/useMessageCompose.js +62 -5
  33. package/dist/backend/messages/useMessageCompose.js.map +2 -2
  34. package/dist/backend/messages/useMessageComposeOperations.js +64 -5
  35. package/dist/backend/messages/useMessageComposeOperations.js.map +2 -2
  36. package/dist/backend/schedule/ScheduleCalendar.js +11 -1
  37. package/dist/backend/schedule/ScheduleCalendar.js.map +2 -2
  38. package/dist/backend/schedule/ScheduleToolbar.js +11 -1
  39. package/dist/backend/schedule/ScheduleToolbar.js.map +2 -2
  40. package/dist/primitives/calendar.js +1 -1
  41. package/dist/primitives/calendar.js.map +1 -1
  42. package/dist/primitives/date-format.js +1 -1
  43. package/dist/primitives/date-format.js.map +1 -1
  44. package/dist/primitives/date-picker-helpers.js +7 -9
  45. package/dist/primitives/date-picker-helpers.js.map +2 -2
  46. package/dist/primitives/date-picker.js +1 -1
  47. package/dist/primitives/date-picker.js.map +2 -2
  48. package/dist/primitives/date-range-picker.js +1 -1
  49. package/dist/primitives/date-range-picker.js.map +2 -2
  50. package/jest.config.cjs +4 -1
  51. package/package.json +6 -7
  52. package/src/ai/AiChat.tsx +138 -22
  53. package/src/ai/AiChatSessions.tsx +89 -9
  54. package/src/ai/__tests__/AiAssistantLauncher.test.tsx +1 -1
  55. package/src/ai/__tests__/AiChat.conversation.test.tsx +86 -0
  56. package/src/ai/__tests__/AiChat.test.tsx +17 -2
  57. package/src/ai/__tests__/conversation-store.test.ts +29 -0
  58. package/src/ai/conversation-store.ts +246 -0
  59. package/src/ai/parts/MutationPreviewCard.tsx +25 -20
  60. package/src/ai/parts/MutationResultCard.tsx +15 -27
  61. package/src/ai/parts/__tests__/MutationPreviewCard.test.tsx +26 -1
  62. package/src/ai/parts/__tests__/MutationResultCard.test.tsx +16 -1
  63. package/src/ai/useAiChat.ts +97 -8
  64. package/src/backend/AuthSessionGuard.tsx +114 -0
  65. package/src/backend/CrudForm.tsx +3 -2
  66. package/src/backend/FilterOverlay.tsx +1 -1
  67. package/src/backend/__tests__/AppShell.test.tsx +1 -1
  68. package/src/backend/__tests__/AuthSessionGuard.test.tsx +150 -0
  69. package/src/backend/__tests__/SidebarCustomizationEditor.test.tsx +8 -1
  70. package/src/backend/dashboard/widgetRegistry.ts +2 -1
  71. package/src/backend/date-range/dateRanges.ts +16 -18
  72. package/src/backend/detail/AttachmentMetadataDialog.tsx +1 -1
  73. package/src/backend/detail/InlineEditors.tsx +1 -1
  74. package/src/backend/injection/__tests__/useAppEvent.test.ts +16 -0
  75. package/src/backend/injection/useAppEvent.ts +2 -9
  76. package/src/backend/injection/widgetRegistry.ts +2 -1
  77. package/src/backend/messages/__tests__/MessageComposer.test.tsx +77 -0
  78. package/src/backend/messages/useMessageCompose.ts +64 -5
  79. package/src/backend/messages/useMessageComposeOperations.ts +70 -4
  80. package/src/backend/schedule/ScheduleCalendar.tsx +11 -1
  81. package/src/backend/schedule/ScheduleToolbar.tsx +11 -1
  82. package/src/primitives/calendar.tsx +2 -2
  83. package/src/primitives/date-format.ts +2 -2
  84. package/src/primitives/date-picker-helpers.ts +7 -9
  85. package/src/primitives/date-picker.tsx +2 -2
  86. package/src/primitives/date-range-picker.tsx +2 -2
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/messages/useMessageCompose.ts"],
4
- "sourcesContent": ["import * as React from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { flash } from '../FlashMessages'\nimport { apiCall } from '../utils/apiCall'\nimport type {\n AttachmentListResponse,\n MessageComposerProps,\n MessageTypeItem,\n UserListItem,\n} from './message-composer.types'\nimport type { MessagePriority } from './message-priority'\nimport type { TagsInputOption } from '../inputs/TagsInput'\nimport {\n useComposeDraftOperation,\n useComposeSendOperation,\n useForwardSubmitOperation,\n useReplySubmitOperation,\n} from './useMessageComposeOperations'\n\nfunction toErrorMessage(payload: unknown): string | null {\n if (!payload) return null\n if (typeof payload === 'string') return payload\n if (Array.isArray(payload)) {\n for (const item of payload) {\n const nested = toErrorMessage(item)\n if (nested) return nested\n }\n return null\n }\n if (typeof payload === 'object') {\n const record = payload as Record<string, unknown>\n return (\n toErrorMessage(record.error)\n ?? toErrorMessage(record.message)\n ?? toErrorMessage(record.detail)\n ?? toErrorMessage(record.details)\n ?? null\n )\n }\n return null\n}\n\nfunction createTemporaryAttachmentRecordId(): string {\n const randomPart =\n typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'\n ? crypto.randomUUID()\n : `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`\n return `messages-composer:${randomPart}`\n}\n\nexport type UseMessageComposeParams = MessageComposerProps\n\nexport type UseMessageComposeResult = {\n t: ReturnType<typeof useT>\n variant: NonNullable<MessageComposerProps['variant']>\n messageId?: string\n open?: boolean\n inline: boolean\n contextPreview: React.ReactNode\n isOpen: boolean\n messageTypes: MessageTypeItem[]\n createableMessageTypes: MessageTypeItem[]\n normalizedRequiredActionMode: 'none' | 'optional' | 'required'\n contextActionOptions: Array<{ id: string; label: string }>\n shouldShowContextActions: boolean\n isComposePublicVisibility: boolean\n attachmentEntityId: string\n attachmentRecordId: string\n recipientIds: string[]\n setRecipientIds: React.Dispatch<React.SetStateAction<string[]>>\n messageType: string\n setMessageType: React.Dispatch<React.SetStateAction<string>>\n subject: string\n setSubject: React.Dispatch<React.SetStateAction<string>>\n body: string\n setBody: React.Dispatch<React.SetStateAction<string>>\n bodyFormat: 'text' | 'markdown'\n setBodyFormat: React.Dispatch<React.SetStateAction<'text' | 'markdown'>>\n priority: MessagePriority\n setPriority: React.Dispatch<React.SetStateAction<MessagePriority>>\n visibility: 'public' | 'internal'\n setVisibility: React.Dispatch<React.SetStateAction<'public' | 'internal'>>\n externalEmail: string\n setExternalEmail: React.Dispatch<React.SetStateAction<string>>\n sendViaEmail: boolean\n setSendViaEmail: React.Dispatch<React.SetStateAction<boolean>>\n contextActionRequired: boolean\n setContextActionRequired: React.Dispatch<React.SetStateAction<boolean>>\n contextActionType: string\n setContextActionType: React.Dispatch<React.SetStateAction<string>>\n replyAll: boolean\n setReplyAll: React.Dispatch<React.SetStateAction<boolean>>\n includeAttachments: boolean\n setIncludeAttachments: React.Dispatch<React.SetStateAction<boolean>>\n submitting: boolean\n submitMode: 'send' | 'draft'\n submitError: string | null\n composerTitle: string\n submitLabel: string\n selectedRecipientOptions: TagsInputOption[]\n resolveRecipientLabel: (id: string) => string\n loadRecipientSuggestions: (query?: string) => Promise<TagsInputOption[]>\n loadAttachmentIds: () => Promise<string[]>\n handleSaveDraft: () => void\n handleBack: () => void\n handleSubmit: ({ saveAsDraft }?: { saveAsDraft?: boolean }) => Promise<boolean>\n handleDialogOpenChange: (nextOpen: boolean) => void\n handleKeyDown: (event: React.KeyboardEvent<HTMLDivElement>) => void\n}\n\ntype ForwardPreviewResponse = {\n subject?: string\n body?: string\n}\n\nexport function useMessageCompose({\n variant: variantProp = 'compose',\n messageId,\n open,\n onOpenChange,\n inline = false,\n lockedType = null,\n contextObject = null,\n requiredActionConfig = null,\n contextPreview = null,\n defaultValues,\n onSuccess,\n onCancel,\n}: UseMessageComposeParams): UseMessageComposeResult {\n const t = useT()\n const variant = variantProp\n const isOpen = inline ? true : Boolean(open)\n const recipientSuggestionsCacheRef = React.useRef<TagsInputOption[] | null>(null)\n\n const [recipientIds, setRecipientIds] = React.useState<string[]>([])\n const [recipientMap, setRecipientMap] = React.useState<Record<string, TagsInputOption>>({})\n const [messageType, setMessageType] = React.useState(lockedType ?? 'default')\n const [subject, setSubject] = React.useState('')\n const [body, setBody] = React.useState('')\n const [bodyFormat, setBodyFormat] = React.useState<'text' | 'markdown'>('text')\n const [priority, setPriority] = React.useState<MessagePriority>('normal')\n const [visibility, setVisibility] = React.useState<'public' | 'internal'>('internal')\n const [externalEmail, setExternalEmail] = React.useState('')\n const [attachmentIds, setAttachmentIds] = React.useState<string[]>([])\n const [sendViaEmail, setSendViaEmail] = React.useState(false)\n const [contextActionRequired, setContextActionRequired] = React.useState(false)\n const [contextActionType, setContextActionType] = React.useState('')\n const [replyAll, setReplyAll] = React.useState(false)\n const [includeAttachments, setIncludeAttachments] = React.useState(true)\n const [temporaryAttachmentRecordId, setTemporaryAttachmentRecordId] = React.useState<string>(() =>\n createTemporaryAttachmentRecordId(),\n )\n const [submitting, setSubmitting] = React.useState(false)\n const [submitMode, setSubmitMode] = React.useState<'send' | 'draft'>('send')\n const [submitError, setSubmitError] = React.useState<string | null>(null)\n // Tracks whether the composer is currently in the \"open\" lifecycle so the init\n // effect below only runs on the closed \u2192 open transition, not on every parent\n // re-render that produces a new `defaultValues` / `contextObject` reference\n // while the user is typing. Without this guard, an inline literal\n // `defaultValues={{...}}` in a re-rendering parent (e.g. message detail page\n // with live notification badges or queue progress) would clear the body /\n // subject mid-keystroke. CI shard 9 surfaced this as TC-MSG-009 timing out\n // because `keyboard.type` characters appeared to \"type nowhere\" \u2014 they were\n // typed correctly, then immediately wiped by the next effect run.\n const isOpenRef = React.useRef(false)\n const wasOpenRef = React.useRef(false)\n const submitLockReleaseRef = React.useRef<(() => void) | null>(null)\n\n const messageTypesQuery = useQuery({\n queryKey: ['messages', 'types'],\n enabled: variant === 'compose' && isOpen,\n staleTime: 5 * 60 * 1000,\n queryFn: async () => {\n const call = await apiCall<{ items?: MessageTypeItem[] }>('/api/messages/types')\n if (!call.ok) {\n throw new Error(\n toErrorMessage(call.result)\n ?? t('messages.errors.loadTypesFailed', 'Failed to load message types.'),\n )\n }\n return Array.isArray(call.result?.items) ? call.result?.items ?? [] : []\n },\n })\n\n const messageTypes = React.useMemo(\n () => messageTypesQuery.data ?? [],\n [messageTypesQuery.data],\n )\n const createableMessageTypes = React.useMemo(\n () => messageTypes.filter((item) => item.isCreateableByUser !== false),\n [messageTypes],\n )\n const normalizedRequiredActionMode = requiredActionConfig?.mode ?? 'none'\n const contextActionOptions = React.useMemo(\n () => (requiredActionConfig?.options ?? []).filter((option) => option.id.trim().length > 0),\n [requiredActionConfig?.options],\n )\n const shouldShowContextActions = (\n variant === 'compose'\n && Boolean(contextObject)\n && normalizedRequiredActionMode !== 'none'\n && contextActionOptions.length > 0\n )\n\n const isComposePublicVisibility = variant === 'compose' && visibility === 'public'\n\n const attachmentEntityId = variant === 'compose' && messageId ? 'messages:message' : 'attachments:library'\n const attachmentRecordId = variant === 'compose' && messageId ? messageId : temporaryAttachmentRecordId\n\n const loadAttachmentIds = React.useCallback(async (): Promise<string[]> => {\n const params = new URLSearchParams()\n params.set('entityId', attachmentEntityId)\n params.set('recordId', attachmentRecordId)\n\n const call = await apiCall<AttachmentListResponse>(`/api/attachments?${params.toString()}`)\n if (!call.ok) {\n throw new Error(\n toErrorMessage(call.result)\n ?? t('messages.errors.loadAttachmentOptionsFailed', 'Failed to load attachments.'),\n )\n }\n\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n const nextIds = items\n .map((item) => (typeof item?.id === 'string' ? item.id : ''))\n .filter((id) => id.length > 0)\n\n setAttachmentIds(nextIds)\n return nextIds\n }, [attachmentEntityId, attachmentRecordId, t])\n\n React.useEffect(() => {\n if (!isOpen) {\n isOpenRef.current = false\n return\n }\n // Only initialize on the closed \u2192 open transition. Subsequent parent\n // re-renders that change `defaultValues` / `contextObject` references\n // (inline object literals are a new reference on every render) MUST NOT\n // overwrite state the user has typed in.\n if (isOpenRef.current) return\n isOpenRef.current = true\n\n const nextRecipients = defaultValues?.recipients?.filter((value) => typeof value === 'string' && value.trim().length > 0) ?? []\n const dedupedRecipients = Array.from(new Set(nextRecipients))\n\n setRecipientIds(dedupedRecipients)\n setMessageType(lockedType ?? defaultValues?.type ?? 'default')\n setSubject(defaultValues?.subject ?? '')\n setBody(defaultValues?.body ?? '')\n setBodyFormat(defaultValues?.bodyFormat ?? 'text')\n setPriority(defaultValues?.priority ?? 'normal')\n setVisibility(defaultValues?.visibility ?? 'internal')\n setExternalEmail(defaultValues?.externalEmail ?? '')\n setAttachmentIds(\n Array.isArray(defaultValues?.attachmentIds)\n ? defaultValues.attachmentIds.filter((id): id is string => typeof id === 'string' && id.trim().length > 0)\n : [],\n )\n setSendViaEmail(Boolean(defaultValues?.sendViaEmail))\n if (contextObject) {\n const defaultContextActionType = requiredActionConfig?.defaultActionType?.trim() ?? ''\n const fallbackContextActionType = contextObject.actionType?.trim() ?? ''\n const selectedActionType = defaultContextActionType || fallbackContextActionType\n const selectedActionAllowed = contextActionOptions.some((option) => option.id === selectedActionType)\n const nextActionType = selectedActionAllowed ? selectedActionType : ''\n setContextActionType(nextActionType)\n if (normalizedRequiredActionMode === 'required') {\n setContextActionRequired(true)\n } else if (normalizedRequiredActionMode === 'optional') {\n setContextActionRequired(Boolean(nextActionType) || Boolean(contextObject.actionRequired))\n } else {\n setContextActionRequired(Boolean(contextObject.actionRequired))\n }\n } else {\n setContextActionType('')\n setContextActionRequired(false)\n }\n setReplyAll(Boolean(defaultValues?.replyAll))\n setIncludeAttachments(defaultValues?.includeAttachments !== false)\n setTemporaryAttachmentRecordId(createTemporaryAttachmentRecordId())\n setSubmitError(null)\n }, [\n contextActionOptions,\n contextObject,\n defaultValues,\n isOpen,\n lockedType,\n normalizedRequiredActionMode,\n requiredActionConfig?.defaultActionType,\n ])\n\n React.useEffect(() => {\n if (!isOpen) {\n submitLockReleaseRef.current?.()\n submitLockReleaseRef.current = null\n wasOpenRef.current = false\n return\n }\n\n const justOpened = isOpen && !wasOpenRef.current\n wasOpenRef.current = isOpen\n if (!justOpened) return\n submitLockReleaseRef.current?.()\n submitLockReleaseRef.current = null\n setSubmitting(false)\n setSubmitMode('send')\n }, [isOpen])\n\n React.useEffect(() => () => {\n submitLockReleaseRef.current?.()\n submitLockReleaseRef.current = null\n }, [])\n\n React.useEffect(() => {\n if (!isOpen) return\n if (variant !== 'forward') return\n if (!messageId) return\n\n let isActive = true\n\n void (async () => {\n const call = await apiCall<ForwardPreviewResponse>(`/api/messages/${encodeURIComponent(messageId)}/forward-preview`)\n if (!isActive) return\n\n if (!call.ok) {\n const message = toErrorMessage(call.result)\n ?? t('messages.errors.forwardPreviewFailed', 'Failed to load forward preview.')\n setSubmitError(message)\n flash(message, 'error')\n return\n }\n\n if (typeof call.result?.subject === 'string') {\n setSubject((previousValue) => (previousValue.trim().length > 0 ? previousValue : call.result?.subject ?? ''))\n }\n if (typeof call.result?.body === 'string') {\n setBody((previousValue) => (previousValue.trim().length > 0 ? previousValue : call.result?.body ?? ''))\n }\n setBodyFormat('text')\n })().catch((error) => {\n if (!isActive) return\n const message = error instanceof Error\n ? error.message\n : t('messages.errors.forwardPreviewFailed', 'Failed to load forward preview.')\n setSubmitError(message)\n flash(message, 'error')\n })\n\n return () => {\n isActive = false\n }\n }, [isOpen, messageId, t, variant])\n\n React.useEffect(() => {\n if (!isOpen) return\n if (variant !== 'compose' && variant !== 'reply') return\n void loadAttachmentIds().catch(() => null)\n }, [isOpen, loadAttachmentIds, variant])\n\n React.useEffect(() => {\n if (variant !== 'compose') return\n if (!createableMessageTypes.length) return\n\n if (lockedType) {\n if (createableMessageTypes.some((item) => item.type === lockedType)) {\n setMessageType(lockedType)\n return\n }\n const defaultType = createableMessageTypes.find((item) => item.type === 'default')\n setMessageType(defaultType?.type ?? createableMessageTypes[0]?.type ?? 'default')\n return\n }\n\n if (createableMessageTypes.some((item) => item.type === messageType)) return\n\n const defaultType = createableMessageTypes.find((item) => item.type === 'default')\n setMessageType(defaultType?.type ?? createableMessageTypes[0]?.type ?? 'default')\n }, [createableMessageTypes, lockedType, messageType, variant])\n\n React.useEffect(() => {\n if (variant !== 'compose') return\n if (visibility !== 'public') return\n setSendViaEmail(true)\n setRecipientIds([])\n }, [variant, visibility])\n\n React.useEffect(() => {\n if (isOpen) return\n recipientSuggestionsCacheRef.current = null\n }, [isOpen])\n\n const resolveRecipientLabel = React.useCallback((id: string) => {\n return recipientMap[id]?.label ?? id\n }, [recipientMap])\n\n const selectedRecipientOptions = React.useMemo(() => {\n return recipientIds.map((id) => recipientMap[id] ?? { value: id, label: id })\n }, [recipientIds, recipientMap])\n\n const loadRecipientSuggestions = React.useCallback(async (_query?: string) => {\n const cachedOptions = recipientSuggestionsCacheRef.current\n if (cachedOptions) {\n return cachedOptions\n }\n\n const params = new URLSearchParams()\n params.set('page', '1')\n params.set('pageSize', '100')\n // Recipient lookup is filtered in TagsInput because incremental auth user search is unreliable.\n\n const call = await apiCall<{ items?: UserListItem[] }>(`/api/auth/users?${params.toString()}`)\n if (!call.ok) {\n return []\n }\n\n const rawItems = Array.isArray(call.result?.items) ? call.result?.items ?? [] : []\n const options: TagsInputOption[] = []\n for (const item of rawItems) {\n if (!item || typeof item !== 'object') continue\n const id = typeof item.id === 'string' ? item.id : ''\n if (!id) continue\n\n const email = typeof item.email === 'string' && item.email.trim().length ? item.email.trim() : id\n const name = typeof item.name === 'string' && item.name.trim().length ? item.name.trim() : undefined\n\n options.push({\n value: id,\n label: email,\n description: name,\n })\n }\n\n if (options.length) {\n setRecipientMap((prev) => {\n const next = { ...prev }\n for (const option of options) {\n next[option.value] = option\n }\n return next\n })\n }\n\n recipientSuggestionsCacheRef.current = options\n return options\n }, [])\n\n const handleCancel = React.useCallback(() => {\n if (submitting) return\n if (!inline) {\n onOpenChange?.(false)\n }\n onCancel?.()\n }, [inline, onCancel, onOpenChange, submitting])\n\n const composeSendOperation = useComposeSendOperation({\n t,\n messageType,\n createableMessageTypes,\n priority,\n visibility,\n externalEmail,\n recipientIds,\n subject,\n body,\n bodyFormat,\n sendViaEmail,\n contextObject,\n defaultValues,\n contextActionOptions,\n normalizedRequiredActionMode,\n shouldShowContextActions,\n contextActionRequired,\n contextActionType,\n })\n\n const composeDraftOperation = useComposeDraftOperation({\n t,\n messageType,\n priority,\n visibility,\n externalEmail,\n recipientIds,\n subject,\n body,\n bodyFormat,\n sendViaEmail,\n contextObject,\n defaultValues,\n contextActionOptions,\n normalizedRequiredActionMode,\n shouldShowContextActions,\n contextActionRequired,\n contextActionType,\n })\n\n const replyOperation = useReplySubmitOperation({\n t,\n messageId,\n body,\n bodyFormat,\n replyAll,\n recipientIds,\n sendViaEmail,\n })\n\n const forwardOperation = useForwardSubmitOperation({\n t,\n messageId,\n recipientIds,\n body,\n includeAttachments,\n sendViaEmail,\n })\n\n const handleSubmit = React.useCallback(async ({ saveAsDraft = false }: { saveAsDraft?: boolean } = {}) => {\n if (submitting) return false\n\n setSubmitError(null)\n\n const isComposeDraftSubmit = saveAsDraft && variant === 'compose'\n const operation = isComposeDraftSubmit\n ? composeDraftOperation\n : variant === 'compose'\n ? composeSendOperation\n : variant === 'reply'\n ? replyOperation\n : forwardOperation\n\n const validationMessage = operation.validate()\n if (validationMessage) {\n setSubmitError(validationMessage)\n flash(validationMessage, 'error')\n return false\n }\n\n setSubmitMode(isComposeDraftSubmit ? 'draft' : 'send')\n setSubmitting(true)\n let keepSubmitLock = false\n let shouldReturnFalse = false\n\n try {\n let nextAttachmentIds = attachmentIds\n if (operation.requiresAttachmentRefresh) {\n try {\n nextAttachmentIds = await loadAttachmentIds()\n } catch (error) {\n const message = error instanceof Error\n ? error.message\n : t('messages.errors.loadAttachmentOptionsFailed', 'Failed to load attachments.')\n setSubmitError(message)\n flash(message, 'error')\n shouldReturnFalse = true\n }\n }\n\n if (!shouldReturnFalse) {\n const { endpoint, payload } = operation.buildRequest({ attachmentIds: nextAttachmentIds })\n\n const call = await apiCall<{ id?: string }>(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n\n if (!call.ok) {\n const message = toErrorMessage(call.result) ?? t('messages.errors.sendFailed', 'Failed to send message.')\n setSubmitError(message)\n flash(message, 'error')\n shouldReturnFalse = true\n } else {\n flash(operation.successMessage, 'success')\n keepSubmitLock = true\n\n onSuccess?.({ id: call.result?.id })\n\n if (!inline) {\n onOpenChange?.(false)\n }\n }\n }\n } catch (error) {\n const message = error instanceof Error\n ? error.message\n : t('messages.errors.sendFailed', 'Failed to send message.')\n setSubmitError(message)\n flash(message, 'error')\n shouldReturnFalse = true\n } finally {\n if (!keepSubmitLock) {\n setSubmitting(false)\n }\n setSubmitMode('send')\n }\n\n if (shouldReturnFalse) {\n return false\n }\n\n if (keepSubmitLock) {\n return await new Promise<boolean>((resolve) => {\n submitLockReleaseRef.current = () => resolve(true)\n })\n }\n\n return true\n }, [\n attachmentIds,\n composeDraftOperation,\n composeSendOperation,\n forwardOperation,\n inline,\n loadAttachmentIds,\n onOpenChange,\n onSuccess,\n replyOperation,\n submitting,\n t,\n variant,\n ])\n\n const handleSaveDraft = React.useCallback(() => {\n if (variant !== 'compose') return\n void handleSubmit({ saveAsDraft: true })\n }, [handleSubmit, variant])\n\n const handleBack = React.useCallback(() => {\n if (submitting) return\n handleCancel()\n }, [handleCancel, submitting])\n\n const handleDialogOpenChange = React.useCallback((nextOpen: boolean) => {\n if (nextOpen) {\n onOpenChange?.(true)\n return\n }\n void handleBack()\n }, [handleBack, onOpenChange])\n\n const handleKeyDown = React.useCallback((event: React.KeyboardEvent<HTMLDivElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n void handleSubmit()\n return\n }\n\n if (event.key === 'Escape') {\n event.preventDefault()\n handleCancel()\n }\n }, [handleCancel, handleSubmit])\n\n const composerTitle = variant === 'reply'\n ? t('messages.reply', 'Reply')\n : variant === 'forward'\n ? t('messages.forward', 'Forward')\n : t('messages.compose', 'Compose message')\n\n const submitLabel = submitting\n ? submitMode === 'draft'\n ? t('messages.savingDraft', 'Saving draft...')\n : t('messages.sending', 'Sending...')\n : variant === 'reply'\n ? t('messages.reply', 'Reply')\n : variant === 'forward'\n ? t('messages.forward', 'Forward')\n : t('messages.send', 'Send')\n\n return {\n t,\n variant,\n messageId,\n open,\n inline,\n contextPreview,\n isOpen,\n messageTypes,\n createableMessageTypes,\n normalizedRequiredActionMode,\n contextActionOptions,\n shouldShowContextActions,\n isComposePublicVisibility,\n attachmentEntityId,\n attachmentRecordId,\n recipientIds,\n setRecipientIds,\n messageType,\n setMessageType,\n subject,\n setSubject,\n body,\n setBody,\n bodyFormat,\n setBodyFormat,\n priority,\n setPriority,\n visibility,\n setVisibility,\n externalEmail,\n setExternalEmail,\n sendViaEmail,\n setSendViaEmail,\n contextActionRequired,\n setContextActionRequired,\n contextActionType,\n setContextActionType,\n replyAll,\n setReplyAll,\n includeAttachments,\n setIncludeAttachments,\n submitting,\n submitMode,\n submitError,\n composerTitle,\n submitLabel,\n selectedRecipientOptions,\n resolveRecipientLabel,\n loadRecipientSuggestions,\n loadAttachmentIds,\n handleSaveDraft,\n handleBack,\n handleSubmit,\n handleDialogOpenChange,\n handleKeyDown,\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as React from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { flash } from '../FlashMessages'\nimport { apiCall } from '../utils/apiCall'\nimport type {\n AttachmentListResponse,\n MessageComposerProps,\n MessageTypeItem,\n UserListItem,\n} from './message-composer.types'\nimport type { MessagePriority } from './message-priority'\nimport type { TagsInputOption } from '../inputs/TagsInput'\nimport {\n useComposeDraftOperation,\n useComposeSendOperation,\n useForwardSubmitOperation,\n useReplySubmitOperation,\n useSendDraftOperation,\n useUpdateDraftOperation,\n} from './useMessageComposeOperations'\n\nfunction toErrorMessage(payload: unknown): string | null {\n if (!payload) return null\n if (typeof payload === 'string') return payload\n if (Array.isArray(payload)) {\n for (const item of payload) {\n const nested = toErrorMessage(item)\n if (nested) return nested\n }\n return null\n }\n if (typeof payload === 'object') {\n const record = payload as Record<string, unknown>\n return (\n toErrorMessage(record.error)\n ?? toErrorMessage(record.message)\n ?? toErrorMessage(record.detail)\n ?? toErrorMessage(record.details)\n ?? null\n )\n }\n return null\n}\n\nfunction createTemporaryAttachmentRecordId(): string {\n const randomPart =\n typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'\n ? crypto.randomUUID()\n : `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`\n return `messages-composer:${randomPart}`\n}\n\nexport type UseMessageComposeParams = MessageComposerProps\n\nexport type UseMessageComposeResult = {\n t: ReturnType<typeof useT>\n variant: NonNullable<MessageComposerProps['variant']>\n messageId?: string\n open?: boolean\n inline: boolean\n contextPreview: React.ReactNode\n isOpen: boolean\n messageTypes: MessageTypeItem[]\n createableMessageTypes: MessageTypeItem[]\n normalizedRequiredActionMode: 'none' | 'optional' | 'required'\n contextActionOptions: Array<{ id: string; label: string }>\n shouldShowContextActions: boolean\n isComposePublicVisibility: boolean\n attachmentEntityId: string\n attachmentRecordId: string\n recipientIds: string[]\n setRecipientIds: React.Dispatch<React.SetStateAction<string[]>>\n messageType: string\n setMessageType: React.Dispatch<React.SetStateAction<string>>\n subject: string\n setSubject: React.Dispatch<React.SetStateAction<string>>\n body: string\n setBody: React.Dispatch<React.SetStateAction<string>>\n bodyFormat: 'text' | 'markdown'\n setBodyFormat: React.Dispatch<React.SetStateAction<'text' | 'markdown'>>\n priority: MessagePriority\n setPriority: React.Dispatch<React.SetStateAction<MessagePriority>>\n visibility: 'public' | 'internal'\n setVisibility: React.Dispatch<React.SetStateAction<'public' | 'internal'>>\n externalEmail: string\n setExternalEmail: React.Dispatch<React.SetStateAction<string>>\n sendViaEmail: boolean\n setSendViaEmail: React.Dispatch<React.SetStateAction<boolean>>\n contextActionRequired: boolean\n setContextActionRequired: React.Dispatch<React.SetStateAction<boolean>>\n contextActionType: string\n setContextActionType: React.Dispatch<React.SetStateAction<string>>\n replyAll: boolean\n setReplyAll: React.Dispatch<React.SetStateAction<boolean>>\n includeAttachments: boolean\n setIncludeAttachments: React.Dispatch<React.SetStateAction<boolean>>\n submitting: boolean\n submitMode: 'send' | 'draft'\n submitError: string | null\n composerTitle: string\n submitLabel: string\n selectedRecipientOptions: TagsInputOption[]\n resolveRecipientLabel: (id: string) => string\n loadRecipientSuggestions: (query?: string) => Promise<TagsInputOption[]>\n loadAttachmentIds: () => Promise<string[]>\n handleSaveDraft: () => void\n handleBack: () => void\n handleSubmit: ({ saveAsDraft }?: { saveAsDraft?: boolean }) => Promise<boolean>\n handleDialogOpenChange: (nextOpen: boolean) => void\n handleKeyDown: (event: React.KeyboardEvent<HTMLDivElement>) => void\n}\n\ntype ForwardPreviewResponse = {\n subject?: string\n body?: string\n}\n\nexport function useMessageCompose({\n variant: variantProp = 'compose',\n messageId,\n open,\n onOpenChange,\n inline = false,\n lockedType = null,\n contextObject = null,\n requiredActionConfig = null,\n contextPreview = null,\n defaultValues,\n onSuccess,\n onCancel,\n}: UseMessageComposeParams): UseMessageComposeResult {\n const t = useT()\n const variant = variantProp\n const isOpen = inline ? true : Boolean(open)\n const recipientSuggestionsCacheRef = React.useRef<TagsInputOption[] | null>(null)\n\n const [recipientIds, setRecipientIds] = React.useState<string[]>([])\n const [recipientMap, setRecipientMap] = React.useState<Record<string, TagsInputOption>>({})\n const [messageType, setMessageType] = React.useState(lockedType ?? 'default')\n const [subject, setSubject] = React.useState('')\n const [body, setBody] = React.useState('')\n const [bodyFormat, setBodyFormat] = React.useState<'text' | 'markdown'>('text')\n const [priority, setPriority] = React.useState<MessagePriority>('normal')\n const [visibility, setVisibility] = React.useState<'public' | 'internal'>('internal')\n const [externalEmail, setExternalEmail] = React.useState('')\n const [attachmentIds, setAttachmentIds] = React.useState<string[]>([])\n const [sendViaEmail, setSendViaEmail] = React.useState(false)\n const [contextActionRequired, setContextActionRequired] = React.useState(false)\n const [contextActionType, setContextActionType] = React.useState('')\n const [replyAll, setReplyAll] = React.useState(false)\n const [includeAttachments, setIncludeAttachments] = React.useState(true)\n const [temporaryAttachmentRecordId, setTemporaryAttachmentRecordId] = React.useState<string>(() =>\n createTemporaryAttachmentRecordId(),\n )\n const [submitting, setSubmitting] = React.useState(false)\n const [submitMode, setSubmitMode] = React.useState<'send' | 'draft'>('send')\n const [submitError, setSubmitError] = React.useState<string | null>(null)\n // Tracks whether the composer is currently in the \"open\" lifecycle so the init\n // effect below only runs on the closed \u2192 open transition, not on every parent\n // re-render that produces a new `defaultValues` / `contextObject` reference\n // while the user is typing. Without this guard, an inline literal\n // `defaultValues={{...}}` in a re-rendering parent (e.g. message detail page\n // with live notification badges or queue progress) would clear the body /\n // subject mid-keystroke. CI shard 9 surfaced this as TC-MSG-009 timing out\n // because `keyboard.type` characters appeared to \"type nowhere\" \u2014 they were\n // typed correctly, then immediately wiped by the next effect run.\n const isOpenRef = React.useRef(false)\n const wasOpenRef = React.useRef(false)\n const submitLockReleaseRef = React.useRef<(() => void) | null>(null)\n\n const messageTypesQuery = useQuery({\n queryKey: ['messages', 'types'],\n enabled: variant === 'compose' && isOpen,\n staleTime: 5 * 60 * 1000,\n queryFn: async () => {\n const call = await apiCall<{ items?: MessageTypeItem[] }>('/api/messages/types')\n if (!call.ok) {\n throw new Error(\n toErrorMessage(call.result)\n ?? t('messages.errors.loadTypesFailed', 'Failed to load message types.'),\n )\n }\n return Array.isArray(call.result?.items) ? call.result?.items ?? [] : []\n },\n })\n\n const messageTypes = React.useMemo(\n () => messageTypesQuery.data ?? [],\n [messageTypesQuery.data],\n )\n const createableMessageTypes = React.useMemo(\n () => messageTypes.filter((item) => item.isCreateableByUser !== false),\n [messageTypes],\n )\n const normalizedRequiredActionMode = requiredActionConfig?.mode ?? 'none'\n const contextActionOptions = React.useMemo(\n () => (requiredActionConfig?.options ?? []).filter((option) => option.id.trim().length > 0),\n [requiredActionConfig?.options],\n )\n const shouldShowContextActions = (\n variant === 'compose'\n && Boolean(contextObject)\n && normalizedRequiredActionMode !== 'none'\n && contextActionOptions.length > 0\n )\n\n const isComposePublicVisibility = variant === 'compose' && visibility === 'public'\n\n const attachmentEntityId = variant === 'compose' && messageId ? 'messages:message' : 'attachments:library'\n const attachmentRecordId = variant === 'compose' && messageId ? messageId : temporaryAttachmentRecordId\n\n const loadAttachmentIds = React.useCallback(async (): Promise<string[]> => {\n const params = new URLSearchParams()\n params.set('entityId', attachmentEntityId)\n params.set('recordId', attachmentRecordId)\n\n const call = await apiCall<AttachmentListResponse>(`/api/attachments?${params.toString()}`)\n if (!call.ok) {\n throw new Error(\n toErrorMessage(call.result)\n ?? t('messages.errors.loadAttachmentOptionsFailed', 'Failed to load attachments.'),\n )\n }\n\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n const nextIds = items\n .map((item) => (typeof item?.id === 'string' ? item.id : ''))\n .filter((id) => id.length > 0)\n\n setAttachmentIds(nextIds)\n return nextIds\n }, [attachmentEntityId, attachmentRecordId, t])\n\n React.useEffect(() => {\n if (!isOpen) {\n isOpenRef.current = false\n return\n }\n // Only initialize on the closed \u2192 open transition. Subsequent parent\n // re-renders that change `defaultValues` / `contextObject` references\n // (inline object literals are a new reference on every render) MUST NOT\n // overwrite state the user has typed in.\n if (isOpenRef.current) return\n isOpenRef.current = true\n\n const nextRecipients = defaultValues?.recipients?.filter((value) => typeof value === 'string' && value.trim().length > 0) ?? []\n const dedupedRecipients = Array.from(new Set(nextRecipients))\n\n setRecipientIds(dedupedRecipients)\n setMessageType(lockedType ?? defaultValues?.type ?? 'default')\n setSubject(defaultValues?.subject ?? '')\n setBody(defaultValues?.body ?? '')\n setBodyFormat(defaultValues?.bodyFormat ?? 'text')\n setPriority(defaultValues?.priority ?? 'normal')\n setVisibility(defaultValues?.visibility ?? 'internal')\n setExternalEmail(defaultValues?.externalEmail ?? '')\n setAttachmentIds(\n Array.isArray(defaultValues?.attachmentIds)\n ? defaultValues.attachmentIds.filter((id): id is string => typeof id === 'string' && id.trim().length > 0)\n : [],\n )\n setSendViaEmail(Boolean(defaultValues?.sendViaEmail))\n if (contextObject) {\n const defaultContextActionType = requiredActionConfig?.defaultActionType?.trim() ?? ''\n const fallbackContextActionType = contextObject.actionType?.trim() ?? ''\n const selectedActionType = defaultContextActionType || fallbackContextActionType\n const selectedActionAllowed = contextActionOptions.some((option) => option.id === selectedActionType)\n const nextActionType = selectedActionAllowed ? selectedActionType : ''\n setContextActionType(nextActionType)\n if (normalizedRequiredActionMode === 'required') {\n setContextActionRequired(true)\n } else if (normalizedRequiredActionMode === 'optional') {\n setContextActionRequired(Boolean(nextActionType) || Boolean(contextObject.actionRequired))\n } else {\n setContextActionRequired(Boolean(contextObject.actionRequired))\n }\n } else {\n setContextActionType('')\n setContextActionRequired(false)\n }\n setReplyAll(Boolean(defaultValues?.replyAll))\n setIncludeAttachments(defaultValues?.includeAttachments !== false)\n setTemporaryAttachmentRecordId(createTemporaryAttachmentRecordId())\n setSubmitError(null)\n }, [\n contextActionOptions,\n contextObject,\n defaultValues,\n isOpen,\n lockedType,\n normalizedRequiredActionMode,\n requiredActionConfig?.defaultActionType,\n ])\n\n React.useEffect(() => {\n if (!isOpen) {\n submitLockReleaseRef.current?.()\n submitLockReleaseRef.current = null\n wasOpenRef.current = false\n return\n }\n\n const justOpened = isOpen && !wasOpenRef.current\n wasOpenRef.current = isOpen\n if (!justOpened) return\n submitLockReleaseRef.current?.()\n submitLockReleaseRef.current = null\n setSubmitting(false)\n setSubmitMode('send')\n }, [isOpen])\n\n React.useEffect(() => () => {\n submitLockReleaseRef.current?.()\n submitLockReleaseRef.current = null\n }, [])\n\n React.useEffect(() => {\n if (!isOpen) return\n if (variant !== 'forward') return\n if (!messageId) return\n\n let isActive = true\n\n void (async () => {\n const call = await apiCall<ForwardPreviewResponse>(`/api/messages/${encodeURIComponent(messageId)}/forward-preview`)\n if (!isActive) return\n\n if (!call.ok) {\n const message = toErrorMessage(call.result)\n ?? t('messages.errors.forwardPreviewFailed', 'Failed to load forward preview.')\n setSubmitError(message)\n flash(message, 'error')\n return\n }\n\n if (typeof call.result?.subject === 'string') {\n setSubject((previousValue) => (previousValue.trim().length > 0 ? previousValue : call.result?.subject ?? ''))\n }\n if (typeof call.result?.body === 'string') {\n setBody((previousValue) => (previousValue.trim().length > 0 ? previousValue : call.result?.body ?? ''))\n }\n setBodyFormat('text')\n })().catch((error) => {\n if (!isActive) return\n const message = error instanceof Error\n ? error.message\n : t('messages.errors.forwardPreviewFailed', 'Failed to load forward preview.')\n setSubmitError(message)\n flash(message, 'error')\n })\n\n return () => {\n isActive = false\n }\n }, [isOpen, messageId, t, variant])\n\n React.useEffect(() => {\n if (!isOpen) return\n if (variant !== 'compose' && variant !== 'reply') return\n void loadAttachmentIds().catch(() => null)\n }, [isOpen, loadAttachmentIds, variant])\n\n React.useEffect(() => {\n if (variant !== 'compose') return\n if (!createableMessageTypes.length) return\n\n if (lockedType) {\n if (createableMessageTypes.some((item) => item.type === lockedType)) {\n setMessageType(lockedType)\n return\n }\n const defaultType = createableMessageTypes.find((item) => item.type === 'default')\n setMessageType(defaultType?.type ?? createableMessageTypes[0]?.type ?? 'default')\n return\n }\n\n if (createableMessageTypes.some((item) => item.type === messageType)) return\n\n const defaultType = createableMessageTypes.find((item) => item.type === 'default')\n setMessageType(defaultType?.type ?? createableMessageTypes[0]?.type ?? 'default')\n }, [createableMessageTypes, lockedType, messageType, variant])\n\n React.useEffect(() => {\n if (variant !== 'compose') return\n if (visibility !== 'public') return\n setSendViaEmail(true)\n setRecipientIds([])\n }, [variant, visibility])\n\n React.useEffect(() => {\n if (isOpen) return\n recipientSuggestionsCacheRef.current = null\n }, [isOpen])\n\n const resolveRecipientLabel = React.useCallback((id: string) => {\n return recipientMap[id]?.label ?? id\n }, [recipientMap])\n\n const selectedRecipientOptions = React.useMemo(() => {\n return recipientIds.map((id) => recipientMap[id] ?? { value: id, label: id })\n }, [recipientIds, recipientMap])\n\n const loadRecipientSuggestions = React.useCallback(async (_query?: string) => {\n const cachedOptions = recipientSuggestionsCacheRef.current\n if (cachedOptions) {\n return cachedOptions\n }\n\n const params = new URLSearchParams()\n params.set('page', '1')\n params.set('pageSize', '100')\n // Recipient lookup is filtered in TagsInput because incremental auth user search is unreliable.\n\n const call = await apiCall<{ items?: UserListItem[] }>(\n `/api/auth/users?${params.toString()}`,\n {\n headers: {\n 'x-om-forbidden-redirect': '0',\n },\n },\n ).catch(() => null)\n if (!call) {\n return []\n }\n if (!call.ok) {\n return []\n }\n\n const rawItems = Array.isArray(call.result?.items) ? call.result?.items ?? [] : []\n const options: TagsInputOption[] = []\n for (const item of rawItems) {\n if (!item || typeof item !== 'object') continue\n const id = typeof item.id === 'string' ? item.id : ''\n if (!id) continue\n\n const email = typeof item.email === 'string' && item.email.trim().length ? item.email.trim() : id\n const name = typeof item.name === 'string' && item.name.trim().length ? item.name.trim() : undefined\n\n options.push({\n value: id,\n label: email,\n description: name,\n })\n }\n\n if (options.length) {\n setRecipientMap((prev) => {\n const next = { ...prev }\n for (const option of options) {\n next[option.value] = option\n }\n return next\n })\n }\n\n recipientSuggestionsCacheRef.current = options\n return options\n }, [])\n\n const handleCancel = React.useCallback(() => {\n if (submitting) return\n if (!inline) {\n onOpenChange?.(false)\n }\n onCancel?.()\n }, [inline, onCancel, onOpenChange, submitting])\n\n const composeSendOperation = useComposeSendOperation({\n t,\n messageType,\n createableMessageTypes,\n priority,\n visibility,\n externalEmail,\n recipientIds,\n subject,\n body,\n bodyFormat,\n sendViaEmail,\n contextObject,\n defaultValues,\n contextActionOptions,\n normalizedRequiredActionMode,\n shouldShowContextActions,\n contextActionRequired,\n contextActionType,\n })\n\n const composeDraftOperation = useComposeDraftOperation({\n t,\n messageId,\n messageType,\n priority,\n visibility,\n externalEmail,\n recipientIds,\n subject,\n body,\n bodyFormat,\n sendViaEmail,\n contextObject,\n defaultValues,\n contextActionOptions,\n normalizedRequiredActionMode,\n shouldShowContextActions,\n contextActionRequired,\n contextActionType,\n })\n\n const replyOperation = useReplySubmitOperation({\n t,\n messageId,\n body,\n bodyFormat,\n replyAll,\n recipientIds,\n sendViaEmail,\n })\n\n const forwardOperation = useForwardSubmitOperation({\n t,\n messageId,\n recipientIds,\n body,\n includeAttachments,\n sendViaEmail,\n })\n\n const sendDraftOperation = useSendDraftOperation({\n t,\n messageId: messageId ?? '',\n messageType,\n priority,\n visibility,\n externalEmail,\n recipientIds,\n subject,\n body,\n bodyFormat,\n sendViaEmail,\n contextObject,\n defaultValues,\n contextActionOptions,\n normalizedRequiredActionMode,\n shouldShowContextActions,\n contextActionRequired,\n contextActionType,\n })\n\n const updateDraftOperation = useUpdateDraftOperation({\n t,\n messageId: messageId ?? '',\n messageType,\n priority,\n visibility,\n externalEmail,\n recipientIds,\n subject,\n body,\n bodyFormat,\n sendViaEmail,\n contextObject,\n defaultValues,\n contextActionOptions,\n normalizedRequiredActionMode,\n shouldShowContextActions,\n contextActionRequired,\n contextActionType,\n })\n\n const handleSubmit = React.useCallback(async ({ saveAsDraft = false }: { saveAsDraft?: boolean } = {}) => {\n if (submitting) return false\n\n setSubmitError(null)\n\n const isEditingExistingDraft = variant === 'compose' && Boolean(messageId)\n const isComposeDraftSubmit = saveAsDraft && variant === 'compose'\n const operation = isComposeDraftSubmit\n ? (isEditingExistingDraft ? updateDraftOperation : composeDraftOperation)\n : variant === 'compose'\n ? (isEditingExistingDraft ? sendDraftOperation : composeSendOperation)\n : variant === 'reply'\n ? replyOperation\n : forwardOperation\n\n const validationMessage = operation.validate()\n if (validationMessage) {\n setSubmitError(validationMessage)\n flash(validationMessage, 'error')\n return false\n }\n\n setSubmitMode(isComposeDraftSubmit ? 'draft' : 'send')\n setSubmitting(true)\n let keepSubmitLock = false\n let shouldReturnFalse = false\n\n try {\n let nextAttachmentIds = attachmentIds\n if (operation.requiresAttachmentRefresh) {\n try {\n nextAttachmentIds = await loadAttachmentIds()\n } catch (error) {\n const message = error instanceof Error\n ? error.message\n : t('messages.errors.loadAttachmentOptionsFailed', 'Failed to load attachments.')\n setSubmitError(message)\n flash(message, 'error')\n shouldReturnFalse = true\n }\n }\n\n if (!shouldReturnFalse) {\n const { endpoint, method, payload } = operation.buildRequest({ attachmentIds: nextAttachmentIds })\n\n const call = await apiCall<{ id?: string }>(endpoint, {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n\n if (!call.ok) {\n const message = toErrorMessage(call.result) ?? t('messages.errors.sendFailed', 'Failed to send message.')\n setSubmitError(message)\n flash(message, 'error')\n shouldReturnFalse = true\n } else {\n flash(operation.successMessage, 'success')\n keepSubmitLock = true\n\n onSuccess?.({ id: call.result?.id })\n\n if (!inline) {\n onOpenChange?.(false)\n }\n }\n }\n } catch (error) {\n const message = error instanceof Error\n ? error.message\n : t('messages.errors.sendFailed', 'Failed to send message.')\n setSubmitError(message)\n flash(message, 'error')\n shouldReturnFalse = true\n } finally {\n if (!keepSubmitLock) {\n setSubmitting(false)\n }\n setSubmitMode('send')\n }\n\n if (shouldReturnFalse) {\n return false\n }\n\n if (keepSubmitLock) {\n return await new Promise<boolean>((resolve) => {\n submitLockReleaseRef.current = () => resolve(true)\n })\n }\n\n return true\n }, [\n attachmentIds,\n composeDraftOperation,\n composeSendOperation,\n forwardOperation,\n inline,\n loadAttachmentIds,\n messageId,\n onOpenChange,\n onSuccess,\n replyOperation,\n sendDraftOperation,\n submitting,\n t,\n updateDraftOperation,\n variant,\n ])\n\n const handleSaveDraft = React.useCallback(() => {\n if (variant !== 'compose') return\n void handleSubmit({ saveAsDraft: true })\n }, [handleSubmit, variant])\n\n const handleBack = React.useCallback(() => {\n if (submitting) return\n handleCancel()\n }, [handleCancel, submitting])\n\n const handleDialogOpenChange = React.useCallback((nextOpen: boolean) => {\n if (nextOpen) {\n onOpenChange?.(true)\n return\n }\n void handleBack()\n }, [handleBack, onOpenChange])\n\n const handleKeyDown = React.useCallback((event: React.KeyboardEvent<HTMLDivElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n void handleSubmit()\n return\n }\n\n if (event.key === 'Escape') {\n event.preventDefault()\n handleCancel()\n }\n }, [handleCancel, handleSubmit])\n\n const composerTitle = variant === 'reply'\n ? t('messages.reply', 'Reply')\n : variant === 'forward'\n ? t('messages.forward', 'Forward')\n : t('messages.compose', 'Compose message')\n\n const submitLabel = submitting\n ? submitMode === 'draft'\n ? t('messages.savingDraft', 'Saving draft...')\n : t('messages.sending', 'Sending...')\n : variant === 'reply'\n ? t('messages.reply', 'Reply')\n : variant === 'forward'\n ? t('messages.forward', 'Forward')\n : t('messages.send', 'Send')\n\n return {\n t,\n variant,\n messageId,\n open,\n inline,\n contextPreview,\n isOpen,\n messageTypes,\n createableMessageTypes,\n normalizedRequiredActionMode,\n contextActionOptions,\n shouldShowContextActions,\n isComposePublicVisibility,\n attachmentEntityId,\n attachmentRecordId,\n recipientIds,\n setRecipientIds,\n messageType,\n setMessageType,\n subject,\n setSubject,\n body,\n setBody,\n bodyFormat,\n setBodyFormat,\n priority,\n setPriority,\n visibility,\n setVisibility,\n externalEmail,\n setExternalEmail,\n sendViaEmail,\n setSendViaEmail,\n contextActionRequired,\n setContextActionRequired,\n contextActionType,\n setContextActionType,\n replyAll,\n setReplyAll,\n includeAttachments,\n setIncludeAttachments,\n submitting,\n submitMode,\n submitError,\n composerTitle,\n submitLabel,\n selectedRecipientOptions,\n resolveRecipientLabel,\n loadRecipientSuggestions,\n loadAttachmentIds,\n handleSaveDraft,\n handleBack,\n handleSubmit,\n handleDialogOpenChange,\n handleKeyDown,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["defaultType"]
7
7
  }
@@ -84,6 +84,7 @@ function useComposeSendOperation(params) {
84
84
  },
85
85
  buildRequest: ({ attachmentIds }) => ({
86
86
  endpoint: "/api/messages",
87
+ method: "POST",
87
88
  payload: buildComposePayload(params, { attachmentIds, isDraft: false })
88
89
  }),
89
90
  successMessage: params.t("messages.flash.sentSuccess", "Message sent."),
@@ -93,10 +94,23 @@ function useComposeSendOperation(params) {
93
94
  function useComposeDraftOperation(params) {
94
95
  return React.useMemo(() => ({
95
96
  validate: () => null,
96
- buildRequest: ({ attachmentIds }) => ({
97
- endpoint: "/api/messages",
98
- payload: buildComposePayload(params, { attachmentIds, isDraft: true })
99
- }),
97
+ buildRequest: ({ attachmentIds }) => {
98
+ const payload = buildComposePayload(params, { attachmentIds, isDraft: true });
99
+ if (!params.messageId) {
100
+ return {
101
+ endpoint: "/api/messages",
102
+ method: "POST",
103
+ payload
104
+ };
105
+ }
106
+ const draftUpdatePayload = { ...payload };
107
+ delete draftUpdatePayload.isDraft;
108
+ return {
109
+ endpoint: `/api/messages/${encodeURIComponent(params.messageId)}`,
110
+ method: "PATCH",
111
+ payload: draftUpdatePayload
112
+ };
113
+ },
100
114
  successMessage: params.t("messages.flash.draftSaved", "Draft saved."),
101
115
  requiresAttachmentRefresh: true
102
116
  }), [params]);
@@ -114,6 +128,7 @@ function useReplySubmitOperation(params) {
114
128
  },
115
129
  buildRequest: ({ attachmentIds }) => ({
116
130
  endpoint: `/api/messages/${encodeURIComponent(params.messageId ?? "")}/reply`,
131
+ method: "POST",
117
132
  payload: {
118
133
  body: params.body,
119
134
  bodyFormat: params.bodyFormat,
@@ -143,6 +158,7 @@ function useForwardSubmitOperation(params) {
143
158
  },
144
159
  buildRequest: () => ({
145
160
  endpoint: `/api/messages/${encodeURIComponent(params.messageId ?? "")}/forward`,
161
+ method: "POST",
146
162
  payload: {
147
163
  recipients: mapRecipients(params.recipientIds),
148
164
  body: params.body,
@@ -154,10 +170,53 @@ function useForwardSubmitOperation(params) {
154
170
  requiresAttachmentRefresh: false
155
171
  }), [params]);
156
172
  }
173
+ function useSendDraftOperation(params) {
174
+ return React.useMemo(() => ({
175
+ validate: () => {
176
+ if (params.visibility !== "public" && params.recipientIds.length === 0) {
177
+ return params.t("messages.errors.noRecipients", "Please add at least one recipient.");
178
+ }
179
+ if (params.visibility === "public" && !isValidEmailAddress(params.externalEmail.trim())) {
180
+ return params.t("messages.errors.noExternalEmail", "Please enter a valid external email.");
181
+ }
182
+ if (!params.subject.trim()) {
183
+ return params.t("messages.errors.noSubject", "Please enter a subject.");
184
+ }
185
+ if (!params.body.trim()) {
186
+ return params.t("messages.errors.noBody", "Please enter a message.");
187
+ }
188
+ return null;
189
+ },
190
+ buildRequest: ({ attachmentIds }) => ({
191
+ endpoint: `/api/messages/${encodeURIComponent(params.messageId)}`,
192
+ method: "PATCH",
193
+ payload: buildComposePayload(params, { attachmentIds, isDraft: false })
194
+ }),
195
+ successMessage: params.t("messages.flash.sentSuccess", "Message sent."),
196
+ requiresAttachmentRefresh: true
197
+ }), [params]);
198
+ }
199
+ function useUpdateDraftOperation(params) {
200
+ return React.useMemo(() => ({
201
+ validate: () => null,
202
+ buildRequest: ({ attachmentIds }) => {
203
+ const { isDraft: _omit, ...payload } = buildComposePayload(params, { attachmentIds, isDraft: true });
204
+ return {
205
+ endpoint: `/api/messages/${encodeURIComponent(params.messageId)}`,
206
+ method: "PATCH",
207
+ payload
208
+ };
209
+ },
210
+ successMessage: params.t("messages.flash.draftSaved", "Draft saved."),
211
+ requiresAttachmentRefresh: true
212
+ }), [params]);
213
+ }
157
214
  export {
158
215
  useComposeDraftOperation,
159
216
  useComposeSendOperation,
160
217
  useForwardSubmitOperation,
161
- useReplySubmitOperation
218
+ useReplySubmitOperation,
219
+ useSendDraftOperation,
220
+ useUpdateDraftOperation
162
221
  };
163
222
  //# sourceMappingURL=useMessageComposeOperations.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/messages/useMessageComposeOperations.ts"],
4
- "sourcesContent": ["import * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type {\n MessageComposerContextObject,\n MessageComposerProps,\n MessageTypeItem,\n} from './message-composer.types'\nimport type { MessagePriority } from './message-priority'\n\ntype Translator = ReturnType<typeof useT>\n\ntype MessageRecipient = {\n userId: string\n type: 'to'\n}\n\ntype SubmitRequest = {\n endpoint: string\n payload: Record<string, unknown>\n}\n\ntype BaseOperation = {\n validate: () => string | null\n buildRequest: (input: { attachmentIds: string[] }) => SubmitRequest\n successMessage: string\n requiresAttachmentRefresh: boolean\n}\n\ntype ComposeOperationParams = {\n t: Translator\n messageType: string\n createableMessageTypes: MessageTypeItem[]\n priority: MessagePriority\n visibility: 'public' | 'internal'\n externalEmail: string\n recipientIds: string[]\n subject: string\n body: string\n bodyFormat: 'text' | 'markdown'\n sendViaEmail: boolean\n contextObject: MessageComposerContextObject | null\n defaultValues: MessageComposerProps['defaultValues']\n contextActionOptions: Array<{ id: string; label: string }>\n normalizedRequiredActionMode: 'none' | 'optional' | 'required'\n shouldShowContextActions: boolean\n contextActionRequired: boolean\n contextActionType: string\n}\n\ntype ReplyOperationParams = {\n t: Translator\n messageId?: string\n body: string\n bodyFormat: 'text' | 'markdown'\n replyAll: boolean\n recipientIds: string[]\n sendViaEmail: boolean\n}\n\ntype ForwardOperationParams = {\n t: Translator\n messageId?: string\n recipientIds: string[]\n body: string\n includeAttachments: boolean\n sendViaEmail: boolean\n}\n\ntype DraftOperationParams = Omit<ComposeOperationParams, 'createableMessageTypes'>\n\nfunction isValidEmailAddress(value: string): boolean {\n const email = value.trim()\n if (!email || email.length > 254) return false\n\n const atIndex = email.indexOf('@')\n if (atIndex <= 0 || atIndex !== email.lastIndexOf('@') || atIndex === email.length - 1) return false\n\n for (const char of email) {\n if (char === ' ' || char === '\\t' || char === '\\n' || char === '\\r') return false\n }\n\n const localPart = email.slice(0, atIndex)\n const domainPart = email.slice(atIndex + 1)\n if (!localPart || !domainPart || localPart.length > 64) return false\n if (domainPart.length > 253 || !domainPart.includes('.')) return false\n if (domainPart.startsWith('.') || domainPart.endsWith('.') || domainPart.includes('..')) return false\n\n const domainLabels = domainPart.split('.')\n for (const label of domainLabels) {\n if (!label) return false\n if (label.startsWith('-') || label.endsWith('-')) return false\n }\n\n return true\n}\n\nfunction mapRecipients(recipientIds: string[]): MessageRecipient[] {\n return recipientIds.map((userId) => ({ userId, type: 'to' }))\n}\n\nfunction buildComposePayload(\n params: ComposeOperationParams | DraftOperationParams,\n input: { attachmentIds: string[]; isDraft: boolean },\n): Record<string, unknown> {\n const publicMessage = params.visibility === 'public'\n const sourceEntityType = params.contextObject?.sourceEntityType ?? params.defaultValues?.sourceEntityType\n const sourceEntityId = params.contextObject?.sourceEntityId ?? params.defaultValues?.sourceEntityId\n const selectedContextActionOption = params.contextActionOptions.find((option) => option.id === params.contextActionType)\n const resolvedContextActionRequired = params.shouldShowContextActions\n ? (params.normalizedRequiredActionMode === 'required' ? true : params.contextActionRequired)\n : (params.contextObject?.actionRequired ?? false)\n const resolvedContextActionType = resolvedContextActionRequired\n ? (params.shouldShowContextActions ? params.contextActionType : params.contextObject?.actionType)\n : undefined\n const resolvedContextActionLabel = resolvedContextActionRequired\n ? (params.shouldShowContextActions ? selectedContextActionOption?.label : params.contextObject?.actionLabel)\n : undefined\n const contextObjects = params.contextObject\n ? [{\n entityModule: params.contextObject.entityModule,\n entityType: params.contextObject.entityType,\n entityId: params.contextObject.entityId,\n actionRequired: resolvedContextActionRequired,\n actionType: resolvedContextActionType,\n actionLabel: resolvedContextActionLabel,\n }]\n : undefined\n\n return {\n type: params.messageType,\n priority: params.priority,\n visibility: params.visibility,\n externalEmail: publicMessage ? params.externalEmail.trim() : undefined,\n externalName: undefined,\n recipients: publicMessage ? [] : mapRecipients(params.recipientIds),\n subject: params.subject.trim(),\n body: params.body,\n bodyFormat: params.bodyFormat,\n sourceEntityType: sourceEntityType ?? undefined,\n sourceEntityId: sourceEntityId ?? undefined,\n objects: contextObjects,\n attachmentIds: input.attachmentIds.length > 0 ? input.attachmentIds : undefined,\n sendViaEmail: publicMessage ? true : params.sendViaEmail,\n isDraft: input.isDraft,\n }\n}\n\nexport function useComposeSendOperation(params: ComposeOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => {\n if (params.visibility !== 'public' && params.recipientIds.length === 0) {\n return params.t('messages.errors.noRecipients', 'Please add at least one recipient.')\n }\n if (params.visibility === 'public' && !isValidEmailAddress(params.externalEmail.trim())) {\n return params.t('messages.errors.noExternalEmail', 'Please enter a valid external email.')\n }\n if (!params.subject.trim()) {\n return params.t('messages.errors.noSubject', 'Please enter a subject.')\n }\n if (!params.body.trim()) {\n return params.t('messages.errors.noBody', 'Please enter a message.')\n }\n if (!params.createableMessageTypes.some((item) => item.type === params.messageType)) {\n return params.t('messages.errors.sendFailed', 'Failed to send message.')\n }\n if (\n params.shouldShowContextActions\n && params.normalizedRequiredActionMode === 'required'\n && !params.contextActionType\n ) {\n return params.t('messages.composer.objectPicker.errors.actionRequired', 'Select an action.')\n }\n if (\n params.shouldShowContextActions\n && params.normalizedRequiredActionMode === 'optional'\n && params.contextActionRequired\n && !params.contextActionType\n ) {\n return params.t('messages.composer.objectPicker.errors.actionRequired', 'Select an action.')\n }\n return null\n },\n buildRequest: ({ attachmentIds }) => ({\n endpoint: '/api/messages',\n payload: buildComposePayload(params, { attachmentIds, isDraft: false }),\n }),\n successMessage: params.t('messages.flash.sentSuccess', 'Message sent.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\nexport function useComposeDraftOperation(params: DraftOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => null,\n buildRequest: ({ attachmentIds }) => ({\n endpoint: '/api/messages',\n payload: buildComposePayload(params, { attachmentIds, isDraft: true }),\n }),\n successMessage: params.t('messages.flash.draftSaved', 'Draft saved.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\nexport function useReplySubmitOperation(params: ReplyOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => {\n if (!params.body.trim()) {\n return params.t('messages.errors.noBody', 'Please enter a message.')\n }\n if (!params.messageId) {\n return params.t('messages.errors.invalidContext', 'Message context is missing.')\n }\n return null\n },\n buildRequest: ({ attachmentIds }) => ({\n endpoint: `/api/messages/${encodeURIComponent(params.messageId ?? '')}/reply`,\n payload: {\n body: params.body,\n bodyFormat: params.bodyFormat,\n replyAll: params.replyAll,\n recipients: params.recipientIds.length > 0 ? mapRecipients(params.recipientIds) : undefined,\n attachmentIds: attachmentIds.length > 0 ? attachmentIds : undefined,\n sendViaEmail: params.sendViaEmail,\n },\n }),\n successMessage: params.t('messages.flash.replySuccess', 'Reply sent.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\nexport function useForwardSubmitOperation(params: ForwardOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => {\n if (params.recipientIds.length === 0) {\n return params.t('messages.errors.noRecipients', 'Please add at least one recipient.')\n }\n if (!params.messageId) {\n return params.t('messages.errors.invalidContext', 'Message context is missing.')\n }\n if (!params.body.trim()) {\n return params.t('messages.errors.noBody', 'Please enter a message.')\n }\n return null\n },\n buildRequest: () => ({\n endpoint: `/api/messages/${encodeURIComponent(params.messageId ?? '')}/forward`,\n payload: {\n recipients: mapRecipients(params.recipientIds),\n body: params.body,\n includeAttachments: params.includeAttachments,\n sendViaEmail: params.sendViaEmail,\n },\n }),\n successMessage: params.t('messages.flash.forwardSuccess', 'Message forwarded.'),\n requiresAttachmentRefresh: false,\n }), [params])\n}\n\nexport type MessageComposeSubmitOperation = BaseOperation\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type {\n MessageComposerContextObject,\n MessageComposerProps,\n MessageTypeItem,\n} from './message-composer.types'\nimport type { MessagePriority } from './message-priority'\n\ntype Translator = ReturnType<typeof useT>\n\ntype MessageRecipient = {\n userId: string\n type: 'to'\n}\n\ntype SubmitRequest = {\n endpoint: string\n method: 'POST' | 'PATCH'\n payload: Record<string, unknown>\n}\n\ntype BaseOperation = {\n validate: () => string | null\n buildRequest: (input: { attachmentIds: string[] }) => SubmitRequest\n successMessage: string\n requiresAttachmentRefresh: boolean\n}\n\ntype ComposeOperationParams = {\n t: Translator\n messageType: string\n createableMessageTypes: MessageTypeItem[]\n priority: MessagePriority\n visibility: 'public' | 'internal'\n externalEmail: string\n recipientIds: string[]\n subject: string\n body: string\n bodyFormat: 'text' | 'markdown'\n sendViaEmail: boolean\n contextObject: MessageComposerContextObject | null\n defaultValues: MessageComposerProps['defaultValues']\n contextActionOptions: Array<{ id: string; label: string }>\n normalizedRequiredActionMode: 'none' | 'optional' | 'required'\n shouldShowContextActions: boolean\n contextActionRequired: boolean\n contextActionType: string\n}\n\ntype ReplyOperationParams = {\n t: Translator\n messageId?: string\n body: string\n bodyFormat: 'text' | 'markdown'\n replyAll: boolean\n recipientIds: string[]\n sendViaEmail: boolean\n}\n\ntype ForwardOperationParams = {\n t: Translator\n messageId?: string\n recipientIds: string[]\n body: string\n includeAttachments: boolean\n sendViaEmail: boolean\n}\n\ntype DraftOperationParams = Omit<ComposeOperationParams, 'createableMessageTypes'>\n & { messageId?: string }\n\nfunction isValidEmailAddress(value: string): boolean {\n const email = value.trim()\n if (!email || email.length > 254) return false\n\n const atIndex = email.indexOf('@')\n if (atIndex <= 0 || atIndex !== email.lastIndexOf('@') || atIndex === email.length - 1) return false\n\n for (const char of email) {\n if (char === ' ' || char === '\\t' || char === '\\n' || char === '\\r') return false\n }\n\n const localPart = email.slice(0, atIndex)\n const domainPart = email.slice(atIndex + 1)\n if (!localPart || !domainPart || localPart.length > 64) return false\n if (domainPart.length > 253 || !domainPart.includes('.')) return false\n if (domainPart.startsWith('.') || domainPart.endsWith('.') || domainPart.includes('..')) return false\n\n const domainLabels = domainPart.split('.')\n for (const label of domainLabels) {\n if (!label) return false\n if (label.startsWith('-') || label.endsWith('-')) return false\n }\n\n return true\n}\n\nfunction mapRecipients(recipientIds: string[]): MessageRecipient[] {\n return recipientIds.map((userId) => ({ userId, type: 'to' }))\n}\n\nfunction buildComposePayload(\n params: ComposeOperationParams | DraftOperationParams,\n input: { attachmentIds: string[]; isDraft: boolean },\n): Record<string, unknown> {\n const publicMessage = params.visibility === 'public'\n const sourceEntityType = params.contextObject?.sourceEntityType ?? params.defaultValues?.sourceEntityType\n const sourceEntityId = params.contextObject?.sourceEntityId ?? params.defaultValues?.sourceEntityId\n const selectedContextActionOption = params.contextActionOptions.find((option) => option.id === params.contextActionType)\n const resolvedContextActionRequired = params.shouldShowContextActions\n ? (params.normalizedRequiredActionMode === 'required' ? true : params.contextActionRequired)\n : (params.contextObject?.actionRequired ?? false)\n const resolvedContextActionType = resolvedContextActionRequired\n ? (params.shouldShowContextActions ? params.contextActionType : params.contextObject?.actionType)\n : undefined\n const resolvedContextActionLabel = resolvedContextActionRequired\n ? (params.shouldShowContextActions ? selectedContextActionOption?.label : params.contextObject?.actionLabel)\n : undefined\n const contextObjects = params.contextObject\n ? [{\n entityModule: params.contextObject.entityModule,\n entityType: params.contextObject.entityType,\n entityId: params.contextObject.entityId,\n actionRequired: resolvedContextActionRequired,\n actionType: resolvedContextActionType,\n actionLabel: resolvedContextActionLabel,\n }]\n : undefined\n\n return {\n type: params.messageType,\n priority: params.priority,\n visibility: params.visibility,\n externalEmail: publicMessage ? params.externalEmail.trim() : undefined,\n externalName: undefined,\n recipients: publicMessage ? [] : mapRecipients(params.recipientIds),\n subject: params.subject.trim(),\n body: params.body,\n bodyFormat: params.bodyFormat,\n sourceEntityType: sourceEntityType ?? undefined,\n sourceEntityId: sourceEntityId ?? undefined,\n objects: contextObjects,\n attachmentIds: input.attachmentIds.length > 0 ? input.attachmentIds : undefined,\n sendViaEmail: publicMessage ? true : params.sendViaEmail,\n isDraft: input.isDraft,\n }\n}\n\nexport function useComposeSendOperation(params: ComposeOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => {\n if (params.visibility !== 'public' && params.recipientIds.length === 0) {\n return params.t('messages.errors.noRecipients', 'Please add at least one recipient.')\n }\n if (params.visibility === 'public' && !isValidEmailAddress(params.externalEmail.trim())) {\n return params.t('messages.errors.noExternalEmail', 'Please enter a valid external email.')\n }\n if (!params.subject.trim()) {\n return params.t('messages.errors.noSubject', 'Please enter a subject.')\n }\n if (!params.body.trim()) {\n return params.t('messages.errors.noBody', 'Please enter a message.')\n }\n if (!params.createableMessageTypes.some((item) => item.type === params.messageType)) {\n return params.t('messages.errors.sendFailed', 'Failed to send message.')\n }\n if (\n params.shouldShowContextActions\n && params.normalizedRequiredActionMode === 'required'\n && !params.contextActionType\n ) {\n return params.t('messages.composer.objectPicker.errors.actionRequired', 'Select an action.')\n }\n if (\n params.shouldShowContextActions\n && params.normalizedRequiredActionMode === 'optional'\n && params.contextActionRequired\n && !params.contextActionType\n ) {\n return params.t('messages.composer.objectPicker.errors.actionRequired', 'Select an action.')\n }\n return null\n },\n buildRequest: ({ attachmentIds }) => ({\n endpoint: '/api/messages',\n method: 'POST',\n payload: buildComposePayload(params, { attachmentIds, isDraft: false }),\n }),\n successMessage: params.t('messages.flash.sentSuccess', 'Message sent.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\nexport function useComposeDraftOperation(params: DraftOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => null,\n buildRequest: ({ attachmentIds }) => {\n const payload = buildComposePayload(params, { attachmentIds, isDraft: true })\n if (!params.messageId) {\n return {\n endpoint: '/api/messages',\n method: 'POST',\n payload,\n }\n }\n\n const draftUpdatePayload = { ...payload }\n delete draftUpdatePayload.isDraft\n return {\n endpoint: `/api/messages/${encodeURIComponent(params.messageId)}`,\n method: 'PATCH',\n payload: draftUpdatePayload,\n }\n },\n successMessage: params.t('messages.flash.draftSaved', 'Draft saved.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\nexport function useReplySubmitOperation(params: ReplyOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => {\n if (!params.body.trim()) {\n return params.t('messages.errors.noBody', 'Please enter a message.')\n }\n if (!params.messageId) {\n return params.t('messages.errors.invalidContext', 'Message context is missing.')\n }\n return null\n },\n buildRequest: ({ attachmentIds }) => ({\n endpoint: `/api/messages/${encodeURIComponent(params.messageId ?? '')}/reply`,\n method: 'POST',\n payload: {\n body: params.body,\n bodyFormat: params.bodyFormat,\n replyAll: params.replyAll,\n recipients: params.recipientIds.length > 0 ? mapRecipients(params.recipientIds) : undefined,\n attachmentIds: attachmentIds.length > 0 ? attachmentIds : undefined,\n sendViaEmail: params.sendViaEmail,\n },\n }),\n successMessage: params.t('messages.flash.replySuccess', 'Reply sent.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\nexport function useForwardSubmitOperation(params: ForwardOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => {\n if (params.recipientIds.length === 0) {\n return params.t('messages.errors.noRecipients', 'Please add at least one recipient.')\n }\n if (!params.messageId) {\n return params.t('messages.errors.invalidContext', 'Message context is missing.')\n }\n if (!params.body.trim()) {\n return params.t('messages.errors.noBody', 'Please enter a message.')\n }\n return null\n },\n buildRequest: () => ({\n endpoint: `/api/messages/${encodeURIComponent(params.messageId ?? '')}/forward`,\n method: 'POST',\n payload: {\n recipients: mapRecipients(params.recipientIds),\n body: params.body,\n includeAttachments: params.includeAttachments,\n sendViaEmail: params.sendViaEmail,\n },\n }),\n successMessage: params.t('messages.flash.forwardSuccess', 'Message forwarded.'),\n requiresAttachmentRefresh: false,\n }), [params])\n}\n\ntype SendDraftOperationParams = DraftOperationParams & { messageId: string }\n\nexport function useSendDraftOperation(params: SendDraftOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => {\n if (params.visibility !== 'public' && params.recipientIds.length === 0) {\n return params.t('messages.errors.noRecipients', 'Please add at least one recipient.')\n }\n if (params.visibility === 'public' && !isValidEmailAddress(params.externalEmail.trim())) {\n return params.t('messages.errors.noExternalEmail', 'Please enter a valid external email.')\n }\n if (!params.subject.trim()) {\n return params.t('messages.errors.noSubject', 'Please enter a subject.')\n }\n if (!params.body.trim()) {\n return params.t('messages.errors.noBody', 'Please enter a message.')\n }\n return null\n },\n buildRequest: ({ attachmentIds }) => ({\n endpoint: `/api/messages/${encodeURIComponent(params.messageId)}`,\n method: 'PATCH' as const,\n payload: buildComposePayload(params, { attachmentIds, isDraft: false }),\n }),\n successMessage: params.t('messages.flash.sentSuccess', 'Message sent.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\ntype UpdateDraftOperationParams = DraftOperationParams & { messageId: string }\n\nexport function useUpdateDraftOperation(params: UpdateDraftOperationParams): BaseOperation {\n return React.useMemo(() => ({\n validate: () => null,\n buildRequest: ({ attachmentIds }) => {\n const { isDraft: _omit, ...payload } = buildComposePayload(params, { attachmentIds, isDraft: true })\n return {\n endpoint: `/api/messages/${encodeURIComponent(params.messageId)}`,\n method: 'PATCH' as const,\n payload,\n }\n },\n successMessage: params.t('messages.flash.draftSaved', 'Draft saved.'),\n requiresAttachmentRefresh: true,\n }), [params])\n}\n\nexport type MessageComposeSubmitOperation = BaseOperation\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,7 +2,17 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { Calendar, dateFnsLocalizer } from "react-big-calendar";
5
- import { addDays, differenceInCalendarDays, endOfDay, endOfMonth, endOfWeek, format, getDay, parse, startOfDay, startOfMonth, startOfWeek } from "date-fns";
5
+ import { addDays } from "date-fns/addDays";
6
+ import { differenceInCalendarDays } from "date-fns/differenceInCalendarDays";
7
+ import { endOfDay } from "date-fns/endOfDay";
8
+ import { endOfMonth } from "date-fns/endOfMonth";
9
+ import { endOfWeek } from "date-fns/endOfWeek";
10
+ import { format } from "date-fns/format";
11
+ import { getDay } from "date-fns/getDay";
12
+ import { parse } from "date-fns/parse";
13
+ import { startOfDay } from "date-fns/startOfDay";
14
+ import { startOfMonth } from "date-fns/startOfMonth";
15
+ import { startOfWeek } from "date-fns/startOfWeek";
6
16
  import { enUS } from "date-fns/locale/en-US";
7
17
  import { Button } from "../../primitives/button.js";
8
18
  import { expandRecurringItems } from "./recurrence.js";
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/schedule/ScheduleCalendar.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Calendar, dateFnsLocalizer, type View, type SlotInfo } from 'react-big-calendar'\nimport { addDays, differenceInCalendarDays, endOfDay, endOfMonth, endOfWeek, format, getDay, parse, startOfDay, startOfMonth, startOfWeek } from 'date-fns'\nimport { enUS } from 'date-fns/locale/en-US'\nimport type { ScheduleItem, ScheduleRange, ScheduleSlot, ScheduleViewMode } from './types'\nimport { Button } from '../../primitives/button'\nimport { expandRecurringItems } from './recurrence'\n\ntype CalendarEvent = {\n id: string\n title: string\n start: Date\n end: Date\n resource: ScheduleItem\n}\n\nconst localizer = dateFnsLocalizer({\n format,\n parse,\n startOfWeek,\n getDay,\n locales: { 'en-US': enUS },\n})\n\nconst VIEW_MAP: Record<ScheduleViewMode, View> = {\n day: 'day',\n week: 'week',\n month: 'month',\n agenda: 'agenda',\n}\n\nfunction deriveRange(date: Date, view: ScheduleViewMode, agendaLength: number): ScheduleRange {\n if (view === 'day') {\n return { start: startOfDay(date), end: endOfDay(date) }\n }\n if (view === 'week') {\n return { start: startOfWeek(date, { locale: enUS }), end: endOfWeek(date, { locale: enUS }) }\n }\n if (view === 'month') {\n return { start: startOfMonth(date), end: endOfMonth(date) }\n }\n const length = Math.max(1, agendaLength)\n return { start: startOfDay(date), end: endOfDay(addDays(date, length - 1)) }\n}\n\nfunction normalizeRange(\n nextRange: Date[] | { start: Date; end: Date } | null | undefined,\n view: ScheduleViewMode,\n agendaLength: number,\n): ScheduleRange | null {\n if (!nextRange) return null\n if (Array.isArray(nextRange)) {\n if (nextRange.length === 0) return null\n if (view === 'agenda') {\n return { start: nextRange[0], end: nextRange[nextRange.length - 1] }\n }\n return deriveRange(nextRange[0], view, agendaLength)\n }\n if (nextRange.start && nextRange.end) return { start: nextRange.start, end: nextRange.end }\n return deriveRange(new Date(), view, agendaLength)\n}\n\nfunction getEventStyles(item: ScheduleItem): React.CSSProperties {\n if (item.kind === 'event') {\n return { backgroundColor: 'rgba(59, 130, 246, 0.15)', border: '1px solid rgba(59, 130, 246, 0.5)', color: '#1e3a8a' }\n }\n if (item.kind === 'exception') {\n return { backgroundColor: 'rgba(148, 163, 184, 0.2)', border: '1px solid rgba(100, 116, 139, 0.6)', color: '#334155' }\n }\n return { backgroundColor: 'rgba(16, 185, 129, 0.15)', border: '1px solid rgba(16, 185, 129, 0.5)', color: '#064e3b' }\n}\n\nexport type ScheduleCalendarProps = {\n items: ScheduleItem[]\n view: ScheduleViewMode\n range: ScheduleRange\n onRangeChange: (range: ScheduleRange) => void\n onViewChange: (view: ScheduleViewMode) => void\n onItemClick?: (item: ScheduleItem) => void\n onSlotClick?: (slot: ScheduleSlot) => void\n}\n\nexport default function ScheduleCalendar({\n items,\n view,\n range,\n onRangeChange,\n onViewChange,\n onItemClick,\n onSlotClick,\n}: ScheduleCalendarProps) {\n const agendaLength = React.useMemo(\n () => Math.max(1, differenceInCalendarDays(range.end, range.start) + 1),\n [range.end, range.start],\n )\n const currentView = VIEW_MAP[view]\n const expandedItems = React.useMemo(() => expandRecurringItems(items, range), [items, range])\n const events = React.useMemo<CalendarEvent[]>(\n () => expandedItems.map((item) => ({\n id: item.id,\n title: item.title,\n start: item.startsAt,\n end: item.endsAt,\n resource: item,\n })),\n [expandedItems],\n )\n\n const handleNavigate = React.useCallback((date: Date, nextView?: View) => {\n const resolvedView = (nextView ?? currentView) as ScheduleViewMode\n onRangeChange(deriveRange(date, resolvedView, agendaLength))\n }, [agendaLength, currentView, onRangeChange])\n\n const handleRangeChange = React.useCallback((nextRange: Date[] | { start: Date; end: Date }, nextView?: View) => {\n const resolvedView = (nextView ?? currentView) as ScheduleViewMode\n const normalized = normalizeRange(nextRange, resolvedView, agendaLength)\n if (normalized) onRangeChange(normalized)\n }, [agendaLength, currentView, onRangeChange])\n\n const handleViewChange = React.useCallback((nextView: View) => {\n const resolved = nextView as ScheduleViewMode\n if (resolved !== view) {\n onViewChange(resolved)\n onRangeChange(deriveRange(new Date(), resolved, agendaLength))\n }\n }, [agendaLength, onRangeChange, onViewChange, view])\n\n const handleSelectEvent = React.useCallback(\n (event: CalendarEvent) => onItemClick?.(event.resource),\n [onItemClick],\n )\n\n const handleSelectSlot = React.useCallback(\n (slot: SlotInfo) => {\n if (!onSlotClick) return\n onSlotClick({ start: slot.start, end: slot.end })\n },\n [onSlotClick],\n )\n\n const eventPropGetter = React.useCallback(\n (event: CalendarEvent) => ({ style: getEventStyles(event.resource) }),\n [],\n )\n\n const calendarStyle = React.useMemo<React.CSSProperties>(() => ({ height: 640 }), [])\n\n const components = React.useMemo(\n () => ({\n event: ({ event }: { event: CalendarEvent }) => {\n const resource = event.resource\n const hasLink = Boolean(resource.linkLabel) && typeof onItemClick === 'function'\n return (\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"truncate text-xs font-medium\">{resource.title}</span>\n {hasLink ? (\n <Button\n type=\"button\"\n variant=\"link\"\n size=\"sm\"\n className=\"h-auto p-0 text-overline\"\n onClick={(clickEvent) => {\n clickEvent.stopPropagation()\n onItemClick?.(resource)\n }}\n >\n {resource.linkLabel}\n </Button>\n ) : null}\n </div>\n )\n },\n }),\n [onItemClick],\n )\n\n return (\n <Calendar\n localizer={localizer}\n culture=\"en-US\"\n events={events}\n view={currentView}\n date={range.start}\n toolbar={false}\n selectable={Boolean(onSlotClick)}\n popup\n length={agendaLength}\n onView={handleViewChange}\n onNavigate={handleNavigate}\n onRangeChange={handleRangeChange}\n onSelectEvent={handleSelectEvent}\n onSelectSlot={handleSelectSlot}\n eventPropGetter={eventPropGetter}\n components={components}\n style={calendarStyle}\n />\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Calendar, dateFnsLocalizer, type View, type SlotInfo } from 'react-big-calendar'\nimport { addDays } from 'date-fns/addDays'\nimport { differenceInCalendarDays } from 'date-fns/differenceInCalendarDays'\nimport { endOfDay } from 'date-fns/endOfDay'\nimport { endOfMonth } from 'date-fns/endOfMonth'\nimport { endOfWeek } from 'date-fns/endOfWeek'\nimport { format } from 'date-fns/format'\nimport { getDay } from 'date-fns/getDay'\nimport { parse } from 'date-fns/parse'\nimport { startOfDay } from 'date-fns/startOfDay'\nimport { startOfMonth } from 'date-fns/startOfMonth'\nimport { startOfWeek } from 'date-fns/startOfWeek'\nimport { enUS } from 'date-fns/locale/en-US'\nimport type { ScheduleItem, ScheduleRange, ScheduleSlot, ScheduleViewMode } from './types'\nimport { Button } from '../../primitives/button'\nimport { expandRecurringItems } from './recurrence'\n\ntype CalendarEvent = {\n id: string\n title: string\n start: Date\n end: Date\n resource: ScheduleItem\n}\n\nconst localizer = dateFnsLocalizer({\n format,\n parse,\n startOfWeek,\n getDay,\n locales: { 'en-US': enUS },\n})\n\nconst VIEW_MAP: Record<ScheduleViewMode, View> = {\n day: 'day',\n week: 'week',\n month: 'month',\n agenda: 'agenda',\n}\n\nfunction deriveRange(date: Date, view: ScheduleViewMode, agendaLength: number): ScheduleRange {\n if (view === 'day') {\n return { start: startOfDay(date), end: endOfDay(date) }\n }\n if (view === 'week') {\n return { start: startOfWeek(date, { locale: enUS }), end: endOfWeek(date, { locale: enUS }) }\n }\n if (view === 'month') {\n return { start: startOfMonth(date), end: endOfMonth(date) }\n }\n const length = Math.max(1, agendaLength)\n return { start: startOfDay(date), end: endOfDay(addDays(date, length - 1)) }\n}\n\nfunction normalizeRange(\n nextRange: Date[] | { start: Date; end: Date } | null | undefined,\n view: ScheduleViewMode,\n agendaLength: number,\n): ScheduleRange | null {\n if (!nextRange) return null\n if (Array.isArray(nextRange)) {\n if (nextRange.length === 0) return null\n if (view === 'agenda') {\n return { start: nextRange[0], end: nextRange[nextRange.length - 1] }\n }\n return deriveRange(nextRange[0], view, agendaLength)\n }\n if (nextRange.start && nextRange.end) return { start: nextRange.start, end: nextRange.end }\n return deriveRange(new Date(), view, agendaLength)\n}\n\nfunction getEventStyles(item: ScheduleItem): React.CSSProperties {\n if (item.kind === 'event') {\n return { backgroundColor: 'rgba(59, 130, 246, 0.15)', border: '1px solid rgba(59, 130, 246, 0.5)', color: '#1e3a8a' }\n }\n if (item.kind === 'exception') {\n return { backgroundColor: 'rgba(148, 163, 184, 0.2)', border: '1px solid rgba(100, 116, 139, 0.6)', color: '#334155' }\n }\n return { backgroundColor: 'rgba(16, 185, 129, 0.15)', border: '1px solid rgba(16, 185, 129, 0.5)', color: '#064e3b' }\n}\n\nexport type ScheduleCalendarProps = {\n items: ScheduleItem[]\n view: ScheduleViewMode\n range: ScheduleRange\n onRangeChange: (range: ScheduleRange) => void\n onViewChange: (view: ScheduleViewMode) => void\n onItemClick?: (item: ScheduleItem) => void\n onSlotClick?: (slot: ScheduleSlot) => void\n}\n\nexport default function ScheduleCalendar({\n items,\n view,\n range,\n onRangeChange,\n onViewChange,\n onItemClick,\n onSlotClick,\n}: ScheduleCalendarProps) {\n const agendaLength = React.useMemo(\n () => Math.max(1, differenceInCalendarDays(range.end, range.start) + 1),\n [range.end, range.start],\n )\n const currentView = VIEW_MAP[view]\n const expandedItems = React.useMemo(() => expandRecurringItems(items, range), [items, range])\n const events = React.useMemo<CalendarEvent[]>(\n () => expandedItems.map((item) => ({\n id: item.id,\n title: item.title,\n start: item.startsAt,\n end: item.endsAt,\n resource: item,\n })),\n [expandedItems],\n )\n\n const handleNavigate = React.useCallback((date: Date, nextView?: View) => {\n const resolvedView = (nextView ?? currentView) as ScheduleViewMode\n onRangeChange(deriveRange(date, resolvedView, agendaLength))\n }, [agendaLength, currentView, onRangeChange])\n\n const handleRangeChange = React.useCallback((nextRange: Date[] | { start: Date; end: Date }, nextView?: View) => {\n const resolvedView = (nextView ?? currentView) as ScheduleViewMode\n const normalized = normalizeRange(nextRange, resolvedView, agendaLength)\n if (normalized) onRangeChange(normalized)\n }, [agendaLength, currentView, onRangeChange])\n\n const handleViewChange = React.useCallback((nextView: View) => {\n const resolved = nextView as ScheduleViewMode\n if (resolved !== view) {\n onViewChange(resolved)\n onRangeChange(deriveRange(new Date(), resolved, agendaLength))\n }\n }, [agendaLength, onRangeChange, onViewChange, view])\n\n const handleSelectEvent = React.useCallback(\n (event: CalendarEvent) => onItemClick?.(event.resource),\n [onItemClick],\n )\n\n const handleSelectSlot = React.useCallback(\n (slot: SlotInfo) => {\n if (!onSlotClick) return\n onSlotClick({ start: slot.start, end: slot.end })\n },\n [onSlotClick],\n )\n\n const eventPropGetter = React.useCallback(\n (event: CalendarEvent) => ({ style: getEventStyles(event.resource) }),\n [],\n )\n\n const calendarStyle = React.useMemo<React.CSSProperties>(() => ({ height: 640 }), [])\n\n const components = React.useMemo(\n () => ({\n event: ({ event }: { event: CalendarEvent }) => {\n const resource = event.resource\n const hasLink = Boolean(resource.linkLabel) && typeof onItemClick === 'function'\n return (\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"truncate text-xs font-medium\">{resource.title}</span>\n {hasLink ? (\n <Button\n type=\"button\"\n variant=\"link\"\n size=\"sm\"\n className=\"h-auto p-0 text-overline\"\n onClick={(clickEvent) => {\n clickEvent.stopPropagation()\n onItemClick?.(resource)\n }}\n >\n {resource.linkLabel}\n </Button>\n ) : null}\n </div>\n )\n },\n }),\n [onItemClick],\n )\n\n return (\n <Calendar\n localizer={localizer}\n culture=\"en-US\"\n events={events}\n view={currentView}\n date={range.start}\n toolbar={false}\n selectable={Boolean(onSlotClick)}\n popup\n length={agendaLength}\n onView={handleViewChange}\n onNavigate={handleNavigate}\n onRangeChange={handleRangeChange}\n onSelectEvent={handleSelectEvent}\n onSelectSlot={handleSelectSlot}\n eventPropGetter={eventPropGetter}\n components={components}\n style={calendarStyle}\n />\n )\n}\n"],
5
+ "mappings": ";AAqKU,SACE,KADF;AAnKV,YAAY,WAAW;AACvB,SAAS,UAAU,wBAAkD;AACrE,SAAS,eAAe;AACxB,SAAS,gCAAgC;AACzC,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAC3B,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,cAAc;AACvB,SAAS,aAAa;AACtB,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAS,mBAAmB;AAC5B,SAAS,YAAY;AAErB,SAAS,cAAc;AACvB,SAAS,4BAA4B;AAUrC,MAAM,YAAY,iBAAiB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS,EAAE,SAAS,KAAK;AAC3B,CAAC;AAED,MAAM,WAA2C;AAAA,EAC/C,KAAK;AAAA,EACL,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AACV;AAEA,SAAS,YAAY,MAAY,MAAwB,cAAqC;AAC5F,MAAI,SAAS,OAAO;AAClB,WAAO,EAAE,OAAO,WAAW,IAAI,GAAG,KAAK,SAAS,IAAI,EAAE;AAAA,EACxD;AACA,MAAI,SAAS,QAAQ;AACnB,WAAO,EAAE,OAAO,YAAY,MAAM,EAAE,QAAQ,KAAK,CAAC,GAAG,KAAK,UAAU,MAAM,EAAE,QAAQ,KAAK,CAAC,EAAE;AAAA,EAC9F;AACA,MAAI,SAAS,SAAS;AACpB,WAAO,EAAE,OAAO,aAAa,IAAI,GAAG,KAAK,WAAW,IAAI,EAAE;AAAA,EAC5D;AACA,QAAM,SAAS,KAAK,IAAI,GAAG,YAAY;AACvC,SAAO,EAAE,OAAO,WAAW,IAAI,GAAG,KAAK,SAAS,QAAQ,MAAM,SAAS,CAAC,CAAC,EAAE;AAC7E;AAEA,SAAS,eACP,WACA,MACA,cACsB;AACtB,MAAI,CAAC,UAAW,QAAO;AACvB,MAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B,QAAI,UAAU,WAAW,EAAG,QAAO;AACnC,QAAI,SAAS,UAAU;AACrB,aAAO,EAAE,OAAO,UAAU,CAAC,GAAG,KAAK,UAAU,UAAU,SAAS,CAAC,EAAE;AAAA,IACrE;AACA,WAAO,YAAY,UAAU,CAAC,GAAG,MAAM,YAAY;AAAA,EACrD;AACA,MAAI,UAAU,SAAS,UAAU,IAAK,QAAO,EAAE,OAAO,UAAU,OAAO,KAAK,UAAU,IAAI;AAC1F,SAAO,YAAY,oBAAI,KAAK,GAAG,MAAM,YAAY;AACnD;AAEA,SAAS,eAAe,MAAyC;AAC/D,MAAI,KAAK,SAAS,SAAS;AACzB,WAAO,EAAE,iBAAiB,4BAA4B,QAAQ,qCAAqC,OAAO,UAAU;AAAA,EACtH;AACA,MAAI,KAAK,SAAS,aAAa;AAC7B,WAAO,EAAE,iBAAiB,4BAA4B,QAAQ,sCAAsC,OAAO,UAAU;AAAA,EACvH;AACA,SAAO,EAAE,iBAAiB,4BAA4B,QAAQ,qCAAqC,OAAO,UAAU;AACtH;AAYe,SAAR,iBAAkC;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AACxB,QAAM,eAAe,MAAM;AAAA,IACzB,MAAM,KAAK,IAAI,GAAG,yBAAyB,MAAM,KAAK,MAAM,KAAK,IAAI,CAAC;AAAA,IACtE,CAAC,MAAM,KAAK,MAAM,KAAK;AAAA,EACzB;AACA,QAAM,cAAc,SAAS,IAAI;AACjC,QAAM,gBAAgB,MAAM,QAAQ,MAAM,qBAAqB,OAAO,KAAK,GAAG,CAAC,OAAO,KAAK,CAAC;AAC5F,QAAM,SAAS,MAAM;AAAA,IACnB,MAAM,cAAc,IAAI,CAAC,UAAU;AAAA,MACjC,IAAI,KAAK;AAAA,MACT,OAAO,KAAK;AAAA,MACZ,OAAO,KAAK;AAAA,MACZ,KAAK,KAAK;AAAA,MACV,UAAU;AAAA,IACZ,EAAE;AAAA,IACF,CAAC,aAAa;AAAA,EAChB;AAEA,QAAM,iBAAiB,MAAM,YAAY,CAAC,MAAY,aAAoB;AACxE,UAAM,eAAgB,YAAY;AAClC,kBAAc,YAAY,MAAM,cAAc,YAAY,CAAC;AAAA,EAC7D,GAAG,CAAC,cAAc,aAAa,aAAa,CAAC;AAE7C,QAAM,oBAAoB,MAAM,YAAY,CAAC,WAAgD,aAAoB;AAC/G,UAAM,eAAgB,YAAY;AAClC,UAAM,aAAa,eAAe,WAAW,cAAc,YAAY;AACvE,QAAI,WAAY,eAAc,UAAU;AAAA,EAC1C,GAAG,CAAC,cAAc,aAAa,aAAa,CAAC;AAE7C,QAAM,mBAAmB,MAAM,YAAY,CAAC,aAAmB;AAC7D,UAAM,WAAW;AACjB,QAAI,aAAa,MAAM;AACrB,mBAAa,QAAQ;AACrB,oBAAc,YAAY,oBAAI,KAAK,GAAG,UAAU,YAAY,CAAC;AAAA,IAC/D;AAAA,EACF,GAAG,CAAC,cAAc,eAAe,cAAc,IAAI,CAAC;AAEpD,QAAM,oBAAoB,MAAM;AAAA,IAC9B,CAAC,UAAyB,cAAc,MAAM,QAAQ;AAAA,IACtD,CAAC,WAAW;AAAA,EACd;AAEA,QAAM,mBAAmB,MAAM;AAAA,IAC7B,CAAC,SAAmB;AAClB,UAAI,CAAC,YAAa;AAClB,kBAAY,EAAE,OAAO,KAAK,OAAO,KAAK,KAAK,IAAI,CAAC;AAAA,IAClD;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAEA,QAAM,kBAAkB,MAAM;AAAA,IAC5B,CAAC,WAA0B,EAAE,OAAO,eAAe,MAAM,QAAQ,EAAE;AAAA,IACnE,CAAC;AAAA,EACH;AAEA,QAAM,gBAAgB,MAAM,QAA6B,OAAO,EAAE,QAAQ,IAAI,IAAI,CAAC,CAAC;AAEpF,QAAM,aAAa,MAAM;AAAA,IACvB,OAAO;AAAA,MACL,OAAO,CAAC,EAAE,MAAM,MAAgC;AAC9C,cAAM,WAAW,MAAM;AACvB,cAAM,UAAU,QAAQ,SAAS,SAAS,KAAK,OAAO,gBAAgB;AACtE,eACE,qBAAC,SAAI,WAAU,2CACb;AAAA,8BAAC,UAAK,WAAU,gCAAgC,mBAAS,OAAM;AAAA,UAC9D,UACC;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,WAAU;AAAA,cACV,SAAS,CAAC,eAAe;AACvB,2BAAW,gBAAgB;AAC3B,8BAAc,QAAQ;AAAA,cACxB;AAAA,cAEC,mBAAS;AAAA;AAAA,UACZ,IACE;AAAA,WACN;AAAA,MAEJ;AAAA,IACF;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,SAAQ;AAAA,MACR;AAAA,MACA,MAAM;AAAA,MACN,MAAM,MAAM;AAAA,MACZ,SAAS;AAAA,MACT,YAAY,QAAQ,WAAW;AAAA,MAC/B,OAAK;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,eAAe;AAAA,MACf,cAAc;AAAA,MACd;AAAA,MACA;AAAA,MACA,OAAO;AAAA;AAAA,EACT;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -5,7 +5,17 @@ import { Button } from "../../primitives/button.js";
5
5
  import { Input } from "../../primitives/input.js";
6
6
  import { cn } from "@open-mercato/shared/lib/utils";
7
7
  import { useT } from "@open-mercato/shared/lib/i18n/context";
8
- import { addDays, addMonths, addWeeks, differenceInCalendarDays, endOfDay, endOfMonth, endOfWeek, format, startOfDay, startOfMonth, startOfWeek } from "date-fns";
8
+ import { addDays } from "date-fns/addDays";
9
+ import { addMonths } from "date-fns/addMonths";
10
+ import { addWeeks } from "date-fns/addWeeks";
11
+ import { differenceInCalendarDays } from "date-fns/differenceInCalendarDays";
12
+ import { endOfDay } from "date-fns/endOfDay";
13
+ import { endOfMonth } from "date-fns/endOfMonth";
14
+ import { endOfWeek } from "date-fns/endOfWeek";
15
+ import { format } from "date-fns/format";
16
+ import { startOfDay } from "date-fns/startOfDay";
17
+ import { startOfMonth } from "date-fns/startOfMonth";
18
+ import { startOfWeek } from "date-fns/startOfWeek";
9
19
  import { enUS } from "date-fns/locale/en-US";
10
20
  import { ChevronLeft, ChevronRight } from "lucide-react";
11
21
  const VIEW_OPTIONS = [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/schedule/ScheduleToolbar.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '../../primitives/button'\nimport { Input } from '../../primitives/input'\nimport type { ScheduleRange, ScheduleViewMode } from './types'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { addDays, addMonths, addWeeks, differenceInCalendarDays, endOfDay, endOfMonth, endOfWeek, format, startOfDay, startOfMonth, startOfWeek } from 'date-fns'\nimport { enUS } from 'date-fns/locale/en-US'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\n\nconst VIEW_OPTIONS: Array<{ id: ScheduleViewMode; labelKey: string; fallback: string }> = [\n { id: 'day', labelKey: 'schedule.view.day', fallback: 'Day' },\n { id: 'week', labelKey: 'schedule.view.week', fallback: 'Week' },\n { id: 'month', labelKey: 'schedule.view.month', fallback: 'Month' },\n { id: 'agenda', labelKey: 'schedule.view.agenda', fallback: 'Agenda' },\n]\n\nfunction formatDateInputValue(value: Date): string {\n const year = value.getFullYear()\n const month = String(value.getMonth() + 1).padStart(2, '0')\n const day = String(value.getDate()).padStart(2, '0')\n return `${year}-${month}-${day}`\n}\n\nfunction parseDateInputValue(value: string, fallback: Date): Date {\n if (!value) return fallback\n const next = new Date(`${value}T00:00:00`)\n return Number.isNaN(next.getTime()) ? fallback : next\n}\n\nexport type ScheduleToolbarProps = {\n view: ScheduleViewMode\n range: ScheduleRange\n timezone?: string\n onViewChange: (view: ScheduleViewMode) => void\n onRangeChange: (range: ScheduleRange) => void\n onTimezoneChange?: (timezone: string) => void\n className?: string\n}\n\nexport function ScheduleToolbar({\n view,\n range,\n timezone,\n onViewChange,\n onRangeChange,\n onTimezoneChange,\n className,\n}: ScheduleToolbarProps) {\n const t = useT()\n const rangeLength = React.useMemo(\n () => Math.max(1, differenceInCalendarDays(range.end, range.start) + 1),\n [range.end, range.start],\n )\n const deriveRangeForView = React.useCallback((base: Date, nextView: ScheduleViewMode): ScheduleRange => {\n if (nextView === 'day') {\n const start = startOfDay(base)\n return { start, end: endOfDay(start) }\n }\n if (nextView === 'week') {\n return { start: startOfWeek(base, { locale: enUS }), end: endOfWeek(base, { locale: enUS }) }\n }\n if (nextView === 'month') {\n return { start: startOfMonth(base), end: endOfMonth(base) }\n }\n const start = startOfDay(base)\n return { start, end: endOfDay(addDays(start, rangeLength - 1)) }\n }, [rangeLength])\n const rangeLabel = React.useMemo(() => {\n if (view === 'day') {\n return format(range.start, 'EEE, MMM d')\n }\n if (view === 'week') {\n const startLabel = format(range.start, 'MMM d')\n const endLabel = format(range.end, 'MMM d')\n const yearLabel = format(range.start, 'yyyy')\n return `${startLabel} - ${endLabel}, ${yearLabel}`\n }\n if (view === 'month') {\n return format(range.start, 'MMMM yyyy')\n }\n const startLabel = format(range.start, 'MMM d')\n const endLabel = format(range.end, 'MMM d, yyyy')\n return `${startLabel} - ${endLabel}`\n }, [range.end, range.start, view])\n\n const shiftRange = React.useCallback((direction: 'prev' | 'next') => {\n const multiplier = direction === 'prev' ? -1 : 1\n if (view === 'day') {\n const nextStart = startOfDay(addDays(range.start, multiplier))\n onRangeChange({ start: nextStart, end: endOfDay(nextStart) })\n return\n }\n if (view === 'week') {\n const base = addWeeks(range.start, multiplier)\n onRangeChange({\n start: startOfWeek(base, { locale: enUS }),\n end: endOfWeek(base, { locale: enUS }),\n })\n return\n }\n if (view === 'month') {\n const base = addMonths(range.start, multiplier)\n onRangeChange({ start: startOfMonth(base), end: endOfMonth(base) })\n return\n }\n const nextStart = startOfDay(addDays(range.start, multiplier * rangeLength))\n onRangeChange({ start: nextStart, end: endOfDay(addDays(nextStart, rangeLength - 1)) })\n }, [onRangeChange, range.start, rangeLength, view])\n\n return (\n <div className={cn('flex flex-col gap-3 rounded-xl border bg-card p-4 md:flex-row md:items-center md:justify-between', className)}>\n <div className=\"flex flex-wrap items-center gap-2\">\n {VIEW_OPTIONS.map((option) => (\n <Button\n key={option.id}\n variant={view === option.id ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => {\n if (option.id === view) return\n onViewChange(option.id)\n onRangeChange(deriveRangeForView(new Date(), option.id))\n }}\n >\n {t(option.labelKey, option.fallback)}\n </Button>\n ))}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => shiftRange('prev')} aria-label={t('schedule.range.prev', 'Previous')}>\n <ChevronLeft className=\"size-4\" aria-hidden />\n </Button>\n <div className=\"text-sm font-medium text-foreground\">{rangeLabel}</div>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => shiftRange('next')} aria-label={t('schedule.range.next', 'Next')}>\n <ChevronRight className=\"size-4\" aria-hidden />\n </Button>\n </div>\n <div className=\"flex flex-wrap items-center gap-3\">\n <label className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <span>{t('schedule.range.start', 'Start')}</span>\n <Input\n type=\"date\"\n value={formatDateInputValue(range.start)}\n onChange={(event) => {\n const nextStart = parseDateInputValue(event.target.value, range.start)\n onRangeChange({ start: nextStart, end: range.end })\n }}\n className=\"h-8 w-full sm:w-[140px]\"\n />\n </label>\n <label className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <span>{t('schedule.range.end', 'End')}</span>\n <Input\n type=\"date\"\n value={formatDateInputValue(range.end)}\n onChange={(event) => {\n const nextEnd = parseDateInputValue(event.target.value, range.end)\n onRangeChange({ start: range.start, end: nextEnd })\n }}\n className=\"h-8 w-full sm:w-[140px]\"\n />\n </label>\n <label className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <span>{t('schedule.range.timezone', 'Timezone')}</span>\n <Input\n type=\"text\"\n value={timezone ?? ''}\n onChange={(event) => onTimezoneChange?.(event.target.value)}\n className=\"h-8 w-full sm:w-[180px]\"\n placeholder={t('schedule.range.timezone.placeholder', 'UTC')}\n />\n </label>\n </div>\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '../../primitives/button'\nimport { Input } from '../../primitives/input'\nimport type { ScheduleRange, ScheduleViewMode } from './types'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { addDays } from 'date-fns/addDays'\nimport { addMonths } from 'date-fns/addMonths'\nimport { addWeeks } from 'date-fns/addWeeks'\nimport { differenceInCalendarDays } from 'date-fns/differenceInCalendarDays'\nimport { endOfDay } from 'date-fns/endOfDay'\nimport { endOfMonth } from 'date-fns/endOfMonth'\nimport { endOfWeek } from 'date-fns/endOfWeek'\nimport { format } from 'date-fns/format'\nimport { startOfDay } from 'date-fns/startOfDay'\nimport { startOfMonth } from 'date-fns/startOfMonth'\nimport { startOfWeek } from 'date-fns/startOfWeek'\nimport { enUS } from 'date-fns/locale/en-US'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\n\nconst VIEW_OPTIONS: Array<{ id: ScheduleViewMode; labelKey: string; fallback: string }> = [\n { id: 'day', labelKey: 'schedule.view.day', fallback: 'Day' },\n { id: 'week', labelKey: 'schedule.view.week', fallback: 'Week' },\n { id: 'month', labelKey: 'schedule.view.month', fallback: 'Month' },\n { id: 'agenda', labelKey: 'schedule.view.agenda', fallback: 'Agenda' },\n]\n\nfunction formatDateInputValue(value: Date): string {\n const year = value.getFullYear()\n const month = String(value.getMonth() + 1).padStart(2, '0')\n const day = String(value.getDate()).padStart(2, '0')\n return `${year}-${month}-${day}`\n}\n\nfunction parseDateInputValue(value: string, fallback: Date): Date {\n if (!value) return fallback\n const next = new Date(`${value}T00:00:00`)\n return Number.isNaN(next.getTime()) ? fallback : next\n}\n\nexport type ScheduleToolbarProps = {\n view: ScheduleViewMode\n range: ScheduleRange\n timezone?: string\n onViewChange: (view: ScheduleViewMode) => void\n onRangeChange: (range: ScheduleRange) => void\n onTimezoneChange?: (timezone: string) => void\n className?: string\n}\n\nexport function ScheduleToolbar({\n view,\n range,\n timezone,\n onViewChange,\n onRangeChange,\n onTimezoneChange,\n className,\n}: ScheduleToolbarProps) {\n const t = useT()\n const rangeLength = React.useMemo(\n () => Math.max(1, differenceInCalendarDays(range.end, range.start) + 1),\n [range.end, range.start],\n )\n const deriveRangeForView = React.useCallback((base: Date, nextView: ScheduleViewMode): ScheduleRange => {\n if (nextView === 'day') {\n const start = startOfDay(base)\n return { start, end: endOfDay(start) }\n }\n if (nextView === 'week') {\n return { start: startOfWeek(base, { locale: enUS }), end: endOfWeek(base, { locale: enUS }) }\n }\n if (nextView === 'month') {\n return { start: startOfMonth(base), end: endOfMonth(base) }\n }\n const start = startOfDay(base)\n return { start, end: endOfDay(addDays(start, rangeLength - 1)) }\n }, [rangeLength])\n const rangeLabel = React.useMemo(() => {\n if (view === 'day') {\n return format(range.start, 'EEE, MMM d')\n }\n if (view === 'week') {\n const startLabel = format(range.start, 'MMM d')\n const endLabel = format(range.end, 'MMM d')\n const yearLabel = format(range.start, 'yyyy')\n return `${startLabel} - ${endLabel}, ${yearLabel}`\n }\n if (view === 'month') {\n return format(range.start, 'MMMM yyyy')\n }\n const startLabel = format(range.start, 'MMM d')\n const endLabel = format(range.end, 'MMM d, yyyy')\n return `${startLabel} - ${endLabel}`\n }, [range.end, range.start, view])\n\n const shiftRange = React.useCallback((direction: 'prev' | 'next') => {\n const multiplier = direction === 'prev' ? -1 : 1\n if (view === 'day') {\n const nextStart = startOfDay(addDays(range.start, multiplier))\n onRangeChange({ start: nextStart, end: endOfDay(nextStart) })\n return\n }\n if (view === 'week') {\n const base = addWeeks(range.start, multiplier)\n onRangeChange({\n start: startOfWeek(base, { locale: enUS }),\n end: endOfWeek(base, { locale: enUS }),\n })\n return\n }\n if (view === 'month') {\n const base = addMonths(range.start, multiplier)\n onRangeChange({ start: startOfMonth(base), end: endOfMonth(base) })\n return\n }\n const nextStart = startOfDay(addDays(range.start, multiplier * rangeLength))\n onRangeChange({ start: nextStart, end: endOfDay(addDays(nextStart, rangeLength - 1)) })\n }, [onRangeChange, range.start, rangeLength, view])\n\n return (\n <div className={cn('flex flex-col gap-3 rounded-xl border bg-card p-4 md:flex-row md:items-center md:justify-between', className)}>\n <div className=\"flex flex-wrap items-center gap-2\">\n {VIEW_OPTIONS.map((option) => (\n <Button\n key={option.id}\n variant={view === option.id ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => {\n if (option.id === view) return\n onViewChange(option.id)\n onRangeChange(deriveRangeForView(new Date(), option.id))\n }}\n >\n {t(option.labelKey, option.fallback)}\n </Button>\n ))}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => shiftRange('prev')} aria-label={t('schedule.range.prev', 'Previous')}>\n <ChevronLeft className=\"size-4\" aria-hidden />\n </Button>\n <div className=\"text-sm font-medium text-foreground\">{rangeLabel}</div>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => shiftRange('next')} aria-label={t('schedule.range.next', 'Next')}>\n <ChevronRight className=\"size-4\" aria-hidden />\n </Button>\n </div>\n <div className=\"flex flex-wrap items-center gap-3\">\n <label className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <span>{t('schedule.range.start', 'Start')}</span>\n <Input\n type=\"date\"\n value={formatDateInputValue(range.start)}\n onChange={(event) => {\n const nextStart = parseDateInputValue(event.target.value, range.start)\n onRangeChange({ start: nextStart, end: range.end })\n }}\n className=\"h-8 w-full sm:w-[140px]\"\n />\n </label>\n <label className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <span>{t('schedule.range.end', 'End')}</span>\n <Input\n type=\"date\"\n value={formatDateInputValue(range.end)}\n onChange={(event) => {\n const nextEnd = parseDateInputValue(event.target.value, range.end)\n onRangeChange({ start: range.start, end: nextEnd })\n }}\n className=\"h-8 w-full sm:w-[140px]\"\n />\n </label>\n <label className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <span>{t('schedule.range.timezone', 'Timezone')}</span>\n <Input\n type=\"text\"\n value={timezone ?? ''}\n onChange={(event) => onTimezoneChange?.(event.target.value)}\n className=\"h-8 w-full sm:w-[180px]\"\n placeholder={t('schedule.range.timezone.placeholder', 'UTC')}\n />\n </label>\n </div>\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["start", "startLabel", "endLabel", "nextStart"]
7
7
  }
@@ -3,7 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { DayPicker, useDayPicker } from "react-day-picker";
5
5
  import { ChevronLeft, ChevronRight } from "lucide-react";
6
- import { format } from "date-fns";
6
+ import { format } from "date-fns/format";
7
7
  import { cn } from "@open-mercato/shared/lib/utils";
8
8
  function MonthNavButton({
9
9
  direction,