@open-mercato/core 0.7.1-develop.7186.1.6e080a5017 → 0.7.1-develop.7193.1.910a5b0a1e

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/TasksSection.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Loader2, Pencil, Trash2 } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { StatusBadge, type StatusBadgeVariant } from '@open-mercato/ui/primitives/status-badge'\nimport { mapDictionaryColorToTone } from '@open-mercato/shared/lib/query/advanced-filter'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { LoadingMessage, TabEmptyState } from '@open-mercato/ui/backend/detail'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { InteractionSummary, SectionAction, TabEmptyStateConfig, TodoLinkSummary, Translator } from './types'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { formatDate, resolveTodoHref } from './utils'\nimport { formatDateTime } from '@open-mercato/shared/lib/time'\nimport { TimelineItemHeader } from './TimelineItemHeader'\nimport { TaskDialog } from './TaskDialog'\nimport { usePersonTasks, type TaskFormPayload } from './hooks/usePersonTasks'\nimport { useCustomerDictionary } from './hooks/useCustomerDictionary'\nimport { useInteractions, type InteractionCreatePayload } from './hooks/useInteractions'\nimport { mapInteractionRecordToTodoSummary } from '../../lib/interactionCompatibility'\n\ntype GuardedMutationRunner = <T>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\ntype TasksSectionProps = {\n entityId: string | null\n initialTasks: TodoLinkSummary[]\n emptyLabel: string\n addActionLabel: string\n emptyState: TabEmptyStateConfig\n onActionChange?: (action: SectionAction | null) => void\n onLoadingChange?: (isLoading: boolean) => void\n onDataRefresh?: () => void\n translator?: Translator\n entityName?: string | null\n dialogContextKey?: string\n dialogContextFallback?: string\n /** When true, use the canonical interactions API instead of the legacy todos API. */\n useCanonicalInteractions?: boolean\n runGuardedMutation?: GuardedMutationRunner\n}\n\nconst RESERVED_TASK_CUSTOM_KEYS = new Set(['priority', 'description', 'due_at', 'dueAt'])\n\nfunction toTimestamp(value: string | null | undefined): number | null {\n if (!value) return null\n const timestamp = new Date(value).getTime()\n return Number.isNaN(timestamp) ? null : timestamp\n}\n\nfunction sortTaskSummaries(tasks: TodoLinkSummary[]): TodoLinkSummary[] {\n return [...tasks].sort((left, right) => {\n const leftDue = toTimestamp(left.dueAt)\n const rightDue = toTimestamp(right.dueAt)\n if (leftDue !== null || rightDue !== null) {\n if (leftDue === null) return 1\n if (rightDue === null) return -1\n if (leftDue !== rightDue) return leftDue - rightDue\n }\n const leftCreated = toTimestamp(left.createdAt) ?? 0\n const rightCreated = toTimestamp(right.createdAt) ?? 0\n if (leftCreated !== rightCreated) return rightCreated - leftCreated\n return left.id.localeCompare(right.id)\n })\n}\n\nconst STATUS_BADGE_VARIANTS = new Set<StatusBadgeVariant>(['success', 'warning', 'error', 'info', 'neutral'])\n\nfunction coerceStatusBadgeVariant(tone: ReturnType<typeof mapDictionaryColorToTone>): StatusBadgeVariant {\n return tone && STATUS_BADGE_VARIANTS.has(tone as StatusBadgeVariant) ? (tone as StatusBadgeVariant) : 'neutral'\n}\n\nfunction buildInitialFormValues(task: TodoLinkSummary | null): Record<string, unknown> | undefined {\n if (!task) return undefined\n const values: Record<string, unknown> = {\n title: task.title ?? '',\n is_done: task.isDone ?? false,\n status: task.status ?? 'planned',\n description: task.description ?? '',\n priority: task.priority ?? '',\n scheduledAt: task.dueAt ?? '',\n }\n if (task.customValues) {\n for (const [key, value] of Object.entries(task.customValues)) {\n if (RESERVED_TASK_CUSTOM_KEYS.has(key)) continue\n const formKey = `cf_${key}`\n if (values[formKey] === undefined) values[formKey] = value\n }\n }\n return values\n}\n\nexport function TasksSection({\n entityId,\n initialTasks,\n emptyLabel,\n addActionLabel,\n emptyState,\n onActionChange,\n onLoadingChange,\n onDataRefresh,\n translator,\n entityName,\n dialogContextKey,\n dialogContextFallback,\n useCanonicalInteractions = false,\n runGuardedMutation,\n}: TasksSectionProps) {\n const tHook = useT()\n const fallbackTranslator = React.useMemo<Translator>(() => createTranslatorWithFallback(tHook), [tHook])\n const t: Translator = React.useMemo(() => translator ?? fallbackTranslator, [translator, fallbackTranslator])\n const runWriteMutation = React.useCallback(\n async <T,>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>): Promise<T> => {\n if (!runGuardedMutation) {\n return operation()\n }\n return runGuardedMutation(operation, mutationPayload)\n },\n [runGuardedMutation],\n )\n\n const scopeVersion = useOrganizationScopeVersion()\n const statusDictionary = useCustomerDictionary('interaction-statuses', scopeVersion)\n const statusMap = statusDictionary.data?.map ?? null\n\n // Legacy path: usePersonTasks (default)\n const legacyResult = usePersonTasks({ entityId, initialTasks })\n\n // Canonical path: useInteractions with planned-status filter\n const canonicalResult = useInteractions({\n entityId: useCanonicalInteractions ? entityId : null,\n typeFilter: 'task',\n })\n\n // Map canonical interactions to the TodoLinkSummary shape used by the rendering below\n const canonicalTasks = React.useMemo<TodoLinkSummary[]>(\n () => (useCanonicalInteractions ? canonicalResult.interactions.map(mapInteractionRecordToTodoSummary) : []),\n [useCanonicalInteractions, canonicalResult.interactions],\n )\n\n const canonicalCreateTask = React.useCallback(\n async (payload: TaskFormPayload) => {\n if (!entityId) throw new Error('Task creation requires an entity id')\n const interactionPayload: InteractionCreatePayload = {\n entityId,\n interactionType: 'task',\n title: payload.base.title,\n status: payload.base.status ?? (payload.base.is_done ? 'done' : 'planned'),\n priority: payload.base.priority ?? null,\n body: payload.base.description ?? null,\n scheduledAt: payload.base.scheduledAt ?? null,\n customValues: payload.custom,\n }\n await canonicalResult.createInteraction(interactionPayload)\n },\n [canonicalResult, entityId],\n )\n\n const canonicalUpdateTask = React.useCallback(\n async (task: TodoLinkSummary, payload: TaskFormPayload) => {\n await canonicalResult.updateInteraction(task.todoId, {\n title: payload.base.title,\n status: payload.base.status ?? (payload.base.is_done ? 'done' : 'planned'),\n priority: payload.base.priority ?? null,\n body: payload.base.description ?? null,\n scheduledAt: payload.base.scheduledAt ?? null,\n customValues: payload.custom,\n })\n },\n [canonicalResult],\n )\n\n const canonicalToggleTask = React.useCallback(\n async (task: TodoLinkSummary, nextIsDone: boolean) => {\n if (nextIsDone) {\n await canonicalResult.completeInteraction(task.todoId)\n } else {\n // Reopen: set status back to planned via update\n await canonicalResult.updateInteraction(task.todoId, { status: 'planned' })\n }\n },\n [canonicalResult],\n )\n\n const canonicalUnlinkTask = React.useCallback(\n async (task: TodoLinkSummary) => {\n await canonicalResult.deleteInteraction(task.todoId)\n },\n [canonicalResult],\n )\n\n // Unified interface: pick the active data source based on the flag\n const tasks = useCanonicalInteractions ? canonicalTasks : legacyResult.tasks\n const isInitialLoading = useCanonicalInteractions ? canonicalResult.isInitialLoading : legacyResult.isInitialLoading\n const isLoadingMore = useCanonicalInteractions ? canonicalResult.isLoadingMore : legacyResult.isLoadingMore\n const isMutating = useCanonicalInteractions ? canonicalResult.isMutating : legacyResult.isMutating\n const hasMore = useCanonicalInteractions ? canonicalResult.hasMore : legacyResult.hasMore\n const loadMore = useCanonicalInteractions ? canonicalResult.loadMore : legacyResult.loadMore\n const refresh = useCanonicalInteractions ? canonicalResult.refresh : legacyResult.refresh\n const createTask = useCanonicalInteractions ? canonicalCreateTask : legacyResult.createTask\n const updateTask = useCanonicalInteractions ? canonicalUpdateTask : legacyResult.updateTask\n const toggleTask = useCanonicalInteractions ? canonicalToggleTask : legacyResult.toggleTask\n const unlinkTask = useCanonicalInteractions ? canonicalUnlinkTask : legacyResult.unlinkTask\n const pendingTaskId = useCanonicalInteractions ? canonicalResult.pendingId : legacyResult.pendingTaskId\n const error = useCanonicalInteractions ? canonicalResult.error : legacyResult.error\n const sortedTasks = React.useMemo(() => sortTaskSummaries(tasks), [tasks])\n\n const [dialogOpen, setDialogOpen] = React.useState(false)\n const [dialogMode, setDialogMode] = React.useState<'create' | 'edit'>('create')\n const [editingTask, setEditingTask] = React.useState<TodoLinkSummary | null>(null)\n const sentinelRef = React.useRef<HTMLDivElement | null>(null)\n\n const dialogContextMessage = React.useMemo(() => {\n if (!dialogContextKey || !entityName) return undefined\n return t(dialogContextKey, dialogContextFallback ?? 'This task will be linked to {{name}}', { name: entityName })\n }, [dialogContextFallback, dialogContextKey, entityName, t])\n\n const openCreateDialog = React.useCallback(() => {\n setEditingTask(null)\n setDialogMode('create')\n setDialogOpen(true)\n }, [])\n\n const openEditDialog = React.useCallback((task: TodoLinkSummary) => {\n setEditingTask(task)\n setDialogMode('edit')\n setDialogOpen(true)\n }, [])\n\n const closeDialog = React.useCallback(() => {\n setDialogOpen(false)\n setEditingTask(null)\n }, [])\n\n React.useEffect(() => {\n if (!onActionChange) return\n if (!entityId) {\n onActionChange(null)\n return\n }\n onActionChange({\n label: addActionLabel,\n onClick: openCreateDialog,\n disabled: isMutating,\n })\n return () => {\n onActionChange(null)\n }\n }, [addActionLabel, entityId, isMutating, onActionChange, openCreateDialog])\n\n React.useEffect(() => {\n if (!onLoadingChange) return\n onLoadingChange(isInitialLoading || isMutating)\n }, [isInitialLoading, isMutating, onLoadingChange])\n\n React.useEffect(() => {\n if (!hasMore) return\n if (typeof IntersectionObserver === 'undefined') return\n const el = sentinelRef.current\n if (!el) return\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries.some((entry) => entry.isIntersecting)) {\n loadMore().catch(() => {})\n }\n },\n { rootMargin: '200px 0px 200px 0px' },\n )\n observer.observe(el)\n return () => {\n observer.disconnect()\n }\n }, [hasMore, loadMore])\n\n const handleCreate = React.useCallback(\n async (payload: TaskFormPayload) => {\n try {\n await runWriteMutation(\n () => createTask(payload),\n {\n entityId,\n title: payload.base.title,\n isDone: payload.base.is_done ?? undefined,\n },\n )\n flash(t('customers.people.detail.tasks.createSuccess', 'Task created'), 'success')\n await Promise.resolve(onDataRefresh?.())\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customers.people.detail.tasks.error', 'Failed to create task')\n flash(message, 'error')\n throw err\n }\n },\n [createTask, entityId, onDataRefresh, runWriteMutation, t],\n )\n\n const handleUpdate = React.useCallback(\n async (task: TodoLinkSummary, payload: TaskFormPayload) => {\n try {\n await runWriteMutation(\n () => updateTask(task, payload),\n {\n id: task.id,\n todoId: task.todoId,\n title: payload.base.title,\n isDone: payload.base.is_done ?? undefined,\n },\n )\n flash(t('customers.people.detail.tasks.updateSuccess', 'Task updated'), 'success')\n await Promise.resolve(onDataRefresh?.())\n } catch (err) {\n const message =\n err instanceof Error ? err.message : t('customers.people.detail.tasks.updateError', 'Failed to update task')\n flash(message, 'error')\n throw err\n }\n },\n [onDataRefresh, runWriteMutation, t, updateTask],\n )\n\n const handleToggle = React.useCallback(\n async (task: TodoLinkSummary, nextIsDone: boolean) => {\n try {\n await runWriteMutation(\n () => toggleTask(task, nextIsDone),\n {\n id: task.id,\n todoId: task.todoId,\n isDone: nextIsDone,\n },\n )\n flash(\n nextIsDone\n ? t('customers.people.detail.tasks.completeSuccess', 'Task marked as done')\n : t('customers.people.detail.tasks.reopenSuccess', 'Task reopened'),\n 'success',\n )\n await Promise.resolve(onDataRefresh?.())\n } catch (err) {\n const message =\n err instanceof Error ? err.message : t('customers.people.detail.tasks.toggleError', 'Failed to update task status')\n flash(message, 'error')\n }\n },\n [onDataRefresh, runWriteMutation, t, toggleTask],\n )\n\n const handleDelete = React.useCallback(\n async (task: TodoLinkSummary) => {\n try {\n await runWriteMutation(\n () => unlinkTask(task),\n {\n id: task.id,\n todoId: task.todoId,\n },\n )\n flash(t('customers.people.detail.tasks.deleteSuccess', 'Task removed'), 'success')\n await Promise.resolve(onDataRefresh?.())\n await refresh()\n } catch (err) {\n const message =\n err instanceof Error ? err.message : t('customers.people.detail.tasks.deleteError', 'Failed to remove task')\n flash(message, 'error')\n }\n },\n [onDataRefresh, refresh, runWriteMutation, t, unlinkTask],\n )\n\n const handleCancel = React.useCallback(\n async (task: TodoLinkSummary) => {\n if (!useCanonicalInteractions) return\n try {\n await runWriteMutation(\n () => canonicalResult.cancelInteraction(task.todoId),\n { id: task.todoId },\n )\n flash(t('customers.people.detail.tasks.cancelSuccess', 'Task canceled'), 'success')\n await Promise.resolve(onDataRefresh?.())\n } catch (err) {\n const message =\n err instanceof Error ? err.message : t('customers.people.detail.tasks.cancelError', 'Failed to cancel task')\n flash(message, 'error')\n }\n },\n [canonicalResult, onDataRefresh, runWriteMutation, t, useCanonicalInteractions],\n )\n\n const renderTaskMeta = React.useCallback(\n (task: TodoLinkSummary) => {\n const meta: string[] = []\n if (task.status === 'canceled') {\n meta.push(t('customers.people.detail.tasks.status.canceled', 'Canceled'))\n }\n if (typeof task.priority === 'number') {\n meta.push(t('customers.people.detail.tasks.priorityLabel', 'Priority {{priority}}', { priority: task.priority }))\n }\n if (task.severity) {\n meta.push(\n t(\n `customers.people.detail.tasks.severity.${task.severity}`,\n task.severity.charAt(0).toUpperCase() + task.severity.slice(1),\n ),\n )\n }\n if (task.dueAt) {\n const dueLabel =\n formatDate(task.dueAt) ??\n formatDateTime(task.dueAt) ??\n t('customers.people.detail.tasks.dueLabel', 'Due {{date}}', { date: task.dueAt })\n meta.push(t('customers.people.detail.tasks.dueLabel', 'Due {{date}}', { date: dueLabel }))\n }\n return meta\n },\n [t],\n )\n\n const handleDialogSubmit = React.useCallback(\n async (payload: TaskFormPayload) => {\n if (dialogMode === 'edit' && editingTask) {\n await handleUpdate(editingTask, payload)\n } else {\n await handleCreate(payload)\n }\n },\n [dialogMode, editingTask, handleCreate, handleUpdate],\n )\n\n const hasTasks = sortedTasks.length > 0\n\n return (\n <div className=\"mt-0 space-y-6\">\n <div className=\"space-y-4\">\n {isInitialLoading ? (\n <LoadingMessage\n label={t('customers.people.detail.tasks.loading', 'Loading tasks\u2026')}\n className=\"border-0 bg-transparent p-0 py-8 justify-center\"\n />\n ) : null}\n\n {!isInitialLoading && !hasTasks ? (\n <TabEmptyState title={emptyState.title}>\n <Button asChild variant=\"outline\" size=\"sm\" className=\"mt-2\">\n <Link href=\"/backend/customer-tasks\">\n {t('customers.people.detail.tasks.viewAll', 'View all tasks')}\n </Link>\n </Button>\n </TabEmptyState>\n ) : null}\n\n {!isInitialLoading && hasTasks ? (\n <div className=\"space-y-4\">\n {error ? (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-sm text-destructive\">\n {error}\n </div>\n ) : null}\n {sortedTasks.map((task) => {\n const todoHref = task.externalHref ?? resolveTodoHref(task.todoSource, task.todoId)\n const createdLabel = formatDateTime(task.createdAt) ?? emptyLabel\n const meta = renderTaskMeta(task)\n const title = task.title ?? t('customers.people.detail.tasks.untitled', 'Untitled task')\n const isDone = task.isDone === true\n const isCanceled = task.status === 'canceled'\n const checkboxId = `person-task-${task.id}`\n const isPendingToggle = pendingTaskId === task.todoId\n return (\n <article key={task.id} className=\"group space-y-3 rounded-lg border bg-card p-4 transition hover:border-border/70\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <TimelineItemHeader\n title={\n <span className=\"inline-flex items-center gap-2\">\n <input\n id={checkboxId}\n type=\"checkbox\"\n checked={isDone}\n onChange={(event) => {\n const next = event.target.checked\n void handleToggle(task, next)\n }}\n disabled={isMutating || isPendingToggle || isCanceled}\n className=\"h-4 w-4 rounded border\"\n />\n <span\n className={cn(\n 'text-sm font-semibold',\n isDone ? 'line-through text-muted-foreground' : undefined,\n isCanceled ? 'text-muted-foreground' : undefined,\n )}\n >\n {title}\n </span>\n {task.status ? (\n <StatusBadge\n variant={coerceStatusBadgeVariant(\n mapDictionaryColorToTone(statusMap?.[task.status]?.color ?? null),\n )}\n dot\n >\n {statusMap?.[task.status]?.label ?? task.status}\n </StatusBadge>\n ) : null}\n </span>\n }\n timestamp={task.createdAt}\n fallbackTimestampLabel={createdLabel}\n />\n <div className=\"flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100 focus-within:opacity-100\">\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => openEditDialog(task)}\n disabled={isMutating}\n aria-label={t('ui.actions.edit', 'Edit')}\n >\n {isMutating && editingTask?.id === task.id && dialogMode === 'edit' ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Pencil className=\"h-4 w-4\" />\n )}\n </IconButton>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => handleDelete(task)}\n disabled={isMutating}\n aria-label={t('ui.actions.delete', 'Delete')}\n >\n {isMutating ? <Loader2 className=\"h-4 w-4 animate-spin text-destructive\" /> : <Trash2 className=\"h-4 w-4\" />}\n </IconButton>\n </div>\n </div>\n {meta.length ? (\n <div className=\"flex flex-wrap items-center gap-3 text-xs text-muted-foreground\">\n {meta.map((entry) => (\n <span key={`${task.id}-${entry}`} className=\"rounded bg-muted px-2 py-1 text-xs font-medium text-muted-foreground\">\n {entry}\n </span>\n ))}\n </div>\n ) : null}\n {task.description ? (\n <p className=\"text-sm text-muted-foreground whitespace-pre-wrap\">{task.description}</p>\n ) : null}\n <div className=\"flex flex-wrap items-center gap-3 text-xs\">\n {useCanonicalInteractions && !isDone && !isCanceled ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-auto px-0 text-xs font-medium text-muted-foreground hover:bg-transparent hover:text-foreground\"\n onClick={() => void handleCancel(task)}\n disabled={isMutating || isPendingToggle}\n >\n {t('customers.people.detail.tasks.cancelAction', 'Cancel task')}\n </Button>\n ) : null}\n {todoHref ? (\n <Link href={todoHref} className=\"text-primary hover:underline\">\n {t('customers.people.detail.tasks.openTask', 'Open task')}\n </Link>\n ) : null}\n </div>\n </article>\n )\n })}\n <div ref={sentinelRef} />\n {hasMore ? (\n <div className=\"flex justify-center\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => loadMore().catch(() => {})} disabled={isLoadingMore}>\n {isLoadingMore ? (\n <>\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n {t('customers.people.detail.tasks.loadingMore', 'Loading\u2026')}\n </>\n ) : (\n t('customers.people.detail.tasks.loadMore', 'Load more')\n )}\n </Button>\n </div>\n ) : null}\n {isLoadingMore ? (\n <div className=\"flex justify-center text-xs text-muted-foreground\">\n <Loader2 className=\"mr-2 h-3 w-3 animate-spin\" />\n {t('customers.people.detail.tasks.loadingMore', 'Loading\u2026')}\n </div>\n ) : null}\n </div>\n ) : null}\n {!isInitialLoading && hasTasks ? (\n <div className=\"flex justify-center\">\n <Button asChild variant=\"outline\" size=\"sm\">\n <Link href=\"/backend/customer-tasks\">\n {t('customers.people.detail.tasks.viewAll', 'View all tasks')}\n </Link>\n </Button>\n </div>\n ) : null}\n </div>\n\n <TaskDialog\n open={dialogOpen}\n mode={dialogMode}\n onOpenChange={(next) => {\n if (!next) closeDialog()\n else setDialogOpen(true)\n }}\n initialValues={buildInitialFormValues(editingTask)}\n onSubmit={handleDialogSubmit}\n isSubmitting={isMutating}\n contextMessage={dialogContextMessage}\n useCanonicalInteractions={useCanonicalInteractions}\n />\n </div>\n )\n}\n\nexport default TasksSection\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Loader2, Pencil, Trash2 } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { StatusBadge, type StatusBadgeVariant } from '@open-mercato/ui/primitives/status-badge'\nimport { mapDictionaryColorToTone } from '@open-mercato/shared/lib/query/advanced-filter'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { LoadingMessage, TabEmptyState } from '@open-mercato/ui/backend/detail'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { InteractionSummary, SectionAction, TabEmptyStateConfig, TodoLinkSummary, Translator } from './types'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { formatDate, resolveTodoHref } from './utils'\nimport { formatDateTime } from '@open-mercato/shared/lib/time'\nimport { TimelineItemHeader } from './TimelineItemHeader'\nimport { TaskDialog } from './TaskDialog'\nimport { usePersonTasks, type TaskFormPayload } from './hooks/usePersonTasks'\nimport { useCustomerDictionary } from './hooks/useCustomerDictionary'\nimport { useInteractions, type InteractionCreatePayload } from './hooks/useInteractions'\nimport { mapInteractionRecordToTodoSummary } from '../../lib/interactionCompatibility'\n\ntype GuardedMutationRunner = <T>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\ntype TasksSectionProps = {\n entityId: string | null\n initialTasks: TodoLinkSummary[]\n emptyLabel: string\n addActionLabel: string\n emptyState: TabEmptyStateConfig\n onActionChange?: (action: SectionAction | null) => void\n onLoadingChange?: (isLoading: boolean) => void\n onDataRefresh?: () => void\n translator?: Translator\n entityName?: string | null\n dialogContextKey?: string\n dialogContextFallback?: string\n /** When true, use the canonical interactions API instead of the legacy todos API. */\n useCanonicalInteractions?: boolean\n runGuardedMutation?: GuardedMutationRunner\n}\n\nconst RESERVED_TASK_CUSTOM_KEYS = new Set(['priority', 'description', 'due_at', 'dueAt'])\n\nfunction toTimestamp(value: string | null | undefined): number | null {\n if (!value) return null\n const timestamp = new Date(value).getTime()\n return Number.isNaN(timestamp) ? null : timestamp\n}\n\nfunction sortTaskSummaries(tasks: TodoLinkSummary[]): TodoLinkSummary[] {\n return [...tasks].sort((left, right) => {\n const leftDue = toTimestamp(left.dueAt)\n const rightDue = toTimestamp(right.dueAt)\n if (leftDue !== null || rightDue !== null) {\n if (leftDue === null) return 1\n if (rightDue === null) return -1\n if (leftDue !== rightDue) return leftDue - rightDue\n }\n const leftCreated = toTimestamp(left.createdAt) ?? 0\n const rightCreated = toTimestamp(right.createdAt) ?? 0\n if (leftCreated !== rightCreated) return rightCreated - leftCreated\n return left.id.localeCompare(right.id)\n })\n}\n\nconst STATUS_BADGE_VARIANTS = new Set<StatusBadgeVariant>(['success', 'warning', 'error', 'info', 'neutral'])\n\nfunction coerceStatusBadgeVariant(tone: ReturnType<typeof mapDictionaryColorToTone>): StatusBadgeVariant {\n return tone && STATUS_BADGE_VARIANTS.has(tone as StatusBadgeVariant) ? (tone as StatusBadgeVariant) : 'neutral'\n}\n\nfunction buildInitialFormValues(task: TodoLinkSummary | null): Record<string, unknown> | undefined {\n if (!task) return undefined\n const values: Record<string, unknown> = {\n title: task.title ?? '',\n is_done: task.isDone ?? false,\n status: task.status ?? 'planned',\n description: task.description ?? '',\n priority: task.priority ?? '',\n scheduledAt: task.dueAt ?? '',\n }\n if (task.customValues) {\n for (const [key, value] of Object.entries(task.customValues)) {\n if (RESERVED_TASK_CUSTOM_KEYS.has(key)) continue\n const formKey = `cf_${key}`\n if (values[formKey] === undefined) values[formKey] = value\n }\n }\n return values\n}\n\nexport function TasksSection({\n entityId,\n initialTasks,\n emptyLabel,\n addActionLabel,\n emptyState,\n onActionChange,\n onLoadingChange,\n onDataRefresh,\n translator,\n entityName,\n dialogContextKey,\n dialogContextFallback,\n useCanonicalInteractions = false,\n runGuardedMutation,\n}: TasksSectionProps) {\n const tHook = useT()\n const fallbackTranslator = React.useMemo<Translator>(() => createTranslatorWithFallback(tHook), [tHook])\n const t: Translator = React.useMemo(() => translator ?? fallbackTranslator, [translator, fallbackTranslator])\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const runWriteMutation = React.useCallback(\n async <T,>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>): Promise<T> => {\n if (!runGuardedMutation) {\n return operation()\n }\n return runGuardedMutation(operation, mutationPayload)\n },\n [runGuardedMutation],\n )\n\n const scopeVersion = useOrganizationScopeVersion()\n const statusDictionary = useCustomerDictionary('interaction-statuses', scopeVersion)\n const statusMap = statusDictionary.data?.map ?? null\n\n // Legacy path: usePersonTasks (default)\n const legacyResult = usePersonTasks({ entityId, initialTasks })\n\n // Canonical path: useInteractions with planned-status filter\n const canonicalResult = useInteractions({\n entityId: useCanonicalInteractions ? entityId : null,\n typeFilter: 'task',\n })\n\n // Map canonical interactions to the TodoLinkSummary shape used by the rendering below\n const canonicalTasks = React.useMemo<TodoLinkSummary[]>(\n () => (useCanonicalInteractions ? canonicalResult.interactions.map(mapInteractionRecordToTodoSummary) : []),\n [useCanonicalInteractions, canonicalResult.interactions],\n )\n\n const canonicalCreateTask = React.useCallback(\n async (payload: TaskFormPayload) => {\n if (!entityId) throw new Error('Task creation requires an entity id')\n const interactionPayload: InteractionCreatePayload = {\n entityId,\n interactionType: 'task',\n title: payload.base.title,\n status: payload.base.status ?? (payload.base.is_done ? 'done' : 'planned'),\n priority: payload.base.priority ?? null,\n body: payload.base.description ?? null,\n scheduledAt: payload.base.scheduledAt ?? null,\n customValues: payload.custom,\n }\n await canonicalResult.createInteraction(interactionPayload)\n },\n [canonicalResult, entityId],\n )\n\n const canonicalUpdateTask = React.useCallback(\n async (task: TodoLinkSummary, payload: TaskFormPayload) => {\n await canonicalResult.updateInteraction(task.todoId, {\n title: payload.base.title,\n status: payload.base.status ?? (payload.base.is_done ? 'done' : 'planned'),\n priority: payload.base.priority ?? null,\n body: payload.base.description ?? null,\n scheduledAt: payload.base.scheduledAt ?? null,\n customValues: payload.custom,\n })\n },\n [canonicalResult],\n )\n\n const canonicalToggleTask = React.useCallback(\n async (task: TodoLinkSummary, nextIsDone: boolean) => {\n if (nextIsDone) {\n await canonicalResult.completeInteraction(task.todoId)\n } else {\n // Reopen: set status back to planned via update\n await canonicalResult.updateInteraction(task.todoId, { status: 'planned' })\n }\n },\n [canonicalResult],\n )\n\n const canonicalUnlinkTask = React.useCallback(\n async (task: TodoLinkSummary) => {\n await canonicalResult.deleteInteraction(task.todoId)\n },\n [canonicalResult],\n )\n\n // Unified interface: pick the active data source based on the flag\n const tasks = useCanonicalInteractions ? canonicalTasks : legacyResult.tasks\n const isInitialLoading = useCanonicalInteractions ? canonicalResult.isInitialLoading : legacyResult.isInitialLoading\n const isLoadingMore = useCanonicalInteractions ? canonicalResult.isLoadingMore : legacyResult.isLoadingMore\n const isMutating = useCanonicalInteractions ? canonicalResult.isMutating : legacyResult.isMutating\n const hasMore = useCanonicalInteractions ? canonicalResult.hasMore : legacyResult.hasMore\n const loadMore = useCanonicalInteractions ? canonicalResult.loadMore : legacyResult.loadMore\n const refresh = useCanonicalInteractions ? canonicalResult.refresh : legacyResult.refresh\n const createTask = useCanonicalInteractions ? canonicalCreateTask : legacyResult.createTask\n const updateTask = useCanonicalInteractions ? canonicalUpdateTask : legacyResult.updateTask\n const toggleTask = useCanonicalInteractions ? canonicalToggleTask : legacyResult.toggleTask\n const unlinkTask = useCanonicalInteractions ? canonicalUnlinkTask : legacyResult.unlinkTask\n const pendingTaskId = useCanonicalInteractions ? canonicalResult.pendingId : legacyResult.pendingTaskId\n const error = useCanonicalInteractions ? canonicalResult.error : legacyResult.error\n const sortedTasks = React.useMemo(() => sortTaskSummaries(tasks), [tasks])\n\n const [dialogOpen, setDialogOpen] = React.useState(false)\n const [dialogMode, setDialogMode] = React.useState<'create' | 'edit'>('create')\n const [editingTask, setEditingTask] = React.useState<TodoLinkSummary | null>(null)\n const sentinelRef = React.useRef<HTMLDivElement | null>(null)\n\n const dialogContextMessage = React.useMemo(() => {\n if (!dialogContextKey || !entityName) return undefined\n return t(dialogContextKey, dialogContextFallback ?? 'This task will be linked to {{name}}', { name: entityName })\n }, [dialogContextFallback, dialogContextKey, entityName, t])\n\n const openCreateDialog = React.useCallback(() => {\n setEditingTask(null)\n setDialogMode('create')\n setDialogOpen(true)\n }, [])\n\n const openEditDialog = React.useCallback((task: TodoLinkSummary) => {\n setEditingTask(task)\n setDialogMode('edit')\n setDialogOpen(true)\n }, [])\n\n const closeDialog = React.useCallback(() => {\n setDialogOpen(false)\n setEditingTask(null)\n }, [])\n\n React.useEffect(() => {\n if (!onActionChange) return\n if (!entityId) {\n onActionChange(null)\n return\n }\n onActionChange({\n label: addActionLabel,\n onClick: openCreateDialog,\n disabled: isMutating,\n })\n return () => {\n onActionChange(null)\n }\n }, [addActionLabel, entityId, isMutating, onActionChange, openCreateDialog])\n\n React.useEffect(() => {\n if (!onLoadingChange) return\n onLoadingChange(isInitialLoading || isMutating)\n }, [isInitialLoading, isMutating, onLoadingChange])\n\n React.useEffect(() => {\n if (!hasMore) return\n if (typeof IntersectionObserver === 'undefined') return\n const el = sentinelRef.current\n if (!el) return\n\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries.some((entry) => entry.isIntersecting)) {\n loadMore().catch(() => {})\n }\n },\n { rootMargin: '200px 0px 200px 0px' },\n )\n observer.observe(el)\n return () => {\n observer.disconnect()\n }\n }, [hasMore, loadMore])\n\n const handleCreate = React.useCallback(\n async (payload: TaskFormPayload) => {\n try {\n await runWriteMutation(\n () => createTask(payload),\n {\n entityId,\n title: payload.base.title,\n isDone: payload.base.is_done ?? undefined,\n },\n )\n flash(t('customers.people.detail.tasks.createSuccess', 'Task created'), 'success')\n await Promise.resolve(onDataRefresh?.())\n } catch (err) {\n const message = err instanceof Error ? err.message : t('customers.people.detail.tasks.error', 'Failed to create task')\n flash(message, 'error')\n throw err\n }\n },\n [createTask, entityId, onDataRefresh, runWriteMutation, t],\n )\n\n const handleUpdate = React.useCallback(\n async (task: TodoLinkSummary, payload: TaskFormPayload) => {\n try {\n await runWriteMutation(\n () => updateTask(task, payload),\n {\n id: task.id,\n todoId: task.todoId,\n title: payload.base.title,\n isDone: payload.base.is_done ?? undefined,\n },\n )\n flash(t('customers.people.detail.tasks.updateSuccess', 'Task updated'), 'success')\n await Promise.resolve(onDataRefresh?.())\n } catch (err) {\n const message =\n err instanceof Error ? err.message : t('customers.people.detail.tasks.updateError', 'Failed to update task')\n flash(message, 'error')\n throw err\n }\n },\n [onDataRefresh, runWriteMutation, t, updateTask],\n )\n\n const handleToggle = React.useCallback(\n async (task: TodoLinkSummary, nextIsDone: boolean) => {\n try {\n await runWriteMutation(\n () => toggleTask(task, nextIsDone),\n {\n id: task.id,\n todoId: task.todoId,\n isDone: nextIsDone,\n },\n )\n flash(\n nextIsDone\n ? t('customers.people.detail.tasks.completeSuccess', 'Task marked as done')\n : t('customers.people.detail.tasks.reopenSuccess', 'Task reopened'),\n 'success',\n )\n await Promise.resolve(onDataRefresh?.())\n } catch (err) {\n const message =\n err instanceof Error ? err.message : t('customers.people.detail.tasks.toggleError', 'Failed to update task status')\n flash(message, 'error')\n }\n },\n [onDataRefresh, runWriteMutation, t, toggleTask],\n )\n\n const handleDelete = React.useCallback(\n async (task: TodoLinkSummary) => {\n const approved = await confirm({\n title: t('customers.people.detail.tasks.deleteConfirmTitle', 'Remove task?'),\n description: t(\n 'customers.people.detail.tasks.deleteConfirmDescription',\n 'This task will be removed from every view it appears in. This action cannot be undone.',\n ),\n confirmText: t('customers.people.detail.tasks.deleteConfirmAction', 'Remove task'),\n variant: 'destructive',\n })\n if (!approved) return\n try {\n await runWriteMutation(\n () => unlinkTask(task),\n {\n id: task.id,\n todoId: task.todoId,\n },\n )\n flash(t('customers.people.detail.tasks.deleteSuccess', 'Task removed'), 'success')\n await Promise.resolve(onDataRefresh?.())\n await refresh()\n } catch (err) {\n const message =\n err instanceof Error ? err.message : t('customers.people.detail.tasks.deleteError', 'Failed to remove task')\n flash(message, 'error')\n }\n },\n [confirm, onDataRefresh, refresh, runWriteMutation, t, unlinkTask],\n )\n\n const handleCancel = React.useCallback(\n async (task: TodoLinkSummary) => {\n if (!useCanonicalInteractions) return\n try {\n await runWriteMutation(\n () => canonicalResult.cancelInteraction(task.todoId),\n { id: task.todoId },\n )\n flash(t('customers.people.detail.tasks.cancelSuccess', 'Task canceled'), 'success')\n await Promise.resolve(onDataRefresh?.())\n } catch (err) {\n const message =\n err instanceof Error ? err.message : t('customers.people.detail.tasks.cancelError', 'Failed to cancel task')\n flash(message, 'error')\n }\n },\n [canonicalResult, onDataRefresh, runWriteMutation, t, useCanonicalInteractions],\n )\n\n const renderTaskMeta = React.useCallback(\n (task: TodoLinkSummary) => {\n const meta: string[] = []\n if (task.status === 'canceled') {\n meta.push(t('customers.people.detail.tasks.status.canceled', 'Canceled'))\n }\n if (typeof task.priority === 'number') {\n meta.push(t('customers.people.detail.tasks.priorityLabel', 'Priority {{priority}}', { priority: task.priority }))\n }\n if (task.severity) {\n meta.push(\n t(\n `customers.people.detail.tasks.severity.${task.severity}`,\n task.severity.charAt(0).toUpperCase() + task.severity.slice(1),\n ),\n )\n }\n if (task.dueAt) {\n const dueLabel =\n formatDate(task.dueAt) ??\n formatDateTime(task.dueAt) ??\n t('customers.people.detail.tasks.dueLabel', 'Due {{date}}', { date: task.dueAt })\n meta.push(t('customers.people.detail.tasks.dueLabel', 'Due {{date}}', { date: dueLabel }))\n }\n return meta\n },\n [t],\n )\n\n const handleDialogSubmit = React.useCallback(\n async (payload: TaskFormPayload) => {\n if (dialogMode === 'edit' && editingTask) {\n await handleUpdate(editingTask, payload)\n } else {\n await handleCreate(payload)\n }\n },\n [dialogMode, editingTask, handleCreate, handleUpdate],\n )\n\n const hasTasks = sortedTasks.length > 0\n\n return (\n <div className=\"mt-0 space-y-6\">\n <div className=\"space-y-4\">\n {isInitialLoading ? (\n <LoadingMessage\n label={t('customers.people.detail.tasks.loading', 'Loading tasks\u2026')}\n className=\"border-0 bg-transparent p-0 py-8 justify-center\"\n />\n ) : null}\n\n {!isInitialLoading && !hasTasks ? (\n <TabEmptyState title={emptyState.title}>\n <Button asChild variant=\"outline\" size=\"sm\" className=\"mt-2\">\n <Link href=\"/backend/customer-tasks\">\n {t('customers.people.detail.tasks.viewAll', 'View all tasks')}\n </Link>\n </Button>\n </TabEmptyState>\n ) : null}\n\n {!isInitialLoading && hasTasks ? (\n <div className=\"space-y-4\">\n {error ? (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-sm text-destructive\">\n {error}\n </div>\n ) : null}\n {sortedTasks.map((task) => {\n const todoHref = task.externalHref ?? resolveTodoHref(task.todoSource, task.todoId)\n const createdLabel = formatDateTime(task.createdAt) ?? emptyLabel\n const meta = renderTaskMeta(task)\n const title = task.title ?? t('customers.people.detail.tasks.untitled', 'Untitled task')\n const isDone = task.isDone === true\n const isCanceled = task.status === 'canceled'\n const checkboxId = `person-task-${task.id}`\n const isPendingToggle = pendingTaskId === task.todoId\n return (\n <article key={task.id} className=\"group space-y-3 rounded-lg border bg-card p-4 transition hover:border-border/70\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <TimelineItemHeader\n title={\n <span className=\"inline-flex items-center gap-2\">\n <input\n id={checkboxId}\n type=\"checkbox\"\n checked={isDone}\n onChange={(event) => {\n const next = event.target.checked\n void handleToggle(task, next)\n }}\n disabled={isMutating || isPendingToggle || isCanceled}\n className=\"h-4 w-4 rounded border\"\n />\n <span\n className={cn(\n 'text-sm font-semibold',\n isDone ? 'line-through text-muted-foreground' : undefined,\n isCanceled ? 'text-muted-foreground' : undefined,\n )}\n >\n {title}\n </span>\n {task.status ? (\n <StatusBadge\n variant={coerceStatusBadgeVariant(\n mapDictionaryColorToTone(statusMap?.[task.status]?.color ?? null),\n )}\n dot\n >\n {statusMap?.[task.status]?.label ?? task.status}\n </StatusBadge>\n ) : null}\n </span>\n }\n timestamp={task.createdAt}\n fallbackTimestampLabel={createdLabel}\n />\n <div className=\"flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100 focus-within:opacity-100\">\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => openEditDialog(task)}\n disabled={isMutating}\n aria-label={t('ui.actions.edit', 'Edit')}\n >\n {isMutating && editingTask?.id === task.id && dialogMode === 'edit' ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Pencil className=\"h-4 w-4\" />\n )}\n </IconButton>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => handleDelete(task)}\n disabled={isMutating}\n aria-label={t('ui.actions.delete', 'Delete')}\n >\n {isMutating ? <Loader2 className=\"h-4 w-4 animate-spin text-destructive\" /> : <Trash2 className=\"h-4 w-4\" />}\n </IconButton>\n </div>\n </div>\n {meta.length ? (\n <div className=\"flex flex-wrap items-center gap-3 text-xs text-muted-foreground\">\n {meta.map((entry) => (\n <span key={`${task.id}-${entry}`} className=\"rounded bg-muted px-2 py-1 text-xs font-medium text-muted-foreground\">\n {entry}\n </span>\n ))}\n </div>\n ) : null}\n {task.description ? (\n <p className=\"text-sm text-muted-foreground whitespace-pre-wrap\">{task.description}</p>\n ) : null}\n <div className=\"flex flex-wrap items-center gap-3 text-xs\">\n {useCanonicalInteractions && !isDone && !isCanceled ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-auto px-0 text-xs font-medium text-muted-foreground hover:bg-transparent hover:text-foreground\"\n onClick={() => void handleCancel(task)}\n disabled={isMutating || isPendingToggle}\n >\n {t('customers.people.detail.tasks.cancelAction', 'Cancel task')}\n </Button>\n ) : null}\n {todoHref ? (\n <Link href={todoHref} className=\"text-primary hover:underline\">\n {t('customers.people.detail.tasks.openTask', 'Open task')}\n </Link>\n ) : null}\n </div>\n </article>\n )\n })}\n <div ref={sentinelRef} />\n {hasMore ? (\n <div className=\"flex justify-center\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => loadMore().catch(() => {})} disabled={isLoadingMore}>\n {isLoadingMore ? (\n <>\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n {t('customers.people.detail.tasks.loadingMore', 'Loading\u2026')}\n </>\n ) : (\n t('customers.people.detail.tasks.loadMore', 'Load more')\n )}\n </Button>\n </div>\n ) : null}\n {isLoadingMore ? (\n <div className=\"flex justify-center text-xs text-muted-foreground\">\n <Loader2 className=\"mr-2 h-3 w-3 animate-spin\" />\n {t('customers.people.detail.tasks.loadingMore', 'Loading\u2026')}\n </div>\n ) : null}\n </div>\n ) : null}\n {!isInitialLoading && hasTasks ? (\n <div className=\"flex justify-center\">\n <Button asChild variant=\"outline\" size=\"sm\">\n <Link href=\"/backend/customer-tasks\">\n {t('customers.people.detail.tasks.viewAll', 'View all tasks')}\n </Link>\n </Button>\n </div>\n ) : null}\n </div>\n\n <TaskDialog\n open={dialogOpen}\n mode={dialogMode}\n onOpenChange={(next) => {\n if (!next) closeDialog()\n else setDialogOpen(true)\n }}\n initialValues={buildInitialFormValues(editingTask)}\n onSubmit={handleDialogSubmit}\n isSubmitting={isMutating}\n contextMessage={dialogContextMessage}\n useCanonicalInteractions={useCanonicalInteractions}\n />\n\n {ConfirmDialogElement}\n </div>\n )\n}\n\nexport default TasksSection\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -14,9 +14,32 @@ const DEFAULT_REMINDER_MINUTES = {
14
14
  email: 15,
15
15
  note: 15
16
16
  };
17
+ const DEFAULT_SLOT_MINUTES = 30;
18
+ const TASK_DEFAULT_HOUR = 17;
19
+ const NEXT_DAY_START_HOUR = 9;
17
20
  function padDatePart(value) {
18
21
  return String(value).padStart(2, "0");
19
22
  }
23
+ function isSameLocalDay(left, right) {
24
+ return left.getFullYear() === right.getFullYear() && left.getMonth() === right.getMonth() && left.getDate() === right.getDate();
25
+ }
26
+ function nextSlotAfter(now) {
27
+ const next = new Date(now);
28
+ next.setSeconds(0, 0);
29
+ next.setMinutes(next.getMinutes() + (DEFAULT_SLOT_MINUTES - next.getMinutes() % DEFAULT_SLOT_MINUTES));
30
+ if (!isSameLocalDay(next, now)) {
31
+ next.setHours(NEXT_DAY_START_HOUR, 0, 0, 0);
32
+ }
33
+ return next;
34
+ }
35
+ function resolveDefaultActivityStart(type, now) {
36
+ if (type === "task") {
37
+ const endOfWorkingDay = new Date(now);
38
+ endOfWorkingDay.setHours(TASK_DEFAULT_HOUR, 0, 0, 0);
39
+ if (endOfWorkingDay.getTime() > now.getTime()) return endOfWorkingDay;
40
+ }
41
+ return nextSlotAfter(now);
42
+ }
20
43
  function formatLocalDateInput(date) {
21
44
  return `${date.getFullYear()}-${padDatePart(date.getMonth() + 1)}-${padDatePart(date.getDate())}`;
22
45
  }
@@ -24,10 +47,12 @@ function formatLocalTimeInput(date) {
24
47
  return `${padDatePart(date.getHours())}:${padDatePart(date.getMinutes())}`;
25
48
  }
26
49
  function useScheduleFormState({ open, editData }) {
50
+ const isEditing = Boolean(editData?.id);
51
+ const [initialStart] = React.useState(() => resolveDefaultActivityStart("meeting", /* @__PURE__ */ new Date()));
27
52
  const [activityType, setActivityType] = React.useState("meeting");
28
53
  const [title, setTitle] = React.useState("");
29
- const [date, setDate] = React.useState(() => formatLocalDateInput(/* @__PURE__ */ new Date()));
30
- const [startTime, setStartTime] = React.useState("10:00");
54
+ const [date, setDate] = React.useState(() => formatLocalDateInput(initialStart));
55
+ const [startTime, setStartTime] = React.useState(() => formatLocalTimeInput(initialStart));
31
56
  const [duration, setDuration] = React.useState(30);
32
57
  const [allDay, setAllDay] = React.useState(false);
33
58
  const [description, setDescription] = React.useState("");
@@ -52,10 +77,8 @@ function useScheduleFormState({ open, editData }) {
52
77
  setActivityType(resolvedType);
53
78
  setTitle(editData.title ?? "");
54
79
  const sourceTimestamp = editData.occurredAt ?? editData.scheduledAt ?? null;
55
- const seedDate = sourceTimestamp ? new Date(sourceTimestamp) : /* @__PURE__ */ new Date();
56
- const seedDateValid = !Number.isNaN(seedDate.getTime());
57
- const fallbackNow = /* @__PURE__ */ new Date();
58
- const dateForForm = seedDateValid ? seedDate : fallbackNow;
80
+ const seedDate = sourceTimestamp ? new Date(sourceTimestamp) : null;
81
+ const dateForForm = seedDate && !Number.isNaN(seedDate.getTime()) ? seedDate : resolveDefaultActivityStart(resolvedType, /* @__PURE__ */ new Date());
59
82
  setDate(formatLocalDateInput(dateForForm));
60
83
  setStartTime(formatLocalTimeInput(dateForForm));
61
84
  setDuration(editData.durationMinutes ?? 30);
@@ -108,10 +131,11 @@ function useScheduleFormState({ open, editData }) {
108
131
  });
109
132
  }
110
133
  } else {
134
+ const defaultStart = resolveDefaultActivityStart("meeting", /* @__PURE__ */ new Date());
111
135
  setActivityType("meeting");
112
136
  setTitle("");
113
- setDate(formatLocalDateInput(/* @__PURE__ */ new Date()));
114
- setStartTime("10:00");
137
+ setDate(formatLocalDateInput(defaultStart));
138
+ setStartTime(formatLocalTimeInput(defaultStart));
115
139
  setDuration(30);
116
140
  setAllDay(false);
117
141
  setDescription("");
@@ -131,14 +155,14 @@ function useScheduleFormState({ open, editData }) {
131
155
  }, [open, editData]);
132
156
  const lastReminderTypeRef = React.useRef("meeting");
133
157
  React.useEffect(() => {
134
- if (!open || editData) {
158
+ if (!open || isEditing) {
135
159
  lastReminderTypeRef.current = activityType;
136
160
  return;
137
161
  }
138
162
  if (lastReminderTypeRef.current === activityType) return;
139
163
  lastReminderTypeRef.current = activityType;
140
164
  setReminderMinutes(DEFAULT_REMINDER_MINUTES[activityType]);
141
- }, [activityType, editData, open]);
165
+ }, [activityType, isEditing, open]);
142
166
  const removeParticipant = React.useCallback((index) => {
143
167
  setParticipants((prev) => prev.filter((_, i) => i !== index));
144
168
  }, []);
@@ -198,6 +222,7 @@ function useScheduleFormState({ open, editData }) {
198
222
  }
199
223
  export {
200
224
  PARTICIPANT_COLORS,
225
+ resolveDefaultActivityStart,
201
226
  useScheduleFormState
202
227
  };
203
228
  //# sourceMappingURL=useScheduleFormState.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/customers/components/detail/schedule/useScheduleFormState.ts"],
4
- "sourcesContent": ["import * as React from 'react'\nimport type { ActivityType } from './fieldConfig'\n\nexport type RsvpStatus = 'pending' | 'accepted' | 'declined' | 'tentative'\n\nexport type Participant = {\n userId?: string\n name: string\n email?: string\n color?: string\n status?: RsvpStatus\n}\n\nexport type LinkedEntity = {\n id: string\n type: 'company' | 'deal' | 'offer'\n label: string\n}\n\nexport type ScheduleActivityEditData = {\n id: string\n /** Record version for the OSS optimistic-lock header on edit (#2055). */\n updatedAt?: string | null\n interactionType?: string\n title?: string | null\n body?: string | null\n scheduledAt?: string | null\n /**\n * Historical timestamp for completed activities (status `done`). Required for\n * the edit prefill to restore the original date/time instead of falling back\n * to \"today\" (#1807).\n */\n occurredAt?: string | null\n durationMinutes?: number | null\n /** Interaction priority column (0-100, nullable) backing the task priority control (#5943). */\n priority?: number | null\n location?: string | null\n allDay?: boolean | null\n recurrenceRule?: string | null\n recurrenceEnd?: string | null\n participants?: Array<{ userId?: string; name?: string; email?: string; status?: string }> | null\n reminderMinutes?: number | null\n visibility?: string | null\n linkedEntities?: Array<{ id: string; type: string; label: string }> | null\n guestPermissions?: { canInviteOthers?: boolean; canModify?: boolean; canSeeList?: boolean } | null\n}\n\nexport const PARTICIPANT_COLORS = [\n 'bg-chart-emerald',\n 'bg-chart-blue',\n 'bg-chart-orange',\n 'bg-chart-violet',\n 'bg-chart-pink',\n 'bg-chart-teal',\n]\n\n// Per-Figma defaults for the Reminder dropdown when the user picks an activity\n// type. Meeting/email keep the standard 15 min; tasks default to 1 day (1440 min)\n// because they're plan-ahead artefacts; calls default to 5 min as a stand-in for\n// the Figma \"After call ends\" treatment (which would need a non-numeric sentinel\n// in the API contract \u2014 tracked as a follow-up).\nconst DEFAULT_REMINDER_MINUTES: Record<ActivityType, number> = {\n meeting: 15,\n call: 5,\n task: 1440,\n email: 15,\n note: 15,\n}\n\nfunction padDatePart(value: number): string {\n return String(value).padStart(2, '0')\n}\n\nfunction formatLocalDateInput(date: Date): string {\n return `${date.getFullYear()}-${padDatePart(date.getMonth() + 1)}-${padDatePart(date.getDate())}`\n}\n\nfunction formatLocalTimeInput(date: Date): string {\n return `${padDatePart(date.getHours())}:${padDatePart(date.getMinutes())}`\n}\n\ninterface UseScheduleFormStateParams {\n open: boolean\n editData: ScheduleActivityEditData | null | undefined\n}\n\nexport function useScheduleFormState({ open, editData }: UseScheduleFormStateParams) {\n const [activityType, setActivityType] = React.useState<ActivityType>('meeting')\n const [title, setTitle] = React.useState('')\n const [date, setDate] = React.useState(() => formatLocalDateInput(new Date()))\n const [startTime, setStartTime] = React.useState('10:00')\n const [duration, setDuration] = React.useState(30)\n const [allDay, setAllDay] = React.useState(false)\n const [description, setDescription] = React.useState('')\n const [markdownEnabled, setMarkdownEnabled] = React.useState(true)\n const [location, setLocation] = React.useState('')\n const [reminderMinutes, setReminderMinutes] = React.useState(15)\n const [visibility, setVisibility] = React.useState('team')\n const [participants, setParticipants] = React.useState<Participant[]>([])\n const [linkedEntities, setLinkedEntities] = React.useState<LinkedEntity[]>([])\n const [recurrenceEnabled, setRecurrenceEnabled] = React.useState(false)\n const [recurrenceDays, setRecurrenceDays] = React.useState<boolean[]>([true, false, true, false, false, false, false])\n const [recurrenceEndType, setRecurrenceEndType] = React.useState<'never' | 'count' | 'date'>('never')\n const [recurrenceCount, setRecurrenceCount] = React.useState(8)\n const [recurrenceEndDate, setRecurrenceEndDate] = React.useState('')\n const [conflict, setConflict] = React.useState<string | null>(null)\n const [saving, setSaving] = React.useState(false)\n const [guestPermissions, setGuestPermissions] = React.useState({ canInviteOthers: true, canModify: false, canSeeList: true })\n\n React.useEffect(() => {\n if (open) {\n if (editData) {\n // Edit mode: populate from existing interaction\n const resolvedType = (editData.interactionType as ActivityType) ?? 'meeting'\n setActivityType(resolvedType)\n setTitle(editData.title ?? '')\n // For historical activities the canonical timestamp is `occurredAt`; for\n // planned/future ones it's `scheduledAt`. Without this fallback editing a\n // past activity prefilled to \"today\" instead of its actual moment (#1807).\n // Keep seed values in the user's local timezone, matching the cluster-E\n // local-day convention.\n const sourceTimestamp = editData.occurredAt ?? editData.scheduledAt ?? null\n const seedDate = sourceTimestamp ? new Date(sourceTimestamp) : new Date()\n const seedDateValid = !Number.isNaN(seedDate.getTime())\n const fallbackNow = new Date()\n const dateForForm = seedDateValid ? seedDate : fallbackNow\n setDate(formatLocalDateInput(dateForForm))\n setStartTime(formatLocalTimeInput(dateForForm))\n setDuration(editData.durationMinutes ?? 30)\n setAllDay(editData.allDay ?? false)\n setDescription(editData.body ?? '')\n setLocation(editData.location ?? '')\n // Use per-type default when the editData omits an explicit reminder\n // (the menu-driven \"New X\" flow opens the dialog with `reminderMinutes: null`).\n setReminderMinutes(editData.reminderMinutes ?? DEFAULT_REMINDER_MINUTES[resolvedType])\n setVisibility(editData.visibility ?? 'team')\n setParticipants(\n Array.isArray(editData.participants)\n ? editData.participants.map((p, i) => ({\n userId: p.userId,\n name: p.name ?? p.email ?? p.userId ?? '',\n email: p.email,\n color: PARTICIPANT_COLORS[i % PARTICIPANT_COLORS.length],\n status: (p.status ?? 'pending') as RsvpStatus,\n }))\n : [],\n )\n setLinkedEntities(\n Array.isArray(editData.linkedEntities)\n ? editData.linkedEntities.map((e) => ({ id: e.id, type: e.type as LinkedEntity['type'], label: e.label }))\n : [],\n )\n if (editData.recurrenceRule) {\n setRecurrenceEnabled(true)\n // Parse RRULE to set days and end type\n const rule = editData.recurrenceRule\n const byDayMatch = rule.match(/BYDAY=([A-Z,]+)/)\n if (byDayMatch) {\n const dayNames = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU']\n const activeDays = byDayMatch[1].split(',')\n setRecurrenceDays(dayNames.map((d) => activeDays.includes(d)))\n }\n const countMatch = rule.match(/COUNT=(\\d+)/)\n const untilMatch = rule.match(/UNTIL=(\\d{8})/)\n if (countMatch) {\n setRecurrenceEndType('count')\n setRecurrenceCount(Number(countMatch[1]))\n } else if (untilMatch) {\n setRecurrenceEndType('date')\n const raw = untilMatch[1]\n setRecurrenceEndDate(`${raw.slice(0, 4)}-${raw.slice(4, 6)}-${raw.slice(6, 8)}`)\n } else {\n setRecurrenceEndType('never')\n }\n } else {\n setRecurrenceEnabled(false)\n }\n if (editData.guestPermissions) {\n setGuestPermissions({\n canInviteOthers: editData.guestPermissions.canInviteOthers ?? true,\n canModify: editData.guestPermissions.canModify ?? false,\n canSeeList: editData.guestPermissions.canSeeList ?? true,\n })\n }\n } else {\n // Create mode: reset all fields\n setActivityType('meeting')\n setTitle('')\n setDate(formatLocalDateInput(new Date()))\n setStartTime('10:00')\n setDuration(30)\n setAllDay(false)\n setDescription('')\n setLocation('')\n setReminderMinutes(DEFAULT_REMINDER_MINUTES.meeting)\n setVisibility('team')\n setParticipants([])\n setLinkedEntities([])\n setRecurrenceEnabled(false)\n }\n setConflict(null)\n }\n return () => {\n // Safety net: restore body scroll if Radix Dialog fails to clean up\n document.body.style.removeProperty('overflow')\n document.body.style.removeProperty('pointer-events')\n }\n }, [open, editData])\n\n // Update the Reminder default when the activity type changes in create mode.\n // Skipped in edit mode (the persisted value wins), and gated by `open` to\n // avoid flipping the default in a closed-but-mounted dialog.\n const lastReminderTypeRef = React.useRef<ActivityType>('meeting')\n React.useEffect(() => {\n if (!open || editData) {\n lastReminderTypeRef.current = activityType\n return\n }\n if (lastReminderTypeRef.current === activityType) return\n lastReminderTypeRef.current = activityType\n setReminderMinutes(DEFAULT_REMINDER_MINUTES[activityType])\n }, [activityType, editData, open])\n\n const removeParticipant = React.useCallback((index: number) => {\n setParticipants((prev) => prev.filter((_, i) => i !== index))\n }, [])\n\n const toggleRecurrenceDay = React.useCallback((index: number) => {\n setRecurrenceDays((prev) => {\n const next = [...prev]\n next[index] = !next[index]\n return next\n })\n }, [])\n\n return {\n activityType,\n setActivityType,\n title,\n setTitle,\n date,\n setDate,\n startTime,\n setStartTime,\n duration,\n setDuration,\n allDay,\n setAllDay,\n description,\n setDescription,\n markdownEnabled,\n setMarkdownEnabled,\n location,\n setLocation,\n reminderMinutes,\n setReminderMinutes,\n visibility,\n setVisibility,\n participants,\n setParticipants,\n linkedEntities,\n setLinkedEntities,\n recurrenceEnabled,\n setRecurrenceEnabled,\n recurrenceDays,\n setRecurrenceDays,\n recurrenceEndType,\n setRecurrenceEndType,\n recurrenceCount,\n setRecurrenceCount,\n recurrenceEndDate,\n setRecurrenceEndDate,\n conflict,\n setConflict,\n saving,\n setSaving,\n guestPermissions,\n setGuestPermissions,\n removeParticipant,\n toggleRecurrenceDay,\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as React from 'react'\nimport type { ActivityType } from './fieldConfig'\n\nexport type RsvpStatus = 'pending' | 'accepted' | 'declined' | 'tentative'\n\nexport type Participant = {\n userId?: string\n name: string\n email?: string\n color?: string\n status?: RsvpStatus\n}\n\nexport type LinkedEntity = {\n id: string\n type: 'company' | 'deal' | 'offer'\n label: string\n}\n\nexport type ScheduleActivityEditData = {\n id: string\n /** Record version for the OSS optimistic-lock header on edit (#2055). */\n updatedAt?: string | null\n interactionType?: string\n title?: string | null\n body?: string | null\n scheduledAt?: string | null\n /**\n * Historical timestamp for completed activities (status `done`). Required for\n * the edit prefill to restore the original date/time instead of falling back\n * to \"today\" (#1807).\n */\n occurredAt?: string | null\n durationMinutes?: number | null\n /** Interaction priority column (0-100, nullable) backing the task priority control (#5943). */\n priority?: number | null\n location?: string | null\n allDay?: boolean | null\n recurrenceRule?: string | null\n recurrenceEnd?: string | null\n participants?: Array<{ userId?: string; name?: string; email?: string; status?: string }> | null\n reminderMinutes?: number | null\n visibility?: string | null\n linkedEntities?: Array<{ id: string; type: string; label: string }> | null\n guestPermissions?: { canInviteOthers?: boolean; canModify?: boolean; canSeeList?: boolean } | null\n}\n\nexport const PARTICIPANT_COLORS = [\n 'bg-chart-emerald',\n 'bg-chart-blue',\n 'bg-chart-orange',\n 'bg-chart-violet',\n 'bg-chart-pink',\n 'bg-chart-teal',\n]\n\n// Per-Figma defaults for the Reminder dropdown when the user picks an activity\n// type. Meeting/email keep the standard 15 min; tasks default to 1 day (1440 min)\n// because they're plan-ahead artefacts; calls default to 5 min as a stand-in for\n// the Figma \"After call ends\" treatment (which would need a non-numeric sentinel\n// in the API contract \u2014 tracked as a follow-up).\nconst DEFAULT_REMINDER_MINUTES: Record<ActivityType, number> = {\n meeting: 15,\n call: 5,\n task: 1440,\n email: 15,\n note: 15,\n}\n\n// Create-mode date/time defaults. A fixed morning slot made every activity opened\n// later in the day start in the past, so the seed is always computed forward from\n// \"now\" (#5940): tasks are plan-ahead artefacts and default to the end of the\n// working day, everything else to the next half-hour slot.\nconst DEFAULT_SLOT_MINUTES = 30\nconst TASK_DEFAULT_HOUR = 17\nconst NEXT_DAY_START_HOUR = 9\n\nfunction padDatePart(value: number): string {\n return String(value).padStart(2, '0')\n}\n\nfunction isSameLocalDay(left: Date, right: Date): boolean {\n return (\n left.getFullYear() === right.getFullYear() &&\n left.getMonth() === right.getMonth() &&\n left.getDate() === right.getDate()\n )\n}\n\nfunction nextSlotAfter(now: Date): Date {\n const next = new Date(now)\n next.setSeconds(0, 0)\n next.setMinutes(next.getMinutes() + (DEFAULT_SLOT_MINUTES - (next.getMinutes() % DEFAULT_SLOT_MINUTES)))\n if (!isSameLocalDay(next, now)) {\n next.setHours(NEXT_DAY_START_HOUR, 0, 0, 0)\n }\n return next\n}\n\n/**\n * Default start moment for a newly created activity of `type`, always strictly\n * after `now` so a brand-new record is never born overdue (#5940).\n */\nexport function resolveDefaultActivityStart(type: ActivityType, now: Date): Date {\n if (type === 'task') {\n const endOfWorkingDay = new Date(now)\n endOfWorkingDay.setHours(TASK_DEFAULT_HOUR, 0, 0, 0)\n if (endOfWorkingDay.getTime() > now.getTime()) return endOfWorkingDay\n }\n return nextSlotAfter(now)\n}\n\nfunction formatLocalDateInput(date: Date): string {\n return `${date.getFullYear()}-${padDatePart(date.getMonth() + 1)}-${padDatePart(date.getDate())}`\n}\n\nfunction formatLocalTimeInput(date: Date): string {\n return `${padDatePart(date.getHours())}:${padDatePart(date.getMinutes())}`\n}\n\ninterface UseScheduleFormStateParams {\n open: boolean\n editData: ScheduleActivityEditData | null | undefined\n}\n\nexport function useScheduleFormState({ open, editData }: UseScheduleFormStateParams) {\n // An empty `id` is the menu-driven \"New X\" convention \u2014 a create with a preset\n // type, not an edit. The save path already reads it this way (`isSaveEdit`), so\n // the seeding below must agree or new records inherit edit-mode fallbacks (#5940).\n const isEditing = Boolean(editData?.id)\n const [initialStart] = React.useState(() => resolveDefaultActivityStart('meeting', new Date()))\n const [activityType, setActivityType] = React.useState<ActivityType>('meeting')\n const [title, setTitle] = React.useState('')\n const [date, setDate] = React.useState(() => formatLocalDateInput(initialStart))\n const [startTime, setStartTime] = React.useState(() => formatLocalTimeInput(initialStart))\n const [duration, setDuration] = React.useState(30)\n const [allDay, setAllDay] = React.useState(false)\n const [description, setDescription] = React.useState('')\n const [markdownEnabled, setMarkdownEnabled] = React.useState(true)\n const [location, setLocation] = React.useState('')\n const [reminderMinutes, setReminderMinutes] = React.useState(15)\n const [visibility, setVisibility] = React.useState('team')\n const [participants, setParticipants] = React.useState<Participant[]>([])\n const [linkedEntities, setLinkedEntities] = React.useState<LinkedEntity[]>([])\n const [recurrenceEnabled, setRecurrenceEnabled] = React.useState(false)\n const [recurrenceDays, setRecurrenceDays] = React.useState<boolean[]>([true, false, true, false, false, false, false])\n const [recurrenceEndType, setRecurrenceEndType] = React.useState<'never' | 'count' | 'date'>('never')\n const [recurrenceCount, setRecurrenceCount] = React.useState(8)\n const [recurrenceEndDate, setRecurrenceEndDate] = React.useState('')\n const [conflict, setConflict] = React.useState<string | null>(null)\n const [saving, setSaving] = React.useState(false)\n const [guestPermissions, setGuestPermissions] = React.useState({ canInviteOthers: true, canModify: false, canSeeList: true })\n\n React.useEffect(() => {\n if (open) {\n if (editData) {\n // Edit mode: populate from existing interaction\n const resolvedType = (editData.interactionType as ActivityType) ?? 'meeting'\n setActivityType(resolvedType)\n setTitle(editData.title ?? '')\n // For historical activities the canonical timestamp is `occurredAt`; for\n // planned/future ones it's `scheduledAt`. Without this fallback editing a\n // past activity prefilled to \"today\" instead of its actual moment (#1807).\n // Keep seed values in the user's local timezone, matching the cluster-E\n // local-day convention.\n const sourceTimestamp = editData.occurredAt ?? editData.scheduledAt ?? null\n const seedDate = sourceTimestamp ? new Date(sourceTimestamp) : null\n // No usable timestamp means this is a preset create (or a corrupt row), so\n // fall forward to the create-mode default instead of \"now\" (#5940).\n const dateForForm =\n seedDate && !Number.isNaN(seedDate.getTime())\n ? seedDate\n : resolveDefaultActivityStart(resolvedType, new Date())\n setDate(formatLocalDateInput(dateForForm))\n setStartTime(formatLocalTimeInput(dateForForm))\n setDuration(editData.durationMinutes ?? 30)\n setAllDay(editData.allDay ?? false)\n setDescription(editData.body ?? '')\n setLocation(editData.location ?? '')\n // Use per-type default when the editData omits an explicit reminder\n // (the menu-driven \"New X\" flow opens the dialog with `reminderMinutes: null`).\n setReminderMinutes(editData.reminderMinutes ?? DEFAULT_REMINDER_MINUTES[resolvedType])\n setVisibility(editData.visibility ?? 'team')\n setParticipants(\n Array.isArray(editData.participants)\n ? editData.participants.map((p, i) => ({\n userId: p.userId,\n name: p.name ?? p.email ?? p.userId ?? '',\n email: p.email,\n color: PARTICIPANT_COLORS[i % PARTICIPANT_COLORS.length],\n status: (p.status ?? 'pending') as RsvpStatus,\n }))\n : [],\n )\n setLinkedEntities(\n Array.isArray(editData.linkedEntities)\n ? editData.linkedEntities.map((e) => ({ id: e.id, type: e.type as LinkedEntity['type'], label: e.label }))\n : [],\n )\n if (editData.recurrenceRule) {\n setRecurrenceEnabled(true)\n // Parse RRULE to set days and end type\n const rule = editData.recurrenceRule\n const byDayMatch = rule.match(/BYDAY=([A-Z,]+)/)\n if (byDayMatch) {\n const dayNames = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU']\n const activeDays = byDayMatch[1].split(',')\n setRecurrenceDays(dayNames.map((d) => activeDays.includes(d)))\n }\n const countMatch = rule.match(/COUNT=(\\d+)/)\n const untilMatch = rule.match(/UNTIL=(\\d{8})/)\n if (countMatch) {\n setRecurrenceEndType('count')\n setRecurrenceCount(Number(countMatch[1]))\n } else if (untilMatch) {\n setRecurrenceEndType('date')\n const raw = untilMatch[1]\n setRecurrenceEndDate(`${raw.slice(0, 4)}-${raw.slice(4, 6)}-${raw.slice(6, 8)}`)\n } else {\n setRecurrenceEndType('never')\n }\n } else {\n setRecurrenceEnabled(false)\n }\n if (editData.guestPermissions) {\n setGuestPermissions({\n canInviteOthers: editData.guestPermissions.canInviteOthers ?? true,\n canModify: editData.guestPermissions.canModify ?? false,\n canSeeList: editData.guestPermissions.canSeeList ?? true,\n })\n }\n } else {\n // Create mode: reset all fields\n const defaultStart = resolveDefaultActivityStart('meeting', new Date())\n setActivityType('meeting')\n setTitle('')\n setDate(formatLocalDateInput(defaultStart))\n setStartTime(formatLocalTimeInput(defaultStart))\n setDuration(30)\n setAllDay(false)\n setDescription('')\n setLocation('')\n setReminderMinutes(DEFAULT_REMINDER_MINUTES.meeting)\n setVisibility('team')\n setParticipants([])\n setLinkedEntities([])\n setRecurrenceEnabled(false)\n }\n setConflict(null)\n }\n return () => {\n // Safety net: restore body scroll if Radix Dialog fails to clean up\n document.body.style.removeProperty('overflow')\n document.body.style.removeProperty('pointer-events')\n }\n }, [open, editData])\n\n // Update the Reminder default when the activity type changes in create mode.\n // Skipped in edit mode (the persisted value wins), and gated by `open` to\n // avoid flipping the default in a closed-but-mounted dialog.\n const lastReminderTypeRef = React.useRef<ActivityType>('meeting')\n React.useEffect(() => {\n if (!open || isEditing) {\n lastReminderTypeRef.current = activityType\n return\n }\n if (lastReminderTypeRef.current === activityType) return\n lastReminderTypeRef.current = activityType\n setReminderMinutes(DEFAULT_REMINDER_MINUTES[activityType])\n }, [activityType, isEditing, open])\n\n const removeParticipant = React.useCallback((index: number) => {\n setParticipants((prev) => prev.filter((_, i) => i !== index))\n }, [])\n\n const toggleRecurrenceDay = React.useCallback((index: number) => {\n setRecurrenceDays((prev) => {\n const next = [...prev]\n next[index] = !next[index]\n return next\n })\n }, [])\n\n return {\n activityType,\n setActivityType,\n title,\n setTitle,\n date,\n setDate,\n startTime,\n setStartTime,\n duration,\n setDuration,\n allDay,\n setAllDay,\n description,\n setDescription,\n markdownEnabled,\n setMarkdownEnabled,\n location,\n setLocation,\n reminderMinutes,\n setReminderMinutes,\n visibility,\n setVisibility,\n participants,\n setParticipants,\n linkedEntities,\n setLinkedEntities,\n recurrenceEnabled,\n setRecurrenceEnabled,\n recurrenceDays,\n setRecurrenceDays,\n recurrenceEndType,\n setRecurrenceEndType,\n recurrenceCount,\n setRecurrenceCount,\n recurrenceEndDate,\n setRecurrenceEndDate,\n conflict,\n setConflict,\n saving,\n setSaving,\n guestPermissions,\n setGuestPermissions,\n removeParticipant,\n toggleRecurrenceDay,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -239,6 +239,19 @@ async function GET(req) {
239
239
  );
240
240
  const stalledPartitions = activePartitions.filter((p) => p.status === "stalled");
241
241
  const scopeCandidate = !preferOrg || !scopeRow || scopeRow.orgMatch ? scopeRow : null;
242
+ const computeScopeStatus = (row) => {
243
+ const heartbeatDate = row.heartbeat_at ? new Date(row.heartbeat_at) : null;
244
+ const finishedDate = row.finished_at ? new Date(row.finished_at) : null;
245
+ if (finishedDate) return row.status === "failed" ? "failed" : "completed";
246
+ if (!heartbeatDate || Date.now() - heartbeatDate.getTime() > HEARTBEAT_STALE_MS) {
247
+ return "stalled";
248
+ }
249
+ return row.status || "reindexing";
250
+ };
251
+ const scopeStatus = scopeCandidate ? computeScopeStatus(scopeCandidate.row) : null;
252
+ const scopeStartedAt = scopeCandidate?.row.started_at ? new Date(scopeCandidate.row.started_at).toISOString() : null;
253
+ const scopeFinishedAt = scopeCandidate?.row.finished_at ? new Date(scopeCandidate.row.finished_at).toISOString() : null;
254
+ const scopeHeartbeatAt = scopeCandidate?.row.heartbeat_at ? new Date(scopeCandidate.row.heartbeat_at).toISOString() : null;
242
255
  let status = "idle";
243
256
  if (activePartitions.length) {
244
257
  if (runningPartitions.length) {
@@ -248,10 +261,12 @@ async function GET(req) {
248
261
  }
249
262
  } else if (partitions.some((p) => p.status === "failed") || scopeCandidate?.row.finished_at && scopeCandidate.row.status === "failed") {
250
263
  status = "failed";
264
+ } else if (!partitions.length && scopeStatus && scopeStatus !== "completed") {
265
+ status = scopeStatus;
251
266
  }
252
- const startedAt = activePartitions[0]?.startedAt ?? partitions[0]?.startedAt ?? null;
253
- const finishedAt = status === "idle" || status === "failed" ? partitions.find((p) => p.finishedAt)?.finishedAt ?? null : null;
254
- const heartbeatAt = activePartitions[0]?.heartbeatAt ?? partitions[0]?.heartbeatAt ?? null;
267
+ const startedAt = activePartitions[0]?.startedAt ?? partitions[0]?.startedAt ?? (!partitions.length ? scopeStartedAt : null);
268
+ const finishedAt = status === "idle" || status === "failed" ? partitions.find((p) => p.finishedAt)?.finishedAt ?? (!partitions.length ? scopeFinishedAt : null) : null;
269
+ const heartbeatAt = activePartitions[0]?.heartbeatAt ?? partitions[0]?.heartbeatAt ?? (!partitions.length ? scopeHeartbeatAt : null);
255
270
  const jobTotalCount = partitions.reduce((sum, p) => sum + (p.totalCount ?? 0), 0);
256
271
  const processedSum = partitions.reduce((sum, p) => sum + (p.processedCount ?? 0), 0);
257
272
  const processedCount = jobTotalCount ? Math.min(jobTotalCount, processedSum) : processedSum || null;
@@ -264,15 +279,7 @@ async function GET(req) {
264
279
  totalCount: jobTotalCount ? jobTotalCount : scopeCandidate?.row?.total_count ?? null,
265
280
  partitions,
266
281
  scope: scopeCandidate ? {
267
- status: (() => {
268
- const heartbeatDate = scopeCandidate.row.heartbeat_at ? new Date(scopeCandidate.row.heartbeat_at) : null;
269
- const finishedDate = scopeCandidate.row.finished_at ? new Date(scopeCandidate.row.finished_at) : null;
270
- if (finishedDate) return scopeCandidate.row.status === "failed" ? "failed" : "completed";
271
- if (!heartbeatDate || Date.now() - heartbeatDate.getTime() > HEARTBEAT_STALE_MS) {
272
- return "stalled";
273
- }
274
- return scopeCandidate.row.status || "reindexing";
275
- })(),
282
+ status: scopeStatus,
276
283
  processedCount: scopeCandidate.row.processed_count ?? null,
277
284
  totalCount: scopeCandidate.row.total_count ?? null
278
285
  } : null